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"
|
||||
thiserror = "1.0.21"
|
||||
static_assertions = "1.1.0"
|
||||
http = "0.2.3"
|
||||
|
||||
# Feature optional dependencies
|
||||
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 mut response = rocket::Response::new();
|
||||
response.set_header(ContentType::new("application", "json"));
|
||||
|
||||
if self.0.is_ok() {
|
||||
if let Some(cache_control) = self.0.cache_control().value() {
|
||||
response.set_header(Header::new("cache-control", cache_control));
|
||||
}
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
for (name, value) in resp.http_headers() {
|
||||
response.insert_header(name, value);
|
||||
response.append_header(name, value);
|
||||
}
|
||||
}
|
||||
response.set_body(Body::from_json(&resp)?);
|
||||
|
|
|
@ -86,7 +86,7 @@ impl Reply for BatchResponse {
|
|||
if let (Ok(name), Ok(value)) =
|
||||
(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 fnv::FnvHashMap;
|
||||
use http::header::{AsHeaderName, HeaderMap, IntoHeaderName};
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{SerializeSeq, Serializer};
|
||||
use serde::Serialize;
|
||||
|
@ -320,7 +321,7 @@ pub struct QueryEnvInner {
|
|||
pub fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
||||
pub uploads: Vec<UploadValue>,
|
||||
pub ctx_data: Arc<Data>,
|
||||
pub http_headers: spin::Mutex<HashMap<String, String>>,
|
||||
pub http_headers: spin::Mutex<HeaderMap<String>>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -443,12 +444,124 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
.and_then(|d| d.downcast_ref::<D>())
|
||||
}
|
||||
|
||||
/// Sets an HTTP header to response.
|
||||
pub fn set_http_header(&self, name: impl Into<String>, value: impl Into<String>) {
|
||||
/// Returns whether the HTTP header `key` is currently set on the response
|
||||
///
|
||||
/// # 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
|
||||
.http_headers
|
||||
.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> {
|
||||
|
|
|
@ -70,7 +70,7 @@ impl Extension for LoggerExtension {
|
|||
struct DisplayError<'a> {
|
||||
log: &'a LoggerExtension,
|
||||
e: &'a ServerError,
|
||||
};
|
||||
}
|
||||
impl<'a> Display for DisplayError<'a> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[Error] ")?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use http::header::HeaderMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -25,7 +25,7 @@ pub struct Response {
|
|||
|
||||
/// HTTP headers
|
||||
#[serde(skip)]
|
||||
pub http_headers: HashMap<String, String>,
|
||||
pub http_headers: HeaderMap<String>,
|
||||
}
|
||||
|
||||
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)> {
|
||||
let it: Box<dyn Iterator<Item = (&str, &str)>> = match self {
|
||||
BatchResponse::Single(resp) => Box::new(
|
||||
resp.http_headers
|
||||
.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(
|
||||
resp.iter()
|
||||
.map(|r| {
|
||||
r.http_headers
|
||||
.iter()
|
||||
.map(|item| (item.0.as_str(), item.1.as_str()))
|
||||
.map(|(key, value)| (key.as_str(), value.as_str()))
|
||||
})
|
||||
.flatten(),
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue
Block a user