1use std::{fmt::Debug, future::Future, hash::Hash};
2
3#[cfg(feature = "unstable_protocol_v2")]
4use futures::{StreamExt as _, future};
5#[cfg(feature = "unstable_protocol_v2")]
6use serde::{Serialize, de::DeserializeOwned};
7
8use crate::Raw;
9#[cfg(feature = "unstable_protocol_v2")]
10use crate::RawJsonRpcId;
11use crate::jsonrpc::{Builder, handlers::NullHandler, run::NullRun};
12#[cfg(feature = "unstable_protocol_v2")]
13use crate::jsonrpc::{TransportFrame, raw_is_response_only_shape};
14use crate::role::{HasPeer, RemoteStyle};
15use crate::util::MatchDispatchFrom;
16#[cfg(feature = "unstable_protocol_v2")]
17use crate::v1::schema::InitializeRequest;
18use crate::v1::schema::PROXY_METHOD_NAMES;
19use crate::v1::schema::SessionId;
20#[cfg(feature = "unstable_protocol_v2")]
21use crate::{
22 Channel, RawJsonRpcError, RawJsonRpcMessage, RawJsonRpcParams,
23 RawJsonRpcResponse as RpcResponse,
24};
25#[cfg(feature = "unstable_protocol_v2")]
26use crate::{ConnectTo, DynConnectTo};
27use crate::{ConnectionTo, Dispatch, HandleDispatchFrom, Handled, Role, RoleId};
28#[cfg(feature = "unstable_protocol_v2")]
29use crate::{schema::ProtocolVersion, v2::schema as v2};
30
31#[derive(serde::Deserialize)]
32struct NewSessionResponseEnvelope {
33 #[serde(rename = "sessionId")]
34 session_id: SessionId,
35}
36
37#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct Client;
42
43impl Role for Client {
44 type Counterpart = Agent;
45
46 fn default_handle_dispatch_from(
53 &self,
54 message: Dispatch,
55 connection: ConnectionTo<Client>,
56 ) -> impl Future<Output = Result<Handled<Dispatch>, crate::Error>> + Send {
57 #[cfg(feature = "unstable_protocol_v2")]
58 let message = match message {
59 Dispatch::Request(request, responder)
60 if connection.protocol_version() == Some(ProtocolVersion::V2) =>
61 {
62 match unhandled_v2_entity_cancel(&request) {
63 Some(error) => {
64 return std::future::ready(
65 responder.respond_with_error(error).map(|()| Handled::Yes),
66 );
67 }
68 None => Dispatch::Request(request, responder),
69 }
70 }
71 message => message,
72 };
73 #[cfg(not(feature = "unstable_protocol_v2"))]
74 drop(connection);
75 std::future::ready(Ok(Handled::No {
76 message,
77 retry: false,
78 }))
79 }
80
81 fn role_id(&self) -> RoleId {
82 RoleId::from_singleton(self)
83 }
84
85 fn counterpart(&self) -> Self::Counterpart {
86 Agent
87 }
88}
89
90impl Client {
91 pub fn v1(self) -> crate::v1::Builder<Client> {
98 Builder::new(self).v1_client()
99 }
100
101 #[cfg(feature = "unstable_protocol_v2")]
115 pub fn v2(self) -> crate::v2::Builder<Client> {
116 Builder::new(self).v2_client()
117 }
118
119 #[cfg(feature = "unstable_protocol_v2")]
132 #[must_use]
133 pub fn protocol_connector(self) -> ClientProtocolConnector {
134 ClientProtocolConnector::new()
135 }
136}
137
138#[cfg(feature = "unstable_protocol_v2")]
149#[derive(Debug, Default)]
150pub struct ClientProtocolConnector {
151 v1: Option<DynConnectToFactory<Agent>>,
152 v2: Option<DynConnectToFactory<Agent>>,
153}
154
155#[cfg(feature = "unstable_protocol_v2")]
156impl ClientProtocolConnector {
157 #[must_use]
159 pub fn new() -> Self {
160 Self::default()
161 }
162
163 #[must_use]
165 pub fn with_v1<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
166 where
167 C: ConnectTo<Agent>,
168 {
169 self.v1 = Some(DynConnectToFactory::new(client));
170 self
171 }
172
173 #[must_use]
175 pub fn with_v2<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
176 where
177 C: ConnectTo<Agent>,
178 {
179 self.v2 = Some(DynConnectToFactory::new(client));
180 self
181 }
182
183 pub async fn connect_to<C>(
186 mut self,
187 mut agent: impl FnMut() -> C + Send + 'static,
188 ) -> Result<(), crate::Error>
189 where
190 C: ConnectTo<Client>,
191 {
192 let supported = SupportedClientProtocols {
193 v1: self.v1.is_some(),
194 v2: self.v2.is_some(),
195 };
196 let Some(selected) = supported.highest_configured() else {
197 return Err(crate::Error::invalid_request()
198 .data("client protocol connector has no configured ACP protocol implementations"));
199 };
200
201 match selected {
202 ClientProtocol::V1 => {
203 let client = self
204 .v1
205 .as_mut()
206 .expect("selected protocol is configured")
207 .create();
208 connect_client_protocol(ClientProtocol::V1, client, agent()).await
209 }
210 ClientProtocol::V2 => {
211 let client = self
212 .v2
213 .as_mut()
214 .expect("selected protocol is configured")
215 .create();
216 let agent_connection = RunningProtocolPeer::new(agent());
217 let (client, initialize) =
218 start_client_protocol(ClientProtocol::V2, client).await?;
219 let v2_initialize_as_v1 = normalize_v2_initialize_params_for_reuse(&initialize);
223 let (client, agent_connection, initialize_response) =
224 send_initialize_and_receive(client, agent_connection, initialize).await?;
225
226 if initialize_response_negotiated_v1(&initialize_response)
227 && let Some(v1) = self.v1.as_mut()
228 {
229 let fallback_client = v1.create();
230 let (fallback_client, fallback_initialize) =
231 start_client_protocol(ClientProtocol::V1, fallback_client).await?;
232 let v1_initialize =
233 validated_initialize_params::<InitializeRequest>(&fallback_initialize)?;
234
235 if v2_initialize_as_v1
236 .as_ref()
237 .is_ok_and(|v2_initialize| v2_initialize == &v1_initialize)
238 {
239 let fallback_response = initialize_response.with_id(
240 initialize_request_id(&fallback_initialize)
241 .expect("validated initialize request has an id"),
242 );
243 drop(client);
248 fallback_client.send(fallback_response)?;
249 return pipe_protocol_peers_until_done(fallback_client, agent_connection)
250 .await;
251 }
252
253 drop((
257 client,
258 fallback_client,
259 agent_connection,
260 initialize_response,
261 ));
262 return connect_client_protocol(ClientProtocol::V1, v1.create(), agent()).await;
263 }
264
265 client.send(initialize_response.into_message())?;
266 pipe_protocol_peers_until_done(client, agent_connection).await
267 }
268 }
269 }
270}
271
272#[cfg(feature = "unstable_protocol_v2")]
273struct DynConnectToFactory<R: Role> {
274 inner: Box<dyn FnMut() -> DynConnectTo<R> + Send>,
275}
276
277#[cfg(feature = "unstable_protocol_v2")]
278impl<R: Role> DynConnectToFactory<R> {
279 fn new<C>(mut factory: impl FnMut() -> C + Send + 'static) -> Self
280 where
281 C: ConnectTo<R>,
282 {
283 Self {
284 inner: Box::new(move || DynConnectTo::new(factory())),
285 }
286 }
287
288 fn create(&mut self) -> DynConnectTo<R> {
289 (self.inner)()
290 }
291}
292
293#[cfg(feature = "unstable_protocol_v2")]
294impl<R: Role> Debug for DynConnectToFactory<R> {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 f.debug_struct("DynConnectToFactory")
297 .finish_non_exhaustive()
298 }
299}
300
301impl HasPeer<Client> for Client {
302 fn remote_style(&self, _peer: Client) -> RemoteStyle {
303 RemoteStyle::Counterpart
304 }
305}
306
307#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
312pub struct Agent;
313
314impl Role for Agent {
315 type Counterpart = Client;
316
317 fn role_id(&self) -> RoleId {
318 RoleId::from_singleton(self)
319 }
320
321 fn counterpart(&self) -> Self::Counterpart {
322 Client
323 }
324
325 async fn default_handle_dispatch_from(
326 &self,
327 message: Dispatch,
328 connection: ConnectionTo<Agent>,
329 ) -> Result<Handled<Dispatch>, crate::Error> {
330 MatchDispatchFrom::new(message, &connection)
331 .if_dispatch_from(Agent, async |message: Dispatch| {
332 let retry = message.has_session_id()
348 && connection
349 .protocol_version()
350 .is_none_or(|version| version == crate::schema::ProtocolVersion::V1);
351 Ok(Handled::No { message, retry })
352 })
353 .await
354 .done()
355 }
356}
357
358#[cfg(feature = "unstable_protocol_v2")]
368fn unhandled_v2_entity_cancel(request: &crate::UntypedMessage) -> Option<crate::Error> {
369 let method = request.method();
370 let (key, label) = if method == v2::AGENT_METHOD_NAMES.tool_call_cancel {
371 ("toolCallId", "tool call")
372 } else if method == v2::AGENT_METHOD_NAMES.terminal_cancel {
373 ("terminalId", "terminal")
374 } else {
375 return None;
376 };
377 let error = crate::Error::new(
378 crate::ErrorCode::INVALID_PARAMS,
379 format!("the {label} is unknown or not cancellable"),
380 );
381 Some(
382 match request
383 .params()
384 .get(key)
385 .and_then(serde_json::Value::as_str)
386 {
387 Some(id) => error.data(serde_json::json!({ key: id })),
388 None => error,
389 },
390 )
391}
392
393impl Agent {
394 pub fn v1(self) -> crate::v1::Builder<Agent> {
401 Builder::new(self).v1_agent()
402 }
403
404 #[cfg(feature = "unstable_protocol_v2")]
433 pub fn v2(self) -> crate::v2::Builder<Agent> {
434 Builder::new(self).v2_agent()
435 }
436
437 #[cfg(feature = "unstable_protocol_v2")]
469 #[must_use]
470 pub fn protocol_router(self) -> AgentProtocolRouter {
471 AgentProtocolRouter::new()
472 }
473}
474
475#[cfg(feature = "unstable_protocol_v2")]
485#[derive(Debug, Default)]
486pub struct AgentProtocolRouter {
487 v1: Option<DynConnectTo<Client>>,
488 v2: Option<DynConnectTo<Client>>,
489}
490
491#[cfg(feature = "unstable_protocol_v2")]
492impl AgentProtocolRouter {
493 #[must_use]
495 pub fn new() -> Self {
496 Self::default()
497 }
498
499 #[must_use]
501 pub fn with_v1(mut self, agent: impl ConnectTo<Client>) -> Self {
502 self.v1 = Some(DynConnectTo::new(agent));
503 self
504 }
505
506 #[must_use]
508 pub fn with_v2(mut self, agent: impl ConnectTo<Client>) -> Self {
509 self.v2 = Some(DynConnectTo::new(agent));
510 self
511 }
512}
513
514#[cfg(feature = "unstable_protocol_v2")]
515impl ConnectTo<Client> for AgentProtocolRouter {
516 async fn connect_to(self, client: impl ConnectTo<Agent>) -> Result<(), crate::Error> {
517 let supported = SupportedProtocols {
518 v1: self.v1.is_some(),
519 v2: self.v2.is_some(),
520 };
521 let client = RunningProtocolPeer::new(client);
522 let Some((first_frame, client, selected)) =
523 select_protocol_peer(client, "initialize", supported).await?
524 else {
525 return Ok(());
526 };
527 let agent = selected
528 .take_agent(self)
529 .expect("the router only selects a configured implementation");
530
531 let agent = RunningProtocolPeer::new(agent);
532 agent.send_frame(first_frame)?;
533 pipe_protocol_peers_until_done(client, agent).await
534 }
535}
536
537#[cfg(feature = "unstable_protocol_v2")]
546async fn select_protocol_peer(
547 mut peer: RunningProtocolPeer,
548 initialize_method: &str,
549 supported: SupportedProtocols,
550) -> Result<Option<(TransportFrame, RunningProtocolPeer, SelectedProtocol)>, crate::Error> {
551 loop {
552 let Some((mut frame, next_peer)) = peer.next_frame().await? else {
553 return Ok(None);
554 };
555 peer = next_peer;
556 let message = match initialize_message_mut(&mut frame) {
557 Ok(Some(message)) => message,
558 Ok(None) => continue,
559 Err(error) => {
564 send_initialize_error(&peer.tx, &frame, error)?;
565 continue;
566 }
567 };
568 let selected = match select_initialize_protocol(message, initialize_method, supported) {
569 Ok(selected) => selected,
570 Err(_)
573 if supported.v1 && !requests_protocol_v2(message, initialize_method, supported) =>
574 {
575 SelectedProtocol::V1
576 }
577 Err(error) => {
578 send_initialize_error(&peer.tx, &frame, error)?;
579 continue;
580 }
581 };
582 if !selected.is_configured(supported) {
583 send_initialize_error(&peer.tx, &frame, selected.unsupported_error(supported))?;
584 continue;
585 }
586 return Ok(Some((frame, peer, selected)));
587 }
588}
589
590#[cfg(feature = "unstable_protocol_v2")]
591#[derive(Debug, Clone, Copy, PartialEq, Eq)]
592enum SelectedProtocol {
593 V1,
594 V2,
595}
596
597#[cfg(feature = "unstable_protocol_v2")]
598impl SelectedProtocol {
599 fn take_agent(self, agent: AgentProtocolRouter) -> Option<DynConnectTo<Client>> {
600 match self {
601 Self::V1 => agent.v1,
602 Self::V2 => agent.v2,
603 }
604 }
605
606 fn is_configured(self, supported: SupportedProtocols) -> bool {
607 match self {
608 Self::V1 => supported.v1,
609 Self::V2 => supported.v2,
610 }
611 }
612
613 fn version(self) -> ProtocolVersion {
614 match self {
615 Self::V1 => ProtocolVersion::V1,
616 Self::V2 => ProtocolVersion::V2,
617 }
618 }
619
620 fn name(self) -> &'static str {
621 match self {
622 Self::V1 => "1",
623 Self::V2 => "2",
624 }
625 }
626
627 fn unsupported_error(self, supported: SupportedProtocols) -> crate::Error {
628 crate::Error::invalid_request().data(format!(
629 "ACP protocol version {} is not configured; this endpoint supports {}",
630 self.name(),
631 supported.description()
632 ))
633 }
634}
635
636#[cfg(feature = "unstable_protocol_v2")]
637#[derive(Debug, Clone, Copy, PartialEq, Eq)]
638struct SupportedProtocols {
639 v1: bool,
640 v2: bool,
641}
642
643#[cfg(feature = "unstable_protocol_v2")]
644impl SupportedProtocols {
645 fn select(self, requested: ProtocolVersion) -> Option<SelectedProtocol> {
646 if self.v2 && requested >= ProtocolVersion::V2 {
647 return Some(SelectedProtocol::V2);
648 }
649 if self.v1 {
652 return Some(SelectedProtocol::V1);
653 }
654
655 None
656 }
657
658 fn versions(self) -> Vec<ProtocolVersion> {
659 [
660 self.v1.then_some(ProtocolVersion::V1),
661 self.v2.then_some(ProtocolVersion::V2),
662 ]
663 .into_iter()
664 .flatten()
665 .collect()
666 }
667
668 fn description(self) -> String {
669 match (self.v1, self.v2) {
670 (true, true) => "ACP protocol versions 1 and 2".into(),
671 (true, false) => "ACP protocol version 1".into(),
672 (false, true) => "ACP protocol version 2".into(),
673 (false, false) => "no ACP protocol versions".into(),
674 }
675 }
676}
677
678#[cfg(feature = "unstable_protocol_v2")]
686fn select_initialize_protocol(
687 message: &mut RawJsonRpcMessage,
688 initialize_method: &str,
689 supported: SupportedProtocols,
690) -> Result<SelectedProtocol, crate::Error> {
691 let RawJsonRpcMessage::Request(request) = message else {
692 return Err(crate::Error::invalid_request().data(format!(
693 "first ACP message must be a `{initialize_method}` request"
694 )));
695 };
696
697 if request.method.as_ref() != initialize_method {
698 return Err(crate::Error::invalid_request()
699 .data(format!("first ACP request must be `{initialize_method}`")));
700 }
701
702 let Some(RawJsonRpcParams::Object(params)) = &mut request.params else {
703 return Err(invalid_initialize_protocol_version());
704 };
705 let Some(protocol_version) = params.get("protocolVersion") else {
706 return Err(invalid_initialize_protocol_version());
707 };
708
709 let requested = serde_json::from_value::<ProtocolVersion>(protocol_version.clone())
710 .map_err(|_| invalid_initialize_protocol_version())?;
711 let selected = select_configured_protocol(requested, supported)?;
712 if selected == SelectedProtocol::V2 && request.id == RawJsonRpcId::Null {
714 return Err(crate::jsonrpc::raw_id::null_v2_request_id());
715 }
716 rewrite_initialize_params(params, requested, selected)?;
717
718 Ok(selected)
719}
720
721#[cfg(feature = "unstable_protocol_v2")]
724fn requested_initialize_version(
725 message: &RawJsonRpcMessage,
726 initialize_method: &str,
727) -> Option<ProtocolVersion> {
728 let RawJsonRpcMessage::Request(request) = message else {
729 return None;
730 };
731 if request.method.as_ref() != initialize_method {
732 return None;
733 }
734 let Some(RawJsonRpcParams::Object(params)) = &request.params else {
735 return None;
736 };
737 serde_json::from_value(params.get("protocolVersion")?.clone()).ok()
738}
739
740#[cfg(feature = "unstable_protocol_v2")]
744fn requests_protocol_v2(
745 message: &RawJsonRpcMessage,
746 initialize_method: &str,
747 supported: SupportedProtocols,
748) -> bool {
749 supported.v2
750 && requested_initialize_version(message, initialize_method)
751 .is_some_and(|requested| requested >= ProtocolVersion::V2)
752}
753
754#[cfg(feature = "unstable_protocol_v2")]
755fn initialize_request_params(
756 message: &RawJsonRpcMessage,
757) -> Result<&serde_json::Map<String, serde_json::Value>, crate::Error> {
758 let RawJsonRpcMessage::Request(request) = message else {
759 return Err(
760 crate::Error::invalid_request().data("first ACP message must be an initialize request")
761 );
762 };
763
764 if request.method.as_ref() != "initialize" {
765 return Err(crate::Error::invalid_request().data("first ACP request must be initialize"));
766 }
767
768 let Some(RawJsonRpcParams::Object(params)) = &request.params else {
769 return Err(invalid_initialize_protocol_version());
770 };
771 if !params.contains_key("protocolVersion") {
772 return Err(invalid_initialize_protocol_version());
773 }
774 Ok(params)
775}
776
777#[cfg(feature = "unstable_protocol_v2")]
778fn validated_initialize_params<T: DeserializeOwned>(
779 message: &RawJsonRpcMessage,
780) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
781 let params = initialize_request_params(message)?;
782 parse_initialize_params::<T>(params)?;
783 Ok(params.clone())
784}
785
786#[cfg(feature = "unstable_protocol_v2")]
787fn normalize_v2_initialize_params_for_reuse(
788 message: &RawJsonRpcMessage,
789) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
790 let params = initialize_request_params(message)?;
791 let requested = params
792 .get("protocolVersion")
793 .cloned()
794 .ok_or_else(invalid_initialize_protocol_version)
795 .and_then(|version| {
796 serde_json::from_value::<ProtocolVersion>(version)
797 .map_err(|_| invalid_initialize_protocol_version())
798 })?;
799 if requested == ProtocolVersion::V1 {
800 parse_initialize_params::<InitializeRequest>(params)?;
801 return Ok(params.clone());
802 }
803 normalize_v2_initialize_params_for_v1(params, true)
804}
805
806#[cfg(feature = "unstable_protocol_v2")]
807fn rewrite_initialize_params(
808 params: &mut serde_json::Map<String, serde_json::Value>,
809 requested: ProtocolVersion,
810 selected: SelectedProtocol,
811) -> Result<(), crate::Error> {
812 if requested == selected.version() {
816 match selected {
817 SelectedProtocol::V1 => {
818 parse_initialize_params::<InitializeRequest>(params)?;
819 }
820 SelectedProtocol::V2 => {
821 parse_initialize_params::<v2::InitializeRequest>(params)?;
822 }
823 }
824 return Ok(());
825 }
826
827 match selected {
828 SelectedProtocol::V1 => {
829 if requested > ProtocolVersion::V1 {
830 *params = normalize_v2_initialize_params_for_v1(params, false)?;
831 } else {
832 let mut initialize = parse_initialize_params::<InitializeRequest>(params)?;
833 initialize.protocol_version = ProtocolVersion::V1;
834 *params = serialize_initialize_params(initialize)?;
835 }
836 Ok(())
837 }
838 SelectedProtocol::V2 => {
839 *params = crate::jsonrpc::normalize_newer_v2_initialize_request(params)?;
842 Ok(())
843 }
844 }
845}
846
847#[cfg(feature = "unstable_protocol_v2")]
848fn normalize_v2_initialize_params_for_v1(
849 params: &serde_json::Map<String, serde_json::Value>,
850 require_lossless: bool,
851) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
852 if !require_lossless {
853 return crate::jsonrpc::normalize_v1_initialize_request(&serde_json::Value::Object(
854 params.clone(),
855 ));
856 }
857 let initialize = parse_initialize_params::<v2::InitializeRequest>(params)?;
861 let mut target = serialize_initialize_params(initialize)?;
862 if require_lossless && target != *params {
863 return Err(invalid_initialize_params(
864 "v2 initialize parameters are not losslessly representable in v1",
865 ));
866 }
867
868 target.insert(
869 "protocolVersion".into(),
870 serde_json::to_value(ProtocolVersion::V1).map_err(crate::Error::into_internal_error)?,
871 );
872 let info = target
873 .remove("info")
874 .ok_or_else(|| invalid_initialize_params("v2 InitializeRequest.info is required"))?;
875 target.insert("clientInfo".into(), info);
876 let capabilities = target
877 .remove("capabilities")
878 .and_then(|capabilities| capabilities.as_object().cloned())
879 .ok_or_else(|| {
880 crate::util::internal_error("v2 initialize capabilities did not serialize as an object")
881 })?;
882 let mut capabilities = capabilities;
883 if let Some(auth) = capabilities
884 .get_mut("auth")
885 .and_then(serde_json::Value::as_object_mut)
886 {
887 let terminal = auth.remove("terminal");
888 if require_lossless
889 && terminal
890 .as_ref()
891 .and_then(serde_json::Value::as_object)
892 .is_some_and(|terminal| terminal.contains_key("_meta"))
893 {
894 return Err(invalid_initialize_params(
895 "v2 terminal authentication metadata is not representable in v1",
896 ));
897 }
898 auth.insert("terminal".into(), terminal.is_some().into());
899 }
900 capabilities.insert(
901 "session".into(),
902 serde_json::json!({ "configOptions": { "boolean": {} } }),
903 );
904 target.insert("clientCapabilities".into(), capabilities.into());
905
906 let initialize = parse_initialize_params::<InitializeRequest>(&target)?;
907 let normalized = serialize_initialize_params(initialize)?;
908 if require_lossless && !json_object_contains(&normalized, &target) {
909 return Err(invalid_initialize_params(
910 "v2 initialize parameters are not losslessly representable in v1",
911 ));
912 }
913 Ok(normalized)
914}
915
916#[cfg(all(test, feature = "unstable_protocol_v2"))]
917mod initialize_normalization_tests {
918 use super::*;
919
920 fn v2_initialize_params() -> serde_json::Map<String, serde_json::Value> {
921 let value = serde_json::to_value(v2::InitializeRequest::new(
922 ProtocolVersion::V2,
923 v2::Implementation::new("test-client", "1.0.0"),
924 ))
925 .expect("serialize v2 initialize request");
926 value
927 .as_object()
928 .expect("initialize params serialize as an object")
929 .clone()
930 }
931
932 #[test]
933 fn v2_tolerant_fields_are_canonicalized_before_v1_normalization() {
934 let mut params = v2_initialize_params();
935 params.insert(
936 "capabilities".into(),
937 serde_json::Value::String("malformed".into()),
938 );
939 params.insert(
940 "_meta".into(),
941 serde_json::Value::String("malformed".into()),
942 );
943
944 let normalized = normalize_v2_initialize_params_for_v1(¶ms, false)
945 .expect("tolerant v2 fields should normalize through their defaults");
946 let normalized = serde_json::Value::Object(normalized);
947
948 assert!(normalized.get("_meta").is_none());
949 assert_eq!(
950 normalized.pointer("/clientCapabilities/session/configOptions/boolean"),
951 Some(&serde_json::json!({}))
952 );
953 }
954
955 #[test]
956 fn noncanonical_v2_fields_disable_reuse_but_not_v1_routing() {
957 let mut params = v2_initialize_params();
958 params
959 .get_mut("info")
960 .and_then(serde_json::Value::as_object_mut)
961 .expect("v2 initialize info is an object")
962 .insert("buildCommit".into(), serde_json::json!("abc123"));
963
964 normalize_v2_initialize_params_for_v1(¶ms, false)
965 .expect("v1 routing may ignore parameters unavailable in v1");
966 normalize_v2_initialize_params_for_v1(¶ms, true)
967 .expect_err("connection reuse requires lossless normalization");
968 }
969
970 #[test]
971 fn v1_reuse_probe_preserves_raw_initialize_params() {
972 let mut params = v2_initialize_params();
973 params.insert(
974 "protocolVersion".into(),
975 serde_json::json!(ProtocolVersion::V1),
976 );
977 let message = RawJsonRpcMessage::request(
978 "initialize".into(),
979 serde_json::Value::Object(params.clone()),
980 RawJsonRpcId::Number(1),
981 )
982 .expect("build initialize request");
983
984 let normalized = normalize_v2_initialize_params_for_reuse(&message)
985 .expect("v1-shaped initialize request should be valid");
986
987 assert_eq!(normalized, params);
988 }
989
990 #[test]
991 fn null_v2_terminal_marker_meta_is_omitted_before_v1_normalization() {
992 let mut params = v2_initialize_params();
993 params.insert(
994 "capabilities".into(),
995 serde_json::json!({
996 "auth": {
997 "terminal": { "_meta": null }
998 }
999 }),
1000 );
1001
1002 let normalized = normalize_v2_initialize_params_for_v1(¶ms, false)
1003 .expect("null marker metadata is equivalent to omission");
1004 let normalized = serde_json::Value::Object(normalized);
1005
1006 assert_eq!(
1007 normalized.pointer("/clientCapabilities/auth/terminal"),
1008 Some(&serde_json::Value::Bool(true))
1009 );
1010 }
1011
1012 #[test]
1013 fn terminal_marker_metadata_disables_reuse_but_not_v1_routing() {
1014 let mut params = v2_initialize_params();
1015 params.insert(
1016 "capabilities".into(),
1017 serde_json::json!({
1018 "auth": {
1019 "terminal": {
1020 "_meta": { "source": "test" }
1021 }
1022 }
1023 }),
1024 );
1025
1026 normalize_v2_initialize_params_for_v1(¶ms, false)
1027 .expect("v1 routing may discard terminal marker metadata");
1028 normalize_v2_initialize_params_for_v1(¶ms, true)
1029 .expect_err("connection reuse must preserve terminal marker metadata");
1030 }
1031}
1032
1033#[cfg(feature = "unstable_protocol_v2")]
1034fn parse_initialize_params<T: DeserializeOwned>(
1035 params: &serde_json::Map<String, serde_json::Value>,
1036) -> Result<T, crate::Error> {
1037 serde_json::from_value(serde_json::Value::Object(params.clone()))
1038 .map_err(invalid_initialize_params)
1039}
1040
1041#[cfg(feature = "unstable_protocol_v2")]
1042fn serialize_initialize_params(
1043 initialize: impl Serialize,
1044) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
1045 let value = serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?;
1046 let serde_json::Value::Object(object) = value else {
1047 return Err(crate::util::internal_error(
1048 "initialize params did not serialize to an object",
1049 ));
1050 };
1051 Ok(object)
1052}
1053
1054#[cfg(feature = "unstable_protocol_v2")]
1055fn json_object_contains(
1056 actual: &serde_json::Map<String, serde_json::Value>,
1057 expected: &serde_json::Map<String, serde_json::Value>,
1058) -> bool {
1059 fn contains(actual: &serde_json::Value, expected: &serde_json::Value) -> bool {
1060 match (actual, expected) {
1061 (serde_json::Value::Object(actual), serde_json::Value::Object(expected)) => expected
1062 .iter()
1063 .all(|(key, value)| actual.get(key).is_some_and(|item| contains(item, value))),
1064 _ => actual == expected,
1065 }
1066 }
1067
1068 expected
1069 .iter()
1070 .all(|(key, value)| actual.get(key).is_some_and(|item| contains(item, value)))
1071}
1072
1073#[cfg(feature = "unstable_protocol_v2")]
1074fn select_configured_protocol(
1075 requested: ProtocolVersion,
1076 supported: SupportedProtocols,
1077) -> Result<SelectedProtocol, crate::Error> {
1078 supported.select(requested).ok_or_else(|| {
1079 crate::v2::schema::Error::unsupported_protocol_version(requested, &supported.versions())
1080 .into()
1081 })
1082}
1083
1084#[cfg(feature = "unstable_protocol_v2")]
1085fn invalid_initialize_protocol_version() -> crate::Error {
1086 crate::Error::invalid_params()
1087 .data("initialize.protocolVersion must be a valid ACP protocol version")
1088}
1089
1090#[cfg(feature = "unstable_protocol_v2")]
1091fn invalid_initialize_params(error: impl ToString) -> crate::Error {
1092 crate::Error::invalid_params().data(format!("invalid initialize params: {}", error.to_string()))
1093}
1094
1095#[cfg(feature = "unstable_protocol_v2")]
1096fn send_initialize_error(
1097 tx: &futures::channel::mpsc::UnboundedSender<TransportFrame>,
1098 frame: &TransportFrame,
1099 error: crate::Error,
1100) -> Result<(), crate::Error> {
1101 fn response_for_message(
1102 entry: &RawJsonRpcMessage,
1103 initialize_error: &crate::Error,
1104 ) -> Option<RawJsonRpcMessage> {
1105 match entry {
1106 RawJsonRpcMessage::Request(request) => Some(RawJsonRpcMessage::response(
1107 request.id.clone(),
1108 Err(initialize_error.clone()),
1109 )),
1110 RawJsonRpcMessage::Notification(_) | RawJsonRpcMessage::Response(_) => None,
1111 }
1112 }
1113
1114 let response = match frame {
1115 TransportFrame::Single(entry) => {
1116 let Some(response) = response_for_message(entry, &error) else {
1117 return Ok(());
1118 };
1119 TransportFrame::Single(response)
1120 }
1121 TransportFrame::Malformed { raw, error } if !raw_is_response_only_shape(raw) => {
1122 TransportFrame::Single(RawJsonRpcMessage::response(
1123 RawJsonRpcId::Null,
1124 Err(error.clone()),
1125 ))
1126 }
1127 TransportFrame::Malformed { .. } => return Ok(()),
1128 };
1129
1130 tx.unbounded_send(response)
1131 .map_err(crate::util::internal_error)
1132}
1133
1134#[cfg(feature = "unstable_protocol_v2")]
1135struct RunningProtocolPeer {
1136 rx: futures::channel::mpsc::UnboundedReceiver<TransportFrame>,
1137 tx: futures::channel::mpsc::UnboundedSender<TransportFrame>,
1138 driver: ProtocolPeerDriver,
1139}
1140
1141#[cfg(feature = "unstable_protocol_v2")]
1142enum ProtocolPeerDriver {
1143 Passive,
1144 Active(crate::ConnectionDriver),
1145 Completed {
1146 finish: Option<crate::component::FinishControl>,
1147 },
1148}
1149
1150#[cfg(feature = "unstable_protocol_v2")]
1151impl ProtocolPeerDriver {
1152 fn into_driver(self) -> Option<crate::ConnectionDriver> {
1153 match self {
1154 Self::Passive => None,
1155 Self::Active(driver) => Some(driver),
1156 Self::Completed { finish } => Some(match finish {
1160 Some(mut finish) => {
1161 crate::ConnectionDriver::with_finish(future::ready(Ok(())), move || {
1162 finish.request();
1163 })
1164 }
1165 None => crate::ConnectionDriver::new(future::ready(Ok(()))),
1166 }),
1167 }
1168 }
1169}
1170
1171#[cfg(feature = "unstable_protocol_v2")]
1172impl RunningProtocolPeer {
1173 fn new<R: Role>(component: impl ConnectTo<R>) -> Self {
1174 let (Channel { rx, tx }, future) = component.into_channel_and_future();
1175 let driver = match future {
1176 None => ProtocolPeerDriver::Passive,
1177 Some(future) => ProtocolPeerDriver::Active(future),
1178 };
1179 Self { rx, tx, driver }
1180 }
1181
1182 async fn next_frame(self) -> Result<Option<(TransportFrame, Self)>, crate::Error> {
1183 let Self { mut rx, tx, driver } = self;
1184 let ProtocolPeerDriver::Active(mut future) = driver else {
1185 return Ok(rx
1186 .next()
1187 .await
1188 .map(|frame| (frame, Self { rx, tx, driver })));
1189 };
1190
1191 match future::select(&mut future, Box::pin(rx.next())).await {
1194 future::Either::Right((Some(frame), _)) => Ok(Some((
1195 frame,
1196 Self {
1197 rx,
1198 tx,
1199 driver: ProtocolPeerDriver::Active(future),
1200 },
1201 ))),
1202 future::Either::Right((None, _)) => {
1203 drop(tx);
1204 future.await?;
1205 Ok(None)
1206 }
1207 future::Either::Left((result, next_message)) => {
1208 result?;
1209 drop(next_message);
1210 rx.close();
1213 let Some(frame) = rx.next().await else {
1214 return Ok(None);
1215 };
1216 Ok(Some((
1217 frame,
1218 Self {
1219 rx,
1220 tx,
1221 driver: ProtocolPeerDriver::Completed {
1222 finish: future.take_finish(),
1223 },
1224 },
1225 )))
1226 }
1227 }
1228 }
1229
1230 async fn next_message(self) -> Result<Option<(RawJsonRpcMessage, Self)>, crate::Error> {
1231 let Some((frame, peer)) = self.next_frame().await? else {
1232 return Ok(None);
1233 };
1234 Ok(Some((initialize_message(frame)?, peer)))
1235 }
1236
1237 fn send(&self, message: RawJsonRpcMessage) -> Result<(), crate::Error> {
1238 self.send_frame(TransportFrame::Single(message))
1239 }
1240
1241 fn send_frame(&self, frame: TransportFrame) -> Result<(), crate::Error> {
1242 self.tx
1243 .unbounded_send(frame)
1244 .map_err(crate::util::internal_error)
1245 }
1246}
1247
1248#[cfg(feature = "unstable_protocol_v2")]
1249fn initialize_message(frame: TransportFrame) -> Result<RawJsonRpcMessage, crate::Error> {
1250 match frame {
1251 TransportFrame::Single(message) => Ok(message),
1252 TransportFrame::Malformed { error, .. } => Err(error),
1253 }
1254}
1255
1256#[cfg(feature = "unstable_protocol_v2")]
1257fn initialize_message_mut(
1258 frame: &mut TransportFrame,
1259) -> Result<Option<&mut RawJsonRpcMessage>, crate::Error> {
1260 match frame {
1261 TransportFrame::Single(RawJsonRpcMessage::Response(_)) => Ok(None),
1262 TransportFrame::Single(entry) => Ok(Some(entry)),
1263 TransportFrame::Malformed { raw, .. } if raw_is_response_only_shape(raw) => Ok(None),
1264 TransportFrame::Malformed { error, .. } => Err(error.clone()),
1265 }
1266}
1267
1268#[cfg(feature = "unstable_protocol_v2")]
1272async fn pipe_protocol_peers_until_done(
1273 left: RunningProtocolPeer,
1274 right: RunningProtocolPeer,
1275) -> Result<(), crate::Error> {
1276 let mut left_driver = left.driver.into_driver();
1277 let mut right_driver = right.driver.into_driver();
1278 let left_passive = left_driver.is_none();
1279 let right_passive = right_driver.is_none();
1280 let left_finish = left_driver
1281 .as_mut()
1282 .and_then(crate::ConnectionDriver::take_finish);
1283 let right_finish = right_driver
1284 .as_mut()
1285 .and_then(crate::ConnectionDriver::take_finish);
1286 let (stop_left_tx, stop_left_rx) = futures::channel::oneshot::channel();
1287 let (stop_right_tx, stop_right_rx) = futures::channel::oneshot::channel();
1288 let stop = async |rx: futures::channel::oneshot::Receiver<()>| {
1289 if rx.await.is_err() {
1290 future::pending::<()>().await;
1291 }
1292 };
1293 let left_to_right = Box::pin(
1294 Channel {
1295 rx: left.rx,
1296 tx: right.tx,
1297 }
1298 .copy_with_driver_until(left_driver, stop(stop_left_rx)),
1299 );
1300 let right_to_left = Box::pin(
1301 Channel {
1302 rx: right.rx,
1303 tx: left.tx,
1304 }
1305 .copy_with_driver_until(right_driver, stop(stop_right_rx)),
1306 );
1307
1308 match future::select(left_to_right, right_to_left).await {
1309 future::Either::Left((result, right_to_left)) => {
1310 result?;
1311 if !left_passive {
1312 let _ = stop_right_tx.send(());
1313 }
1314 if left_passive || right_finish.is_some() {
1315 if !left_passive && let Some(mut finish) = right_finish {
1316 finish.request();
1317 }
1318 right_to_left.await
1319 } else {
1320 crate::util::run_until(right_to_left, future::ready(Ok(()))).await
1323 }
1324 }
1325 future::Either::Right((result, left_to_right)) => {
1326 result?;
1327 if !right_passive {
1328 let _ = stop_left_tx.send(());
1329 }
1330 if right_passive || left_finish.is_some() {
1331 if !right_passive && let Some(mut finish) = left_finish {
1332 finish.request();
1333 }
1334 left_to_right.await
1335 } else {
1336 crate::util::run_until(left_to_right, future::ready(Ok(()))).await
1337 }
1338 }
1339 }
1340}
1341
1342#[cfg(feature = "unstable_protocol_v2")]
1343#[derive(Debug)]
1344struct InitializeResponse {
1345 id: RawJsonRpcId,
1346 result: Result<serde_json::Value, Box<RawJsonRpcError>>,
1347}
1348
1349#[cfg(feature = "unstable_protocol_v2")]
1350impl InitializeResponse {
1351 fn from_message(message: RawJsonRpcMessage) -> Result<Self, crate::Error> {
1352 match message {
1353 RawJsonRpcMessage::Response(RpcResponse::Result { id, result }) => Ok(Self {
1354 id,
1355 result: Ok(result),
1356 }),
1357 RawJsonRpcMessage::Response(RpcResponse::Error { id, error }) => Ok(Self {
1358 id,
1359 result: Err(error),
1360 }),
1361 message => Err(crate::Error::invalid_request().data(format!(
1362 "first ACP response must be an initialize response, got {message:?}",
1363 ))),
1364 }
1365 }
1366
1367 fn into_message(self) -> RawJsonRpcMessage {
1368 RawJsonRpcMessage::Response(RpcResponse::new(self.id, self.result))
1369 }
1370
1371 fn with_id(self, id: RawJsonRpcId) -> RawJsonRpcMessage {
1372 RawJsonRpcMessage::Response(RpcResponse::new(id, self.result))
1373 }
1374
1375 fn protocol_version(&self) -> Option<ProtocolVersion> {
1376 serde_json::from_value(self.result.as_ref().ok()?.get("protocolVersion")?.clone()).ok()
1377 }
1378}
1379
1380#[cfg(all(test, feature = "unstable_protocol_v2"))]
1381mod raw_initialize_tests {
1382 use super::*;
1383 use serde_json::json;
1384
1385 #[test]
1386 fn initialize_error_forwarding_preserves_raw_fields() {
1387 for data in [
1388 None,
1389 Some(serde_json::Value::Null),
1390 Some(json!({"detail":"kept"})),
1391 ] {
1392 let mut error = json!({
1393 "code":-32000, "message":"peer", "extension":{"retry":true}
1394 });
1395 if let Some(data) = data {
1396 error["data"] = data;
1397 }
1398 let wire = json!({"jsonrpc":"2.0", "id":"original", "error":error});
1399 let response =
1400 InitializeResponse::from_message(serde_json::from_value(wire.clone()).unwrap())
1401 .unwrap();
1402 assert_eq!(serde_json::to_value(response.into_message()).unwrap(), wire);
1403 let response =
1404 InitializeResponse::from_message(serde_json::from_value(wire).unwrap()).unwrap();
1405 let forwarded = response.with_id(RawJsonRpcId::Str("replacement".into()));
1406 assert_eq!(
1407 serde_json::to_value(forwarded).unwrap(),
1408 json!({"jsonrpc":"2.0", "id":"replacement", "error":error})
1409 );
1410 }
1411 }
1412}
1413
1414#[cfg(feature = "unstable_protocol_v2")]
1415#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1416enum ClientProtocol {
1417 V1,
1418 V2,
1419}
1420
1421#[cfg(feature = "unstable_protocol_v2")]
1422impl ClientProtocol {
1423 fn name(self) -> &'static str {
1424 match self {
1425 Self::V1 => "1",
1426 Self::V2 => "2",
1427 }
1428 }
1429}
1430
1431#[cfg(feature = "unstable_protocol_v2")]
1432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1433struct SupportedClientProtocols {
1434 v1: bool,
1435 v2: bool,
1436}
1437
1438#[cfg(feature = "unstable_protocol_v2")]
1439impl SupportedClientProtocols {
1440 fn highest_configured(self) -> Option<ClientProtocol> {
1441 if self.v2 {
1442 return Some(ClientProtocol::V2);
1443 }
1444
1445 if self.v1 {
1446 return Some(ClientProtocol::V1);
1447 }
1448
1449 None
1450 }
1451}
1452
1453#[cfg(feature = "unstable_protocol_v2")]
1454async fn start_client_protocol(
1455 protocol: ClientProtocol,
1456 client: DynConnectTo<Agent>,
1457) -> Result<(RunningProtocolPeer, RawJsonRpcMessage), crate::Error> {
1458 let client = RunningProtocolPeer::new(client);
1459 let Some((initialize, client)) = client.next_message().await? else {
1460 return Err(crate::Error::invalid_request().data(format!(
1461 "ACP protocol version {} client implementation ended before initialize",
1462 protocol.name()
1463 )));
1464 };
1465 ensure_client_initialize_request(protocol, &initialize)?;
1466 Ok((client, initialize))
1467}
1468
1469#[cfg(feature = "unstable_protocol_v2")]
1470async fn send_initialize_and_receive(
1471 client: RunningProtocolPeer,
1472 agent: RunningProtocolPeer,
1473 initialize: RawJsonRpcMessage,
1474) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
1475 agent.send(initialize)?;
1476 let Some((response, agent)) = agent.next_message().await? else {
1477 return Err(crate::Error::internal_error().data("agent closed before initialize response"));
1478 };
1479 let response = InitializeResponse::from_message(response)?;
1480 Ok((client, agent, response))
1481}
1482
1483#[cfg(feature = "unstable_protocol_v2")]
1484async fn initialize_client_protocol(
1485 protocol: ClientProtocol,
1486 client: DynConnectTo<Agent>,
1487 agent: impl ConnectTo<Client>,
1488) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
1489 let agent = RunningProtocolPeer::new(agent);
1490 let (client, initialize) = start_client_protocol(protocol, client).await?;
1491 send_initialize_and_receive(client, agent, initialize).await
1492}
1493
1494#[cfg(feature = "unstable_protocol_v2")]
1495async fn connect_client_protocol(
1496 protocol: ClientProtocol,
1497 client: DynConnectTo<Agent>,
1498 agent: impl ConnectTo<Client>,
1499) -> Result<(), crate::Error> {
1500 let (client, agent, initialize_response) =
1501 initialize_client_protocol(protocol, client, agent).await?;
1502 client.send(initialize_response.into_message())?;
1503 pipe_protocol_peers_until_done(client, agent).await
1504}
1505
1506#[cfg(feature = "unstable_protocol_v2")]
1507fn ensure_client_initialize_request(
1508 protocol: ClientProtocol,
1509 message: &RawJsonRpcMessage,
1510) -> Result<(), crate::Error> {
1511 let RawJsonRpcMessage::Request(request) = message else {
1512 return Err(crate::Error::invalid_request().data(format!(
1513 "ACP protocol version {} client implementation must send initialize first",
1514 protocol.name()
1515 )));
1516 };
1517
1518 if request.method.as_ref() != "initialize" {
1519 return Err(crate::Error::invalid_request().data(format!(
1520 "ACP protocol version {} client implementation must send initialize first",
1521 protocol.name()
1522 )));
1523 }
1524
1525 Ok(())
1526}
1527
1528#[cfg(feature = "unstable_protocol_v2")]
1529fn initialize_request_id(message: &RawJsonRpcMessage) -> Option<RawJsonRpcId> {
1530 let RawJsonRpcMessage::Request(request) = message else {
1531 return None;
1532 };
1533 Some(request.id.clone())
1534}
1535
1536#[cfg(feature = "unstable_protocol_v2")]
1537fn initialize_response_negotiated_v1(response: &InitializeResponse) -> bool {
1538 response.protocol_version() == Some(ProtocolVersion::V1)
1539}
1540
1541impl HasPeer<Agent> for Agent {
1542 fn remote_style(&self, _peer: Agent) -> RemoteStyle {
1543 RemoteStyle::Counterpart
1544 }
1545}
1546
1547#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1556pub struct Proxy;
1557
1558impl Role for Proxy {
1559 type Counterpart = Conductor;
1560
1561 fn default_handle_dispatch_from(
1562 &self,
1563 message: crate::Dispatch,
1564 _connection: crate::ConnectionTo<Self>,
1565 ) -> impl Future<Output = Result<crate::Handled<crate::Dispatch>, crate::Error>> + Send {
1566 std::future::ready(Ok(Handled::No {
1567 message,
1568 retry: false,
1569 }))
1570 }
1571
1572 fn role_id(&self) -> RoleId {
1573 RoleId::from_singleton(self)
1574 }
1575
1576 fn counterpart(&self) -> Self::Counterpart {
1577 Conductor
1578 }
1579}
1580
1581impl Proxy {
1582 pub fn v1(self) -> crate::v1::Builder<Proxy> {
1597 Builder::new(self).v1_proxy()
1598 }
1599
1600 #[cfg(feature = "unstable_protocol_v2")]
1612 pub fn v2(self) -> crate::v2::Builder<Proxy> {
1613 Builder::new(self).v2_proxy()
1614 }
1615
1616 #[cfg(feature = "unstable_protocol_v2")]
1635 #[must_use]
1636 pub fn protocol_router(self) -> ProxyProtocolRouter {
1637 ProxyProtocolRouter::new()
1638 }
1639}
1640
1641#[cfg(feature = "unstable_protocol_v2")]
1652#[derive(Debug, Default)]
1653pub struct ProxyProtocolRouter {
1654 v1: Option<DynConnectTo<Conductor>>,
1655 v2: Option<DynConnectTo<Conductor>>,
1656}
1657
1658#[cfg(feature = "unstable_protocol_v2")]
1659impl ProxyProtocolRouter {
1660 #[must_use]
1662 pub fn new() -> Self {
1663 Self::default()
1664 }
1665
1666 #[must_use]
1668 pub fn with_v1(mut self, proxy: impl ConnectTo<Conductor>) -> Self {
1669 self.v1 = Some(DynConnectTo::new(proxy));
1670 self
1671 }
1672
1673 #[must_use]
1675 pub fn with_v2(mut self, proxy: impl ConnectTo<Conductor>) -> Self {
1676 self.v2 = Some(DynConnectTo::new(proxy));
1677 self
1678 }
1679}
1680
1681#[cfg(feature = "unstable_protocol_v2")]
1682impl ConnectTo<Conductor> for ProxyProtocolRouter {
1683 async fn connect_to(self, conductor: impl ConnectTo<Proxy>) -> Result<(), crate::Error> {
1684 let supported = SupportedProtocols {
1685 v1: self.v1.is_some(),
1686 v2: self.v2.is_some(),
1687 };
1688 let conductor = RunningProtocolPeer::new(conductor);
1689 let Some((first_frame, conductor, selected)) =
1690 select_protocol_peer(conductor, PROXY_METHOD_NAMES.proxy_initialize, supported).await?
1691 else {
1692 return Ok(());
1693 };
1694 let proxy = selected
1695 .take_proxy(self)
1696 .expect("the router only selects a configured implementation");
1697
1698 let proxy = RunningProtocolPeer::new(proxy);
1699 proxy.send_frame(first_frame)?;
1700 pipe_protocol_peers_until_done(conductor, proxy).await
1701 }
1702}
1703
1704#[cfg(feature = "unstable_protocol_v2")]
1705impl SelectedProtocol {
1706 fn take_proxy(self, proxy: ProxyProtocolRouter) -> Option<DynConnectTo<Conductor>> {
1707 match self {
1708 Self::V1 => proxy.v1,
1709 Self::V2 => proxy.v2,
1710 }
1711 }
1712}
1713
1714impl HasPeer<Proxy> for Proxy {
1715 fn remote_style(&self, _peer: Proxy) -> RemoteStyle {
1716 RemoteStyle::Counterpart
1717 }
1718}
1719
1720#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1725pub struct Conductor;
1726
1727impl Role for Conductor {
1728 type Counterpart = Proxy;
1729
1730 fn role_id(&self) -> RoleId {
1731 RoleId::from_singleton(self)
1732 }
1733
1734 fn counterpart(&self) -> Self::Counterpart {
1735 Proxy
1736 }
1737
1738 async fn default_handle_dispatch_from(
1739 &self,
1740 message: Dispatch,
1741 cx: ConnectionTo<Conductor>,
1742 ) -> Result<Handled<Dispatch>, crate::Error> {
1743 forward_proxy_dispatch(message, cx).await
1744 }
1745}
1746
1747async fn forward_proxy_dispatch(
1756 message: Dispatch,
1757 cx: ConnectionTo<Conductor>,
1758) -> Result<Handled<Dispatch>, crate::Error> {
1759 let message = match message {
1760 Dispatch::Request(request, responder) if request.method() == "initialize" => {
1761 responder.respond_with_error(crate::Error::invalid_request().data(format!(
1762 "proxies must be initialized with `{}`",
1763 PROXY_METHOD_NAMES.proxy_initialize
1764 )))?;
1765 return Ok(Handled::Yes);
1766 }
1767 Dispatch::Request(mut request, responder)
1768 if request.method() == PROXY_METHOD_NAMES.proxy_initialize =>
1769 {
1770 request.method = "initialize".to_string();
1771 cx.send_ordered_request_to::<Raw, _, _>(Agent, request)
1772 .forward_response_to(responder)?;
1773 return Ok(Handled::Yes);
1774 }
1775 Dispatch::Request(request, responder) if request.method() == "session/new" => {
1776 let sent = cx.send_ordered_request_to::<Raw, _, _>(Agent, request);
1777 let sent = sent.forward_cancellation_from(responder.cancellation());
1782 sent.on_receiving_result({
1783 let cx = cx.clone();
1784 async move |result| {
1785 if let Ok(response) = &result
1789 && let Err(error) = install_proxy_session_routing(&cx, response)
1790 {
1791 tracing::warn!(
1792 error = ?crate::util::redacted_error(error.code),
1793 "forwarding a `session/new` result without session routing"
1794 );
1795 }
1796 responder.respond_with_result(result)
1797 }
1798 })?;
1799 return Ok(Handled::Yes);
1800 }
1801 message => message,
1802 };
1803
1804 MatchDispatchFrom::new(message, &cx)
1805 .if_dispatch_from(Client, async |message: Dispatch| {
1806 cx.send_proxied_message_to(Agent, message)
1807 })
1808 .await
1809 .if_dispatch_from(Agent, async |message: Dispatch| {
1810 cx.send_proxied_message_to(Client, message)
1811 })
1812 .await
1813 .done()
1814}
1815
1816fn install_proxy_session_routing(
1818 cx: &ConnectionTo<Conductor>,
1819 response: &serde_json::Value,
1820) -> Result<(), crate::Error> {
1821 let envelope: NewSessionResponseEnvelope = crate::util::json_cast(response)?;
1822 cx.add_dynamic_handler(ProxySessionMessages::new(envelope.session_id))?
1823 .detach();
1824 Ok(())
1825}
1826
1827impl Conductor {
1828 pub fn builder(self) -> Builder<Conductor, NullHandler, NullRun> {
1830 Builder::new(self)
1831 }
1832}
1833
1834impl HasPeer<Client> for Conductor {
1835 fn remote_style(&self, _peer: Client) -> RemoteStyle {
1836 RemoteStyle::Predecessor
1837 }
1838}
1839
1840impl HasPeer<Agent> for Conductor {
1841 fn remote_style(&self, _peer: Agent) -> RemoteStyle {
1842 RemoteStyle::Successor
1843 }
1844}
1845
1846pub(crate) struct ProxySessionMessages {
1851 session_id: SessionId,
1852}
1853
1854impl ProxySessionMessages {
1855 pub fn new(session_id: SessionId) -> Self {
1857 Self { session_id }
1858 }
1859}
1860
1861impl<Counterpart: Role> HandleDispatchFrom<Counterpart> for ProxySessionMessages
1862where
1863 Counterpart: HasPeer<Agent> + HasPeer<Client>,
1864{
1865 async fn handle_dispatch_from(
1866 &mut self,
1867 message: Dispatch,
1868 connection: ConnectionTo<Counterpart>,
1869 ) -> Result<Handled<Dispatch>, crate::Error> {
1870 MatchDispatchFrom::new(message, &connection)
1871 .if_dispatch_from(Agent, async |message| {
1872 if let Some(session_id) = message.get_session_id()?
1874 && session_id == self.session_id
1875 {
1876 connection.send_proxied_message_to(Client, message)?;
1877 return Ok(Handled::Yes);
1878 }
1879
1880 Ok(Handled::No {
1882 message,
1883 retry: false,
1884 })
1885 })
1886 .await
1887 .done()
1888 }
1889
1890 fn describe_chain(&self) -> impl std::fmt::Debug {
1891 format!("ProxySessionMessages({})", self.session_id)
1892 }
1893}
1894
1895#[cfg(all(test, feature = "unstable_protocol_v2"))]
1896mod lifetime_tests {
1897 use super::*;
1898 use crate::{ConnectionDriver, UntypedRole};
1899 use futures::FutureExt as _;
1900
1901 fn frame() -> TransportFrame {
1902 TransportFrame::parse_json(r#"{"jsonrpc":"2.0","method":"test/queued","params":{}}"#)
1903 }
1904
1905 #[tokio::test]
1906 async fn passive_initialization_waits_for_a_frame_not_driver_readiness() {
1907 let (channel, remote) = Channel::duplex();
1908 let peer = RunningProtocolPeer::new::<UntypedRole>(channel);
1909 let mut next = Box::pin(peer.next_frame());
1910 assert!(next.as_mut().now_or_never().is_none());
1911
1912 remote.tx.unbounded_send(frame()).unwrap();
1913 let (_, peer) = next.await.unwrap().expect("passive peer remains connected");
1914 assert!(matches!(peer.driver, ProtocolPeerDriver::Passive));
1915 }
1916
1917 #[tokio::test]
1918 async fn owned_peer_preserves_finish_metadata_through_active_and_completed_states() {
1919 let (Channel { rx, tx }, remote) = Channel::duplex();
1920 let (done_tx, done_rx) = futures::channel::oneshot::channel();
1921 let (finish_tx, finish_rx) = futures::channel::oneshot::channel();
1922 let peer = RunningProtocolPeer {
1923 rx,
1924 tx,
1925 driver: ProtocolPeerDriver::Active(ConnectionDriver::with_finish(
1926 async move {
1927 done_rx.await.unwrap();
1928 Ok(())
1929 },
1930 move || {
1931 let _ = finish_tx.send(());
1932 },
1933 )),
1934 };
1935
1936 remote.tx.unbounded_send(frame()).unwrap();
1937 let (_, peer) = peer.next_frame().await.unwrap().unwrap();
1938 assert!(matches!(&peer.driver, ProtocolPeerDriver::Active(_)));
1939
1940 remote.tx.unbounded_send(frame()).unwrap();
1941 done_tx.send(()).unwrap();
1942 let (_, peer) = peer.next_frame().await.unwrap().unwrap();
1943 assert!(matches!(
1944 &peer.driver,
1945 ProtocolPeerDriver::Completed { finish: Some(_) }
1946 ));
1947 let mut driver = peer
1948 .driver
1949 .into_driver()
1950 .expect("completed owned work must not become passive");
1951 assert!(driver.request_finish());
1952 finish_rx.await.unwrap();
1953 driver.await.unwrap();
1954 }
1955
1956 #[tokio::test]
1957 async fn active_initialization_drains_accepted_frames_without_escaped_sender_eof() {
1958 let (Channel { rx, tx }, remote) = Channel::duplex();
1959 remote.tx.unbounded_send(frame()).unwrap();
1960 remote.tx.unbounded_send(frame()).unwrap();
1961 let mut polls = 0;
1962 let driver = ConnectionDriver::new(future::poll_fn(move |_| {
1963 polls += 1;
1964 assert_eq!(polls, 1, "the completed driver must never be re-polled");
1965 std::task::Poll::Ready(Ok(()))
1966 }));
1967 let peer = RunningProtocolPeer {
1968 rx,
1969 tx,
1970 driver: ProtocolPeerDriver::Active(driver),
1971 };
1972
1973 let (_, peer) = peer.next_frame().await.unwrap().unwrap();
1974 assert!(
1975 remote.tx.unbounded_send(frame()).is_err(),
1976 "active completion must reject new output from escaped handles"
1977 );
1978 let (_, peer) = peer.next_frame().await.unwrap().unwrap();
1979 assert!(peer.next_frame().await.unwrap().is_none());
1980 }
1981
1982 #[tokio::test]
1983 async fn ready_initialization_driver_error_beats_queued_frames() {
1984 let (Channel { rx, tx }, remote) = Channel::duplex();
1985 remote.tx.unbounded_send(frame()).unwrap();
1986 let error = crate::Error::internal_error().data("owned initialization failed");
1987 let peer = RunningProtocolPeer {
1988 rx,
1989 tx,
1990 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(future::ready(Err(
1991 error.clone()
1992 )))),
1993 };
1994
1995 match peer.next_frame().await {
1996 Err(actual) => assert_eq!(actual, error),
1997 Ok(_) => panic!("ready driver error must not be hidden by a queued frame"),
1998 }
1999 }
2000
2001 struct QueuedFinalClient;
2002
2003 impl ConnectTo<Agent> for QueuedFinalClient {
2004 async fn connect_to(self, agent: impl ConnectTo<Client>) -> Result<(), crate::Error> {
2005 let (mut channel, driver) = agent.into_channel_and_future();
2006 let foreground = async move {
2007 channel
2008 .tx
2009 .unbounded_send(TransportFrame::Single(RawJsonRpcMessage::request(
2010 "initialize".into(),
2011 serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2012 RawJsonRpcId::Number(1),
2013 )?))
2014 .unwrap();
2015 assert!(channel.rx.next().await.is_some(), "initialize response");
2016 for index in 0..3 {
2019 channel
2020 .tx
2021 .unbounded_send(TransportFrame::Single(RawJsonRpcMessage::notification(
2022 "test/final".into(),
2023 serde_json::json!({ "index": index, "payload": "x".repeat(1024) }),
2024 )?))
2025 .unwrap();
2026 }
2027 Ok(())
2028 };
2029 match driver {
2030 Some(driver) => crate::util::run_until(driver, foreground).await,
2031 None => foreground.await,
2032 }
2033 }
2034 }
2035
2036 #[tokio::test]
2037 async fn connector_completion_flushes_byte_streams_without_remote_read_eof() {
2038 use tokio::io::{AsyncBufReadExt as _, AsyncWriteExt as _, BufReader};
2039 use tokio_util::compat::{TokioAsyncReadCompatExt as _, TokioAsyncWriteCompatExt as _};
2040
2041 let (writer, remote_reader) = tokio::io::duplex(64);
2042 let (mut remote_input, reader) = tokio::io::duplex(64);
2043 let physical = crate::ByteStreams::new(writer.compat_write(), reader.compat());
2044 let mut physical = Some(crate::DynConnectTo::<Client>::new(physical));
2045 let connector = tokio::spawn(
2046 ClientProtocolConnector::new()
2047 .with_v1(|| QueuedFinalClient)
2048 .connect_to(move || physical.take().expect("one physical connection")),
2049 );
2050 let mut lines = BufReader::new(remote_reader).lines();
2051
2052 tokio::time::timeout(std::time::Duration::from_secs(5), async {
2053 let initialize = lines
2054 .next_line()
2055 .await
2056 .unwrap()
2057 .expect("initialize request");
2058 let value: serde_json::Value = serde_json::from_str(&initialize).unwrap();
2059 assert_eq!(value["method"], "initialize");
2060 remote_input
2061 .write_all(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"protocolVersion\":1}}\n")
2062 .await
2063 .unwrap();
2064 for index in 0..3 {
2065 let line = lines
2066 .next_line()
2067 .await
2068 .unwrap()
2069 .expect("accepted final output");
2070 let value: serde_json::Value = serde_json::from_str(&line).unwrap();
2071 assert_eq!(value["params"]["index"], index);
2072 assert_eq!(value["params"]["payload"].as_str().unwrap().len(), 1024);
2073 }
2074 connector.await.unwrap().unwrap();
2075 assert!(lines.next_line().await.unwrap().is_none());
2076 })
2077 .await
2078 .expect("physical flush must not wait for independent remote input EOF");
2079 drop(remote_input);
2080 }
2081
2082 #[derive(Default, Debug)]
2083 struct GatedLineSinkState {
2084 pending: Vec<String>,
2085 flushed: Vec<String>,
2086 closed: bool,
2087 dropped: bool,
2088 }
2089
2090 struct GatedLineSink {
2091 state: std::sync::Arc<std::sync::Mutex<GatedLineSinkState>>,
2092 release: futures::channel::oneshot::Receiver<()>,
2093 released: bool,
2094 }
2095
2096 impl futures::Sink<String> for GatedLineSink {
2097 type Error = std::io::Error;
2098
2099 fn poll_ready(
2100 self: std::pin::Pin<&mut Self>,
2101 _: &mut std::task::Context<'_>,
2102 ) -> std::task::Poll<Result<(), Self::Error>> {
2103 std::task::Poll::Ready(Ok(()))
2104 }
2105
2106 fn start_send(self: std::pin::Pin<&mut Self>, line: String) -> Result<(), Self::Error> {
2107 self.state.lock().unwrap().pending.push(line);
2108 Ok(())
2109 }
2110
2111 fn poll_flush(
2112 mut self: std::pin::Pin<&mut Self>,
2113 cx: &mut std::task::Context<'_>,
2114 ) -> std::task::Poll<Result<(), Self::Error>> {
2115 if !self.released {
2116 if std::pin::Pin::new(&mut self.release).poll(cx).is_pending() {
2117 return std::task::Poll::Pending;
2118 }
2119 self.released = true;
2120 }
2121 let mut state = self.state.lock().unwrap();
2122 let pending = std::mem::take(&mut state.pending);
2123 state.flushed.extend(pending);
2124 std::task::Poll::Ready(Ok(()))
2125 }
2126
2127 fn poll_close(
2128 mut self: std::pin::Pin<&mut Self>,
2129 cx: &mut std::task::Context<'_>,
2130 ) -> std::task::Poll<Result<(), Self::Error>> {
2131 match self.as_mut().poll_flush(cx) {
2132 std::task::Poll::Ready(Ok(())) => {
2133 self.state.lock().unwrap().closed = true;
2134 std::task::Poll::Ready(Ok(()))
2135 }
2136 result => result,
2137 }
2138 }
2139 }
2140
2141 impl Drop for GatedLineSink {
2142 fn drop(&mut self) {
2143 self.state.lock().unwrap().dropped = true;
2144 }
2145 }
2146
2147 #[tokio::test]
2148 async fn foreground_completion_flushes_lines_with_already_normalized_remote_input() {
2149 let state = std::sync::Arc::new(std::sync::Mutex::new(GatedLineSinkState::default()));
2150 let (release_tx, release_rx) = futures::channel::oneshot::channel();
2151 let sink = GatedLineSink {
2152 state: state.clone(),
2153 release: release_rx,
2154 released: false,
2155 };
2156 let (remote_input, incoming) = futures::channel::mpsc::unbounded();
2157 remote_input
2158 .unbounded_send(Ok(
2159 r#"{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1}}"#.to_string(),
2160 ))
2161 .unwrap();
2162 remote_input
2163 .unbounded_send(Ok(
2164 r#"{"jsonrpc":"2.0","method":"test/queued","params":{}}"#.to_string(),
2165 ))
2166 .unwrap();
2167 let physical = RunningProtocolPeer::new::<Client>(crate::Lines::new(sink, incoming));
2168 let (initialize, physical) = physical.next_frame().await.unwrap().unwrap();
2170 assert_eq!(
2171 futures::Stream::size_hint(&physical.rx).0,
2172 1,
2173 "trailing input must already be in the original normalized queue"
2174 );
2175
2176 let (Channel { rx, tx }, mut local) = Channel::duplex();
2177 tx.unbounded_send(initialize).unwrap();
2178 let foreground = RunningProtocolPeer {
2179 rx,
2180 tx,
2181 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(async move {
2182 assert!(local.rx.next().await.is_some(), "initialize response");
2183 for index in 0..3 {
2184 local
2185 .tx
2186 .unbounded_send(TransportFrame::Single(RawJsonRpcMessage::notification(
2187 "test/final".into(),
2188 serde_json::json!({ "index": index, "payload": "x".repeat(1024) }),
2189 )?))
2190 .unwrap();
2191 }
2192 drop(local);
2194 Ok(())
2195 })),
2196 };
2197 let mut bridge = Box::pin(pipe_protocol_peers_until_done(foreground, physical));
2198 let early_result = bridge.as_mut().now_or_never();
2199 assert!(
2200 !state.lock().unwrap().pending.is_empty(),
2201 "the real physical writer must accept output before the gated flush"
2202 );
2203 let _released = release_tx.send(());
2205 let result = match early_result {
2206 Some(result) => result,
2207 None => tokio::time::timeout(std::time::Duration::from_secs(1), bridge)
2208 .await
2209 .expect("physical drain must not require remote input EOF"),
2210 };
2211 let state = state.lock().unwrap();
2212 assert!(
2213 result.is_ok() && state.flushed.len() == 3 && state.closed,
2214 "accepted output must drain cleanly despite queued remote input: result={result:?}, pending={}, flushed={}, closed={}, dropped={}",
2215 state.pending.len(),
2216 state.flushed.len(),
2217 state.closed,
2218 state.dropped,
2219 );
2220 for (index, line) in state.flushed.iter().enumerate() {
2221 let value: serde_json::Value = serde_json::from_str(line).unwrap();
2222 assert_eq!(value["method"], "test/final");
2223 assert_eq!(value["params"]["index"], index);
2224 assert_eq!(value["params"]["payload"].as_str().unwrap().len(), 1024);
2225 }
2226 drop(remote_input);
2227 }
2228
2229 #[tokio::test]
2230 async fn foreground_completion_keeps_read_errors_during_lines_drain() {
2231 let state = std::sync::Arc::new(std::sync::Mutex::new(GatedLineSinkState::default()));
2232 let (_release_tx, release_rx) = futures::channel::oneshot::channel();
2233 let sink = GatedLineSink {
2234 state: state.clone(),
2235 release: release_rx,
2236 released: false,
2237 };
2238 let (remote_input, incoming) = futures::channel::mpsc::unbounded();
2239 let physical = RunningProtocolPeer::new::<Client>(crate::Lines::new(sink, incoming));
2240 let (Channel { rx, tx }, local) = Channel::duplex();
2241 local.tx.unbounded_send(frame()).unwrap();
2242 drop(local);
2243 let foreground = RunningProtocolPeer {
2244 rx,
2245 tx,
2246 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(future::ready(Ok(())))),
2247 };
2248 let mut bridge = Box::pin(pipe_protocol_peers_until_done(foreground, physical));
2249 assert!(bridge.as_mut().now_or_never().is_none());
2250 assert_eq!(state.lock().unwrap().pending.len(), 1);
2251
2252 remote_input
2255 .unbounded_send(Ok(r#"{"jsonrpc":"2.0","method":"late"}"#.to_string()))
2256 .unwrap();
2257 remote_input
2258 .unbounded_send(Err(std::io::Error::other("read failed after foreground")))
2259 .unwrap();
2260 let error = tokio::time::timeout(std::time::Duration::from_secs(1), bridge)
2261 .await
2262 .expect("read failure must not wait for the sink gate")
2263 .expect_err("real read error must win over foreground success");
2264 assert!(
2265 error
2266 .data
2267 .unwrap()
2268 .to_string()
2269 .contains("read failed after foreground"),
2270 "the read error must not become a receiver-gone forwarding error"
2271 );
2272 assert_eq!(state.lock().unwrap().flushed, Vec::<String>::new());
2273 }
2274
2275 #[tokio::test]
2276 async fn foreground_completion_cancels_opposed_opaque_work_in_both_directions() {
2277 for foreground_on_left in [true, false] {
2278 let (Channel { rx, tx }, foreground_remote) = Channel::duplex();
2279 foreground_remote.tx.unbounded_send(frame()).unwrap();
2280 let foreground = RunningProtocolPeer {
2281 rx,
2282 tx,
2283 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(future::ready(Ok(())))),
2284 };
2285 let (Channel { rx, tx }, mut opposed_remote) = Channel::duplex();
2286 let (work_tx, work_rx) = futures::channel::oneshot::channel::<()>();
2287 let opposed = RunningProtocolPeer {
2288 rx,
2289 tx,
2290 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(async move {
2291 work_rx.await.map_err(crate::util::internal_error)?;
2292 Ok(())
2293 })),
2294 };
2295 let mut bridge = Box::pin(if foreground_on_left {
2296 pipe_protocol_peers_until_done(foreground, opposed)
2297 } else {
2298 pipe_protocol_peers_until_done(opposed, foreground)
2299 });
2300
2301 assert_eq!(
2302 bridge.as_mut().now_or_never(),
2303 Some(Ok(())),
2304 "finite foreground must not join an opaque pending peer"
2305 );
2306 assert!(work_tx.is_canceled(), "opaque work must be dropped");
2307 assert!(opposed_remote.rx.next().await.is_some());
2308 assert!(opposed_remote.rx.next().await.is_none());
2309 }
2310 }
2311
2312 #[tokio::test]
2313 async fn foreground_completion_does_not_hide_opposed_ready_driver_error() {
2314 let (Channel { rx, tx }, _foreground_remote) = Channel::duplex();
2315 let foreground = RunningProtocolPeer {
2316 rx,
2317 tx,
2318 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(future::ready(Ok(())))),
2319 };
2320 let (Channel { rx, tx }, _opposed_remote) = Channel::duplex();
2321 let error = crate::Error::internal_error().data("opposed driver failed");
2322 let opposed = RunningProtocolPeer {
2323 rx,
2324 tx,
2325 driver: ProtocolPeerDriver::Active(ConnectionDriver::new(future::ready(Err(
2326 error.clone()
2327 )))),
2328 };
2329
2330 assert_eq!(
2331 pipe_protocol_peers_until_done(foreground, opposed).await,
2332 Err(error)
2333 );
2334 }
2335
2336 #[tokio::test]
2337 async fn passive_protocol_bridge_preserves_the_reverse_half_after_eof() {
2338 let (left, mut remote_left) = Channel::duplex();
2339 let (right, mut remote_right) = Channel::duplex();
2340 let mut bridge = Box::pin(pipe_protocol_peers_until_done(
2341 RunningProtocolPeer::new::<UntypedRole>(left),
2342 RunningProtocolPeer::new::<UntypedRole>(right),
2343 ));
2344 remote_left.tx.close_channel();
2345 assert!(bridge.as_mut().now_or_never().is_none());
2346 assert!(remote_right.rx.next().await.is_none());
2347
2348 remote_right.tx.unbounded_send(frame()).unwrap();
2349 remote_right.tx.close_channel();
2350 bridge.await.unwrap();
2351 assert!(remote_left.rx.next().await.is_some());
2352 assert!(remote_left.rx.next().await.is_none());
2353 }
2354}