How to handle interrupted responses in nevow?
J.P's excellent article http://jcalderone.livejournal.com/50890.html explains how to avoid calling Request.finish() on a request after its connection was lost, because that now raises an exception. The code in the article is able to avoid calling finish() because it handles the rendering itself. The call to finish() is right there in _delayedRender. But I'm using Nevow (because it's awesome) and all that stuff is done behind the scenes, where I can't get to it. My application has a form, which uses the Post/Redirect/Get pattern (http://en.wikipedia.org/wiki/Post/Redirect/Get) to avoid double-posting. Because processing is not instant, the browser receives the HTTP header telling it to redirect before the processing has finished. When the processing finishes, the Deferred embedded in the web page fires with a value, Stan finishes flattening the page, and Nevow calls Request.finish(). By then the browser has closed the connection and sent in the GET request. So I get an error every single time. What is the correct method to tell Nevow to abandon the request, please? This is a completely self-contained example, to be run with twistd: from nevow import tags, inevow, loaders, appserver from nevow.rend import Page from nevow.url import URL from twisted.internet.task import deferLater from twisted.internet import reactor from twisted.application import service from twisted.application.internet import TCPServer from twisted.web import http class Redirector(Page): count = 0 message = 0 def _increment(self): self.count += 1 return self.count def slow_operation(self): return deferLater(reactor, 3, self._increment) def beforeRender(self, ctx): req = inevow.IRequest(ctx) if req.method == 'POST': oldurl = URL.fromContext(ctx) newurl = oldurl.remove('cmd') req.setResponseCode(http.SEE_OTHER) req.setHeader("Location", newurl) self.message = self.slow_operation() def render_message(self, ctx, data): return self.message docFactory = loaders.stan( tags.html[ tags.head[tags.title['Slow page']], tags.body[ tags.h1['Problem with interrupted connections'], tags.form(method='post', action='')[ tags.button(type='submit', name='cmd', value='click')[ 'Click' ] ], tags.p(render=tags.directive('message')) ] ] ) application = service.Application('Demo') site = appserver.NevowSite(Redirector()) webservice = TCPServer(8123, site) webservice.setName('WUI') webservice.setServiceParent(application) Thanks in advance, Peter.
On 1 Oct, 02:31 pm, peter.westlake@pobox.com wrote:
J.P's excellent article http://jcalderone.livejournal.com/50890.html explains how to avoid calling Request.finish() on a request after its connection was lost, because that now raises an exception. The code in the article is able to avoid calling finish() because it handles the rendering itself. The call to finish() is right there in _delayedRender. But I'm using Nevow (because it's awesome) and all that stuff is done behind the scenes, where I can't get to it.
Mmm. Indeed. Nevow doesn't make it easy to handle this case. Nevow itself should be taking some action in this case, I think. One possibility would be to cancel the Deferred associated with the child lookup/rendering operation. This would depend on having a new enough version of Twisted (such that Deferred cancellation is available) though. Otherwise all Nevow could really do is ignore the result of the Deferred when it comes. Actually, ignoring the result might be a sensible thing to implement first anyway. It should be simpler and have no application consequences except to get rid of the now-disallowed finish() call. Are you interested in helping implement this? Jean-Paul
My application has a form, which uses the Post/Redirect/Get pattern (http://en.wikipedia.org/wiki/Post/Redirect/Get) to avoid double-posting. Because processing is not instant, the browser receives the HTTP header telling it to redirect before the processing has finished. When the processing finishes, the Deferred embedded in the web page fires with a value, Stan finishes flattening the page, and Nevow calls Request.finish(). By then the browser has closed the connection and sent in the GET request. So I get an error every single time.
What is the correct method to tell Nevow to abandon the request, please?
This is a completely self-contained example, to be run with twistd:
from nevow import tags, inevow, loaders, appserver from nevow.rend import Page from nevow.url import URL from twisted.internet.task import deferLater from twisted.internet import reactor from twisted.application import service from twisted.application.internet import TCPServer from twisted.web import http
class Redirector(Page):
count = 0 message = 0
def _increment(self): self.count += 1 return self.count
def slow_operation(self): return deferLater(reactor, 3, self._increment)
def beforeRender(self, ctx): req = inevow.IRequest(ctx) if req.method == 'POST': oldurl = URL.fromContext(ctx) newurl = oldurl.remove('cmd') req.setResponseCode(http.SEE_OTHER) req.setHeader("Location", newurl) self.message = self.slow_operation()
def render_message(self, ctx, data): return self.message
docFactory = loaders.stan( tags.html[ tags.head[tags.title['Slow page']], tags.body[ tags.h1['Problem with interrupted connections'], tags.form(method='post', action='')[ tags.button(type='submit', name='cmd', value='click')[ 'Click' ] ], tags.p(render=tags.directive('message')) ] ] )
application = service.Application('Demo')
site = appserver.NevowSite(Redirector()) webservice = TCPServer(8123, site) webservice.setName('WUI') webservice.setServiceParent(application)
Thanks in advance,
Peter.
_______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
On Fri, 08 Oct 2010 13:19 +0000, exarkun@twistedmatrix.com wrote:
On 1 Oct, 02:31 pm, peter.westlake@pobox.com wrote:
J.P's excellent article http://jcalderone.livejournal.com/50890.html explains how to avoid calling Request.finish() on a request after its connection was lost, because that now raises an exception. The code in the article is able to avoid calling finish() because it handles the rendering itself. The call to finish() is right there in _delayedRender. But I'm using Nevow (because it's awesome) and all that stuff is done behind the scenes, where I can't get to it.
Mmm. Indeed. Nevow doesn't make it easy to handle this case.
Nevow itself should be taking some action in this case, I think. One possibility would be to cancel the Deferred associated with the child lookup/rendering operation. This would depend on having a new enough version of Twisted (such that Deferred cancellation is available) though. Otherwise all Nevow could really do is ignore the result of the Deferred when it comes.
Is the version of Twisted with cancellation more recent than the version of Nevow that detects the error? If not, it might be reasonable to expect cancellation to be available. Or would it be reasonable to test for it at the beginning of the Nevow source?
Actually, ignoring the result might be a sensible thing to implement first anyway. It should be simpler and have no application consequences except to get rid of the now-disallowed finish() call.
Are you interested in helping implement this?
I'm certainly willing to try! Especially as I have a cron job that mails me whenever it finds an exception in the log. Any hints as to how to go about it would be very welcome. So far I've found NevowRequest._cbFinishRender in appserver.py - is that the right sort of area? And NevowSite.handleSegment? Peter.
participants (2)
-
exarkun@twistedmatrix.com
-
Peter Westlake