Mercurial > hg
comparison mercurial/keepalive.py @ 9726:430e59ff3437
keepalive: handle broken pipes gracefully during large POSTs
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Mon, 02 Nov 2009 11:03:22 -0500 |
parents | 908c5906091b |
children | 08a0f04b56bd |
comparison
equal
deleted
inserted
replaced
9725:3f522d2fa633 | 9726:430e59ff3437 |
---|---|
21 # - fix for digest auth (inspired from urllib2.py @ Python v2.4) | 21 # - fix for digest auth (inspired from urllib2.py @ Python v2.4) |
22 # Modified by Dirkjan Ochtman: | 22 # Modified by Dirkjan Ochtman: |
23 # - import md5 function from a local util module | 23 # - import md5 function from a local util module |
24 # Modified by Martin Geisler: | 24 # Modified by Martin Geisler: |
25 # - moved md5 function from local util module to this module | 25 # - moved md5 function from local util module to this module |
26 # Modified by Augie Fackler: | |
27 # - add safesend method and use it to prevent broken pipe errors | |
28 # on large POST requests | |
26 | 29 |
27 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive. | 30 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive. |
28 | 31 |
29 >>> import urllib2 | 32 >>> import urllib2 |
30 >>> from keepalive import HTTPHandler | 33 >>> from keepalive import HTTPHandler |
106 | 109 |
107 """ | 110 """ |
108 | 111 |
109 # $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $ | 112 # $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $ |
110 | 113 |
111 import urllib2 | 114 import errno |
112 import httplib | 115 import httplib |
113 import socket | 116 import socket |
114 import thread | 117 import thread |
118 import urllib2 | |
115 | 119 |
116 DEBUG = None | 120 DEBUG = None |
117 | 121 |
118 import sys | 122 import sys |
119 if sys.version_info < (2, 4): HANDLE_ERRORS = 1 | 123 if sys.version_info < (2, 4): HANDLE_ERRORS = 1 |
493 total += len(line) | 497 total += len(line) |
494 if sizehint and total >= sizehint: | 498 if sizehint and total >= sizehint: |
495 break | 499 break |
496 return list | 500 return list |
497 | 501 |
502 def safesend(self, str): | |
503 """Send `str' to the server. | |
504 | |
505 Shamelessly ripped off from httplib to patch a bad behavior. | |
506 """ | |
507 # _broken_pipe_resp is an attribute we set in this function | |
508 # if the socket is closed while we're sending data but | |
509 # the server sent us a response before hanging up. | |
510 # In that case, we want to pretend to send the rest of the | |
511 # outgoing data, and then let the user use getresponse() | |
512 # (which we wrap) to get this last response before | |
513 # opening a new socket. | |
514 if getattr(self, '_broken_pipe_resp', None) is not None: | |
515 return | |
516 | |
517 if self.sock is None: | |
518 if self.auto_open: | |
519 self.connect() | |
520 else: | |
521 raise httplib.NotConnected() | |
522 | |
523 # send the data to the server. if we get a broken pipe, then close | |
524 # the socket. we want to reconnect when somebody tries to send again. | |
525 # | |
526 # NOTE: we DO propagate the error, though, because we cannot simply | |
527 # ignore the error... the caller will know if they can retry. | |
528 if self.debuglevel > 0: | |
529 print "send:", repr(str) | |
530 try: | |
531 blocksize=8192 | |
532 if hasattr(str,'read') : | |
533 if self.debuglevel > 0: print "sendIng a read()able" | |
534 data=str.read(blocksize) | |
535 while data: | |
536 self.sock.sendall(data) | |
537 data=str.read(blocksize) | |
538 else: | |
539 self.sock.sendall(str) | |
540 except socket.error, v: | |
541 reraise = True | |
542 if v[0] == errno.EPIPE: # Broken pipe | |
543 if self._HTTPConnection__state == httplib._CS_REQ_SENT: | |
544 self._broken_pipe_resp = None | |
545 self._broken_pipe_resp = self.getresponse() | |
546 reraise = False | |
547 self.close() | |
548 if reraise: | |
549 raise | |
550 | |
551 def wrapgetresponse(cls): | |
552 """Wraps getresponse in cls with a broken-pipe sane version. | |
553 """ | |
554 def safegetresponse(self): | |
555 # In safesend() we might set the _broken_pipe_resp | |
556 # attribute, in which case the socket has already | |
557 # been closed and we just need to give them the response | |
558 # back. Otherwise, we use the normal response path. | |
559 r = getattr(self, '_broken_pipe_resp', None) | |
560 if r is not None: | |
561 return r | |
562 return cls.getresponse(self) | |
563 safegetresponse.__doc__ = cls.getresponse.__doc__ | |
564 return safegetresponse | |
498 | 565 |
499 class HTTPConnection(httplib.HTTPConnection): | 566 class HTTPConnection(httplib.HTTPConnection): |
500 # use the modified response class | 567 # use the modified response class |
501 response_class = HTTPResponse | 568 response_class = HTTPResponse |
569 send = safesend | |
570 getresponse = wrapgetresponse(httplib.HTTPConnection) | |
571 | |
502 | 572 |
503 ######################################################################### | 573 ######################################################################### |
504 ##### TEST FUNCTIONS | 574 ##### TEST FUNCTIONS |
505 ######################################################################### | 575 ######################################################################### |
506 | 576 |