Qgelm

Twisted Network Programming Essentials, 2nd Edition

Originalartikel

Backup

<html> <meta charset=„UTF-8“/><meta http-equiv=„X-UA-Compatible“ content=„IE=edge“/><title>4. Web Servers - Twisted Network Programming Essentials, 2nd Edition [Book]</title><meta name=„viewport“ content=„width=device-width, initial-scale=1“/><meta property=„og:title“ content=„Twisted Network Programming Essentials, 2nd Edition“/><meta itemprop=„isPartOf“ content=„/library/view/twisted-network-programming/9781449326104/“/><meta itemprop=„name“ content=„4. Web Servers“/><meta property=„og:url“ itemprop=„url“ content=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html“/><meta property=„og:site_name“ content=„O&#8217;Reilly | Safari“/><meta property=„og:image“ itemprop=„thumbnailUrl“ content=„https://www.oreilly.com/library/cover/9781449326104/“/><meta property=„og:image:secure_url“ itemprop=„thumbnailUrl“ content=„https://www.safaribooksonline.com/library/cover/9781449326104/360h/“/><meta property=„og:description“ itemprop=„description“ name=„description“ content=„Chapter&#160;4.&#160;Web Servers This chapter will first extend our experience with writing basic TCP servers to the construction of basic HTTP servers. With that context and understanding of the &#8230; - Selection from Twisted Network Programming Essentials, 2nd Edition [Book]“/><meta itemprop=„inLanguage“ content=„en“/><meta itemprop=„publisher“ content=„O'Reilly Media, Inc.“/><meta property=„og:type“ content=„article“/><meta property=„og:book:isbn“ itemprop=„isbn“ content=„9781449326111“/><meta property=„og:book:author“ itemprop=„author“ content=„Jessica McKellar“/><meta property=„og:book:author“ itemprop=„author“ content=„Abe Fettig“/><meta property=„og:book:tag“ itemprop=„about“ content=„Python“/><meta property=„og:image:width“ content=„400“/><meta property=„og:image:height“ content=„400“/><meta name=„twitter:card“ content=„summary“/><meta name=„twitter:site“ content=„@safari“/><header class=„global“ readability=„0“><a href=„https://www.oreilly.com/go/oreilly“ class=„orm-topbar“><svg xmlns=„http://www.w3.org/2000/svg“ viewbox=„0 0 59.13 9.88“><desc>O'Reilly logo</desc><rect x=„29.71“ y=„0.42“ width=„1.54“ height=„9.22“/></svg></a> </header><section id=„trial-overlay“><div class=„trial-overlay-content“ readability=„33“> <h2 class=„trial-modal-title“>Stay ahead with the world's most comprehensive technology and business learning platform.</h2> <h2>With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.</h2> </div> </section><section role=„document“ readability=„188“><section id=„sbo-reader“ readability=„29“><div class=„sbo-reader-content sbo-sample-reader“ readability=„126“> <div id=„sbo-rt-content“ readability=„221“> <section class=„chapter“ title=„Chapter&#160;4.&#160;Web Servers“ epub:type=„chapter“ id=„twistedadn-CHP-4“ readability=„58“><h2 class=„title“>Chapter&#160;4.&#160;Web Servers</h2> <p>This chapter will first extend our experience with writing basic TCP servers to the construction of basic HTTP servers. With that context and understanding of the HTTP protocol in hand, we&#8217;ll then abandon the low-level API in favor of the high-level

twisted.web

APIs used for constructing sophisticated web servers.</p> <div class=„note“ title=„Note“ readability=„13“> <h3 class=„title“>Note</h3> <p>Twisted Web is the Twisted subproject focusing on HTTP communication. It has robust HTTP 1.1 and HTTPS client and server implementations, proxy support, WSGI integration, basic HTML templating, and more.</p> </div> <div class=„sect1“ title=„Responding to HTTP Requests: A Low-Level Review“ readability=„83“> <h2 class=„title c1“ id=„twistedadn-CHP-4-SECT-1“>Responding to HTTP Requests: A Low-Level Review</h2> <p>The HyperText Transfer Protocol (HTTP) is a request/response application-layer protocol, where requests are initiated by a client to a server, which responds with the requested resource. It is text-based and newline-delimited, and thus easy for humans to read.</p> <p>To experiment with the HTTP protocol we&#8217;ll create a subclass of

protocol.Protocol

, the same class we used to build our echo servers and clients in <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch02.html“ title=„Chapter&#160;2.&#160;Building Basic Clients and Servers“>Chapter&#160;2</a>. Our protocol will know how to accept a connection, process the request, and send back an HTTP-formatted response.</p> <p>This section is intended as both a glimpse under the hood and a refresher on the HTTP protocol. When building real web servers, you&#8217;ll almost certainly use the higher-level

twisted.web

APIs Twisted provides. If you&#8217;d prefer to skip to that content, head over to <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-SECT-2“ title=„Handling GET Requests“>Handling GET Requests</a>.</p> <div class=„sect2“ title=„The Structure of an HTTP Request“ readability=„83“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-1.1“>The Structure of an HTTP Request</h3> <p>Every HTTP request starts with a single line containing the HTTP method, the path to the desired resource, and the HTTP version. Following this line are an arbitrary number of header lines. A blank line indicates the end of the headers. The header section is optionally followed by additional data called the <em>body</em> of the request, such as data being posted from an HTML form.</p> <p>Here&#8217;s an example of a minimal HTTP request. This request asks the server to perform the method

GET

on the root resource <em>/</em> using HTTP version 1.1:</p> <pre class=„programlisting“>GET / HTTP/1.1 Host: www.example.com</pre> <p>We can emulate a web browser and make this HTTP GET request manually using the <em>telnet</em> utility (taking care to remember the newline after the headers):</p> <pre class=„programlisting“>$ <strong class=„userinput“>

telnet www.google.com 80

</strong> Trying 74.125.131.99… Connected to www.l.google.com. Escape character is '^]'. <strong class=„userinput“>

GET / HTTP/1.1 Host: www.google.com

</strong></pre> <p>The server responds with a line containing the HTTP version used for the response and an HTTP status code. Like the request, the response contains header lines followed by a blank line and the message body. A minimal HTTP response might look like this:</p> <pre class=„programlisting“>HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 17 Connection: Close Hello HTTP world!</pre> <p><em>www.google.com</em>&#8217;s response is more complicated, since it is setting cookies and various security headers, but the format is the same.</p> <p>To write our own HTTP server, we can implement a

Protocol

that parses newline-delimited input, parses out the headers, and returns an HTTP-formatted response. <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-1“ title=„Example&#160;4-1.&#160;webecho.py“>Example&#160;4-1</a> shows a simple HTTP implementation that echoes each request back to the client.</p> <div class=„example“ readability=„12“> <p>Example&#160;4-1.&#160;webecho.py</p> <div class=„example-contents“ readability=„45“> <pre class=„kn“>from

twisted.protocols
import
basic
from
twisted.internet
import
protocol
,
reactor
class
HTTPEchoProtocol
(
basic
.
LineReceiver
):
  <code class="k">def</code> <code class="nf">__init__</code><code class="p">(</code><code class="bp">self</code><code class="p">):</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">lines</code> <code class="o">=</code> <code class="p">[]</code>
  <code class="k">def</code> <code class="nf">lineReceived</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">line</code><code class="p">):</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">lines</code><code class="o">.</code><code class="n">append</code><code class="p">(</code><code class="n">line</code><code class="p">)</code>
      <code class="k">if</code> <code class="ow">not</code> <code class="n">line</code><code class="p">:</code>
          <code class="bp">self</code><code class="o">.</code><code class="n">sendResponse</code><code class="p">()</code>
  <code class="k">def</code> <code class="nf">sendResponse</code><code class="p">(</code><code class="bp">self</code><code class="p">):</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">sendLine</code><code class="p">(</code><code class="s">"HTTP/1.1 200 OK"</code><code class="p">)</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">sendLine</code><code class="p">(</code><code class="s">""</code><code class="p">)</code>
      <code class="n">responseBody</code> <code class="o">=</code> <code class="s">"You said:</code><code class="se">\r\n\r\n</code><code class="s">"</code> <code class="o">+</code> <code class="s">"</code><code class="se">\r\n</code><code class="s">"</code><code class="o">.</code><code class="n">join</code><code class="p">(</code><code class="bp">self</code><code class="o">.</code><code class="n">lines</code><code class="p">)</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">transport</code><code class="o">.</code><code class="n">write</code><code class="p">(</code><code class="n">responseBody</code><code class="p">)</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">transport</code><code class="o">.</code><code class="n">loseConnection</code><code class="p">()</code>
class
HTTPEchoFactory
(
protocol
.
ServerFactory
):
  <code class="k">def</code> <code class="nf">buildProtocol</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">addr</code><code class="p">):</code>
      <code class="k">return</code> <code class="n">HTTPEchoProtocol</code><code class="p">()</code>
reactor
.
listenTCP
(
8000
,
HTTPEchoFactory
())
reactor
.
run
()

</pre></div> </div> <p>As with our basic TCP servers from <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch02.html“ title=„Chapter&#160;2.&#160;Building Basic Clients and Servers“>Chapter&#160;2</a>, we create a protocol factory,

HTTPEchoFactory

, inheriting from

protocol.ServerFactory

. It builds instances of our

HTTPEchoProtocol

, which inherits from

basic.LineReceiver

so we don&#8217;t have to write our own boilerplate code for handling newline-delimited protocols.</p> <p>We keep track of lines as they are received in

lineReceived

until we reach an empty line, the carriage return and line feed (

\r\n

) marking the end of the headers sent by the client. We then echo back the request text and terminate the connection.</p> <p>HTTP uses TCP as its transport-layer protocol, so we use

listenTCP

to register callbacks with the reactor to get notified when TCP packets containing our HTTP data arrive on our designated port.</p> <p>We can start this web server with <em class=„filename“>python webecho.py</em> then interact with the server through <em>telnet</em> or a web browser.</p> <p>Using <em>telnet</em>, the communication will look something like:</p> <pre class=„programlisting“>$ <strong class=„userinput“>

telnet localhost 8000

</strong> Trying 127.0.0.1… Connected to localhost. Escape character is '^]'. <strong class=„userinput“>

GET / HTTP/1.1 Host: localhost:8000 X-Header: "My test header"

</strong> HTTP/1.1 200 OK You said: GET / HTTP/1.1 Host: localhost:8000 X-Header: „My test header“ Connection closed by foreign host.</pre> <p>It&#8217;s interesting to see what extra information your browser adds when making HTTP requests. To send a request to the server from a browser, visit <em>http://localhost:8000</em>.</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-FIG-1“ title=„Figure&#160;4-1.&#160;Browser GET request“>Figure&#160;4-1</a> shows what I get when I make this request from Chrome on my MacBook.</p> <div class=„figure“ readability=„7“> <div class=„figure-contents mediaobject“><img src=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/httpatomoreillycomsourceoreillyimages1555582.png“ alt=„Browser GET request“ width=„782“ height=„253“/></div> <p>Figure&#160;4-1.&#160;Browser GET request</p> </div> <p>By default, Chrome is telling websites about my operating system and browser and that I browse in English, as well as passing other headers specifying properties for the response.</p> </div> <div class=„sect2“ title=„Parsing HTTP Requests“ readability=„48“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-1.2“>Parsing HTTP Requests</h3> <p>The

HTTPEchoProtocol

class in <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-1“ title=„Example&#160;4-1.&#160;webecho.py“>Example&#160;4-1</a> understands the structure of an HTTP request, but it doesn&#8217;t know how to parse the request and respond with the resource being requested. To do this, we&#8217;ll need to make our first foray into

twisted.web

.</p> <p>An HTTP request is represented by

twisted.web.http.Request

. We can specify how requests are processed by subclassing

http.Request

and overriding its

process

method. <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-2“ title=„Example&#160;4-2.&#160;requesthandler.py“>Example&#160;4-2</a> subclasses

http.Request

to serve one of three resources: an HTML page for the root resource <em>/</em>, a page for <em>/about</em>, and a 404

http.NOT_FOUND

if any other path is specified.</p> <div class=„example“ readability=„12“> <p>Example&#160;4-2.&#160;requesthandler.py</p> <div class=„example-contents“ readability=„47“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web
import
http
class
MyRequestHandler
(
http
.
Request
):
  <code class="n">resources</code> <code class="o">=</code> <code class="p">{</code>
      <code class="s">'/'</code><code class="p">:</code> <code class="s">'&lt;h1&gt;Home&lt;/h1&gt;Home page'</code><code class="p">,</code>
      <code class="s">'/about'</code><code class="p">:</code> <code class="s">'&lt;h1&gt;About&lt;/h1&gt;All about me'</code><code class="p">,</code>
      <code class="p">}</code>
  <code class="k">def</code> <code class="nf">process</code><code class="p">(</code><code class="bp">self</code><code class="p">):</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">setHeader</code><code class="p">(</code><code class="s">'Content-Type'</code><code class="p">,</code> <code class="s">'text/html'</code><code class="p">)</code>
      <code class="k">if</code> <code class="bp">self</code><code class="o">.</code><code class="n">resources</code><code class="o">.</code><code class="n">has_key</code><code class="p">(</code><code class="bp">self</code><code class="o">.</code><code class="n">path</code><code class="p">):</code>
          <code class="bp">self</code><code class="o">.</code><code class="n">write</code><code class="p">(</code><code class="bp">self</code><code class="o">.</code><code class="n">resources</code><code class="p">[</code><code class="bp">self</code><code class="o">.</code><code class="n">path</code><code class="p">])</code>
      <code class="k">else</code><code class="p">:</code>
          <code class="bp">self</code><code class="o">.</code><code class="n">setResponseCode</code><code class="p">(</code><code class="n">http</code><code class="o">.</code><code class="n">NOT_FOUND</code><code class="p">)</code>
          <code class="bp">self</code><code class="o">.</code><code class="n">write</code><code class="p">(</code><code class="s">"&lt;h1&gt;Not Found&lt;/h1&gt;Sorry, no such resource."</code><code class="p">)</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">finish</code><code class="p">()</code>
class
MyHTTP
(
http
.
HTTPChannel
):
  <code class="n">requestFactory</code> <code class="o">=</code> <code class="n">MyRequestHandler</code>
class
MyHTTPFactory
(
http
.
HTTPFactory
):
  <code class="k">def</code> <code class="nf">buildProtocol</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">addr</code><code class="p">):</code>
      <code class="k">return</code> <code class="n">MyHTTP</code><code class="p">()</code>
reactor
.
listenTCP
(
8000
,
MyHTTPFactory
())
reactor
.
run
()

</pre></div> </div> <p>As always, we register a factory that generates instances of our protocol with the reactor. In this case, instead of subclassing

protocol.Protocol

directly, we are taking advantage of a higher-level API,

http.HTTPChannel

, which inherits from

basic.LineReceiver

and already understands the structure of an HTTP request and the numerous behaviors required by the HTTP RFCs.</p> <p>Our

MyHTTP

protocol specifies how to process requests by setting its

requestFactory

instance variable to

MyRequestHander

, which subclasses

http.Request

.

Request

&#8217;s

process

method is a noop that must be overridden in subclasses, which we do here. The HTTP response code is 200 unless overridden with

setResponseCode

, as we do to send a 404

http.NOT_FOUND

when an unknown resource is requested.</p> <p>To test this server, run <em class=„filename“>python requesthandler.py</em>; this will start up the web server on port 8000. You can then test accessing the supported resources, <em>http://localhost:8000/</em> and <em>http://localhost:8000/about</em>, and an unsupported resource like <em>http://localhost:8000/foo</em>.</p> </div> </div> <div class=„sect1“ title=„Handling GET Requests“ readability=„57“> <h2 class=„title c1“ id=„twistedadn-CHP-4-SECT-2“>Handling GET Requests</h2> <p>Now that we have a good grasp of the structure of the HTTP protocol and how the low-level APIs work, we can move up to the high-level APIs in

twisted.web.server

that facilitate the construction of more sophisticated web servers.</p> <div class=„sect2“ title=„Serving Static Content“ readability=„43“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-2.2“>Serving Static Content</h3> <p>A common task for a web server is to be able to serve static content out of some directory. <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-3“ title=„Example&#160;4-3.&#160;static_content.py“>Example&#160;4-3</a> shows a basic implementation.</p> <div class=„example“ readability=„9“> <p>Example&#160;4-3.&#160;static_content.py</p> <div class=„example-contents“ readability=„39“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.server
import
Site
from
twisted.web.static
import
File
resource
=
File
(
'/var/www/mysite'
)
factory
=
Site
(
resource
)
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <p>At this level we no longer have to worry about HTTP protocol details. Instead, we use a

Site

, which subclasses

http.HTTPFactory

and manages HTTP sessions and dispatching to resources for us. A

Site

is initialized with the resource to which it is managing access.</p> <p>A resource must provide the

IResource

interface, which describes how the resource gets rendered and how child resources in the resource hierarchy are added and accessed. In this case, we initialize our

Site

with a

File

resource representing a regular, non-interpreted file.</p> <div class=„tip“ title=„Tip“ readability=„12“> <h3 class=„title“>Tip</h3> <p>

twisted.web

contains implementations for many common resources. Besides

File

, available resources include a customizable

DirectoryListing

and

ErrorPage

, a

ProxyResource

that renders results retrieved from another server, and an

XMLRPC

implementation.</p> </div> <p>The

Site

is registered with the reactor, which will then listen for requests on port 8000.</p> <p>After starting the web server with <em class=„filename“>python static_content.py</em>, we can visit <em>http://localhost:8000</em> in a web browser. The server serves up a directory listing for all of the files in <em>/var/www/mysite/</em> (replace that path with a valid path to a directory on your system).</p> <div class=„sect3“ title=„Static URL dispatch“ readability=„20“> <h4 class=„title“ id=„id928285“>Static URL dispatch</h4> <p>What if you&#8217;d like to serve different content at different URLs?</p> <p>We can create a hierarchy of resources to serve at different URLs by registering

Resource

s as children of the root resource using its

putChild

method. <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-4“ title=„Example&#160;4-4.&#160;static_dispatch.py“>Example&#160;4-4</a> demonstrates this static URL dispatch.</p> <div class=„example“ readability=„10“> <p>Example&#160;4-4.&#160;static_dispatch.py</p> <div class=„example-contents“ readability=„42“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.server
import
Site
from
twisted.web.static
import
File
root
=
File
(
'/var/www/mysite'
)
root
.
putChild
(
"doc"
,
File
(
"/usr/share/doc"
))
root
.
putChild
(
"logs"
,
File
(
"/var/log/mysitelogs"
))
factory
=
Site
(
root
)
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <p>Now, visiting <em>http://localhost:8000/</em> in a web browser will serve content from <em>/var/www/mysite</em>, <em>http://localhost:8000/doc</em> will serve content from <em>/usr/share/doc</em>, and <em>http://localhost:8000/logs/</em> will serve content from <em>/var/log/mysitelogs</em>.</p> <p>These

Resource

hierarchies can be extended to arbitrary depths by registering child resources with existing resources in the hierarchy.</p> </div> </div> <div class=„sect2“ title=„Serving Dynamic Content“ readability=„44“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-2.3“>Serving Dynamic Content</h3> <p>Serving dynamic content looks very similar to serving static content&#8212;the big difference is that instead of using an existing

Resource

, like

File

, you&#8217;ll subclass

Resource

to define the new dynamic resource you want a

Site

to serve.</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-5“ title=„Example&#160;4-5.&#160;dynamic_content.py“>Example&#160;4-5</a> implements a simple clock page that displays the local time when you visit any URL.</p> <div class=„example“ readability=„11“> <p>Example&#160;4-5.&#160;dynamic_content.py</p> <div class=„example-contents“ readability=„43“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.resource
import
Resource
from
twisted.web.server
import
Site
import
time
class
ClockPage
(
Resource
):
  <code class="n">isLeaf</code> <code class="o">=</code> <code class="bp">True</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">return</code> <code class="s">"The local time is </code><code class="si">%s</code><code class="s">"</code> <code class="o">%</code> <code class="p">(</code><code class="n">time</code><code class="o">.</code><code class="n">ctime</code><code class="p">(),)</code>
resource
=
ClockPage
()
factory
=
Site
(
resource
)
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <p>

ClockPage

is a subclass of

Resource

. We implement a

render_

method for every HTTP method we want to support; in this case we only care about supporting GET requests, so

render_GET

is all we implement. If we were to POST to this web server, we&#8217;d get a 405 Method Not Allowed unless we also implemented

render_POST

.</p> <p>The rendering method is passed the request made by the client. This is not an instance of

twisted.web.http.Request

, as in <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-2“ title=„Example&#160;4-2.&#160;requesthandler.py“>Example&#160;4-2</a>; it is instead an instance of

twisted.web.server.Request

, which subclasses

http.Request

and understands application-layer ideas like session management and rendering.</p> <p>

render_GET

returns whatever we want served as a response to a GET request. In this case, we return a string containing the local time. If we start our server with <em class=„filename“>python dynamic_content.py</em>, we can visit any URL on <em>http://localhost:8000</em> with a web browser and see the local time displayed and updated as we reload.</p> <p>The

isLeaf

instance variable describes whether or not a resource will have children. Without more work on our part (as demonstrated in <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-6“ title=„Example&#160;4-6.&#160;dynamic_dispatch.py“>Example&#160;4-6</a>), only leaf resources get rendered; if we set

isLeaf

to

False

and restart the server, attempting to view any URL will produce a 404 No Such Resource.</p> </div> <div class=„sect2“ title=„Dynamic Dispatch“ readability=„68“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-3.1“>Dynamic Dispatch</h3> <p>We know how to serve static and dynamic content. The next step is to be able to respond to requests dynamically, serving different resources based on the URL.</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-6“ title=„Example&#160;4-6.&#160;dynamic_dispatch.py“>Example&#160;4-6</a> demonstrates a calendar server that displays the calendar for the year provided in the URL. For example, visiting <em>http://localhost:8000/2013</em> will display the calendar for 2013, as shown in <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-FIG-2“ title=„Figure&#160;4-2.&#160;Calendar“>Figure&#160;4-2</a>.</p> <div class=„example“ readability=„14“> <p>Example&#160;4-6.&#160;dynamic_dispatch.py</p> <div class=„example-contents“ readability=„50“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.resource
import
Resource
,
NoResource
from
twisted.web.server
import
Site
from
calendar
import
calendar
class
YearPage
(
Resource
):
  <code class="k">def</code> <code class="nf">__init__</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">year</code><code class="p">):</code>
      <code class="n">Resource</code><code class="o">.</code><code class="n">__init__</code><code class="p">(</code><code class="bp">self</code><code class="p">)</code>
      <code class="bp">self</code><code class="o">.</code><code class="n">year</code> <code class="o">=</code> <code class="n">year</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">return</code> <code class="s">"&lt;html&gt;&lt;body&gt;&lt;pre&gt;</code><code class="si">%s</code><code class="s">&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;"</code> <code class="o">%</code> <code class="p">(</code><code class="n">calendar</code><code class="p">(</code><code class="bp">self</code><code class="o">.</code><code class="n">year</code><code class="p">),)</code>
class
CalendarHome
(
Resource
):
  <code class="k">def</code> <code class="nf">getChild</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">name</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">if</code> <code class="n">name</code> <code class="o">==</code> <code class="s">''</code><code class="p">:</code>
          <code class="k">return</code> <code class="bp">self</code>
      <code class="k">if</code> <code class="n">name</code><code class="o">.</code><code class="n">isdigit</code><code class="p">():</code>
          <code class="k">return</code> <code class="n">YearPage</code><code class="p">(</code><code class="nb">int</code><code class="p">(</code><code class="n">name</code><code class="p">))</code>
      <code class="k">else</code><code class="p">:</code>
          <code class="k">return</code> <code class="n">NoResource</code><code class="p">()</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">return</code> <code class="s">"&lt;html&gt;&lt;body&gt;Welcome to the calendar server!&lt;/body&gt;&lt;/html&gt;"</code>
root
=
CalendarHome
()
factory
=
Site
(
root
)
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <div class=„figure“ readability=„7“> <div class=„figure-contents mediaobject“><img src=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/httpatomoreillycomsourceoreillyimages1555583.png“ alt=„Calendar“ width=„748“ height=„226“/></div> <p>Figure&#160;4-2.&#160;Calendar</p> </div> <p>This example has the same structure as <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-3“ title=„Example&#160;4-3.&#160;static_content.py“>Example&#160;4-3</a>. A TCP server is started on port 8000, serving the content registered with a

Site

, which is a subclass of

twisted.web.http.HTTPFactory

and knows how to manage access to resources.</p> <p>The root resource is

CalendarHome

, which subclasses

Resource

to specify how to look up child resources and how to render itself.</p> <p>

CalendarHome.getChild

describes how to traverse a URL from left to right until we get a renderable resource. If there is no additional component to the requested URL (i.e., the request was for <em>/</em> ),

CalendarHome

returns itself to be rendered by invoking its

render_GET

method. If the URL has an additional component to its path that is an integer, an instance of

YearPage

is rendered. If that path component couldn&#8217;t be converted to a number, an instance of

twisted.web.error.NoResource

is returned instead, which will render a generic 404 page.</p> <p>There are a few subtle points to this example that deserve highlighting.</p> <div class=„sect3“ title=„Creating resources that are both renderable and have children“ readability=„49“> <h4 class=„title“ id=„id946146“>Creating resources that are both renderable and have children</h4> <p>Note that

CalendarHome

does not set

isLeaf

to

True

, and yet it is still rendered when we visit <em>http://localhost:8000</em>.</p> <p>In general, only resources that are leaves are rendered; this can be because

isLeaf

is set to

True

or because when traversing the resource hierarchy, that resource is where we are when the URL runs out. However, when

isLeaf

is

True

for a resource, its

getChild

method is never called. Thus, for resources that have children,

isLeaf

cannot be set to

True

.</p> <p>If we want

CalendarHome

to both get rendered and have children, we must override its

getChild

method to dictate resource generation.</p> <p>In

CalendarHome.getChild

, if

name == ''

(i.e., if we are requesting the root resource), we return ourself to get rendered. Without that

if

condition, visiting <em>http://localhost:8000</em> would produce a 404.</p> <p>Similarly,

YearPage

does not have

isLeaf

set to

True

. That means that when we visit <em>http://localhost:8000/2013</em>, we get a rendered calendar because 2013 is at the end of the URL, but if we visit <em>http://localhost:8000/2013/foo</em>, we get a 404.</p> <p>If we want <em>http://localhost:8000/2013/foo</em> to generate a calendar just like <em>http://localhost:8000/2013</em>, we need to set

isLeaf

to

True

or have

YearPage

override

getChild

to return itself, like we do in

CalendarHome

.</p> </div> <div class=„sect3“ title=„Redirects“ readability=„24“> <h4 class=„title“ id=„id943442“>Redirects</h4> <p>In <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-6“ title=„Example&#160;4-6.&#160;dynamic_dispatch.py“>Example&#160;4-6</a>, visiting <em>http://localhost:8000</em> produced a welcome page. What if we wanted <em>http://localhost:8000</em> to instead redirect to the calendar for the current year?</p> <p>In the relevant render method (e.g.,

render_GET

), instead of rendering the resource at a given URL, we need to construct a redirect with

twisted.web.util.redirectTo

.

redirectTo

takes as arguments the URL component to which to redirect, and the request, which still needs to be rendered.</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-7“ title=„Example&#160;4-7.&#160;redirectTo“>Example&#160;4-7</a> shows a revised

CalenderHome.render_GET

that redirects to the URL for the current year&#8217;s calendar (e.g., <em>http://localhost:8000/2013</em>) upon requesting the root resource at <em>http://localhost:8000</em>.</p> <div class=„example“ readability=„9“> <p>Example&#160;4-7.&#160;redirectTo</p> <div class=„example-contents“ readability=„39“> <pre class=„kn“>from

datetime
import
datetime
from
twisted.web.util
import
redirectTo
def
render_GET
(
self
,
request
):
  <code class="k">return</code> <code class="n">redirectTo</code><code class="p">(</code><code class="n">datetime</code><code class="o">.</code><code class="n">now</code><code class="p">()</code><code class="o">.</code><code class="n">year</code><code class="p">,</code> <code class="n">request</code><code class="p">)</code></pre></div>

</div> </div> </div> </div> <div class=„sect1“ title=„Handling POST Requests“ readability=„16“> <h2 class=„title c1“ id=„twistedadn-CHP-4-SECT-3“>Handling POST Requests</h2> <p>To handle POST requests, implement a

render_POST

method in your

Resource

.</p> <div class=„sect2“ title=„A Minimal POST Example“ readability=„21“> <h3 class=„title“ id=„twistedadn-CHP-4-SECT-3.2“>A Minimal POST Example</h3> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-8“ title=„Example&#160;4-8.&#160;handle_post.py“>Example&#160;4-8</a> serves a page where users can fill out and submit to the web server the contents of a text box. The server will then display that text back to the user.</p> <div class=„example“ readability=„12“> <p>Example&#160;4-8.&#160;handle_post.py</p> <div class=„example-contents“ readability=„45“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.resource
import
Resource
from
twisted.web.server
import
Site
import
cgi
class
FormPage
(
Resource
):
  <code class="n">isLeaf</code> <code class="o">=</code> <code class="bp">True</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">return</code> <code class="s">"""</code>
&lt;html&gt;
 &lt;body&gt;
  &lt;form method="POST"&gt;
   &lt;input name="form-field" type="text" /&gt;
   &lt;input type="submit" /&gt;
   &lt;/form&gt;
   &lt;/body&gt;
   &lt;/html&gt;
"""
  <code class="k">def</code> <code class="nf">render_POST</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="k">return</code> <code class="s">"""</code>
&lt;html&gt;
 &lt;body&gt;You submitted: 
%s
&lt;/body&gt;
 &lt;/html&gt;
"""
%
(
cgi
.
escape
(
request
.
args
[
"form-field"
][
0
]),)
factory
=
Site
(
FormPage
())
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <p>The

FormPage
Resource

in <em class=„filename“>handle_post.py</em> implements both

render_GET

and

render_POST

methods.</p> <p>

render_GET

returns the HTML for a blank page with a text box called

"form-field"

. When a visitor visits <em>http://localhost:8000</em>, she will see this form.</p> <p>

render_POST

extracts the text inputted by the user from

request.args

, sanitizes it with

cgi.escape

, and returns HTML displaying what the user submitted.</p> </div> </div> <div class=„sect1“ title=„Asynchronous Responses“ readability=„46“> <h2 class=„title c1“ id=„I_sect14_id390469“>Asynchronous Responses</h2> <p>In all of the Twisted web server examples up to this point, we have assumed that the server can instantaneously respond to clients without having to first retrieve an expensive resource (say, from a database query) or do expensive computation. What happens when responding to a request blocks?</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-9“ title=„Example&#160;4-9.&#160;blocking.py“>Example&#160;4-9</a> implements a dummy

BusyPage

resource that sleeps for five seconds before returning a response to the request.</p> <div class=„example“ readability=„11“> <p>Example&#160;4-9.&#160;blocking.py</p> <div class=„example-contents“ readability=„44“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.web.resource
import
Resource
from
twisted.web.server
import
Site
import
time
class
BusyPage
(
Resource
):
  <code class="n">isLeaf</code> <code class="o">=</code> <code class="bp">True</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="n">time</code><code class="o">.</code><code class="n">sleep</code><code class="p">(</code><code class="mi">5</code><code class="p">)</code>
      <code class="k">return</code> <code class="s">"Finally done, at </code><code class="si">%s</code><code class="s">"</code> <code class="o">%</code> <code class="p">(</code><code class="n">time</code><code class="o">.</code><code class="n">asctime</code><code class="p">(),)</code>
factory
=
Site
(
BusyPage
())
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <p>If you run this server and then load <em>http://localhost:8000</em> in several browser tabs in quick succession, you&#8217;ll observe that the last page to load will load N*5 seconds after the first page request, where N is the number of requests to the server. In other words, the requests are processed serially.</p> <p>This is terrible performance! We need our web server to be responding to other requests while an expensive resource is being processed.</p> <p>One of the great properties of this asynchronous framework is that we can achieve the responsiveness that we want without introducing threads by using the

Deferred

API we already know and love.</p> <p><a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-10“ title=„Example&#160;4-10.&#160;non_blocking.py“>Example&#160;4-10</a> demonstrates how to use a

Deferred

instead of blocking on an expensive resource.

deferLater

replaces the blocking

time.sleep(5)

with a

Deferred

that will fire after five seconds, with a callback to

_delayedRender

to finish the request when the fake resource becomes available. Then, instead of waiting on that resource,

render_GET

returns

NOT_DONE_YET

immediately, freeing up the web server to process other requests.</p> <div class=„example“ readability=„13“> <p>Example&#160;4-10.&#160;non_blocking.py</p> <div class=„example-contents“ readability=„49“> <pre class=„kn“>from

twisted.internet
import
reactor
from
twisted.internet.task
import
deferLater
from
twisted.web.resource
import
Resource
from
twisted.web.server
import
Site
,
NOT_DONE_YET
import
time
class
BusyPage
(
Resource
):
  <code class="n">isLeaf</code> <code class="o">=</code> <code class="bp">True</code>
  <code class="k">def</code> <code class="nf">_delayedRender</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="n">request</code><code class="o">.</code><code class="n">write</code><code class="p">(</code><code class="s">"Finally done, at </code><code class="si">%s</code><code class="s">"</code> <code class="o">%</code> <code class="p">(</code><code class="n">time</code><code class="o">.</code><code class="n">asctime</code><code class="p">(),))</code>
      <code class="n">request</code><code class="o">.</code><code class="n">finish</code><code class="p">()</code>
  <code class="k">def</code> <code class="nf">render_GET</code><code class="p">(</code><code class="bp">self</code><code class="p">,</code> <code class="n">request</code><code class="p">):</code>
      <code class="n">d</code> <code class="o">=</code> <code class="n">deferLater</code><code class="p">(</code><code class="n">reactor</code><code class="p">,</code> <code class="mi">5</code><code class="p">,</code> <code class="k">lambda</code><code class="p">:</code> <code class="n">request</code><code class="p">)</code>
      <code class="n">d</code><code class="o">.</code><code class="n">addCallback</code><code class="p">(</code><code class="bp">self</code><code class="o">.</code><code class="n">_delayedRender</code><code class="p">)</code>
      <code class="k">return</code> <code class="n">NOT_DONE_YET</code>
factory
=
Site
(
BusyPage
())
reactor
.
listenTCP
(
8000
,
factory
)
reactor
.
run
()

</pre></div> </div> <div class=„tip“ title=„Tip“ readability=„14“> <h3 class=„title“>Tip</h3> <p>If you run <a class=„xref“ href=„https://www.oreilly.com/library/view/twisted-network-programming/9781449326104/ch04.html#twistedadn-CHP-4-EX-10“ title=„Example&#160;4-10.&#160;non_blocking.py“>Example&#160;4-10</a> and then load multiple instances of <em>http://localhost:8000</em> in a browser, you may still find that the requests are processed serially. This is not Twisted&#8217;s fault: some browsers, notably Chrome, serialize requests to the same resource. You can verify that the web server isn&#8217;t blocking by issuing several simultaneous requests through

cURL

or a quick Python script.</p> </div> </div> <div class=„sect1“ title=„More Practice and Next Steps“ readability=„32“> <h2 class=„title c1“ id=„I_sect14_id390474“>More Practice and Next Steps</h2> <p>This chapter introduced Twisted HTTP servers, from the lowest-level APIs up through

twisted.web.server

. We saw examples of serving static and dynamic content, handling GET and POST requests, and how to keep our servers responsive with asynchronous responses using

Deferred

s.</p> <p>The <a class=„ulink“ href=„http://bit.ly/XSAVlP“ target=„_top“>Twisted Web HOWTO index</a> has several in-depth tutorials related to HTTP servers, including on deployment and templating. <a class=„ulink“ href=„http://bit.ly/XSAYhm“ target=„_top“>This page</a> is an excellent series of short, self-contained examples of Twisted Web concepts.</p> <p>The <a class=„ulink“ href=„http://bit.ly/XSAZ4Z“ target=„_top“>Twisted Web examples directory</a> has a variety of server examples, including examples for proxies, an XML-RPC server, and rendering the output of a server process.</p> <p>Twisted is not a &#8220;web framework&#8221; like Django, web.py, or Flask. However, one of its many roles is as a framework for building frameworks! An example of this is the <a class=„ulink“ href=„http://bit.ly/XSAZBW“ target=„_top“>Klein micro-web framework</a>, which you can also browse and download at that GitHub page.</p> </div> </section></div> <section class=„t-bottom-cta bottom-cta bottom-cta-book free-chapter“ readability=„-23“><h2>With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.</h2> </section></div> </section></section><noscript> </noscript> </html>

Cookies helfen bei der Bereitstellung von Inhalten. Diese Website verwendet Cookies. Mit der Nutzung der Website erklären Sie sich damit einverstanden, dass Cookies auf Ihrem Computer gespeichert werden. Außerdem bestätigen Sie, dass Sie unsere Datenschutzerklärung gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information