Plus ("+" vs. "%2B") encoded search strings for opds search feeds are now working in request string (fix for #2175)
This commit is contained in:
parent
2f949ce1dd
commit
baffe1f537
29
cps/gevent_wsgi.py
Normal file
29
cps/gevent_wsgi.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 2022 OzzieIsaacs
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from gevent.pywsgi import WSGIHandler
|
||||||
|
|
||||||
|
class MyWSGIHandler(WSGIHandler):
|
||||||
|
def get_environ(self):
|
||||||
|
env = super().get_environ()
|
||||||
|
path, __ = self.path.split('?', 1) if '?' in self.path else (self.path, '')
|
||||||
|
env['RAW_URI'] = path
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ def feed_osd():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_cc_search(query):
|
def feed_cc_search(query):
|
||||||
# Handle strange query from Libera Reader with + instead of spaces
|
# Handle strange query from Libera Reader with + instead of spaces
|
||||||
plus_query = unquote_plus(request.base_url.split('/opds/search/')[1]).strip()
|
plus_query = unquote_plus(request.environ['RAW_URI'].split('/opds/search/')[1]).strip()
|
||||||
return feed_search(plus_query)
|
return feed_search(plus_query)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import subprocess # nosec
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from gevent.pywsgi import WSGIServer
|
from gevent.pywsgi import WSGIServer
|
||||||
|
from .gevent_wsgi import MyWSGIHandler
|
||||||
from gevent.pool import Pool
|
from gevent.pool import Pool
|
||||||
from gevent import __version__ as _version
|
from gevent import __version__ as _version
|
||||||
from greenlet import GreenletExit
|
from greenlet import GreenletExit
|
||||||
@ -32,7 +33,7 @@ try:
|
|||||||
VERSION = 'Gevent ' + _version
|
VERSION = 'Gevent ' + _version
|
||||||
_GEVENT = True
|
_GEVENT = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from tornado.wsgi import WSGIContainer
|
from .tornado_wsgi import MyWSGIContainer
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
from tornado import version as _version
|
from tornado import version as _version
|
||||||
@ -202,7 +203,8 @@ class WebServer(object):
|
|||||||
if output is None:
|
if output is None:
|
||||||
output = _readable_listen_address(self.listen_address, self.listen_port)
|
output = _readable_listen_address(self.listen_address, self.listen_port)
|
||||||
log.info('Starting Gevent server on %s', output)
|
log.info('Starting Gevent server on %s', output)
|
||||||
self.wsgiserver = WSGIServer(sock, self.app, log=self.access_logger, spawn=Pool(), **ssl_args)
|
self.wsgiserver = WSGIServer(sock, self.app, log=self.access_logger, handler_class=MyWSGIHandler,
|
||||||
|
spawn=Pool(), **ssl_args)
|
||||||
if ssl_args:
|
if ssl_args:
|
||||||
wrap_socket = self.wsgiserver.wrap_socket
|
wrap_socket = self.wsgiserver.wrap_socket
|
||||||
def my_wrap_socket(*args, **kwargs):
|
def my_wrap_socket(*args, **kwargs):
|
||||||
@ -225,8 +227,8 @@ class WebServer(object):
|
|||||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
log.info('Starting Tornado server on %s', _readable_listen_address(self.listen_address, self.listen_port))
|
log.info('Starting Tornado server on %s', _readable_listen_address(self.listen_address, self.listen_port))
|
||||||
|
|
||||||
# Max Buffersize set to 200MB )
|
# Max Buffersize set to 200MB
|
||||||
http_server = HTTPServer(WSGIContainer(self.app),
|
http_server = HTTPServer(MyWSGIContainer(self.app),
|
||||||
max_buffer_size=209700000,
|
max_buffer_size=209700000,
|
||||||
ssl_options=self.ssl_args)
|
ssl_options=self.ssl_args)
|
||||||
http_server.listen(self.listen_port, self.listen_address)
|
http_server.listen(self.listen_port, self.listen_address)
|
||||||
|
94
cps/tornado_wsgi.py
Normal file
94
cps/tornado_wsgi.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 2022 OzzieIsaacs
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from tornado.wsgi import WSGIContainer
|
||||||
|
import tornado
|
||||||
|
|
||||||
|
from tornado import escape
|
||||||
|
from tornado import httputil
|
||||||
|
|
||||||
|
from typing import List, Tuple, Optional, Callable, Any, Dict, Text
|
||||||
|
from types import TracebackType
|
||||||
|
import typing
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from typing import Type # noqa: F401
|
||||||
|
from wsgiref.types import WSGIApplication as WSGIAppType # noqa: F4
|
||||||
|
|
||||||
|
class MyWSGIContainer(WSGIContainer):
|
||||||
|
|
||||||
|
def __call__(self, request: httputil.HTTPServerRequest) -> None:
|
||||||
|
data = {} # type: Dict[str, Any]
|
||||||
|
response = [] # type: List[bytes]
|
||||||
|
|
||||||
|
def start_response(
|
||||||
|
status: str,
|
||||||
|
headers: List[Tuple[str, str]],
|
||||||
|
exc_info: Optional[
|
||||||
|
Tuple[
|
||||||
|
"Optional[Type[BaseException]]",
|
||||||
|
Optional[BaseException],
|
||||||
|
Optional[TracebackType],
|
||||||
|
]
|
||||||
|
] = None,
|
||||||
|
) -> Callable[[bytes], Any]:
|
||||||
|
data["status"] = status
|
||||||
|
data["headers"] = headers
|
||||||
|
return response.append
|
||||||
|
|
||||||
|
app_response = self.wsgi_application(
|
||||||
|
MyWSGIContainer.environ(request), start_response
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
response.extend(app_response)
|
||||||
|
body = b"".join(response)
|
||||||
|
finally:
|
||||||
|
if hasattr(app_response, "close"):
|
||||||
|
app_response.close() # type: ignore
|
||||||
|
if not data:
|
||||||
|
raise Exception("WSGI app did not call start_response")
|
||||||
|
|
||||||
|
status_code_str, reason = data["status"].split(" ", 1)
|
||||||
|
status_code = int(status_code_str)
|
||||||
|
headers = data["headers"] # type: List[Tuple[str, str]]
|
||||||
|
header_set = set(k.lower() for (k, v) in headers)
|
||||||
|
body = escape.utf8(body)
|
||||||
|
if status_code != 304:
|
||||||
|
if "content-length" not in header_set:
|
||||||
|
headers.append(("Content-Length", str(len(body))))
|
||||||
|
if "content-type" not in header_set:
|
||||||
|
headers.append(("Content-Type", "text/html; charset=UTF-8"))
|
||||||
|
if "server" not in header_set:
|
||||||
|
headers.append(("Server", "TornadoServer/%s" % tornado.version))
|
||||||
|
|
||||||
|
start_line = httputil.ResponseStartLine("HTTP/1.1", status_code, reason)
|
||||||
|
header_obj = httputil.HTTPHeaders()
|
||||||
|
for key, value in headers:
|
||||||
|
header_obj.add(key, value)
|
||||||
|
assert request.connection is not None
|
||||||
|
request.connection.write_headers(start_line, header_obj, chunk=body)
|
||||||
|
request.connection.finish()
|
||||||
|
self._log(status_code, request)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def environ(request: httputil.HTTPServerRequest) -> Dict[Text, Any]:
|
||||||
|
environ = WSGIContainer.environ(request)
|
||||||
|
environ['RAW_URI'] = request.path
|
||||||
|
return environ
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user