Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 3760:b8f52d030f1a
ignore common network errors, like "Connection reset by peer"
catch socket errors when sending http headers.
| author | Alexander Smishlajev <a1s@users.sourceforge.net> |
|---|---|
| date | Wed, 15 Nov 2006 06:27:15 +0000 |
| parents | a2d22d0de0bc |
| children | aef19fff38dd |
comparison
equal
deleted
inserted
replaced
| 3759:3a4cce7e77fb | 3760:b8f52d030f1a |
|---|---|
| 1 # $Id: client.py,v 1.228 2006-11-09 00:36:21 richard Exp $ | 1 # $Id: client.py,v 1.229 2006-11-15 06:27:15 a1s Exp $ |
| 2 | 2 |
| 3 """WWW request handler (also used in the stand-alone server). | 3 """WWW request handler (also used in the stand-alone server). |
| 4 """ | 4 """ |
| 5 __docformat__ = 'restructuredtext' | 5 __docformat__ = 'restructuredtext' |
| 6 | 6 |
| 106 FV_ERROR_MESSAGE = re.compile(r'[@:]error_message') | 106 FV_ERROR_MESSAGE = re.compile(r'[@:]error_message') |
| 107 | 107 |
| 108 # Note: index page stuff doesn't appear here: | 108 # Note: index page stuff doesn't appear here: |
| 109 # columns, sort, sortdir, filter, group, groupdir, search_text, | 109 # columns, sort, sortdir, filter, group, groupdir, search_text, |
| 110 # pagesize, startwith | 110 # pagesize, startwith |
| 111 | |
| 112 # list of network error codes that shouldn't be reported to tracker admin | |
| 113 # (error descriptions from FreeBSD intro(2)) | |
| 114 IGNORE_NET_ERRORS = ( | |
| 115 # A write on a pipe, socket or FIFO for which there is | |
| 116 # no process to read the data. | |
| 117 errno.EPIPE, | |
| 118 # A connection was forcibly closed by a peer. | |
| 119 # This normally results from a loss of the connection | |
| 120 # on the remote socket due to a timeout or a reboot. | |
| 121 errno.ECONNRESET, | |
| 122 # Software caused connection abort. A connection abort | |
| 123 # was caused internal to your host machine. | |
| 124 errno.ECONNABORTED, | |
| 125 # A connect or send request failed because the connected party | |
| 126 # did not properly respond after a period of time. | |
| 127 errno.ETIMEDOUT, | |
| 128 ) | |
| 111 | 129 |
| 112 def __init__(self, instance, request, env, form=None, translator=None): | 130 def __init__(self, instance, request, env, form=None, translator=None): |
| 113 # re-seed the random number generator | 131 # re-seed the random number generator |
| 114 random.seed() | 132 random.seed() |
| 115 self.start = time.time() | 133 self.start = time.time() |
| 817 break | 835 break |
| 818 else: | 836 else: |
| 819 raise ValueError, 'No such action "%s"'%action_name | 837 raise ValueError, 'No such action "%s"'%action_name |
| 820 return action_klass | 838 return action_klass |
| 821 | 839 |
| 840 def _socket_op(self, call, *args, **kwargs): | |
| 841 """Execute socket-related operation, catch common network errors | |
| 842 | |
| 843 Parameters: | |
| 844 call: a callable to execute | |
| 845 args, kwargs: call arguments | |
| 846 | |
| 847 """ | |
| 848 try: | |
| 849 call(*args, **kwargs) | |
| 850 except socket.error, err: | |
| 851 if err.errno not in self.IGNORE_NET_ERRORS: | |
| 852 raise | |
| 853 | |
| 822 def write(self, content): | 854 def write(self, content): |
| 823 if not self.headers_done: | 855 if not self.headers_done: |
| 824 self.header() | 856 self.header() |
| 825 if self.env['REQUEST_METHOD'] != 'HEAD': | 857 if self.env['REQUEST_METHOD'] != 'HEAD': |
| 826 try: | 858 self._socket_op(self.request.wfile.write, content) |
| 827 self.request.wfile.write(content) | |
| 828 except socket.error, error: | |
| 829 # the end-user has gone away | |
| 830 if error.errno != errno.EPIPE: | |
| 831 raise | |
| 832 | 859 |
| 833 def write_html(self, content): | 860 def write_html(self, content): |
| 834 if not self.headers_done: | 861 if not self.headers_done: |
| 835 # at this point, we are sure about Content-Type | 862 # at this point, we are sure about Content-Type |
| 836 self.additional_headers['Content-Type'] = \ | 863 self.additional_headers['Content-Type'] = \ |
| 845 # recode output | 872 # recode output |
| 846 content = content.decode(self.STORAGE_CHARSET, 'replace') | 873 content = content.decode(self.STORAGE_CHARSET, 'replace') |
| 847 content = content.encode(self.charset, 'xmlcharrefreplace') | 874 content = content.encode(self.charset, 'xmlcharrefreplace') |
| 848 | 875 |
| 849 # and write | 876 # and write |
| 850 try: | 877 self._socket_op(self.request.wfile.write, content) |
| 851 self.request.wfile.write(content) | |
| 852 except socket.error, error: | |
| 853 # the end-user has gone away | |
| 854 if error.errno != errno.EPIPE: | |
| 855 raise | |
| 856 | 878 |
| 857 def setHeader(self, header, value): | 879 def setHeader(self, header, value): |
| 858 '''Override a header to be returned to the user's browser. | 880 '''Override a header to be returned to the user's browser. |
| 859 ''' | 881 ''' |
| 860 self.additional_headers[header] = value | 882 self.additional_headers[header] = value |
| 879 cookie = "%s=%s; Path=%s;"%(name, value, path) | 901 cookie = "%s=%s; Path=%s;"%(name, value, path) |
| 880 if expire is not None: | 902 if expire is not None: |
| 881 cookie += " expires=%s;"%Cookie._getdate(expire) | 903 cookie += " expires=%s;"%Cookie._getdate(expire) |
| 882 headers.append(('Set-Cookie', cookie)) | 904 headers.append(('Set-Cookie', cookie)) |
| 883 | 905 |
| 884 self.request.start_response(headers, response) | 906 self._socket_op(self.request.start_response, headers, response) |
| 885 | 907 |
| 886 self.headers_done = 1 | 908 self.headers_done = 1 |
| 887 if self.debug: | 909 if self.debug: |
| 888 self.headers_sent = headers | 910 self.headers_sent = headers |
| 889 | 911 |
