Urias BT
Urias BT

Reputation: 101

How can I set a timeout in Python HTTPServer

I have a Python (2.7.13) HTTP Server running in Debian, I want to stop any GET request that takes longer than 10 seconds, but can't find a solution anywhere.

I already tried all the snippets posted in the following question: How to implement Timeout in BaseHTTPServer.BaseHTTPRequestHandler Python

#!/usr/bin/env python
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import os

class handlr(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type','text-html')
        self.end_headers()
        self.wfile.write(os.popen('sleep 20 & echo "this took 20 seconds"').read())

def run():
    server_address = ('127.0.0.1', 8080)
    httpd = HTTPServer(server_address, handlr)
    httpd.serve_forever()

if __name__ == '__main__':
    run()

As a test, I'm running a shell command that takes 20 seconds to execute, so I need the server stop before that.

Upvotes: 3

Views: 3392

Answers (1)

dtanabe
dtanabe

Reputation: 1661

Put your operation on a background thread, and then wait for your background thread to finish. There isn't a general-purpose safe way to abort threads, so this implementation unfortunately leaves the function running in the background even though it had already given up.

If you can, you might consider putting a proxy server (like say nginx) in front of your server and let it handle timeouts for you, or perhaps use a more robust HTTP server implementation that allows this as a configuration option. But the answer below should basically cover it.

#!/usr/bin/env python
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import os
import threading

class handlr(BaseHTTPRequestHandler):
    def do_GET(self):
        result, succeeded = run_with_timeout(lambda: os.popen('sleep 20 & echo "this took 20 seconds"').read(), timeout=3)
        if succeeded:
            self.send_response(200)
            self.send_header('Content-type','text-html')
            self.end_headers()
            self.wfile.write(os.popen('sleep 20 & echo "this took 20 seconds"').read())
        else:
            self.send_response(500)
            self.send_header('Content-type','text-html')
            self.end_headers()
            self.wfile.write('<html><head></head><body>Sad panda</body></html>')
        self.wfile.close()

def run():
    server_address = ('127.0.0.1', 8080)
    httpd = HTTPServer(server_address, handlr)
    httpd.serve_forever()

def run_with_timeout(fn, timeout):
    lock = threading.Lock()
    result = [None, False]
    def run_callback():
        r = fn()
        with lock:
            result[0] = r
            result[1] = True

    t = threading.Thread(target=run_callback)
    t.daemon = True
    t.start()
    t.join(timeout)
    with lock:
        return tuple(result)

if __name__ == '__main__':
    run()

Upvotes: 1

Related Questions