JLTChiu JLTChiu - 2 months ago 14
Python Question

How to set the server timeout in python Klein?

I am using python Klein http://klein.readthedocs.io/en/latest/ for setting up a web service. I had checked the documentation but I still don't know how to set the timeout of the service. Can anyone who is more familiar with tool shows how to set the timeout to 15 seconds? Thanks!

Answer

You could call Request.loseConnection() to drop the request connection to the client after an set timeout interval. Here is a quick example:

from twisted.internet import reactor, task, defer
from klein import Klein

app = Klein()
request_timeout = 10 # seconds

@app.route('/delayed/<int:n>')
@defer.inlineCallbacks
def timeoutRequest(request, n):
    work = serverTask(n)       # work that might take too long

    drop = reactor.callLater(
        request_timeout,    # drop request connection after n seconds
        dropRequest,        # function to drop request connection
            request,        # pass request obj into dropRequest()
            work)           # pass worker deferred obj to dropRequest()

    try:
        result = yield work     # work has completed, get result
        drop.cancel()           # cancel the task to drop the request connection
    except:
        result = 'Request dropped'

    defer.returnValue(result)

def serverTask(n):
    """
    A simulation of a task that takes n number of seconds to complete.
    """
    d = task.deferLater(reactor, n, lambda: 'delayed for %d seconds' % (n))
    return d

def dropRequest(request, deferred):
    """
    Drop the request connection and cancel any deferreds
    """
    request.loseConnection()
    deferred.cancel()

app.run('localhost', 9000)

To try this out, go to http://localhost:9000/delayed/2 then http://localhost:9000/delayed/20 to test a scenario when the task doesn't complete in time. Don't forget to cancel all tasks, deferreds, threads, etc related to this request or you could potentially waste lots of memory.

Code Explanation

Server Side Task: Client goes to /delayed/<n> endpoint with a specified delay value. A server side task (serverTask()) starts and for the sake of simplicity and to simulate a busy task, deferLater was used to return a string after n seconds.

Request Timeout: Using callLater function, after the request_timeout interval, call the dropRequest function and pass request and all work deferreds that need to be canceled (in this case there's only work). When the request_timeout has passed then the request connection will be closed (request.loseConnection()) and deferreds will be cancelled (deferred.cancel).

Yield Server Task Result: In a try/except block, the result will be yielded when the value is available or, if the timeout has passed and connection is dropped, an error will occur and the Request dropped message will be returned.

Alternative

This really doesn't seem like a desirable scenario and should be avoided if possible, but I could see a need for this kind of functionality. Also, though rare, keep in mind that loseConnection doesn't always fully close a connection (this is due to TCP implementation not so much Twisted). A better solution would be to cancel a server side task when the client disconnects (which may be a bit easier to catch). This can be done by attaching an addErrback to Request.notifyFinish(). Here is an example using just Twisted (http://twistedmatrix.com/documents/current/web/howto/web-in-60/interrupted.html).