1 <h1>Lesson
2 the dynamic web
</h1>
4 <li>https://docs.python.org/
3</li>
5 <li>https://docs.python.org/
3/library/http.html
</li>
6 <li>https://github.com/python/cpython/blob/
3.12/Lib/http/server.py
</li>
7 <li>https://docs.python.org/
3/tutorial
</li>
8 <li>https://docs.python.org/
3/tutorial/inputoutput.html#formatted-string-literals
</li>
9 <li>https://developer.mozilla.org/en-US/docs/Glossary/Favicon
</li>
12 <p>In the mid to late
1990s the web started becoming dynamic. Web servers might return different content to different users who requested the same URL. A common example of this was a hit counter which would increment each time any user visited the website, or a guest book to allow users to leave messages.
</p>
13 <h2>Learning objective
</h2>
14 <p>The goal is to build a simple dynamic web server.
<br />
15 - Understand how the HTTP method and path map to a Python function call.
<br />
16 - Build a web server that returns dynamically generated content.
</p>
18 <h3>Build a simple HTTP server in Python
</h3>
19 <p>Create a file,
<code>ex2.py
</code> and paste the following into it:
</p>
20 <pre><code>import http.server
23 listen_address = ('localhost',
8000)
24 request_handler = http.server.SimpleHTTPRequestHandler
25 server = http.server.HTTPServer(listen_address, request_handler)
26 server.serve_forever()
28 if __name__ == '__main__':
31 <p>Run it with
<code>python3 ex2.py
</code>. It should work exactly the same as
<code>python3 -mhttp.server
</code> from the previous exercise.
</p>
32 <h3>Return HTML from a function instead of a file
</h3>
33 <pre><code>import http.server
36 listen_address = ('localhost',
8000)
37 request_handler = MyRequestHandler
38 server = http.server.HTTPServer(listen_address, request_handler)
39 server.serve_forever()
41 class MyRequestHandler(http.server.BaseHTTPRequestHandler):
42 def write(self, text):
43 self.wfile.write(text.encode('utf-
8'))
46 self.send_response(
200)
47 self.send_header('Content-type', 'text/html')
49 self.write('
<html
>')
50 self.write('
<head
><title
>My web server!
</title
></head
>')
51 self.write('
<body
>Hi there!
</body
>')
52 self.write('
</html
>')
54 if __name__ == '__main__':
57 <h3>Make the HTML dynamic!
</h3>
58 <p>Add
<code>COUNTER =
42</code> to the top of the file.
</p>
59 <p>Then modify your
<code>GET
</code> handler to return some dynamic HTML! Something like this...
</p>
60 <pre><code>global COUNTER
62 self.write(f'We have had
<b
>{COUNTER}
</b
> visitors today')
64 <p>You can try adding other information to the response too:
</p>
65 <pre><code>self.write('You requested: ' + self.path + '
<br
>')
66 self.write('You are using this client: ' + self.headers.get('user-agent') + '
<br
>')
68 <h3>A few things to note
</h3>
69 <p>Remember doing raw HTTP requests in the previous exercise? With the code above if a client does
<code>GET /file.txt
</code> then Python's
<code>http.server
</code> library parses the HTTP request and does something like the following:
<br />
70 - Creates a new instance of the
<code>MyRequestHandler
</code> class.
<br />
71 - Sets the HTTP path as:
<code>self.path = '/file.txt'
</code> for this new instance.
<br />
72 - Calls the
<code>do_GET()
</code> function on this new instance, because the HTTP method was
<code>GET
</code>.
</p>
73 <h3>Fix the double-counting bug
</h3>
74 <p>Notice that if you press
<code>ctrl-shift-R
</code> to reload your counter is going up by two at a time? If you look at the log you can see this is because Firefox is requesting
<code>/favicon.ico
</code>. This is the little icon next to the URL in the address bar. Our site isn't fancy enough for this, so we should modify
<code>do_GET()
</code> to return a
<code>404 not found
</code> for these requests.
</p>
75 <p>Something like this:
</p>
76 <pre><code>if not self.path.endswith('.html'):
77 self.send_response(
404)
79 self.write('File not found')
82 <p>Now try visiting some URL that doesn't end with
<code>.html
</code> and you'll see your 'not found' message.
</p>