Decouple server loop from http completely

This commit is contained in:
Kovid Goyal 2015-05-23 09:16:01 +05:30
parent 28aee5acbb
commit ec3692fec4
4 changed files with 13 additions and 35 deletions

View File

@ -7,12 +7,6 @@ __license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
class NonHTTPConnRequest(Exception):
def __init__(self, data=None):
Exception.__init__(self, '')
self.data = data
class MaxSizeExceeded(Exception):
def __init__(self, prefix, size, limit):

View File

@ -16,7 +16,7 @@ from operator import itemgetter
from calibre import as_unicode
from calibre.constants import __version__
from calibre.srv.errors import (
MaxSizeExceeded, NonHTTPConnRequest, HTTP404, IfNoneMatch, BadChunkedInput, RangeNotSatisfiable)
MaxSizeExceeded, HTTP404, IfNoneMatch, BadChunkedInput, RangeNotSatisfiable)
from calibre.srv.respond import finalize_output, generate_static_output
from calibre.srv.utils import MultiDict, http_date, socket_errors_to_ignore
@ -143,7 +143,7 @@ def read_headers(readline): # {{{
return hdict
# }}}
def http_communicate(conn):
def http_communicate(handle_request, conn):
' Represents interaction with a http client over a single, persistent connection '
request_seen = False
def repr_for_pair(pair):
@ -163,7 +163,7 @@ def http_communicate(conn):
# the HTTPPair constructor, the error doesn't
# get written to the previous request.
pair = None
pair = conn.server_loop.http_handler(conn)
pair = HTTPPair(handle_request, conn)
# This order of operations should guarantee correct pipelining.
pair.parse_request()
@ -186,8 +186,6 @@ def http_communicate(conn):
# Don't bother writing the 408 if the response
# has already started being written.
simple_response(pair, httplib.REQUEST_TIMEOUT)
except NonHTTPConnRequest:
raise
except socket.error:
# This socket is broken. Log the error and close connection
conn.server_loop.log.exception(
@ -601,4 +599,4 @@ class HTTPPair(object):
def create_http_handler(handle_request):
return partial(HTTPPair, handle_request)
return partial(http_communicate, handle_request)

View File

@ -13,8 +13,7 @@ from threading import Thread, current_thread, Lock
from io import DEFAULT_BUFFER_SIZE, BytesIO
from calibre.constants import iswindows
from calibre.srv.errors import NonHTTPConnRequest, MaxSizeExceeded
from calibre.srv.http import http_communicate
from calibre.srv.errors import MaxSizeExceeded
from calibre.srv.opts import Options
from calibre.srv.utils import socket_errors_to_ignore, socket_error_eintr, socket_errors_nonblocking, Corked
from calibre.utils.socket_inheritance import set_socket_inherit
@ -384,9 +383,6 @@ class Connection(object): # {{{
self.socket_file = SocketFile(socket)
self.closed = False
def nonhttp_communicate(self, data):
self.server_loop.nonhttp_handler(self, data)
def close(self):
"""Close the socket underlying this connection."""
if self.closed:
@ -425,10 +421,7 @@ class WorkerThread(Thread): # {{{
if conn is None:
return # Clean exit
with conn, self:
try:
http_communicate(conn)
except NonHTTPConnRequest as e:
conn.nonhttp_communicate(e.data)
self.server_loop.req_resp_handler(conn)
except (KeyboardInterrupt, SystemExit):
self.server_loop.stop()
except socket.error:
@ -547,23 +540,15 @@ class ServerLoop(object):
def __init__(
self,
req_resp_handler,
bind_address=('localhost', 8080),
http_handler=None,
nonhttp_handler=None,
opts=None,
# A calibre logging object. If None a default log that logs to
# stdout is used
log=None
):
self.opts = opts or Options()
if http_handler is None:
def aborth(*args, **kwargs):
raise NonHTTPConnRequest()
http_handler = aborth
self.http_handler = http_handler
self.nonhttp_handler = nonhttp_handler
if http_handler is None and nonhttp_handler is None:
raise ValueError('You must specify at least one protocol handler')
self.req_resp_handler = req_resp_handler
self.log = log or ThreadSafeLog(level=ThreadSafeLog.DEBUG)
self.gso_cache, self.gso_lock = {}, Lock()
ba = bind_address
@ -811,7 +796,7 @@ class ServerLoop(object):
s.close()
return sock
def echo_handler(conn, data):
def echo_handler(conn):
keep_going = True
while keep_going:
try:
@ -826,7 +811,7 @@ def echo_handler(conn, data):
conn.socket_file.flush()
if __name__ == '__main__':
s = ServerLoop(nonhttp_handler=echo_handler)
s = ServerLoop(echo_handler)
with HandleInterrupt(s.tick_once):
try:
s.serve_forever()

View File

@ -39,8 +39,9 @@ class TestServer(Thread):
from calibre.srv.http import create_http_handler
kwargs['shutdown_timeout'] = kwargs.get('shutdown_timeout', 0.1)
self.loop = ServerLoop(
create_http_handler(handler),
opts=Options(**kwargs),
bind_address=('localhost', 0), http_handler=create_http_handler(handler),
bind_address=('localhost', 0),
log=TestLog(level=ThreadSafeLog.WARN),
)
self.log = self.loop.log
@ -68,4 +69,4 @@ class TestServer(Thread):
def change_handler(self, handler):
from calibre.srv.http import create_http_handler
self.loop.http_handler = create_http_handler(handler)
self.loop.req_resp_handler = create_http_handler(handler)