0

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!

JLTChiu
  • 983
  • 3
  • 12
  • 28
  • What do you want to timeout? Session, request, ....? – notorious.no Sep 27 '16 at 17:28
  • request timeout I believe? So when the server receive a call and can't response it in a fixed time frame (maybe 10 seconds), it returns time out to the client. – JLTChiu Sep 28 '16 at 15:12
  • ok. Can you add ``twisted`` to the tags next time you have a ``klien`` question? This way twisted devs can find your question too. – notorious.no Sep 28 '16 at 16:52

1 Answers1

1

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

notorious.no
  • 4,919
  • 3
  • 20
  • 34