mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -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>'
|
__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):
|
class MaxSizeExceeded(Exception):
|
||||||
|
|
||||||
def __init__(self, prefix, size, limit):
|
def __init__(self, prefix, size, limit):
|
||||||
|
@ -16,7 +16,7 @@ from operator import itemgetter
|
|||||||
from calibre import as_unicode
|
from calibre import as_unicode
|
||||||
from calibre.constants import __version__
|
from calibre.constants import __version__
|
||||||
from calibre.srv.errors import (
|
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.respond import finalize_output, generate_static_output
|
||||||
from calibre.srv.utils import MultiDict, http_date, socket_errors_to_ignore
|
from calibre.srv.utils import MultiDict, http_date, socket_errors_to_ignore
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ def read_headers(readline): # {{{
|
|||||||
return hdict
|
return hdict
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def http_communicate(conn):
|
def http_communicate(handle_request, conn):
|
||||||
' Represents interaction with a http client over a single, persistent connection '
|
' Represents interaction with a http client over a single, persistent connection '
|
||||||
request_seen = False
|
request_seen = False
|
||||||
def repr_for_pair(pair):
|
def repr_for_pair(pair):
|
||||||
@ -163,7 +163,7 @@ def http_communicate(conn):
|
|||||||
# the HTTPPair constructor, the error doesn't
|
# the HTTPPair constructor, the error doesn't
|
||||||
# get written to the previous request.
|
# get written to the previous request.
|
||||||
pair = None
|
pair = None
|
||||||
pair = conn.server_loop.http_handler(conn)
|
pair = HTTPPair(handle_request, conn)
|
||||||
|
|
||||||
# This order of operations should guarantee correct pipelining.
|
# This order of operations should guarantee correct pipelining.
|
||||||
pair.parse_request()
|
pair.parse_request()
|
||||||
@ -186,8 +186,6 @@ def http_communicate(conn):
|
|||||||
# Don't bother writing the 408 if the response
|
# Don't bother writing the 408 if the response
|
||||||
# has already started being written.
|
# has already started being written.
|
||||||
simple_response(pair, httplib.REQUEST_TIMEOUT)
|
simple_response(pair, httplib.REQUEST_TIMEOUT)
|
||||||
except NonHTTPConnRequest:
|
|
||||||
raise
|
|
||||||
except socket.error:
|
except socket.error:
|
||||||
# This socket is broken. Log the error and close connection
|
# This socket is broken. Log the error and close connection
|
||||||
conn.server_loop.log.exception(
|
conn.server_loop.log.exception(
|
||||||
@ -601,4 +599,4 @@ class HTTPPair(object):
|
|||||||
|
|
||||||
|
|
||||||
def create_http_handler(handle_request):
|
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 io import DEFAULT_BUFFER_SIZE, BytesIO
|
||||||
|
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
from calibre.srv.errors import NonHTTPConnRequest, MaxSizeExceeded
|
from calibre.srv.errors import MaxSizeExceeded
|
||||||
from calibre.srv.http import http_communicate
|
|
||||||
from calibre.srv.opts import Options
|
from calibre.srv.opts import Options
|
||||||
from calibre.srv.utils import socket_errors_to_ignore, socket_error_eintr, socket_errors_nonblocking, Corked
|
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
|
from calibre.utils.socket_inheritance import set_socket_inherit
|
||||||
@ -384,9 +383,6 @@ class Connection(object): # {{{
|
|||||||
self.socket_file = SocketFile(socket)
|
self.socket_file = SocketFile(socket)
|
||||||
self.closed = False
|
self.closed = False
|
||||||
|
|
||||||
def nonhttp_communicate(self, data):
|
|
||||||
self.server_loop.nonhttp_handler(self, data)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close the socket underlying this connection."""
|
"""Close the socket underlying this connection."""
|
||||||
if self.closed:
|
if self.closed:
|
||||||
@ -425,10 +421,7 @@ class WorkerThread(Thread): # {{{
|
|||||||
if conn is None:
|
if conn is None:
|
||||||
return # Clean exit
|
return # Clean exit
|
||||||
with conn, self:
|
with conn, self:
|
||||||
try:
|
self.server_loop.req_resp_handler(conn)
|
||||||
http_communicate(conn)
|
|
||||||
except NonHTTPConnRequest as e:
|
|
||||||
conn.nonhttp_communicate(e.data)
|
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
self.server_loop.stop()
|
self.server_loop.stop()
|
||||||
except socket.error:
|
except socket.error:
|
||||||
@ -547,23 +540,15 @@ class ServerLoop(object):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
req_resp_handler,
|
||||||
bind_address=('localhost', 8080),
|
bind_address=('localhost', 8080),
|
||||||
http_handler=None,
|
|
||||||
nonhttp_handler=None,
|
|
||||||
opts=None,
|
opts=None,
|
||||||
# A calibre logging object. If None a default log that logs to
|
# A calibre logging object. If None a default log that logs to
|
||||||
# stdout is used
|
# stdout is used
|
||||||
log=None
|
log=None
|
||||||
):
|
):
|
||||||
self.opts = opts or Options()
|
self.opts = opts or Options()
|
||||||
if http_handler is None:
|
self.req_resp_handler = req_resp_handler
|
||||||
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.log = log or ThreadSafeLog(level=ThreadSafeLog.DEBUG)
|
self.log = log or ThreadSafeLog(level=ThreadSafeLog.DEBUG)
|
||||||
self.gso_cache, self.gso_lock = {}, Lock()
|
self.gso_cache, self.gso_lock = {}, Lock()
|
||||||
ba = bind_address
|
ba = bind_address
|
||||||
@ -811,7 +796,7 @@ class ServerLoop(object):
|
|||||||
s.close()
|
s.close()
|
||||||
return sock
|
return sock
|
||||||
|
|
||||||
def echo_handler(conn, data):
|
def echo_handler(conn):
|
||||||
keep_going = True
|
keep_going = True
|
||||||
while keep_going:
|
while keep_going:
|
||||||
try:
|
try:
|
||||||
@ -826,7 +811,7 @@ def echo_handler(conn, data):
|
|||||||
conn.socket_file.flush()
|
conn.socket_file.flush()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
s = ServerLoop(nonhttp_handler=echo_handler)
|
s = ServerLoop(echo_handler)
|
||||||
with HandleInterrupt(s.tick_once):
|
with HandleInterrupt(s.tick_once):
|
||||||
try:
|
try:
|
||||||
s.serve_forever()
|
s.serve_forever()
|
||||||
|
@ -39,8 +39,9 @@ class TestServer(Thread):
|
|||||||
from calibre.srv.http import create_http_handler
|
from calibre.srv.http import create_http_handler
|
||||||
kwargs['shutdown_timeout'] = kwargs.get('shutdown_timeout', 0.1)
|
kwargs['shutdown_timeout'] = kwargs.get('shutdown_timeout', 0.1)
|
||||||
self.loop = ServerLoop(
|
self.loop = ServerLoop(
|
||||||
|
create_http_handler(handler),
|
||||||
opts=Options(**kwargs),
|
opts=Options(**kwargs),
|
||||||
bind_address=('localhost', 0), http_handler=create_http_handler(handler),
|
bind_address=('localhost', 0),
|
||||||
log=TestLog(level=ThreadSafeLog.WARN),
|
log=TestLog(level=ThreadSafeLog.WARN),
|
||||||
)
|
)
|
||||||
self.log = self.loop.log
|
self.log = self.loop.log
|
||||||
@ -68,4 +69,4 @@ class TestServer(Thread):
|
|||||||
|
|
||||||
def change_handler(self, handler):
|
def change_handler(self, handler):
|
||||||
from calibre.srv.http import create_http_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