Translation of UI (german and english)
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
This commit is contained in:
Vendored
+152
-114
@@ -20,9 +20,9 @@ WSGI is the Python standard for web servers, and allows for interoperability
|
||||
between Tornado and other Python web frameworks and servers. This module
|
||||
provides WSGI support in two ways:
|
||||
|
||||
* `WSGIApplication` is a version of `tornado.web.Application` that can run
|
||||
inside a WSGI server. This is useful for running a Tornado app on another
|
||||
HTTP server, such as Google App Engine. See the `WSGIApplication` class
|
||||
* `WSGIAdapter` converts a `tornado.web.Application` to the WSGI application
|
||||
interface. This is useful for running a Tornado app on another
|
||||
HTTP server, such as Google App Engine. See the `WSGIAdapter` class
|
||||
documentation for limitations that apply.
|
||||
* `WSGIContainer` lets you run other WSGI applications and frameworks on the
|
||||
Tornado HTTP server. For example, with this class you can mix Django
|
||||
@@ -32,25 +32,17 @@ provides WSGI support in two ways:
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
|
||||
import sys
|
||||
import time
|
||||
from io import BytesIO
|
||||
import tornado
|
||||
|
||||
from tornado.concurrent import Future
|
||||
from tornado import escape
|
||||
from tornado import httputil
|
||||
from tornado.log import access_log
|
||||
from tornado import web
|
||||
from tornado.escape import native_str, parse_qs_bytes
|
||||
from tornado.util import bytes_type, unicode_type
|
||||
from tornado.escape import native_str
|
||||
from tornado.util import unicode_type
|
||||
|
||||
try:
|
||||
from io import BytesIO # python 3
|
||||
except ImportError:
|
||||
from cStringIO import StringIO as BytesIO # python 2
|
||||
|
||||
try:
|
||||
import Cookie # py2
|
||||
except ImportError:
|
||||
import http.cookies as Cookie # py3
|
||||
|
||||
try:
|
||||
import urllib.parse as urllib_parse # py3
|
||||
@@ -63,7 +55,7 @@ except ImportError:
|
||||
# here to minimize the temptation to use them in non-wsgi contexts.
|
||||
if str is unicode_type:
|
||||
def to_wsgi_str(s):
|
||||
assert isinstance(s, bytes_type)
|
||||
assert isinstance(s, bytes)
|
||||
return s.decode('latin1')
|
||||
|
||||
def from_wsgi_str(s):
|
||||
@@ -71,7 +63,7 @@ if str is unicode_type:
|
||||
return s.encode('latin1')
|
||||
else:
|
||||
def to_wsgi_str(s):
|
||||
assert isinstance(s, bytes_type)
|
||||
assert isinstance(s, bytes)
|
||||
return s
|
||||
|
||||
def from_wsgi_str(s):
|
||||
@@ -82,11 +74,84 @@ else:
|
||||
class WSGIApplication(web.Application):
|
||||
"""A WSGI equivalent of `tornado.web.Application`.
|
||||
|
||||
`WSGIApplication` is very similar to `tornado.web.Application`,
|
||||
except no asynchronous methods are supported (since WSGI does not
|
||||
support non-blocking requests properly). If you call
|
||||
``self.flush()`` or other asynchronous methods in your request
|
||||
handlers running in a `WSGIApplication`, we throw an exception.
|
||||
.. deprecated:: 4.0
|
||||
|
||||
Use a regular `.Application` and wrap it in `WSGIAdapter` instead.
|
||||
"""
|
||||
def __call__(self, environ, start_response):
|
||||
return WSGIAdapter(self)(environ, start_response)
|
||||
|
||||
|
||||
# WSGI has no facilities for flow control, so just return an already-done
|
||||
# Future when the interface requires it.
|
||||
_dummy_future = Future()
|
||||
_dummy_future.set_result(None)
|
||||
|
||||
|
||||
class _WSGIConnection(httputil.HTTPConnection):
|
||||
def __init__(self, method, start_response, context):
|
||||
self.method = method
|
||||
self.start_response = start_response
|
||||
self.context = context
|
||||
self._write_buffer = []
|
||||
self._finished = False
|
||||
self._expected_content_remaining = None
|
||||
self._error = None
|
||||
|
||||
def set_close_callback(self, callback):
|
||||
# WSGI has no facility for detecting a closed connection mid-request,
|
||||
# so we can simply ignore the callback.
|
||||
pass
|
||||
|
||||
def write_headers(self, start_line, headers, chunk=None, callback=None):
|
||||
if self.method == 'HEAD':
|
||||
self._expected_content_remaining = 0
|
||||
elif 'Content-Length' in headers:
|
||||
self._expected_content_remaining = int(headers['Content-Length'])
|
||||
else:
|
||||
self._expected_content_remaining = None
|
||||
self.start_response(
|
||||
'%s %s' % (start_line.code, start_line.reason),
|
||||
[(native_str(k), native_str(v)) for (k, v) in headers.get_all()])
|
||||
if chunk is not None:
|
||||
self.write(chunk, callback)
|
||||
elif callback is not None:
|
||||
callback()
|
||||
return _dummy_future
|
||||
|
||||
def write(self, chunk, callback=None):
|
||||
if self._expected_content_remaining is not None:
|
||||
self._expected_content_remaining -= len(chunk)
|
||||
if self._expected_content_remaining < 0:
|
||||
self._error = httputil.HTTPOutputError(
|
||||
"Tried to write more data than Content-Length")
|
||||
raise self._error
|
||||
self._write_buffer.append(chunk)
|
||||
if callback is not None:
|
||||
callback()
|
||||
return _dummy_future
|
||||
|
||||
def finish(self):
|
||||
if (self._expected_content_remaining is not None and
|
||||
self._expected_content_remaining != 0):
|
||||
self._error = httputil.HTTPOutputError(
|
||||
"Tried to write %d bytes less than Content-Length" %
|
||||
self._expected_content_remaining)
|
||||
raise self._error
|
||||
self._finished = True
|
||||
|
||||
|
||||
class _WSGIRequestContext(object):
|
||||
def __init__(self, remote_ip, protocol):
|
||||
self.remote_ip = remote_ip
|
||||
self.protocol = protocol
|
||||
|
||||
def __str__(self):
|
||||
return self.remote_ip
|
||||
|
||||
|
||||
class WSGIAdapter(object):
|
||||
"""Converts a `tornado.web.Application` instance into a WSGI application.
|
||||
|
||||
Example usage::
|
||||
|
||||
@@ -99,115 +164,83 @@ class WSGIApplication(web.Application):
|
||||
self.write("Hello, world")
|
||||
|
||||
if __name__ == "__main__":
|
||||
application = tornado.wsgi.WSGIApplication([
|
||||
application = tornado.web.Application([
|
||||
(r"/", MainHandler),
|
||||
])
|
||||
server = wsgiref.simple_server.make_server('', 8888, application)
|
||||
wsgi_app = tornado.wsgi.WSGIAdapter(application)
|
||||
server = wsgiref.simple_server.make_server('', 8888, wsgi_app)
|
||||
server.serve_forever()
|
||||
|
||||
See the `appengine demo
|
||||
<https://github.com/facebook/tornado/tree/master/demos/appengine>`_
|
||||
<https://github.com/tornadoweb/tornado/tree/stable/demos/appengine>`_
|
||||
for an example of using this module to run a Tornado app on Google
|
||||
App Engine.
|
||||
|
||||
WSGI applications use the same `.RequestHandler` class, but not
|
||||
``@asynchronous`` methods or ``flush()``. This means that it is
|
||||
not possible to use `.AsyncHTTPClient`, or the `tornado.auth` or
|
||||
`tornado.websocket` modules.
|
||||
In WSGI mode asynchronous methods are not supported. This means
|
||||
that it is not possible to use `.AsyncHTTPClient`, or the
|
||||
`tornado.auth` or `tornado.websocket` modules.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
"""
|
||||
def __init__(self, handlers=None, default_host="", **settings):
|
||||
web.Application.__init__(self, handlers, default_host, transforms=[],
|
||||
wsgi=True, **settings)
|
||||
def __init__(self, application):
|
||||
if isinstance(application, WSGIApplication):
|
||||
self.application = lambda request: web.Application.__call__(
|
||||
application, request)
|
||||
else:
|
||||
self.application = application
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
handler = web.Application.__call__(self, HTTPRequest(environ))
|
||||
assert handler._finished
|
||||
reason = handler._reason
|
||||
status = str(handler._status_code) + " " + reason
|
||||
headers = list(handler._headers.get_all())
|
||||
if hasattr(handler, "_new_cookie"):
|
||||
for cookie in handler._new_cookie.values():
|
||||
headers.append(("Set-Cookie", cookie.OutputString(None)))
|
||||
start_response(status,
|
||||
[(native_str(k), native_str(v)) for (k, v) in headers])
|
||||
return handler._write_buffer
|
||||
|
||||
|
||||
class HTTPRequest(object):
|
||||
"""Mimics `tornado.httpserver.HTTPRequest` for WSGI applications."""
|
||||
def __init__(self, environ):
|
||||
"""Parses the given WSGI environment to construct the request."""
|
||||
self.method = environ["REQUEST_METHOD"]
|
||||
self.path = urllib_parse.quote(from_wsgi_str(environ.get("SCRIPT_NAME", "")))
|
||||
self.path += urllib_parse.quote(from_wsgi_str(environ.get("PATH_INFO", "")))
|
||||
self.uri = self.path
|
||||
self.arguments = {}
|
||||
self.query = environ.get("QUERY_STRING", "")
|
||||
if self.query:
|
||||
self.uri += "?" + self.query
|
||||
self.arguments = parse_qs_bytes(native_str(self.query),
|
||||
keep_blank_values=True)
|
||||
self.version = "HTTP/1.1"
|
||||
self.headers = httputil.HTTPHeaders()
|
||||
method = environ["REQUEST_METHOD"]
|
||||
uri = urllib_parse.quote(from_wsgi_str(environ.get("SCRIPT_NAME", "")))
|
||||
uri += urllib_parse.quote(from_wsgi_str(environ.get("PATH_INFO", "")))
|
||||
if environ.get("QUERY_STRING"):
|
||||
uri += "?" + environ["QUERY_STRING"]
|
||||
headers = httputil.HTTPHeaders()
|
||||
if environ.get("CONTENT_TYPE"):
|
||||
self.headers["Content-Type"] = environ["CONTENT_TYPE"]
|
||||
headers["Content-Type"] = environ["CONTENT_TYPE"]
|
||||
if environ.get("CONTENT_LENGTH"):
|
||||
self.headers["Content-Length"] = environ["CONTENT_LENGTH"]
|
||||
headers["Content-Length"] = environ["CONTENT_LENGTH"]
|
||||
for key in environ:
|
||||
if key.startswith("HTTP_"):
|
||||
self.headers[key[5:].replace("_", "-")] = environ[key]
|
||||
if self.headers.get("Content-Length"):
|
||||
self.body = environ["wsgi.input"].read(
|
||||
int(self.headers["Content-Length"]))
|
||||
headers[key[5:].replace("_", "-")] = environ[key]
|
||||
if headers.get("Content-Length"):
|
||||
body = environ["wsgi.input"].read(
|
||||
int(headers["Content-Length"]))
|
||||
else:
|
||||
self.body = ""
|
||||
self.protocol = environ["wsgi.url_scheme"]
|
||||
self.remote_ip = environ.get("REMOTE_ADDR", "")
|
||||
body = b""
|
||||
protocol = environ["wsgi.url_scheme"]
|
||||
remote_ip = environ.get("REMOTE_ADDR", "")
|
||||
if environ.get("HTTP_HOST"):
|
||||
self.host = environ["HTTP_HOST"]
|
||||
host = environ["HTTP_HOST"]
|
||||
else:
|
||||
self.host = environ["SERVER_NAME"]
|
||||
|
||||
# Parse request body
|
||||
self.files = {}
|
||||
httputil.parse_body_arguments(self.headers.get("Content-Type", ""),
|
||||
self.body, self.arguments, self.files)
|
||||
|
||||
self._start_time = time.time()
|
||||
self._finish_time = None
|
||||
|
||||
def supports_http_1_1(self):
|
||||
"""Returns True if this request supports HTTP/1.1 semantics"""
|
||||
return self.version == "HTTP/1.1"
|
||||
|
||||
@property
|
||||
def cookies(self):
|
||||
"""A dictionary of Cookie.Morsel objects."""
|
||||
if not hasattr(self, "_cookies"):
|
||||
self._cookies = Cookie.SimpleCookie()
|
||||
if "Cookie" in self.headers:
|
||||
try:
|
||||
self._cookies.load(
|
||||
native_str(self.headers["Cookie"]))
|
||||
except Exception:
|
||||
self._cookies = None
|
||||
return self._cookies
|
||||
|
||||
def full_url(self):
|
||||
"""Reconstructs the full URL for this request."""
|
||||
return self.protocol + "://" + self.host + self.uri
|
||||
|
||||
def request_time(self):
|
||||
"""Returns the amount of time it took for this request to execute."""
|
||||
if self._finish_time is None:
|
||||
return time.time() - self._start_time
|
||||
else:
|
||||
return self._finish_time - self._start_time
|
||||
host = environ["SERVER_NAME"]
|
||||
connection = _WSGIConnection(method, start_response,
|
||||
_WSGIRequestContext(remote_ip, protocol))
|
||||
request = httputil.HTTPServerRequest(
|
||||
method, uri, "HTTP/1.1", headers=headers, body=body,
|
||||
host=host, connection=connection)
|
||||
request._parse_body()
|
||||
self.application(request)
|
||||
if connection._error:
|
||||
raise connection._error
|
||||
if not connection._finished:
|
||||
raise Exception("request did not finish synchronously")
|
||||
return connection._write_buffer
|
||||
|
||||
|
||||
class WSGIContainer(object):
|
||||
r"""Makes a WSGI-compatible function runnable on Tornado's HTTP server.
|
||||
|
||||
.. warning::
|
||||
|
||||
WSGI is a *synchronous* interface, while Tornado's concurrency model
|
||||
is based on single-threaded asynchronous execution. This means that
|
||||
running a WSGI app with Tornado's `WSGIContainer` is *less scalable*
|
||||
than running the same app in a multi-threaded WSGI server like
|
||||
``gunicorn`` or ``uwsgi``. Use `WSGIContainer` only when there are
|
||||
benefits to combining Tornado and WSGI in the same process that
|
||||
outweigh the reduced scalability.
|
||||
|
||||
Wrap a WSGI function in a `WSGIContainer` and pass it to `.HTTPServer` to
|
||||
run it. For example::
|
||||
|
||||
@@ -242,10 +275,12 @@ class WSGIContainer(object):
|
||||
return response.append
|
||||
app_response = self.wsgi_application(
|
||||
WSGIContainer.environ(request), start_response)
|
||||
response.extend(app_response)
|
||||
body = b"".join(response)
|
||||
if hasattr(app_response, "close"):
|
||||
app_response.close()
|
||||
try:
|
||||
response.extend(app_response)
|
||||
body = b"".join(response)
|
||||
finally:
|
||||
if hasattr(app_response, "close"):
|
||||
app_response.close()
|
||||
if not data:
|
||||
raise Exception("WSGI app did not call start_response")
|
||||
|
||||
@@ -272,7 +307,7 @@ class WSGIContainer(object):
|
||||
|
||||
@staticmethod
|
||||
def environ(request):
|
||||
"""Converts a `tornado.httpserver.HTTPRequest` to a WSGI environment.
|
||||
"""Converts a `tornado.httputil.HTTPServerRequest` to a WSGI environment.
|
||||
"""
|
||||
hostport = request.host.split(":")
|
||||
if len(hostport) == 2:
|
||||
@@ -285,7 +320,7 @@ class WSGIContainer(object):
|
||||
"REQUEST_METHOD": request.method,
|
||||
"SCRIPT_NAME": "",
|
||||
"PATH_INFO": to_wsgi_str(escape.url_unescape(
|
||||
request.path, encoding=None, plus=False)),
|
||||
request.path, encoding=None, plus=False)),
|
||||
"QUERY_STRING": request.query,
|
||||
"REMOTE_ADDR": request.remote_ip,
|
||||
"SERVER_NAME": host,
|
||||
@@ -318,3 +353,6 @@ class WSGIContainer(object):
|
||||
summary = request.method + " " + request.uri + " (" + \
|
||||
request.remote_ip + ")"
|
||||
log_method("%d %s %.2fms", status_code, summary, request_time)
|
||||
|
||||
|
||||
HTTPRequest = httputil.HTTPServerRequest
|
||||
|
||||
Reference in New Issue
Block a user