1use std::{
2 borrow::Cow,
3 collections::HashMap,
4 convert::Infallible,
5 fmt::Display,
6 pin::Pin,
7 sync::Arc,
8 task::{Context, Poll},
9 time::Duration,
10};
11
12use bytes::Bytes;
13use futures::{Stream, StreamExt, future::BoxFuture};
14use http::{HeaderMap, Method, Request, Response, header::ALLOW};
15use http_body::Body;
16use http_body_util::{BodyExt, Full, combinators::BoxBody};
17use pin_project_lite::pin_project;
18use tokio_stream::wrappers::ReceiverStream;
19use tokio_util::sync::CancellationToken;
20
21use super::session::{
22 RestoreOutcome, SessionId, SessionManager, SessionRestoreMarker, SessionState, SessionStore,
23};
24use crate::{
25 RoleServer,
26 model::{
27 ClientCapabilities, ClientJsonRpcMessage, ClientNotification, ClientRequest, ErrorCode,
28 ErrorData, GetExtensions, GetMeta, Implementation, InitializeRequest,
29 InitializeRequestParams, InitializedNotification, JsonObject, JsonRpcError,
30 ProtocolVersion, RequestId, ServerJsonRpcMessage,
31 },
32 serve_server,
33 service::serve_directly_with_ct,
34 transport::{
35 OneshotTransport, TransportAdapterIdentity,
36 common::{
37 http_header::{
38 EVENT_STREAM_MIME_TYPE, HEADER_LAST_EVENT_ID, HEADER_MCP_PROTOCOL_VERSION,
39 HEADER_SESSION_ID, JSON_MIME_TYPE,
40 },
41 mcp_headers,
42 server_side_http::{
43 BoxResponse, ServerSseMessage, accepted_response, expect_json,
44 internal_error_response, sse_stream_response, unexpected_message_response,
45 },
46 },
47 },
48};
49
50pub(crate) const DEFAULT_MAX_REQUEST_BODY_BYTES: usize = 4 * 1024 * 1024;
52
53#[non_exhaustive]
54#[derive(Debug, Clone)]
55pub struct StreamableHttpServerConfig {
56 pub sse_keep_alive: Option<Duration>,
58 pub sse_retry: Option<Duration>,
60 pub stateful_mode: bool,
63 pub json_response: bool,
68 pub cancellation_token: CancellationToken,
73 pub allowed_hosts: Vec<String>,
83 pub allowed_origins: Vec<String>,
92 pub session_store: Option<Arc<dyn SessionStore>>,
114 pub max_request_body_bytes: usize,
120}
121
122impl std::fmt::Debug for dyn SessionStore {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 f.write_str("<SessionStore>")
125 }
126}
127
128impl Default for StreamableHttpServerConfig {
129 fn default() -> Self {
130 Self {
131 sse_keep_alive: Some(Duration::from_secs(15)),
132 sse_retry: Some(Duration::from_secs(3)),
133 stateful_mode: true,
134 json_response: false,
135 cancellation_token: CancellationToken::new(),
136 allowed_hosts: vec!["localhost".into(), "127.0.0.1".into(), "::1".into()],
137 allowed_origins: vec![],
138 session_store: None,
139 max_request_body_bytes: DEFAULT_MAX_REQUEST_BODY_BYTES,
140 }
141 }
142}
143
144impl StreamableHttpServerConfig {
145 pub fn with_allowed_hosts(
146 mut self,
147 allowed_hosts: impl IntoIterator<Item = impl Into<String>>,
148 ) -> Self {
149 self.allowed_hosts = allowed_hosts.into_iter().map(Into::into).collect();
150 self
151 }
152 pub fn disable_allowed_hosts(mut self) -> Self {
154 self.allowed_hosts.clear();
155 self
156 }
157 pub fn with_allowed_origins(
158 mut self,
159 allowed_origins: impl IntoIterator<Item = impl Into<String>>,
160 ) -> Self {
161 self.allowed_origins = allowed_origins.into_iter().map(Into::into).collect();
162 self
163 }
164 pub fn disable_allowed_origins(mut self) -> Self {
166 self.allowed_origins.clear();
167 self
168 }
169 pub fn with_sse_keep_alive(mut self, duration: Option<Duration>) -> Self {
170 self.sse_keep_alive = duration;
171 self
172 }
173
174 pub fn with_sse_retry(mut self, duration: Option<Duration>) -> Self {
175 self.sse_retry = duration;
176 self
177 }
178
179 pub fn with_stateful_mode(mut self, stateful: bool) -> Self {
180 self.stateful_mode = stateful;
181 self
182 }
183
184 pub fn with_json_response(mut self, json_response: bool) -> Self {
185 self.json_response = json_response;
186 self
187 }
188
189 pub fn with_cancellation_token(mut self, token: CancellationToken) -> Self {
190 self.cancellation_token = token;
191 self
192 }
193
194 pub fn with_max_request_body_bytes(mut self, bytes: usize) -> Self {
196 self.max_request_body_bytes = bytes;
197 self
198 }
199}
200
201#[expect(
202 clippy::result_large_err,
203 reason = "BoxResponse is intentionally large; matches other handlers in this file"
204)]
205fn validate_protocol_version_header(
211 headers: &http::HeaderMap,
212 allow_unknown: bool,
213) -> Result<(), BoxResponse> {
214 if let Some(value) = headers.get(HEADER_MCP_PROTOCOL_VERSION) {
215 let version_str = value.to_str().map_err(|_| {
216 Response::builder()
217 .status(http::StatusCode::BAD_REQUEST)
218 .body(
219 Full::new(Bytes::from(
220 "Bad Request: Invalid MCP-Protocol-Version header encoding",
221 ))
222 .boxed(),
223 )
224 .expect("valid response")
225 })?;
226 let is_known = ProtocolVersion::KNOWN_VERSIONS
227 .iter()
228 .any(|v| v.as_str() == version_str);
229 if !allow_unknown && !is_known {
230 return Err(Response::builder()
231 .status(http::StatusCode::BAD_REQUEST)
232 .body(
233 Full::new(Bytes::from(format!(
234 "Bad Request: Unsupported MCP-Protocol-Version: {version_str}"
235 )))
236 .boxed(),
237 )
238 .expect("valid response"));
239 }
240 }
241 Ok(())
242}
243
244fn message_has_per_request_protocol_version(message: &ClientJsonRpcMessage) -> bool {
245 match message {
246 ClientJsonRpcMessage::Request(request) => {
247 request.request.get_meta().protocol_version().is_some()
248 }
249 _ => false,
250 }
251}
252
253fn invalid_request_jsonrpc_response(
254 id: Option<RequestId>,
255 message: impl Into<Cow<'static, str>>,
256) -> BoxResponse {
257 let err = JsonRpcError::new(id, ErrorData::invalid_request(message, None));
258 let body = serde_json::to_vec(&err).expect("serialize JsonRpcError");
259 Response::builder()
260 .status(http::StatusCode::BAD_REQUEST)
261 .header(http::header::CONTENT_TYPE, JSON_MIME_TYPE)
262 .body(Full::new(Bytes::from(body)).boxed())
263 .expect("valid response")
264}
265
266fn invalid_params_jsonrpc_response(
267 id: Option<RequestId>,
268 message: impl Into<Cow<'static, str>>,
269) -> BoxResponse {
270 let err = JsonRpcError::new(id, ErrorData::invalid_params(message, None));
271 let body = serde_json::to_vec(&err).expect("serialize JsonRpcError");
272 Response::builder()
273 .status(http::StatusCode::BAD_REQUEST)
274 .header(http::header::CONTENT_TYPE, JSON_MIME_TYPE)
275 .body(Full::new(Bytes::from(body)).boxed())
276 .expect("valid response")
277}
278
279#[expect(
280 clippy::result_large_err,
281 reason = "BoxResponse is intentionally large; matches other handlers in this file"
282)]
283fn validate_header_matches_init_body(
285 headers: &http::HeaderMap,
286 body_version: &str,
287 request_id: Option<RequestId>,
288) -> Result<(), BoxResponse> {
289 let Some(header_value) = headers.get(HEADER_MCP_PROTOCOL_VERSION) else {
290 return Ok(());
291 };
292 let header_str = header_value.to_str().map_err(|_| {
293 invalid_request_jsonrpc_response(
294 request_id.clone(),
295 "Invalid Request: MCP-Protocol-Version header is not valid UTF-8",
296 )
297 })?;
298 if header_str != body_version {
299 tracing::warn!(
300 header = header_str,
301 body = body_version,
302 "rejecting initialize: MCP-Protocol-Version header does not match params.protocolVersion"
303 );
304 return Err(invalid_request_jsonrpc_response(
305 request_id,
306 format!(
307 "Invalid Request: MCP-Protocol-Version header ({header_str}) does not match initialize params.protocolVersion ({body_version})"
308 ),
309 ));
310 }
311 Ok(())
312}
313
314#[expect(
315 clippy::result_large_err,
316 reason = "BoxResponse is intentionally large; matches other handlers in this file"
317)]
318fn validate_request_protocol_version_meta(
319 headers: &HeaderMap,
320 message: &ClientJsonRpcMessage,
321) -> Result<(), BoxResponse> {
322 let ClientJsonRpcMessage::Request(request) = message else {
323 return Ok(());
324 };
325 if matches!(&request.request, ClientRequest::InitializeRequest(_)) {
326 return Ok(());
327 }
328 let is_discover = matches!(&request.request, ClientRequest::DiscoverRequest(_));
329 let Some(meta_version) = request.request.get_meta().protocol_version() else {
330 if is_discover {
331 return Err(invalid_params_jsonrpc_response(
332 Some(request.id.clone()),
333 "Invalid params: server/discover requires protocolVersion in request _meta",
334 ));
335 }
336 return Ok(());
337 };
338 let Some(header_version) = headers
339 .get(HEADER_MCP_PROTOCOL_VERSION)
340 .and_then(|value| value.to_str().ok())
341 else {
342 return Err(invalid_request_jsonrpc_response(
343 Some(request.id.clone()),
344 "Invalid Request: request _meta protocolVersion requires MCP-Protocol-Version header",
345 ));
346 };
347 if header_version != meta_version.as_str() {
348 return Err(header_mismatch_jsonrpc_response(
349 Some(request.id.clone()),
350 format!(
351 "MCP-Protocol-Version header ({header_version}) does not match request _meta protocolVersion ({meta_version})"
352 ),
353 ));
354 }
355 Ok(())
356}
357
358fn jsonrpc_http_status(message: &ServerJsonRpcMessage) -> http::StatusCode {
359 let ServerJsonRpcMessage::Error(error) = message else {
360 return http::StatusCode::OK;
361 };
362 match error.error.code {
365 ErrorCode::UNSUPPORTED_PROTOCOL_VERSION
366 | ErrorCode::MISSING_REQUIRED_CLIENT_CAPABILITY
367 | ErrorCode::INVALID_PARAMS => http::StatusCode::BAD_REQUEST,
368 ErrorCode::METHOD_NOT_FOUND => http::StatusCode::NOT_FOUND,
369 _ => http::StatusCode::OK,
370 }
371}
372
373fn jsonrpc_message_response(
374 message: ServerJsonRpcMessage,
375 map_protocol_status: bool,
376) -> Result<BoxResponse, BoxResponse> {
377 let status = if map_protocol_status {
378 jsonrpc_http_status(&message)
379 } else {
380 http::StatusCode::OK
381 };
382 let body =
383 serde_json::to_vec(&message).map_err(internal_error_response("serialize json response"))?;
384 Ok(Response::builder()
385 .status(status)
386 .header(http::header::CONTENT_TYPE, JSON_MIME_TYPE)
387 .body(Full::new(Bytes::from(body)).boxed())
388 .expect("valid response"))
389}
390
391fn header_mismatch_jsonrpc_response(
392 id: Option<RequestId>,
393 message: impl Into<Cow<'static, str>>,
394) -> BoxResponse {
395 let err = JsonRpcError::new(id, ErrorData::header_mismatch(message, None));
396 let body = serde_json::to_vec(&err).expect("serialize JsonRpcError");
397 Response::builder()
398 .status(http::StatusCode::BAD_REQUEST)
399 .header(http::header::CONTENT_TYPE, JSON_MIME_TYPE)
400 .body(Full::new(Bytes::from(body)).boxed())
401 .expect("valid response")
402}
403
404#[expect(
411 clippy::result_large_err,
412 reason = "BoxResponse is intentionally large; matches other handlers in this file"
413)]
414fn validate_standard_headers(
415 headers: &HeaderMap,
416 message: &ClientJsonRpcMessage,
417 tool_schema: impl Fn(&str) -> Option<Arc<JsonObject>>,
418) -> Result<(), BoxResponse> {
419 let version_requires_headers = headers
420 .get(HEADER_MCP_PROTOCOL_VERSION)
421 .and_then(|value| value.to_str().ok())
422 .is_some_and(|version| version >= ProtocolVersion::STANDARD_HEADERS.as_str());
423 if !version_requires_headers {
424 return Ok(());
425 }
426
427 let request_id = match message {
428 ClientJsonRpcMessage::Request(req) => {
429 if matches!(&req.request, ClientRequest::InitializeRequest(_)) {
430 return Ok(());
431 }
432 Some(req.id.clone())
433 }
434 ClientJsonRpcMessage::Notification(_) => None,
435 _ => return Ok(()),
436 };
437
438 let Ok(value) = serde_json::to_value(message) else {
439 return Ok(());
440 };
441 let schema = value
443 .get("method")
444 .and_then(|method| method.as_str())
445 .filter(|method| *method == "tools/call")
446 .and_then(|_| value.get("params"))
447 .and_then(|params| params.get("name"))
448 .and_then(|name| name.as_str())
449 .and_then(tool_schema);
450 if let Err(reason) = mcp_headers::validate_request_headers(headers, &value, schema.as_deref()) {
451 return Err(header_mismatch_jsonrpc_response(request_id, reason));
452 }
453 Ok(())
454}
455
456fn forbidden_response(message: impl Into<String>) -> BoxResponse {
457 Response::builder()
458 .status(http::StatusCode::FORBIDDEN)
459 .body(Full::new(Bytes::from(message.into())).boxed())
460 .expect("valid response")
461}
462
463fn normalize_host(host: &str) -> String {
464 host.trim_matches('[')
465 .trim_matches(']')
466 .to_ascii_lowercase()
467}
468
469#[derive(Debug, Clone, PartialEq, Eq)]
470struct NormalizedAuthority {
471 host: String,
472 port: Option<u16>,
473}
474
475fn normalize_authority(host: &str, port: Option<u16>) -> NormalizedAuthority {
476 NormalizedAuthority {
477 host: normalize_host(host),
478 port,
479 }
480}
481
482fn parse_allowed_authority(allowed: &str) -> Option<NormalizedAuthority> {
483 let allowed = allowed.trim();
484 if allowed.is_empty() {
485 return None;
486 }
487
488 if let Ok(authority) = http::uri::Authority::try_from(allowed) {
489 return Some(normalize_authority(authority.host(), authority.port_u16()));
490 }
491
492 Some(normalize_authority(allowed, None))
493}
494
495fn host_is_allowed(host: &NormalizedAuthority, allowed_hosts: &[String]) -> bool {
496 if allowed_hosts.is_empty() {
497 return true;
499 }
500 allowed_hosts
501 .iter()
502 .filter_map(|allowed| parse_allowed_authority(allowed))
503 .any(|allowed| {
504 allowed.host == host.host
505 && match allowed.port {
506 Some(port) => host.port == Some(port),
507 None => true,
508 }
509 })
510}
511
512#[derive(Debug, Clone, PartialEq, Eq)]
513enum NormalizedOrigin {
514 Null,
515 Tuple {
516 scheme: String,
517 host: String,
518 port: Option<u16>,
519 },
520}
521
522fn parse_origin_value(value: &str) -> Option<NormalizedOrigin> {
523 let value = value.trim();
524 if value.is_empty() {
525 return None;
526 }
527 if value.eq_ignore_ascii_case("null") {
528 return Some(NormalizedOrigin::Null);
529 }
530 let uri = http::Uri::try_from(value).ok()?;
531 let scheme = uri.scheme_str()?.to_ascii_lowercase();
532 let authority = uri.authority()?;
533 Some(NormalizedOrigin::Tuple {
534 scheme,
535 host: normalize_host(authority.host()),
536 port: authority.port_u16(),
537 })
538}
539
540fn origin_is_allowed(origin: &NormalizedOrigin, allowed_origins: &[String]) -> bool {
541 if allowed_origins.is_empty() {
542 return true;
543 }
544 allowed_origins
545 .iter()
546 .filter_map(|raw| parse_origin_value(raw))
547 .any(|allowed| match (&allowed, origin) {
548 (NormalizedOrigin::Null, NormalizedOrigin::Null) => true,
549 (
550 NormalizedOrigin::Tuple {
551 scheme: a_scheme,
552 host: a_host,
553 port: a_port,
554 },
555 NormalizedOrigin::Tuple {
556 scheme: o_scheme,
557 host: o_host,
558 port: o_port,
559 },
560 ) => a_scheme == o_scheme && a_host == o_host && (a_port.is_none() || a_port == o_port),
561 _ => false,
562 })
563}
564
565fn bad_request_response(message: &str) -> BoxResponse {
566 let body = Full::from(message.to_string()).boxed();
567
568 http::Response::builder()
569 .status(http::StatusCode::BAD_REQUEST)
570 .header(http::header::CONTENT_TYPE, "text/plain; charset=utf-8")
571 .body(body)
572 .expect("failed to build bad request response")
573}
574
575fn parse_host_header(
576 uri: &http::Uri,
577 headers: &HeaderMap,
578) -> Result<NormalizedAuthority, BoxResponse> {
579 if let Some(host) = headers.get(http::header::HOST) {
580 let host_str = host
581 .to_str()
582 .inspect_err(|_| {
583 tracing::warn!(host = ?host, "rejected request with non-UTF-8 Host header");
584 })
585 .map_err(|_| bad_request_response("Bad Request: Invalid Host header encoding"))?;
586 let authority = http::uri::Authority::try_from(host_str)
587 .inspect_err(|_| {
588 tracing::warn!(
589 host = host_str,
590 "rejected request with malformed Host header"
591 );
592 })
593 .map_err(|_| bad_request_response("Bad Request: Invalid Host header"))?;
594 return Ok(normalize_authority(authority.host(), authority.port_u16()));
595 }
596 let authority = uri.authority().ok_or_else(|| {
599 tracing::warn!("rejected request with missing Host header and no :authority");
600 bad_request_response("Bad Request: missing Host header")
601 })?;
602 Ok(normalize_authority(authority.host(), authority.port_u16()))
603}
604
605fn validate_dns_rebinding_headers(
606 uri: &http::Uri,
607 headers: &HeaderMap,
608 config: &StreamableHttpServerConfig,
609) -> Result<(), BoxResponse> {
610 let host = parse_host_header(uri, headers)?;
611 if !host_is_allowed(&host, &config.allowed_hosts) {
612 tracing::warn!(
613 host = ?host,
614 "rejected request with disallowed Host header (possible DNS rebinding attempt)",
615 );
616 return Err(forbidden_response("Forbidden: Host header is not allowed"));
617 }
618 validate_origin_header(headers, &config.allowed_origins)?;
619 Ok(())
620}
621
622fn validate_origin_header(
623 headers: &HeaderMap,
624 allowed_origins: &[String],
625) -> Result<(), BoxResponse> {
626 if allowed_origins.is_empty() {
627 return Ok(());
628 }
629 let Some(origin_header) = headers.get(http::header::ORIGIN) else {
630 return Ok(());
631 };
632 let origin_str = origin_header
633 .to_str()
634 .inspect_err(|_| {
635 tracing::warn!(origin = ?origin_header, "rejected request with non-UTF-8 Origin header");
636 })
637 .map_err(|_| bad_request_response("Bad Request: Invalid Origin header encoding"))?;
638 let origin = parse_origin_value(origin_str).ok_or_else(|| {
639 tracing::warn!(
640 origin = origin_str,
641 "rejected request with malformed Origin header",
642 );
643 bad_request_response("Bad Request: Invalid Origin header")
644 })?;
645 if !origin_is_allowed(&origin, allowed_origins) {
646 tracing::warn!(
647 origin = ?origin,
648 "rejected request with disallowed Origin header (possible cross-origin attack)",
649 );
650 return Err(forbidden_response(
651 "Forbidden: Origin header is not allowed",
652 ));
653 }
654 Ok(())
655}
656
657pub struct StreamableHttpService<S, M> {
741 pub config: StreamableHttpServerConfig,
742 session_manager: Arc<M>,
743 service_factory: Arc<dyn Fn() -> Result<S, std::io::Error> + Send + Sync>,
744 pending_restores: Option<
749 Arc<tokio::sync::RwLock<HashMap<SessionId, tokio::sync::watch::Sender<Option<bool>>>>>,
750 >,
751 tool_schemas: Arc<std::sync::RwLock<HashMap<String, Option<Arc<JsonObject>>>>>,
755}
756
757impl<S, M> Clone for StreamableHttpService<S, M> {
758 fn clone(&self) -> Self {
759 Self {
760 config: self.config.clone(),
761 session_manager: self.session_manager.clone(),
762 service_factory: self.service_factory.clone(),
763 pending_restores: self.pending_restores.clone(),
764 tool_schemas: self.tool_schemas.clone(),
765 }
766 }
767}
768
769impl<RequestBody, S, M> tower_service::Service<Request<RequestBody>> for StreamableHttpService<S, M>
770where
771 RequestBody: Body + Send + 'static,
772 S: crate::ServerHandler + Send + 'static,
773 M: SessionManager,
774 RequestBody::Error: Display,
775 RequestBody::Data: Send + 'static,
776{
777 type Response = BoxResponse;
778 type Error = Infallible;
779 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
780 fn call(&mut self, req: http::Request<RequestBody>) -> Self::Future {
781 let service = self.clone();
782 Box::pin(async move {
783 let response = service.handle(req).await;
784 Ok(response)
785 })
786 }
787 fn poll_ready(
788 &mut self,
789 _cx: &mut std::task::Context<'_>,
790 ) -> std::task::Poll<Result<(), Self::Error>> {
791 std::task::Poll::Ready(Ok(()))
792 }
793}
794
795struct PendingRestoreGuard {
803 pending_restores:
804 Arc<tokio::sync::RwLock<HashMap<SessionId, tokio::sync::watch::Sender<Option<bool>>>>>,
805 session_id: SessionId,
806 watch_tx: tokio::sync::watch::Sender<Option<bool>>,
807 result: bool,
809}
810
811impl Drop for PendingRestoreGuard {
812 fn drop(&mut self) {
813 let _ = self.watch_tx.send(Some(self.result));
815 let pending_restores = self.pending_restores.clone();
817 let session_id = self.session_id.clone();
818 tokio::spawn(async move {
819 pending_restores.write().await.remove(&session_id);
820 });
821 }
822}
823
824impl<S, M> StreamableHttpService<S, M>
825where
826 S: crate::ServerHandler + Send + 'static,
827 M: SessionManager,
828{
829 pub fn new(
830 service_factory: impl Fn() -> Result<S, std::io::Error> + Send + Sync + 'static,
831 session_manager: Arc<M>,
832 config: StreamableHttpServerConfig,
833 ) -> Self {
834 let pending_restores = config.session_store.is_some().then(|| {
835 Arc::new(tokio::sync::RwLock::new(HashMap::<
836 SessionId,
837 tokio::sync::watch::Sender<Option<bool>>,
838 >::new()))
839 });
840 Self {
841 config,
842 session_manager,
843 service_factory: Arc::new(service_factory),
844 pending_restores,
845 tool_schemas: Arc::new(std::sync::RwLock::new(HashMap::new())),
846 }
847 }
848 fn get_service(&self) -> Result<S, std::io::Error> {
849 (self.service_factory)()
850 }
851
852 async fn serve_negotiated_request_directly(
854 &self,
855 service: S,
856 mut request: crate::model::JsonRpcRequest<ClientRequest>,
857 parts: http::request::Parts,
858 ) -> Result<BoxResponse, BoxResponse> {
859 let peer_info = Self::peer_info_for_stateless_request(&request, &parts.headers);
860 request.request.extensions_mut().insert(parts);
861 let (transport, mut receiver) =
862 OneshotTransport::<RoleServer>::new(ClientJsonRpcMessage::Request(request));
863 let request_ct = CancellationToken::new();
867 let service = serve_directly_with_ct(service, transport, peer_info, request_ct.clone());
868 tokio::spawn(async move {
869 let _ = service.waiting().await;
870 });
871
872 let cancel = self.config.cancellation_token.child_token();
873 let mut disconnect_guard = Some(request_ct.clone().drop_guard());
878 let first = tokio::select! {
879 message = receiver.recv() => {
880 if let Some(guard) = disconnect_guard.take() {
881 guard.disarm();
882 }
883 message
884 }
885 _ = cancel.cancelled() => None,
886 }
887 .ok_or_else(|| {
888 internal_error_response("empty response")(std::io::Error::new(
889 std::io::ErrorKind::UnexpectedEof,
890 "no response message received from handler",
891 ))
892 })?;
893
894 if self.config.json_response || jsonrpc_http_status(&first) != http::StatusCode::OK {
895 request_ct.cancel();
902 return jsonrpc_message_response(first, true);
903 }
904
905 let stream = futures::stream::once(async move { first })
908 .chain(ReceiverStream::new(receiver))
909 .map(|message| {
910 tracing::trace!(?message);
911 ServerSseMessage::from_message(message)
912 });
913 Ok(sse_stream_response(
914 CancelOnDisconnect::new(stream, request_ct),
915 self.config.sse_keep_alive,
916 self.config.cancellation_token.child_token(),
917 ))
918 }
919
920 fn tool_schema(&self, name: &str) -> Option<Arc<JsonObject>> {
924 if let Ok(cache) = self.tool_schemas.read() {
925 if let Some(schema) = cache.get(name) {
926 return schema.clone();
927 }
928 }
929 let schema = self
930 .get_service()
931 .ok()
932 .and_then(|service| service.get_tool(name))
933 .map(|tool| tool.input_schema);
934 if let Ok(mut cache) = self.tool_schemas.write() {
935 cache.insert(name.to_owned(), schema.clone());
936 }
937 schema
938 }
939
940 fn spawn_session_worker(
948 session_manager: Arc<M>,
949 session_id: SessionId,
950 service: S,
951 transport: M::Transport,
952 init_done_tx: Option<tokio::sync::oneshot::Sender<()>>,
953 ) where
954 S: crate::ServerHandler + Send + 'static,
955 M: SessionManager,
956 {
957 tokio::spawn(async move {
958 let svc =
959 serve_server::<S, M::Transport, _, TransportAdapterIdentity>(service, transport)
960 .await;
961 match svc {
962 Ok(svc) => {
963 if let Some(tx) = init_done_tx {
964 let _ = tx.send(());
965 }
966 let _ = svc.waiting().await;
967 }
968 Err(e) => {
969 tracing::error!("Failed to serve session: {e}");
970 }
972 }
973 let _ = session_manager
974 .close_session(&session_id)
975 .await
976 .inspect_err(|e| {
977 tracing::error!("Failed to close session {session_id}: {e}");
978 });
979 });
980 }
981
982 async fn try_restore_from_store(
992 &self,
993 session_id: &SessionId,
994 parts: &http::request::Parts,
995 ) -> Result<bool, std::io::Error>
996 where
997 S: crate::ServerHandler + Send + 'static,
998 M: SessionManager,
999 {
1000 let (Some(pending_restores), Some(store)) =
1002 (&self.pending_restores, &self.config.session_store)
1003 else {
1004 return Ok(false);
1005 };
1006
1007 let (watch_tx, _watch_rx) = tokio::sync::watch::channel(None::<bool>);
1012 {
1013 let mut pending = pending_restores.write().await;
1014 if let Some(tx) = pending.get(session_id) {
1015 let mut rx = tx.subscribe();
1016 drop(pending);
1017 let result = rx
1019 .wait_for(|r| r.is_some())
1020 .await
1021 .map(|r| r.unwrap_or(false))
1022 .unwrap_or(false);
1023 return Ok(result);
1024 }
1025 pending.insert(session_id.clone(), watch_tx.clone());
1026 }
1027
1028 let mut guard = PendingRestoreGuard {
1030 pending_restores: pending_restores.clone(),
1031 session_id: session_id.clone(),
1032 watch_tx: watch_tx.clone(),
1033 result: false,
1034 };
1035
1036 let state = match store.load(session_id.as_ref()).await {
1038 Ok(Some(s)) => s,
1039 Ok(None) => {
1040 return Ok(false);
1041 }
1042 Err(e) => {
1043 tracing::error!(
1044 session_id = session_id.as_ref(),
1045 error = %e,
1046 "session store load failed during restore"
1047 );
1048 return Err(std::io::Error::other(e));
1049 }
1050 };
1051
1052 let transport = match self
1054 .session_manager
1055 .restore_session(session_id.clone())
1056 .await
1057 .map_err(|e| std::io::Error::other(e.to_string()))
1058 {
1059 Ok(RestoreOutcome::Restored(t)) => t,
1060 Ok(RestoreOutcome::AlreadyPresent) => {
1061 return Err(std::io::Error::other(
1064 "restore_session returned AlreadyPresent unexpectedly; session manager might have modified the session store outside of the restore_session API",
1065 ));
1066 }
1067 Ok(RestoreOutcome::NotSupported) => {
1068 return Ok(false);
1069 }
1070 Err(e) => {
1071 return Err(e);
1072 }
1073 };
1074
1075 let service = match self.get_service() {
1077 Ok(s) => s,
1078 Err(e) => {
1079 return Err(e);
1080 }
1081 };
1082
1083 let mut restore_init = ClientJsonRpcMessage::request(
1087 ClientRequest::InitializeRequest(InitializeRequest {
1088 params: state.initialize_params,
1089 ..Default::default()
1090 }),
1091 crate::model::NumberOrString::Number(0),
1092 );
1093 restore_init.insert_extension(parts.clone());
1094 restore_init.insert_extension(SessionRestoreMarker {
1095 id: session_id.clone(),
1096 });
1097 let mut restore_initialized = ClientJsonRpcMessage::notification(
1098 ClientNotification::InitializedNotification(InitializedNotification {
1099 ..Default::default()
1100 }),
1101 );
1102 restore_initialized.insert_extension(parts.clone());
1103 restore_initialized.insert_extension(SessionRestoreMarker {
1104 id: session_id.clone(),
1105 });
1106 let (init_done_tx, init_done_rx) = tokio::sync::oneshot::channel::<()>();
1108
1109 Self::spawn_session_worker(
1110 self.session_manager.clone(),
1111 session_id.clone(),
1112 service,
1113 transport,
1114 Some(init_done_tx),
1115 );
1116
1117 if let Err(e) = self
1118 .session_manager
1119 .initialize_session(session_id, restore_init)
1120 .await
1121 .map_err(|e| std::io::Error::other(e.to_string()))
1122 {
1123 return Err(e);
1124 }
1125
1126 if let Err(e) = self
1127 .session_manager
1128 .accept_message(session_id, restore_initialized)
1129 .await
1130 .map_err(|e| std::io::Error::other(e.to_string()))
1131 {
1132 return Err(e);
1133 }
1134
1135 if init_done_rx.await.is_err() {
1136 return Err(std::io::Error::other(
1137 "serve_server initialization failed during restore",
1138 ));
1139 }
1140
1141 guard.result = true;
1143
1144 tracing::debug!(
1145 session_id = session_id.as_ref(),
1146 "session restored from external store"
1147 );
1148 Ok(true)
1149 }
1150 pub async fn handle<B>(&self, request: Request<B>) -> Response<BoxBody<Bytes, Infallible>>
1151 where
1152 B: Body + Send + 'static,
1153 B::Error: Display,
1154 {
1155 if let Err(response) =
1156 validate_dns_rebinding_headers(request.uri(), request.headers(), &self.config)
1157 {
1158 return response;
1159 }
1160 let method = request.method().clone();
1161 let allowed_methods = match self.config.stateful_mode {
1162 true => "GET, POST, DELETE",
1163 false => "POST",
1164 };
1165 let result = match (method, self.config.stateful_mode) {
1166 (Method::POST, _) => self.handle_post(request).await,
1167 (Method::GET, true) => self.handle_get(request).await,
1169 (Method::DELETE, true) => self.handle_delete(request).await,
1170 _ => {
1171 let response = Response::builder()
1173 .status(http::StatusCode::METHOD_NOT_ALLOWED)
1174 .header(ALLOW, allowed_methods)
1175 .body(Full::new(Bytes::from("Method Not Allowed")).boxed())
1176 .expect("valid response");
1177 return response;
1178 }
1179 };
1180 match result {
1181 Ok(response) => response,
1182 Err(response) => response,
1183 }
1184 }
1185 async fn handle_get<B>(&self, request: Request<B>) -> Result<BoxResponse, BoxResponse>
1186 where
1187 B: Body + Send + 'static,
1188 B::Error: Display,
1189 {
1190 if !request
1192 .headers()
1193 .get(http::header::ACCEPT)
1194 .and_then(|header| header.to_str().ok())
1195 .is_some_and(|header| header.contains(EVENT_STREAM_MIME_TYPE))
1196 {
1197 return Ok(Response::builder()
1198 .status(http::StatusCode::NOT_ACCEPTABLE)
1199 .body(
1200 Full::new(Bytes::from(
1201 "Not Acceptable: Client must accept text/event-stream",
1202 ))
1203 .boxed(),
1204 )
1205 .expect("valid response"));
1206 }
1207 let session_id = request
1209 .headers()
1210 .get(HEADER_SESSION_ID)
1211 .and_then(|v| v.to_str().ok())
1212 .map(|s| s.to_owned().into());
1213 let Some(session_id) = session_id else {
1214 return Ok(Response::builder()
1216 .status(http::StatusCode::BAD_REQUEST)
1217 .body(Full::new(Bytes::from("Bad Request: Session ID is required")).boxed())
1218 .expect("valid response"));
1219 };
1220 let has_session = self
1222 .session_manager
1223 .has_session(&session_id)
1224 .await
1225 .map_err(internal_error_response("check session"))?;
1226 let (parts, _) = request.into_parts();
1227 if !has_session {
1228 let restored = self
1230 .try_restore_from_store(&session_id, &parts)
1231 .await
1232 .map_err(internal_error_response("restore session"))?;
1233 if !restored {
1234 return Ok(Response::builder()
1236 .status(http::StatusCode::NOT_FOUND)
1237 .body(Full::new(Bytes::from("Not Found: Session not found")).boxed())
1238 .expect("valid response"));
1239 }
1240 }
1241 validate_protocol_version_header(&parts.headers, false)?;
1243 let last_event_id = parts
1245 .headers
1246 .get(HEADER_LAST_EVENT_ID)
1247 .and_then(|v| v.to_str().ok())
1248 .map(|s| s.to_owned());
1249 if let Some(last_event_id) = last_event_id {
1250 match self
1251 .session_manager
1252 .resume(&session_id, last_event_id)
1253 .await
1254 {
1255 Ok(stream) => {
1256 return Ok(sse_stream_response(
1257 stream,
1258 self.config.sse_keep_alive,
1259 self.config.cancellation_token.child_token(),
1260 ));
1261 }
1262 Err(e) => {
1263 tracing::warn!("Resume failed ({e}), returning empty stream");
1269 return Ok(sse_stream_response(
1270 futures::stream::empty(),
1271 None,
1272 self.config.cancellation_token.child_token(),
1273 ));
1274 }
1275 }
1276 }
1277 let stream = self
1279 .session_manager
1280 .create_standalone_stream(&session_id)
1281 .await
1282 .map_err(internal_error_response("create standalone stream"))?;
1283 let stream = if let Some(retry) = self.config.sse_retry {
1284 let priming = ServerSseMessage::priming("0", retry);
1285 futures::stream::once(async move { priming })
1286 .chain(stream)
1287 .left_stream()
1288 } else {
1289 stream.right_stream()
1290 };
1291 Ok(sse_stream_response(
1292 stream,
1293 self.config.sse_keep_alive,
1294 self.config.cancellation_token.child_token(),
1295 ))
1296 }
1297
1298 async fn handle_post<B>(&self, request: Request<B>) -> Result<BoxResponse, BoxResponse>
1299 where
1300 B: Body + Send + 'static,
1301 B::Error: Display,
1302 {
1303 if !request
1305 .headers()
1306 .get(http::header::ACCEPT)
1307 .and_then(|header| header.to_str().ok())
1308 .is_some_and(|header| {
1309 header.contains(JSON_MIME_TYPE) && header.contains(EVENT_STREAM_MIME_TYPE)
1310 })
1311 {
1312 return Ok(Response::builder()
1313 .status(http::StatusCode::NOT_ACCEPTABLE)
1314 .body(Full::new(Bytes::from("Not Acceptable: Client must accept both application/json and text/event-stream")).boxed())
1315 .expect("valid response"));
1316 }
1317
1318 if !request
1320 .headers()
1321 .get(http::header::CONTENT_TYPE)
1322 .and_then(|header| header.to_str().ok())
1323 .is_some_and(|header| header.starts_with(JSON_MIME_TYPE))
1324 {
1325 return Ok(Response::builder()
1326 .status(http::StatusCode::UNSUPPORTED_MEDIA_TYPE)
1327 .body(
1328 Full::new(Bytes::from(
1329 "Unsupported Media Type: Content-Type must be application/json",
1330 ))
1331 .boxed(),
1332 )
1333 .expect("valid response"));
1334 }
1335
1336 let (part, body) = request.into_parts();
1338 let mut message = match expect_json(body, self.config.max_request_body_bytes).await {
1339 Ok(message) => message,
1340 Err(response) => return Ok(response),
1341 };
1342
1343 if self.config.stateful_mode {
1344 let session_id = part
1346 .headers
1347 .get(HEADER_SESSION_ID)
1348 .and_then(|v| v.to_str().ok());
1349 if let Some(session_id) = session_id {
1350 let session_id = session_id.to_owned().into();
1351 let has_session = self
1352 .session_manager
1353 .has_session(&session_id)
1354 .await
1355 .map_err(internal_error_response("check session"))?;
1356 if !has_session {
1357 let restored = self
1359 .try_restore_from_store(&session_id, &part)
1360 .await
1361 .map_err(internal_error_response("restore session"))?;
1362 if !restored {
1363 return Ok(Response::builder()
1365 .status(http::StatusCode::NOT_FOUND)
1366 .body(Full::new(Bytes::from("Not Found: Session not found")).boxed())
1367 .expect("valid response"));
1368 }
1369 }
1370
1371 let has_per_request_version = message_has_per_request_protocol_version(&message);
1373 validate_protocol_version_header(&part.headers, has_per_request_version)?;
1374 validate_request_protocol_version_meta(&part.headers, &message)?;
1375 validate_standard_headers(&part.headers, &message, |name| self.tool_schema(name))?;
1377
1378 match &mut message {
1380 ClientJsonRpcMessage::Request(req) => {
1381 req.request.extensions_mut().insert(part);
1382 }
1383 ClientJsonRpcMessage::Notification(not) => {
1384 not.notification.extensions_mut().insert(part);
1385 }
1386 _ => {
1387 }
1389 }
1390
1391 match message {
1392 ClientJsonRpcMessage::Request(_) => {
1393 let stream = self
1397 .session_manager
1398 .create_stream(&session_id, message)
1399 .await
1400 .map_err(internal_error_response("get session"))?;
1401 Ok(sse_stream_response(
1402 stream,
1403 self.config.sse_keep_alive,
1404 self.config.cancellation_token.child_token(),
1405 ))
1406 }
1407 ClientJsonRpcMessage::Notification(_)
1408 | ClientJsonRpcMessage::Response(_)
1409 | ClientJsonRpcMessage::Error(_) => {
1410 self.session_manager
1412 .accept_message(&session_id, message)
1413 .await
1414 .map_err(internal_error_response("accept message"))?;
1415 Ok(accepted_response())
1416 }
1417 }
1418 } else {
1419 if matches!(
1420 &message,
1421 ClientJsonRpcMessage::Request(request)
1422 if matches!(&request.request, ClientRequest::DiscoverRequest(_))
1423 ) {
1424 validate_protocol_version_header(
1425 &part.headers,
1426 message_has_per_request_protocol_version(&message),
1427 )?;
1428 validate_standard_headers(&part.headers, &message, |name| {
1429 self.tool_schema(name)
1430 })?;
1431 validate_request_protocol_version_meta(&part.headers, &message)?;
1432 let ClientJsonRpcMessage::Request(request) = message else {
1433 unreachable!("guarded as a request above");
1434 };
1435 let service = self
1436 .get_service()
1437 .map_err(internal_error_response("get service"))?;
1438 return self
1439 .serve_negotiated_request_directly(service, request, part)
1440 .await;
1441 }
1442 let stored_init_params = match &mut message {
1445 ClientJsonRpcMessage::Request(req) => {
1446 let ClientRequest::InitializeRequest(init_req) = &req.request else {
1447 return Err(unexpected_message_response("initialize request"));
1448 };
1449 validate_header_matches_init_body(
1451 &part.headers,
1452 init_req.params.protocol_version.as_str(),
1453 Some(req.id.clone()),
1454 )?;
1455 let stored_init_params = self
1456 .config
1457 .session_store
1458 .as_ref()
1459 .map(|_| init_req.params.clone());
1460 req.request.extensions_mut().insert(part);
1462 stored_init_params
1463 }
1464 _ => {
1465 return Err(unexpected_message_response("initialize request"));
1466 }
1467 };
1468 let service = self
1469 .get_service()
1470 .map_err(internal_error_response("get service"))?;
1471 let (session_id, transport) = self
1472 .session_manager
1473 .create_session()
1474 .await
1475 .map_err(internal_error_response("create session"))?;
1476 Self::spawn_session_worker(
1478 self.session_manager.clone(),
1479 session_id.clone(),
1480 service,
1481 transport,
1482 None,
1483 );
1484 let response = self
1486 .session_manager
1487 .initialize_session(&session_id, message)
1488 .await
1489 .map_err(internal_error_response("create stream"))?;
1490 if let (Some(store), Some(params)) =
1492 (&self.config.session_store, stored_init_params)
1493 {
1494 let state = SessionState {
1495 initialize_params: params,
1496 };
1497 let _ = store
1498 .store(session_id.as_ref(), &state)
1499 .await
1500 .inspect_err(|e| {
1501 tracing::warn!(
1502 "Failed to persist session {} to store: {e}",
1503 session_id
1504 );
1505 });
1506 }
1507 let stream =
1508 futures::stream::once(async move { ServerSseMessage::from_message(response) });
1509 let stream = if let Some(retry) = self.config.sse_retry {
1511 let priming = ServerSseMessage::priming("0", retry);
1512 futures::stream::once(async move { priming })
1513 .chain(stream)
1514 .left_stream()
1515 } else {
1516 stream.right_stream()
1517 };
1518 let mut response = sse_stream_response(
1519 stream,
1520 self.config.sse_keep_alive,
1521 self.config.cancellation_token.child_token(),
1522 );
1523
1524 response.headers_mut().insert(
1525 HEADER_SESSION_ID,
1526 session_id
1527 .parse()
1528 .map_err(internal_error_response("create session id header"))?,
1529 );
1530 Ok(response)
1531 }
1532 } else {
1533 let has_per_request_version = message_has_per_request_protocol_version(&message);
1537 match &message {
1538 ClientJsonRpcMessage::Request(req) => {
1539 if let ClientRequest::InitializeRequest(init_req) = &req.request {
1540 validate_header_matches_init_body(
1541 &part.headers,
1542 init_req.params.protocol_version.as_str(),
1543 Some(req.id.clone()),
1544 )?;
1545 } else {
1546 validate_protocol_version_header(&part.headers, has_per_request_version)?;
1547 }
1548 }
1549 _ => {
1550 validate_protocol_version_header(&part.headers, has_per_request_version)?;
1551 }
1552 }
1553 validate_standard_headers(&part.headers, &message, |name| self.tool_schema(name))?;
1555 validate_request_protocol_version_meta(&part.headers, &message)?;
1556 let service = self
1557 .get_service()
1558 .map_err(internal_error_response("get service"))?;
1559 match message {
1560 ClientJsonRpcMessage::Request(mut request) => {
1561 let negotiates_per_request = has_per_request_version
1562 || matches!(&request.request, ClientRequest::DiscoverRequest(_));
1563 if negotiates_per_request {
1564 return self
1565 .serve_negotiated_request_directly(service, request, part)
1566 .await;
1567 }
1568 let peer_info = Self::peer_info_for_stateless_request(&request, &part.headers);
1575 request.request.extensions_mut().insert(part);
1576 let (transport, mut receiver) =
1577 OneshotTransport::<RoleServer>::new(ClientJsonRpcMessage::Request(request));
1578 let request_ct = CancellationToken::new();
1583 let service =
1584 serve_directly_with_ct(service, transport, peer_info, request_ct.clone());
1585 tokio::spawn(async move {
1586 let _ = service.waiting().await;
1588 });
1589 if self.config.json_response {
1590 let cancel = self.config.cancellation_token.child_token();
1594 let mut disconnect_guard = Some(request_ct.clone().drop_guard());
1600 let Some(message) = (tokio::select! {
1601 res = receiver.recv() => {
1602 if let Some(guard) = disconnect_guard.take() {
1603 guard.disarm();
1604 }
1605 res
1606 }
1607 _ = cancel.cancelled() => None,
1608 }) else {
1609 return Err(internal_error_response("empty response")(
1610 std::io::Error::new(
1611 std::io::ErrorKind::UnexpectedEof,
1612 "no response message received from handler",
1613 ),
1614 ));
1615 };
1616 tracing::trace!(?message);
1617 if matches!(
1618 message,
1619 ServerJsonRpcMessage::Response(_) | ServerJsonRpcMessage::Error(_)
1620 ) {
1621 let body = serde_json::to_vec(&message).map_err(|e| {
1622 internal_error_response("serialize json response")(e)
1623 })?;
1624 Ok(Response::builder()
1625 .status(http::StatusCode::OK)
1626 .header(http::header::CONTENT_TYPE, JSON_MIME_TYPE)
1627 .body(Full::new(Bytes::from(body)).boxed())
1628 .expect("valid response"))
1629 } else {
1630 let first = futures::stream::once(async move {
1634 ServerSseMessage::from_message(message)
1635 });
1636 let remaining = ReceiverStream::new(receiver).map(|message| {
1637 tracing::trace!(?message);
1638 ServerSseMessage::from_message(message)
1639 });
1640 Ok(sse_stream_response(
1641 CancelOnDisconnect::new(first.chain(remaining), request_ct),
1642 self.config.sse_keep_alive,
1643 self.config.cancellation_token.child_token(),
1644 ))
1645 }
1646 } else {
1647 let stream = ReceiverStream::new(receiver).map(|message| {
1650 tracing::trace!(?message);
1651 ServerSseMessage::from_message(message)
1652 });
1653 let stream = CancelOnDisconnect::new(stream, request_ct);
1654 Ok(sse_stream_response(
1655 stream,
1656 self.config.sse_keep_alive,
1657 self.config.cancellation_token.child_token(),
1658 ))
1659 }
1660 }
1661 ClientJsonRpcMessage::Notification(_notification) => {
1662 Ok(accepted_response())
1664 }
1665 ClientJsonRpcMessage::Response(_json_rpc_response) => Ok(accepted_response()),
1666 ClientJsonRpcMessage::Error(_json_rpc_error) => Ok(accepted_response()),
1667 }
1668 }
1669 }
1670
1671 async fn handle_delete<B>(&self, request: Request<B>) -> Result<BoxResponse, BoxResponse>
1672 where
1673 B: Body + Send + 'static,
1674 B::Error: Display,
1675 {
1676 let session_id = request
1678 .headers()
1679 .get(HEADER_SESSION_ID)
1680 .and_then(|v| v.to_str().ok())
1681 .map(|s| s.to_owned().into());
1682 let Some(session_id) = session_id else {
1683 return Ok(Response::builder()
1685 .status(http::StatusCode::BAD_REQUEST)
1686 .body(Full::new(Bytes::from("Bad Request: Session ID is required")).boxed())
1687 .expect("valid response"));
1688 };
1689 validate_protocol_version_header(request.headers(), false)?;
1691 self.session_manager
1693 .close_session(&session_id)
1694 .await
1695 .map_err(internal_error_response("close session"))?;
1696 if let Some(store) = &self.config.session_store {
1699 let _ = store.delete(session_id.as_ref()).await.inspect_err(|e| {
1700 tracing::warn!("Failed to delete session {} from store: {e}", session_id);
1701 });
1702 }
1703 Ok(accepted_response())
1704 }
1705
1706 fn peer_info_for_stateless_request(
1716 request: &crate::model::JsonRpcRequest<ClientRequest>,
1717 headers: &HeaderMap,
1718 ) -> Option<InitializeRequestParams> {
1719 let version = if let ClientRequest::InitializeRequest(ref init) = request.request {
1720 init.params.protocol_version.clone()
1721 } else {
1722 headers
1723 .get(HEADER_MCP_PROTOCOL_VERSION)
1724 .and_then(|v| v.to_str().ok())
1725 .and_then(|s| serde_json::from_value(serde_json::Value::String(s.to_owned())).ok())
1726 .unwrap_or(ProtocolVersion::V_2025_03_26)
1727 };
1728 Some(InitializeRequestParams {
1729 meta: None,
1730 protocol_version: version,
1731 capabilities: ClientCapabilities::default(),
1732 client_info: Implementation::default(),
1733 })
1734 }
1735}
1736
1737pin_project! {
1738 struct CancelOnDisconnect<S> {
1749 #[pin]
1750 inner: S,
1751 ct: Option<CancellationToken>,
1752 }
1753 impl<S> PinnedDrop for CancelOnDisconnect<S> {
1754 fn drop(this: Pin<&mut Self>) {
1755 let this = this.project();
1756 if let Some(ct) = this.ct.take() {
1757 ct.cancel();
1758 }
1759 }
1760 }
1761}
1762
1763impl<S> CancelOnDisconnect<S> {
1764 fn new(inner: S, ct: CancellationToken) -> Self {
1765 Self {
1766 inner,
1767 ct: Some(ct),
1768 }
1769 }
1770}
1771
1772impl<S: Stream> Stream for CancelOnDisconnect<S> {
1773 type Item = S::Item;
1774
1775 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1776 let this = self.project();
1777 let polled = this.inner.poll_next(cx);
1778 if let Poll::Ready(None) = &polled {
1779 *this.ct = None;
1781 }
1782 polled
1783 }
1784}