##// END OF EJS Templates
keepalive: handle broken pipes gracefully during large POSTs
Augie Fackler -
r9726:430e59ff default
parent child Browse files
Show More
@@ -23,6 +23,9 b''
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
@@ -108,10 +111,11 b' EXTRA ATTRIBUTES AND METHODS'
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
@@ -495,10 +499,76 b' class HTTPResponse(httplib.HTTPResponse)'
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
@@ -408,10 +408,14 b' class httphandler(keepalive.HTTPHandler)'
408 self.close_all()
408 self.close_all()
409
409
410 if has_https:
410 if has_https:
411 class httpsconnection(httplib.HTTPSConnection):
411 class BetterHTTPS(httplib.HTTPSConnection):
412 send = keepalive.safesend
413
414 class httpsconnection(BetterHTTPS):
412 response_class = keepalive.HTTPResponse
415 response_class = keepalive.HTTPResponse
413 # must be able to send big bundle as stream.
416 # must be able to send big bundle as stream.
414 send = _gen_sendfile(httplib.HTTPSConnection)
417 send = _gen_sendfile(BetterHTTPS)
418 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
415
419
416 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
420 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
417 def __init__(self, ui):
421 def __init__(self, ui):
General Comments 0
You need to be logged in to leave comments. Login now