mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Decouple server loop from http completely
This commit is contained in:
parent
28aee5acbb
commit
ec3692fec4
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user