@@ -50,11 +50,7 @@ pub struct HttpEndpoint {
50
50
impl HttpEndpoint {
51
51
/// Creates an endpoint for the given host and default HTTP port.
52
52
pub fn for_host ( host : String ) -> Self {
53
- Self {
54
- host,
55
- port : None ,
56
- path : String :: from ( "/" ) ,
57
- }
53
+ Self { host, port : None , path : String :: from ( "/" ) }
58
54
}
59
55
60
56
/// Specifies a port to use with the endpoint.
@@ -107,7 +103,10 @@ impl HttpClient {
107
103
pub fn connect < E : ToSocketAddrs > ( endpoint : E ) -> std:: io:: Result < Self > {
108
104
let address = match endpoint. to_socket_addrs ( ) ?. next ( ) {
109
105
None => {
110
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput , "could not resolve to any addresses" ) ) ;
106
+ return Err ( std:: io:: Error :: new (
107
+ std:: io:: ErrorKind :: InvalidInput ,
108
+ "could not resolve to any addresses" ,
109
+ ) ) ;
111
110
} ,
112
111
Some ( address) => address,
113
112
} ;
@@ -129,12 +128,16 @@ impl HttpClient {
129
128
/// Returns the response body in `F` format.
130
129
#[ allow( dead_code) ]
131
130
pub async fn get < F > ( & mut self , uri : & str , host : & str ) -> std:: io:: Result < F >
132
- where F : TryFrom < Vec < u8 > , Error = std:: io:: Error > {
131
+ where
132
+ F : TryFrom < Vec < u8 > , Error = std:: io:: Error > ,
133
+ {
133
134
let request = format ! (
134
135
"GET {} HTTP/1.1\r \n \
135
136
Host: {}\r \n \
136
137
Connection: keep-alive\r \n \
137
- \r \n ", uri, host) ;
138
+ \r \n ",
139
+ uri, host
140
+ ) ;
138
141
let response_body = self . send_request_with_retry ( & request) . await ?;
139
142
F :: try_from ( response_body)
140
143
}
@@ -145,8 +148,12 @@ impl HttpClient {
145
148
/// The request body consists of the provided JSON `content`. Returns the response body in `F`
146
149
/// format.
147
150
#[ allow( dead_code) ]
148
- pub async fn post < F > ( & mut self , uri : & str , host : & str , auth : & str , content : serde_json:: Value ) -> std:: io:: Result < F >
149
- where F : TryFrom < Vec < u8 > , Error = std:: io:: Error > {
151
+ pub async fn post < F > (
152
+ & mut self , uri : & str , host : & str , auth : & str , content : serde_json:: Value ,
153
+ ) -> std:: io:: Result < F >
154
+ where
155
+ F : TryFrom < Vec < u8 > , Error = std:: io:: Error > ,
156
+ {
150
157
let content = content. to_string ( ) ;
151
158
let request = format ! (
152
159
"POST {} HTTP/1.1\r \n \
@@ -156,7 +163,13 @@ impl HttpClient {
156
163
Content-Type: application/json\r \n \
157
164
Content-Length: {}\r \n \
158
165
\r \n \
159
- {}", uri, host, auth, content. len( ) , content) ;
166
+ {}",
167
+ uri,
168
+ host,
169
+ auth,
170
+ content. len( ) ,
171
+ content
172
+ ) ;
160
173
let response_body = self . send_request_with_retry ( & request) . await ?;
161
174
F :: try_from ( response_body)
162
175
}
@@ -218,8 +231,10 @@ impl HttpClient {
218
231
let mut reader = std:: io:: BufReader :: new ( limited_stream) ;
219
232
220
233
macro_rules! read_line {
221
- ( ) => { read_line!( 0 ) } ;
222
- ( $retry_count: expr) => { {
234
+ ( ) => {
235
+ read_line!( 0 )
236
+ } ;
237
+ ( $retry_count: expr) => { {
223
238
let mut line = String :: new( ) ;
224
239
let mut timeout_count: u64 = 0 ;
225
240
let bytes_read = loop {
@@ -236,7 +251,7 @@ impl HttpClient {
236
251
} else {
237
252
continue ;
238
253
}
239
- }
254
+ } ,
240
255
Err ( e) => return Err ( e) ,
241
256
}
242
257
} ;
@@ -245,29 +260,39 @@ impl HttpClient {
245
260
0 => None ,
246
261
_ => {
247
262
// Remove trailing CRLF
248
- if line. ends_with( '\n' ) { line. pop( ) ; if line. ends_with( '\r' ) { line. pop( ) ; } }
263
+ if line. ends_with( '\n' ) {
264
+ line. pop( ) ;
265
+ if line. ends_with( '\r' ) {
266
+ line. pop( ) ;
267
+ }
268
+ }
249
269
Some ( line)
250
270
} ,
251
271
}
252
- } }
272
+ } } ;
253
273
}
254
274
255
275
// Read and parse status line
256
276
// Note that we allow retrying a few times to reach TCP_STREAM_RESPONSE_TIMEOUT.
257
- let status_line = read_line ! ( TCP_STREAM_RESPONSE_TIMEOUT . as_secs( ) / TCP_STREAM_TIMEOUT . as_secs( ) )
258
- . ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: UnexpectedEof , "no status line" ) ) ?;
277
+ let status_line =
278
+ read_line ! ( TCP_STREAM_RESPONSE_TIMEOUT . as_secs( ) / TCP_STREAM_TIMEOUT . as_secs( ) )
279
+ . ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: UnexpectedEof , "no status line" ) ) ?;
259
280
let status = HttpStatus :: parse ( & status_line) ?;
260
281
261
282
// Read and parse relevant headers
262
283
let mut message_length = HttpMessageLength :: Empty ;
263
284
loop {
264
285
let line = read_line ! ( )
265
286
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: UnexpectedEof , "no headers" ) ) ?;
266
- if line. is_empty ( ) { break ; }
287
+ if line. is_empty ( ) {
288
+ break ;
289
+ }
267
290
268
291
let header = HttpHeader :: parse ( & line) ?;
269
292
if header. has_name ( "Content-Length" ) {
270
- let length = header. value . parse ( )
293
+ let length = header
294
+ . value
295
+ . parse ( )
271
296
. map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , e) ) ?;
272
297
if let HttpMessageLength :: Empty = message_length {
273
298
message_length = HttpMessageLength :: ContentLength ( length) ;
@@ -285,10 +310,13 @@ impl HttpClient {
285
310
let read_limit = MAX_HTTP_MESSAGE_BODY_SIZE - reader. buffer ( ) . len ( ) ;
286
311
reader. get_mut ( ) . set_limit ( read_limit as u64 ) ;
287
312
let contents = match message_length {
288
- HttpMessageLength :: Empty => { Vec :: new ( ) } ,
313
+ HttpMessageLength :: Empty => Vec :: new ( ) ,
289
314
HttpMessageLength :: ContentLength ( length) => {
290
315
if length == 0 || length > MAX_HTTP_MESSAGE_BODY_SIZE {
291
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , format ! ( "invalid response length: {} bytes" , length) ) ) ;
316
+ return Err ( std:: io:: Error :: new (
317
+ std:: io:: ErrorKind :: InvalidData ,
318
+ format ! ( "invalid response length: {} bytes" , length) ,
319
+ ) ) ;
292
320
} else {
293
321
let mut content = vec ! [ 0 ; length] ;
294
322
#[ cfg( feature = "tokio" ) ]
@@ -301,7 +329,9 @@ impl HttpClient {
301
329
HttpMessageLength :: TransferEncoding ( coding) => {
302
330
if !coding. eq_ignore_ascii_case ( "chunked" ) {
303
331
return Err ( std:: io:: Error :: new (
304
- std:: io:: ErrorKind :: InvalidInput , "unsupported transfer coding" ) )
332
+ std:: io:: ErrorKind :: InvalidInput ,
333
+ "unsupported transfer coding" ,
334
+ ) ) ;
305
335
} else {
306
336
let mut content = Vec :: new ( ) ;
307
337
#[ cfg( feature = "tokio" ) ]
@@ -323,7 +353,8 @@ impl HttpClient {
323
353
324
354
// Decode the chunk header to obtain the chunk size.
325
355
let mut buffer = Vec :: new ( ) ;
326
- let mut decoder = chunked_transfer:: Decoder :: new ( chunk_header. as_bytes ( ) ) ;
356
+ let mut decoder =
357
+ chunked_transfer:: Decoder :: new ( chunk_header. as_bytes ( ) ) ;
327
358
decoder. read_to_end ( & mut buffer) ?;
328
359
329
360
// Read the chunk body.
@@ -350,10 +381,7 @@ impl HttpClient {
350
381
351
382
if !status. is_ok ( ) {
352
383
// TODO: Handle 3xx redirection responses.
353
- let error = HttpError {
354
- status_code : status. code . to_string ( ) ,
355
- contents,
356
- } ;
384
+ let error = HttpError { status_code : status. code . to_string ( ) , contents } ;
357
385
return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , error) ) ;
358
386
}
359
387
@@ -391,20 +419,30 @@ impl<'a> HttpStatus<'a> {
391
419
fn parse ( line : & ' a String ) -> std:: io:: Result < HttpStatus < ' a > > {
392
420
let mut tokens = line. splitn ( 3 , ' ' ) ;
393
421
394
- let http_version = tokens. next ( )
422
+ let http_version = tokens
423
+ . next ( )
395
424
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "no HTTP-Version" ) ) ?;
396
- if !http_version. eq_ignore_ascii_case ( "HTTP/1.1" ) &&
397
- !http_version. eq_ignore_ascii_case ( "HTTP/1.0" ) {
398
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "invalid HTTP-Version" ) ) ;
425
+ if !http_version. eq_ignore_ascii_case ( "HTTP/1.1" )
426
+ && !http_version. eq_ignore_ascii_case ( "HTTP/1.0" )
427
+ {
428
+ return Err ( std:: io:: Error :: new (
429
+ std:: io:: ErrorKind :: InvalidData ,
430
+ "invalid HTTP-Version" ,
431
+ ) ) ;
399
432
}
400
433
401
- let code = tokens. next ( )
434
+ let code = tokens
435
+ . next ( )
402
436
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "no Status-Code" ) ) ?;
403
437
if code. len ( ) != 3 || !code. chars ( ) . all ( |c| c. is_ascii_digit ( ) ) {
404
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "invalid Status-Code" ) ) ;
438
+ return Err ( std:: io:: Error :: new (
439
+ std:: io:: ErrorKind :: InvalidData ,
440
+ "invalid Status-Code" ,
441
+ ) ) ;
405
442
}
406
443
407
- let _reason = tokens. next ( )
444
+ let _reason = tokens
445
+ . next ( )
408
446
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "no Reason-Phrase" ) ) ?;
409
447
410
448
Ok ( Self { code } )
@@ -430,9 +468,11 @@ impl<'a> HttpHeader<'a> {
430
468
/// [RFC 7230]: https://tools.ietf.org/html/rfc7230#section-3.2
431
469
fn parse ( line : & ' a String ) -> std:: io:: Result < HttpHeader < ' a > > {
432
470
let mut tokens = line. splitn ( 2 , ':' ) ;
433
- let name = tokens. next ( )
471
+ let name = tokens
472
+ . next ( )
434
473
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "no header name" ) ) ?;
435
- let value = tokens. next ( )
474
+ let value = tokens
475
+ . next ( )
436
476
. ok_or ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "no header value" ) ) ?
437
477
. trim_start ( ) ;
438
478
Ok ( Self { name, value } )
@@ -524,7 +564,7 @@ mod endpoint_tests {
524
564
assert_eq ! ( addr, std_addrs. next( ) . unwrap( ) ) ;
525
565
}
526
566
assert ! ( std_addrs. next( ) . is_none( ) ) ;
527
- }
567
+ } ,
528
568
}
529
569
}
530
570
}
@@ -559,7 +599,11 @@ pub(crate) mod client_tests {
559
599
"{}\r \n \
560
600
Content-Length: {}\r \n \
561
601
\r \n \
562
- {}", status, body. len( ) , body)
602
+ {}",
603
+ status,
604
+ body. len( ) ,
605
+ body
606
+ )
563
607
} ,
564
608
MessageBody :: ChunkedContent ( body) => {
565
609
let mut chuncked_body = Vec :: new ( ) ;
@@ -572,7 +616,10 @@ pub(crate) mod client_tests {
572
616
"{}\r \n \
573
617
Transfer-Encoding: chunked\r \n \
574
618
\r \n \
575
- {}", status, String :: from_utf8( chuncked_body) . unwrap( ) )
619
+ {}",
620
+ status,
621
+ String :: from_utf8( chuncked_body) . unwrap( )
622
+ )
576
623
} ,
577
624
} ;
578
625
HttpServer :: responding_with ( response)
@@ -606,14 +653,20 @@ pub(crate) mod client_tests {
606
653
. lines ( )
607
654
. take_while ( |line| !line. as_ref ( ) . unwrap ( ) . is_empty ( ) )
608
655
. count ( ) ;
609
- if lines_read == 0 { continue ; }
656
+ if lines_read == 0 {
657
+ continue ;
658
+ }
610
659
611
660
for chunk in response. as_bytes ( ) . chunks ( 16 ) {
612
661
if shutdown_signaled. load ( std:: sync:: atomic:: Ordering :: SeqCst ) {
613
662
return ;
614
663
} else {
615
- if let Err ( _) = stream. write ( chunk) { break ; }
616
- if let Err ( _) = stream. flush ( ) { break ; }
664
+ if let Err ( _) = stream. write ( chunk) {
665
+ break ;
666
+ }
667
+ if let Err ( _) = stream. flush ( ) {
668
+ break ;
669
+ }
617
670
}
618
671
}
619
672
}
@@ -636,8 +689,12 @@ pub(crate) mod client_tests {
636
689
fn connect_to_unresolvable_host ( ) {
637
690
match HttpClient :: connect ( ( "example.invalid" , 80 ) ) {
638
691
Err ( e) => {
639
- assert ! ( e. to_string( ) . contains( "failed to lookup address information" ) ||
640
- e. to_string( ) . contains( "No such host" ) , "{:?}" , e) ;
692
+ assert ! (
693
+ e. to_string( ) . contains( "failed to lookup address information" )
694
+ || e. to_string( ) . contains( "No such host" ) ,
695
+ "{:?}" ,
696
+ e
697
+ ) ;
641
698
} ,
642
699
Ok ( _) => panic ! ( "Expected error" ) ,
643
700
}
@@ -705,7 +762,9 @@ pub(crate) mod client_tests {
705
762
let response = format ! (
706
763
"HTTP/1.1 302 Found\r \n \
707
764
Location: {}\r \n \
708
- \r \n ", "Z" . repeat( MAX_HTTP_MESSAGE_HEADER_SIZE ) ) ;
765
+ \r \n ",
766
+ "Z" . repeat( MAX_HTTP_MESSAGE_HEADER_SIZE )
767
+ ) ;
709
768
let server = HttpServer :: responding_with ( response) ;
710
769
711
770
let mut client = HttpClient :: connect ( & server. endpoint ( ) ) . unwrap ( ) ;
@@ -727,7 +786,10 @@ pub(crate) mod client_tests {
727
786
match client. get :: < BinaryResponse > ( "/foo" , "foo.com" ) . await {
728
787
Err ( e) => {
729
788
assert_eq ! ( e. kind( ) , std:: io:: ErrorKind :: InvalidData ) ;
730
- assert_eq ! ( e. get_ref( ) . unwrap( ) . to_string( ) , "invalid response length: 8032001 bytes" ) ;
789
+ assert_eq ! (
790
+ e. get_ref( ) . unwrap( ) . to_string( ) ,
791
+ "invalid response length: 8032001 bytes"
792
+ ) ;
731
793
} ,
732
794
Ok ( _) => panic ! ( "Expected error" ) ,
733
795
}
@@ -740,7 +802,8 @@ pub(crate) mod client_tests {
740
802
"HTTP/1.1 200 OK\r \n \
741
803
Transfer-Encoding: gzip\r \n \
742
804
\r \n \
743
- foobar") ;
805
+ foobar",
806
+ ) ;
744
807
let server = HttpServer :: responding_with ( response) ;
745
808
746
809
let mut client = HttpClient :: connect ( & server. endpoint ( ) ) . unwrap ( ) ;
0 commit comments