mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-26 10:00:37 +00:00
bbf6d9b026
Bugfix for feeds - removed categories related and up - load new books now working - category random now working login page is free of non accessible elements boolean custom column is vivible in UI books with only with certain languages can be shown book shelfs can be deleted from UI Anonymous user view is more resticted Added browse of series in sidebar Dependencys in vendor folder are updated to newer versions (licencs files are now present) Bugfix editing Authors names Made upload on windows working
285 lines
11 KiB
Python
285 lines
11 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Copyright 2009 Facebook
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""A non-blocking, single-threaded HTTP server.
|
|
|
|
Typical applications have little direct interaction with the `HTTPServer`
|
|
class except to start a server at the beginning of the process
|
|
(and even that is often done indirectly via `tornado.web.Application.listen`).
|
|
|
|
.. versionchanged:: 4.0
|
|
|
|
The ``HTTPRequest`` class that used to live in this module has been moved
|
|
to `tornado.httputil.HTTPServerRequest`. The old name remains as an alias.
|
|
"""
|
|
|
|
from __future__ import absolute_import, division, print_function, with_statement
|
|
|
|
import socket
|
|
|
|
from tornado.escape import native_str
|
|
from tornado.http1connection import HTTP1ServerConnection, HTTP1ConnectionParameters
|
|
from tornado import gen
|
|
from tornado import httputil
|
|
from tornado import iostream
|
|
from tornado import netutil
|
|
from tornado.tcpserver import TCPServer
|
|
|
|
|
|
class HTTPServer(TCPServer, httputil.HTTPServerConnectionDelegate):
|
|
r"""A non-blocking, single-threaded HTTP server.
|
|
|
|
A server is defined by a subclass of `.HTTPServerConnectionDelegate`,
|
|
or, for backwards compatibility, a callback that takes an
|
|
`.HTTPServerRequest` as an argument. The delegate is usually a
|
|
`tornado.web.Application`.
|
|
|
|
`HTTPServer` supports keep-alive connections by default
|
|
(automatically for HTTP/1.1, or for HTTP/1.0 when the client
|
|
requests ``Connection: keep-alive``).
|
|
|
|
If ``xheaders`` is ``True``, we support the
|
|
``X-Real-Ip``/``X-Forwarded-For`` and
|
|
``X-Scheme``/``X-Forwarded-Proto`` headers, which override the
|
|
remote IP and URI scheme/protocol for all requests. These headers
|
|
are useful when running Tornado behind a reverse proxy or load
|
|
balancer. The ``protocol`` argument can also be set to ``https``
|
|
if Tornado is run behind an SSL-decoding proxy that does not set one of
|
|
the supported ``xheaders``.
|
|
|
|
To make this server serve SSL traffic, send the ``ssl_options`` dictionary
|
|
argument with the arguments required for the `ssl.wrap_socket` method,
|
|
including ``certfile`` and ``keyfile``. (In Python 3.2+ you can pass
|
|
an `ssl.SSLContext` object instead of a dict)::
|
|
|
|
HTTPServer(applicaton, ssl_options={
|
|
"certfile": os.path.join(data_dir, "mydomain.crt"),
|
|
"keyfile": os.path.join(data_dir, "mydomain.key"),
|
|
})
|
|
|
|
`HTTPServer` initialization follows one of three patterns (the
|
|
initialization methods are defined on `tornado.tcpserver.TCPServer`):
|
|
|
|
1. `~tornado.tcpserver.TCPServer.listen`: simple single-process::
|
|
|
|
server = HTTPServer(app)
|
|
server.listen(8888)
|
|
IOLoop.instance().start()
|
|
|
|
In many cases, `tornado.web.Application.listen` can be used to avoid
|
|
the need to explicitly create the `HTTPServer`.
|
|
|
|
2. `~tornado.tcpserver.TCPServer.bind`/`~tornado.tcpserver.TCPServer.start`:
|
|
simple multi-process::
|
|
|
|
server = HTTPServer(app)
|
|
server.bind(8888)
|
|
server.start(0) # Forks multiple sub-processes
|
|
IOLoop.instance().start()
|
|
|
|
When using this interface, an `.IOLoop` must *not* be passed
|
|
to the `HTTPServer` constructor. `~.TCPServer.start` will always start
|
|
the server on the default singleton `.IOLoop`.
|
|
|
|
3. `~tornado.tcpserver.TCPServer.add_sockets`: advanced multi-process::
|
|
|
|
sockets = tornado.netutil.bind_sockets(8888)
|
|
tornado.process.fork_processes(0)
|
|
server = HTTPServer(app)
|
|
server.add_sockets(sockets)
|
|
IOLoop.instance().start()
|
|
|
|
The `~.TCPServer.add_sockets` interface is more complicated,
|
|
but it can be used with `tornado.process.fork_processes` to
|
|
give you more flexibility in when the fork happens.
|
|
`~.TCPServer.add_sockets` can also be used in single-process
|
|
servers if you want to create your listening sockets in some
|
|
way other than `tornado.netutil.bind_sockets`.
|
|
|
|
.. versionchanged:: 4.0
|
|
Added ``decompress_request``, ``chunk_size``, ``max_header_size``,
|
|
``idle_connection_timeout``, ``body_timeout``, ``max_body_size``
|
|
arguments. Added support for `.HTTPServerConnectionDelegate`
|
|
instances as ``request_callback``.
|
|
|
|
.. versionchanged:: 4.1
|
|
`.HTTPServerConnectionDelegate.start_request` is now called with
|
|
two arguments ``(server_conn, request_conn)`` (in accordance with the
|
|
documentation) instead of one ``(request_conn)``.
|
|
"""
|
|
def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
|
|
xheaders=False, ssl_options=None, protocol=None,
|
|
decompress_request=False,
|
|
chunk_size=None, max_header_size=None,
|
|
idle_connection_timeout=None, body_timeout=None,
|
|
max_body_size=None, max_buffer_size=None):
|
|
self.request_callback = request_callback
|
|
self.no_keep_alive = no_keep_alive
|
|
self.xheaders = xheaders
|
|
self.protocol = protocol
|
|
self.conn_params = HTTP1ConnectionParameters(
|
|
decompress=decompress_request,
|
|
chunk_size=chunk_size,
|
|
max_header_size=max_header_size,
|
|
header_timeout=idle_connection_timeout or 3600,
|
|
max_body_size=max_body_size,
|
|
body_timeout=body_timeout)
|
|
TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
|
|
max_buffer_size=max_buffer_size,
|
|
read_chunk_size=chunk_size)
|
|
self._connections = set()
|
|
|
|
@gen.coroutine
|
|
def close_all_connections(self):
|
|
while self._connections:
|
|
# Peek at an arbitrary element of the set
|
|
conn = next(iter(self._connections))
|
|
yield conn.close()
|
|
|
|
def handle_stream(self, stream, address):
|
|
context = _HTTPRequestContext(stream, address,
|
|
self.protocol)
|
|
conn = HTTP1ServerConnection(
|
|
stream, self.conn_params, context)
|
|
self._connections.add(conn)
|
|
conn.start_serving(self)
|
|
|
|
def start_request(self, server_conn, request_conn):
|
|
return _ServerRequestAdapter(self, server_conn, request_conn)
|
|
|
|
def on_close(self, server_conn):
|
|
self._connections.remove(server_conn)
|
|
|
|
|
|
class _HTTPRequestContext(object):
|
|
def __init__(self, stream, address, protocol):
|
|
self.address = address
|
|
self.protocol = protocol
|
|
# Save the socket's address family now so we know how to
|
|
# interpret self.address even after the stream is closed
|
|
# and its socket attribute replaced with None.
|
|
if stream.socket is not None:
|
|
self.address_family = stream.socket.family
|
|
else:
|
|
self.address_family = None
|
|
# In HTTPServerRequest we want an IP, not a full socket address.
|
|
if (self.address_family in (socket.AF_INET, socket.AF_INET6) and
|
|
address is not None):
|
|
self.remote_ip = address[0]
|
|
else:
|
|
# Unix (or other) socket; fake the remote address.
|
|
self.remote_ip = '0.0.0.0'
|
|
if protocol:
|
|
self.protocol = protocol
|
|
elif isinstance(stream, iostream.SSLIOStream):
|
|
self.protocol = "https"
|
|
else:
|
|
self.protocol = "http"
|
|
self._orig_remote_ip = self.remote_ip
|
|
self._orig_protocol = self.protocol
|
|
|
|
def __str__(self):
|
|
if self.address_family in (socket.AF_INET, socket.AF_INET6):
|
|
return self.remote_ip
|
|
elif isinstance(self.address, bytes):
|
|
# Python 3 with the -bb option warns about str(bytes),
|
|
# so convert it explicitly.
|
|
# Unix socket addresses are str on mac but bytes on linux.
|
|
return native_str(self.address)
|
|
else:
|
|
return str(self.address)
|
|
|
|
def _apply_xheaders(self, headers):
|
|
"""Rewrite the ``remote_ip`` and ``protocol`` fields."""
|
|
# Squid uses X-Forwarded-For, others use X-Real-Ip
|
|
ip = headers.get("X-Forwarded-For", self.remote_ip)
|
|
ip = ip.split(',')[-1].strip()
|
|
ip = headers.get("X-Real-Ip", ip)
|
|
if netutil.is_valid_ip(ip):
|
|
self.remote_ip = ip
|
|
# AWS uses X-Forwarded-Proto
|
|
proto_header = headers.get(
|
|
"X-Scheme", headers.get("X-Forwarded-Proto",
|
|
self.protocol))
|
|
if proto_header in ("http", "https"):
|
|
self.protocol = proto_header
|
|
|
|
def _unapply_xheaders(self):
|
|
"""Undo changes from `_apply_xheaders`.
|
|
|
|
Xheaders are per-request so they should not leak to the next
|
|
request on the same connection.
|
|
"""
|
|
self.remote_ip = self._orig_remote_ip
|
|
self.protocol = self._orig_protocol
|
|
|
|
|
|
class _ServerRequestAdapter(httputil.HTTPMessageDelegate):
|
|
"""Adapts the `HTTPMessageDelegate` interface to the interface expected
|
|
by our clients.
|
|
"""
|
|
def __init__(self, server, server_conn, request_conn):
|
|
self.server = server
|
|
self.connection = request_conn
|
|
self.request = None
|
|
if isinstance(server.request_callback,
|
|
httputil.HTTPServerConnectionDelegate):
|
|
self.delegate = server.request_callback.start_request(
|
|
server_conn, request_conn)
|
|
self._chunks = None
|
|
else:
|
|
self.delegate = None
|
|
self._chunks = []
|
|
|
|
def headers_received(self, start_line, headers):
|
|
if self.server.xheaders:
|
|
self.connection.context._apply_xheaders(headers)
|
|
if self.delegate is None:
|
|
self.request = httputil.HTTPServerRequest(
|
|
connection=self.connection, start_line=start_line,
|
|
headers=headers)
|
|
else:
|
|
return self.delegate.headers_received(start_line, headers)
|
|
|
|
def data_received(self, chunk):
|
|
if self.delegate is None:
|
|
self._chunks.append(chunk)
|
|
else:
|
|
return self.delegate.data_received(chunk)
|
|
|
|
def finish(self):
|
|
if self.delegate is None:
|
|
self.request.body = b''.join(self._chunks)
|
|
self.request._parse_body()
|
|
self.server.request_callback(self.request)
|
|
else:
|
|
self.delegate.finish()
|
|
self._cleanup()
|
|
|
|
def on_connection_close(self):
|
|
if self.delegate is None:
|
|
self._chunks = None
|
|
else:
|
|
self.delegate.on_connection_close()
|
|
self._cleanup()
|
|
|
|
def _cleanup(self):
|
|
if self.server.xheaders:
|
|
self.connection.context._unapply_xheaders()
|
|
|
|
|
|
HTTPRequest = httputil.HTTPServerRequest
|