@@ -179,6 +179,76 @@ def _build_tunnel_request(host, port, headers):
179
179
return tunnel_request
180
180
181
181
182
+ async def _start_http_request (request , state_machine , conn ):
183
+ """
184
+ Send the request using the given state machine and connection, wait
185
+ for the response headers, and return them.
186
+
187
+ If we get response headers early, then we stop sending and return
188
+ immediately, poisoning the state machine along the way so that we know
189
+ it can't be re-used.
190
+
191
+ This is a standalone function because we use it both to set up both
192
+ CONNECT requests and real requests.
193
+ """
194
+ # Before we begin, confirm that the state machine is ok.
195
+ if (state_machine .our_state is not h11 .IDLE or
196
+ state_machine .their_state is not h11 .IDLE ):
197
+ raise ProtocolError ("Invalid internal state transition" )
198
+
199
+ request_bytes_iterable = _request_bytes_iterable (request , state_machine )
200
+
201
+ send_aborted = True
202
+
203
+ async def next_bytes_to_send ():
204
+ nonlocal send_aborted
205
+ try :
206
+ return next (request_bytes_iterable )
207
+ except StopIteration :
208
+ # We successfully sent the whole body!
209
+ send_aborted = False
210
+ return None
211
+
212
+ h11_response = None
213
+
214
+ def consume_bytes (data ):
215
+ nonlocal h11_response
216
+
217
+ state_machine .receive_data (data )
218
+ while True :
219
+ event = state_machine .next_event ()
220
+ if event is h11 .NEED_DATA :
221
+ break
222
+ elif isinstance (event , h11 .InformationalResponse ):
223
+ # Ignore 1xx responses
224
+ continue
225
+ elif isinstance (event , h11 .Response ):
226
+ # We have our response! Save it and get out of here.
227
+ h11_response = event
228
+ raise LoopAbort
229
+ else :
230
+ # Can't happen
231
+ raise RuntimeError ("Unexpected h11 event {}" .format (event ))
232
+
233
+ await conn .send_and_receive_for_a_while (
234
+ next_bytes_to_send , consume_bytes )
235
+ assert h11_response is not None
236
+
237
+ if send_aborted :
238
+ # Our state machine thinks we sent a bunch of data... but maybe we
239
+ # didn't! Maybe our send got cancelled while we were only half-way
240
+ # through sending the last chunk, and then h11 thinks we sent a
241
+ # complete request and we actually didn't. Then h11 might think we can
242
+ # re-use this connection, even though we can't. So record this in
243
+ # h11's state machine.
244
+ # XX need to implement this in h11
245
+ # state_machine.poison()
246
+ # XX kluge for now
247
+ state_machine ._cstate .process_error (state_machine .our_role )
248
+
249
+ return h11_response
250
+
251
+
182
252
async def _read_until_event (state_machine , conn ):
183
253
"""
184
254
A loop that keeps issuing reads and feeding the data into h11 and
@@ -279,9 +349,10 @@ async def send_request(self, request, read_timeout):
279
349
"""
280
350
Given a Request object, performs the logic required to get a response.
281
351
"""
282
- return await self . _start_http_request (
352
+ h11_response = await _start_http_request (
283
353
request , self ._state_machine , self ._sock
284
354
)
355
+ return _response_from_h11 (h11_response , self )
285
356
286
357
async def _tunnel (self , conn ):
287
358
"""
@@ -296,11 +367,12 @@ async def _tunnel(self, conn):
296
367
297
368
tunnel_state_machine = h11 .Connection (our_role = h11 .CLIENT )
298
369
299
- tunnel_response = await self . _start_http_request (
370
+ h11_response = await _start_http_request (
300
371
tunnel_request , tunnel_state_machine , conn
301
372
)
373
+ tunnel_response = _response_from_h11 (h11_response , self )
302
374
303
- if tunnel_response .status_code != 200 :
375
+ if h11_response .status_code != 200 :
304
376
conn .forceful_close ()
305
377
raise FailedTunnelError (
306
378
"Unable to establish CONNECT tunnel" , tunnel_response
@@ -354,75 +426,6 @@ async def connect(self, ssl_context=None,
354
426
# XX We should pick one of these names and use it consistently...
355
427
self ._sock = conn
356
428
357
- async def _start_http_request (self , request , state_machine , conn ):
358
- """
359
- Send the request using the given state machine and connection, wait
360
- for the response headers, and return them.
361
-
362
- If we get response headers early, then we stop sending and return
363
- immediately, poisoning the state machine along the way so that we know
364
- it can't be re-used.
365
-
366
- This is a standalone function because we use it both to set up both
367
- CONNECT requests and real requests.
368
- """
369
- # Before we begin, confirm that the state machine is ok.
370
- if (state_machine .our_state is not h11 .IDLE or
371
- state_machine .their_state is not h11 .IDLE ):
372
- raise ProtocolError ("Invalid internal state transition" )
373
-
374
- request_bytes_iterable = _request_bytes_iterable (request , state_machine )
375
-
376
- send_aborted = True
377
-
378
- async def next_bytes_to_send ():
379
- nonlocal send_aborted
380
- try :
381
- return next (request_bytes_iterable )
382
- except StopIteration :
383
- # We successfully sent the whole body!
384
- send_aborted = False
385
- return None
386
-
387
- h11_response = None
388
-
389
- def consume_bytes (data ):
390
- nonlocal h11_response
391
-
392
- state_machine .receive_data (data )
393
- while True :
394
- event = state_machine .next_event ()
395
- if event is h11 .NEED_DATA :
396
- break
397
- elif isinstance (event , h11 .InformationalResponse ):
398
- # Ignore 1xx responses
399
- continue
400
- elif isinstance (event , h11 .Response ):
401
- # We have our response! Save it and get out of here.
402
- h11_response = event
403
- raise LoopAbort
404
- else :
405
- # Can't happen
406
- raise RuntimeError ("Unexpected h11 event {}" .format (event ))
407
-
408
- await conn .send_and_receive_for_a_while (
409
- next_bytes_to_send , consume_bytes )
410
- assert h11_response is not None
411
-
412
- if send_aborted :
413
- # Our state machine thinks we sent a bunch of data... but maybe we
414
- # didn't! Maybe our send got cancelled while we were only half-way
415
- # through sending the last chunk, and then h11 thinks we sent a
416
- # complete request and we actually didn't. Then h11 might think we can
417
- # re-use this connection, even though we can't. So record this in
418
- # h11's state machine.
419
- # XX need to implement this in h11
420
- # state_machine.poison()
421
- # XX kluge for now
422
- state_machine ._cstate .process_error (state_machine .our_role )
423
-
424
- return _response_from_h11 (h11_response , self )
425
-
426
429
async def close (self ):
427
430
"""
428
431
Close this connection.
0 commit comments