async_graphql/http/
mod.rs1#[cfg(feature = "altair")]
4mod altair_source;
5#[cfg(feature = "graphiql")]
6mod graphiql_plugin;
7#[cfg(feature = "graphiql")]
8mod graphiql_source;
9#[cfg(feature = "graphiql")]
10mod graphiql_v2_source;
11mod multipart;
12mod multipart_subscribe;
13#[cfg(feature = "playground")]
14mod playground_source;
15mod websocket;
16
17#[cfg(feature = "altair")]
18pub use altair_source::*;
19use futures_util::io::{AsyncRead, AsyncReadExt};
20#[cfg(feature = "graphiql")]
21pub use graphiql_plugin::{GraphiQLPlugin, graphiql_plugin_explorer};
22#[cfg(feature = "graphiql")]
23pub use graphiql_source::graphiql_source;
24#[cfg(feature = "graphiql")]
25pub use graphiql_v2_source::{Credentials, GraphiQLSource};
26pub use multipart::MultipartOptions;
27pub use multipart_subscribe::{create_multipart_mixed_stream, is_accept_multipart_mixed};
28#[cfg(feature = "playground")]
29pub use playground_source::{GraphQLPlaygroundConfig, playground_source};
30use serde::Deserialize;
31pub use websocket::{
32 ALL_WEBSOCKET_PROTOCOLS, ClientMessage, DefaultOnConnInitType, DefaultOnPingType,
33 Protocols as WebSocketProtocols, WebSocket, WsMessage, default_on_connection_init,
34 default_on_ping,
35};
36
37use crate::{BatchRequest, ParseRequestError, Request};
38
39pub fn parse_query_string(input: &str) -> Result<Request, ParseRequestError> {
41 #[derive(Deserialize)]
42 struct RequestSerde {
43 #[serde(default)]
44 pub query: String,
45 pub operation_name: Option<String>,
46 pub variables: Option<String>,
47 pub extensions: Option<String>,
48 }
49
50 let request: RequestSerde = serde_urlencoded::from_str(input).map_err(std::io::Error::other)?;
51 let variables = request
52 .variables
53 .map(|data| serde_json::from_str(&data))
54 .transpose()
55 .map_err(|err| std::io::Error::other(format!("invalid variables: {}", err)))?
56 .unwrap_or_default();
57 let extensions = request
58 .extensions
59 .map(|data| serde_json::from_str(&data))
60 .transpose()
61 .map_err(|err| std::io::Error::other(format!("invalid extensions: {}", err)))?
62 .unwrap_or_default();
63
64 Ok(Request {
65 operation_name: request.operation_name,
66 variables,
67 extensions,
68 ..Request::new(request.query)
69 })
70}
71
72pub async fn receive_body(
74 content_type: Option<impl AsRef<str>>,
75 body: impl AsyncRead + Send,
76 opts: MultipartOptions,
77) -> Result<Request, ParseRequestError> {
78 receive_batch_body(content_type, body, opts)
79 .await?
80 .into_single()
81}
82
83pub async fn receive_batch_body(
85 content_type: Option<impl AsRef<str>>,
86 body: impl AsyncRead + Send,
87 opts: MultipartOptions,
88) -> Result<BatchRequest, ParseRequestError> {
89 let content_type = content_type
91 .as_ref()
92 .map(AsRef::as_ref)
93 .unwrap_or("application/graphql-response+json");
94
95 let content_type: mime::Mime = content_type.parse()?;
96
97 match (content_type.type_(), content_type.subtype()) {
98 (mime::MULTIPART, _) => {
100 if let Some(boundary) = content_type.get_param("boundary") {
101 multipart::receive_batch_multipart(body, boundary.to_string(), opts).await
102 } else {
103 Err(ParseRequestError::InvalidMultipart(
104 multer::Error::NoBoundary,
105 ))
106 }
107 }
108 _ => receive_batch_body_no_multipart(&content_type, body).await,
113 }
114}
115
116pub(super) async fn receive_batch_body_no_multipart(
120 content_type: &mime::Mime,
121 body: impl AsyncRead + Send,
122) -> Result<BatchRequest, ParseRequestError> {
123 assert_ne!(content_type.type_(), mime::MULTIPART, "received multipart");
124 match (content_type.type_(), content_type.subtype()) {
125 #[cfg(feature = "cbor")]
126 (mime::OCTET_STREAM, _) | (mime::APPLICATION, mime::OCTET_STREAM) => {
129 receive_batch_cbor(body).await
130 }
131 _ => receive_batch_json(body).await,
133 }
134}
135pub async fn receive_json(body: impl AsyncRead) -> Result<Request, ParseRequestError> {
137 receive_batch_json(body).await?.into_single()
138}
139
140pub async fn receive_batch_json(body: impl AsyncRead) -> Result<BatchRequest, ParseRequestError> {
142 let mut data = Vec::new();
143 futures_util::pin_mut!(body);
144 body.read_to_end(&mut data)
145 .await
146 .map_err(ParseRequestError::Io)?;
147 serde_json::from_slice::<BatchRequest>(&data)
148 .map_err(|e| ParseRequestError::InvalidRequest(Box::new(e)))
149}
150
151#[cfg(feature = "cbor")]
153#[cfg_attr(docsrs, doc(cfg(feature = "cbor")))]
154pub async fn receive_cbor(body: impl AsyncRead) -> Result<Request, ParseRequestError> {
155 receive_batch_cbor(body).await?.into_single()
156}
157
158#[cfg(feature = "cbor")]
160#[cfg_attr(docsrs, doc(cfg(feature = "cbor")))]
161pub async fn receive_batch_cbor(body: impl AsyncRead) -> Result<BatchRequest, ParseRequestError> {
162 let mut data = Vec::new();
163 futures_util::pin_mut!(body);
164 body.read_to_end(&mut data)
165 .await
166 .map_err(ParseRequestError::Io)?;
167 serde_cbor::from_slice::<BatchRequest>(&data)
168 .map_err(|e| ParseRequestError::InvalidRequest(Box::new(e)))
169}
170
171#[cfg(test)]
172mod tests {
173 use std::collections::HashMap;
174
175 use async_graphql_value::Extensions;
176
177 use super::*;
178 use crate::{Variables, value};
179
180 #[test]
181 fn test_parse_query_string() {
182 let request = parse_query_string("variables=%7B%7D&extensions=%7B%22persistedQuery%22%3A%7B%22sha256Hash%22%3A%22cde5de0a350a19c59f8ddcd9646e5f260b2a7d5649ff6be8e63e9462934542c3%22%2C%22version%22%3A1%7D%7D").unwrap();
183 assert_eq!(request.query.as_str(), "");
184 assert_eq!(request.variables, Variables::default());
185 assert_eq!(request.extensions, {
186 let mut extensions = HashMap::new();
187 extensions.insert("persistedQuery".to_string(), value!({
188 "sha256Hash": "cde5de0a350a19c59f8ddcd9646e5f260b2a7d5649ff6be8e63e9462934542c3",
189 "version": 1,
190 }));
191 Extensions(extensions)
192 });
193
194 let request = parse_query_string("query={a}&variables=%7B%22a%22%3A10%7D").unwrap();
195 assert_eq!(request.query.as_str(), "{a}");
196 assert_eq!(
197 request.variables,
198 Variables::from_value(value!({ "a" : 10 }))
199 );
200 }
201}