Make the wsgi server work, and it works really well, keepalives, http 1.1, yay

This commit is contained in:
donovan
2008-04-18 13:53:35 -07:00
parent 936a79f509
commit 7acf7918b3
2 changed files with 83 additions and 24 deletions

View File

@@ -156,7 +156,7 @@ class GreenSocket(object):
self.sendcount = 0 self.sendcount = 0
self.recvcount = 0 self.recvcount = 0
self.recvbuffer = '' self.recvbuffer = ''
self._closed = False self.closed = False
def accept(self): def accept(self):
fd = self.fd fd = self.fd
@@ -173,9 +173,9 @@ class GreenSocket(object):
return fn(*args, **kw) return fn(*args, **kw)
def close(self, *args, **kw): def close(self, *args, **kw):
if self._closed: if self.closed:
return return
self._closed = True self.closed = True
fn = self.close = self.fd.close fn = self.close = self.fd.close
try: try:
res = fn(*args, **kw) res = fn(*args, **kw)
@@ -272,15 +272,19 @@ class GreenFile(object):
def __init__(self, fd): def __init__(self, fd):
self.sock = fd self.sock = fd
self.closed = False
def close(self): def close(self):
self.sock.close() self.sock.close()
self.closed = True
def fileno(self): def fileno(self):
return self.sock.fileno() return self.sock.fileno()
# TODO next # TODO next
# TODO flush
def flush(self):
pass
def write(self, data): def write(self, data):
return self.sock.sendall(data) return self.sock.sendall(data)

View File

@@ -26,6 +26,7 @@ THE SOFTWARE.
import errno import errno
import sys import sys
import time import time
import traceback
import urllib import urllib
import socket import socket
import cStringIO import cStringIO
@@ -35,8 +36,22 @@ import BaseHTTPServer
from eventlet import api from eventlet import api
from eventlet.httpdate import format_date_time from eventlet.httpdate import format_date_time
class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
class Input(object):
def __init__(self, rfile, content_length):
self.rfile = rfile
self.content_length = content_length
def read(self, length=None):
if length is None:
length = self.content_length
if length is None:
return ''
return self.rfile.read(length)
class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def log_message(self, format, *args): def log_message(self, format, *args):
self.server.log_message("%s - - [%s] %s" % ( self.server.log_message("%s - - [%s] %s" % (
self.address_string(), self.address_string(),
@@ -71,8 +86,10 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
wfile = self.wfile wfile = self.wfile
num_blocks = None num_blocks = None
use_chunked = False
def write(data, _write=wfile.write): def write(data, _write=wfile.write):
towrite = []
if not headers_set: if not headers_set:
raise AssertionError("write() before start_response()") raise AssertionError("write() before start_response()")
elif not headers_sent: elif not headers_sent:
@@ -80,16 +97,24 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
headers_sent.append(1) headers_sent.append(1)
for k, v in response_headers: for k, v in response_headers:
header_dict[k.lower()] = k header_dict[k.lower()] = k
_write('HTTP/1.0 %s\r\n' % status) towrite.append('%s %s\r\n' % (self.protocol_version, status))
# send Date header? # send Date header?
if 'date' not in header_dict: if 'date' not in header_dict:
_write('Date: %s\r\n' % (format_date_time(time.time()),)) towrite.append('Date: %s\r\n' % (format_date_time(time.time()),))
if 'content-length' not in header_dict and num_blocks == 1: if num_blocks == 1:
_write('Content-Length: %s\r\n' % (len(data),)) towrite.append('Content-Length: %s\r\n' % (len(data),))
elif use_chunked:
towrite.append('Transfer-Encoding: chunked\r\n')
for header in response_headers: for header in response_headers:
_write('%s: %s\r\n' % header) towrite.append('%s: %s\r\n' % header)
_write('\r\n') towrite.append('\r\n')
_write(data)
if use_chunked:
## Write the chunked encoding
towrite.append("%x\r\n%s\r\n" % (len(data), data))
else:
towrite.append(data)
_write(''.join(towrite))
def start_request(status, response_headers, exc_info=None): def start_request(status, response_headers, exc_info=None):
if exc_info: if exc_info:
@@ -106,18 +131,48 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
headers_set[:] = [status, response_headers] headers_set[:] = [status, response_headers]
return write return write
result = self.server.app(self.environ, start_request) try:
result = self.server.app(self.environ, start_request)
except Exception, e:
exc = traceback.format_exc()
if not headers_sent:
start_request("500 Internal Server Error", [('Content-type', 'text/plain')])
write(exc)
return
try: try:
num_blocks = len(result) num_blocks = len(result)
except (TypeError, AttributeError, NotImplementedError): except (TypeError, AttributeError, NotImplementedError):
pass if self.protocol_version == 'HTTP/1.1':
use_chunked = True
try: try:
for data in result: towrite = []
if data: try:
write(data) for data in result:
if data:
towrite.append(data)
if reduce(
lambda x, y: x + y,
map(
lambda x: len(x), towrite)) > 4096:
write(''.join(towrite))
del towrite[:]
except Exception, e:
exc = traceback.format_exc()
if not headers_sent:
start_request("500 Internal Server Error", [('Content-type', 'text/plain')])
write(exc)
return
if towrite:
write(''.join(towrite))
if use_chunked:
wfile.write('0\r\n\r\n')
if not headers_sent: if not headers_sent:
write('') write('')
except Exception, e:
traceback.print_exc()
finally: finally:
if hasattr(result, 'close'): if hasattr(result, 'close'):
result.close() result.close()
@@ -150,6 +205,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
env['REMOTE_ADDR'] = self.client_address[0] env['REMOTE_ADDR'] = self.client_address[0]
env['GATEWAY_INTERFACE'] = 'CGI/1.1' env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['wsgi.input'] = Input(self.rfile, length)
for h in self.headers.headers: for h in self.headers.headers:
k, v = h.split(':', 1) k, v = h.split(':', 1)
k = k.replace('-', '_').upper() k = k.replace('-', '_').upper()
@@ -165,10 +222,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
return env return env
def finish(self): def finish(self):
# Override SocketServer.StreamRequestHandler.finish because BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
# we only need to call close on the socket, not the makefile'd things self.connection.close()
self.request.close()
class Server(BaseHTTPServer.HTTPServer): class Server(BaseHTTPServer.HTTPServer):
@@ -186,7 +241,6 @@ class Server(BaseHTTPServer.HTTPServer):
def get_environ(self): def get_environ(self):
socket = self.socket socket = self.socket
d = { d = {
'wsgi.input': socket,
'wsgi.errors': sys.stderr, 'wsgi.errors': sys.stderr,
'wsgi.version': (1, 0), 'wsgi.version': (1, 0),
'wsgi.multithread': True, 'wsgi.multithread': True,
@@ -200,6 +254,7 @@ class Server(BaseHTTPServer.HTTPServer):
def process_request(self, (socket, address)): def process_request(self, (socket, address)):
proto = HttpProtocol(socket, address, self) proto = HttpProtocol(socket, address, self)
proto.handle()
def log_message(self, message): def log_message(self, message):
self.log.write(message + '\n') self.log.write(message + '\n')