|
@@
-36,6
+36,7
b' httplib, but has several additional feat'
|
|
36
|
* notices when the server responds early to a request
|
|
36
|
* notices when the server responds early to a request
|
|
37
|
* implements ssl inline instead of in a different class
|
|
37
|
* implements ssl inline instead of in a different class
|
|
38
|
"""
|
|
38
|
"""
|
|
|
|
|
39
|
from __future__ import absolute_import
|
|
39
|
|
|
40
|
|
|
40
|
# Many functions in this file have too many arguments.
|
|
41
|
# Many functions in this file have too many arguments.
|
|
41
|
# pylint: disable=R0913
|
|
42
|
# pylint: disable=R0913
|
|
@@
-48,8
+49,10
b' import rfc822'
|
|
48
|
import select
|
|
49
|
import select
|
|
49
|
import socket
|
|
50
|
import socket
|
|
50
|
|
|
51
|
|
|
51
|
import _readers
|
|
52
|
from . import (
|
|
52
|
import socketutil
|
|
53
|
_readers,
|
|
|
|
|
54
|
socketutil,
|
|
|
|
|
55
|
)
|
|
53
|
|
|
56
|
|
|
54
|
logger = logging.getLogger(__name__)
|
|
57
|
logger = logging.getLogger(__name__)
|
|
55
|
|
|
58
|
|
|
@@
-124,6
+127,12
b' class HTTPResponse(object):'
|
|
124
|
# pylint: disable=W0212
|
|
127
|
# pylint: disable=W0212
|
|
125
|
self._reader._close()
|
|
128
|
self._reader._close()
|
|
126
|
|
|
129
|
|
|
|
|
|
130
|
def getheader(self, header, default=None):
|
|
|
|
|
131
|
return self.headers.getheader(header, default=default)
|
|
|
|
|
132
|
|
|
|
|
|
133
|
def getheaders(self):
|
|
|
|
|
134
|
return self.headers.items()
|
|
|
|
|
135
|
|
|
127
|
def readline(self):
|
|
136
|
def readline(self):
|
|
128
|
"""Read a single line from the response body.
|
|
137
|
"""Read a single line from the response body.
|
|
129
|
|
|
138
|
|
|
@@
-279,6
+288,14
b' class HTTPResponse(object):'
|
|
279
|
# pylint: disable=W0212
|
|
288
|
# pylint: disable=W0212
|
|
280
|
self._load_response = self._reader._load
|
|
289
|
self._load_response = self._reader._load
|
|
281
|
|
|
290
|
|
|
|
|
|
291
|
def _foldheaders(headers):
|
|
|
|
|
292
|
"""Given some headers, rework them so we can safely overwrite values.
|
|
|
|
|
293
|
|
|
|
|
|
294
|
>>> _foldheaders({'Accept-Encoding': 'wat'})
|
|
|
|
|
295
|
{'accept-encoding': ('Accept-Encoding', 'wat')}
|
|
|
|
|
296
|
"""
|
|
|
|
|
297
|
return dict((k.lower(), (k, v)) for k, v in headers.iteritems())
|
|
|
|
|
298
|
|
|
282
|
|
|
299
|
|
|
283
|
class HTTPConnection(object):
|
|
300
|
class HTTPConnection(object):
|
|
284
|
"""Connection to a single http server.
|
|
301
|
"""Connection to a single http server.
|
|
@@
-292,7
+309,8
b' class HTTPConnection(object):'
|
|
292
|
def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
|
|
309
|
def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
|
|
293
|
timeout=TIMEOUT_DEFAULT,
|
|
310
|
timeout=TIMEOUT_DEFAULT,
|
|
294
|
continue_timeout=TIMEOUT_ASSUME_CONTINUE,
|
|
311
|
continue_timeout=TIMEOUT_ASSUME_CONTINUE,
|
|
295
|
proxy_hostport=None, ssl_wrap_socket=None, **ssl_opts):
|
|
312
|
proxy_hostport=None, proxy_headers=None,
|
|
|
|
|
313
|
ssl_wrap_socket=None, **ssl_opts):
|
|
296
|
"""Create a new HTTPConnection.
|
|
314
|
"""Create a new HTTPConnection.
|
|
297
|
|
|
315
|
|
|
298
|
Args:
|
|
316
|
Args:
|
|
@@
-307,6
+325,13
b' class HTTPConnection(object):'
|
|
307
|
"100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
|
|
325
|
"100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
|
|
308
|
proxy_hostport: Optional. Tuple of (host, port) to use as an http
|
|
326
|
proxy_hostport: Optional. Tuple of (host, port) to use as an http
|
|
309
|
proxy for the connection. Default is to not use a proxy.
|
|
327
|
proxy for the connection. Default is to not use a proxy.
|
|
|
|
|
328
|
proxy_headers: Optional dict of header keys and values to send to
|
|
|
|
|
329
|
a proxy when using CONNECT. For compatibility with
|
|
|
|
|
330
|
httplib, the Proxy-Authorization header may be
|
|
|
|
|
331
|
specified in headers for request(), which will clobber
|
|
|
|
|
332
|
any such header specified here if specified. Providing
|
|
|
|
|
333
|
this option and not proxy_hostport will raise an
|
|
|
|
|
334
|
ValueError.
|
|
310
|
ssl_wrap_socket: Optional function to use for wrapping
|
|
335
|
ssl_wrap_socket: Optional function to use for wrapping
|
|
311
|
sockets. If unspecified, the one from the ssl module will
|
|
336
|
sockets. If unspecified, the one from the ssl module will
|
|
312
|
be used if available, or something that's compatible with
|
|
337
|
be used if available, or something that's compatible with
|
|
@@
-330,10
+355,7
b' class HTTPConnection(object):'
|
|
330
|
elif use_ssl is None:
|
|
355
|
elif use_ssl is None:
|
|
331
|
use_ssl = (port == 443)
|
|
356
|
use_ssl = (port == 443)
|
|
332
|
elif port is None:
|
|
357
|
elif port is None:
|
|
333
|
if use_ssl:
|
|
358
|
port = (use_ssl and 443 or 80)
|
|
334
|
port = 443
|
|
|
|
|
335
|
else:
|
|
|
|
|
336
|
port = 80
|
|
|
|
|
337
|
self.port = port
|
|
359
|
self.port = port
|
|
338
|
if use_ssl and not socketutil.have_ssl:
|
|
360
|
if use_ssl and not socketutil.have_ssl:
|
|
339
|
raise Exception('ssl requested but unavailable on this Python')
|
|
361
|
raise Exception('ssl requested but unavailable on this Python')
|
|
@@
-346,13
+368,20
b' class HTTPConnection(object):'
|
|
346
|
self._current_response_taken = False
|
|
368
|
self._current_response_taken = False
|
|
347
|
if proxy_hostport is None:
|
|
369
|
if proxy_hostport is None:
|
|
348
|
self._proxy_host = self._proxy_port = None
|
|
370
|
self._proxy_host = self._proxy_port = None
|
|
|
|
|
371
|
if proxy_headers:
|
|
|
|
|
372
|
raise ValueError(
|
|
|
|
|
373
|
'proxy_headers may not be specified unless '
|
|
|
|
|
374
|
'proxy_hostport is also specified.')
|
|
|
|
|
375
|
else:
|
|
|
|
|
376
|
self._proxy_headers = {}
|
|
349
|
else:
|
|
377
|
else:
|
|
350
|
self._proxy_host, self._proxy_port = proxy_hostport
|
|
378
|
self._proxy_host, self._proxy_port = proxy_hostport
|
|
|
|
|
379
|
self._proxy_headers = _foldheaders(proxy_headers or {})
|
|
351
|
|
|
380
|
|
|
352
|
self.timeout = timeout
|
|
381
|
self.timeout = timeout
|
|
353
|
self.continue_timeout = continue_timeout
|
|
382
|
self.continue_timeout = continue_timeout
|
|
354
|
|
|
383
|
|
|
355
|
def _connect(self):
|
|
384
|
def _connect(self, proxy_headers):
|
|
356
|
"""Connect to the host and port specified in __init__."""
|
|
385
|
"""Connect to the host and port specified in __init__."""
|
|
357
|
if self.sock:
|
|
386
|
if self.sock:
|
|
358
|
return
|
|
387
|
return
|
|
@@
-362,10
+391,9
b' class HTTPConnection(object):'
|
|
362
|
sock = socketutil.create_connection((self._proxy_host,
|
|
391
|
sock = socketutil.create_connection((self._proxy_host,
|
|
363
|
self._proxy_port))
|
|
392
|
self._proxy_port))
|
|
364
|
if self.ssl:
|
|
393
|
if self.ssl:
|
|
365
|
# TODO proxy header support
|
|
|
|
|
366
|
data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
|
|
394
|
data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
|
|
367
|
self.port),
|
|
395
|
self.port),
|
|
368
|
{}, HTTP_VER_1_0)
|
|
396
|
proxy_headers, HTTP_VER_1_0)
|
|
369
|
sock.send(data)
|
|
397
|
sock.send(data)
|
|
370
|
sock.setblocking(0)
|
|
398
|
sock.setblocking(0)
|
|
371
|
r = self.response_class(sock, self.timeout, 'CONNECT')
|
|
399
|
r = self.response_class(sock, self.timeout, 'CONNECT')
|
|
@@
-468,10
+496,10
b' class HTTPConnection(object):'
|
|
468
|
return True
|
|
496
|
return True
|
|
469
|
return False
|
|
497
|
return False
|
|
470
|
|
|
498
|
|
|
471
|
def _reconnect(self, where):
|
|
499
|
def _reconnect(self, where, pheaders):
|
|
472
|
logger.info('reconnecting during %s', where)
|
|
500
|
logger.info('reconnecting during %s', where)
|
|
473
|
self.close()
|
|
501
|
self.close()
|
|
474
|
self._connect()
|
|
502
|
self._connect(pheaders)
|
|
475
|
|
|
503
|
|
|
476
|
def request(self, method, path, body=None, headers={},
|
|
504
|
def request(self, method, path, body=None, headers={},
|
|
477
|
expect_continue=False):
|
|
505
|
expect_continue=False):
|
|
@@
-492,11
+520,20
b' class HTTPConnection(object):'
|
|
492
|
|
|
520
|
|
|
493
|
logger.info('sending %s request for %s to %s on port %s',
|
|
521
|
logger.info('sending %s request for %s to %s on port %s',
|
|
494
|
method, path, self.host, self.port)
|
|
522
|
method, path, self.host, self.port)
|
|
495
|
hdrs = dict((k.lower(), (k, v)) for k, v in headers.iteritems())
|
|
523
|
hdrs = _foldheaders(headers)
|
|
496
|
if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
|
|
524
|
if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
|
|
497
|
expect_continue = True
|
|
525
|
expect_continue = True
|
|
498
|
elif expect_continue:
|
|
526
|
elif expect_continue:
|
|
499
|
hdrs['expect'] = ('Expect', '100-Continue')
|
|
527
|
hdrs['expect'] = ('Expect', '100-Continue')
|
|
|
|
|
528
|
# httplib compatibility: if the user specified a
|
|
|
|
|
529
|
# proxy-authorization header, that's actually intended for a
|
|
|
|
|
530
|
# proxy CONNECT action, not the real request, but only if
|
|
|
|
|
531
|
# we're going to use a proxy.
|
|
|
|
|
532
|
pheaders = dict(self._proxy_headers)
|
|
|
|
|
533
|
if self._proxy_host and self.ssl:
|
|
|
|
|
534
|
pa = hdrs.pop('proxy-authorization', None)
|
|
|
|
|
535
|
if pa is not None:
|
|
|
|
|
536
|
pheaders['proxy-authorization'] = pa
|
|
500
|
|
|
537
|
|
|
501
|
chunked = False
|
|
538
|
chunked = False
|
|
502
|
if body and HDR_CONTENT_LENGTH not in hdrs:
|
|
539
|
if body and HDR_CONTENT_LENGTH not in hdrs:
|
|
@@
-513,7
+550,7
b' class HTTPConnection(object):'
|
|
513
|
# conditions where we'll want to retry, so make a note of the
|
|
550
|
# conditions where we'll want to retry, so make a note of the
|
|
514
|
# state of self.sock
|
|
551
|
# state of self.sock
|
|
515
|
fresh_socket = self.sock is None
|
|
552
|
fresh_socket = self.sock is None
|
|
516
|
self._connect()
|
|
553
|
self._connect(pheaders)
|
|
517
|
outgoing_headers = self._buildheaders(
|
|
554
|
outgoing_headers = self._buildheaders(
|
|
518
|
method, path, hdrs, self.http_version)
|
|
555
|
method, path, hdrs, self.http_version)
|
|
519
|
response = None
|
|
556
|
response = None
|
|
@@
-588,7
+625,7
b' class HTTPConnection(object):'
|
|
588
|
logger.info(
|
|
625
|
logger.info(
|
|
589
|
'Connection appeared closed in read on first'
|
|
626
|
'Connection appeared closed in read on first'
|
|
590
|
' request loop iteration, will retry.')
|
|
627
|
' request loop iteration, will retry.')
|
|
591
|
self._reconnect('read')
|
|
628
|
self._reconnect('read', pheaders)
|
|
592
|
continue
|
|
629
|
continue
|
|
593
|
else:
|
|
630
|
else:
|
|
594
|
# We didn't just send the first data hunk,
|
|
631
|
# We didn't just send the first data hunk,
|
|
@@
-645,7
+682,7
b' class HTTPConnection(object):'
|
|
645
|
elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
|
|
682
|
elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
|
|
646
|
and not first):
|
|
683
|
and not first):
|
|
647
|
raise
|
|
684
|
raise
|
|
648
|
self._reconnect('write')
|
|
685
|
self._reconnect('write', pheaders)
|
|
649
|
amt = self.sock.send(out)
|
|
686
|
amt = self.sock.send(out)
|
|
650
|
logger.debug('sent %d', amt)
|
|
687
|
logger.debug('sent %d', amt)
|
|
651
|
first = False
|
|
688
|
first = False
|
|
@@
-664,8
+701,8
b' class HTTPConnection(object):'
|
|
664
|
# data at all, and in all probability the socket was
|
|
701
|
# data at all, and in all probability the socket was
|
|
665
|
# closed before the server even saw our request. Try
|
|
702
|
# closed before the server even saw our request. Try
|
|
666
|
# the request again on a fresh socket.
|
|
703
|
# the request again on a fresh socket.
|
|
667
|
logging.debug('response._select() failed during request().'
|
|
704
|
logger.debug('response._select() failed during request().'
|
|
668
|
' Assuming request needs to be retried.')
|
|
705
|
' Assuming request needs to be retried.')
|
|
669
|
self.sock = None
|
|
706
|
self.sock = None
|
|
670
|
# Call this method explicitly to re-try the
|
|
707
|
# Call this method explicitly to re-try the
|
|
671
|
# request. We don't use self.request() because
|
|
708
|
# request. We don't use self.request() because
|