Fix the problem that some integrations overwritten HTTP headers. #793
This commit is contained in:
parent
b9feedc2ca
commit
c2feefdf09
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# [3.0.25] 2022-1-24
|
||||
|
||||
- Fix the problem that some integrations overwritten HTTP headers. [#793](https://github.com/async-graphql/async-graphql/issues/793)
|
||||
|
||||
# [3.0.24] 2022-1-24
|
||||
|
||||
- Remove `'static` bound for `impl From<T> for Error`.
|
||||
|
|
|
@ -177,15 +177,14 @@ impl Responder for GraphQLResponse {
|
|||
type Body = BoxBody;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse {
|
||||
let mut res = HttpResponse::build(StatusCode::OK);
|
||||
let mut builder = HttpResponse::build(StatusCode::OK);
|
||||
|
||||
if self.0.is_ok() {
|
||||
if let Some(cache_control) = self.0.cache_control().value() {
|
||||
res.append_header((http::header::CACHE_CONTROL, cache_control));
|
||||
builder.append_header((http::header::CACHE_CONTROL, cache_control));
|
||||
}
|
||||
}
|
||||
for (name, value) in self.0.http_headers() {
|
||||
res.append_header((name, value));
|
||||
}
|
||||
|
||||
let accept = req
|
||||
.headers()
|
||||
.get(http::header::ACCEPT)
|
||||
|
@ -209,7 +208,13 @@ impl Responder for GraphQLResponse {
|
|||
},
|
||||
),
|
||||
};
|
||||
res.content_type(ct);
|
||||
res.body(body)
|
||||
|
||||
let mut resp = builder.content_type(ct).body(body);
|
||||
|
||||
for (name, value) in self.0.http_headers_iter() {
|
||||
resp.headers_mut().append(name, value);
|
||||
}
|
||||
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ where
|
|||
{
|
||||
/// Specify the initial subscription context data, usually you can get something from the
|
||||
/// incoming request to create it.
|
||||
#[must_use]
|
||||
pub fn with_data(self, data: Data) -> Self {
|
||||
Self { data, ..self }
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use axum::body::{boxed, Body, BoxBody};
|
||||
use axum::http;
|
||||
use axum::http::header::HeaderName;
|
||||
use axum::http::{HeaderValue, Response};
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
|
@ -38,14 +37,8 @@ impl IntoResponse for GraphQLResponse {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (name, value) in self.0.http_headers() {
|
||||
if let (Ok(name), Ok(value)) = (
|
||||
HeaderName::try_from(name.as_bytes()),
|
||||
HeaderValue::from_str(value),
|
||||
) {
|
||||
resp.headers_mut().insert(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
resp.headers_mut().extend(self.0.http_headers());
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ where
|
|||
{
|
||||
/// Specify the initial subscription context data, usually you can get something from the
|
||||
/// incoming request to create it.
|
||||
#[must_use]
|
||||
pub fn with_data(self, data: Data) -> Self {
|
||||
Self { data, ..self }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use poem::http::header::HeaderName;
|
||||
use poem::web::Json;
|
||||
use poem::{IntoResponse, Response};
|
||||
|
||||
|
@ -38,13 +37,7 @@ impl IntoResponse for GraphQLBatchResponse {
|
|||
}
|
||||
}
|
||||
|
||||
for (name, value) in self.0.http_headers() {
|
||||
if let (Ok(name), Ok(value)) = (TryInto::<HeaderName>::try_into(name), value.try_into())
|
||||
{
|
||||
resp.headers_mut().append(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
resp.headers_mut().extend(self.0.http_headers());
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,6 +183,7 @@ where
|
|||
{
|
||||
/// Specify the initial subscription context data, usually you can get something from the
|
||||
/// incoming request to create it.
|
||||
#[must_use]
|
||||
pub fn with_data(self, data: Data) -> Self {
|
||||
Self { data, ..self }
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ impl GraphQLRequest {
|
|||
}
|
||||
|
||||
/// Insert some data for this request.
|
||||
#[must_use]
|
||||
pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
|
||||
self.0.data.insert(data);
|
||||
self
|
||||
|
@ -215,8 +216,11 @@ impl<'r> Responder<'r, 'static> for GraphQLResponse {
|
|||
response.set_header(Header::new("cache-control", cache_control));
|
||||
}
|
||||
}
|
||||
for (name, value) in self.0.http_headers() {
|
||||
response.adjoin_header(Header::new(name.to_string(), value.to_string()));
|
||||
|
||||
for (name, value) in self.0.http_headers_iter() {
|
||||
if let Ok(value) = value.to_str() {
|
||||
response.adjoin_header(Header::new(name.as_str().to_string(), value.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
response.set_sized_body(body.len(), Cursor::new(body));
|
||||
|
|
|
@ -167,9 +167,13 @@ 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.append_header(name, value);
|
||||
|
||||
for (name, value) in resp.http_headers_iter() {
|
||||
if let Ok(value) = value.to_str() {
|
||||
response.append_header(name.as_str(), value);
|
||||
}
|
||||
}
|
||||
|
||||
response.set_body(Body::from_json(&resp)?);
|
||||
Ok(response)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use std::io::ErrorKind;
|
|||
use async_graphql::http::MultipartOptions;
|
||||
use async_graphql::{BatchRequest, ObjectType, Schema, SubscriptionType};
|
||||
use futures_util::TryStreamExt;
|
||||
use warp::hyper::header::HeaderName;
|
||||
use warp::reply::Response as WarpResponse;
|
||||
use warp::{Buf, Filter, Rejection, Reply};
|
||||
|
||||
|
@ -85,13 +84,8 @@ impl Reply for GraphQLBatchResponse {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (name, value) in self.0.http_headers() {
|
||||
if let (Ok(name), Ok(value)) = (TryInto::<HeaderName>::try_into(name), value.try_into())
|
||||
{
|
||||
resp.headers_mut().append(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
resp.headers_mut().extend(self.0.http_headers());
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,6 +233,7 @@ where
|
|||
{
|
||||
/// Specify the initial subscription context data, usually you can get something from the
|
||||
/// incoming request to create it.
|
||||
#[must_use]
|
||||
pub fn with_data(self, data: Data) -> Self {
|
||||
Self { data, ..self }
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex};
|
|||
use async_graphql_value::{Value as InputValue, Variables};
|
||||
use fnv::FnvHashMap;
|
||||
use http::header::{AsHeaderName, HeaderMap, IntoHeaderName};
|
||||
use http::HeaderValue;
|
||||
use serde::ser::{SerializeSeq, Serializer};
|
||||
use serde::Serialize;
|
||||
|
||||
|
@ -242,7 +243,7 @@ pub struct QueryEnvInner {
|
|||
pub uploads: Vec<UploadValue>,
|
||||
pub session_data: Arc<Data>,
|
||||
pub ctx_data: Arc<Data>,
|
||||
pub http_headers: Mutex<HeaderMap<String>>,
|
||||
pub http_headers: Mutex<HeaderMap>,
|
||||
pub disable_introspection: bool,
|
||||
pub errors: Mutex<Vec<ServerError>>,
|
||||
}
|
||||
|
@ -434,6 +435,7 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
/// ```no_run
|
||||
/// use async_graphql::*;
|
||||
/// use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||
/// use ::http::HeaderValue;
|
||||
///
|
||||
/// struct Query;
|
||||
///
|
||||
|
@ -453,7 +455,7 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
/// // 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()));
|
||||
/// assert_eq!(was_in_headers, Some(HeaderValue::from_static("1234")));
|
||||
///
|
||||
/// String::from("Hello world")
|
||||
/// }
|
||||
|
@ -462,13 +464,17 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
pub fn insert_http_header(
|
||||
&self,
|
||||
name: impl IntoHeaderName,
|
||||
value: impl Into<String>,
|
||||
) -> Option<String> {
|
||||
self.query_env
|
||||
.http_headers
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(name, value.into())
|
||||
value: impl TryInto<HeaderValue>,
|
||||
) -> Option<HeaderValue> {
|
||||
if let Ok(value) = value.try_into() {
|
||||
self.query_env
|
||||
.http_headers
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(name, value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a HTTP header to response.
|
||||
|
@ -503,12 +509,20 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn append_http_header(&self, name: impl IntoHeaderName, value: impl Into<String>) -> bool {
|
||||
self.query_env
|
||||
.http_headers
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(name, value.into())
|
||||
pub fn append_http_header(
|
||||
&self,
|
||||
name: impl IntoHeaderName,
|
||||
value: impl TryInto<HeaderValue>,
|
||||
) -> bool {
|
||||
if let Ok(value) = value.try_into() {
|
||||
self.query_env
|
||||
.http_headers
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(name, value)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn var_value(&self, name: &str, pos: Pos) -> ServerResult<Value> {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use http::header::HeaderMap;
|
||||
use http::header::{HeaderMap, HeaderName};
|
||||
use http::HeaderValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{CacheControl, Result, ServerError, Value};
|
||||
|
@ -26,7 +27,7 @@ pub struct Response {
|
|||
|
||||
/// HTTP headers
|
||||
#[serde(skip)]
|
||||
pub http_headers: HeaderMap<String>,
|
||||
pub http_headers: HeaderMap,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
@ -57,7 +58,7 @@ impl Response {
|
|||
|
||||
/// Set the http headers of the response.
|
||||
#[must_use]
|
||||
pub fn http_headers(self, http_headers: HeaderMap<String>) -> Self {
|
||||
pub fn http_headers(self, http_headers: HeaderMap) -> Self {
|
||||
Self {
|
||||
http_headers,
|
||||
..self
|
||||
|
@ -128,25 +129,30 @@ impl BatchResponse {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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(|(key, value)| (key.as_str(), value.as_str())),
|
||||
),
|
||||
BatchResponse::Batch(resp) => Box::new(
|
||||
resp.iter()
|
||||
.map(|r| {
|
||||
r.http_headers
|
||||
.iter()
|
||||
.map(|(key, value)| (key.as_str(), value.as_str()))
|
||||
})
|
||||
.flatten(),
|
||||
),
|
||||
};
|
||||
it
|
||||
/// Returns HTTP headers map.
|
||||
pub fn http_headers(&self) -> HeaderMap {
|
||||
match self {
|
||||
BatchResponse::Single(resp) => resp.http_headers.clone(),
|
||||
BatchResponse::Batch(resp) => resp.iter().fold(HeaderMap::new(), |mut acc, resp| {
|
||||
acc.extend(resp.http_headers.clone());
|
||||
acc
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns HTTP headers iterator.
|
||||
pub fn http_headers_iter(&self) -> impl Iterator<Item = (HeaderName, HeaderValue)> {
|
||||
let headers = self.http_headers();
|
||||
|
||||
let mut current_name = None;
|
||||
headers.into_iter().filter_map(move |(name, value)| {
|
||||
if let Some(name) = name {
|
||||
current_name = Some(name);
|
||||
}
|
||||
current_name
|
||||
.clone()
|
||||
.map(|current_name| (current_name, value))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use ::http::HeaderValue;
|
||||
use async_graphql::*;
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -37,8 +38,14 @@ pub async fn test_http_headers() {
|
|||
|
||||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||
let resp = schema.execute("{ value }").await;
|
||||
assert_eq!(resp.http_headers.get("A").map(|s| &**s), Some("1"));
|
||||
assert_eq!(
|
||||
resp.http_headers.get("A"),
|
||||
Some(&HeaderValue::from_static("1"))
|
||||
);
|
||||
|
||||
let resp = schema.execute("{ err }").await;
|
||||
assert_eq!(resp.http_headers.get("A").map(|s| &**s), Some("1"));
|
||||
assert_eq!(
|
||||
resp.http_headers.get("A"),
|
||||
Some(&HeaderValue::from_static("1"))
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue