summaryrefslogtreecommitdiff
path: root/doc/web/howto/web-in-60/interrupted.html
blob: f4701a33463734d6cd23de7a6247a082f166c3c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  <head>
<title>Twisted Documentation: Interrupted Responses</title>
<link href="../stylesheet.css" rel="stylesheet" type="text/css"/>
  </head>

  <body bgcolor="white">
    <h1 class="title">Interrupted Responses</h1>
    <div class="toc"><ol/></div>
    <div class="content">
<span/>

<p>The previous example had a Resource that generates its response
asynchronously rather than immediately upon the call to its render method. When
generating responses asynchronously, the possibility is introduced that the
connection to the client may be lost before the response is generated. In such a
case, it is often desirable to abandon the response generation entirely, since
there is nothing to do with the data once it is produced. This example shows how
to be notified that the connection has been lost.</p>

<p>This example will build upon the <a href="asynchronous.html" shape="rect">asynchronous
responses example</a> which simply (if not very realistically) generated its
response after a fixed delay. We will expand that resource so that as soon as
the client connection is lost, the delayed event is cancelled and the response
is never generated.</p>

<p>The feature this example relies on is provided by another <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.web.server.Request.html" title="twisted.web.server.Request">Request</a></code> method: <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.web.http.Request.notifyFinish.html" title="twisted.web.http.Request.notifyFinish">notifyFinish</a></code>. This method returns a new
Deferred which will fire with <code>None</code> if the request is successfully
responded to or with an error otherwise - for example if the connection is lost
before the response is sent.</p>

<p>The example starts in a familiar way, with the requisite Twisted imports and
a resource class with the same <code>_delayedRender</code> used previously:</p>

<pre class="python"><p class="py-linenumber">1
2
3
4
5
6
7
8
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">resource</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Resource</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">server</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">NOT_DONE_YET</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">DelayedResource</span>(<span class="py-src-parameter">Resource</span>):
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">_delayedRender</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
        <span class="py-src-variable">request</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;&lt;html&gt;&lt;body&gt;Sorry to keep you waiting.&lt;/body&gt;&lt;/html&gt;&quot;</span>)
        <span class="py-src-variable">request</span>.<span class="py-src-variable">finish</span>()
</pre>

<p>Before defining the render method, we're going to define an errback
(an errback being a callback that gets called when there's an error),
though. This will be the errback attached to the <code>Deferred</code>
returned by <code>Request.notifyFinish</code>. It will cancel the
delayed call to <code>_delayedRender</code>.</p>

<pre class="python"><p class="py-linenumber">1
2
3
</p>...
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">_responseFailed</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">err</span>, <span class="py-src-parameter">call</span>):
        <span class="py-src-variable">call</span>.<span class="py-src-variable">cancel</span>()
</pre>

<p>Finally, the render method will set up the delayed call just as it
did before, and return <code>NOT_DONE_YET</code> likewise. However, it
will also use <code>Request.notifyFinish</code> to make
sure <code>_responseFailed</code> is called if appropriate.</p>

<pre class="python"><p class="py-linenumber">1
2
3
4
5
</p>...
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">render_GET</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
        <span class="py-src-variable">call</span> = <span class="py-src-variable">reactor</span>.<span class="py-src-variable">callLater</span>(<span class="py-src-number">5</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">_delayedRender</span>, <span class="py-src-variable">request</span>)
        <span class="py-src-variable">request</span>.<span class="py-src-variable">notifyFinish</span>().<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">_responseFailed</span>, <span class="py-src-variable">call</span>)
        <span class="py-src-keyword">return</span> <span class="py-src-variable">NOT_DONE_YET</span>
</pre>

<p>Notice that since <code>_responseFailed</code> needs a reference to
the delayed call object in order to cancel it, we passed that object
to <code>addErrback</code>. Any additional arguments passed
to <code>addErrback</code> (or <code>addCallback</code>) will be
passed along to the errback after the <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.python.failure.Failure.html" title="twisted.python.failure.Failure">Failure</a></code> instance which is always
passed as the first argument. Passing <code>call</code> here means it
will be passed to <code>_responseFailed</code>, where it is expected
and required.</p>

<p>That covers almost all the code for this example. Here's the entire example
without interruptions, as an <a href="rpy-scripts.html" shape="rect">rpy script</a>:</p>

<pre class="python"><p class="py-linenumber"> 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">resource</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Resource</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">server</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">NOT_DONE_YET</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">DelayedResource</span>(<span class="py-src-parameter">Resource</span>):
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">_delayedRender</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
        <span class="py-src-variable">request</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;&lt;html&gt;&lt;body&gt;Sorry to keep you waiting.&lt;/body&gt;&lt;/html&gt;&quot;</span>)
        <span class="py-src-variable">request</span>.<span class="py-src-variable">finish</span>()

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">_responseFailed</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">err</span>, <span class="py-src-parameter">call</span>):
        <span class="py-src-variable">call</span>.<span class="py-src-variable">cancel</span>()

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">render_GET</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
        <span class="py-src-variable">call</span> = <span class="py-src-variable">reactor</span>.<span class="py-src-variable">callLater</span>(<span class="py-src-number">5</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">_delayedRender</span>, <span class="py-src-variable">request</span>)
        <span class="py-src-variable">request</span>.<span class="py-src-variable">notifyFinish</span>().<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">_responseFailed</span>, <span class="py-src-variable">call</span>)
        <span class="py-src-keyword">return</span> <span class="py-src-variable">NOT_DONE_YET</span>

<span class="py-src-variable">resource</span> = <span class="py-src-variable">DelayedResource</span>()
</pre>

<p>Toss this into <code>example.rpy</code>, fire it up with <code>twistd -n
web --path .</code>, and
hit <a href="http://localhost:8080/example.rpy" shape="rect">http://localhost:8080/example.rpy</a>. If
you wait five seconds, you'll get the page content. If you interrupt the request
before then, say by hitting escape (in Firefox, at least), then you'll see
perhaps the most boring demonstration ever - no page content, and nothing in the
server logs. Success!</p>

</div>

    <p><a href="../index.html">Index</a></p>
    <span class="version">Version: 12.1.0</span>
  </body>
</html>