Update http-headers to use http::HeaderMap
This commit is contained in:
parent
b8279d1e44
commit
f92dda0271
|
@ -55,6 +55,7 @@ serde_json = "1.0.48"
|
||||||
spin = "0.7.0"
|
spin = "0.7.0"
|
||||||
thiserror = "1.0.21"
|
thiserror = "1.0.21"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
|
http = "0.2.3"
|
||||||
|
|
||||||
# Feature optional dependencies
|
# Feature optional dependencies
|
||||||
bson = { version = "1.0.0", optional = true }
|
bson = { version = "1.0.0", optional = true }
|
||||||
|
|
|
@ -169,17 +169,17 @@ impl<'r> Responder<'r, 'static> for Response {
|
||||||
let body = serde_json::to_string(&self.0).unwrap();
|
let body = serde_json::to_string(&self.0).unwrap();
|
||||||
|
|
||||||
let mut response = rocket::Response::new();
|
let mut response = rocket::Response::new();
|
||||||
|
response.set_header(ContentType::new("application", "json"));
|
||||||
|
|
||||||
if self.0.is_ok() {
|
if self.0.is_ok() {
|
||||||
if let Some(cache_control) = self.0.cache_control().value() {
|
if let Some(cache_control) = self.0.cache_control().value() {
|
||||||
response.set_header(Header::new("cache-control", cache_control));
|
response.set_header(Header::new("cache-control", cache_control));
|
||||||
}
|
}
|
||||||
for (name, value) in self.0.http_headers() {
|
for (name, value) in self.0.http_headers() {
|
||||||
response.set_header(Header::new(name.to_string(), value.to_string()));
|
response.adjoin_header(Header::new(name.to_string(), value.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.set_header(ContentType::new("application", "json"));
|
|
||||||
response.set_sized_body(body.len(), Cursor::new(body));
|
response.set_sized_body(body.len(), Cursor::new(body));
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
|
|
|
@ -164,7 +164,7 @@ pub fn respond(resp: impl Into<async_graphql::BatchResponse>) -> tide::Result {
|
||||||
response.insert_header(headers::CACHE_CONTROL, cache_control);
|
response.insert_header(headers::CACHE_CONTROL, cache_control);
|
||||||
}
|
}
|
||||||
for (name, value) in resp.http_headers() {
|
for (name, value) in resp.http_headers() {
|
||||||
response.insert_header(name, value);
|
response.append_header(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.set_body(Body::from_json(&resp)?);
|
response.set_body(Body::from_json(&resp)?);
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl Reply for BatchResponse {
|
||||||
if let (Ok(name), Ok(value)) =
|
if let (Ok(name), Ok(value)) =
|
||||||
(TryInto::<HeaderName>::try_into(name), value.try_into())
|
(TryInto::<HeaderName>::try_into(name), value.try_into())
|
||||||
{
|
{
|
||||||
resp.headers_mut().insert(name, value);
|
resp.headers_mut().append(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
121
src/context.rs
121
src/context.rs
|
@ -10,6 +10,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use async_graphql_value::Value as InputValue;
|
use async_graphql_value::Value as InputValue;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
use http::header::{AsHeaderName, HeaderMap, IntoHeaderName};
|
||||||
use serde::de::{Deserialize, Deserializer};
|
use serde::de::{Deserialize, Deserializer};
|
||||||
use serde::ser::{SerializeSeq, Serializer};
|
use serde::ser::{SerializeSeq, Serializer};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -320,7 +321,7 @@ pub struct QueryEnvInner {
|
||||||
pub fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
pub fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
||||||
pub uploads: Vec<UploadValue>,
|
pub uploads: Vec<UploadValue>,
|
||||||
pub ctx_data: Arc<Data>,
|
pub ctx_data: Arc<Data>,
|
||||||
pub http_headers: spin::Mutex<HashMap<String, String>>,
|
pub http_headers: spin::Mutex<HeaderMap<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -443,12 +444,124 @@ impl<'a, T> ContextBase<'a, T> {
|
||||||
.and_then(|d| d.downcast_ref::<D>())
|
.and_then(|d| d.downcast_ref::<D>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an HTTP header to response.
|
/// Returns whether the HTTP header `key` is currently set on the response
|
||||||
pub fn set_http_header(&self, name: impl Into<String>, value: impl Into<String>) {
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use async_graphql::*;
|
||||||
|
/// use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||||
|
///
|
||||||
|
/// struct Query;
|
||||||
|
///
|
||||||
|
/// #[Object]
|
||||||
|
/// impl Query {
|
||||||
|
/// async fn greet(&self, ctx: &Context<'_>) -> String {
|
||||||
|
///
|
||||||
|
/// let header_exists = ctx.http_header_contains("Access-Control-Allow-Origin");
|
||||||
|
/// assert!(!header_exists);
|
||||||
|
///
|
||||||
|
/// ctx.insert_http_header(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||||
|
///
|
||||||
|
/// let header_exists = ctx.http_header_contains("Access-Control-Allow-Origin");
|
||||||
|
/// assert!(header_exists);
|
||||||
|
///
|
||||||
|
/// String::from("Hello world")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn http_header_contains(&self, key: impl AsHeaderName) -> bool {
|
||||||
|
self.query_env.http_headers.lock().contains_key(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a HTTP header to response.
|
||||||
|
///
|
||||||
|
/// If the header was not currently set on the response, then `None` is returned.
|
||||||
|
///
|
||||||
|
/// If the response already contained this header then the new value is associated with this key
|
||||||
|
/// and __all the previous values are removed__, however only a the first previous
|
||||||
|
/// value is returned.
|
||||||
|
///
|
||||||
|
/// See [`http::HeaderMap`] for more details on the underlying implementation
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use async_graphql::*;
|
||||||
|
/// use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||||
|
///
|
||||||
|
/// struct Query;
|
||||||
|
///
|
||||||
|
/// #[Object]
|
||||||
|
/// impl Query {
|
||||||
|
/// async fn greet(&self, ctx: &Context<'_>) -> String {
|
||||||
|
///
|
||||||
|
/// // Headers can be inserted using the `http` constants
|
||||||
|
/// let was_in_headers = ctx.insert_http_header(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||||
|
/// assert_eq!(was_in_headers, None);
|
||||||
|
///
|
||||||
|
/// // They can also be inserted using &str
|
||||||
|
/// let was_in_headers = ctx.insert_http_header("Custom-Header", "1234");
|
||||||
|
/// assert_eq!(was_in_headers, None);
|
||||||
|
///
|
||||||
|
/// // If multiple headers with the same key are `inserted` then the most recent
|
||||||
|
/// // one overwrites the previous. If you want multiple headers for the same key, use
|
||||||
|
/// // `append_http_header` for subsequent headers
|
||||||
|
/// let was_in_headers = ctx.insert_http_header("Custom-Header", "Hello World");
|
||||||
|
/// assert_eq!(was_in_headers, Some("1234".to_string()));
|
||||||
|
///
|
||||||
|
/// String::from("Hello world")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn insert_http_header(
|
||||||
|
&self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<String>,
|
||||||
|
) -> Option<String> {
|
||||||
self.query_env
|
self.query_env
|
||||||
.http_headers
|
.http_headers
|
||||||
.lock()
|
.lock()
|
||||||
.insert(name.into(), value.into());
|
.insert(name, value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a HTTP header to response.
|
||||||
|
///
|
||||||
|
/// If the header was not currently set on the response, then `false` is returned.
|
||||||
|
///
|
||||||
|
/// If the response did have this header then the new value is appended to the end of the
|
||||||
|
/// list of values currently associated with the key, however the key is not updated
|
||||||
|
/// _(which is important for types that can be `==` without being identical)_.
|
||||||
|
///
|
||||||
|
/// See [`http::HeaderMap`] for more details on the underlying implementation
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use async_graphql::*;
|
||||||
|
/// use ::http::header::SET_COOKIE;
|
||||||
|
///
|
||||||
|
/// struct Query;
|
||||||
|
///
|
||||||
|
/// #[Object]
|
||||||
|
/// impl Query {
|
||||||
|
/// async fn greet(&self, ctx: &Context<'_>) -> String {
|
||||||
|
/// // Insert the first instance of the header
|
||||||
|
/// ctx.insert_http_header(SET_COOKIE, "Chocolate Chip");
|
||||||
|
///
|
||||||
|
/// // Subsequent values should be appended
|
||||||
|
/// let header_already_exists = ctx.append_http_header("Set-Cookie", "Macadamia");
|
||||||
|
/// assert!(header_already_exists);
|
||||||
|
///
|
||||||
|
/// String::from("Hello world")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn append_http_header(&self, name: impl IntoHeaderName, value: impl Into<String>) -> bool {
|
||||||
|
self.query_env
|
||||||
|
.http_headers
|
||||||
|
.lock()
|
||||||
|
.append(name, value.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_value(&self, name: &str, pos: Pos) -> ServerResult<Value> {
|
fn var_value(&self, name: &str, pos: Pos) -> ServerResult<Value> {
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl Extension for LoggerExtension {
|
||||||
struct DisplayError<'a> {
|
struct DisplayError<'a> {
|
||||||
log: &'a LoggerExtension,
|
log: &'a LoggerExtension,
|
||||||
e: &'a ServerError,
|
e: &'a ServerError,
|
||||||
};
|
}
|
||||||
impl<'a> Display for DisplayError<'a> {
|
impl<'a> Display for DisplayError<'a> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[Error] ")?;
|
write!(f, "[Error] ")?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use http::header::HeaderMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ pub struct Response {
|
||||||
|
|
||||||
/// HTTP headers
|
/// HTTP headers
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub http_headers: HashMap<String, String>,
|
pub http_headers: HeaderMap<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
|
@ -116,20 +116,20 @@ impl BatchResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets HTTP headers
|
/// Provides an iterator over all of the HTTP headers set on the response
|
||||||
pub fn http_headers(&self) -> impl Iterator<Item = (&str, &str)> {
|
pub fn http_headers(&self) -> impl Iterator<Item = (&str, &str)> {
|
||||||
let it: Box<dyn Iterator<Item = (&str, &str)>> = match self {
|
let it: Box<dyn Iterator<Item = (&str, &str)>> = match self {
|
||||||
BatchResponse::Single(resp) => Box::new(
|
BatchResponse::Single(resp) => Box::new(
|
||||||
resp.http_headers
|
resp.http_headers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| (item.0.as_str(), item.1.as_str())),
|
.map(|(key, value)| (key.as_str(), value.as_str())),
|
||||||
),
|
),
|
||||||
BatchResponse::Batch(resp) => Box::new(
|
BatchResponse::Batch(resp) => Box::new(
|
||||||
resp.iter()
|
resp.iter()
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
r.http_headers
|
r.http_headers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| (item.0.as_str(), item.1.as_str()))
|
.map(|(key, value)| (key.as_str(), value.as_str()))
|
||||||
})
|
})
|
||||||
.flatten(),
|
.flatten(),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user