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