260 |
260 |
261 class httpconnection(keepalive.HTTPConnection): |
261 class httpconnection(keepalive.HTTPConnection): |
262 # must be able to send big bundle as stream. |
262 # must be able to send big bundle as stream. |
263 send = _gen_sendfile(keepalive.HTTPConnection) |
263 send = _gen_sendfile(keepalive.HTTPConnection) |
264 |
264 |
265 def _proxytunnel(self): |
|
266 proxyheaders = dict( |
|
267 [(x, self.headers[x]) for x in self.headers |
|
268 if x.lower().startswith('proxy-')]) |
|
269 self._set_hostport(self.host, self.port) |
|
270 self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport)) |
|
271 for header in proxyheaders.iteritems(): |
|
272 self.send('%s: %s\r\n' % header) |
|
273 self.send('\r\n') |
|
274 |
|
275 # majority of the following code is duplicated from |
|
276 # httplib.HTTPConnection as there are no adequate places to |
|
277 # override functions to provide the needed functionality |
|
278 res = self.response_class(self.sock, |
|
279 strict=self.strict, |
|
280 method=self._method) |
|
281 |
|
282 while True: |
|
283 version, status, reason = res._read_status() |
|
284 if status != httplib.CONTINUE: |
|
285 break |
|
286 while True: |
|
287 skip = res.fp.readline().strip() |
|
288 if not skip: |
|
289 break |
|
290 res.status = status |
|
291 res.reason = reason.strip() |
|
292 |
|
293 if res.status == 200: |
|
294 while True: |
|
295 line = res.fp.readline() |
|
296 if line == '\r\n': |
|
297 break |
|
298 return True |
|
299 |
|
300 if version == 'HTTP/1.0': |
|
301 res.version = 10 |
|
302 elif version.startswith('HTTP/1.'): |
|
303 res.version = 11 |
|
304 elif version == 'HTTP/0.9': |
|
305 res.version = 9 |
|
306 else: |
|
307 raise httplib.UnknownProtocol(version) |
|
308 |
|
309 if res.version == 9: |
|
310 res.length = None |
|
311 res.chunked = 0 |
|
312 res.will_close = 1 |
|
313 res.msg = httplib.HTTPMessage(cStringIO.StringIO()) |
|
314 return False |
|
315 |
|
316 res.msg = httplib.HTTPMessage(res.fp) |
|
317 res.msg.fp = None |
|
318 |
|
319 # are we using the chunked-style of transfer encoding? |
|
320 trenc = res.msg.getheader('transfer-encoding') |
|
321 if trenc and trenc.lower() == "chunked": |
|
322 res.chunked = 1 |
|
323 res.chunk_left = None |
|
324 else: |
|
325 res.chunked = 0 |
|
326 |
|
327 # will the connection close at the end of the response? |
|
328 res.will_close = res._check_close() |
|
329 |
|
330 # do we have a Content-Length? |
|
331 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" |
|
332 length = res.msg.getheader('content-length') |
|
333 if length and not res.chunked: |
|
334 try: |
|
335 res.length = int(length) |
|
336 except ValueError: |
|
337 res.length = None |
|
338 else: |
|
339 if res.length < 0: # ignore nonsensical negative lengths |
|
340 res.length = None |
|
341 else: |
|
342 res.length = None |
|
343 |
|
344 # does the body have a fixed length? (of zero) |
|
345 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or |
|
346 100 <= status < 200 or # 1xx codes |
|
347 res._method == 'HEAD'): |
|
348 res.length = 0 |
|
349 |
|
350 # if the connection remains open, and we aren't using chunked, and |
|
351 # a content-length was not provided, then assume that the connection |
|
352 # WILL close. |
|
353 if (not res.will_close and |
|
354 not res.chunked and |
|
355 res.length is None): |
|
356 res.will_close = 1 |
|
357 |
|
358 self.proxyres = res |
|
359 |
|
360 return False |
|
361 |
|
362 def connect(self): |
265 def connect(self): |
363 if has_https and self.realhost: # use CONNECT proxy |
266 if has_https and self.realhost: # use CONNECT proxy |
364 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
267 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
365 self.sock.connect((self.host, self.port)) |
268 self.sock.connect((self.host, self.port)) |
366 if self._proxytunnel(): |
269 if _generic_proxytunnel(self): |
367 # we do not support client x509 certificates |
270 # we do not support client x509 certificates |
368 self.sock = _ssl_wrap_socket(self.sock, None, None) |
271 self.sock = _ssl_wrap_socket(self.sock, None, None) |
369 else: |
272 else: |
370 keepalive.HTTPConnection.connect(self) |
273 keepalive.HTTPConnection.connect(self) |
371 |
274 |
376 self.close() |
279 self.close() |
377 self.proxyres = None |
280 self.proxyres = None |
378 return proxyres |
281 return proxyres |
379 return keepalive.HTTPConnection.getresponse(self) |
282 return keepalive.HTTPConnection.getresponse(self) |
380 |
283 |
|
284 # general transaction handler to support different ways to handle |
|
285 # HTTPS proxying before and after Python 2.6.3. |
|
286 def _generic_start_transaction(handler, h, req): |
|
287 if hasattr(req, '_tunnel_host') and req._tunnel_host: |
|
288 tunnel_host = req._tunnel_host |
|
289 if tunnel_host[:7] not in ['http://', 'https:/']: |
|
290 tunnel_host = 'https://' + tunnel_host |
|
291 new_tunnel = True |
|
292 else: |
|
293 tunnel_host = req.get_selector() |
|
294 new_tunnel = False |
|
295 |
|
296 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy |
|
297 urlparts = urlparse.urlparse(tunnel_host) |
|
298 if new_tunnel or urlparts[0] == 'https': # only use CONNECT for HTTPS |
|
299 if ':' in urlparts[1]: |
|
300 realhost, realport = urlparts[1].split(':') |
|
301 realport = int(realport) |
|
302 else: |
|
303 realhost = urlparts[1] |
|
304 realport = 443 |
|
305 |
|
306 h.realhost = realhost |
|
307 h.realport = realport |
|
308 h.headers = req.headers.copy() |
|
309 h.headers.update(handler.parent.addheaders) |
|
310 return |
|
311 |
|
312 h.realhost = None |
|
313 h.realport = None |
|
314 h.headers = None |
|
315 |
|
316 def _generic_proxytunnel(self): |
|
317 proxyheaders = dict( |
|
318 [(x, self.headers[x]) for x in self.headers |
|
319 if x.lower().startswith('proxy-')]) |
|
320 self._set_hostport(self.host, self.port) |
|
321 self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport)) |
|
322 for header in proxyheaders.iteritems(): |
|
323 self.send('%s: %s\r\n' % header) |
|
324 self.send('\r\n') |
|
325 |
|
326 # majority of the following code is duplicated from |
|
327 # httplib.HTTPConnection as there are no adequate places to |
|
328 # override functions to provide the needed functionality |
|
329 res = self.response_class(self.sock, |
|
330 strict=self.strict, |
|
331 method=self._method) |
|
332 |
|
333 while True: |
|
334 version, status, reason = res._read_status() |
|
335 if status != httplib.CONTINUE: |
|
336 break |
|
337 while True: |
|
338 skip = res.fp.readline().strip() |
|
339 if not skip: |
|
340 break |
|
341 res.status = status |
|
342 res.reason = reason.strip() |
|
343 |
|
344 if res.status == 200: |
|
345 while True: |
|
346 line = res.fp.readline() |
|
347 if line == '\r\n': |
|
348 break |
|
349 return True |
|
350 |
|
351 if version == 'HTTP/1.0': |
|
352 res.version = 10 |
|
353 elif version.startswith('HTTP/1.'): |
|
354 res.version = 11 |
|
355 elif version == 'HTTP/0.9': |
|
356 res.version = 9 |
|
357 else: |
|
358 raise httplib.UnknownProtocol(version) |
|
359 |
|
360 if res.version == 9: |
|
361 res.length = None |
|
362 res.chunked = 0 |
|
363 res.will_close = 1 |
|
364 res.msg = httplib.HTTPMessage(cStringIO.StringIO()) |
|
365 return False |
|
366 |
|
367 res.msg = httplib.HTTPMessage(res.fp) |
|
368 res.msg.fp = None |
|
369 |
|
370 # are we using the chunked-style of transfer encoding? |
|
371 trenc = res.msg.getheader('transfer-encoding') |
|
372 if trenc and trenc.lower() == "chunked": |
|
373 res.chunked = 1 |
|
374 res.chunk_left = None |
|
375 else: |
|
376 res.chunked = 0 |
|
377 |
|
378 # will the connection close at the end of the response? |
|
379 res.will_close = res._check_close() |
|
380 |
|
381 # do we have a Content-Length? |
|
382 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" |
|
383 length = res.msg.getheader('content-length') |
|
384 if length and not res.chunked: |
|
385 try: |
|
386 res.length = int(length) |
|
387 except ValueError: |
|
388 res.length = None |
|
389 else: |
|
390 if res.length < 0: # ignore nonsensical negative lengths |
|
391 res.length = None |
|
392 else: |
|
393 res.length = None |
|
394 |
|
395 # does the body have a fixed length? (of zero) |
|
396 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or |
|
397 100 <= status < 200 or # 1xx codes |
|
398 res._method == 'HEAD'): |
|
399 res.length = 0 |
|
400 |
|
401 # if the connection remains open, and we aren't using chunked, and |
|
402 # a content-length was not provided, then assume that the connection |
|
403 # WILL close. |
|
404 if (not res.will_close and |
|
405 not res.chunked and |
|
406 res.length is None): |
|
407 res.will_close = 1 |
|
408 |
|
409 self.proxyres = res |
|
410 |
|
411 return False |
|
412 |
381 class httphandler(keepalive.HTTPHandler): |
413 class httphandler(keepalive.HTTPHandler): |
382 def http_open(self, req): |
414 def http_open(self, req): |
383 return self.do_open(httpconnection, req) |
415 return self.do_open(httpconnection, req) |
384 |
416 |
385 def _start_transaction(self, h, req): |
417 def _start_transaction(self, h, req): |
386 if req.get_selector() == req.get_full_url(): # has proxy |
418 _generic_start_transaction(self, h, req) |
387 urlparts = urlparse.urlparse(req.get_selector()) |
|
388 if urlparts[0] == 'https': # only use CONNECT for HTTPS |
|
389 if ':' in urlparts[1]: |
|
390 realhost, realport = urlparts[1].split(':') |
|
391 realport = int(realport) |
|
392 else: |
|
393 realhost = urlparts[1] |
|
394 realport = 443 |
|
395 |
|
396 h.realhost = realhost |
|
397 h.realport = realport |
|
398 h.headers = req.headers.copy() |
|
399 h.headers.update(self.parent.addheaders) |
|
400 return keepalive.HTTPHandler._start_transaction(self, h, req) |
|
401 |
|
402 h.realhost = None |
|
403 h.realport = None |
|
404 h.headers = None |
|
405 return keepalive.HTTPHandler._start_transaction(self, h, req) |
419 return keepalive.HTTPHandler._start_transaction(self, h, req) |
406 |
420 |
407 def __del__(self): |
421 def __del__(self): |
408 self.close_all() |
422 self.close_all() |
409 |
423 |
414 class httpsconnection(BetterHTTPS): |
428 class httpsconnection(BetterHTTPS): |
415 response_class = keepalive.HTTPResponse |
429 response_class = keepalive.HTTPResponse |
416 # must be able to send big bundle as stream. |
430 # must be able to send big bundle as stream. |
417 send = _gen_sendfile(BetterHTTPS) |
431 send = _gen_sendfile(BetterHTTPS) |
418 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection) |
432 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection) |
|
433 |
|
434 def connect(self): |
|
435 if self.realhost: # use CONNECT proxy |
|
436 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
437 self.sock.connect((self.host, self.port)) |
|
438 if _generic_proxytunnel(self): |
|
439 self.sock = _ssl_wrap_socket(self.sock, self.cert_file, self.key_file) |
|
440 else: |
|
441 BetterHTTPS.connect(self) |
419 |
442 |
420 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): |
443 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): |
421 def __init__(self, ui): |
444 def __init__(self, ui): |
422 keepalive.KeepAliveHandler.__init__(self) |
445 keepalive.KeepAliveHandler.__init__(self) |
423 urllib2.HTTPSHandler.__init__(self) |
446 urllib2.HTTPSHandler.__init__(self) |
424 self.ui = ui |
447 self.ui = ui |
425 self.pwmgr = passwordmgr(self.ui) |
448 self.pwmgr = passwordmgr(self.ui) |
|
449 |
|
450 def _start_transaction(self, h, req): |
|
451 _generic_start_transaction(self, h, req) |
|
452 return keepalive.KeepAliveHandler._start_transaction(self, h, req) |
426 |
453 |
427 def https_open(self, req): |
454 def https_open(self, req): |
428 self.auth = self.pwmgr.readauthtoken(req.get_full_url()) |
455 self.auth = self.pwmgr.readauthtoken(req.get_full_url()) |
429 return self.do_open(self._makeconnection, req) |
456 return self.do_open(self._makeconnection, req) |
430 |
457 |