Edgewall Software
Modify

Opened 20 years ago

Closed 16 years ago

Last modified 12 years ago

#1183 closed enhancement (fixed)

[PATCH] Handle disconnecting tracd clients more gracefully.

Reported by: pjd@… Owned by: Christian Boos
Priority: high Milestone: 0.11.5
Component: web frontend/tracd Version: 0.8
Severity: normal Keywords: python23
Cc: Branch:
Release Notes:
API Changes:
Internal Changes:

Description

Currently, when a client disconnects unexpectedly, tracd prints a needlessly verbose traceback:

----------------------------------------
Exception happened during processing of request from ('1.2.3.4', 5678)
Traceback (most recent call last):
  File "/usr/local/lib/python2.4/SocketServer.py", line 463, in process_request_thread
    self.finish_request(request, client_address)
  File "/usr/local/lib/python2.4/SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/local/lib/python2.4/SocketServer.py", line 521, in __init__
    self.handle()
  File "/usr/local/lib/python2.4/BaseHTTPServer.py", line 314, in handle
    self.handle_one_request()
  File "/usr/local/lib/python2.4/BaseHTTPServer.py", line 308, in handle_one_request
    method()
  File "/usr/local/bin/tracd", line 227, in do_GET
    self.do_trac_req()
  File "/usr/local/bin/tracd", line 238, in do_trac_req
    trac.core.send_pretty_error(e, self.env, self)
  File "/usr/local/lib/python2.4/site-packages/trac/core.py", line 494, in send_pretty_error
    req.send_response(500)
  File "/usr/local/lib/python2.4/BaseHTTPServer.py", line 364, in send_response
    self.wfile.write("%s %d %s\r\n" %
  File "/usr/local/lib/python2.4/socket.py", line 256, in write
    self.flush()
  File "/usr/local/lib/python2.4/socket.py", line 243, in flush
    self._sock.sendall(buffer)
error: (32, 'Broken pipe')
----------------------------------------

With the following patch, tracd will log a short message instead, which is about all one can do in this situation.

--- tracd.orig  Tue Jan 18 13:26:40 2005
+++ tracd       Thu Feb  3 12:03:30 2005
@@ -32,6 +32,7 @@
 import shutil
 import getopt
 import locale
+import socket, errno
 import urllib
 import urllib2
 import mimetypes
@@ -235,7 +236,14 @@
         try:
             self.do_real_trac_req()
         except Exception, e:
-            trac.core.send_pretty_error(e, self.env, self)
+            try:
+                trac.core.send_pretty_error(e, self.env, self)
+            except socket.error, (code, msg):
+                if code == errno.EPIPE:
+                    self.log_message('Lost connection to client: %s', self.address_string())
+                else:
+                    raise

     def do_real_trac_req(self):
         start = time.time()

Attachments (1)

trap_connection_interrupted-r4510.diff (1.6 KB ) - added by Christian Boos 18 years ago.
Reimplement silencing of connection interrupted errors in the request dispatcher

Download all attachments as: .zip

Change History (16)

comment:1 by Mark Rowe, 20 years ago

The patch below has been synced with trunk at r1502. It also handles the EPIPE that can occur during dispatch_request:

=== trac/web/standalone.py
==================================================================
--- trac/web/standalone.py  (revision 1281)
+++ trac/web/standalone.py  (local)
@@ -33,6 +33,7 @@
 import sys
 import md5
 import time
+import socket, errno
 import shutil
 import urllib
 import urllib2
@@ -245,8 +246,19 @@
             start = time.time()
             dispatch_request(path_info, req, env)
             self.log.debug('Total request time: %f s', time.time() - start)
+        except socket.error, (code, msg):
+            if code == errno.EPIPE:
+                self.log_message('Lost connection to client: %s', self.address_string())
+            else:
+                raise
         except Exception, e:
-            send_pretty_error(e, env, req)
+            try:
+                send_pretty_error(e, env, req)
+            except socket.error, (code, msg):
+                if code == errno.EPIPE:
+                    self.log_message('Lost connection to client: %s', self.address_string())
+                else:
+                    raise
 
 
 class TracHTTPRequest(Request):

comment:2 by Christian Boos, 20 years ago

This one is even better at removing spurious stack traces, and works also on Windows.

It also fixes the long standing 'Conten-Length' misspelling!

  • trac/web/standalone.py

     
    3333import sys
    3434import md5
    3535import time
     36import socket, errno
    3637import shutil
    3738import urllib
    3839import urllib2
     
    192193        except IOError:
    193194            self.send_error(404, path)
    194195            return
    195         self.send_response(200)
    196         mtype, enc = mimetypes.guess_type(filename)
    197         stat = os.fstat(f.fileno())
    198         content_length = stat[6]
    199         last_modified = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
    200                                       time.gmtime(stat[8]))
    201         self.send_header('Content-Type', mtype)
    202         self.send_header('Conten-Length', str(content_length))
    203         self.send_header('Last-Modified', last_modified)
    204         self.end_headers()
    205         shutil.copyfileobj(f, self.wfile)
    206 
     196        try:
     197            self.send_response(200)
     198            mtype, enc = mimetypes.guess_type(filename)
     199            stat = os.fstat(f.fileno())
     200            content_length = stat[6]
     201            last_modified = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
     202                                          time.gmtime(stat[8]))
     203            self.send_header('Content-Type', mtype)
     204            self.send_header('Content-Length', str(content_length))
     205            self.send_header('Last-Modified', last_modified)
     206            self.end_headers()
     207            shutil.copyfileobj(f, self.wfile)
     208        except socket.error, (code, msg):
     209            if code == errno.EPIPE or code == 10053: # Windows
     210                self.log_message('Lost connection to client: %s', self.address_string())
     211            else:
     212                raise
     213
    207214    def do_POST(self):
    208215        self.do_trac_req()
    209216
     
    245252            start = time.time()
    246253            dispatch_request(path_info, req, env)
    247254            self.log.debug('Total request time: %f s', time.time() - start)
     255        except socket.error, (code, msg):
     256            if code == errno.EPIPE or code == 10053: # Windows
     257                self.log_message('Lost connection to client: %s', self.address_string())
     258            else:
     259                raise
    248260        except Exception, e:
    249             send_pretty_error(e, env, req)
     261            try:
     262                send_pretty_error(e, env, req)
     263            except socket.error, (code, msg):
     264                if code == errno.EPIPE or code == 10053: # Windows
     265                    self.log_message('Lost connection to client: %s', self.address_string())
     266                else:
     267                    raise
    250268
    251269
    252270class TracHTTPRequest(Request):

Should I commit it?

comment:3 by Mark Rowe, 20 years ago

Owner: changed from Jonas Borgström to Christian Boos

Sure. I was also wondering if this patch helped make tracd more stable for you in relation to #1401?

comment:4 by Christian Boos, 20 years ago

Resolution: fixed
Status: newclosed

I committed it [1503].

I also hoped it would help for #1401, but unfortunately it didn't help (it still crashes even on Linux).

comment:5 by Christian Boos, 18 years ago

Resolution: fixed
Status: closedreopened

Was 0.9, but looks like the WSGI changes got rid of this…

Exception happened during processing of request from ('127.0.0.1', 2939)
Traceback (most recent call last):
  File "C:\Program Files\ActiveState\Python-2.4\lib\SocketServer.py", line 463, in process_request_thread
    self.finish_request(request, client_address)
  File "C:\Program Files\ActiveState\Python-2.4\lib\SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\ActiveState\Python-2.4\lib\SocketServer.py", line 521, in __init__
    self.handle()
  File "C:\Program Files\ActiveState\Python-2.4\lib\BaseHTTPServer.py", line 316, in handle
    self.handle_one_request()
  File "C:\Workspace\src\trac\branches\0.10-stable\trac\web\wsgi.py", line 174, in handle_one_request
    gateway.run(self.server.application)
  File "C:\Workspace\src\trac\branches\0.10-stable\trac\web\wsgi.py", line 95, in run
    self._write(chunk)
  File "C:\Workspace\src\trac\branches\0.10-stable\trac\web\wsgi.py", line 195, in _write
    self.handler.send_response(int(status[:3]))
  File "C:\Program Files\ActiveState\Python-2.4\lib\BaseHTTPServer.py", line 370, in send_response
    self.send_header('Server', self.version_string())
  File "C:\Program Files\ActiveState\Python-2.4\lib\BaseHTTPServer.py", line 377, in send_header
    self.wfile.write("%s: %s\r\n" % (keyword, value))
  File "C:\Program Files\ActiveState\Python-2.4\lib\socket.py", line 256, in write
    self.flush()
  File "C:\Program Files\ActiveState\Python-2.4\lib\socket.py", line 243, in flush
127.0.0.1 - - [08/Jan/2007 10:05:04] "GET /devel/timeline HTTP/1.1" 200 -
    self._sock.sendall(buffer)
error: (10053, 'Software caused connection abort')

(the above with 0.10.4dev)

by Christian Boos, 18 years ago

Reimplement silencing of connection interrupted errors in the request dispatcher

comment:6 by Christian Boos, 18 years ago

Keywords: review added
Milestone: 0.10.4
Priority: lownormal

In attachment:trap_connection_interrupted-r4510.diff, I did this error catching in trac/web/main.py, as doing it in wsgi.py didn't work reliably.

Doing:

  • trac/web/wsgi.py

     
    171171    def handle_one_request(self):
    172172        environ = self.setup_environ()
    173173        gateway = self.server.gateway(self, environ)
    174         gateway.run(self.server.application)
     174        import errno, socket
     175        try:
     176            gateway.run(self.server.application)
     177        except socket.error, (code, msg):
     178            if code == errno.EPIPE or code == 10053: # Windows
     179                pass
     180            else:
     181                raise
    175182
    176183    def finish(self):
    177184        """We need to help the garbage collector a little."""

didn't prevent the following kind of exception:

Traceback (most recent call last):
  File "C:\Workspace\src\trac\trunk\trac\web\main.py", line 395, in dispatch_request
    dispatcher.dispatch(req)
  File "C:\Workspace\src\trac\trunk\trac\web\main.py", line 231, in dispatch
    req.send(output, content_type or 'text/html')
  File "C:\Workspace\src\trac\trunk\trac\web\api.py", line 313, in send
    self.write(content)
  File "C:\Workspace\src\trac\trunk\trac\web\api.py", line 410, in write
    self._write(data)
  File "C:\Workspace\src\trac\trunk\trac\web\wsgi.py", line 202, in _write
    self.handler.send_response(int(status[:3]))
  File "C:\Program Files\ActiveState\Python-2.4\lib\BaseHTTPServer.py", line 370, in send_response
    self.send_header('Server', self.version_string())
  File "C:\Program Files\ActiveState\Python-2.4\lib\BaseHTTPServer.py", line 377, in send_header
    self.wfile.write("%s: %s\r\n" % (keyword, value))
  File "C:\Program Files\ActiveState\Python-2.4\lib\socket.py", line 256, in write
    self.flush()
  File "C:\Program Files\ActiveState\Python-2.4\lib\socket.py", line 243, in flush
    self._sock.sendall(buffer)
error: (10053, 'Software caused connection abort')

By the way, while testing this patch, I've noticed it's quite easy to "saturate" tracd and have all the db connections in use; new requests are then blocking on the pool lock and after a while the timeout exception is raised. This is a separate issue, though, but easy to reproduce when you keep the "F5" key down ;)

comment:7 by Christian Boos, 18 years ago

Milestone: 0.10.50.11

in reply to:  6 comment:8 by Christian Boos, 17 years ago

Keywords: review removed
Priority: normalhigh

Replying to cboos:

… I've noticed it's quite easy to "saturate" tracd and have all the db connections in use; new requests are then blocking on the pool lock and after a while the timeout exception is raised. This is a separate issue, though, but easy to reproduce when you keep the "F5" key down ;)

r6261 seems to have fixed that.

comment:9 by Christian Boos, 16 years ago

Milestone: 0.11-retriage0.11.5

Move high prio tickets from 0.11-retriage to next maintenance release.

comment:10 by Christian Boos, 16 years ago

Resolution: fixed
Status: reopenedclosed

Fixed in r8080. Before writing to the client, we first check if the file object is not already closed.

comment:11 by Christian Boos, 16 years ago

Keywords: python23 added

Note that the fix doesn't work with Python 2.3 (see r8128).

comment:12 by anonymous, 15 years ago

Component: web frontend/tracdversion control/log view

comment:13 by Christian Boos, 15 years ago

Component: version control/log viewweb frontend/tracd

Please stop messing with this server, thanks.

comment:14 by Christian Boos, 15 years ago

See also #8582.

comment:15 by Christian Boos, 12 years ago

And #11049.

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain Christian Boos.
The resolution will be deleted. Next status will be 'reopened'.
to The owner will be changed from Christian Boos to the specified user.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.