Search code examples
pythonweb-servicesklein-mvc

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!


Solution

  • 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).