Show More
@@ -262,108 +262,11 b' class httpconnection(keepalive.HTTPConne' | |||
|
262 | 262 | # must be able to send big bundle as stream. |
|
263 | 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 | 265 | def connect(self): |
|
363 | 266 | if has_https and self.realhost: # use CONNECT proxy |
|
364 | 267 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
365 | 268 | self.sock.connect((self.host, self.port)) |
|
366 |
if self |
|
|
269 | if _generic_proxytunnel(self): | |
|
367 | 270 | # we do not support client x509 certificates |
|
368 | 271 | self.sock = _ssl_wrap_socket(self.sock, None, None) |
|
369 | 272 | else: |
@@ -378,30 +281,141 b' class httpconnection(keepalive.HTTPConne' | |||
|
378 | 281 | return proxyres |
|
379 | 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 | 413 | class httphandler(keepalive.HTTPHandler): |
|
382 | 414 | def http_open(self, req): |
|
383 | 415 | return self.do_open(httpconnection, req) |
|
384 | 416 | |
|
385 | 417 | def _start_transaction(self, h, req): |
|
386 | if req.get_selector() == req.get_full_url(): # has proxy | |
|
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 | |
|
418 | _generic_start_transaction(self, h, req) | |
|
405 | 419 | return keepalive.HTTPHandler._start_transaction(self, h, req) |
|
406 | 420 | |
|
407 | 421 | def __del__(self): |
@@ -417,6 +431,15 b' if has_https:' | |||
|
417 | 431 | send = _gen_sendfile(BetterHTTPS) |
|
418 | 432 | getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection) |
|
419 | 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) | |
|
442 | ||
|
420 | 443 | class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): |
|
421 | 444 | def __init__(self, ui): |
|
422 | 445 | keepalive.KeepAliveHandler.__init__(self) |
@@ -424,6 +447,10 b' if has_https:' | |||
|
424 | 447 | self.ui = ui |
|
425 | 448 | self.pwmgr = passwordmgr(self.ui) |
|
426 | 449 | |
|
450 | def _start_transaction(self, h, req): | |
|
451 | _generic_start_transaction(self, h, req) | |
|
452 | return keepalive.KeepAliveHandler._start_transaction(self, h, req) | |
|
453 | ||
|
427 | 454 | def https_open(self, req): |
|
428 | 455 | self.auth = self.pwmgr.readauthtoken(req.get_full_url()) |
|
429 | 456 | return self.do_open(self._makeconnection, req) |
General Comments 0
You need to be logged in to leave comments.
Login now