Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python BaseHTTPServer, how do I catch/trap "broken pipe" errors?

I build a short url translator engine in Python, and I'm seeing a TON of "broken pipe" errors, and I'm curious how to trap it best when using the BaseHTTPServer classes. This isn't the entire code, but gives you an idea of what I'm doing so far:

    from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
    import memcache

    class clientThread(BaseHTTPRequestHandler):

            def do_GET(self):
                    content = None
                    http_code,response_txt,long_url = \
                            self.ag_trans_url(self.path,content,'GET')
                    self.http_output( http_code, response_txt, long_url )
                    return

            def http_output(self,http_code,response_txt,long_url):
                    self.send_response(http_code)
                    self.send_header('Content-type','text/plain')
                    if long_url:
                            self.send_header('Location', long_url)
                    self.end_headers()
                    if response_txt:
                            self.wfile.write(response_txt)
                    return

        def ag_trans_url(self, orig_short_url, post_action, getpost):
                short_url = 'http://foo.co' + orig_short_url

                # fetch it from memcache
                long_url = mc.get(short_url)

                # other magic happens to look it up from db if there was nothing
                # in memcache, etc
                return (302, None, log_url)

def populate_memcache()
        # connect to db, do lots of mc.set() calls

def main():
        populate_memcache()
        try:
                port = 8001
                if len(sys.argv) > 1:
                        port = int(sys.argv[1])
                server = HTTPServer(('',port), clientThread)
                #server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                print '[',str(datetime.datetime.now()),'] short url processing has begun'

                server.serve_forever()
        except KeyboardInterrupt,SystemExit:
                print '^C received, shutting down server'
                server.socket.close()

The code itself works great, but started throwing errors almost immediately when in production:

Traceback (most recent call last):
  File "/usr/lib/python2.5/SocketServer.py", line 222, in handle_request
    self.process_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 241, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.5/SocketServer.py", line 522, in __init__
    self.handle()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 316, in handle
    self.handle_one_request()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 310, in handle_one_request
    method()
  File "/opt/short_url_redirector/shorturl.py", line 38, in do_GET
    self.http_output( http_code, response_txt, long_url )
  File "/opt/short_url_redirector/shorturl.py", line 52, in http_output
    self.send_response(http_code)
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 370, in send_response
    self.send_header('Server', self.version_string())
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 376, in send_header
    self.wfile.write("%s: %s\r\n" % (keyword, value))
  File "/usr/lib/python2.5/socket.py", line 274, in write
    self.flush()
  File "/usr/lib/python2.5/socket.py", line 261, in flush
    self._sock.sendall(buffer)
error: (32, 'Broken pipe')

The bulk of these errors seem to stem from having a problem calling the send_header() method where all I'm writing out is this:

self.send_header('Location', long_url)

So I'm curious where in my code to try to trap for this IO exception... do I write try/except calls around each of the self.send_header/self.end_headers/self.wfile.write calls? The other error I see from time to time is this one, but not sure which exception to watch to even catch this:

Traceback (most recent call last):
  File "/usr/lib/python2.5/SocketServer.py", line 222, in handle_request
    self.process_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 241, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.5/SocketServer.py", line 522, in __init__
    self.handle()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 316, in handle
    self.handle_one_request()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 299, in handle_one_request
    self.raw_requestline = self.rfile.readline()
  File "/usr/lib/python2.5/socket.py", line 381, in readline
    data = self._sock.recv(self._rbufsize)
error: (104, 'Connection reset by peer')
like image 575
iandouglas Avatar asked May 19 '11 18:05

iandouglas


2 Answers

This appears to be a bug in SocketServer, see this link Python Bug: 14574

A fix (works for me in Python 2.7) is to override the SocketServer.StreamRequestHandler finish() method, something like this:

...
def finish(self,*args,**kw):
  try:
    if not self.wfile.closed:
      self.wfile.flush()
      self.wfile.close()
  except socket.error:
    pass
  self.rfile.close()

  #Don't call the base class finish() method as it does the above
  #return SocketServer.StreamRequestHandler.finish(self)
like image 149
Jason M Avatar answered Sep 18 '22 12:09

Jason M


The "broken pipe" exception means that your code tried to write to a socket/pipe which the other end has closed. If the other end is a web browser, the user could have stopped the request. You can ignore the traceback; it does not indicate a serious problem. If you want to suppress the message, you can put a try ... except block around all of the code in your http_output function, and log the exception if you like.

Additionally, if you want your HTTP server to process more than one request at a time, you need your server class to use one of the SocketServer.ForkingMixIn and SocketServer.ThreadingMixIn classes. Check the documentation of the SocketServer module for details.

Add: The "connection reset by peer" exception means that your code tried to read from a dead socket. If you want to suppress the traceback, you will need to extend the BaseHTTPServer class and override the handle_one_request method to add a try ... except block. You will need a new server class anyway, to implement the earlier suggestion about processing more than one request at a time.

like image 29
Kushal Kumaran Avatar answered Sep 19 '22 12:09

Kushal Kumaran