The asynchat module is an extension to asyncore. It provides additional support for line-oriented protocols. It also provides improved buffering support, via the push methods and the "producer" mechanism.
Example 7-12 implements a very minimal HTTP responder. It simply returns an HTML document containing information from an HTTP request (the output appears in the browser window).
Example 7-12. Using the asynchat Module to Implement a Minimal HTTP Server
File: asynchat-example-1.py
import asyncore, asynchat
import os, socket, string
PORT = 8000
class HTTPChannel(asynchat.async_chat):
def _ _init_ _(self, server, sock, addr):
asynchat.async_chat._ _init_ _(self, sock)
self.set_terminator("
")
self.request = None
self.data = ""
self.shutdown = 0
def collect_incoming_data(self, data):
self.data = self.data + data
def found_terminator(self):
if not self.request:
# got the request line
self.request = string.split(self.data, None, 2)
if len(self.request) != 3:
self.shutdown = 1
else:
self.push("HTTP/1.0 200 OK
")
self.push("Content-type: text/html
")
self.push("
")
self.data = self.data + "
"
self.set_terminator("
") # look for end of headers
else:
# return payload.
self.push("
")
self.push(self.data)
self.push("
") self.close_when_done() class HTTPServer(asyncore.dispatcher): def _ _init_ _(self, port): self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.bind(("", port)) self.listen(5) def handle_accept(self): conn, addr = self.accept() HTTPChannel(self, conn, addr) # # try it out s = HTTPServer(PORT) print "serving at port", PORT, "..." asyncore.loop() GET / HTTP/1.1 Accept: */* Accept-Language: en, sv Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; Bruce/1.0) Host: localhost:8000 Connection: Keep-Alive
The producer interface allows you to "push" objects that are too large to store in memory. asyncore calls the producer's more method whenever it needs more data. To signal end of file, just return an empty string.
Example 7-13 implements a very simple file-based HTTP server, using a simple FileProducer class that reads data from a file, a few kilobytes at the time.
Example 7-13. Using the asynchat Module to Implement a Simple HTTP Server
File: asynchat-example-2.py
import asyncore, asynchat
import os, socket, string, sys
import StringIO, mimetools
ROOT = "."
PORT = 8000
class HTTPChannel(asynchat.async_chat):
def _ _init_ _(self, server, sock, addr):
asynchat.async_chat._ _init_ _(self, sock)
self.server = server
self.set_terminator("
")
self.header = None
self.data = ""
self.shutdown = 0
def collect_incoming_data(self, data):
self.data = self.data + data
if len(self.data) > 16384:
# limit the header size to prevent attacks
self.shutdown = 1
def found_terminator(self):
if not self.header:
# parse http header
fp = StringIO.StringIO(self.data)
request = string.split(fp.readline(), None, 2)
if len(request) != 3:
# badly formed request; just shut down
self.shutdown = 1
else:
# parse message header
self.header = mimetools.Message(fp)
self.set_terminator("
")
self.server.handle_request(
self, request[0], request[1], self.header
)
self.close_when_done()
self.data = ""
else:
pass # ignore body data, for now
def pushstatus(self, status, explanation="OK"):
self.push("HTTP/1.0 %d %s
" % (status, explanation))
class FileProducer:
# a producer that reads data from a file object
def _ _init_ _(self, file):
self.file = file
def more(self):
if self.file:
data = self.file.read(2048)
if data:
return data
self.file = None
return ""
class HTTPServer(asyncore.dispatcher):
def _ _init_ _(self, port=None, request=None):
if not port:
port = 80
self.port = port
if request:
self.handle_request = request # external request handler
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(("", port))
self.listen(5)
def handle_accept(self):
conn, addr = self.accept()
HTTPChannel(self, conn, addr)
def handle_request(self, channel, method, path, header):
try:
# this is not safe!
while path[:1] == "/":
path = path[1:]
filename = os.path.join(ROOT, path)
print path, "=>", filename
file = open(filename, "r")
except IOError:
channel.pushstatus(404, "Not found")
channel.push("Content-type: text/html
")
channel.push("
")
channel.push("
File not found. ") else: channel.pushstatus(200, "OK") channel.push("Content-type: text/html ") channel.push(" ") channel.push_with_producer(FileProducer(file)) # # try it out s = HTTPServer(PORT) print "serving at port", PORT asyncore.loop() serving at port 8000 log: adding channel log: adding channel samples/sample.htm => .samples/sample.htm log: closing channel 96:
Core Modules
More Standard Modules
Threads and Processes
Data Representation
File Formats
Mail and News Message Processing
Network Protocols
Internationalization
Multimedia Modules
Data Storage
Tools and Utilities
Platform-Specific Modules
Implementation Support Modules
Other Modules