7

In my python script, I am trying to run a web server:

server = BaseHTTPServer.HTTPServer(('127.0.0.1',8080), RequestHandler)

I have a request handler class:

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        # Doing Some Stuff.

Now I always wait for some data to catch in do_GET. I want to implement a timeout operation where I want this web server to close after lets say 60 seconds. I am not able to implement that. Please suggest me how can I implement auto shut down operation for web server in this scenario.

Thanks Tara Singh

SimonJ
  • 21,076
  • 1
  • 35
  • 50
Tara Singh
  • 1,821
  • 6
  • 28
  • 36

8 Answers8

12

Assuming I've understood your question correctly, you can't implement a read timeout in do_GET since the request has already been read by the time this method is called.

Since BaseHTTPRequestHandler extends StreamRequestHandler which in turn extends BaseRequestHandler, you can override setup() to initialise the socket with a timeout:

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  def setup(self):
    BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
    self.request.settimeout(60)

  def do_GET(self):
    # ...
SimonJ
  • 21,076
  • 1
  • 35
  • 50
  • I already tried this, its not working. Do I need to override any other method along with setup? I just want server to shut down on its own no matter what is is processing after lets say 60 seconds. – Tara Singh Dec 12 '10 at 00:49
  • 4
    You should do `self.timeout = 60` before calling the base class' `setup()` method (internally it makes a call to `settimeout` based on this setting.) – malthe Apr 22 '15 at 09:50
7

I actually found that setting a value for self.timeout in def setup didn't work if I then called the superclass. It looks like the Handler's setup and init methods aren't called during creation of the HTTPServer instance. I used pydevd to confirm this.

So, I took a backward step:

httpd = BaseHTTPServer.HTTPServer(server_address, MyHttpHandler)
httpd.timeout = 10

Which works perfectly, and without overriding core code, or building your own derivative classes. It looks like you'd have to overwrite the HTTPServer code, rather than the Handler code, if you wanted to do it that way.

TinBane
  • 866
  • 11
  • 19
  • Can somebody confirm that this works? It looks to be by far the easiest solution! – Pylinux Oct 24 '16 at 07:52
  • I'm using it on production code (python 2.7.11). If it wasn't working for me, then my code wouldn't work. I've tested varying the figure, and it's easy to see it working, because when I remove it my process hangs until it gets an http request, which could be hours later. – TinBane Oct 27 '16 at 00:01
  • 4
    This does **not** work if you call [serve_forever](https://docs.python.org/2.7/library/socketserver.html#SocketServer.BaseServer.serve_forever) afterwards (which most people probably do). Setting the `timeout` attribute of the `StreamRequestHandler` (as others have advised) always works though (but it's not [documented](https://github.com/python/cpython/blob/4845aa6ef813313051db34726afc76c0e7dc5fee/Lib/SocketServer.py#L691)). – Tey' Nov 19 '18 at 20:49
  • 1
    Worked for my use case. I'm using httpd.handle_request(). Tried various ways until I found this. Thanks! – AdrianTut Dec 06 '19 at 17:33
  • That's interesting, so the behaviour is different whether you are using serve_forever or handle_request? I wonder if that's intended or not. – TinBane May 07 '20 at 05:36
3
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    timeout = 60 # StreamRequestHandler.setup
    def do_GET(self):
        # Doing Some Stuff.
  • 2
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit](https://meta.stackoverflow.com/posts/360251/edit) your answer to add some explanation, including the assumptions you’ve made. – Maximilian Peters May 14 '18 at 13:13
2
timeout = 0.1  # seconds

class WebHTTPServer(BaseHTTPServer.HTTPServer):
    def server_bind(self):
        BaseHTTPServer.HTTPServer.server_bind(self)
        self.socket.settimeout(timeout)

class WebReqHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    <etc> 

if __name__ == '__main__':
    server = WebHTTPServer(('',port), WebReqHandler)
    while 1:
        server.handle_request()
        <do other things>
2

As pointed out by Tey' in TinBane's answer, timeout attribute will not work with serve_forever() method, as stated in the doc:

server_forever

The workaround is to use a custom loop for handling the request as pointed out by user3483992

while True: server.handle_request()

handle_request() will then trigger the method handle_timeout() at the end of the given timeout as expected:

enter image description here

...except the handle_timeout method is doing nothing:

enter image description here

A solution is then to provide another implementation for this method, such as (credit here):

server.handle_timeout = lambda: (_ for _ in ()).throw(TimeoutError())

In short:

try:
     server = HTTPServer(('', PORT_NUMBER), `yourAwesomeRequestHandler`)
     server.timeout = 10
     server.handle_timeout = lambda: (_ for _ in ()).throw(TimeoutError())
     while True: server.handle_request()
except TimeoutError:
    // TODO
Gregoire Cattan
  • 555
  • 7
  • 19
2

I managed to get timeouts working for HTTP requests with

self.rfile._sock.settimeout(60)

Hope this helps

tul
  • 1,709
  • 16
  • 15
1
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, request, client_address, server):
    BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server)   
    self.timeout = 60
  • The `self.timeout = 60` needs to be set before the call to the super class's constructor in order to work properly. – Mo2 Feb 15 '16 at 07:45
0

Given the popularity of python's http.server module, a minimal drop-in copy-pasteable replacement for python3 -m http.server with a timeout is likely useful for others that find this page:

~20 second timeout:

python3 -c "from http.server import HTTPServer, SimpleHTTPRequestHandler
import time
start = time.time()
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
while not time.time() - start > 20:
    httpd.handle_request()"
Brett Holman
  • 743
  • 6
  • 18