blob: 7e308a5200a184a763b405519dee538837b8e124 [file] [log] [blame]
Greg Stein5e0fa402000-06-26 08:28:01 +00001"""HTTP/1.1 client library
Guido van Rossum41999c11997-12-09 00:12:23 +00002
Greg Stein5e0fa402000-06-26 08:28:01 +00003<intro stuff goes here>
4<other stuff, too>
Guido van Rossum41999c11997-12-09 00:12:23 +00005
Greg Stein5e0fa402000-06-26 08:28:01 +00006HTTPConnection go through a number of "states", which defines when a client
7may legally make another request or fetch the response for a particular
8request. This diagram details these state transitions:
Guido van Rossum41999c11997-12-09 00:12:23 +00009
Greg Stein5e0fa402000-06-26 08:28:01 +000010 (null)
11 |
12 | HTTPConnection()
13 v
14 Idle
15 |
16 | putrequest()
17 v
18 Request-started
19 |
20 | ( putheader() )* endheaders()
21 v
22 Request-sent
23 |
24 | response = getresponse()
25 v
26 Unread-response [Response-headers-read]
27 |\____________________
Tim Peters5ceadc82001-01-13 19:16:21 +000028 | |
29 | response.read() | putrequest()
30 v v
31 Idle Req-started-unread-response
32 ______/|
33 / |
34 response.read() | | ( putheader() )* endheaders()
35 v v
36 Request-started Req-sent-unread-response
37 |
38 | response.read()
39 v
40 Request-sent
Greg Stein5e0fa402000-06-26 08:28:01 +000041
42This 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
47
48Note: 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.
58
59Logical State __state __response
60------------- ------- ----------
61Idle _CS_IDLE None
62Request-started _CS_REQ_STARTED None
63Request-sent _CS_REQ_SENT None
64Unread-response _CS_IDLE <response_class>
65Req-started-unread-response _CS_REQ_STARTED <response_class>
66Req-sent-unread-response _CS_REQ_SENT <response_class>
Guido van Rossum41999c11997-12-09 00:12:23 +000067"""
Guido van Rossum23acc951994-02-21 16:36:04 +000068
Jeremy Hylton6459c8d2001-10-11 17:47:22 +000069import errno
Guido van Rossum65ab98c1995-08-07 20:13:02 +000070import mimetools
Jeremy Hylton6459c8d2001-10-11 17:47:22 +000071import socket
Jeremy Hylton8acf1e02002-03-08 19:35:51 +000072from urlparse import urlsplit
Guido van Rossum23acc951994-02-21 16:36:04 +000073
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000074try:
Greg Steindd6eefb2000-07-18 09:09:48 +000075 from cStringIO import StringIO
Greg Stein5e0fa402000-06-26 08:28:01 +000076except ImportError:
Greg Steindd6eefb2000-07-18 09:09:48 +000077 from StringIO import StringIO
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000078
Skip Montanaro951a8842001-06-01 16:25:38 +000079__all__ = ["HTTP", "HTTPResponse", "HTTPConnection", "HTTPSConnection",
80 "HTTPException", "NotConnected", "UnknownProtocol",
81 "UnknownTransferEncoding", "IllegalKeywordArgument",
Skip Montanarof3f87f72002-03-24 16:56:45 +000082 "UnimplementedFileMode", "IncompleteRead", "InvalidURL",
Skip Montanaro951a8842001-06-01 16:25:38 +000083 "ImproperConnectionState", "CannotSendRequest", "CannotSendHeader",
84 "ResponseNotReady", "BadStatusLine", "error"]
Skip Montanaro2dd42762001-01-23 15:35:05 +000085
Guido van Rossum23acc951994-02-21 16:36:04 +000086HTTP_PORT = 80
Guido van Rossum09c8b6c1999-12-07 21:37:17 +000087HTTPS_PORT = 443
88
Greg Stein5e0fa402000-06-26 08:28:01 +000089_UNKNOWN = 'UNKNOWN'
90
91# connection states
92_CS_IDLE = 'Idle'
93_CS_REQ_STARTED = 'Request-started'
94_CS_REQ_SENT = 'Request-sent'
95
96
97class HTTPResponse:
Jeremy Hylton30f86742000-09-18 22:50:38 +000098 def __init__(self, sock, debuglevel=0):
Greg Steindd6eefb2000-07-18 09:09:48 +000099 self.fp = sock.makefile('rb', 0)
Jeremy Hylton30f86742000-09-18 22:50:38 +0000100 self.debuglevel = debuglevel
Greg Stein5e0fa402000-06-26 08:28:01 +0000101
Greg Steindd6eefb2000-07-18 09:09:48 +0000102 self.msg = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000103
Greg Steindd6eefb2000-07-18 09:09:48 +0000104 # from the Status-Line of the response
Tim Peters07e99cb2001-01-14 23:47:14 +0000105 self.version = _UNKNOWN # HTTP-Version
106 self.status = _UNKNOWN # Status-Code
107 self.reason = _UNKNOWN # Reason-Phrase
Greg Stein5e0fa402000-06-26 08:28:01 +0000108
Tim Peters07e99cb2001-01-14 23:47:14 +0000109 self.chunked = _UNKNOWN # is "chunked" being used?
110 self.chunk_left = _UNKNOWN # bytes left to read in current chunk
111 self.length = _UNKNOWN # number of bytes left in response
112 self.will_close = _UNKNOWN # conn will close at end of response
Greg Stein5e0fa402000-06-26 08:28:01 +0000113
Greg Steindd6eefb2000-07-18 09:09:48 +0000114 def begin(self):
115 if self.msg is not None:
116 # we've already started reading the response
117 return
Greg Stein5e0fa402000-06-26 08:28:01 +0000118
Greg Stein5e0fa402000-06-26 08:28:01 +0000119 line = self.fp.readline()
Jeremy Hylton30f86742000-09-18 22:50:38 +0000120 if self.debuglevel > 0:
121 print "reply:", repr(line)
Greg Steindd6eefb2000-07-18 09:09:48 +0000122 try:
Guido van Rossum34735a62000-12-15 15:09:42 +0000123 [version, status, reason] = line.split(None, 2)
Greg Steindd6eefb2000-07-18 09:09:48 +0000124 except ValueError:
125 try:
Guido van Rossum34735a62000-12-15 15:09:42 +0000126 [version, status] = line.split(None, 1)
Greg Steindd6eefb2000-07-18 09:09:48 +0000127 reason = ""
128 except ValueError:
Jeremy Hylton110941a2000-10-12 19:58:36 +0000129 version = "HTTP/0.9"
130 status = "200"
131 reason = ""
Greg Steindd6eefb2000-07-18 09:09:48 +0000132 if version[:5] != 'HTTP/':
133 self.close()
134 raise BadStatusLine(line)
Greg Stein5e0fa402000-06-26 08:28:01 +0000135
Jeremy Hylton23d40472001-04-13 14:57:08 +0000136 # The status code is a three-digit number
137 try:
138 self.status = status = int(status)
139 if status < 100 or status > 999:
140 raise BadStatusLine(line)
141 except ValueError:
142 raise BadStatusLine(line)
Guido van Rossum34735a62000-12-15 15:09:42 +0000143 self.reason = reason.strip()
Greg Stein5e0fa402000-06-26 08:28:01 +0000144
Greg Steindd6eefb2000-07-18 09:09:48 +0000145 if version == 'HTTP/1.0':
146 self.version = 10
Jeremy Hylton110941a2000-10-12 19:58:36 +0000147 elif version.startswith('HTTP/1.'):
Tim Peters07e99cb2001-01-14 23:47:14 +0000148 self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1
Jeremy Hylton110941a2000-10-12 19:58:36 +0000149 elif version == 'HTTP/0.9':
150 self.version = 9
Greg Steindd6eefb2000-07-18 09:09:48 +0000151 else:
152 raise UnknownProtocol(version)
Greg Stein5e0fa402000-06-26 08:28:01 +0000153
Jeremy Hylton110941a2000-10-12 19:58:36 +0000154 if self.version == 9:
155 self.msg = mimetools.Message(StringIO())
156 return
157
Greg Steindd6eefb2000-07-18 09:09:48 +0000158 self.msg = mimetools.Message(self.fp, 0)
Jeremy Hylton30f86742000-09-18 22:50:38 +0000159 if self.debuglevel > 0:
160 for hdr in self.msg.headers:
161 print "header:", hdr,
Greg Stein5e0fa402000-06-26 08:28:01 +0000162
Greg Steindd6eefb2000-07-18 09:09:48 +0000163 # don't let the msg keep an fp
164 self.msg.fp = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000165
Greg Steindd6eefb2000-07-18 09:09:48 +0000166 # are we using the chunked-style of transfer encoding?
167 tr_enc = self.msg.getheader('transfer-encoding')
168 if tr_enc:
Guido van Rossum34735a62000-12-15 15:09:42 +0000169 if tr_enc.lower() != 'chunked':
Greg Steindd6eefb2000-07-18 09:09:48 +0000170 raise UnknownTransferEncoding()
171 self.chunked = 1
172 self.chunk_left = None
173 else:
174 self.chunked = 0
Greg Stein5e0fa402000-06-26 08:28:01 +0000175
Greg Steindd6eefb2000-07-18 09:09:48 +0000176 # will the connection close at the end of the response?
177 conn = self.msg.getheader('connection')
178 if conn:
Guido van Rossum34735a62000-12-15 15:09:42 +0000179 conn = conn.lower()
Greg Steindd6eefb2000-07-18 09:09:48 +0000180 # a "Connection: close" will always close the connection. if we
181 # don't see that and this is not HTTP/1.1, then the connection will
182 # close unless we see a Keep-Alive header.
Guido van Rossum34735a62000-12-15 15:09:42 +0000183 self.will_close = conn.find('close') != -1 or \
Greg Steindd6eefb2000-07-18 09:09:48 +0000184 ( self.version != 11 and \
185 not self.msg.getheader('keep-alive') )
186 else:
187 # for HTTP/1.1, the connection will always remain open
188 # otherwise, it will remain open IFF we see a Keep-Alive header
189 self.will_close = self.version != 11 and \
190 not self.msg.getheader('keep-alive')
Greg Stein5e0fa402000-06-26 08:28:01 +0000191
Greg Steindd6eefb2000-07-18 09:09:48 +0000192 # do we have a Content-Length?
193 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
194 length = self.msg.getheader('content-length')
195 if length and not self.chunked:
Jeremy Hylton30a81812000-09-14 20:34:27 +0000196 try:
197 self.length = int(length)
198 except ValueError:
199 self.length = None
Greg Steindd6eefb2000-07-18 09:09:48 +0000200 else:
201 self.length = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000202
Greg Steindd6eefb2000-07-18 09:09:48 +0000203 # does the body have a fixed length? (of zero)
Tim Peters07e99cb2001-01-14 23:47:14 +0000204 if (status == 204 or # No Content
205 status == 304 or # Not Modified
206 100 <= status < 200): # 1xx codes
Greg Steindd6eefb2000-07-18 09:09:48 +0000207 self.length = 0
Greg Stein5e0fa402000-06-26 08:28:01 +0000208
Greg Steindd6eefb2000-07-18 09:09:48 +0000209 # if the connection remains open, and we aren't using chunked, and
210 # a content-length was not provided, then assume that the connection
211 # WILL close.
212 if not self.will_close and \
213 not self.chunked and \
214 self.length is None:
215 self.will_close = 1
Greg Stein5e0fa402000-06-26 08:28:01 +0000216
Greg Steindd6eefb2000-07-18 09:09:48 +0000217 def close(self):
218 if self.fp:
219 self.fp.close()
220 self.fp = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000221
Greg Steindd6eefb2000-07-18 09:09:48 +0000222 def isclosed(self):
223 # NOTE: it is possible that we will not ever call self.close(). This
224 # case occurs when will_close is TRUE, length is None, and we
225 # read up to the last byte, but NOT past it.
226 #
227 # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be
228 # called, meaning self.isclosed() is meaningful.
229 return self.fp is None
230
231 def read(self, amt=None):
232 if self.fp is None:
233 return ''
234
235 if self.chunked:
236 chunk_left = self.chunk_left
237 value = ''
238 while 1:
239 if chunk_left is None:
240 line = self.fp.readline()
Guido van Rossum34735a62000-12-15 15:09:42 +0000241 i = line.find(';')
Greg Steindd6eefb2000-07-18 09:09:48 +0000242 if i >= 0:
Tim Peters07e99cb2001-01-14 23:47:14 +0000243 line = line[:i] # strip chunk-extensions
Guido van Rossum34735a62000-12-15 15:09:42 +0000244 chunk_left = int(line, 16)
Greg Steindd6eefb2000-07-18 09:09:48 +0000245 if chunk_left == 0:
246 break
247 if amt is None:
248 value = value + self._safe_read(chunk_left)
249 elif amt < chunk_left:
250 value = value + self._safe_read(amt)
251 self.chunk_left = chunk_left - amt
252 return value
253 elif amt == chunk_left:
254 value = value + self._safe_read(amt)
Tim Peters07e99cb2001-01-14 23:47:14 +0000255 self._safe_read(2) # toss the CRLF at the end of the chunk
Greg Steindd6eefb2000-07-18 09:09:48 +0000256 self.chunk_left = None
257 return value
258 else:
259 value = value + self._safe_read(chunk_left)
260 amt = amt - chunk_left
261
262 # we read the whole chunk, get another
Tim Peters07e99cb2001-01-14 23:47:14 +0000263 self._safe_read(2) # toss the CRLF at the end of the chunk
Greg Steindd6eefb2000-07-18 09:09:48 +0000264 chunk_left = None
265
266 # read and discard trailer up to the CRLF terminator
267 ### note: we shouldn't have any trailers!
268 while 1:
269 line = self.fp.readline()
270 if line == '\r\n':
271 break
272
273 # we read everything; close the "file"
274 self.close()
275
276 return value
277
278 elif amt is None:
279 # unbounded read
280 if self.will_close:
281 s = self.fp.read()
282 else:
283 s = self._safe_read(self.length)
Tim Peters07e99cb2001-01-14 23:47:14 +0000284 self.close() # we read everything
Greg Steindd6eefb2000-07-18 09:09:48 +0000285 return s
286
287 if self.length is not None:
288 if amt > self.length:
289 # clip the read to the "end of response"
290 amt = self.length
291 self.length = self.length - amt
292
293 # we do not use _safe_read() here because this may be a .will_close
294 # connection, and the user is reading more bytes than will be provided
295 # (for example, reading in 1k chunks)
296 s = self.fp.read(amt)
297
Greg Steindd6eefb2000-07-18 09:09:48 +0000298 return s
299
300 def _safe_read(self, amt):
301 """Read the number of bytes requested, compensating for partial reads.
302
303 Normally, we have a blocking socket, but a read() can be interrupted
304 by a signal (resulting in a partial read).
305
306 Note that we cannot distinguish between EOF and an interrupt when zero
307 bytes have been read. IncompleteRead() will be raised in this
308 situation.
309
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.
313 """
314 s = ''
315 while amt > 0:
316 chunk = self.fp.read(amt)
317 if not chunk:
318 raise IncompleteRead(s)
319 s = s + chunk
320 amt = amt - len(chunk)
321 return s
322
323 def getheader(self, name, default=None):
324 if self.msg is None:
325 raise ResponseNotReady()
326 return self.msg.getheader(name, default)
Greg Stein5e0fa402000-06-26 08:28:01 +0000327
328
329class HTTPConnection:
330
Greg Steindd6eefb2000-07-18 09:09:48 +0000331 _http_vsn = 11
332 _http_vsn_str = 'HTTP/1.1'
Greg Stein5e0fa402000-06-26 08:28:01 +0000333
Greg Steindd6eefb2000-07-18 09:09:48 +0000334 response_class = HTTPResponse
335 default_port = HTTP_PORT
336 auto_open = 1
Jeremy Hylton30f86742000-09-18 22:50:38 +0000337 debuglevel = 0
Greg Stein5e0fa402000-06-26 08:28:01 +0000338
Greg Steindd6eefb2000-07-18 09:09:48 +0000339 def __init__(self, host, port=None):
340 self.sock = None
341 self.__response = None
342 self.__state = _CS_IDLE
Greg Stein5e0fa402000-06-26 08:28:01 +0000343
Greg Steindd6eefb2000-07-18 09:09:48 +0000344 self._set_hostport(host, port)
Greg Stein5e0fa402000-06-26 08:28:01 +0000345
Greg Steindd6eefb2000-07-18 09:09:48 +0000346 def _set_hostport(self, host, port):
347 if port is None:
Guido van Rossum34735a62000-12-15 15:09:42 +0000348 i = host.find(':')
Greg Steindd6eefb2000-07-18 09:09:48 +0000349 if i >= 0:
Skip Montanaro9d389972002-03-24 16:53:50 +0000350 try:
351 port = int(host[i+1:])
352 except ValueError:
353 raise InvalidURL, "nonnumeric port: '%s'"%host[i+1:]
Greg Steindd6eefb2000-07-18 09:09:48 +0000354 host = host[:i]
355 else:
356 port = self.default_port
357 self.host = host
358 self.port = port
Greg Stein5e0fa402000-06-26 08:28:01 +0000359
Jeremy Hylton30f86742000-09-18 22:50:38 +0000360 def set_debuglevel(self, level):
361 self.debuglevel = level
362
Greg Steindd6eefb2000-07-18 09:09:48 +0000363 def connect(self):
364 """Connect to the host and port specified in __init__."""
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000365 msg = "getaddrinfo returns an empty list"
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000366 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
367 af, socktype, proto, canonname, sa = res
368 try:
369 self.sock = socket.socket(af, socktype, proto)
370 if self.debuglevel > 0:
371 print "connect: (%s, %s)" % (self.host, self.port)
372 self.sock.connect(sa)
373 except socket.error, msg:
374 if self.debuglevel > 0:
375 print 'connect fail:', (self.host, self.port)
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000376 if self.sock:
377 self.sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000378 self.sock = None
379 continue
380 break
381 if not self.sock:
382 raise socket.error, msg
Greg Stein5e0fa402000-06-26 08:28:01 +0000383
Greg Steindd6eefb2000-07-18 09:09:48 +0000384 def close(self):
385 """Close the connection to the HTTP server."""
386 if self.sock:
Tim Peters07e99cb2001-01-14 23:47:14 +0000387 self.sock.close() # close it manually... there may be other refs
Greg Steindd6eefb2000-07-18 09:09:48 +0000388 self.sock = None
389 if self.__response:
390 self.__response.close()
391 self.__response = None
392 self.__state = _CS_IDLE
Greg Stein5e0fa402000-06-26 08:28:01 +0000393
Greg Steindd6eefb2000-07-18 09:09:48 +0000394 def send(self, str):
395 """Send `str' to the server."""
396 if self.sock is None:
397 if self.auto_open:
398 self.connect()
399 else:
400 raise NotConnected()
Greg Stein5e0fa402000-06-26 08:28:01 +0000401
Greg Steindd6eefb2000-07-18 09:09:48 +0000402 # send the data to the server. if we get a broken pipe, then close
403 # the socket. we want to reconnect when somebody tries to send again.
404 #
405 # NOTE: we DO propagate the error, though, because we cannot simply
406 # ignore the error... the caller will know if they can retry.
Jeremy Hylton30f86742000-09-18 22:50:38 +0000407 if self.debuglevel > 0:
408 print "send:", repr(str)
Greg Steindd6eefb2000-07-18 09:09:48 +0000409 try:
Martin v. Löwise12454f2002-02-16 23:06:19 +0000410 self.sock.sendall(str)
Greg Steindd6eefb2000-07-18 09:09:48 +0000411 except socket.error, v:
Tim Peters07e99cb2001-01-14 23:47:14 +0000412 if v[0] == 32: # Broken pipe
Greg Steindd6eefb2000-07-18 09:09:48 +0000413 self.close()
414 raise
Greg Stein5e0fa402000-06-26 08:28:01 +0000415
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000416 def putrequest(self, method, url, skip_host=0):
Greg Steindd6eefb2000-07-18 09:09:48 +0000417 """Send a request to the server.
Greg Stein5e0fa402000-06-26 08:28:01 +0000418
Greg Steindd6eefb2000-07-18 09:09:48 +0000419 `method' specifies an HTTP request method, e.g. 'GET'.
420 `url' specifies the object being requested, e.g. '/index.html'.
421 """
Greg Stein5e0fa402000-06-26 08:28:01 +0000422
Greg Steindd6eefb2000-07-18 09:09:48 +0000423 # check if a prior response has been completed
424 if self.__response and self.__response.isclosed():
425 self.__response = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000426
Greg Steindd6eefb2000-07-18 09:09:48 +0000427 #
428 # in certain cases, we cannot issue another request on this connection.
429 # this occurs when:
430 # 1) we are in the process of sending a request. (_CS_REQ_STARTED)
431 # 2) a response to a previous request has signalled that it is going
432 # to close the connection upon completion.
433 # 3) the headers for the previous response have not been read, thus
434 # we cannot determine whether point (2) is true. (_CS_REQ_SENT)
435 #
436 # if there is no prior response, then we can request at will.
437 #
438 # if point (2) is true, then we will have passed the socket to the
439 # response (effectively meaning, "there is no prior response"), and
440 # will open a new one when a new request is made.
441 #
442 # Note: if a prior response exists, then we *can* start a new request.
443 # We are not allowed to begin fetching the response to this new
444 # request, however, until that prior response is complete.
445 #
446 if self.__state == _CS_IDLE:
447 self.__state = _CS_REQ_STARTED
448 else:
449 raise CannotSendRequest()
Greg Stein5e0fa402000-06-26 08:28:01 +0000450
Greg Steindd6eefb2000-07-18 09:09:48 +0000451 if not url:
452 url = '/'
453 str = '%s %s %s\r\n' % (method, url, self._http_vsn_str)
Greg Stein5e0fa402000-06-26 08:28:01 +0000454
Greg Steindd6eefb2000-07-18 09:09:48 +0000455 try:
456 self.send(str)
457 except socket.error, v:
458 # trap 'Broken pipe' if we're allowed to automatically reconnect
459 if v[0] != 32 or not self.auto_open:
460 raise
461 # try one more time (the socket was closed; this will reopen)
462 self.send(str)
Greg Stein5e0fa402000-06-26 08:28:01 +0000463
Greg Steindd6eefb2000-07-18 09:09:48 +0000464 if self._http_vsn == 11:
465 # Issue some standard headers for better HTTP/1.1 compliance
Greg Stein5e0fa402000-06-26 08:28:01 +0000466
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000467 if not skip_host:
468 # this header is issued *only* for HTTP/1.1
469 # connections. more specifically, this means it is
470 # only issued when the client uses the new
471 # HTTPConnection() class. backwards-compat clients
472 # will be using HTTP/1.0 and those clients may be
473 # issuing this header themselves. we should NOT issue
474 # it twice; some web servers (such as Apache) barf
475 # when they see two Host: headers
Guido van Rossumf6922aa2001-01-14 21:03:01 +0000476
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000477 # If we need a non-standard port,include it in the
478 # header. If the request is going through a proxy,
479 # but the host of the actual URL, not the host of the
480 # proxy.
Jeremy Hylton8acf1e02002-03-08 19:35:51 +0000481
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000482 netloc = ''
483 if url.startswith('http'):
484 nil, netloc, nil, nil, nil = urlsplit(url)
485
486 if netloc:
487 self.putheader('Host', netloc)
488 elif self.port == HTTP_PORT:
489 self.putheader('Host', self.host)
490 else:
491 self.putheader('Host', "%s:%s" % (self.host, self.port))
Greg Stein5e0fa402000-06-26 08:28:01 +0000492
Greg Steindd6eefb2000-07-18 09:09:48 +0000493 # note: we are assuming that clients will not attempt to set these
494 # headers since *this* library must deal with the
495 # consequences. this also means that when the supporting
496 # libraries are updated to recognize other forms, then this
497 # code should be changed (removed or updated).
Greg Stein5e0fa402000-06-26 08:28:01 +0000498
Greg Steindd6eefb2000-07-18 09:09:48 +0000499 # we only want a Content-Encoding of "identity" since we don't
500 # support encodings such as x-gzip or x-deflate.
501 self.putheader('Accept-Encoding', 'identity')
Greg Stein5e0fa402000-06-26 08:28:01 +0000502
Greg Steindd6eefb2000-07-18 09:09:48 +0000503 # we can accept "chunked" Transfer-Encodings, but no others
504 # NOTE: no TE header implies *only* "chunked"
505 #self.putheader('TE', 'chunked')
Greg Stein5e0fa402000-06-26 08:28:01 +0000506
Greg Steindd6eefb2000-07-18 09:09:48 +0000507 # if TE is supplied in the header, then it must appear in a
508 # Connection header.
509 #self.putheader('Connection', 'TE')
Greg Stein5e0fa402000-06-26 08:28:01 +0000510
Greg Steindd6eefb2000-07-18 09:09:48 +0000511 else:
512 # For HTTP/1.0, the server will assume "not chunked"
513 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000514
Greg Steindd6eefb2000-07-18 09:09:48 +0000515 def putheader(self, header, value):
516 """Send a request header line to the server.
Greg Stein5e0fa402000-06-26 08:28:01 +0000517
Greg Steindd6eefb2000-07-18 09:09:48 +0000518 For example: h.putheader('Accept', 'text/html')
519 """
520 if self.__state != _CS_REQ_STARTED:
521 raise CannotSendHeader()
Greg Stein5e0fa402000-06-26 08:28:01 +0000522
Greg Steindd6eefb2000-07-18 09:09:48 +0000523 str = '%s: %s\r\n' % (header, value)
524 self.send(str)
Greg Stein5e0fa402000-06-26 08:28:01 +0000525
Greg Steindd6eefb2000-07-18 09:09:48 +0000526 def endheaders(self):
527 """Indicate that the last header line has been sent to the server."""
Greg Stein5e0fa402000-06-26 08:28:01 +0000528
Greg Steindd6eefb2000-07-18 09:09:48 +0000529 if self.__state == _CS_REQ_STARTED:
530 self.__state = _CS_REQ_SENT
531 else:
532 raise CannotSendHeader()
Greg Stein5e0fa402000-06-26 08:28:01 +0000533
Greg Steindd6eefb2000-07-18 09:09:48 +0000534 self.send('\r\n')
Greg Stein5e0fa402000-06-26 08:28:01 +0000535
Greg Steindd6eefb2000-07-18 09:09:48 +0000536 def request(self, method, url, body=None, headers={}):
537 """Send a complete request to the server."""
Greg Stein5e0fa402000-06-26 08:28:01 +0000538
Greg Steindd6eefb2000-07-18 09:09:48 +0000539 try:
540 self._send_request(method, url, body, headers)
541 except socket.error, v:
542 # trap 'Broken pipe' if we're allowed to automatically reconnect
543 if v[0] != 32 or not self.auto_open:
544 raise
545 # try one more time
546 self._send_request(method, url, body, headers)
Greg Stein5e0fa402000-06-26 08:28:01 +0000547
Greg Steindd6eefb2000-07-18 09:09:48 +0000548 def _send_request(self, method, url, body, headers):
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000549 # If headers already contains a host header, then define the
550 # optional skip_host argument to putrequest(). The check is
551 # harder because field names are case insensitive.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000552 if 'Host' in (headers
Jeremy Hylton3921ff62002-03-09 06:07:23 +0000553 or [k for k in headers.iterkeys() if k.lower() == "host"]):
554 self.putrequest(method, url, skip_host=1)
555 else:
556 self.putrequest(method, url)
Greg Stein5e0fa402000-06-26 08:28:01 +0000557
Greg Steindd6eefb2000-07-18 09:09:48 +0000558 if body:
559 self.putheader('Content-Length', str(len(body)))
560 for hdr, value in headers.items():
561 self.putheader(hdr, value)
562 self.endheaders()
Greg Stein5e0fa402000-06-26 08:28:01 +0000563
Greg Steindd6eefb2000-07-18 09:09:48 +0000564 if body:
565 self.send(body)
Greg Stein5e0fa402000-06-26 08:28:01 +0000566
Greg Steindd6eefb2000-07-18 09:09:48 +0000567 def getresponse(self):
568 "Get the response from the server."
Greg Stein5e0fa402000-06-26 08:28:01 +0000569
Greg Steindd6eefb2000-07-18 09:09:48 +0000570 # check if a prior response has been completed
571 if self.__response and self.__response.isclosed():
572 self.__response = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000573
Greg Steindd6eefb2000-07-18 09:09:48 +0000574 #
575 # if a prior response exists, then it must be completed (otherwise, we
576 # cannot read this response's header to determine the connection-close
577 # behavior)
578 #
579 # note: if a prior response existed, but was connection-close, then the
580 # socket and response were made independent of this HTTPConnection
581 # object since a new request requires that we open a whole new
582 # connection
583 #
584 # this means the prior response had one of two states:
585 # 1) will_close: this connection was reset and the prior socket and
586 # response operate independently
587 # 2) persistent: the response was retained and we await its
588 # isclosed() status to become true.
589 #
590 if self.__state != _CS_REQ_SENT or self.__response:
591 raise ResponseNotReady()
Greg Stein5e0fa402000-06-26 08:28:01 +0000592
Jeremy Hylton30f86742000-09-18 22:50:38 +0000593 if self.debuglevel > 0:
594 response = self.response_class(self.sock, self.debuglevel)
595 else:
596 response = self.response_class(self.sock)
Greg Stein5e0fa402000-06-26 08:28:01 +0000597
Greg Steindd6eefb2000-07-18 09:09:48 +0000598 response.begin()
599 self.__state = _CS_IDLE
Greg Stein5e0fa402000-06-26 08:28:01 +0000600
Greg Steindd6eefb2000-07-18 09:09:48 +0000601 if response.will_close:
602 # this effectively passes the connection to the response
603 self.close()
604 else:
605 # remember this, so we can tell when it is complete
606 self.__response = response
Greg Stein5e0fa402000-06-26 08:28:01 +0000607
Greg Steindd6eefb2000-07-18 09:09:48 +0000608 return response
Greg Stein5e0fa402000-06-26 08:28:01 +0000609
610
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000611class FakeSocket:
Greg Steindd6eefb2000-07-18 09:09:48 +0000612 def __init__(self, sock, ssl):
613 self.__sock = sock
614 self.__ssl = ssl
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000615
Jeremy Hylton4d746fc2000-08-23 20:34:17 +0000616 def makefile(self, mode, bufsize=None):
617 """Return a readable file-like object with data from socket.
618
619 This method offers only partial support for the makefile
620 interface of a real socket. It only supports modes 'r' and
621 'rb' and the bufsize argument is ignored.
622
Tim Peters5ceadc82001-01-13 19:16:21 +0000623 The returned object contains *all* of the file data
Jeremy Hylton4d746fc2000-08-23 20:34:17 +0000624 """
Greg Steindd6eefb2000-07-18 09:09:48 +0000625 if mode != 'r' and mode != 'rb':
626 raise UnimplementedFileMode()
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000627
Jeremy Hylton42dd01a2001-02-01 23:35:20 +0000628 msgbuf = []
Greg Steindd6eefb2000-07-18 09:09:48 +0000629 while 1:
630 try:
Jeremy Hylton42dd01a2001-02-01 23:35:20 +0000631 buf = self.__ssl.read()
Jeremy Hylton6459c8d2001-10-11 17:47:22 +0000632 except socket.sslerror, err:
633 if (err[0] == socket.SSL_ERROR_WANT_READ
Neal Norwitz22c5d772002-02-11 17:59:51 +0000634 or err[0] == socket.SSL_ERROR_WANT_WRITE):
Jeremy Hylton6459c8d2001-10-11 17:47:22 +0000635 continue
Martin v. Löwis6af3e2d2002-04-20 07:47:40 +0000636 if (err[0] == socket.SSL_ERROR_ZERO_RETURN
637 or err[0] == socket.SSL_ERROR_EOF):
Jeremy Hylton6459c8d2001-10-11 17:47:22 +0000638 break
639 raise
640 except socket.error, err:
Tim Petersf3623f32001-10-11 18:15:51 +0000641 if err[0] == errno.EINTR:
Jeremy Hylton6459c8d2001-10-11 17:47:22 +0000642 continue
643 raise
Jeremy Hylton42dd01a2001-02-01 23:35:20 +0000644 if buf == '':
645 break
646 msgbuf.append(buf)
647 return StringIO("".join(msgbuf))
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000648
Greg Steindd6eefb2000-07-18 09:09:48 +0000649 def send(self, stuff, flags = 0):
650 return self.__ssl.write(stuff)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000651
Andrew M. Kuchlinga3c0b932002-03-18 22:51:48 +0000652 def sendall(self, stuff, flags = 0):
653 return self.__ssl.write(stuff)
654
Greg Steindd6eefb2000-07-18 09:09:48 +0000655 def recv(self, len = 1024, flags = 0):
656 return self.__ssl.read(len)
Guido van Rossum23acc951994-02-21 16:36:04 +0000657
Greg Steindd6eefb2000-07-18 09:09:48 +0000658 def __getattr__(self, attr):
659 return getattr(self.__sock, attr)
Guido van Rossum09c8b6c1999-12-07 21:37:17 +0000660
Guido van Rossum23acc951994-02-21 16:36:04 +0000661
Greg Stein5e0fa402000-06-26 08:28:01 +0000662class HTTPSConnection(HTTPConnection):
Greg Steindd6eefb2000-07-18 09:09:48 +0000663 "This class allows communication via SSL."
Greg Stein5e0fa402000-06-26 08:28:01 +0000664
Greg Steindd6eefb2000-07-18 09:09:48 +0000665 default_port = HTTPS_PORT
Greg Stein5e0fa402000-06-26 08:28:01 +0000666
Greg Steindd6eefb2000-07-18 09:09:48 +0000667 def __init__(self, host, port=None, **x509):
668 keys = x509.keys()
669 try:
670 keys.remove('key_file')
671 except ValueError:
672 pass
673 try:
674 keys.remove('cert_file')
675 except ValueError:
676 pass
677 if keys:
678 raise IllegalKeywordArgument()
679 HTTPConnection.__init__(self, host, port)
680 self.key_file = x509.get('key_file')
681 self.cert_file = x509.get('cert_file')
Greg Stein5e0fa402000-06-26 08:28:01 +0000682
Greg Steindd6eefb2000-07-18 09:09:48 +0000683 def connect(self):
684 "Connect to a host on a given (SSL) port."
Greg Stein5e0fa402000-06-26 08:28:01 +0000685
Greg Steindd6eefb2000-07-18 09:09:48 +0000686 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
687 sock.connect((self.host, self.port))
Guido van Rossum0aee7222000-12-11 20:32:20 +0000688 realsock = sock
689 if hasattr(sock, "_sock"):
690 realsock = sock._sock
691 ssl = socket.ssl(realsock, self.key_file, self.cert_file)
Greg Steindd6eefb2000-07-18 09:09:48 +0000692 self.sock = FakeSocket(sock, ssl)
Greg Stein5e0fa402000-06-26 08:28:01 +0000693
694
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000695class HTTP:
Greg Steindd6eefb2000-07-18 09:09:48 +0000696 "Compatibility class with httplib.py from 1.5."
Greg Stein5e0fa402000-06-26 08:28:01 +0000697
Greg Steindd6eefb2000-07-18 09:09:48 +0000698 _http_vsn = 10
699 _http_vsn_str = 'HTTP/1.0'
Greg Stein5e0fa402000-06-26 08:28:01 +0000700
Greg Steindd6eefb2000-07-18 09:09:48 +0000701 debuglevel = 0
Greg Stein5e0fa402000-06-26 08:28:01 +0000702
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000703 _connection_class = HTTPConnection
704
Greg Stein81937a42001-08-18 09:20:23 +0000705 def __init__(self, host='', port=None):
Greg Steindd6eefb2000-07-18 09:09:48 +0000706 "Provide a default host, since the superclass requires one."
Greg Stein5e0fa402000-06-26 08:28:01 +0000707
Greg Steindd6eefb2000-07-18 09:09:48 +0000708 # some joker passed 0 explicitly, meaning default port
709 if port == 0:
710 port = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000711
Greg Steindd6eefb2000-07-18 09:09:48 +0000712 # Note that we may pass an empty string as the host; this will throw
713 # an error when we attempt to connect. Presumably, the client code
714 # will call connect before then, with a proper host.
Greg Stein81937a42001-08-18 09:20:23 +0000715 self._setup(self._connection_class(host, port))
Greg Stein5e0fa402000-06-26 08:28:01 +0000716
Greg Stein81937a42001-08-18 09:20:23 +0000717 def _setup(self, conn):
718 self._conn = conn
719
720 # set up delegation to flesh out interface
721 self.send = conn.send
722 self.putrequest = conn.putrequest
723 self.endheaders = conn.endheaders
724 self.set_debuglevel = conn.set_debuglevel
725
726 conn._http_vsn = self._http_vsn
727 conn._http_vsn_str = self._http_vsn_str
Greg Stein5e0fa402000-06-26 08:28:01 +0000728
Greg Steindd6eefb2000-07-18 09:09:48 +0000729 self.file = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000730
Greg Steindd6eefb2000-07-18 09:09:48 +0000731 def connect(self, host=None, port=None):
732 "Accept arguments to set the host/port, since the superclass doesn't."
Greg Stein5e0fa402000-06-26 08:28:01 +0000733
Greg Steindd6eefb2000-07-18 09:09:48 +0000734 if host is not None:
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000735 self._conn._set_hostport(host, port)
736 self._conn.connect()
Greg Stein5e0fa402000-06-26 08:28:01 +0000737
Greg Steindd6eefb2000-07-18 09:09:48 +0000738 def getfile(self):
739 "Provide a getfile, since the superclass' does not use this concept."
740 return self.file
Greg Stein5e0fa402000-06-26 08:28:01 +0000741
Greg Steindd6eefb2000-07-18 09:09:48 +0000742 def putheader(self, header, *values):
743 "The superclass allows only one value argument."
Guido van Rossum34735a62000-12-15 15:09:42 +0000744 self._conn.putheader(header, '\r\n\t'.join(values))
Greg Stein5e0fa402000-06-26 08:28:01 +0000745
Greg Steindd6eefb2000-07-18 09:09:48 +0000746 def getreply(self):
747 """Compat definition since superclass does not define it.
Greg Stein5e0fa402000-06-26 08:28:01 +0000748
Greg Steindd6eefb2000-07-18 09:09:48 +0000749 Returns a tuple consisting of:
750 - server status code (e.g. '200' if all goes well)
751 - server "reason" corresponding to status code
752 - any RFC822 headers in the response from the server
753 """
754 try:
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000755 response = self._conn.getresponse()
Greg Steindd6eefb2000-07-18 09:09:48 +0000756 except BadStatusLine, e:
757 ### hmm. if getresponse() ever closes the socket on a bad request,
758 ### then we are going to have problems with self.sock
Greg Stein5e0fa402000-06-26 08:28:01 +0000759
Greg Steindd6eefb2000-07-18 09:09:48 +0000760 ### should we keep this behavior? do people use it?
761 # keep the socket open (as a file), and return it
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000762 self.file = self._conn.sock.makefile('rb', 0)
Greg Stein5e0fa402000-06-26 08:28:01 +0000763
Greg Steindd6eefb2000-07-18 09:09:48 +0000764 # close our socket -- we want to restart after any protocol error
765 self.close()
Greg Stein5e0fa402000-06-26 08:28:01 +0000766
Greg Steindd6eefb2000-07-18 09:09:48 +0000767 self.headers = None
768 return -1, e.line, None
Greg Stein5e0fa402000-06-26 08:28:01 +0000769
Greg Steindd6eefb2000-07-18 09:09:48 +0000770 self.headers = response.msg
771 self.file = response.fp
772 return response.status, response.reason, response.msg
Greg Stein5e0fa402000-06-26 08:28:01 +0000773
Greg Steindd6eefb2000-07-18 09:09:48 +0000774 def close(self):
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000775 self._conn.close()
Greg Stein5e0fa402000-06-26 08:28:01 +0000776
Greg Steindd6eefb2000-07-18 09:09:48 +0000777 # note that self.file == response.fp, which gets closed by the
778 # superclass. just clear the object ref here.
779 ### hmm. messy. if status==-1, then self.file is owned by us.
780 ### well... we aren't explicitly closing, but losing this ref will
781 ### do it
782 self.file = None
Greg Stein5e0fa402000-06-26 08:28:01 +0000783
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000784if hasattr(socket, 'ssl'):
785 class HTTPS(HTTP):
786 """Compatibility with 1.5 httplib interface
787
788 Python 1.5.2 did not have an HTTPS class, but it defined an
789 interface for sending http requests that is also useful for
Tim Peters5ceadc82001-01-13 19:16:21 +0000790 https.
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000791 """
792
Martin v. Löwisd7bf9742000-09-21 22:09:47 +0000793 _connection_class = HTTPSConnection
Tim Peters5ceadc82001-01-13 19:16:21 +0000794
Greg Stein81937a42001-08-18 09:20:23 +0000795 def __init__(self, host='', port=None, **x509):
796 # provide a default host, pass the X509 cert info
797
798 # urf. compensate for bad input.
799 if port == 0:
800 port = None
801 self._setup(self._connection_class(host, port, **x509))
802
803 # we never actually use these for anything, but we keep them
804 # here for compatibility with post-1.5.2 CVS.
805 self.key_file = x509.get('key_file')
806 self.cert_file = x509.get('cert_file')
807
Greg Stein5e0fa402000-06-26 08:28:01 +0000808
809class HTTPException(Exception):
Greg Steindd6eefb2000-07-18 09:09:48 +0000810 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000811
812class NotConnected(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000813 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000814
Skip Montanaro9d389972002-03-24 16:53:50 +0000815class InvalidURL(HTTPException):
816 pass
817
Greg Stein5e0fa402000-06-26 08:28:01 +0000818class UnknownProtocol(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000819 def __init__(self, version):
820 self.version = version
Greg Stein5e0fa402000-06-26 08:28:01 +0000821
822class UnknownTransferEncoding(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000823 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000824
825class IllegalKeywordArgument(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000826 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000827
828class UnimplementedFileMode(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000829 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000830
831class IncompleteRead(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000832 def __init__(self, partial):
833 self.partial = partial
Greg Stein5e0fa402000-06-26 08:28:01 +0000834
835class ImproperConnectionState(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000836 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000837
838class CannotSendRequest(ImproperConnectionState):
Greg Steindd6eefb2000-07-18 09:09:48 +0000839 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000840
841class CannotSendHeader(ImproperConnectionState):
Greg Steindd6eefb2000-07-18 09:09:48 +0000842 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000843
844class ResponseNotReady(ImproperConnectionState):
Greg Steindd6eefb2000-07-18 09:09:48 +0000845 pass
Greg Stein5e0fa402000-06-26 08:28:01 +0000846
847class BadStatusLine(HTTPException):
Greg Steindd6eefb2000-07-18 09:09:48 +0000848 def __init__(self, line):
849 self.line = line
Greg Stein5e0fa402000-06-26 08:28:01 +0000850
851# for backwards compatibility
852error = HTTPException
853
854
855#
856# snarfed from httplib.py for now...
857#
Guido van Rossum23acc951994-02-21 16:36:04 +0000858def test():
Guido van Rossum41999c11997-12-09 00:12:23 +0000859 """Test this module.
860
861 The test consists of retrieving and displaying the Python
862 home page, along with the error code and error string returned
863 by the www.python.org server.
Guido van Rossum41999c11997-12-09 00:12:23 +0000864 """
Greg Stein5e0fa402000-06-26 08:28:01 +0000865
Guido van Rossum41999c11997-12-09 00:12:23 +0000866 import sys
867 import getopt
868 opts, args = getopt.getopt(sys.argv[1:], 'd')
869 dl = 0
870 for o, a in opts:
871 if o == '-d': dl = dl + 1
872 host = 'www.python.org'
873 selector = '/'
874 if args[0:]: host = args[0]
875 if args[1:]: selector = args[1]
876 h = HTTP()
877 h.set_debuglevel(dl)
878 h.connect(host)
879 h.putrequest('GET', selector)
880 h.endheaders()
Greg Stein5e0fa402000-06-26 08:28:01 +0000881 status, reason, headers = h.getreply()
882 print 'status =', status
883 print 'reason =', reason
Guido van Rossum41999c11997-12-09 00:12:23 +0000884 print
885 if headers:
Guido van Rossum34735a62000-12-15 15:09:42 +0000886 for header in headers.headers: print header.strip()
Guido van Rossum41999c11997-12-09 00:12:23 +0000887 print
888 print h.getfile().read()
Greg Stein5e0fa402000-06-26 08:28:01 +0000889
Jeremy Hylton8acf1e02002-03-08 19:35:51 +0000890 # minimal test that code to extract host from url works
891 class HTTP11(HTTP):
892 _http_vsn = 11
893 _http_vsn_str = 'HTTP/1.1'
894
895 h = HTTP11('www.python.org')
896 h.putrequest('GET', 'http://www.python.org/~jeremy/')
897 h.endheaders()
898 h.getreply()
899 h.close()
900
Greg Stein5e0fa402000-06-26 08:28:01 +0000901 if hasattr(socket, 'ssl'):
Jeremy Hylton29b8d5a2000-08-01 17:33:32 +0000902 host = 'sourceforge.net'
Jeremy Hylton42dd01a2001-02-01 23:35:20 +0000903 selector = '/projects/python'
Greg Steindd6eefb2000-07-18 09:09:48 +0000904 hs = HTTPS()
905 hs.connect(host)
906 hs.putrequest('GET', selector)
907 hs.endheaders()
908 status, reason, headers = hs.getreply()
909 print 'status =', status
910 print 'reason =', reason
911 print
912 if headers:
Guido van Rossum34735a62000-12-15 15:09:42 +0000913 for header in headers.headers: print header.strip()
Greg Steindd6eefb2000-07-18 09:09:48 +0000914 print
915 print hs.getfile().read()
Guido van Rossum23acc951994-02-21 16:36:04 +0000916
Guido van Rossuma0dfc7a1995-09-07 19:28:19 +0000917
Guido van Rossum23acc951994-02-21 16:36:04 +0000918if __name__ == '__main__':
Guido van Rossum41999c11997-12-09 00:12:23 +0000919 test()