@@ -82,6 +82,8 @@ pub(crate) struct ConnectorBuilder {
8282 resolver : Option < DynResolver > ,
8383 #[ cfg( unix) ]
8484 unix_socket : Option < Arc < std:: path:: Path > > ,
85+ #[ cfg( target_os = "windows" ) ]
86+ windows_named_pipe : Option < Arc < std:: ffi:: OsStr > > ,
8587}
8688
8789impl ConnectorBuilder {
@@ -103,13 +105,20 @@ where {
103105 resolver : self . resolver . unwrap_or_else ( DynResolver :: gai) ,
104106 #[ cfg( unix) ]
105107 unix_socket : self . unix_socket ,
108+ #[ cfg( target_os = "windows" ) ]
109+ windows_named_pipe : self . windows_named_pipe ,
106110 } ;
107111
108112 #[ cfg( unix) ]
109113 if base_service. unix_socket . is_some ( ) && !base_service. proxies . is_empty ( ) {
110114 base_service. proxies = Default :: default ( ) ;
111115 log:: trace!( "unix_socket() set, proxies are ignored" ) ;
112116 }
117+ #[ cfg( target_os = "windows" ) ]
118+ if base_service. windows_named_pipe . is_some ( ) && !base_service. proxies . is_empty ( ) {
119+ base_service. proxies = Default :: default ( ) ;
120+ log:: trace!( "windows_named_pipe() set, proxies are ignored" ) ;
121+ }
113122
114123 if layers. is_empty ( ) {
115124 // we have no user-provided layers, only use concrete types
@@ -208,6 +217,8 @@ where {
208217 resolver : None ,
209218 #[ cfg( unix) ]
210219 unix_socket : None ,
220+ #[ cfg( target_os = "windows" ) ]
221+ windows_named_pipe : None ,
211222 }
212223 }
213224
@@ -319,6 +330,8 @@ where {
319330 resolver : None ,
320331 #[ cfg( unix) ]
321332 unix_socket : None ,
333+ #[ cfg( target_os = "windows" ) ]
334+ windows_named_pipe : None ,
322335 }
323336 }
324337
@@ -392,6 +405,8 @@ where {
392405 resolver : None ,
393406 #[ cfg( unix) ]
394407 unix_socket : None ,
408+ #[ cfg( target_os = "windows" ) ]
409+ windows_named_pipe : None ,
395410 }
396411 }
397412
@@ -457,6 +472,11 @@ where {
457472 pub ( crate ) fn set_unix_socket ( & mut self , path : Option < Arc < std:: path:: Path > > ) {
458473 self . unix_socket = path;
459474 }
475+
476+ #[ cfg( target_os = "windows" ) ]
477+ pub ( crate ) fn set_windows_named_pipe ( & mut self , pipe : Option < Arc < std:: ffi:: OsStr > > ) {
478+ self . windows_named_pipe = pipe;
479+ }
460480}
461481
462482#[ allow( missing_debug_implementations) ]
@@ -481,6 +501,8 @@ pub(crate) struct ConnectorService {
481501 /// If set, this always takes priority over TCP.
482502 #[ cfg( unix) ]
483503 unix_socket : Option < Arc < std:: path:: Path > > ,
504+ #[ cfg( target_os = "windows" ) ]
505+ windows_named_pipe : Option < Arc < std:: ffi:: OsStr > > ,
484506}
485507
486508#[ derive( Clone ) ]
@@ -673,21 +695,37 @@ impl ConnectorService {
673695 }
674696 }
675697
676- /// Connect over Unix Domain Socket (or Windows? ).
677- #[ cfg( unix) ]
698+ /// Connect over a local transport: Unix Domain Socket (on Unix) or Windows Named Pipe (on Windows ).
699+ #[ cfg( any ( unix, target_os = "windows" ) ) ]
678700 async fn connect_local_transport ( self , dst : Uri ) -> Result < Conn , BoxError > {
679- let path = self
680- . unix_socket
681- . as_ref ( )
682- . expect ( "connect local must have socket path" )
683- . clone ( ) ;
684- let svc = tower:: service_fn ( move |_| {
685- let fut = tokio:: net:: UnixStream :: connect ( path. clone ( ) ) ;
686- async move {
687- let io = fut. await ?;
688- Ok :: < _ , std:: io:: Error > ( TokioIo :: new ( io) )
689- }
690- } ) ;
701+ #[ cfg( unix) ]
702+ let svc = {
703+ let path = self
704+ . unix_socket
705+ . as_ref ( )
706+ . expect ( "connect local must have socket path" )
707+ . clone ( ) ;
708+ tower:: service_fn ( move |_| {
709+ let fut = tokio:: net:: UnixStream :: connect ( path. clone ( ) ) ;
710+ async move {
711+ let io = fut. await ?;
712+ Ok :: < _ , std:: io:: Error > ( TokioIo :: new ( io) )
713+ }
714+ } )
715+ } ;
716+ #[ cfg( target_os = "windows" ) ]
717+ let svc = {
718+ use tokio:: net:: windows:: named_pipe:: ClientOptions ;
719+ let pipe = self
720+ . windows_named_pipe
721+ . as_ref ( )
722+ . expect ( "connect local must have pipe path" )
723+ . clone ( ) ;
724+ tower:: service_fn ( move |_| {
725+ let pipe = pipe. clone ( ) ;
726+ async move { ClientOptions :: new ( ) . open ( pipe) . map ( TokioIo :: new) }
727+ } )
728+ } ;
691729 let is_proxy = false ;
692730 match self . inner {
693731 #[ cfg( not( feature = "__tls" ) ) ]
@@ -852,6 +890,15 @@ impl ConnectorService {
852890
853891 self . connect_with_maybe_proxy ( proxy_dst, true ) . await
854892 }
893+
894+ #[ cfg( any( unix, target_os = "windows" ) ) ]
895+ fn should_use_local_transport ( & self ) -> bool {
896+ #[ cfg( unix) ]
897+ return self . unix_socket . is_some ( ) ;
898+
899+ #[ cfg( target_os = "windows" ) ]
900+ return self . windows_named_pipe . is_some ( ) ;
901+ }
855902}
856903
857904async fn with_timeout < T , F > ( f : F , timeout : Option < Duration > ) -> Result < T , BoxError >
@@ -882,9 +929,9 @@ impl Service<Uri> for ConnectorService {
882929 log:: debug!( "starting new connection: {dst:?}" ) ;
883930 let timeout = self . simple_timeout ;
884931
885- // Local transports (UDS) skip proxies
886- #[ cfg( unix) ]
887- if self . unix_socket . is_some ( ) {
932+ // Local transports (UDS, Windows Named Pipes ) skip proxies
933+ #[ cfg( any ( unix, target_os = "windows" ) ) ]
934+ if self . should_use_local_transport ( ) {
888935 return Box :: pin ( with_timeout (
889936 self . clone ( ) . connect_local_transport ( dst) ,
890937 timeout,
@@ -1249,6 +1296,39 @@ pub(crate) mod uds {
12491296 ] ;
12501297}
12511298
1299+ // Sealed trait for Windows Named Pipe support
1300+ #[ cfg( target_os = "windows" ) ]
1301+ pub ( crate ) mod windows_named_pipe {
1302+ use std:: ffi:: OsStr ;
1303+ /// A provider for Windows Named Pipe paths.
1304+ ///
1305+ /// This trait is sealed. This allows us to expand support in the future
1306+ /// by controlling who can implement the trait.
1307+ #[ cfg( target_os = "windows" ) ]
1308+ pub trait WindowsNamedPipeProvider {
1309+ #[ doc( hidden) ]
1310+ fn reqwest_windows_named_pipe_path ( & self , _: Internal ) -> & OsStr ;
1311+ }
1312+
1313+ #[ allow( missing_debug_implementations) ]
1314+ pub struct Internal ;
1315+
1316+ macro_rules! as_os_str {
1317+ ( $( $t: ty, ) +) => {
1318+ $(
1319+ impl WindowsNamedPipeProvider for $t {
1320+ #[ doc( hidden) ]
1321+ fn reqwest_windows_named_pipe_path( & self , _: Internal ) -> & OsStr {
1322+ self . as_ref( )
1323+ }
1324+ }
1325+ ) +
1326+ }
1327+ }
1328+
1329+ as_os_str ! [ String , & ' _ str , ] ;
1330+ }
1331+
12521332pub ( crate ) type Connecting = Pin < Box < dyn Future < Output = Result < Conn , BoxError > > + Send > > ;
12531333
12541334#[ cfg( feature = "default-tls" ) ]
0 commit comments