gevent.pywsgi.WSGIServer timeouts

1,172 views
Skip to first unread message

Jim Fulton

unread,
Apr 25, 2012, 9:35:56 AM4/25/12
to gev...@googlegroups.com
Is there any way to make gevent.pywsgi.WSGIServer time out requests
after long periods of client inactivity?

I've observed I can keep requests open indefinitely (> 16 hours) by
not writing request messages or not read ingresponse messages. This
is not good.

I found I could terminate long requests by creating a Timeout object
in an application, however:

- This won't terminate requests that haven't gotten to my app yet, and
- It will terminate requests even if the client is sending or
recieving data.

Jim

--
Jim Fulton
http://www.linkedin.com/in/jimfulton
Jerky is better than bacon! http://www.dublinstore.com/

Denis Bilenko

unread,
Apr 25, 2012, 9:41:30 AM4/25/12
to gev...@googlegroups.com
On Wed, Apr 25, 2012 at 5:35 PM, Jim Fulton <j...@zope.com> wrote:
> Is there any way to make gevent.pywsgi.WSGIServer time out requests
> after long periods of client inactivity?

Perhaps using socket's timeout would work for you?

class MyServer(pywsgi.WSGIServer):

def handle(self, socket, address):
socket.settimeout(<your timeout>)
return pywsgi.WSGIServer.handle(self, socket, address)


Let me know if that works for you. I'm thinking maybe we should have
this functionality in the server itself, configured via timeout
argument to WSGIServer.__init__().

Jim Fulton

unread,
Apr 25, 2012, 11:24:51 AM4/25/12
to gev...@googlegroups.com
On Wed, Apr 25, 2012 at 9:41 AM, Denis Bilenko <denis....@gmail.com> wrote:
> On Wed, Apr 25, 2012 at 5:35 PM, Jim Fulton <j...@zope.com> wrote:
>> Is there any way to make gevent.pywsgi.WSGIServer time out requests
>> after long periods of client inactivity?
>
> Perhaps using socket's timeout would work for you?
>
> class MyServer(pywsgi.WSGIServer):
>
> def handle(self, socket, address):
> socket.settimeout(<your timeout>)
> return pywsgi.WSGIServer.handle(self, socket, address)
>
>
> Let me know if that works for you.

It works great for output. It doesn't seem to work for input.

Here's my server script:

import gevent.pywsgi, wsgiref.simple_server

class MyServer(gevent.pywsgi.WSGIServer):

def handle(self, socket, address):
socket.settimeout(10.0)
return gevent.pywsgi.WSGIServer.handle(self, socket, address)

server = MyServer(('127.0.0.1', 0), wsgiref.simple_server.demo_app)
server.start()
print server.server_port
gevent.sleep(99999)
server.serve_forever()

If I connect to it, but don't send complete input:

>>> s = gevent.socket.create_connection(('localhost', port))
>>> s.sendall('POST /hi.html HTTP/1.0\r\n')

It never times out.

Similarly if I send all headers, and partial body:

>>> s = gevent.socket.create_connection(('localhost', port))
>>> s.sendall('POST / HTTP/1.0\r\n')
>>> s.sendall('Content-Length: 99999\r\n')
>>> s.sendall('\r\n')
>>> s.sendall('a'*1000)

> I'm thinking maybe we should have
> this functionality in the server itself, configured via timeout
> argument to WSGIServer.__init__().

+1

Jim Fulton

unread,
Apr 25, 2012, 11:55:39 AM4/25/12
to gev...@googlegroups.com
On Wed, Apr 25, 2012 at 11:24 AM, Jim Fulton <j...@zope.com> wrote:
> On Wed, Apr 25, 2012 at 9:41 AM, Denis Bilenko <denis....@gmail.com> wrote:
>> On Wed, Apr 25, 2012 at 5:35 PM, Jim Fulton <j...@zope.com> wrote:
>>> Is there any way to make gevent.pywsgi.WSGIServer time out requests
>>> after long periods of client inactivity?
>>
>> Perhaps using socket's timeout would work for you?

...

>> Let me know if that works for you.
>
> It works great for output. Â

Well, with one relatively minor gripe. For response/output timeouts,
I get output like:

Traceback (most recent call last):
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/pywsgi.py",
line 447, in handle_one_response
self.run_application()
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/pywsgi.py",
line 434, in run_application
self.process_result()
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/pywsgi.py",
line 425, in process_result
self.write(data)
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/pywsgi.py",
line 378, in write
self.socket.sendall(msg)
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/socket.py",
line 474, in sendall
data_sent += self.send(_get_memory(data, data_sent), flags,
timeout=timeleft)
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/socket.py",
line 446, in send
self._wait(self._write_event)
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/socket.py",
line 289, in _wait
self.hub.wait(watcher)
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/hub.py",
line 384, in wait
result = self.switch()
File "/home/jim/.buildout/eggs/gevent-1.0b2-py2.6-linux-x86_64.egg/gevent/hub.py",
line 373, in switch
return greenlet.switch(self)
timeout: timed out
{'GATEWAY_INTERFACE': 'CGI/1.1',
'PATH_INFO': '/gen.html',
'QUERY_STRING': 'size=9951660',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '42062',
'REQUEST_METHOD': 'GET',
'SCRIPT_NAME': '',
'SERVER_NAME': 'localhost',
'SERVER_PORT': '51660',
'SERVER_PROTOCOL': 'HTTP/1.0',
'SERVER_SOFTWARE': 'gevent/1.0 Python/2.6',
'webob._parsed_query_vars': (GET([(u'size', u'9951660')]), 'size=9951660'),
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x7f8ba31321e0>,
'wsgi.input': <gevent.pywsgi.Input object at 0x1ccb7d0>,
'wsgi.multiprocess': False,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)} failed with timeout

127.0.0.1 - - [2012-04-25 11:45:42] "GET /gen.html?size=9951660
HTTP/1.0" socket 204145 10.169218

The traceback and environment listing isn't really helpful. I'd
rather it not be output in this case.

In the access-log line, "socket" isn't a valid status code, although I
don't see what a valid status code would be in this case. If the
server timed out waiting for input, then "408 Request Timeout" would
fit. It's odd that HTTP doesn't provide a code for timing out on
output. Perhaps 408 should apply there too. I definately think it
should be a 4xx status rather than a 5xx.

BTW, I'm using 1.0b2.

Jim Fulton

unread,
Jun 15, 2012, 2:21:57 PM6/15/12
to gev...@googlegroups.com
On Wed, Apr 25, 2012 at 11:24 AM, Jim Fulton <j...@zope.com> wrote:
> On Wed, Apr 25, 2012 at 9:41 AM, Denis Bilenko <denis....@gmail.com> wrote:
>> On Wed, Apr 25, 2012 at 5:35 PM, Jim Fulton <j...@zope.com> wrote:
>>> Is there any way to make gevent.pywsgi.WSGIServer time out requests
>>> after long periods of client inactivity?
>>
>> Perhaps using socket's timeout would work for you?
>>
>> class MyServer(pywsgi.WSGIServer):
>>
>>     def handle(self, socket, address):
>>         socket.settimeout(<your timeout>)
>>         return pywsgi.WSGIServer.handle(self, socket, address)
>>
>>
>> Let me know if that works for you.
>
> It works great for output.  It doesn't seem to work for input.

It works for input on trunk. Did you fix it on purpose? :)

When timing out on input, the status is set to 400, because you don't
catch timeout errors.
I think this should be a 408, but it's not a big deal to me.

Output timeouts still say "socket" in the access log, rather than
providing a status. At this point, a real status, likely a 200, has
probably been sent to the client. I suspect the original status
should be used in this case, but again, this isn't a big deal to me.

Any chance we can get a 1.0b3 soon? It's been a while since 1.0b2.

I've actually had a problem with requests "leaking" due to a lack of timeouts.
Jerky is better than bacon! http://zo.pe/Kqm

Jim Fulton

unread,
Jun 15, 2012, 3:42:35 PM6/15/12
to gev...@googlegroups.com
On Fri, Jun 15, 2012 at 2:21 PM, Jim Fulton <j...@zope.com> wrote:
...
> Any chance we can get a 1.0b3 soon? It's been a while since 1.0b2.

Is the trunk thought to be stable (at least as stable as 1.0b2)?
Reply all
Reply to author
Forward
0 new messages