@@ -49,6 +49,11 @@ enum BodyType {
49
49
CloseDelimited ,
50
50
}
51
51
52
+ pub ( crate ) enum PendingReader {
53
+ BeforeBodyStart ,
54
+ Reader ( Box < dyn Read + Send + Sync + ' static > ) ,
55
+ }
56
+
52
57
/// Response instances are created as results of firing off requests.
53
58
///
54
59
/// The `Response` is used to read response headers and decide what to do with the body.
@@ -81,7 +86,7 @@ pub struct Response {
81
86
pub ( crate ) index : ResponseStatusIndex ,
82
87
pub ( crate ) status : u16 ,
83
88
pub ( crate ) headers : Vec < Header > ,
84
- pub ( crate ) reader : Box < dyn Read + Send + Sync + ' static > ,
89
+ pub ( crate ) reader : PendingReader ,
85
90
/// The socket address of the server that sent the response.
86
91
pub ( crate ) remote_addr : SocketAddr ,
87
92
/// The socket address of the client that sent the request.
@@ -282,7 +287,12 @@ impl Response {
282
287
/// # }
283
288
/// ```
284
289
pub fn into_reader ( self ) -> Box < dyn Read + Send + Sync + ' static > {
285
- self . reader
290
+ match self . reader {
291
+ PendingReader :: Reader ( reader) => reader,
292
+ _ => panic ! (
293
+ "It is not valid to call into_reader before Request::stream_to_reader is called"
294
+ ) ,
295
+ }
286
296
}
287
297
288
298
// Determine what to do with the connection after we've read the body.
@@ -548,39 +558,33 @@ impl Response {
548
558
} )
549
559
}
550
560
551
- /// Create a response from a Read trait impl.
552
- ///
553
- /// This is hopefully useful for unit tests.
554
- ///
555
- /// Example:
561
+ /// Create a response from a DeadlineStream, reading and parsing only the status line, headers
562
+ /// and its following CRLF.
556
563
///
557
- /// use std::io::Cursor;
558
- ///
559
- /// let text = "HTTP/1.1 401 Authorization Required\r\n\r\nPlease log in\n";
560
- /// let read = Cursor::new(text.to_string().into_bytes());
561
- /// let resp = ureq::Response::do_from_read(read);
562
- ///
563
- /// assert_eq!(resp.status(), 401);
564
- pub ( crate ) fn do_from_stream ( stream : Stream , unit : & Unit ) -> Result < Response , Error > {
565
- let remote_addr = stream. remote_addr ;
564
+ /// Since this function only reads the status line, header and the following CRLF, the returned
565
+ /// Response will have an empty reader and does not take ownership of DeadlineStream.
566
+ /// To read the following data, the DeadlineStream can be read again after the call to this
567
+ /// function.
568
+ pub ( crate ) fn read_response_head (
569
+ stream : & mut DeadlineStream ,
570
+ unit : & Unit ,
571
+ ) -> Result < Response , Error > {
572
+ let mut bytes_read = 0 ;
573
+ let remote_addr = stream. inner_ref ( ) . remote_addr ;
566
574
567
- let local_addr = match stream. socket ( ) {
575
+ let local_addr = match stream. inner_ref ( ) . socket ( ) {
568
576
Some ( sock) => sock. local_addr ( ) . map_err ( Error :: from) ?,
569
577
None => std:: net:: SocketAddrV4 :: new ( std:: net:: Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) , 0 ) . into ( ) ,
570
578
} ;
571
579
572
- //
573
- // HTTP/1.1 200 OK\r\n
574
- let mut stream = stream:: DeadlineStream :: new ( stream, unit. deadline ) ;
575
-
576
580
// The status line we can ignore non-utf8 chars and parse as_str_lossy().
577
- let status_line = read_next_line ( & mut stream, "the status line" ) ?. into_string_lossy ( ) ;
581
+ let status_line =
582
+ read_next_line ( stream, "the status line" , & mut bytes_read) ?. into_string_lossy ( ) ;
578
583
let ( index, status) = parse_status_line ( status_line. as_str ( ) ) ?;
579
- let http_version = & status_line. as_str ( ) [ 0 ..index. http_version ] ;
580
584
581
585
let mut headers: Vec < Header > = Vec :: new ( ) ;
582
586
while headers. len ( ) <= MAX_HEADER_COUNT {
583
- let line = read_next_line ( & mut stream, "a header" ) ?;
587
+ let line = read_next_line ( stream, "a header" , & mut bytes_read ) ?;
584
588
if line. is_empty ( ) {
585
589
break ;
586
590
}
@@ -595,23 +599,8 @@ impl Response {
595
599
) ) ;
596
600
}
597
601
598
- let compression =
599
- get_header ( & headers, "content-encoding" ) . and_then ( Compression :: from_header_value) ;
600
-
601
- let connection_option =
602
- Self :: connection_option ( http_version, get_header ( & headers, "connection" ) ) ;
603
-
604
- let body_type = Self :: body_type ( & unit. method , status, http_version, & headers) ;
605
-
606
- // remove Content-Encoding and length due to automatic decompression
607
- if compression. is_some ( ) {
608
- headers. retain ( |h| !h. is_name ( "content-encoding" ) && !h. is_name ( "content-length" ) ) ;
609
- }
610
-
611
- let reader =
612
- Self :: stream_to_reader ( stream, unit, body_type, compression, connection_option) ;
613
-
614
602
let url = unit. url . clone ( ) ;
603
+ let reader = PendingReader :: BeforeBodyStart ;
615
604
616
605
let response = Response {
617
606
url,
@@ -627,6 +616,50 @@ impl Response {
627
616
Ok ( response)
628
617
}
629
618
619
+ /// Create a Response from a DeadlineStream
620
+ ///
621
+ /// Parses and comsumes the header from the stream and creates a Response with
622
+ /// the stream as the reader. The response reader also uncompresses the body
623
+ /// if it is compressed.
624
+ pub ( crate ) fn do_from_stream (
625
+ mut stream : DeadlineStream ,
626
+ unit : & Unit ,
627
+ ) -> Result < Response , Error > {
628
+ let mut response = Self :: read_response_head ( & mut stream, unit) ?;
629
+
630
+ let compression = get_header ( & response. headers , "content-encoding" )
631
+ . and_then ( Compression :: from_header_value) ;
632
+
633
+ let connection_option = Self :: connection_option (
634
+ response. http_version ( ) ,
635
+ get_header ( & response. headers , "connection" ) ,
636
+ ) ;
637
+
638
+ let body_type = Self :: body_type (
639
+ & unit. method ,
640
+ response. status ( ) ,
641
+ response. http_version ( ) ,
642
+ & response. headers ,
643
+ ) ;
644
+
645
+ // remove Content-Encoding and length due to automatic decompression
646
+ if compression. is_some ( ) {
647
+ response
648
+ . headers
649
+ . retain ( |h| !h. is_name ( "content-encoding" ) && !h. is_name ( "content-length" ) ) ;
650
+ }
651
+
652
+ response. reader = PendingReader :: Reader ( Self :: stream_to_reader (
653
+ stream,
654
+ unit,
655
+ body_type,
656
+ compression,
657
+ connection_option,
658
+ ) ) ;
659
+
660
+ Ok ( response)
661
+ }
662
+
630
663
#[ cfg( test) ]
631
664
pub fn set_url ( & mut self , url : Url ) {
632
665
self . url = url;
@@ -766,16 +799,23 @@ impl FromStr for Response {
766
799
& request_reader,
767
800
None ,
768
801
) ;
802
+ let stream = stream:: DeadlineStream :: new ( stream, unit. deadline ) ;
769
803
Self :: do_from_stream ( stream, & unit)
770
804
}
771
805
}
772
806
773
- fn read_next_line ( reader : & mut impl BufRead , context : & str ) -> io:: Result < HeaderLine > {
807
+ fn read_next_line (
808
+ reader : & mut impl BufRead ,
809
+ context : & str ,
810
+ running_total : & mut usize ,
811
+ ) -> io:: Result < HeaderLine > {
774
812
let mut buf = Vec :: new ( ) ;
775
813
let result = reader
776
814
. take ( ( MAX_HEADER_SIZE + 1 ) as u64 )
777
815
. read_until ( b'\n' , & mut buf) ;
778
816
817
+ * running_total += buf. len ( ) ;
818
+
779
819
match result {
780
820
Ok ( 0 ) => Err ( io:: Error :: new (
781
821
io:: ErrorKind :: ConnectionAborted ,
@@ -1078,7 +1118,8 @@ mod tests {
1078
1118
const LEN : usize = MAX_HEADER_SIZE + 1 ;
1079
1119
let s = format ! ( "Long-Header: {}\r \n " , "A" . repeat( LEN ) , ) ;
1080
1120
let mut cursor = Cursor :: new ( s) ;
1081
- let result = read_next_line ( & mut cursor, "some context" ) ;
1121
+ let mut bytes_read = 0 ;
1122
+ let result = read_next_line ( & mut cursor, "some context" , & mut bytes_read) ;
1082
1123
let err = result. expect_err ( "did not error on too-large header" ) ;
1083
1124
assert_eq ! ( err. kind( ) , io:: ErrorKind :: Other ) ;
1084
1125
assert_eq ! (
@@ -1117,9 +1158,9 @@ mod tests {
1117
1158
encoding_rs:: WINDOWS_1252 . encode ( "HTTP/1.1 302 Déplacé Temporairement\r \n " ) ;
1118
1159
let bytes = cow. to_vec ( ) ;
1119
1160
let mut reader = io:: BufReader :: new ( io:: Cursor :: new ( bytes) ) ;
1120
- let r = read_next_line ( & mut reader , "test status line" ) ;
1121
- let h = r . unwrap ( ) ;
1122
- assert_eq ! ( h . to_string( ) , "HTTP/1.1 302 D�plac� Temporairement" ) ;
1161
+ let mut bytes_read = 0 ;
1162
+ let header = read_next_line ( & mut reader , "test status line" , & mut bytes_read ) . unwrap ( ) ;
1163
+ assert_eq ! ( header . to_string( ) , "HTTP/1.1 302 D�plac� Temporairement" ) ;
1123
1164
}
1124
1165
1125
1166
#[ test]
@@ -1150,6 +1191,7 @@ mod tests {
1150
1191
& request_reader,
1151
1192
None ,
1152
1193
) ;
1194
+ let s = stream:: DeadlineStream :: new ( s, unit. deadline ) ;
1153
1195
let resp = Response :: do_from_stream ( s. into ( ) , & unit) . unwrap ( ) ;
1154
1196
assert_eq ! ( resp. status( ) , 200 ) ;
1155
1197
assert_eq ! ( resp. header( "x-geo-header" ) , None ) ;
@@ -1204,18 +1246,16 @@ mod tests {
1204
1246
"1.1.1.1:4343" . parse ( ) . unwrap ( ) ,
1205
1247
PoolReturner :: new ( & agent, PoolKey :: from_parts ( "https" , "example.com" , 443 ) ) ,
1206
1248
) ;
1207
- Response :: do_from_stream (
1208
- stream,
1209
- & Unit :: new (
1210
- & agent,
1211
- "GET" ,
1212
- & "https://example.com/" . parse ( ) . unwrap ( ) ,
1213
- vec ! [ ] ,
1214
- & Payload :: Empty . into_read ( ) ,
1215
- None ,
1216
- ) ,
1217
- )
1218
- . unwrap ( ) ;
1249
+ let unit = & Unit :: new (
1250
+ & agent,
1251
+ "GET" ,
1252
+ & "https://example.com/" . parse ( ) . unwrap ( ) ,
1253
+ vec ! [ ] ,
1254
+ & Payload :: Empty . into_read ( ) ,
1255
+ None ,
1256
+ ) ;
1257
+ let stream = stream:: DeadlineStream :: new ( stream, unit. deadline ) ;
1258
+ Response :: do_from_stream ( stream, unit) . unwrap ( ) ;
1219
1259
assert_eq ! ( agent2. state. pool. len( ) , 1 ) ;
1220
1260
}
1221
1261
@@ -1236,18 +1276,16 @@ mod tests {
1236
1276
"1.1.1.1:4343" . parse ( ) . unwrap ( ) ,
1237
1277
PoolReturner :: none ( ) ,
1238
1278
) ;
1239
- let resp = Response :: do_from_stream (
1240
- stream,
1241
- & Unit :: new (
1242
- & agent,
1243
- "GET" ,
1244
- & "https://example.com/" . parse ( ) . unwrap ( ) ,
1245
- vec ! [ ] ,
1246
- & Payload :: Empty . into_read ( ) ,
1247
- None ,
1248
- ) ,
1249
- )
1250
- . unwrap ( ) ;
1279
+ let unit = & Unit :: new (
1280
+ & agent,
1281
+ "GET" ,
1282
+ & "https://example.com/" . parse ( ) . unwrap ( ) ,
1283
+ vec ! [ ] ,
1284
+ & Payload :: Empty . into_read ( ) ,
1285
+ None ,
1286
+ ) ;
1287
+ let stream = stream:: DeadlineStream :: new ( stream, unit. deadline ) ;
1288
+ let resp = Response :: do_from_stream ( stream, unit) . unwrap ( ) ;
1251
1289
let body = resp. into_string ( ) . unwrap ( ) ;
1252
1290
assert_eq ! ( body, "hi\n " ) ;
1253
1291
}
0 commit comments