blob: 8c8084e98ea009947b04a46f282912f642572265 [file] [log] [blame]
Guido van Rossum41999c11997-12-09 00:12:23 +00001"""HTTP client class
Guido van Rossum23acc951994-02-21 16:36:04 +00002
Guido van Rossum41999c11997-12-09 00:12:23 +00003See the following URL for a description of the HTTP/1.0 protocol:
4http://www.w3.org/hypertext/WWW/Protocols/
5(I actually implemented it from a much earlier draft.)
6
7Example:
8
9>>> from httplib import HTTP
10>>> h = HTTP('www.python.org')
11>>> h.putrequest('GET', '/index.html')
12>>> h.putheader('Accept', 'text/html')
13>>> h.putheader('Accept', 'text/plain')
14>>> h.endheaders()
15>>> errcode, errmsg, headers = h.getreply()
16>>> if errcode == 200:
17... f = h.getfile()
18... print f.read() # Print the raw HTML
19...
20<HEAD>
21<TITLE>Python Language Home Page</TITLE>
22[...many more lines...]
23>>>
24
25Note that an HTTP object is used for a single request -- to issue a
26second request to the same server, you create a new HTTP object.
27(This is in accordance with the protocol, which uses a new TCP
28connection for each request.)
29"""
Guido van Rossum23acc951994-02-21 16:36:04 +000030
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000031import os
Guido van Rossum23acc951994-02-21 16:36:04 +000032import socket
33import string
Guido van Rossum65ab98c1995-08-07 20:13:02 +000034import mimetools
Guido van Rossum23acc951994-02-21 16:36:04 +000035
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000036try:
37 from cStringIO import StringIO
38except:
39 from StringIO import StringIO
40
Guido van Rossum23acc951994-02-21 16:36:04 +000041HTTP_VERSION = 'HTTP/1.0'
42HTTP_PORT = 80
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000043HTTPS_PORT = 443
44
45class FakeSocket:
46 def __init__(self, sock, ssl):
Fred Drake13a2c272000-02-10 17:17:14 +000047 self.__sock = sock
48 self.__ssl = ssl
49 return
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000050
Fred Drake13a2c272000-02-10 17:17:14 +000051 def makefile(self, mode): # hopefully, never have to write
52 msgbuf = ""
53 while 1:
54 try:
55 msgbuf = msgbuf + self.__ssl.read()
56 except socket.sslerror, msg:
57 break
58 return StringIO(msgbuf)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000059
60 def send(self, stuff, flags = 0):
Fred Drake13a2c272000-02-10 17:17:14 +000061 return self.__ssl.write(stuff)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000062
63 def recv(self, len = 1024, flags = 0):
Fred Drake13a2c272000-02-10 17:17:14 +000064 return self.__ssl.read(len)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000065
66 def __getattr__(self, attr):
Fred Drake13a2c272000-02-10 17:17:14 +000067 return getattr(self.__sock, attr)
Guido van Rossum23acc951994-02-21 16:36:04 +000068
Guido van Rossum23acc951994-02-21 16:36:04 +000069class HTTP:
Guido van Rossum41999c11997-12-09 00:12:23 +000070 """This class manages a connection to an HTTP server."""
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000071
72 def __init__(self, host = '', port = 0, **x509):
Guido van Rossum41999c11997-12-09 00:12:23 +000073 """Initialize a new instance.
Guido van Rossum23acc951994-02-21 16:36:04 +000074
Guido van Rossum41999c11997-12-09 00:12:23 +000075 If specified, `host' is the name of the remote host to which
76 to connect. If specified, `port' specifies the port to which
77 to connect. By default, httplib.HTTP_PORT is used.
Guido van Rossum23acc951994-02-21 16:36:04 +000078
Guido van Rossum41999c11997-12-09 00:12:23 +000079 """
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000080 self.key_file = x509.get('key_file')
81 self.cert_file = x509.get('cert_file')
Guido van Rossum41999c11997-12-09 00:12:23 +000082 self.debuglevel = 0
83 self.file = None
84 if host: self.connect(host, port)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000085
Guido van Rossum41999c11997-12-09 00:12:23 +000086 def set_debuglevel(self, debuglevel):
87 """Set the debug output level.
Guido van Rossum23acc951994-02-21 16:36:04 +000088
Guido van Rossum41999c11997-12-09 00:12:23 +000089 A non-false value results in debug messages for connection and
90 for all messages sent to and received from the server.
Guido van Rossum23acc951994-02-21 16:36:04 +000091
Guido van Rossum41999c11997-12-09 00:12:23 +000092 """
93 self.debuglevel = debuglevel
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000094
Guido van Rossum41999c11997-12-09 00:12:23 +000095 def connect(self, host, port = 0):
96 """Connect to a host on a given port.
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000097
Guido van Rossum41999c11997-12-09 00:12:23 +000098 Note: This method is automatically invoked by __init__,
99 if a host is specified during instantiation.
Guido van Rossum23acc951994-02-21 16:36:04 +0000100
Guido van Rossum41999c11997-12-09 00:12:23 +0000101 """
102 if not port:
103 i = string.find(host, ':')
104 if i >= 0:
105 host, port = host[:i], host[i+1:]
106 try: port = string.atoi(port)
107 except string.atoi_error:
108 raise socket.error, "nonnumeric port"
109 if not port: port = HTTP_PORT
110 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
111 if self.debuglevel > 0: print 'connect:', (host, port)
Guido van Rossum93a7c0f2000-03-28 21:45:46 +0000112 self.sock.connect((host, port))
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000113
Guido van Rossum41999c11997-12-09 00:12:23 +0000114 def send(self, str):
115 """Send `str' to the server."""
116 if self.debuglevel > 0: print 'send:', `str`
117 self.sock.send(str)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000118
Guido van Rossum41999c11997-12-09 00:12:23 +0000119 def putrequest(self, request, selector):
120 """Send a request to the server.
Guido van Rossum23acc951994-02-21 16:36:04 +0000121
Guido van Rossum41999c11997-12-09 00:12:23 +0000122 `request' specifies an HTTP request method, e.g. 'GET'.
123 `selector' specifies the object being requested, e.g.
124 '/index.html'.
Guido van Rossum23acc951994-02-21 16:36:04 +0000125
Guido van Rossum41999c11997-12-09 00:12:23 +0000126 """
127 if not selector: selector = '/'
128 str = '%s %s %s\r\n' % (request, selector, HTTP_VERSION)
129 self.send(str)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000130
Guido van Rossum41999c11997-12-09 00:12:23 +0000131 def putheader(self, header, *args):
132 """Send a request header line to the server.
Guido van Rossum23acc951994-02-21 16:36:04 +0000133
Guido van Rossum41999c11997-12-09 00:12:23 +0000134 For example: h.putheader('Accept', 'text/html')
Guido van Rossum23acc951994-02-21 16:36:04 +0000135
Guido van Rossum41999c11997-12-09 00:12:23 +0000136 """
137 str = '%s: %s\r\n' % (header, string.joinfields(args,'\r\n\t'))
138 self.send(str)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000139
Guido van Rossum41999c11997-12-09 00:12:23 +0000140 def endheaders(self):
141 """Indicate that the last header line has been sent to the server."""
142 self.send('\r\n')
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000143
Guido van Rossum41999c11997-12-09 00:12:23 +0000144 def getreply(self):
145 """Get a reply from the server.
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000146
Guido van Rossum41999c11997-12-09 00:12:23 +0000147 Returns a tuple consisting of:
148 - server response code (e.g. '200' if all goes well)
149 - server response string corresponding to response code
150 - any RFC822 headers in the response from the server
Guido van Rossum23acc951994-02-21 16:36:04 +0000151
Guido van Rossum41999c11997-12-09 00:12:23 +0000152 """
153 self.file = self.sock.makefile('rb')
154 line = self.file.readline()
155 if self.debuglevel > 0: print 'reply:', `line`
156 try:
157 [ver, code, msg] = string.split(line, None, 2)
158 except ValueError:
Guido van Rossum29c46881998-01-19 22:25:24 +0000159 try:
160 [ver, code] = string.split(line, None, 1)
161 msg = ""
162 except ValueError:
163 self.headers = None
164 return -1, line, self.headers
Guido van Rossum41999c11997-12-09 00:12:23 +0000165 if ver[:5] != 'HTTP/':
166 self.headers = None
167 return -1, line, self.headers
168 errcode = string.atoi(code)
169 errmsg = string.strip(msg)
170 self.headers = mimetools.Message(self.file, 0)
171 return errcode, errmsg, self.headers
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000172
Guido van Rossum41999c11997-12-09 00:12:23 +0000173 def getfile(self):
174 """Get a file object from which to receive data from the HTTP server.
175
176 NOTE: This method must not be invoked until getreplies
177 has been invoked.
178
179 """
180 return self.file
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000181
Guido van Rossum41999c11997-12-09 00:12:23 +0000182 def close(self):
183 """Close the connection to the HTTP server."""
184 if self.file:
185 self.file.close()
186 self.file = None
187 if self.sock:
188 self.sock.close()
189 self.sock = None
Guido van Rossum65ab98c1995-08-07 20:13:02 +0000190
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000191if hasattr(socket, "ssl"):
192 class HTTPS(HTTP):
193 """This class allows communication via SSL."""
194
195 def connect(self, host, port = 0):
196 """Connect to a host on a given port.
197
198 Note: This method is automatically invoked by __init__,
199 if a host is specified during instantiation.
200
201 """
202 if not port:
203 i = string.find(host, ':')
204 if i >= 0:
205 host, port = host[:i], host[i+1:]
206 try: port = string.atoi(port)
207 except string.atoi_error:
208 raise socket.error, "nonnumeric port"
209 if not port: port = HTTPS_PORT
210 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
211 if self.debuglevel > 0: print 'connect:', (host, port)
Guido van Rossum93a7c0f2000-03-28 21:45:46 +0000212 sock.connect((host, port))
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000213 ssl = socket.ssl(sock, self.key_file, self.cert_file)
214 self.sock = FakeSocket(sock, ssl)
215
Guido van Rossum23acc951994-02-21 16:36:04 +0000216
217def test():
Guido van Rossum41999c11997-12-09 00:12:23 +0000218 """Test this module.
219
220 The test consists of retrieving and displaying the Python
221 home page, along with the error code and error string returned
222 by the www.python.org server.
223
224 """
225 import sys
226 import getopt
227 opts, args = getopt.getopt(sys.argv[1:], 'd')
228 dl = 0
229 for o, a in opts:
230 if o == '-d': dl = dl + 1
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000231 print "testing HTTP..."
Guido van Rossum41999c11997-12-09 00:12:23 +0000232 host = 'www.python.org'
233 selector = '/'
234 if args[0:]: host = args[0]
235 if args[1:]: selector = args[1]
236 h = HTTP()
237 h.set_debuglevel(dl)
238 h.connect(host)
239 h.putrequest('GET', selector)
240 h.endheaders()
241 errcode, errmsg, headers = h.getreply()
242 print 'errcode =', errcode
243 print 'errmsg =', errmsg
244 print
245 if headers:
246 for header in headers.headers: print string.strip(header)
247 print
248 print h.getfile().read()
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000249 if hasattr(socket, "ssl"):
250 print "-"*40
251 print "testing HTTPS..."
252 host = 'synergy.as.cmu.edu'
253 selector = '/~geek/'
254 if args[0:]: host = args[0]
255 if args[1:]: selector = args[1]
256 h = HTTPS()
257 h.set_debuglevel(dl)
258 h.connect(host)
259 h.putrequest('GET', selector)
260 h.endheaders()
261 errcode, errmsg, headers = h.getreply()
262 print 'errcode =', errcode
263 print 'errmsg =', errmsg
264 print
265 if headers:
266 for header in headers.headers: print string.strip(header)
267 print
268 print h.getfile().read()
Guido van Rossum23acc951994-02-21 16:36:04 +0000269
Guido van Rossuma0dfc7a1995-09-07 19:28:19 +0000270
Guido van Rossum23acc951994-02-21 16:36:04 +0000271if __name__ == '__main__':
Guido van Rossum41999c11997-12-09 00:12:23 +0000272 test()