8

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

10 Answers 10

13

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):
    # ...
Sign up to request clarification or add additional context in comments.

2 Comments

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.
You should do self.timeout = 60 before calling the base class' setup() method (internally it makes a call to settimeout based on this setting.)
8

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.

5 Comments

Can somebody confirm that this works? It looks to be by far the easiest solution!
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.
This does not work if you call 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).
Worked for my use case. I'm using httpd.handle_request(). Tried various ways until I found this. Thanks!
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.
4

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

Comments

3

I managed to get timeouts working for HTTP requests with

self.rfile._sock.settimeout(60)

Hope this helps

2 Comments

hi, rifle no longer has a _sock property (in piton13.2). However... its still accessible through: self.wfile._sock.settimeout(0.01). this solved the request handler freezing the GIL on rfile.read.
dug a little deeper, and wfile is actually in instance of socketwriter with the requesthandler's self.connection passed through to end up as wfile._sock. ....to set the timeout for the socket is thus simply: self.connection.timeout(0.01) for a 10ms timeout. (note that once you are in the request handler you really ought to have received data, so you should not have to wait long. a roque request with no follow through can hang the rfile.read, this times it out swiftly and you move on with an empty response
3
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    timeout = 60 # StreamRequestHandler.setup
    def do_GET(self):
        # Doing Some Stuff.

1 Comment

Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation 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 your answer to add some explanation, including the assumptions you’ve made.
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>

Comments

1
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, request, client_address, server):
    BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server)   
    self.timeout = 60

1 Comment

The self.timeout = 60 needs to be set before the call to the super class's constructor in order to work properly.
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()"

Comments

0

The problem I ran into with the answers based on using handle_request() instead of serve_forever() is that the server won't time out until it receives the next request. I need a solution for when the server has stopped getting requests but is still hanging around for some reason, so that won't work for me.

Thanks to Gregoire Cattan's answer though, I realized that I can get the behavior I want by subclassing HTTPServer and overriding the service_actions() method. It's called within the serve_forever() loop, so putting the timeout logic there means it will be triggered even in the absence of incoming requests.

#!/usr/bin/env python

"""Run a HTTP server until it times out."""

from http.server import HTTPServer, SimpleHTTPRequestHandler
from sys import stderr
from time import time


PORT = 1313
TIMEOUT = 5


class Server(HTTPServer):
    """Expiring HTTP Server."""

    timeout = TIMEOUT

    def __init__(self, *args, **kwargs):
        """Initialize the server with a start time."""
        self.started = time()
        super().__init__(*args, **kwargs)

    @property
    def age(self):
        """Return the number of seconds the server has been alive."""
        return time() - self.started

    def service_actions(self, *args, **kwargs):
        """Time out if the server has expired."""
        print(self.age, file=stderr)
        if self.age > self.timeout:
            self.stopped = time()
            raise TimeoutError()
        super().service_actions(*args, **kwargs)


def run():
    """Run the web server."""
    server = Server(("", PORT), SimpleHTTPRequestHandler)
    print(f"Starting server at {server.started} with a {TIMEOUT} second timeout.\n", file=stderr)

    try:
        server.serve_forever()
    except TimeoutError:
        ...
    finally:
        print(f"\nShutting down server at {server.stopped}.", file=stderr)
        server.server_close()


if __name__ == "__main__":
    run()

Here's the output:

Starting server at 1715792021.774305 with a 5 second timeout.

0.5272078514099121
1.0283708572387695
1.5295538902282715
2.0307466983795166
2.5319831371307373
3.032681941986084
3.5338847637176514
4.0351338386535645
4.536334037780762
5.037556886672974

Shutting down server at 1715792026.811923.

Comments

0

This way, by overriding BaseRequestHandler setup(self) method, then calling setup(self) method overriden in StreamRequestHandler class.

from http.server import BaseHTTPRequestHandler
from socketserver import StreamRequestHandler

class MyHttpServerHandler(BaseHTTPRequestHandler):

    def setup(self):
         StreamRequestHandler.timeout = 10  # seconds
         StreamRequestHandler.setup(self)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.