| """HTTP client class | 
 |  | 
 | See the following URL for a description of the HTTP/1.0 protocol: | 
 | http://www.w3.org/hypertext/WWW/Protocols/ | 
 | (I actually implemented it from a much earlier draft.) | 
 |  | 
 | Example: | 
 |  | 
 | >>> from httplib import HTTP | 
 | >>> h = HTTP('www.python.org') | 
 | >>> h.putrequest('GET', '/index.html') | 
 | >>> h.putheader('Accept', 'text/html') | 
 | >>> h.putheader('Accept', 'text/plain') | 
 | >>> h.endheaders() | 
 | >>> errcode, errmsg, headers = h.getreply() | 
 | >>> if errcode == 200: | 
 | ...     f = h.getfile() | 
 | ...     print f.read() # Print the raw HTML | 
 | ... | 
 | <HEAD> | 
 | <TITLE>Python Language Home Page</TITLE> | 
 | [...many more lines...] | 
 | >>> | 
 |  | 
 | Note that an HTTP object is used for a single request -- to issue a | 
 | second request to the same server, you create a new HTTP object. | 
 | (This is in accordance with the protocol, which uses a new TCP | 
 | connection for each request.) | 
 | """ | 
 |  | 
 | import socket | 
 | import string | 
 | import mimetools | 
 |  | 
 | HTTP_VERSION = 'HTTP/1.0' | 
 | HTTP_PORT = 80 | 
 |  | 
 | class HTTP: | 
 |     """This class manages a connection to an HTTP server.""" | 
 |      | 
 |     def __init__(self, host = '', port = 0): | 
 |         """Initialize a new instance. | 
 |  | 
 |         If specified, `host' is the name of the remote host to which | 
 |         to connect.  If specified, `port' specifies the port to which | 
 |         to connect.  By default, httplib.HTTP_PORT is used. | 
 |  | 
 |         """ | 
 |         self.debuglevel = 0 | 
 |         self.file = None | 
 |         if host: self.connect(host, port) | 
 |      | 
 |     def set_debuglevel(self, debuglevel): | 
 |         """Set the debug output level. | 
 |  | 
 |         A non-false value results in debug messages for connection and | 
 |         for all messages sent to and received from the server. | 
 |  | 
 |         """ | 
 |         self.debuglevel = debuglevel | 
 |      | 
 |     def connect(self, host, port = 0): | 
 |         """Connect to a host on a given port. | 
 |          | 
 |         Note:  This method is automatically invoked by __init__, | 
 |         if a host is specified during instantiation. | 
 |  | 
 |         """ | 
 |         if not port: | 
 |             i = string.find(host, ':') | 
 |             if i >= 0: | 
 |                 host, port = host[:i], host[i+1:] | 
 |                 try: port = string.atoi(port) | 
 |                 except string.atoi_error: | 
 |                     raise socket.error, "nonnumeric port" | 
 |         if not port: port = HTTP_PORT | 
 |         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 
 |         if self.debuglevel > 0: print 'connect:', (host, port) | 
 |         self.sock.connect(host, port) | 
 |      | 
 |     def send(self, str): | 
 |         """Send `str' to the server.""" | 
 |         if self.debuglevel > 0: print 'send:', `str` | 
 |         self.sock.send(str) | 
 |      | 
 |     def putrequest(self, request, selector): | 
 |         """Send a request to the server. | 
 |  | 
 |         `request' specifies an HTTP request method, e.g. 'GET'. | 
 |         `selector' specifies the object being requested, e.g. | 
 |         '/index.html'. | 
 |  | 
 |         """ | 
 |         if not selector: selector = '/' | 
 |         str = '%s %s %s\r\n' % (request, selector, HTTP_VERSION) | 
 |         self.send(str) | 
 |      | 
 |     def putheader(self, header, *args): | 
 |         """Send a request header line to the server. | 
 |  | 
 |         For example: h.putheader('Accept', 'text/html') | 
 |  | 
 |         """ | 
 |         str = '%s: %s\r\n' % (header, string.joinfields(args,'\r\n\t')) | 
 |         self.send(str) | 
 |      | 
 |     def endheaders(self): | 
 |         """Indicate that the last header line has been sent to the server.""" | 
 |         self.send('\r\n') | 
 |      | 
 |     def getreply(self): | 
 |         """Get a reply from the server. | 
 |          | 
 |         Returns a tuple consisting of: | 
 |         - server response code (e.g. '200' if all goes well) | 
 |         - server response string corresponding to response code | 
 |         - any RFC822 headers in the response from the server | 
 |  | 
 |         """ | 
 |         self.file = self.sock.makefile('rb') | 
 |         line = self.file.readline() | 
 |         if self.debuglevel > 0: print 'reply:', `line` | 
 |         try: | 
 |             [ver, code, msg] = string.split(line, None, 2) | 
 |         except ValueError: | 
 |           try: | 
 |               [ver, code] = string.split(line, None, 1) | 
 |               msg = "" | 
 |           except ValueError: | 
 |               self.headers = None | 
 |               return -1, line, self.headers | 
 |         if ver[:5] != 'HTTP/': | 
 |             self.headers = None | 
 |             return -1, line, self.headers | 
 |         errcode = string.atoi(code) | 
 |         errmsg = string.strip(msg) | 
 |         self.headers = mimetools.Message(self.file, 0) | 
 |         return errcode, errmsg, self.headers | 
 |      | 
 |     def getfile(self): | 
 |         """Get a file object from which to receive data from the HTTP server. | 
 |  | 
 |         NOTE:  This method must not be invoked until getreplies | 
 |         has been invoked. | 
 |  | 
 |         """ | 
 |         return self.file | 
 |      | 
 |     def close(self): | 
 |         """Close the connection to the HTTP server.""" | 
 |         if self.file: | 
 |             self.file.close() | 
 |         self.file = None | 
 |         if self.sock: | 
 |             self.sock.close() | 
 |         self.sock = None | 
 |  | 
 |  | 
 | def test(): | 
 |     """Test this module. | 
 |  | 
 |     The test consists of retrieving and displaying the Python | 
 |     home page, along with the error code and error string returned | 
 |     by the www.python.org server. | 
 |  | 
 |     """ | 
 |     import sys | 
 |     import getopt | 
 |     opts, args = getopt.getopt(sys.argv[1:], 'd') | 
 |     dl = 0 | 
 |     for o, a in opts: | 
 |         if o == '-d': dl = dl + 1 | 
 |     host = 'www.python.org' | 
 |     selector = '/' | 
 |     if args[0:]: host = args[0] | 
 |     if args[1:]: selector = args[1] | 
 |     h = HTTP() | 
 |     h.set_debuglevel(dl) | 
 |     h.connect(host) | 
 |     h.putrequest('GET', selector) | 
 |     h.endheaders() | 
 |     errcode, errmsg, headers = h.getreply() | 
 |     print 'errcode =', errcode | 
 |     print 'errmsg  =', errmsg | 
 |     print | 
 |     if headers: | 
 |         for header in headers.headers: print string.strip(header) | 
 |     print | 
 |     print h.getfile().read() | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     test() |