Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: third_party/google-endpoints/future/backports/http/client.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 """HTTP/1.1 client library
2
3 A backport of the Python 3.3 http/client.py module for python-future.
4
5 <intro stuff goes here>
6 <other stuff, too>
7
8 HTTPConnection goes through a number of "states", which define when a client
9 may legally make another request or fetch the response for a particular
10 request. This diagram details these state transitions:
11
12 (null)
13 |
14 | HTTPConnection()
15 v
16 Idle
17 |
18 | putrequest()
19 v
20 Request-started
21 |
22 | ( putheader() )* endheaders()
23 v
24 Request-sent
25 |
26 | response = getresponse()
27 v
28 Unread-response [Response-headers-read]
29 |\____________________
30 | |
31 | response.read() | putrequest()
32 v v
33 Idle Req-started-unread-response
34 ______/|
35 / |
36 response.read() | | ( putheader() )* endheaders()
37 v v
38 Request-started Req-sent-unread-response
39 |
40 | response.read()
41 v
42 Request-sent
43
44 This diagram presents the following rules:
45 -- a second request may not be started until {response-headers-read}
46 -- a response [object] cannot be retrieved until {request-sent}
47 -- there is no differentiation between an unread response body and a
48 partially read response body
49
50 Note: this enforcement is applied by the HTTPConnection class. The
51 HTTPResponse class does not enforce this state machine, which
52 implies sophisticated clients may accelerate the request/response
53 pipeline. Caution should be taken, though: accelerating the states
54 beyond the above pattern may imply knowledge of the server's
55 connection-close behavior for certain requests. For example, it
56 is impossible to tell whether the server will close the connection
57 UNTIL the response headers have been read; this means that further
58 requests cannot be placed into the pipeline until it is known that
59 the server will NOT be closing the connection.
60
61 Logical State __state __response
62 ------------- ------- ----------
63 Idle _CS_IDLE None
64 Request-started _CS_REQ_STARTED None
65 Request-sent _CS_REQ_SENT None
66 Unread-response _CS_IDLE <response_class>
67 Req-started-unread-response _CS_REQ_STARTED <response_class>
68 Req-sent-unread-response _CS_REQ_SENT <response_class>
69 """
70
71 from __future__ import (absolute_import, division,
72 print_function, unicode_literals)
73 from future.builtins import bytes, int, str, super
74 from future.utils import PY2
75
76 from future.backports.email import parser as email_parser
77 from future.backports.email import message as email_message
78 from future.backports.misc import create_connection as socket_create_connection
79 import io
80 import os
81 import socket
82 import collections
83 from future.backports.urllib.parse import urlsplit
84 import warnings
85 from array import array
86
87 __all__ = ["HTTPResponse", "HTTPConnection",
88 "HTTPException", "NotConnected", "UnknownProtocol",
89 "UnknownTransferEncoding", "UnimplementedFileMode",
90 "IncompleteRead", "InvalidURL", "ImproperConnectionState",
91 "CannotSendRequest", "CannotSendHeader", "ResponseNotReady",
92 "BadStatusLine", "error", "responses"]
93
94 HTTP_PORT = 80
95 HTTPS_PORT = 443
96
97 _UNKNOWN = 'UNKNOWN'
98
99 # connection states
100 _CS_IDLE = 'Idle'
101 _CS_REQ_STARTED = 'Request-started'
102 _CS_REQ_SENT = 'Request-sent'
103
104 # status codes
105 # informational
106 CONTINUE = 100
107 SWITCHING_PROTOCOLS = 101
108 PROCESSING = 102
109
110 # successful
111 OK = 200
112 CREATED = 201
113 ACCEPTED = 202
114 NON_AUTHORITATIVE_INFORMATION = 203
115 NO_CONTENT = 204
116 RESET_CONTENT = 205
117 PARTIAL_CONTENT = 206
118 MULTI_STATUS = 207
119 IM_USED = 226
120
121 # redirection
122 MULTIPLE_CHOICES = 300
123 MOVED_PERMANENTLY = 301
124 FOUND = 302
125 SEE_OTHER = 303
126 NOT_MODIFIED = 304
127 USE_PROXY = 305
128 TEMPORARY_REDIRECT = 307
129
130 # client error
131 BAD_REQUEST = 400
132 UNAUTHORIZED = 401
133 PAYMENT_REQUIRED = 402
134 FORBIDDEN = 403
135 NOT_FOUND = 404
136 METHOD_NOT_ALLOWED = 405
137 NOT_ACCEPTABLE = 406
138 PROXY_AUTHENTICATION_REQUIRED = 407
139 REQUEST_TIMEOUT = 408
140 CONFLICT = 409
141 GONE = 410
142 LENGTH_REQUIRED = 411
143 PRECONDITION_FAILED = 412
144 REQUEST_ENTITY_TOO_LARGE = 413
145 REQUEST_URI_TOO_LONG = 414
146 UNSUPPORTED_MEDIA_TYPE = 415
147 REQUESTED_RANGE_NOT_SATISFIABLE = 416
148 EXPECTATION_FAILED = 417
149 UNPROCESSABLE_ENTITY = 422
150 LOCKED = 423
151 FAILED_DEPENDENCY = 424
152 UPGRADE_REQUIRED = 426
153 PRECONDITION_REQUIRED = 428
154 TOO_MANY_REQUESTS = 429
155 REQUEST_HEADER_FIELDS_TOO_LARGE = 431
156
157 # server error
158 INTERNAL_SERVER_ERROR = 500
159 NOT_IMPLEMENTED = 501
160 BAD_GATEWAY = 502
161 SERVICE_UNAVAILABLE = 503
162 GATEWAY_TIMEOUT = 504
163 HTTP_VERSION_NOT_SUPPORTED = 505
164 INSUFFICIENT_STORAGE = 507
165 NOT_EXTENDED = 510
166 NETWORK_AUTHENTICATION_REQUIRED = 511
167
168 # Mapping status codes to official W3C names
169 responses = {
170 100: 'Continue',
171 101: 'Switching Protocols',
172
173 200: 'OK',
174 201: 'Created',
175 202: 'Accepted',
176 203: 'Non-Authoritative Information',
177 204: 'No Content',
178 205: 'Reset Content',
179 206: 'Partial Content',
180
181 300: 'Multiple Choices',
182 301: 'Moved Permanently',
183 302: 'Found',
184 303: 'See Other',
185 304: 'Not Modified',
186 305: 'Use Proxy',
187 306: '(Unused)',
188 307: 'Temporary Redirect',
189
190 400: 'Bad Request',
191 401: 'Unauthorized',
192 402: 'Payment Required',
193 403: 'Forbidden',
194 404: 'Not Found',
195 405: 'Method Not Allowed',
196 406: 'Not Acceptable',
197 407: 'Proxy Authentication Required',
198 408: 'Request Timeout',
199 409: 'Conflict',
200 410: 'Gone',
201 411: 'Length Required',
202 412: 'Precondition Failed',
203 413: 'Request Entity Too Large',
204 414: 'Request-URI Too Long',
205 415: 'Unsupported Media Type',
206 416: 'Requested Range Not Satisfiable',
207 417: 'Expectation Failed',
208 428: 'Precondition Required',
209 429: 'Too Many Requests',
210 431: 'Request Header Fields Too Large',
211
212 500: 'Internal Server Error',
213 501: 'Not Implemented',
214 502: 'Bad Gateway',
215 503: 'Service Unavailable',
216 504: 'Gateway Timeout',
217 505: 'HTTP Version Not Supported',
218 511: 'Network Authentication Required',
219 }
220
221 # maximal amount of data to read at one time in _safe_read
222 MAXAMOUNT = 1048576
223
224 # maximal line length when calling readline().
225 _MAXLINE = 65536
226 _MAXHEADERS = 100
227
228
229 class HTTPMessage(email_message.Message):
230 # XXX The only usage of this method is in
231 # http.server.CGIHTTPRequestHandler. Maybe move the code there so
232 # that it doesn't need to be part of the public API. The API has
233 # never been defined so this could cause backwards compatibility
234 # issues.
235
236 def getallmatchingheaders(self, name):
237 """Find all header lines matching a given header name.
238
239 Look through the list of headers and find all lines matching a given
240 header name (and their continuation lines). A list of the lines is
241 returned, without interpretation. If the header does not occur, an
242 empty list is returned. If the header occurs multiple times, all
243 occurrences are returned. Case is not important in the header name.
244
245 """
246 name = name.lower() + ':'
247 n = len(name)
248 lst = []
249 hit = 0
250 for line in self.keys():
251 if line[:n].lower() == name:
252 hit = 1
253 elif not line[:1].isspace():
254 hit = 0
255 if hit:
256 lst.append(line)
257 return lst
258
259 def parse_headers(fp, _class=HTTPMessage):
260 """Parses only RFC2822 headers from a file pointer.
261
262 email Parser wants to see strings rather than bytes.
263 But a TextIOWrapper around self.rfile would buffer too many bytes
264 from the stream, bytes which we later need to read as bytes.
265 So we read the correct bytes here, as bytes, for email Parser
266 to parse.
267
268 """
269 headers = []
270 while True:
271 line = fp.readline(_MAXLINE + 1)
272 if len(line) > _MAXLINE:
273 raise LineTooLong("header line")
274 headers.append(line)
275 if len(headers) > _MAXHEADERS:
276 raise HTTPException("got more than %d headers" % _MAXHEADERS)
277 if line in (b'\r\n', b'\n', b''):
278 break
279 hstring = bytes(b'').join(headers).decode('iso-8859-1')
280 return email_parser.Parser(_class=_class).parsestr(hstring)
281
282
283 _strict_sentinel = object()
284
285 class HTTPResponse(io.RawIOBase):
286
287 # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details.
288
289 # The bytes from the socket object are iso-8859-1 strings.
290 # See RFC 2616 sec 2.2 which notes an exception for MIME-encoded
291 # text following RFC 2047. The basic status line parsing only
292 # accepts iso-8859-1.
293
294 def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url=None):
295 # If the response includes a content-length header, we need to
296 # make sure that the client doesn't read more than the
297 # specified number of bytes. If it does, it will block until
298 # the server times out and closes the connection. This will
299 # happen if a self.fp.read() is done (without a size) whether
300 # self.fp is buffered or not. So, no self.fp.read() by
301 # clients unless they know what they are doing.
302 self.fp = sock.makefile("rb")
303 self.debuglevel = debuglevel
304 if strict is not _strict_sentinel:
305 warnings.warn("the 'strict' argument isn't supported anymore; "
306 "http.client now always assumes HTTP/1.x compliant servers.",
307 DeprecationWarning, 2)
308 self._method = method
309
310 # The HTTPResponse object is returned via urllib. The clients
311 # of http and urllib expect different attributes for the
312 # headers. headers is used here and supports urllib. msg is
313 # provided as a backwards compatibility layer for http
314 # clients.
315
316 self.headers = self.msg = None
317
318 # from the Status-Line of the response
319 self.version = _UNKNOWN # HTTP-Version
320 self.status = _UNKNOWN # Status-Code
321 self.reason = _UNKNOWN # Reason-Phrase
322
323 self.chunked = _UNKNOWN # is "chunked" being used?
324 self.chunk_left = _UNKNOWN # bytes left to read in current chunk
325 self.length = _UNKNOWN # number of bytes left in response
326 self.will_close = _UNKNOWN # conn will close at end of response
327
328 def _read_status(self):
329 line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
330 if len(line) > _MAXLINE:
331 raise LineTooLong("status line")
332 if self.debuglevel > 0:
333 print("reply:", repr(line))
334 if not line:
335 # Presumably, the server closed the connection before
336 # sending a valid response.
337 raise BadStatusLine(line)
338 try:
339 version, status, reason = line.split(None, 2)
340 except ValueError:
341 try:
342 version, status = line.split(None, 1)
343 reason = ""
344 except ValueError:
345 # empty version will cause next test to fail.
346 version = ""
347 if not version.startswith("HTTP/"):
348 self._close_conn()
349 raise BadStatusLine(line)
350
351 # The status code is a three-digit number
352 try:
353 status = int(status)
354 if status < 100 or status > 999:
355 raise BadStatusLine(line)
356 except ValueError:
357 raise BadStatusLine(line)
358 return version, status, reason
359
360 def begin(self):
361 if self.headers is not None:
362 # we've already started reading the response
363 return
364
365 # read until we get a non-100 response
366 while True:
367 version, status, reason = self._read_status()
368 if status != CONTINUE:
369 break
370 # skip the header from the 100 response
371 while True:
372 skip = self.fp.readline(_MAXLINE + 1)
373 if len(skip) > _MAXLINE:
374 raise LineTooLong("header line")
375 skip = skip.strip()
376 if not skip:
377 break
378 if self.debuglevel > 0:
379 print("header:", skip)
380
381 self.code = self.status = status
382 self.reason = reason.strip()
383 if version in ("HTTP/1.0", "HTTP/0.9"):
384 # Some servers might still return "0.9", treat it as 1.0 anyway
385 self.version = 10
386 elif version.startswith("HTTP/1."):
387 self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1
388 else:
389 raise UnknownProtocol(version)
390
391 self.headers = self.msg = parse_headers(self.fp)
392
393 if self.debuglevel > 0:
394 for hdr in self.headers:
395 print("header:", hdr, end=" ")
396
397 # are we using the chunked-style of transfer encoding?
398 tr_enc = self.headers.get("transfer-encoding")
399 if tr_enc and tr_enc.lower() == "chunked":
400 self.chunked = True
401 self.chunk_left = None
402 else:
403 self.chunked = False
404
405 # will the connection close at the end of the response?
406 self.will_close = self._check_close()
407
408 # do we have a Content-Length?
409 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
410 self.length = None
411 length = self.headers.get("content-length")
412
413 # are we using the chunked-style of transfer encoding?
414 tr_enc = self.headers.get("transfer-encoding")
415 if length and not self.chunked:
416 try:
417 self.length = int(length)
418 except ValueError:
419 self.length = None
420 else:
421 if self.length < 0: # ignore nonsensical negative lengths
422 self.length = None
423 else:
424 self.length = None
425
426 # does the body have a fixed length? (of zero)
427 if (status == NO_CONTENT or status == NOT_MODIFIED or
428 100 <= status < 200 or # 1xx codes
429 self._method == "HEAD"):
430 self.length = 0
431
432 # if the connection remains open, and we aren't using chunked, and
433 # a content-length was not provided, then assume that the connection
434 # WILL close.
435 if (not self.will_close and
436 not self.chunked and
437 self.length is None):
438 self.will_close = True
439
440 def _check_close(self):
441 conn = self.headers.get("connection")
442 if self.version == 11:
443 # An HTTP/1.1 proxy is assumed to stay open unless
444 # explicitly closed.
445 conn = self.headers.get("connection")
446 if conn and "close" in conn.lower():
447 return True
448 return False
449
450 # Some HTTP/1.0 implementations have support for persistent
451 # connections, using rules different than HTTP/1.1.
452
453 # For older HTTP, Keep-Alive indicates persistent connection.
454 if self.headers.get("keep-alive"):
455 return False
456
457 # At least Akamai returns a "Connection: Keep-Alive" header,
458 # which was supposed to be sent by the client.
459 if conn and "keep-alive" in conn.lower():
460 return False
461
462 # Proxy-Connection is a netscape hack.
463 pconn = self.headers.get("proxy-connection")
464 if pconn and "keep-alive" in pconn.lower():
465 return False
466
467 # otherwise, assume it will close
468 return True
469
470 def _close_conn(self):
471 fp = self.fp
472 self.fp = None
473 fp.close()
474
475 def close(self):
476 super().close() # set "closed" flag
477 if self.fp:
478 self._close_conn()
479
480 # These implementations are for the benefit of io.BufferedReader.
481
482 # XXX This class should probably be revised to act more like
483 # the "raw stream" that BufferedReader expects.
484
485 def flush(self):
486 super().flush()
487 if self.fp:
488 self.fp.flush()
489
490 def readable(self):
491 return True
492
493 # End of "raw stream" methods
494
495 def isclosed(self):
496 """True if the connection is closed."""
497 # NOTE: it is possible that we will not ever call self.close(). This
498 # case occurs when will_close is TRUE, length is None, and we
499 # read up to the last byte, but NOT past it.
500 #
501 # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be
502 # called, meaning self.isclosed() is meaningful.
503 return self.fp is None
504
505 def read(self, amt=None):
506 if self.fp is None:
507 return bytes(b"")
508
509 if self._method == "HEAD":
510 self._close_conn()
511 return bytes(b"")
512
513 if amt is not None:
514 # Amount is given, so call base class version
515 # (which is implemented in terms of self.readinto)
516 return bytes(super(HTTPResponse, self).read(amt))
517 else:
518 # Amount is not given (unbounded read) so we must check self.length
519 # and self.chunked
520
521 if self.chunked:
522 return self._readall_chunked()
523
524 if self.length is None:
525 s = self.fp.read()
526 else:
527 try:
528 s = self._safe_read(self.length)
529 except IncompleteRead:
530 self._close_conn()
531 raise
532 self.length = 0
533 self._close_conn() # we read everything
534 return bytes(s)
535
536 def readinto(self, b):
537 if self.fp is None:
538 return 0
539
540 if self._method == "HEAD":
541 self._close_conn()
542 return 0
543
544 if self.chunked:
545 return self._readinto_chunked(b)
546
547 if self.length is not None:
548 if len(b) > self.length:
549 # clip the read to the "end of response"
550 b = memoryview(b)[0:self.length]
551
552 # we do not use _safe_read() here because this may be a .will_close
553 # connection, and the user is reading more bytes than will be provided
554 # (for example, reading in 1k chunks)
555
556 if PY2:
557 data = self.fp.read(len(b))
558 n = len(data)
559 b[:n] = data
560 else:
561 n = self.fp.readinto(b)
562
563 if not n and b:
564 # Ideally, we would raise IncompleteRead if the content-length
565 # wasn't satisfied, but it might break compatibility.
566 self._close_conn()
567 elif self.length is not None:
568 self.length -= n
569 if not self.length:
570 self._close_conn()
571 return n
572
573 def _read_next_chunk_size(self):
574 # Read the next chunk size from the file
575 line = self.fp.readline(_MAXLINE + 1)
576 if len(line) > _MAXLINE:
577 raise LineTooLong("chunk size")
578 i = line.find(b";")
579 if i >= 0:
580 line = line[:i] # strip chunk-extensions
581 try:
582 return int(line, 16)
583 except ValueError:
584 # close the connection as protocol synchronisation is
585 # probably lost
586 self._close_conn()
587 raise
588
589 def _read_and_discard_trailer(self):
590 # read and discard trailer up to the CRLF terminator
591 ### note: we shouldn't have any trailers!
592 while True:
593 line = self.fp.readline(_MAXLINE + 1)
594 if len(line) > _MAXLINE:
595 raise LineTooLong("trailer line")
596 if not line:
597 # a vanishingly small number of sites EOF without
598 # sending the trailer
599 break
600 if line in (b'\r\n', b'\n', b''):
601 break
602
603 def _readall_chunked(self):
604 assert self.chunked != _UNKNOWN
605 chunk_left = self.chunk_left
606 value = []
607 while True:
608 if chunk_left is None:
609 try:
610 chunk_left = self._read_next_chunk_size()
611 if chunk_left == 0:
612 break
613 except ValueError:
614 raise IncompleteRead(bytes(b'').join(value))
615 value.append(self._safe_read(chunk_left))
616
617 # we read the whole chunk, get another
618 self._safe_read(2) # toss the CRLF at the end of the chunk
619 chunk_left = None
620
621 self._read_and_discard_trailer()
622
623 # we read everything; close the "file"
624 self._close_conn()
625
626 return bytes(b'').join(value)
627
628 def _readinto_chunked(self, b):
629 assert self.chunked != _UNKNOWN
630 chunk_left = self.chunk_left
631
632 total_bytes = 0
633 mvb = memoryview(b)
634 while True:
635 if chunk_left is None:
636 try:
637 chunk_left = self._read_next_chunk_size()
638 if chunk_left == 0:
639 break
640 except ValueError:
641 raise IncompleteRead(bytes(b[0:total_bytes]))
642
643 if len(mvb) < chunk_left:
644 n = self._safe_readinto(mvb)
645 self.chunk_left = chunk_left - n
646 return total_bytes + n
647 elif len(mvb) == chunk_left:
648 n = self._safe_readinto(mvb)
649 self._safe_read(2) # toss the CRLF at the end of the chunk
650 self.chunk_left = None
651 return total_bytes + n
652 else:
653 temp_mvb = mvb[0:chunk_left]
654 n = self._safe_readinto(temp_mvb)
655 mvb = mvb[n:]
656 total_bytes += n
657
658 # we read the whole chunk, get another
659 self._safe_read(2) # toss the CRLF at the end of the chunk
660 chunk_left = None
661
662 self._read_and_discard_trailer()
663
664 # we read everything; close the "file"
665 self._close_conn()
666
667 return total_bytes
668
669 def _safe_read(self, amt):
670 """Read the number of bytes requested, compensating for partial reads.
671
672 Normally, we have a blocking socket, but a read() can be interrupted
673 by a signal (resulting in a partial read).
674
675 Note that we cannot distinguish between EOF and an interrupt when zero
676 bytes have been read. IncompleteRead() will be raised in this
677 situation.
678
679 This function should be used when <amt> bytes "should" be present for
680 reading. If the bytes are truly not available (due to EOF), then the
681 IncompleteRead exception can be used to detect the problem.
682 """
683 s = []
684 while amt > 0:
685 chunk = self.fp.read(min(amt, MAXAMOUNT))
686 if not chunk:
687 raise IncompleteRead(bytes(b'').join(s), amt)
688 s.append(chunk)
689 amt -= len(chunk)
690 return bytes(b"").join(s)
691
692 def _safe_readinto(self, b):
693 """Same as _safe_read, but for reading into a buffer."""
694 total_bytes = 0
695 mvb = memoryview(b)
696 while total_bytes < len(b):
697 if MAXAMOUNT < len(mvb):
698 temp_mvb = mvb[0:MAXAMOUNT]
699 n = self.fp.readinto(temp_mvb)
700 else:
701 n = self.fp.readinto(mvb)
702 if not n:
703 raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b))
704 mvb = mvb[n:]
705 total_bytes += n
706 return total_bytes
707
708 def fileno(self):
709 return self.fp.fileno()
710
711 def getheader(self, name, default=None):
712 if self.headers is None:
713 raise ResponseNotReady()
714 headers = self.headers.get_all(name) or default
715 if isinstance(headers, str) or not hasattr(headers, '__iter__'):
716 return headers
717 else:
718 return ', '.join(headers)
719
720 def getheaders(self):
721 """Return list of (header, value) tuples."""
722 if self.headers is None:
723 raise ResponseNotReady()
724 return list(self.headers.items())
725
726 # We override IOBase.__iter__ so that it doesn't check for closed-ness
727
728 def __iter__(self):
729 return self
730
731 # For compatibility with old-style urllib responses.
732
733 def info(self):
734 return self.headers
735
736 def geturl(self):
737 return self.url
738
739 def getcode(self):
740 return self.status
741
742 class HTTPConnection(object):
743
744 _http_vsn = 11
745 _http_vsn_str = 'HTTP/1.1'
746
747 response_class = HTTPResponse
748 default_port = HTTP_PORT
749 auto_open = 1
750 debuglevel = 0
751
752 def __init__(self, host, port=None, strict=_strict_sentinel,
753 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
754 if strict is not _strict_sentinel:
755 warnings.warn("the 'strict' argument isn't supported anymore; "
756 "http.client now always assumes HTTP/1.x compliant servers.",
757 DeprecationWarning, 2)
758 self.timeout = timeout
759 self.source_address = source_address
760 self.sock = None
761 self._buffer = []
762 self.__response = None
763 self.__state = _CS_IDLE
764 self._method = None
765 self._tunnel_host = None
766 self._tunnel_port = None
767 self._tunnel_headers = {}
768
769 self._set_hostport(host, port)
770
771 def set_tunnel(self, host, port=None, headers=None):
772 """ Sets up the host and the port for the HTTP CONNECT Tunnelling.
773
774 The headers argument should be a mapping of extra HTTP headers
775 to send with the CONNECT request.
776 """
777 self._tunnel_host = host
778 self._tunnel_port = port
779 if headers:
780 self._tunnel_headers = headers
781 else:
782 self._tunnel_headers.clear()
783
784 def _set_hostport(self, host, port):
785 if port is None:
786 i = host.rfind(':')
787 j = host.rfind(']') # ipv6 addresses have [...]
788 if i > j:
789 try:
790 port = int(host[i+1:])
791 except ValueError:
792 if host[i+1:] == "": # http://foo.com:/ == http://foo.com/
793 port = self.default_port
794 else:
795 raise InvalidURL("nonnumeric port: '%s'" % host[i+1:])
796 host = host[:i]
797 else:
798 port = self.default_port
799 if host and host[0] == '[' and host[-1] == ']':
800 host = host[1:-1]
801 self.host = host
802 self.port = port
803
804 def set_debuglevel(self, level):
805 self.debuglevel = level
806
807 def _tunnel(self):
808 self._set_hostport(self._tunnel_host, self._tunnel_port)
809 connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port)
810 connect_bytes = connect_str.encode("ascii")
811 self.send(connect_bytes)
812 for header, value in self._tunnel_headers.items():
813 header_str = "%s: %s\r\n" % (header, value)
814 header_bytes = header_str.encode("latin-1")
815 self.send(header_bytes)
816 self.send(bytes(b'\r\n'))
817
818 response = self.response_class(self.sock, method=self._method)
819 (version, code, message) = response._read_status()
820
821 if code != 200:
822 self.close()
823 raise socket.error("Tunnel connection failed: %d %s" % (code,
824 message.stri p()))
825 while True:
826 line = response.fp.readline(_MAXLINE + 1)
827 if len(line) > _MAXLINE:
828 raise LineTooLong("header line")
829 if not line:
830 # for sites which EOF without sending a trailer
831 break
832 if line in (b'\r\n', b'\n', b''):
833 break
834
835 def connect(self):
836 """Connect to the host and port specified in __init__."""
837 self.sock = socket_create_connection((self.host,self.port),
838 self.timeout, self.source_address)
839 if self._tunnel_host:
840 self._tunnel()
841
842 def close(self):
843 """Close the connection to the HTTP server."""
844 if self.sock:
845 self.sock.close() # close it manually... there may be other refs
846 self.sock = None
847 if self.__response:
848 self.__response.close()
849 self.__response = None
850 self.__state = _CS_IDLE
851
852 def send(self, data):
853 """Send `data' to the server.
854 ``data`` can be a string object, a bytes object, an array object, a
855 file-like object that supports a .read() method, or an iterable object.
856 """
857
858 if self.sock is None:
859 if self.auto_open:
860 self.connect()
861 else:
862 raise NotConnected()
863
864 if self.debuglevel > 0:
865 print("send:", repr(data))
866 blocksize = 8192
867 # Python 2.7 array objects have a read method which is incompatible
868 # with the 2-arg calling syntax below.
869 if hasattr(data, "read") and not isinstance(data, array):
870 if self.debuglevel > 0:
871 print("sendIng a read()able")
872 encode = False
873 try:
874 mode = data.mode
875 except AttributeError:
876 # io.BytesIO and other file-like objects don't have a `mode`
877 # attribute.
878 pass
879 else:
880 if "b" not in mode:
881 encode = True
882 if self.debuglevel > 0:
883 print("encoding file using iso-8859-1")
884 while 1:
885 datablock = data.read(blocksize)
886 if not datablock:
887 break
888 if encode:
889 datablock = datablock.encode("iso-8859-1")
890 self.sock.sendall(datablock)
891 return
892 try:
893 self.sock.sendall(data)
894 except TypeError:
895 if isinstance(data, collections.Iterable):
896 for d in data:
897 self.sock.sendall(d)
898 else:
899 raise TypeError("data should be a bytes-like object "
900 "or an iterable, got %r" % type(data))
901
902 def _output(self, s):
903 """Add a line of output to the current request buffer.
904
905 Assumes that the line does *not* end with \\r\\n.
906 """
907 self._buffer.append(s)
908
909 def _send_output(self, message_body=None):
910 """Send the currently buffered request and clear the buffer.
911
912 Appends an extra \\r\\n to the buffer.
913 A message_body may be specified, to be appended to the request.
914 """
915 self._buffer.extend((bytes(b""), bytes(b"")))
916 msg = bytes(b"\r\n").join(self._buffer)
917 del self._buffer[:]
918 # If msg and message_body are sent in a single send() call,
919 # it will avoid performance problems caused by the interaction
920 # between delayed ack and the Nagle algorithm.
921 if isinstance(message_body, bytes):
922 msg += message_body
923 message_body = None
924 self.send(msg)
925 if message_body is not None:
926 # message_body was not a string (i.e. it is a file), and
927 # we must run the risk of Nagle.
928 self.send(message_body)
929
930 def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
931 """Send a request to the server.
932
933 `method' specifies an HTTP request method, e.g. 'GET'.
934 `url' specifies the object being requested, e.g. '/index.html'.
935 `skip_host' if True does not add automatically a 'Host:' header
936 `skip_accept_encoding' if True does not add automatically an
937 'Accept-Encoding:' header
938 """
939
940 # if a prior response has been completed, then forget about it.
941 if self.__response and self.__response.isclosed():
942 self.__response = None
943
944
945 # in certain cases, we cannot issue another request on this connection.
946 # this occurs when:
947 # 1) we are in the process of sending a request. (_CS_REQ_STARTED)
948 # 2) a response to a previous request has signalled that it is going
949 # to close the connection upon completion.
950 # 3) the headers for the previous response have not been read, thus
951 # we cannot determine whether point (2) is true. (_CS_REQ_SENT)
952 #
953 # if there is no prior response, then we can request at will.
954 #
955 # if point (2) is true, then we will have passed the socket to the
956 # response (effectively meaning, "there is no prior response"), and
957 # will open a new one when a new request is made.
958 #
959 # Note: if a prior response exists, then we *can* start a new request.
960 # We are not allowed to begin fetching the response to this new
961 # request, however, until that prior response is complete.
962 #
963 if self.__state == _CS_IDLE:
964 self.__state = _CS_REQ_STARTED
965 else:
966 raise CannotSendRequest(self.__state)
967
968 # Save the method we use, we need it later in the response phase
969 self._method = method
970 if not url:
971 url = '/'
972 request = '%s %s %s' % (method, url, self._http_vsn_str)
973
974 # Non-ASCII characters should have been eliminated earlier
975 self._output(request.encode('ascii'))
976
977 if self._http_vsn == 11:
978 # Issue some standard headers for better HTTP/1.1 compliance
979
980 if not skip_host:
981 # this header is issued *only* for HTTP/1.1
982 # connections. more specifically, this means it is
983 # only issued when the client uses the new
984 # HTTPConnection() class. backwards-compat clients
985 # will be using HTTP/1.0 and those clients may be
986 # issuing this header themselves. we should NOT issue
987 # it twice; some web servers (such as Apache) barf
988 # when they see two Host: headers
989
990 # If we need a non-standard port,include it in the
991 # header. If the request is going through a proxy,
992 # but the host of the actual URL, not the host of the
993 # proxy.
994
995 netloc = ''
996 if url.startswith('http'):
997 nil, netloc, nil, nil, nil = urlsplit(url)
998
999 if netloc:
1000 try:
1001 netloc_enc = netloc.encode("ascii")
1002 except UnicodeEncodeError:
1003 netloc_enc = netloc.encode("idna")
1004 self.putheader('Host', netloc_enc)
1005 else:
1006 try:
1007 host_enc = self.host.encode("ascii")
1008 except UnicodeEncodeError:
1009 host_enc = self.host.encode("idna")
1010
1011 # As per RFC 273, IPv6 address should be wrapped with []
1012 # when used as Host header
1013
1014 if self.host.find(':') >= 0:
1015 host_enc = bytes(b'[' + host_enc + b']')
1016
1017 if self.port == self.default_port:
1018 self.putheader('Host', host_enc)
1019 else:
1020 host_enc = host_enc.decode("ascii")
1021 self.putheader('Host', "%s:%s" % (host_enc, self.port))
1022
1023 # note: we are assuming that clients will not attempt to set these
1024 # headers since *this* library must deal with the
1025 # consequences. this also means that when the supporting
1026 # libraries are updated to recognize other forms, then this
1027 # code should be changed (removed or updated).
1028
1029 # we only want a Content-Encoding of "identity" since we don't
1030 # support encodings such as x-gzip or x-deflate.
1031 if not skip_accept_encoding:
1032 self.putheader('Accept-Encoding', 'identity')
1033
1034 # we can accept "chunked" Transfer-Encodings, but no others
1035 # NOTE: no TE header implies *only* "chunked"
1036 #self.putheader('TE', 'chunked')
1037
1038 # if TE is supplied in the header, then it must appear in a
1039 # Connection header.
1040 #self.putheader('Connection', 'TE')
1041
1042 else:
1043 # For HTTP/1.0, the server will assume "not chunked"
1044 pass
1045
1046 def putheader(self, header, *values):
1047 """Send a request header line to the server.
1048
1049 For example: h.putheader('Accept', 'text/html')
1050 """
1051 if self.__state != _CS_REQ_STARTED:
1052 raise CannotSendHeader()
1053
1054 if hasattr(header, 'encode'):
1055 header = header.encode('ascii')
1056 values = list(values)
1057 for i, one_value in enumerate(values):
1058 if hasattr(one_value, 'encode'):
1059 values[i] = one_value.encode('latin-1')
1060 elif isinstance(one_value, int):
1061 values[i] = str(one_value).encode('ascii')
1062 value = bytes(b'\r\n\t').join(values)
1063 header = header + bytes(b': ') + value
1064 self._output(header)
1065
1066 def endheaders(self, message_body=None):
1067 """Indicate that the last header line has been sent to the server.
1068
1069 This method sends the request to the server. The optional message_body
1070 argument can be used to pass a message body associated with the
1071 request. The message body will be sent in the same packet as the
1072 message headers if it is a string, otherwise it is sent as a separate
1073 packet.
1074 """
1075 if self.__state == _CS_REQ_STARTED:
1076 self.__state = _CS_REQ_SENT
1077 else:
1078 raise CannotSendHeader()
1079 self._send_output(message_body)
1080
1081 def request(self, method, url, body=None, headers={}):
1082 """Send a complete request to the server."""
1083 self._send_request(method, url, body, headers)
1084
1085 def _set_content_length(self, body):
1086 # Set the content-length based on the body.
1087 thelen = None
1088 try:
1089 thelen = str(len(body))
1090 except TypeError as te:
1091 # If this is a file-like object, try to
1092 # fstat its file descriptor
1093 try:
1094 thelen = str(os.fstat(body.fileno()).st_size)
1095 except (AttributeError, OSError):
1096 # Don't send a length if this failed
1097 if self.debuglevel > 0: print("Cannot stat!!")
1098
1099 if thelen is not None:
1100 self.putheader('Content-Length', thelen)
1101
1102 def _send_request(self, method, url, body, headers):
1103 # Honor explicitly requested Host: and Accept-Encoding: headers.
1104 header_names = dict.fromkeys([k.lower() for k in headers])
1105 skips = {}
1106 if 'host' in header_names:
1107 skips['skip_host'] = 1
1108 if 'accept-encoding' in header_names:
1109 skips['skip_accept_encoding'] = 1
1110
1111 self.putrequest(method, url, **skips)
1112
1113 if body is not None and ('content-length' not in header_names):
1114 self._set_content_length(body)
1115 for hdr, value in headers.items():
1116 self.putheader(hdr, value)
1117 if isinstance(body, str):
1118 # RFC 2616 Section 3.7.1 says that text default has a
1119 # default charset of iso-8859-1.
1120 body = body.encode('iso-8859-1')
1121 self.endheaders(body)
1122
1123 def getresponse(self):
1124 """Get the response from the server.
1125
1126 If the HTTPConnection is in the correct state, returns an
1127 instance of HTTPResponse or of whatever object is returned by
1128 class the response_class variable.
1129
1130 If a request has not been sent or if a previous response has
1131 not be handled, ResponseNotReady is raised. If the HTTP
1132 response indicates that the connection should be closed, then
1133 it will be closed before the response is returned. When the
1134 connection is closed, the underlying socket is closed.
1135 """
1136
1137 # if a prior response has been completed, then forget about it.
1138 if self.__response and self.__response.isclosed():
1139 self.__response = None
1140
1141 # if a prior response exists, then it must be completed (otherwise, we
1142 # cannot read this response's header to determine the connection-close
1143 # behavior)
1144 #
1145 # note: if a prior response existed, but was connection-close, then the
1146 # socket and response were made independent of this HTTPConnection
1147 # object since a new request requires that we open a whole new
1148 # connection
1149 #
1150 # this means the prior response had one of two states:
1151 # 1) will_close: this connection was reset and the prior socket and
1152 # response operate independently
1153 # 2) persistent: the response was retained and we await its
1154 # isclosed() status to become true.
1155 #
1156 if self.__state != _CS_REQ_SENT or self.__response:
1157 raise ResponseNotReady(self.__state)
1158
1159 if self.debuglevel > 0:
1160 response = self.response_class(self.sock, self.debuglevel,
1161 method=self._method)
1162 else:
1163 response = self.response_class(self.sock, method=self._method)
1164
1165 response.begin()
1166 assert response.will_close != _UNKNOWN
1167 self.__state = _CS_IDLE
1168
1169 if response.will_close:
1170 # this effectively passes the connection to the response
1171 self.close()
1172 else:
1173 # remember this, so we can tell when it is complete
1174 self.__response = response
1175
1176 return response
1177
1178 try:
1179 import ssl
1180 from ssl import SSLContext
1181 except ImportError:
1182 pass
1183 else:
1184 class HTTPSConnection(HTTPConnection):
1185 "This class allows communication via SSL."
1186
1187 default_port = HTTPS_PORT
1188
1189 # XXX Should key_file and cert_file be deprecated in favour of context?
1190
1191 def __init__(self, host, port=None, key_file=None, cert_file=None,
1192 strict=_strict_sentinel, timeout=socket._GLOBAL_DEFAULT_TIM EOUT,
1193 source_address=None, **_3to2kwargs):
1194 if 'check_hostname' in _3to2kwargs: check_hostname = _3to2kwargs['ch eck_hostname']; del _3to2kwargs['check_hostname']
1195 else: check_hostname = None
1196 if 'context' in _3to2kwargs: context = _3to2kwargs['context']; del _ 3to2kwargs['context']
1197 else: context = None
1198 super(HTTPSConnection, self).__init__(host, port, strict, timeout,
1199 source_address)
1200 self.key_file = key_file
1201 self.cert_file = cert_file
1202 if context is None:
1203 # Some reasonable defaults
1204 context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1205 context.options |= ssl.OP_NO_SSLv2
1206 will_verify = context.verify_mode != ssl.CERT_NONE
1207 if check_hostname is None:
1208 check_hostname = will_verify
1209 elif check_hostname and not will_verify:
1210 raise ValueError("check_hostname needs a SSL context with "
1211 "either CERT_OPTIONAL or CERT_REQUIRED")
1212 if key_file or cert_file:
1213 context.load_cert_chain(cert_file, key_file)
1214 self._context = context
1215 self._check_hostname = check_hostname
1216
1217 def connect(self):
1218 "Connect to a host on a given (SSL) port."
1219
1220 sock = socket_create_connection((self.host, self.port),
1221 self.timeout, self.source_address)
1222
1223 if self._tunnel_host:
1224 self.sock = sock
1225 self._tunnel()
1226
1227 server_hostname = self.host if ssl.HAS_SNI else None
1228 self.sock = self._context.wrap_socket(sock,
1229 server_hostname=server_hostnam e)
1230 try:
1231 if self._check_hostname:
1232 ssl.match_hostname(self.sock.getpeercert(), self.host)
1233 except Exception:
1234 self.sock.shutdown(socket.SHUT_RDWR)
1235 self.sock.close()
1236 raise
1237
1238 __all__.append("HTTPSConnection")
1239
1240
1241 # ######################################
1242 # # We use the old HTTPSConnection class from Py2.7, because ssl.SSLContext
1243 # # doesn't exist in the Py2.7 stdlib
1244 # class HTTPSConnection(HTTPConnection):
1245 # "This class allows communication via SSL."
1246
1247 # default_port = HTTPS_PORT
1248
1249 # def __init__(self, host, port=None, key_file=None, cert_file=None,
1250 # strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1251 # source_address=None):
1252 # HTTPConnection.__init__(self, host, port, strict, timeout,
1253 # source_address)
1254 # self.key_file = key_file
1255 # self.cert_file = cert_file
1256
1257 # def connect(self):
1258 # "Connect to a host on a given (SSL) port."
1259
1260 # sock = socket_create_connection((self.host, self.port),
1261 # self.timeout, self.source_address)
1262 # if self._tunnel_host:
1263 # self.sock = sock
1264 # self._tunnel()
1265 # self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
1266
1267 # __all__.append("HTTPSConnection")
1268 # ######################################
1269
1270
1271 class HTTPException(Exception):
1272 # Subclasses that define an __init__ must call Exception.__init__
1273 # or define self.args. Otherwise, str() will fail.
1274 pass
1275
1276 class NotConnected(HTTPException):
1277 pass
1278
1279 class InvalidURL(HTTPException):
1280 pass
1281
1282 class UnknownProtocol(HTTPException):
1283 def __init__(self, version):
1284 self.args = version,
1285 self.version = version
1286
1287 class UnknownTransferEncoding(HTTPException):
1288 pass
1289
1290 class UnimplementedFileMode(HTTPException):
1291 pass
1292
1293 class IncompleteRead(HTTPException):
1294 def __init__(self, partial, expected=None):
1295 self.args = partial,
1296 self.partial = partial
1297 self.expected = expected
1298 def __repr__(self):
1299 if self.expected is not None:
1300 e = ', %i more expected' % self.expected
1301 else:
1302 e = ''
1303 return 'IncompleteRead(%i bytes read%s)' % (len(self.partial), e)
1304 def __str__(self):
1305 return repr(self)
1306
1307 class ImproperConnectionState(HTTPException):
1308 pass
1309
1310 class CannotSendRequest(ImproperConnectionState):
1311 pass
1312
1313 class CannotSendHeader(ImproperConnectionState):
1314 pass
1315
1316 class ResponseNotReady(ImproperConnectionState):
1317 pass
1318
1319 class BadStatusLine(HTTPException):
1320 def __init__(self, line):
1321 if not line:
1322 line = repr(line)
1323 self.args = line,
1324 self.line = line
1325
1326 class LineTooLong(HTTPException):
1327 def __init__(self, line_type):
1328 HTTPException.__init__(self, "got more than %d bytes when reading %s"
1329 % (_MAXLINE, line_type))
1330
1331 # for backwards compatibility
1332 error = HTTPException
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698