Commit 0838a0b4 authored by Denis Bilenko's avatar Denis Bilenko

rewrite http.HTTPServer using BaseServer and the new feature in the core

parent 353135cd
# Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details. # Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details.
import sys
import traceback
from gevent import core from gevent import core
from gevent.greenlet import Greenlet from gevent.baseserver import BaseServer
from gevent.event import Event
import _socket
class HTTPServer(object): __all__ = ['HTTPServer']
spawn = Greenlet.spawn # set to None to avoid spawning at all
backlog = 256
def __init__(self, handle=None, spawn='default', backlog=None): class HTTPServer(BaseServer):
if backlog is not None: """An HTTP server based on libevent-http.
self.backlog = backlog
self.listeners = []
self._stopped_event = Event()
self._no_connections_event = Event()
self._requests = {} # maps connection -> list of requests
self.http = core.http()
self.http.set_gencb(self._cb_request)
if handle is not None:
self.handle = handle
if spawn != 'default':
self.spawn = spawn
def start(self, listener, backlog=None): *handle* is called with one argument: an :class:`gevent.core.http_request` instance.
"""Start accepting connections""" """
fileno = getattr(listener, 'fileno', None)
if fileno is not None:
fd = fileno()
sock = listener
else:
sock = self.make_listener(listener, backlog=backlog)
fd = sock.fileno()
self.http.accept(fd)
self.listeners.append(sock)
self._stopped_event.clear()
if self._requests:
self._no_connections_event.clear()
else:
self._no_connections_event.set()
return sock
def make_listener(self, address, backlog=None): def __init__(self, listener, handle=None, backlog=None, spawn='default'):
if backlog is None: BaseServer.__init__(self, listener, handle=handle, backlog=backlog, spawn=spawn)
backlog = self.backlog
sock = _socket.socket()
try:
sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, sock.getsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR) | 1)
except _socket.error:
pass
sock.bind(address)
sock.listen(backlog)
sock.setblocking(False)
return sock
def stop(self, timeout=0):
"""Shutdown the server."""
for sock in self.listeners:
sock.close()
self.socket = []
#2. Set "keep-alive" connections to "close"
# TODO
#3a. set low timeout (min(1s, timeout or 1)) on events belonging to connection (to kill long-polling connections)
# TODO
#3. Wait until every connection is closed or timeout expires
if self._requests:
self._no_connections_event.wait(timeout=timeout)
#4. forcefull close all the connections
# TODO
#5. free http instance
self.http = None self.http = None
#6. notify event created in serve_forever()
self._stopped_event.set()
def handle(self, request):
request.send_reply(200, 'OK', 'It works!')
def _cb_connection_close(self, connection): @property
# make sure requests belonging to this connection cannot be accessed anymore def started(self):
# because they've been freed by libevent return self.http is not None
requests = self._requests.pop(connection._obj, [])
for request in requests:
request.detach()
if not self._requests:
self._no_connections_event.set()
def _cb_request_processed(self, greenlet): def _on_request(self, request):
request = greenlet._request
greenlet._request = None
if request:
if not greenlet.successful():
self.reply_error(request)
requests = self._requests.get(request.connection._obj)
if requests is not None:
requests.remove(request)
def _cb_request(self, request):
try:
spawn = self.spawn spawn = self.spawn
request.connection.set_closecb(self)
self._requests.setdefault(request.connection._obj, []).append(request)
if spawn is None: if spawn is None:
self.handle(request) self.handle(request)
else: else:
greenlet = spawn(self.handle, request) if self.full():
rawlink = getattr(greenlet, 'rawlink', None) request._default_response_code = 503
if rawlink is not None: else:
greenlet._request = request spawn(self.handle, request)
rawlink(self._cb_request_processed)
except:
traceback.print_exc()
try:
sys.stderr.write('Failed to handle request: %s\n\n' % (request, ))
except:
traceback.print_exc()
if request:
self.reply_error(request)
def reply_error(self, request): def start_accepting(self):
if request.response == (0, None): self.http = core.http(self._on_request)
request.send_reply(500, 'Internal Server Error', '<h1>Internal Server Error</h1>') self.http.accept(self.socket.fileno())
def serve_forever(self, listener, backlog=None, stop_timeout=0): def stop_accepting(self):
self.start(listener, backlog=backlog) self.http = None
try:
self._stopped_event.wait()
finally:
Greenlet.spawn(self.stop, timeout=stop_timeout).join()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment