1 """HTTP/1.1 client library
3 <intro stuff goes here>
6 HTTPConnection go through a number of "states", which defines when a client
7 may legally make another request or fetch the response for a particular
8 request. This diagram details these state transitions:
20 | ( putheader() )* endheaders()
24 | response = getresponse()
26 Unread-response [Response-headers-read]
27 |\____________________
29 | response.read() | putrequest()
31 Idle Req-started-unread-response
34 response.read() | | ( putheader() )* endheaders()
36 Request-started Req-sent-unread-response
42 This diagram presents the following rules:
43 -- a second request may not be started until {response-headers-read}
44 -- a response [object] cannot be retrieved until {request-sent}
45 -- there is no differentiation between an unread response body and a
46 partially read response body
48 Note: this enforcement is applied by the HTTPConnection class. The
49 HTTPResponse class does not enforce this state machine, which
50 implies sophisticated clients may accelerate the request/response
51 pipeline. Caution should be taken, though: accelerating the states
52 beyond the above pattern may imply knowledge of the server's
53 connection-close behavior for certain requests. For example, it
54 is impossible to tell whether the server will close the connection
55 UNTIL the response headers have been read; this means that further
56 requests cannot be placed into the pipeline until it is known that
57 the server will NOT be closing the connection.
59 Logical State __state __response
60 ------------- ------- ----------
62 Request-started _CS_REQ_STARTED None
63 Request-sent _CS_REQ_SENT None
64 Unread-response _CS_IDLE <response_class>
65 Req-started-unread-response _CS_REQ_STARTED <response_class>
66 Req-sent-unread-response _CS_REQ_SENT <response_class>
72 from urlparse
import urlsplit
75 from cStringIO
import StringIO
77 from StringIO
import StringIO
79 __all__ = [
"HTTP",
"HTTPResponse",
"HTTPConnection",
"HTTPSConnection",
80 "HTTPException",
"NotConnected",
"UnknownProtocol",
81 "UnknownTransferEncoding",
"IllegalKeywordArgument",
82 "UnimplementedFileMode",
"IncompleteRead",
83 "ImproperConnectionState",
"CannotSendRequest",
"CannotSendHeader",
84 "ResponseNotReady",
"BadStatusLine",
"error"]
93 _CS_REQ_STARTED =
'Request-started'
94 _CS_REQ_SENT =
'Request-sent'
99 self.
fp = sock.makefile(
'rb', 0)
115 if self.
msg is not None:
119 line = self.fp.readline()
121 print "reply:",
repr(line)
123 [version, status, reason] = line.split(
None, 2)
126 [version, status] = line.split(
None, 1)
132 if version[:5] !=
'HTTP/':
138 self.
status = status = int(status)
139 if status < 100
or status > 999:
143 self.
reason = reason.strip()
145 if version ==
'HTTP/1.0':
147 elif version.startswith(
'HTTP/1.'):
149 elif version ==
'HTTP/0.9':
160 for hdr
in self.msg.headers:
161 print "header:", hdr,
167 tr_enc = self.msg.getheader(
'transfer-encoding')
169 if tr_enc.lower() !=
'chunked':
177 conn = self.msg.getheader(
'connection')
183 self.
will_close = conn.find(
'close') != -1
or \
185 not self.msg.getheader(
'keep-alive') )
190 not self.msg.getheader(
'keep-alive')
194 length = self.msg.getheader(
'content-length')
195 if length
and not self.
chunked:
206 100 <= status < 200):
229 return self.
fp is None
239 if chunk_left
is None:
240 line = self.fp.readline()
244 chunk_left = int(line, 16)
249 elif amt < chunk_left:
253 elif amt == chunk_left:
260 amt = amt - chunk_left
269 line = self.fp.readline()
287 if self.
length is not None:
296 s = self.fp.read(amt)
300 def _safe_read(self, amt):
301 """Read the number of bytes requested, compensating for partial reads.
303 Normally, we have a blocking socket, but a read() can be interrupted
304 by a signal (resulting in a partial read).
306 Note that we cannot distinguish between EOF and an interrupt when zero
307 bytes have been read. IncompleteRead() will be raised in this
310 This function should be used when <amt> bytes "should" be present for
311 reading. If the bytes are truly not available (due to EOF), then the
312 IncompleteRead exception can be used to detect the problem.
316 chunk = self.fp.read(amt)
320 amt = amt - len(chunk)
326 return self.msg.getheader(name, default)
332 _http_vsn_str =
'HTTP/1.1'
334 response_class = HTTPResponse
335 default_port = HTTP_PORT
346 def _set_hostport(self, host, port):
350 port = int(host[i+1:])
361 """Connect to the host and port specified in __init__."""
362 msg =
"getaddrinfo returns an empty list"
363 for res
in socket.getaddrinfo(self.
host, self.
port, 0, socket.SOCK_STREAM):
364 af, socktype, proto, canonname, sa = res
368 print "connect: (%s, %s)" % (self.
host, self.
port)
369 self.sock.connect(sa)
370 except socket.error, msg:
372 print 'connect fail:', (self.
host, self.
port)
379 raise socket.error, msg
382 """Close the connection to the HTTP server."""
387 self.__response.close()
392 """Send `str' to the server."""
393 if self.
sock is None:
405 print "send:",
repr(str)
407 self.sock.sendall(str)
408 except socket.error, v:
414 """Send a request to the server.
416 `method' specifies an HTTP request method, e.g. 'GET'.
417 `url' specifies the object being requested, e.g. '/index.html'.
421 if self.
__response and self.__response.isclosed():
454 except socket.error, v:
480 if url.startswith(
'http'):
481 nil, netloc, nil, nil, nil =
urlsplit(url)
485 elif self.
port == HTTP_PORT:
498 self.
putheader(
'Accept-Encoding',
'identity')
513 """Send a request header line to the server.
515 For example: h.putheader('Accept', 'text/html')
517 if self.
__state != _CS_REQ_STARTED:
520 str =
'%s: %s\r\n' % (header, value)
524 """Indicate that the last header line has been sent to the server."""
526 if self.
__state == _CS_REQ_STARTED:
533 def request(self, method, url, body=None, headers={}):
534 """Send a complete request to the server."""
538 except socket.error, v:
545 def _send_request(self, method, url, body, headers):
549 if (headers.has_key(
'Host')
550 or [k
for k
in headers.iterkeys()
if k.lower() ==
"host"]):
557 for hdr, value
in headers.items():
565 "Get the response from the server."
568 if self.
__response and self.__response.isclosed():
598 if response.will_close:
614 """Return a readable file-like object with data from socket.
616 This method offers only partial support for the makefile
617 interface of a real socket. It only supports modes 'r' and
618 'rb' and the bufsize argument
is ignored.
620 The returned object contains *all* of the file data
622 if mode != 'r' and mode != 'rb':
623 raise UnimplementedFileMode()
628 buf = self.__ssl.read()
629 except socket.sslerror, err:
630 if (err[0] == socket.SSL_ERROR_WANT_READ
631 or err[0] == socket.SSL_ERROR_WANT_WRITE
634 if err[0] == socket.SSL_ERROR_ZERO_RETURN:
637 except socket.error, err:
638 if err[0] == errno.EINTR:
644 return StringIO("".join(msgbuf))
646 def send(self, stuff, flags = 0):
647 return self.__ssl.write(stuff)
649 def sendall(self, stuff, flags = 0):
650 return self.__ssl.write(stuff)
652 def recv(self, len = 1024, flags = 0):
653 return self.__ssl.read(len)
655 def __getattr__(self, attr):
656 return getattr(self.__sock, attr)
659 class HTTPSConnection(HTTPConnection):
660 "This class allows communication via SSL."
662 default_port = HTTPS_PORT
664 def __init__(self, host, port=None, **x509):
667 keys.remove('key_file')
671 keys.remove('cert_file')
675 raise IllegalKeywordArgument()
676 HTTPConnection.__init__(self, host, port)
677 self.key_file = x509.get('key_file')
678 self.cert_file = x509.get('cert_file')
681 "Connect to a host on a given (SSL) port."
683 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
684 sock.connect((self.host, self.port))
686 if hasattr(sock, "_sock"):
687 realsock = sock._sock
688 ssl = socket.ssl(realsock, self.key_file, self.cert_file)
689 self.sock = FakeSocket(sock, ssl)
693 "Compatibility class with httplib.py from 1.5."
696 _http_vsn_str = 'HTTP/1.0'
700 _connection_class = HTTPConnection
702 def __init__(self, host='', port=None):
703 "Provide a default host, since the superclass requires one."
705 # some joker passed 0 explicitly, meaning default port
709 # Note that we may pass an empty string as the host; this will throw
710 # an error when we attempt to connect. Presumably, the client code
711 # will call connect before then, with a proper host.
712 self._setup(self._connection_class(host, port))
714 def _setup(self, conn):
717 # set up delegation to flesh out interface
718 self.send = conn.send
719 self.putrequest = conn.putrequest
720 self.endheaders = conn.endheaders
721 self.set_debuglevel = conn.set_debuglevel
723 conn._http_vsn = self._http_vsn
724 conn._http_vsn_str = self._http_vsn_str
728 def connect(self, host=None, port=None):
729 "Accept arguments to set the host/port, since the superclass doesn't."
732 self._conn._set_hostport(host, port)
736 "Provide a getfile, since the superclass' does not use this concept."
739 def putheader(self, header, *values):
740 "The superclass allows only one value argument."
741 self._conn.putheader(header, '\r\n\t'.join(values))
744 """Compat definition since superclass does not define it.
746 Returns a tuple consisting of:
747 - server status code (e.g. '200' if all goes well)
748 - server "reason" corresponding to status code
749 - any RFC822 headers in the response from the server
752 response = self._conn.getresponse()
753 except BadStatusLine, e:
754 ### hmm. if getresponse() ever closes the socket on a bad request,
755 ### then we are going to have problems with self.sock
757 ### should we keep this behavior? do people use it?
758 # keep the socket open (as a file), and return it
759 self.file = self._conn.sock.makefile('rb', 0)
761 # close our socket -- we want to restart after any protocol error
765 return -1, e.line, None
767 self.headers = response.msg
768 self.file = response.fp
769 return response.status, response.reason, response.msg
774 # note that self.file == response.fp, which gets closed by the
775 # superclass. just clear the object ref here.
776 ### hmm. messy. if status==-1, then self.file is owned by us.
777 ### well... we aren't explicitly closing, but losing this ref will
781 if hasattr(socket, 'ssl'):
783 """Compatibility with 1.5 httplib interface
785 Python 1.5.2 did not have an HTTPS class, but it defined an
786 interface for sending http requests that is also useful for
790 _connection_class = HTTPSConnection
792 def __init__(self, host='', port=None, **x509):
793 # provide a default host, pass the X509 cert info
795 # urf. compensate for bad input.
798 self._setup(self._connection_class(host, port, **x509))
800 # we never actually use these for anything, but we keep them
801 # here for compatibility with post-1.5.2 CVS.
802 self.key_file = x509.get('key_file')
803 self.cert_file = x509.get('cert_file')
806 class HTTPException(Exception):
809 class NotConnected(HTTPException):
812 class UnknownProtocol(HTTPException):
813 def __init__(self, version):
814 self.version = version
816 class UnknownTransferEncoding(HTTPException):
819 class IllegalKeywordArgument(HTTPException):
822 class UnimplementedFileMode(HTTPException):
825 class IncompleteRead(HTTPException):
826 def __init__(self, partial):
827 self.partial = partial
829 class ImproperConnectionState(HTTPException):
832 class CannotSendRequest(ImproperConnectionState):
835 class CannotSendHeader(ImproperConnectionState):
838 class ResponseNotReady(ImproperConnectionState):
841 class BadStatusLine(HTTPException):
842 def __init__(self, line):
845 # for backwards compatibility
846 error = HTTPException
850 # snarfed from httplib.py for now...
855 The test consists of retrieving and displaying the Python
856 home page, along with the error code and error string returned
857 by the www.python.org server.
862 opts, args = getopt.getopt(sys.argv[1:], 'd')
865 if o == '-d': dl = dl + 1
866 host = 'www.python.org'
868 if args[0:]: host = args[0]
869 if args[1:]: selector = args[1]
873 h.putrequest('GET', selector)
875 status, reason, headers = h.getreply()
876 print 'status =', status
877 print 'reason =', reason
880 for header in headers.headers: print header.strip()
882 print h.getfile().read()
884 # minimal test that code to extract host from url works
887 _http_vsn_str = 'HTTP/1.1'
889 h = HTTP11('www.python.org')
890 h.putrequest('GET', 'http://www.python.org/~jeremy/')
895 if hasattr(socket, 'ssl'):
896 host = 'sourceforge.net'
897 selector = '/projects/python'
900 hs.putrequest('GET', selector)
902 status, reason, headers = hs.getreply()
903 print 'status =', status
904 print 'reason =', reason
907 for header in headers.headers: print header.strip()
909 print hs.getfile().read()
912 if __name__ == '__main__':