Show More
@@ -7,7 +7,7 b'' | |||||
7 | # This software may be used and distributed according to the terms of the |
|
7 | # This software may be used and distributed according to the terms of the | |
8 | # GNU General Public License version 2, incorporated herein by reference. |
|
8 | # GNU General Public License version 2, incorporated herein by reference. | |
9 |
|
9 | |||
10 | import urllib, urllib2, urlparse, httplib, os, re |
|
10 | import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO | |
11 | from i18n import _ |
|
11 | from i18n import _ | |
12 | import keepalive, util |
|
12 | import keepalive, util | |
13 |
|
13 | |||
@@ -245,18 +245,165 b' def _gen_sendfile(connection):' | |||||
245 | connection.send(self, data) |
|
245 | connection.send(self, data) | |
246 | return _sendfile |
|
246 | return _sendfile | |
247 |
|
247 | |||
|
248 | has_https = hasattr(urllib2, 'HTTPSHandler') | |||
|
249 | if has_https: | |||
|
250 | try: | |||
|
251 | # avoid using deprecated/broken FakeSocket in python 2.6 | |||
|
252 | import ssl | |||
|
253 | _ssl_wrap_socket = ssl.wrap_socket | |||
|
254 | except ImportError: | |||
|
255 | def _ssl_wrap_socket(sock, key_file, cert_file): | |||
|
256 | ssl = socket.ssl(sock, key_file, cert_file) | |||
|
257 | return httplib.FakeSocket(sock, ssl) | |||
|
258 | ||||
248 | class httpconnection(keepalive.HTTPConnection): |
|
259 | class httpconnection(keepalive.HTTPConnection): | |
249 | # must be able to send big bundle as stream. |
|
260 | # must be able to send big bundle as stream. | |
250 | send = _gen_sendfile(keepalive.HTTPConnection) |
|
261 | send = _gen_sendfile(keepalive.HTTPConnection) | |
251 |
|
262 | |||
|
263 | def _proxytunnel(self): | |||
|
264 | proxyheaders = dict( | |||
|
265 | [(x, self.headers[x]) for x in self.headers | |||
|
266 | if x.lower().startswith('proxy-')]) | |||
|
267 | self._set_hostport(self.host, self.port) | |||
|
268 | self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport)) | |||
|
269 | for header in proxyheaders.iteritems(): | |||
|
270 | self.send('%s: %s\r\n' % header) | |||
|
271 | self.send('\r\n') | |||
|
272 | ||||
|
273 | # majority of the following code is duplicated from | |||
|
274 | # httplib.HTTPConnection as there are no adequate places to | |||
|
275 | # override functions to provide the needed functionality | |||
|
276 | res = self.response_class(self.sock, | |||
|
277 | strict=self.strict, | |||
|
278 | method=self._method) | |||
|
279 | ||||
|
280 | while True: | |||
|
281 | version, status, reason = res._read_status() | |||
|
282 | if status != httplib.CONTINUE: | |||
|
283 | break | |||
|
284 | while True: | |||
|
285 | skip = res.fp.readline().strip() | |||
|
286 | if not skip: | |||
|
287 | break | |||
|
288 | res.status = status | |||
|
289 | res.reason = reason.strip() | |||
|
290 | ||||
|
291 | if res.status == 200: | |||
|
292 | while True: | |||
|
293 | line = res.fp.readline() | |||
|
294 | if line == '\r\n': | |||
|
295 | break | |||
|
296 | return True | |||
|
297 | ||||
|
298 | if version == 'HTTP/1.0': | |||
|
299 | res.version = 10 | |||
|
300 | elif version.startswith('HTTP/1.'): | |||
|
301 | res.version = 11 | |||
|
302 | elif version == 'HTTP/0.9': | |||
|
303 | res.version = 9 | |||
|
304 | else: | |||
|
305 | raise httplib.UnknownProtocol(version) | |||
|
306 | ||||
|
307 | if res.version == 9: | |||
|
308 | res.length = None | |||
|
309 | res.chunked = 0 | |||
|
310 | res.will_close = 1 | |||
|
311 | res.msg = httplib.HTTPMessage(cStringIO.StringIO()) | |||
|
312 | return False | |||
|
313 | ||||
|
314 | res.msg = httplib.HTTPMessage(res.fp) | |||
|
315 | res.msg.fp = None | |||
|
316 | ||||
|
317 | # are we using the chunked-style of transfer encoding? | |||
|
318 | trenc = res.msg.getheader('transfer-encoding') | |||
|
319 | if trenc and trenc.lower() == "chunked": | |||
|
320 | res.chunked = 1 | |||
|
321 | res.chunk_left = None | |||
|
322 | else: | |||
|
323 | res.chunked = 0 | |||
|
324 | ||||
|
325 | # will the connection close at the end of the response? | |||
|
326 | res.will_close = res._check_close() | |||
|
327 | ||||
|
328 | # do we have a Content-Length? | |||
|
329 | # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" | |||
|
330 | length = res.msg.getheader('content-length') | |||
|
331 | if length and not res.chunked: | |||
|
332 | try: | |||
|
333 | res.length = int(length) | |||
|
334 | except ValueError: | |||
|
335 | res.length = None | |||
|
336 | else: | |||
|
337 | if res.length < 0: # ignore nonsensical negative lengths | |||
|
338 | res.length = None | |||
|
339 | else: | |||
|
340 | res.length = None | |||
|
341 | ||||
|
342 | # does the body have a fixed length? (of zero) | |||
|
343 | if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or | |||
|
344 | 100 <= status < 200 or # 1xx codes | |||
|
345 | res._method == 'HEAD'): | |||
|
346 | res.length = 0 | |||
|
347 | ||||
|
348 | # if the connection remains open, and we aren't using chunked, and | |||
|
349 | # a content-length was not provided, then assume that the connection | |||
|
350 | # WILL close. | |||
|
351 | if (not res.will_close and | |||
|
352 | not res.chunked and | |||
|
353 | res.length is None): | |||
|
354 | res.will_close = 1 | |||
|
355 | ||||
|
356 | self.proxyres = res | |||
|
357 | ||||
|
358 | return False | |||
|
359 | ||||
|
360 | def connect(self): | |||
|
361 | if has_https and self.realhost: # use CONNECT proxy | |||
|
362 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |||
|
363 | self.sock.connect((self.host, self.port)) | |||
|
364 | if self._proxytunnel(): | |||
|
365 | # we do not support client x509 certificates | |||
|
366 | self.sock = _ssl_wrap_socket(self.sock, None, None) | |||
|
367 | else: | |||
|
368 | keepalive.HTTPConnection.connect(self) | |||
|
369 | ||||
|
370 | def getresponse(self): | |||
|
371 | proxyres = getattr(self, 'proxyres', None) | |||
|
372 | if proxyres: | |||
|
373 | if proxyres.will_close: | |||
|
374 | self.close() | |||
|
375 | self.proxyres = None | |||
|
376 | return proxyres | |||
|
377 | return keepalive.HTTPConnection.getresponse(self) | |||
|
378 | ||||
252 | class httphandler(keepalive.HTTPHandler): |
|
379 | class httphandler(keepalive.HTTPHandler): | |
253 | def http_open(self, req): |
|
380 | def http_open(self, req): | |
254 | return self.do_open(httpconnection, req) |
|
381 | return self.do_open(httpconnection, req) | |
255 |
|
382 | |||
|
383 | def _start_transaction(self, h, req): | |||
|
384 | if req.get_selector() == req.get_full_url(): # has proxy | |||
|
385 | urlparts = urlparse.urlparse(req.get_selector()) | |||
|
386 | if urlparts[0] == 'https': # only use CONNECT for HTTPS | |||
|
387 | if ':' in urlparts[1]: | |||
|
388 | realhost, realport = urlparts[1].split(':') | |||
|
389 | else: | |||
|
390 | realhost = urlparts[1] | |||
|
391 | realport = 443 | |||
|
392 | ||||
|
393 | h.realhost = realhost | |||
|
394 | h.realport = realport | |||
|
395 | h.headers = req.headers.copy() | |||
|
396 | h.headers.update(self.parent.addheaders) | |||
|
397 | return keepalive.HTTPHandler._start_transaction(self, h, req) | |||
|
398 | ||||
|
399 | h.realhost = None | |||
|
400 | h.realport = None | |||
|
401 | h.headers = None | |||
|
402 | return keepalive.HTTPHandler._start_transaction(self, h, req) | |||
|
403 | ||||
256 | def __del__(self): |
|
404 | def __del__(self): | |
257 | self.close_all() |
|
405 | self.close_all() | |
258 |
|
406 | |||
259 | has_https = hasattr(urllib2, 'HTTPSHandler') |
|
|||
260 | if has_https: |
|
407 | if has_https: | |
261 | class httpsconnection(httplib.HTTPSConnection): |
|
408 | class httpsconnection(httplib.HTTPSConnection): | |
262 | response_class = keepalive.HTTPResponse |
|
409 | response_class = keepalive.HTTPResponse |
General Comments 0
You need to be logged in to leave comments.
Login now