4
4
5
5
#[ cfg( feature = "proxy" ) ]
6
6
use socks:: Socks5Stream ;
7
- use std:: io:: { BufRead , BufReader , Write } ;
8
- #[ cfg( not( feature = "proxy" ) ) ]
7
+ use std:: io:: { BufRead , BufReader , Read , Write } ;
9
8
use std:: net:: TcpStream ;
10
9
use std:: net:: { SocketAddr , ToSocketAddrs } ;
10
+ use std:: sync:: { Arc , Mutex } ;
11
11
use std:: time:: { Duration , Instant } ;
12
12
use std:: { error, fmt, io, net, thread} ;
13
13
@@ -38,6 +38,7 @@ pub struct SimpleHttpTransport {
38
38
proxy_addr : net:: SocketAddr ,
39
39
#[ cfg( feature = "proxy" ) ]
40
40
proxy_auth : Option < ( String , String ) > ,
41
+ sock : Arc < Mutex < Option < TcpStream > > > ,
41
42
}
42
43
43
44
impl Default for SimpleHttpTransport {
@@ -57,6 +58,7 @@ impl Default for SimpleHttpTransport {
57
58
) ,
58
59
#[ cfg( feature = "proxy" ) ]
59
60
proxy_auth : None ,
61
+ sock : Arc :: new ( Mutex :: new ( None ) ) ,
60
62
}
61
63
}
62
64
}
@@ -73,29 +75,56 @@ impl SimpleHttpTransport {
73
75
}
74
76
75
77
fn request < R > ( & self , req : impl serde:: Serialize ) -> Result < R , Error >
78
+ where
79
+ R : for < ' a > serde:: de:: Deserialize < ' a > ,
80
+ {
81
+ let mut sock = self . sock . lock ( ) . unwrap ( ) ;
82
+ match self . try_request ( req, & mut sock) {
83
+ Ok ( response) => Ok ( response) ,
84
+ Err ( err) => {
85
+ * sock = None ;
86
+ Err ( err)
87
+ }
88
+ }
89
+ }
90
+
91
+ fn try_request < R > (
92
+ & self ,
93
+ req : impl serde:: Serialize ,
94
+ sock : & mut Option < TcpStream > ,
95
+ ) -> Result < R , Error >
76
96
where
77
97
R : for < ' a > serde:: de:: Deserialize < ' a > ,
78
98
{
79
99
// Open connection
80
100
let request_deadline = Instant :: now ( ) + self . timeout ;
81
- #[ cfg( feature = "proxy" ) ]
82
- let mut sock = if let Some ( ( username, password) ) = & self . proxy_auth {
83
- Socks5Stream :: connect_with_password (
84
- self . proxy_addr ,
85
- self . addr ,
86
- username. as_str ( ) ,
87
- password. as_str ( ) ,
88
- ) ?
89
- . into_inner ( )
90
- } else {
91
- Socks5Stream :: connect ( self . proxy_addr , self . addr ) ?. into_inner ( )
92
- } ;
93
-
94
- #[ cfg( not( feature = "proxy" ) ) ]
95
- let mut sock = TcpStream :: connect_timeout ( & self . addr , self . timeout ) ?;
101
+ if sock. is_none ( ) {
102
+ * sock = Some ( {
103
+ #[ cfg( feature = "proxy" ) ]
104
+ {
105
+ if let Some ( ( username, password) ) = & self . proxy_auth {
106
+ Socks5Stream :: connect_with_password (
107
+ self . proxy_addr ,
108
+ self . addr ,
109
+ username. as_str ( ) ,
110
+ password. as_str ( ) ,
111
+ ) ?
112
+ . into_inner ( )
113
+ } else {
114
+ Socks5Stream :: connect ( self . proxy_addr , self . addr ) ?. into_inner ( )
115
+ }
116
+ }
96
117
97
- sock. set_read_timeout ( Some ( self . timeout ) ) ?;
98
- sock. set_write_timeout ( Some ( self . timeout ) ) ?;
118
+ #[ cfg( not( feature = "proxy" ) ) ]
119
+ {
120
+ let stream = TcpStream :: connect_timeout ( & self . addr , self . timeout ) ?;
121
+ stream. set_read_timeout ( Some ( self . timeout ) ) ?;
122
+ stream. set_write_timeout ( Some ( self . timeout ) ) ?;
123
+ stream
124
+ }
125
+ } )
126
+ } ;
127
+ let sock = sock. as_mut ( ) . unwrap ( ) ;
99
128
100
129
// Serialize the body first so we can set the Content-Length header.
101
130
let body = serde_json:: to_vec ( & req) ?;
@@ -105,7 +134,6 @@ impl SimpleHttpTransport {
105
134
sock. write_all ( self . path . as_bytes ( ) ) ?;
106
135
sock. write_all ( b" HTTP/1.1\r \n " ) ?;
107
136
// Write headers
108
- sock. write_all ( b"Connection: Close\r \n " ) ?;
109
137
sock. write_all ( b"Content-Type: application/json\r \n " ) ?;
110
138
sock. write_all ( b"Content-Length: " ) ?;
111
139
sock. write_all ( body. len ( ) . to_string ( ) . as_bytes ( ) ) ?;
@@ -133,18 +161,39 @@ impl SimpleHttpTransport {
133
161
Err ( _) => return Err ( Error :: HttpParseError ) ,
134
162
} ;
135
163
136
- // Skip response header fields
137
- while get_line ( & mut reader, request_deadline) ? != "\r \n " { }
164
+ // Parse response header fields
165
+ let mut content_length = None ;
166
+ loop {
167
+ let line = get_line ( & mut reader, request_deadline) ?;
168
+
169
+ if line == "\r \n " {
170
+ break ;
171
+ }
172
+
173
+ const CONTENT_LENGTH : & str = "content-length: " ;
174
+ if line. to_lowercase ( ) . starts_with ( CONTENT_LENGTH ) {
175
+ content_length = Some (
176
+ line[ CONTENT_LENGTH . len ( ) ..]
177
+ . trim ( )
178
+ . parse :: < usize > ( )
179
+ . map_err ( |_| Error :: HttpParseError ) ?,
180
+ ) ;
181
+ }
182
+ }
138
183
139
184
if response_code == 401 {
140
185
// There is no body in a 401 response, so don't try to read it
141
186
return Err ( Error :: HttpErrorCode ( response_code) ) ;
142
187
}
143
188
189
+ let content_length = content_length. ok_or ( Error :: HttpParseError ) ?;
190
+
191
+ let mut buffer = vec ! [ 0 ; content_length] ;
192
+
144
193
// Even if it's != 200, we parse the response as we may get a JSONRPC error instead
145
194
// of the less meaningful HTTP error code.
146
- let resp_body = get_lines ( & mut reader ) ?;
147
- match serde_json:: from_str ( & resp_body ) {
195
+ reader . read_exact ( & mut buffer ) ?;
196
+ match serde_json:: from_slice ( & buffer ) {
148
197
Ok ( s) => Ok ( s) ,
149
198
Err ( e) => {
150
199
if response_code != 200 {
@@ -261,23 +310,6 @@ fn get_line<R: BufRead>(reader: &mut R, deadline: Instant) -> Result<String, Err
261
310
Err ( Error :: Timeout )
262
311
}
263
312
264
- /// Read all lines from a buffered reader.
265
- fn get_lines < R : BufRead > ( reader : & mut R ) -> Result < String , Error > {
266
- let mut body: String = String :: new ( ) ;
267
-
268
- for line in reader. lines ( ) {
269
- match line {
270
- Ok ( l) => body. push_str ( & l) ,
271
- // io error occurred, abort
272
- Err ( e) => return Err ( Error :: SocketError ( e) ) ,
273
- }
274
- }
275
- // remove whitespace
276
- body. retain ( |c| !c. is_whitespace ( ) ) ;
277
-
278
- Ok ( body)
279
- }
280
-
281
313
/// Do some very basic manual URL parsing because the uri/url crates
282
314
/// all have unicode-normalization as a dependency and that's broken.
283
315
fn check_url ( url : & str ) -> Result < ( SocketAddr , String ) , Error > {
0 commit comments