Ticket #4577: web-in-60.xhtml.patch
| File web-in-60.xhtml.patch, 47.5 KB (added by jdb, 3 years ago) |
|---|
-
doc/web/howto/web-in-60/asynchronous-deferred.xhtml
diff --git a/doc/web/howto/web-in-60/asynchronous-deferred.xhtml b/doc/web/howto/web-in-60/asynchronous-deferred.xhtml index 78ce564..325164d 100644
a b 21 21 example of using a <code>Deferred</code>-returning API to generate an 22 22 asynchronous response to a request in Twisted Web.</p> 23 23 24 <p><code>Deferred</code> is the result of two consequences of the asynchronous 25 programming approach. First, asynchronous code is frequently (if not always) 26 concerned with some data (in Python, an object) which is not yet available but 27 which probably will be soon. Asynchronous code needs a way to define what will 28 be done to the object once it does exist. It also needs a way to define how to 29 handle errors in the creation or acquisition of that object. These two needs are 30 satisfied by the <i>callbacks</i> and <i>errbacks</i> of a 31 <code>Deferred</code>. Callbacks are added to a <code>Deferred</code> with <code 32 class="API" base="twisted.internet.defer">Deferred.addCallback</code>; errbacks 24 <p><code>Deferred</code> is the result of two consequences of the 25 asynchronous programming approach. First, asynchronous code is 26 frequently (if not always) concerned with some data (in Python, an 27 object) which is not yet available but which probably will be 28 soon. Asynchronous code needs a way to define what will be done to the 29 object once it does exist. It also needs a way to define how to handle 30 errors in the creation or acquisition of that object. These two needs 31 are satisfied by the <i>callbacks</i> and <i>errbacks</i> of 32 a <code>Deferred</code>. Callbacks are added to 33 a <code>Deferred</code> with <code class="API" 34 base="twisted.internet.defer">Deferred.addCallback</code>; errbacks 33 35 are added with <code class="API" 34 base="twisted.internet.defer">Deferred.addErrback</code>. When the object 35 finally does exist, it is passed to <code class="API" 36 base="twisted.internet.defer">Deferred.callback</code> which passes it on to the 37 callback added with <code>addCallback</code>. Similarly, if an error occurs, 38 <code class="API" base="twisted.internet.defer">Deferred.errback</code> is 39 called and the error is passed along to the errback added with 40 <code>addErrback</code>. Second, the events that make asynchronous code actually 41 work often take many different, incompatible forms. <code>Deferred</code> acts 42 as the uniform interface which lets different parts of an asynchronous 43 application interact and isolates them from implementation details they 44 shouldn't be concerned with.</p> 36 base="twisted.internet.defer">Deferred.addErrback</code>. When the 37 object finally does exist, it is passed to <code class="API" 38 base="twisted.internet.defer">Deferred.callback</code> which passes it 39 on to the callback added with <code>addCallback</code>. Similarly, if 40 an error occurs, <code class="API" 41 base="twisted.internet.defer">Deferred.errback</code> is called and 42 the error is passed along to the errback added 43 with <code>addErrback</code>. Second, the events that make 44 asynchronous code actually work often take many different, 45 incompatible forms. <code>Deferred</code> acts as the uniform 46 interface which lets different parts of an asynchronous application 47 interact and isolates them from implementation details they shouldn't 48 be concerned with.</p> 45 49 46 50 <p>That's almost all there is to <code>Deferred</code>. To solidify your new 47 51 understanding, now consider this rewritten version … … 64 68 from twisted.internet import reactor 65 69 </pre> 66 70 67 <p>With the imports done, here's the first part of the68 <code>DelayedResource</code> implementation. Again, this part of the code is 69 identical to the previous version:</p>71 <p>With the imports done, here's the first part of 72 the <code>DelayedResource</code> implementation. Again, this part of 73 the code is identical to the previous version:</p> 70 74 71 75 <pre class="python"> 72 76 class DelayedResource(Resource): … … 75 79 request.finish() 76 80 </pre> 77 81 78 <p>Next we need to define the render method. Here's where things change a 79 bit. Instead of using <code class="API" 80 base="twisted.internet.interfaces.IReactorTime">callLater</code>, We're going to 81 use <code class="API" base="twisted.internet.task">deferLater</code> this 82 time. <code>deferLater</code> accepts a reactor, delay (in seconds, as with 83 <code>callLater</code>), and a function to call after the delay to produce that 84 elusive object discussed in the description of <code>Deferred</code>s. We're 85 also going to use <code>_delayedRender</code> as the callback to add to the 86 <code>Deferred</code> returned by <code>deferLater</code>. Since it expects the 87 request object as an argument, we're going to set up the <code>deferLater</code> 88 call to return a <code>Deferred</code> which has the request object as its 89 result.</p> 82 <p>Next we need to define the render method. Here's where things 83 change a bit. Instead of using <code class="API" 84 base="twisted.internet.interfaces.IReactorTime">callLater</code>, 85 We're going to use <code class="API" 86 base="twisted.internet.task">deferLater</code> this 87 time. <code>deferLater</code> accepts a reactor, delay (in seconds, as 88 with <code>callLater</code>), and a function to call after the delay 89 to produce that elusive object discussed in the description 90 of <code>Deferred</code>s. We're also going to 91 use <code>_delayedRender</code> as the callback to add to 92 the <code>Deferred</code> returned by <code>deferLater</code>. Since 93 it expects the request object as an argument, we're going to set up 94 the <code>deferLater</code> call to return a <code>Deferred</code> 95 which has the request object as its result.</p> 90 96 91 97 <pre class="python"> 92 98 def render_GET(self, request): 93 99 d = deferLater(reactor, 5, lambda: request) 94 100 </pre> 95 101 96 <p>The <code>Deferred</code> referenced by <code>d</code> now needs to have the97 <code>_delayedRender</code> callback added to it. Once this is done, 98 <code>_delayedRender</code> will be called with the result of <code>d</code> 99 (which will be <code>request</code>, of course — the result of <code>(lambda: 100 re quest)()</code>).</p>102 <p>The <code>Deferred</code> referenced by <code>d</code> now needs to 103 have the <code>_delayedRender</code> callback added to it. Once this 104 is done, <code>_delayedRender</code> will be called with the result 105 of <code>d</code> (which will be <code>request</code>, of course — the 106 result of <code>(lambda: request)()</code>).</p> 101 107 102 108 <pre class="python"> 103 109 d.addCallback(self._delayedRender) … … 111 117 return NOT_DONE_YET 112 118 </pre> 113 119 114 <p>And with that, <code>DelayedResource</code> is now implemented based on a 115 <code>Deferred</code>. The example still isn't very realistic, but remember that 116 since <code>Deferred</code>s offer a uniform interface to many different 117 asynchronous event sources, this code now resembles a real application even more 118 closely; you could easily replace <code>deferLater</code> with another 119 <code>Deferred</code>-returning API and suddenly you might have a resource that 120 does something useful.</p> 120 <p>And with that, <code>DelayedResource</code> is now implemented 121 based on a <code>Deferred</code>. The example still isn't very 122 realistic, but remember that since <code>Deferred</code>s offer a 123 uniform interface to many different asynchronous event sources, this 124 code now resembles a real application even more closely; you could 125 easily replace <code>deferLater</code> with 126 another <code>Deferred</code>-returning API and suddenly you might 127 have a resource that does something useful.</p> 121 128 122 129 <p>Finally, here's the complete, uninterrupted example source, as an rpy script:</p> 123 130 -
doc/web/howto/web-in-60/asynchronous.xhtml
diff --git a/doc/web/howto/web-in-60/asynchronous.xhtml b/doc/web/howto/web-in-60/asynchronous.xhtml index 1b9b39f..d35e043 100644
a b 22 22 either way; the same render methods are used. There are three basic differences, 23 23 though.</p> 24 24 25 <p>First, instead of returning the string which will be used as the body of the 26 response, the resource uses <code class="API" 27 base="twisted.web.http">Request.write</code>. This method can be called 28 repeatedly. Each call appends another string to the response body. Second, when 29 the entire response body has been passed to <code>Request.write</code>, the 30 application must call <code class="API" 31 base="twisted.web.http">Request.finish</code>. As you might expect from the 32 name, this ends the response. Finally, in order to make Twisted Web not end the 33 response as soon as the render method returns, the render method must return 34 <code>NOT_DONE_YET</code>. Consider this example:</p> 25 <p>First, instead of returning the string which will be used as the 26 body of the response, the resource uses <code class="API" 27 base="twisted.web.http">Request.write</code>. This method can be 28 called repeatedly. Each call appends another string to the response 29 body. Second, when the entire response body has been passed 30 to <code>Request.write</code>, the application must 31 call <code class="API" 32 base="twisted.web.http">Request.finish</code>. As you might expect 33 from the name, this ends the response. Finally, in order to make 34 Twisted Web not end the response as soon as the render method returns, 35 the render method must return <code>NOT_DONE_YET</code>. Consider this 36 example:</p> 35 37 36 38 <pre class="python"> 37 39 from twisted.web.resource import Resource … … 48 50 return NOT_DONE_YET 49 51 </pre> 50 52 51 <p>If you're not familiar with reactor.<code class="API" 52 base="twisted.internet.interfaces.IReactorTime">callLater</code>, all you really 53 need to know about it to understand this example is that the above usage of it 54 arranges to have <code>self._delayedRender(request)</code> run about 5 seconds 55 after <code>callLater</code> is invoked from this render method and that it 56 returns immediately.</p> 57 58 <p>All three of the elements mentioned earlier can be seen in this example. The 59 resource uses <code>Request.write</code> to set the response body. It uses 60 <code>Request.finish</code> after the entire body has been specified (all with 61 just one call to write in this case). Lastly, it returns 62 <code>NOT_DONE_YET</code> from its render method. So there you have it, 63 asynchronous rendering with Twisted Web.</p> 53 <p>If you're not familiar with the reactor <code class="API" 54 base="twisted.internet.interfaces.IReactorTime">callLater</code> 55 method, all you really need to know about it to understand this 56 example is that the above usage of it arranges to 57 have <code>self._delayedRender(request)</code> run about 5 seconds 58 after <code>callLater</code> is invoked from this render method and 59 that it returns immediately.</p> 60 61 <p>All three of the elements mentioned earlier can be seen in this 62 example. The resource uses <code>Request.write</code> to set the 63 response body. It uses <code>Request.finish</code> after the entire 64 body has been specified (all with just one call to write in this 65 case). Lastly, it returns <code>NOT_DONE_YET</code> from its render 66 method. So there you have it, asynchronous rendering with Twisted 67 Web.</p> 64 68 65 69 <p>Here's a complete rpy script based on this resource class (see the <a 66 70 href="rpy-scripts.xhtml">previous example</a> if you need a reminder about rpy … … 83 87 resource = DelayedResource() 84 88 </pre> 85 89 86 <p>Drop this source into a <code>.rpy</code> file and fire up a server using 87 <code>twistd -n web --path /directory/containing/script/.</code> You'll see that 88 loading the page takes 5 seconds. If you try to load a second before the first 89 completes, it will also take 5 seconds from the time you request it (but it 90 won't be delayed by any other outstanding requests).</p> 90 <p>Drop this source into a <code>.rpy</code> file and fire up a server 91 using <code>twistd -n web --path /directory/containing/script/.</code> 92 You'll see that loading the page takes 5 seconds. If you try to load a 93 second before the first completes, it will also take 5 seconds from 94 the time you request it (but it won't be delayed by any other 95 outstanding requests).</p> 91 96 92 97 <p>Something else to consider when generating responses asynchronously is that 93 98 the client may not wait around to get the response to its -
doc/web/howto/web-in-60/custom-codes.xhtml
diff --git a/doc/web/howto/web-in-60/custom-codes.xhtml b/doc/web/howto/web-in-60/custom-codes.xhtml index 8f5ddfd..ab61a42 100644
a b 47 47 return "<html><body>Please swipe your credit card.</body></html>" 48 48 </pre> 49 49 50 <p>Just like the other resources I've demonstrated, this one returns a string51 from its <code>render_GET</code> method to define the body of the response. All 52 th at's different is the call to53 <code>setResponseCode</code> to override the default response code,50 <p>Just like the other resources I've demonstrated, this one returns a 51 string from its <code>render_GET</code> method to define the body of 52 the response. All that's different is the call 53 to <code>setResponseCode</code> to override the default response code, 54 54 200, with a different one.</p> 55 55 56 56 <p>Finally, the code to set up the site and reactor. We'll put an instance of -
doc/web/howto/web-in-60/dynamic-content.xhtml
diff --git a/doc/web/howto/web-in-60/dynamic-content.xhtml b/doc/web/howto/web-in-60/dynamic-content.xhtml index 888ddc2..e64d238 100644
a b 26 26 protocol implementation. The reactor is the main loop that drives any Twisted 27 27 application; we'll use it to actually create the listening port in a moment.</p> 28 28 29 <p>Next, we'll import one more thing from Twisted Web: <code class="API"30 base="twisted.web.resource">Resource</code>. An instance of 31 <code>Resource</code> (or a subclass) represents a page (technically, the entity 32 addressed by a URI).</p>29 <p>Next, we'll import one more thing from Twisted 30 Web: <code class="API" base="twisted.web.resource">Resource</code>. An 31 instance of <code>Resource</code> (or a subclass) represents a page 32 (technically, the entity addressed by a URI).</p> 33 33 34 34 <pre class="python"> 35 35 from twisted.web.resource import Resource … … 42 42 import time 43 43 </pre> 44 44 45 <p>With imports taken care of, the next step is to define a 46 <code>Resource</code> subclass which has the dynamic rendering behavior we 47 want. Here's a resource which generates a page giving the time:</p> 45 <p>With imports taken care of, the next step is to define 46 a <code>Resource</code> subclass which has the dynamic rendering 47 behavior we want. Here's a resource which generates a page giving the 48 time:</p> 48 49 49 50 <pre class="python"> 50 51 class ClockPage(Resource): … … 53 54 return "<html><body>%s</body></html>" % (time.ctime(),) 54 55 </pre> 55 56 56 <p>Setting <code>isLeaf</code> to <code>True</code> indicates that 57 <code>ClockPage</code> resources will never have any children.</p> 57 <p>Setting <code>isLeaf</code> to <code>True</code> indicates 58 that <code>ClockPage</code> resources will never have any 59 children.</p> 58 60 59 61 <p>The <code>render_GET</code> method here will be called whenever the URI we 60 62 hook this resource up to is requested with the <code>GET</code> method. The byte … … 67 69 factory = Site(resource) 68 70 </pre> 69 71 70 <p>Just as with the previous static content example, this configuration puts our71 resource at the very top of the URI hierarchy, ie at <code>/</code>. With that 72 <code>Site</code> instance, we can tell the reactor to <a 73 href="../../../core/howto/servers.xhtml">create a TCP server</a> and start 74 servicing requests:</p>72 <p>Just as with the previous static content example, this 73 configuration puts our resource at the very top of the URI hierarchy, 74 ie at <code>/</code>. With that <code>Site</code> instance, we can 75 tell the reactor to <a href="../../../core/howto/servers.xhtml">create 76 a TCP server</a> and start servicing requests:</p> 75 77 76 78 <pre class="python"> 77 79 reactor.listenTCP(8880, factory) -
doc/web/howto/web-in-60/dynamic-dispatch.xhtml
diff --git a/doc/web/howto/web-in-60/dynamic-dispatch.xhtml b/doc/web/howto/web-in-60/dynamic-dispatch.xhtml index 8e4ab16..3faa3ac 100644
a b 30 30 from twisted.internet import reactor 31 31 </pre> 32 32 33 <p>With that out of the way, here's the interesting part of this example. We're 34 going to define a resource which renders a whole-year calendar. The year it will 35 render the calendar for will be the year in the request URL. So, for example, 36 <code>/2009</code> will render a calendar for 2009. First, here's a resource 37 that renders a calendar for the year passed to its initializer:</p> 33 <p>With that out of the way, here's the interesting part of this 34 example. We're going to define a resource which renders a whole-year 35 calendar. The year it will render the calendar for will be the year in 36 the request URL. So, for example, <code>/2009</code> will render a 37 calendar for 2009. First, here's a resource that renders a calendar 38 for the year passed to its initializer:</p> 38 39 39 40 <pre class="python"> 40 41 from calendar import calendar -
doc/web/howto/web-in-60/error-handling.xhtml
diff --git a/doc/web/howto/web-in-60/error-handling.xhtml b/doc/web/howto/web-in-60/error-handling.xhtml index 141396c..71d0ddc 100644
a b 50 50 return YearPage(year) 51 51 </pre> 52 52 53 <p>Aside from including the definition of <code>YearPage</code> from the54 previous example, the only other thing left to do is the normal 55 <code>Site</code> and <code>reactor</code> setup. Here's the complete code for 56 this example:</p>53 <p>Aside from including the definition of <code>YearPage</code> from 54 the previous example, the only other thing left to do is the 55 normal <code>Site</code> and <code>reactor</code> setup. Here's the 56 complete code for this example:</p> 57 57 58 58 <pre class="python"> 59 59 from twisted.web.server import Site -
doc/web/howto/web-in-60/handling-posts.xhtml
diff --git a/doc/web/howto/web-in-60/handling-posts.xhtml b/doc/web/howto/web-in-60/handling-posts.xhtml index 70ac268..7d8efd8 100644
a b 56 56 return '<html><body>You submitted: %s</body></html>' % (cgi.escape(request.args["the-field"][0]),) 57 57 </pre> 58 58 59 <p>The main thing to note here is the use of <code>request.args</code>. This is 60 a dictionary-like object that provides access to the contents of the form. The 61 keys in this dictionary are the names of inputs in the form. Each value is a 62 list containing strings (since there can be multiple inputs with the same name), 63 which is why we had to extract the first element to pass to 64 <code>cgi.escape</code>. <code>request.args</code> will be populated from form 65 contents whenever a <code>POST</code> request is made with a content type of 66 <code>application/x-www-form-urlencoded</code> or 67 <code>multipart/form-data</code> (it's also populated by query arguments for any 68 type of request).</p> 59 <p>The main thing to note here is the use 60 of <code>request.args</code>. This is a dictionary-like object that 61 provides access to the contents of the form. The keys in this 62 dictionary are the names of inputs in the form. Each value is a list 63 containing strings (since there can be multiple inputs with the same 64 name), which is why we had to extract the first element to pass 65 to <code>cgi.escape</code>. <code>request.args</code> will be 66 populated from form contents whenever a <code>POST</code> request is 67 made with a content type 68 of <code>application/x-www-form-urlencoded</code> 69 or <code>multipart/form-data</code> (it's also populated by query 70 arguments for any type of request).</p> 69 71 70 72 <p>Finally, the example just needs the usual site creation and port setup:</p> 71 73 -
doc/web/howto/web-in-60/http-auth.xhtml
diff --git a/doc/web/howto/web-in-60/http-auth.xhtml b/doc/web/howto/web-in-60/http-auth.xhtml index 36f3921..5302afe 100644
a b 16 16 resources.</p> 17 17 18 18 <p><code class="API" base="twisted.web">guard</code>, the Twisted Web 19 module which provides most of the APIs that will be used in this example, helps20 you to19 module which provides most of the APIs that will be used in this 20 example, helps you to 21 21 add <a href="http://en.wikipedia.org/wiki/Authentication">authentication</a> 22 and <a href="http://en.wikipedia.org/wiki/Authorization">authorization</a> to a 23 resource hierarchy. It does this by providing a resource which implements 24 <code class="API" base="twisted.web.resource.Resource">getChild</code> to return 25 a <a href="dynamic-dispatch.xhtml">dynamically selected resource</a>. The 26 selection is based on the authentication headers in the request. If those 27 headers indicate that the request is made on behalf of Alice, then Alice's 28 resource will be returned. If they indicate that it was made on behalf of Bob, 29 his will be returned. If the headers contain invalid credentials, an error 30 resource is returned. Whatever happens, once this resource is returned, URL 22 and <a href="http://en.wikipedia.org/wiki/Authorization">authorization</a> 23 to a resource hierarchy. It does this by providing a resource which 24 implements <code class="API" 25 base="twisted.web.resource.Resource">getChild</code> to return 26 a <a href="dynamic-dispatch.xhtml">dynamically selected 27 resource</a>. The selection is based on the authentication headers in 28 the request. If those headers indicate that the request is made on 29 behalf of Alice, then Alice's resource will be returned. If they 30 indicate that it was made on behalf of Bob, his will be returned. If 31 the headers contain invalid credentials, an error resource is 32 returned. Whatever happens, once this resource is returned, URL 31 33 traversal continues as normal from that resource.</p> 32 34 33 35 <p>The resource that implements this is <code class="API" … … 44 46 really need to know is how to implement an <code class="API" 45 47 base="twisted.cred.portal">IRealm</code>.</p> 46 48 47 <p>You need to implement a realm because the realm is the object that actually 48 decides which resources are used for which users. This can be as complex or as 49 simple as it suitable for your application. For this example we'll keep it very 50 simple: each user will have a resource which is a static file listing of the 51 <code>public_html</code> directory in their UNIX home directory. First, we need 52 to import <code>implements</code> from <code>zope.interface</code> and 53 <code>IRealm</code> from <code>twisted.cred.portal</code>. Together these will 54 let me mark this class as a realm (this is mostly - but not entirely - a 49 <p>You need to implement a realm because the realm is the object that 50 actually decides which resources are used for which users. This can be 51 as complex or as simple as it suitable for your application. For this 52 example we'll keep it very simple: each user will have a resource 53 which is a static file listing of the <code>public_html</code> 54 directory in their UNIX home directory. First, we need to 55 import <code>implements</code> from <code>zope.interface</code> 56 and <code>IRealm</code> 57 from <code>twisted.cred.portal</code>. Together these will let me mark 58 this class as a realm (this is mostly - but not entirely - a 55 59 documentation thing). We'll also need <code class="API" 56 base="twisted.web.static">File</code> for the actual implementation later.</p> 60 base="twisted.web.static">File</code> for the actual implementation 61 later.</p> 57 62 58 63 <pre class="python"> 59 64 from zope.interface import implements … … 105 110 exists. However, that's an example for another day...</li> 106 111 </ul> 107 112 108 <p>We're almost ready to set up the resource for this example. To create an109 <code>HTTPAuthSessionWrapper</code>, though, we need two things. First, a 110 portal, which requires the realm above, plus at least one credentials 111 checker:</p>113 <p>We're almost ready to set up the resource for this example. To 114 create an <code>HTTPAuthSessionWrapper</code>, though, we need two 115 things. First, a portal, which requires the realm above, plus at least 116 one credentials checker:</p> 112 117 113 118 <pre class="python"> 114 119 from twisted.cred.portal import Portal … … 124 129 request.</p> 125 130 126 131 <p>Next we need either <code class="API" 127 base="twisted.web.guard">BasicCredentialFactory</code> or 128 <code class="API" base="twisted.web.guard">DigestCredentialFactory</code>. The 129 former knows how to challenge HTTP clients to do basic authentication; the 132 base="twisted.web.guard">BasicCredentialFactory</code> 133 or <code class="API" 134 base="twisted.web.guard">DigestCredentialFactory</code>. The former 135 knows how to challenge HTTP clients to do basic authentication; the 130 136 latter, digest authentication. We'll use digest here:</p> 131 137 132 138 <pre class="python"> … … 135 141 credentialFactory = DigestCredentialFactory("md5", "example.org") 136 142 </pre> 137 143 138 <p>The two parameters to this constructor are the hash algorithm and the HTTP 139 authentication realm which will be used. The only other valid hash algorithm is 140 "sha" (but be careful, MD5 is more widely supported than SHA). The HTTP 141 authentication realm is mostly just a string that is presented to the user to 142 let them know why they're authenticating (you can read more about this in the 143 <a href="http://tools.ietf.org/html/rfc2617">RFC</a>).</p> 144 <p>The two parameters to this constructor are the hash algorithm and 145 the HTTP authentication realm which will be used. The only other valid 146 hash algorithm is "sha" (but be careful, MD5 is more widely supported 147 than SHA). The HTTP authentication realm is mostly just a string that 148 is presented to the user to let them know why they're authenticating 149 (you can read more about this in 150 the <a href="http://tools.ietf.org/html/rfc2617">RFC</a>).</p> 144 151 145 152 <p>With those things created, we can finally 146 153 instantiate <code>HTTPAuthSessionWrapper</code>:</p> … … 152 159 </pre> 153 160 154 161 <p>There's just one last thing that needs to be done 155 here. When <a href="rpy-scripts.xhtml">rpy scripts</a> were introduced, it was156 mentioned that they are evaluated in an unusual context. This is the first 157 example that actually needs to take this into account. It so happens that 158 <code>DigestCredentialFactory</code> instances are stateful. Authentication will 159 only succeed if the same instance is used to both generate challenges and 160 examine the responses to those challenges. However, the normal mode of operation 161 for an rpy script is for it to be re-executed for every request. This leads to a 162 new 163 <code>DigestCredentialFactory</code> being created for every request, preventing162 here. When <a href="rpy-scripts.xhtml">rpy scripts</a> were 163 introduced, it was mentioned that they are evaluated in an unusual 164 context. This is the first example that actually needs to take this 165 into account. It so happens that <code>DigestCredentialFactory</code> 166 instances are stateful. Authentication will only succeed if the same 167 instance is used to both generate challenges and examine the responses 168 to those challenges. However, the normal mode of operation for an rpy 169 script is for it to be re-executed for every request. This leads to a 170 new <code>DigestCredentialFactory</code> being created for every request, preventing 164 171 any authentication attempt from ever succeeding.</p> 165 172 166 173 <p>There are two ways to deal with this. First, and the better of the two ways, -
doc/web/howto/web-in-60/index.xhtml
diff --git a/doc/web/howto/web-in-60/index.xhtml b/doc/web/howto/web-in-60/index.xhtml index 93dc1ae..1874cf8 100644
a b 9 9 10 10 <h1>Twisted.Web In 60 Seconds</h1> 11 11 12 <p>This set of examples contains short, complete applications of13 <code class="API">twisted.web</code>. For subjects not covered here, see 14 the <a href="../using-twistedweb.xhtml">Twisted Web tutorial</a> and the API 15 documentation.</p>12 <p>This set of examples contains short, complete applications 13 of <code class="API">twisted.web</code>. For subjects not covered 14 here, see the <a href="../using-twistedweb.xhtml">Twisted Web 15 tutorial</a> and the API documentation.</p> 16 16 17 17 <ol> 18 18 <li><a href="static-content.xhtml">Serving static content from a directory</a></li> -
doc/web/howto/web-in-60/interrupted.xhtml
diff --git a/doc/web/howto/web-in-60/interrupted.xhtml b/doc/web/howto/web-in-60/interrupted.xhtml index 0babe80..3d36790 100644
a b 45 45 request.finish() 46 46 </pre> 47 47 48 <p>Before defining the render method, we're going to define an errback (an49 errback being a callback that gets called when there's an error), though. This 50 will be the errback attached to the <code>Deferred</code> returned by 51 <code>Request.notifyFinish</code>. It will cancel the delayed call to 52 <code>_delayedRender</code>.</p>48 <p>Before defining the render method, we're going to define an errback 49 (an errback being a callback that gets called when there's an error), 50 though. This will be the errback attached to the <code>Deferred</code> 51 returned by <code>Request.notifyFinish</code>. It will cancel the 52 delayed call to <code>_delayedRender</code>.</p> 53 53 54 54 <pre class="python"> 55 55 def _responseFailed(self, err, call): 56 56 call.cancel() 57 57 </pre> 58 58 59 <p>Finally, the render method will set up the delayed call just as it did60 before, and return <code>NOT_DONE_YET</code> likewise. However, it will also use 61 <code>Request.notifyFinish</code> to make sure <code>_responseFailed</code> is 62 called if appropriate.</p>59 <p>Finally, the render method will set up the delayed call just as it 60 did before, and return <code>NOT_DONE_YET</code> likewise. However, it 61 will also use <code>Request.notifyFinish</code> to make 62 sure <code>_responseFailed</code> is called if appropriate.</p> 63 63 64 64 <pre class="python"> 65 65 def render_GET(self, request): … … 68 68 return NOT_DONE_YET 69 69 </pre> 70 70 71 <p>Notice that since <code>_responseFailed</code> needs a reference to the 72 delayed call object in order to cancel it, we passed that object to 73 <code>addErrback</code>. Any additional arguments passed to 74 <code>addErrback</code> (or <code>addCallback</code>) will be passed along to 75 the errback after the <code class="API" 76 base="twisted.python.failure">Failure</code> instance which is always passed as 77 the first argument. Passing <code>call</code> here means it will be passed to 78 <code>_responseFailed</code>, where it is expected and required.</p> 71 <p>Notice that since <code>_responseFailed</code> needs a reference to 72 the delayed call object in order to cancel it, we passed that object 73 to <code>addErrback</code>. Any additional arguments passed 74 to <code>addErrback</code> (or <code>addCallback</code>) will be 75 passed along to the errback after the <code class="API" 76 base="twisted.python.failure">Failure</code> instance which is always 77 passed as the first argument. Passing <code>call</code> here means it 78 will be passed to <code>_responseFailed</code>, where it is expected 79 and required.</p> 79 80 80 81 <p>That covers almost all the code for this example. Here's the entire example 81 82 without interruptions, as an <a href="rpy-scripts.xhtml">rpy script</a>:</p> -
doc/web/howto/web-in-60/logging-errors.xhtml
diff --git a/doc/web/howto/web-in-60/logging-errors.xhtml b/doc/web/howto/web-in-60/logging-errors.xhtml index cd36927..5897080 100644
a b 15 15 pointless work. However, it did this silently for any error. In this example, 16 16 we'll modify the previous example so that it logs each failed response.</p> 17 17 18 <p>This example will use the Twisted API for logging errors. As was mentioned in 19 the <a href="asynchronous-deferred.xhtml">first example covering Deferreds</a>, 20 errbacks are passed an error. In the previous example, the 21 <code>_responseFailed</code> errback accepted this error as a parameter but 22 ignored it. The only way this example will differ is that this 23 <code>_responseFailed</code> will use that error parameter to log a message.</p> 18 <p>This example will use the Twisted API for logging errors. As was 19 mentioned in the <a href="asynchronous-deferred.xhtml">first example 20 covering Deferreds</a>, errbacks are passed an error. In the previous 21 example, the <code>_responseFailed</code> errback accepted this error 22 as a parameter but ignored it. The only way this example will differ 23 is that this <code>_responseFailed</code> will use that error 24 parameter to log a message.</p> 24 25 25 26 <p>This example will require all of the imports required by the previous example 26 27 plus one new import:</p> … … 29 30 from twisted.python.log import err 30 31 </pre> 31 32 32 <p>The only other part of the previous example which changes is the33 <code>_responseFailed</code> callback, which will now log the error passed to 34 it:</p>33 <p>The only other part of the previous example which changes is 34 the <code>_responseFailed</code> callback, which will now log the 35 error passed to it:</p> 35 36 36 37 <pre class="python"> 37 38 def _responseFailed(self, failure, call): -
doc/web/howto/web-in-60/rpy-scripts.xhtml
diff --git a/doc/web/howto/web-in-60/rpy-scripts.xhtml b/doc/web/howto/web-in-60/rpy-scripts.xhtml index 4c1c2b6..cb8114c 100644
a b 21 21 means fewer lines of code that aren't dedicated to the task you're trying to 22 22 accomplish.</p> 23 23 24 <p>There are some disadvantages, though. An rpy script must have the extension 25 <code>.rpy</code>. This means you can't import it using the usual Python import 26 statement. This means it's hard to re-use code in an rpy script. This also means 27 you can't easily unit test it. The code in an rpy script is evaluated in an 28 unusual context. So, while rpy scripts may be useful for testing out ideas, 29 they're not recommend for much more than that.</p> 24 <p>There are some disadvantages, though. An rpy script must have the 25 extension <code>.rpy</code>. This means you can't import it using the 26 usual Python import statement. This means it's hard to re-use code in 27 an rpy script. This also means you can't easily unit test it. The code 28 in an rpy script is evaluated in an unusual context. So, while rpy 29 scripts may be useful for testing out ideas, they're not recommend for 30 much more than that.</p> 30 31 31 32 <p>Okay, with that warning out of the way, let's dive in. First, as mentioned, 32 33 rpy scripts are Python source files with the <code>.rpy</code> extension. So, … … 47 48 </pre> 48 49 49 50 <p>You may recognize this as the resource from 50 the <a href="dynamic-content.xhtml">first dynamic rendering example</a>. What's 51 different is what you don't see: we didn't import <code>reactor</code> or 52 <code>Site</code>. There are no calls to <code>listenTCP</code> or 53 <code>run</code>. Instead, and this is the core idea for rpy scripts, we just 54 bound the name <code>resource</code> to the resource we want the script to 55 serve. Every rpy script must bind this name, and this name is the only thing 56 Twisted Web will pay attention to in an rpy script.</p> 51 the <a href="dynamic-content.xhtml">first dynamic rendering 52 example</a>. What's different is what you don't see: we didn't 53 import <code>reactor</code> or <code>Site</code>. There are no calls 54 to <code>listenTCP</code> or <code>run</code>. Instead, and this is 55 the core idea for rpy scripts, we just bound the 56 name <code>resource</code> to the resource we want the script to 57 serve. Every rpy script must bind this name, and this name is the only 58 thing Twisted Web will pay attention to in an rpy script.</p> 57 59 58 60 <p>All that's left is to drop this rpy script into a Twisted Web server. There 59 61 are a few ways to do this. The simplest way is with <code>twistd</code>:</p> -
doc/web/howto/web-in-60/session-basics.xhtml
diff --git a/doc/web/howto/web-in-60/session-basics.xhtml b/doc/web/howto/web-in-60/session-basics.xhtml index a6b8e84..8b72a58 100644
a b 16 16 Twisted Web session API: how to get the session object for the current request 17 17 and how to prematurely expire a session.</p> 18 18 19 <p>Before diving into the APIs, let's look at the big picture of sessions in 20 Twisted Web. Sessions are represented by instances of <code class="API" 19 <p>Before diving into the APIs, let's look at the big picture of 20 sessions in Twisted Web. Sessions are represented by instances 21 of <code class="API" 21 22 base="twisted.web.server">Session</code>. The <code class="API" 22 base="twisted.web.server">Site</code> creates a new instance of23 <code>Session</code> the first time an application asks for it for a particular24 session. <code>Session</code> instances are kept on the <code>Site</code> 25 instance until they expire (due to inactivity or because they are explicitly 26 expired). Each time after the first that a particular session's 27 <code>Session</code> object is requested, it is retrieved from 28 the <code>Site</code>.</p>23 base="twisted.web.server">Site</code> creates a new instance 24 of <code>Session</code> the first time an application asks for it for 25 a particular session. <code>Session</code> instances are kept on 26 the <code>Site</code> instance until they expire (due to inactivity or 27 because they are explicitly expired). Each time after the first that a 28 particular session's <code>Session</code> object is requested, it is 29 retrieved from the <code>Site</code>.</p> 29 30 30 31 <p>With the conceptual underpinnings of the upcoming API in place, here comes 31 32 the example. This will be a very simple <a href="rpy-scripts.xhtml">rpy … … 63 64 return 'Your session has been expired.' 64 65 </pre> 65 66 66 <p>Finally, to make the example an rpy script, we'll make an instance of67 <code>ShowSession</code> and give it an instance of <code>ExpireSession</code> 68 as a child using <code class="API"67 <p>Finally, to make the example an rpy script, we'll make an instance 68 of <code>ShowSession</code> and give it an instance 69 of <code>ExpireSession</code> as a child using <code class="API" 69 70 base="twisted.web.resource">Resource.putChild</code>:</p> 70 71 71 72 <pre class="python"> -
doc/web/howto/web-in-60/session-endings.xhtml
diff --git a/doc/web/howto/web-in-60/session-endings.xhtml b/doc/web/howto/web-in-60/session-endings.xhtml index 33852c8..44b4d96 100644
a b 31 31 sessionTimeout = 60 32 32 </pre> 33 33 34 <p>To have Twisted Web actually make use of this session class, rather than the 35 default, it is also necessary to override the <code>sessionFactory</code> attribute of 36 <code class="API" base="twisted.web.server">Site</code>. We could do this with 37 another subclass, but we could also do it to just one instance 34 <p>To have Twisted Web actually make use of this session class, rather 35 than the default, it is also necessary to override 36 the <code>sessionFactory</code> attribute of <code class="API" 37 base="twisted.web.server">Site</code>. We could do this with another 38 subclass, but we could also do it to just one instance 38 39 of <code>Site</code>:</p> 39 40 40 41 <pre class="python"> … … 47 48 <p>Sessions given out for requests served by this <code>Site</code> will 48 49 use <code>ShortSession</code> and only last one minute without activity.</p> 49 50 50 <p>You can have arbitrary functions run when sessions expire, too. This can be 51 useful for cleaning up external resources associated with the session, tracking 52 usage statistics, and more. This functionality is provided via 53 <code class="API" base="twisted.web.server">Session.notifyOnExpire</code>. It 54 accepts a single argument: a function to call when the session expires. Here's a 51 <p>You can have arbitrary functions run when sessions expire, 52 too. This can be useful for cleaning up external resources associated 53 with the session, tracking usage statistics, and more. This 54 functionality is provided via <code class="API" 55 base="twisted.web.server">Session.notifyOnExpire</code>. It accepts a 56 single argument: a function to call when the session expires. Here's a 55 57 trivial example which prints a message whenever a session expires:</p> 56 58 57 59 <pre class="python"> -
doc/web/howto/web-in-60/session-store.xhtml
diff --git a/doc/web/howto/web-in-60/session-store.xhtml b/doc/web/howto/web-in-60/session-store.xhtml index a07c7ff..d961e4f 100644
a b 49 49 50 50 <p><i>What?</i>, I hear you say.</p> 51 51 52 <p>What's shown in this example is the interface and adaption-based API which53 <code>Session</code> exposes for persisting state. There are several critical 54 pieces interacting here:</p>52 <p>What's shown in this example is the interface and adaption-based 53 API which <code>Session</code> exposes for persisting state. There are 54 several critical pieces interacting here:</p> 55 55 56 56 <ul> 57 57 <li><code>ICounter</code> is an interface which serves several purposes. Like … … 69 69 relationship between its three arguments so that adaption will do what we 70 70 want in this case.</li> 71 71 <li>Adaption is performed by the expression <code>ICounter(ses)</code>. This 72 is read as <i>adapt <code>ses</code> to <code>ICounter</code></i>. Because72 is read as : adapt <code>ses</code> to <code>ICounter</code>. Because 73 73 of the <code>registerAdapter</code> call, it is roughly equivalent 74 74 to <code>Counter(ses)</code>. However (because of certain 75 75 things <code>Session</code> does), it also saves the <code>Counter</code> … … 134 134 resource = CounterResource() 135 135 </pre> 136 136 137 <p>One more thing to note is the <code>cache()</code> call at the top of this 138 example. As with the <a href="http-auth.xhtml">previous example</a> where this 139 came up, this rpy script is stateful. This time, it's the <code>ICounter</code> 140 definition and the 141 <code>registerAdapter</code> call that need to be executed only once. If we 142 didn't use <code>cache</code>, every request would define a new, different 143 interface named <code>ICounter</code>. Each of these would be a different key in 144 the session, so the counter would never get past one.</p> 137 <p>One more thing to note is the <code>cache()</code> call at the top 138 of this example. As with the <a href="http-auth.xhtml">previous 139 example</a> where this came up, this rpy script is stateful. This 140 time, it's the <code>ICounter</code> definition and 141 the <code>registerAdapter</code> call that need to be executed only 142 once. If we didn't use <code>cache</code>, every request would define 143 a new, different interface named <code>ICounter</code>. Each of these 144 would be a different key in the session, so the counter would never 145 get past one.</p> 145 146 146 147 </body> 147 148 </html> -
doc/web/howto/web-in-60/static-dispatch.xhtml
diff --git a/doc/web/howto/web-in-60/static-dispatch.xhtml b/doc/web/howto/web-in-60/static-dispatch.xhtml index b1d8e3d..9cf3db5 100644
a b 51 51 root = Resource() 52 52 </pre> 53 53 54 <p>Here comes the interesting part of this example. We're now going to create55 three more resources and attach them to the three URLs <code>/foo</code>, 56 <code>/bar</code>, and <code>/baz</code>:</p>54 <p>Here comes the interesting part of this example. We're now going to 55 create three more resources and attach them to the three 56 URLs <code>/foo</code>, <code>/bar</code>, and <code>/baz</code>:</p> 57 57 58 58 <pre class="python"> 59 59 root.putChild("foo", File("/tmp")) … … 70 70 reactor.run() 71 71 </pre> 72 72 73 <p>With this server running, <code>http://localhost:8880/foo</code> will serve a 74 listing of files from <code>/tmp</code>, <code>http://localhost:8880/bar</code> 75 will serve a listing of files from <code>/lost+found</code>, and 76 <code>http://localhost:8880/baz</code> will serve a listing of files from 77 <code>/opt</code>.</p> 73 <p>With this server running, <code>http://localhost:8880/foo</code> 74 will serve a listing of files 75 from <code>/tmp</code>, <code>http://localhost:8880/bar</code> will 76 serve a listing of files from <code>/lost+found</code>, 77 and <code>http://localhost:8880/baz</code> will serve a listing of 78 files from <code>/opt</code>.</p> 78 79 79 80 <p>Here's the whole example uninterrupted:</p> 80 81 -
doc/web/howto/web-in-60/wsgi.xhtml
diff --git a/doc/web/howto/web-in-60/wsgi.xhtml b/doc/web/howto/web-in-60/wsgi.xhtml index ddc0735..22ff8c1 100644
a b 10 10 <body> 11 11 <h1>WSGI</h1> 12 12 13 <p>The goal of this example is to show you how to use <code class="API" 14 base="twisted.web.wsgi">WSGIResource</code>, another existing 15 <code class="API" base="twisted.web.resource">Resource</code> subclass, to serve 16 <a href="http://www.python.org/dev/peps/pep-0333/">WSGI applications</a> in a 17 Twisted Web server.</p> 13 <p>The goal of this example is to show you how to 14 use <code class="API" base="twisted.web.wsgi">WSGIResource</code>, 15 another existing <code class="API" 16 base="twisted.web.resource">Resource</code> subclass, to 17 serve <a href="http://www.python.org/dev/peps/pep-0333/">WSGI 18 applications</a> in a Twisted Web server.</p> 18 19 19 20 <p>Note thate <code>WSGIResource</code> is a multithreaded WSGI container. Like 20 21 any other WSGI container, you can't do anything asynchronous in your WSGI … … 53 54 resource = WSGIResource(reactor, reactor.getThreadPool(), application) 54 55 </pre> 55 56 56 <p>Let's dwell on this line for a minute. The first parameter passed to 57 <code>WSGIResource</code> is the reactor. Despite the fact that the reactor is 58 global and any code that wants it can always just import it (as, in fact, this 59 rpy script simply does itself), passing it around as a parameter leaves the door 60 open for certain future possibilities - for example, having more than one 61 reactor. There are also testing implications. Consider how much easier it is to 62 unit test a function that accepts a reactor - perhaps a mock reactor specially 63 constructed to make your tests easy to write - rather than importing the real 64 global reactor. That's why <code>WSGIResource</code> requires you to pass the 65 reactor to it.</p> 57 <p>Let's dwell on this line for a minute. The first parameter passed 58 to <code>WSGIResource</code> is the reactor. Despite the fact that the 59 reactor is global and any code that wants it can always just import it 60 (as, in fact, this rpy script simply does itself), passing it around 61 as a parameter leaves the door open for certain future possibilities - 62 for example, having more than one reactor. There are also testing 63 implications. Consider how much easier it is to unit test a function 64 that accepts a reactor - perhaps a mock reactor specially constructed 65 to make your tests easy to write - rather than importing the real 66 global reactor. That's why <code>WSGIResource</code> requires you to 67 pass the reactor to it.</p> 66 68 67 69 <p>The second parameter passed to <code>WSGIResource</code> is 68 70 a <code class="API"
