@@ -20,7 +20,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
20
20
use hyper:: body:: Bytes ;
21
21
use hyper:: body:: Frame ;
22
22
use hyper:: Request ;
23
- use hyper_util:: client:: legacy:: connect:: { capture_connection, HttpConnector } ;
23
+ use hyper_util:: client:: legacy:: connect:: { capture_connection, HttpConnector , HttpInfo } ;
24
24
use hyper_util:: client:: legacy:: Client ;
25
25
use hyper_util:: rt:: { TokioExecutor , TokioIo } ;
26
26
@@ -978,3 +978,91 @@ fn connection_poisoning() {
978
978
assert_eq ! ( num_conns. load( Ordering :: SeqCst ) , 2 ) ;
979
979
assert_eq ! ( num_requests. load( Ordering :: SeqCst ) , 5 ) ;
980
980
}
981
+
982
+ #[ cfg( not( miri) ) ]
983
+ #[ tokio:: test]
984
+ async fn connect_info_on_error ( ) {
985
+ let client = Client :: builder ( TokioExecutor :: new ( ) ) . build ( HttpConnector :: new ( ) ) ;
986
+
987
+ // srv1 accepts one connection, and cancel it after reading the second request.
988
+ let tcp1 = tokio:: net:: TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
989
+ let addr1 = tcp1. local_addr ( ) . unwrap ( ) ;
990
+ let srv1 = tokio:: spawn ( async move {
991
+ let ( mut sock, _addr) = tcp1. accept ( ) . await . unwrap ( ) ;
992
+ let mut buf = [ 0 ; 4096 ] ;
993
+ sock. read ( & mut buf) . await . expect ( "read 1" ) ;
994
+ let body = Bytes :: from ( "Hello, world!" ) ;
995
+ sock. write_all (
996
+ format ! ( "HTTP/1.1 200 OK\r \n Content-Length: {}\r \n \r \n " , body. len( ) ) . as_bytes ( ) ,
997
+ )
998
+ . await
999
+ . expect ( "write header" ) ;
1000
+ sock. write_all ( & body) . await . expect ( "write body" ) ;
1001
+
1002
+ sock. read ( & mut buf) . await . expect ( "read 2" ) ;
1003
+ drop ( sock) ;
1004
+ } ) ;
1005
+
1006
+ // Makes a first request to srv1, which should succeed.
1007
+ {
1008
+ let req = Request :: builder ( )
1009
+ . uri ( format ! ( "http://{addr1}" ) )
1010
+ . body ( Empty :: < Bytes > :: new ( ) )
1011
+ . unwrap ( ) ;
1012
+ let res = client. request ( req) . await . unwrap ( ) ;
1013
+ let http_info = res. extensions ( ) . get :: < HttpInfo > ( ) . unwrap ( ) ;
1014
+ assert_eq ! ( http_info. remote_addr( ) , addr1) ;
1015
+ let res_body = String :: from_utf8 ( res. collect ( ) . await . unwrap ( ) . to_bytes ( ) . into ( ) ) . unwrap ( ) ;
1016
+ assert_eq ! ( res_body, "Hello, world!" ) ;
1017
+ }
1018
+
1019
+ // Makes a second request to srv1, which should use the same connection and fail.
1020
+ {
1021
+ let req = Request :: builder ( )
1022
+ . uri ( format ! ( "http://{addr1}" ) )
1023
+ . body ( Empty :: < Bytes > :: new ( ) )
1024
+ . unwrap ( ) ;
1025
+ let err = client. request ( req) . await . unwrap_err ( ) ;
1026
+ let conn_info = err. connect_info ( ) . unwrap ( ) ;
1027
+ assert ! ( !conn_info. is_proxied( ) ) ;
1028
+ assert ! ( !conn_info. is_negotiated_h2( ) ) ;
1029
+ assert ! ( conn_info. is_reused( ) ) ;
1030
+
1031
+ let mut exts = http:: Extensions :: new ( ) ;
1032
+ conn_info. get_extras ( & mut exts) ;
1033
+ let http_info = exts. get :: < HttpInfo > ( ) . unwrap ( ) ;
1034
+ assert_eq ! ( http_info. remote_addr( ) , addr1) ;
1035
+ }
1036
+
1037
+ srv1. await . unwrap ( ) ;
1038
+
1039
+ // srv2 accepts one connection, reads a request, and immediately closes it.
1040
+ let tcp2 = tokio:: net:: TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
1041
+ let addr2 = tcp2. local_addr ( ) . unwrap ( ) ;
1042
+ let srv2 = tokio:: spawn ( async move {
1043
+ let ( mut sock, _addr) = tcp2. accept ( ) . await . unwrap ( ) ;
1044
+ let mut buf = [ 0 ; 4096 ] ;
1045
+ sock. read ( & mut buf) . await . expect ( "read" ) ;
1046
+ drop ( sock) ;
1047
+ } ) ;
1048
+
1049
+ // Makes a first request to srv2, which should use a fresh connection and fail.
1050
+ {
1051
+ let req = Request :: builder ( )
1052
+ . uri ( format ! ( "http://{addr2}" ) )
1053
+ . body ( Empty :: < Bytes > :: new ( ) )
1054
+ . unwrap ( ) ;
1055
+ let err = client. request ( req) . await . unwrap_err ( ) ;
1056
+ let conn_info = err. connect_info ( ) . unwrap ( ) ;
1057
+ assert ! ( !conn_info. is_proxied( ) ) ;
1058
+ assert ! ( !conn_info. is_negotiated_h2( ) ) ;
1059
+ assert ! ( !conn_info. is_reused( ) ) ;
1060
+
1061
+ let mut exts = http:: Extensions :: new ( ) ;
1062
+ conn_info. get_extras ( & mut exts) ;
1063
+ let http_info = exts. get :: < HttpInfo > ( ) . unwrap ( ) ;
1064
+ assert_eq ! ( http_info. remote_addr( ) , addr2) ;
1065
+ }
1066
+
1067
+ srv2. await . unwrap ( ) ;
1068
+ }
0 commit comments