1use std::{convert::Infallible, future::Future, str::FromStr, time::Duration};
2
3use async_graphql::{
4 Data, Executor, Result,
5 futures_util::task::{Context, Poll},
6 http::{
7 ALL_WEBSOCKET_PROTOCOLS, DefaultOnConnInitType, DefaultOnPingType, WebSocketProtocols,
8 WsMessage, default_on_connection_init, default_on_ping,
9 },
10 runtime::TokioTimer,
11};
12use axum::{
13 Error,
14 body::{Body, HttpBody},
15 extract::{
16 FromRequestParts, WebSocketUpgrade,
17 ws::{CloseFrame, Message},
18 },
19 http::{self, Request, Response, StatusCode, request::Parts},
20 response::IntoResponse,
21};
22use futures_util::{
23 Sink, SinkExt, Stream, StreamExt, future,
24 future::BoxFuture,
25 stream::{SplitSink, SplitStream},
26};
27use tower_service::Service;
28#[cfg(feature = "tracing")]
29use tracing::{Instrument, Span};
30
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35pub struct GraphQLProtocol(WebSocketProtocols);
36
37impl<S> FromRequestParts<S> for GraphQLProtocol
38where
39 S: Send + Sync,
40{
41 type Rejection = StatusCode;
42
43 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
44 parts
45 .headers
46 .get(http::header::SEC_WEBSOCKET_PROTOCOL)
47 .and_then(|value| value.to_str().ok())
48 .and_then(|protocols| {
49 protocols
50 .split(',')
51 .find_map(|p| WebSocketProtocols::from_str(p.trim()).ok())
52 })
53 .map(Self)
54 .ok_or(StatusCode::BAD_REQUEST)
55 }
56}
57
58pub struct GraphQLSubscription<E> {
60 executor: E,
61}
62
63impl<E> Clone for GraphQLSubscription<E>
64where
65 E: Executor,
66{
67 fn clone(&self) -> Self {
68 Self {
69 executor: self.executor.clone(),
70 }
71 }
72}
73
74impl<E> GraphQLSubscription<E>
75where
76 E: Executor,
77{
78 pub fn new(executor: E) -> Self {
80 Self { executor }
81 }
82}
83
84impl<B, E> Service<Request<B>> for GraphQLSubscription<E>
85where
86 B: HttpBody + Send + 'static,
87 E: Executor,
88{
89 type Response = Response<Body>;
90 type Error = Infallible;
91 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
92
93 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
94 Poll::Ready(Ok(()))
95 }
96
97 fn call(&mut self, req: Request<B>) -> Self::Future {
98 let executor = self.executor.clone();
99
100 Box::pin(async move {
101 let (mut parts, _body) = req.into_parts();
102
103 let protocol = match GraphQLProtocol::from_request_parts(&mut parts, &()).await {
104 Ok(protocol) => protocol,
105 Err(err) => return Ok(err.into_response()),
106 };
107 let upgrade = match WebSocketUpgrade::from_request_parts(&mut parts, &()).await {
108 Ok(upgrade) => upgrade,
109 Err(err) => return Ok(err.into_response()),
110 };
111
112 let executor = executor.clone();
113
114 #[cfg(feature = "tracing")]
115 let span = Span::current();
116
117 let resp = upgrade
118 .protocols(ALL_WEBSOCKET_PROTOCOLS)
119 .on_upgrade(move |stream| {
120 let task = GraphQLWebSocket::new(stream, executor, protocol).serve();
121 #[cfg(feature = "tracing")]
122 let task = task.instrument(span);
123 task
124 });
125 Ok(resp.into_response())
126 })
127 }
128}
129
130pub struct GraphQLWebSocket<Sink, Stream, E, OnConnInit, OnPing> {
132 sink: Sink,
133 stream: Stream,
134 executor: E,
135 data: Data,
136 on_connection_init: OnConnInit,
137 on_ping: OnPing,
138 protocol: GraphQLProtocol,
139 keepalive_timeout: Option<Duration>,
140}
141
142impl<S, E>
143 GraphQLWebSocket<
144 SplitSink<S, Message>,
145 SplitStream<S>,
146 E,
147 DefaultOnConnInitType,
148 DefaultOnPingType,
149 >
150where
151 S: Stream<Item = Result<Message, Error>> + Sink<Message>,
152 E: Executor,
153{
154 pub fn new(stream: S, executor: E, protocol: GraphQLProtocol) -> Self {
156 let (sink, stream) = stream.split();
157 GraphQLWebSocket::new_with_pair(sink, stream, executor, protocol)
158 }
159}
160
161impl<Sink, Stream, E> GraphQLWebSocket<Sink, Stream, E, DefaultOnConnInitType, DefaultOnPingType>
162where
163 Sink: futures_util::sink::Sink<Message>,
164 Stream: futures_util::stream::Stream<Item = Result<Message, Error>>,
165 E: Executor,
166{
167 pub fn new_with_pair(
169 sink: Sink,
170 stream: Stream,
171 executor: E,
172 protocol: GraphQLProtocol,
173 ) -> Self {
174 GraphQLWebSocket {
175 sink,
176 stream,
177 executor,
178 data: Data::default(),
179 on_connection_init: default_on_connection_init,
180 on_ping: default_on_ping,
181 protocol,
182 keepalive_timeout: None,
183 }
184 }
185}
186
187impl<Sink, Stream, E, OnConnInit, OnConnInitFut, OnPing, OnPingFut>
188 GraphQLWebSocket<Sink, Stream, E, OnConnInit, OnPing>
189where
190 Sink: futures_util::sink::Sink<Message>,
191 Stream: futures_util::stream::Stream<Item = Result<Message, Error>>,
192 E: Executor,
193 OnConnInit: FnOnce(serde_json::Value) -> OnConnInitFut + Send + 'static,
194 OnConnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
195 OnPing: FnOnce(Option<&Data>, Option<serde_json::Value>) -> OnPingFut + Clone + Send + 'static,
196 OnPingFut: Future<Output = async_graphql::Result<Option<serde_json::Value>>> + Send + 'static,
197{
198 #[must_use]
201 pub fn with_data(self, data: Data) -> Self {
202 Self { data, ..self }
203 }
204
205 #[must_use]
212 pub fn on_connection_init<F, R>(
213 self,
214 callback: F,
215 ) -> GraphQLWebSocket<Sink, Stream, E, F, OnPing>
216 where
217 F: FnOnce(serde_json::Value) -> R + Send + 'static,
218 R: Future<Output = async_graphql::Result<Data>> + Send + 'static,
219 {
220 GraphQLWebSocket {
221 sink: self.sink,
222 stream: self.stream,
223 executor: self.executor,
224 data: self.data,
225 on_connection_init: callback,
226 on_ping: self.on_ping,
227 protocol: self.protocol,
228 keepalive_timeout: self.keepalive_timeout,
229 }
230 }
231
232 #[must_use]
241 pub fn on_ping<F, R>(self, callback: F) -> GraphQLWebSocket<Sink, Stream, E, OnConnInit, F>
242 where
243 F: FnOnce(Option<&Data>, Option<serde_json::Value>) -> R + Clone + Send + 'static,
244 R: Future<Output = Result<Option<serde_json::Value>>> + Send + 'static,
245 {
246 GraphQLWebSocket {
247 sink: self.sink,
248 stream: self.stream,
249 executor: self.executor,
250 data: self.data,
251 on_connection_init: self.on_connection_init,
252 on_ping: callback,
253 protocol: self.protocol,
254 keepalive_timeout: self.keepalive_timeout,
255 }
256 }
257
258 #[must_use]
265 pub fn keepalive_timeout(self, timeout: impl Into<Option<Duration>>) -> Self {
266 Self {
267 keepalive_timeout: timeout.into(),
268 ..self
269 }
270 }
271
272 pub async fn serve(self) {
274 let input = self
275 .stream
276 .take_while(|res| future::ready(res.is_ok()))
277 .map(Result::unwrap)
278 .filter_map(|msg| {
279 if let Message::Text(_) | Message::Binary(_) = msg {
280 future::ready(Some(msg))
281 } else {
282 future::ready(None)
283 }
284 })
285 .map(Message::into_data);
286
287 let stream =
288 async_graphql::http::WebSocket::new(self.executor.clone(), input, self.protocol.0)
289 .connection_data(self.data)
290 .on_connection_init(self.on_connection_init)
291 .on_ping(self.on_ping.clone())
292 .keepalive_timeout(TokioTimer::default(), self.keepalive_timeout)
293 .map(|msg| match msg {
294 WsMessage::Text(text) => Message::Text(text.into()),
295 WsMessage::Close(code, status) => Message::Close(Some(CloseFrame {
296 code,
297 reason: status.into(),
298 })),
299 });
300
301 let sink = self.sink;
302 futures_util::pin_mut!(stream, sink);
303
304 while let Some(item) = stream.next().await {
305 if sink.send(item).await.is_err() {
306 break;
307 }
308 }
309 }
310}