You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.8 KiB
155 lines
5.8 KiB
6 years ago
|
"""Native adapter for serving CherryPy via its builtin server."""
|
||
|
|
||
|
import logging
|
||
|
import sys
|
||
|
import io
|
||
|
|
||
|
import cherrypy
|
||
|
from cherrypy._cperror import format_exc, bare_error
|
||
|
from cherrypy.lib import httputil
|
||
|
from cherrypy import wsgiserver
|
||
|
|
||
|
|
||
|
class NativeGateway(wsgiserver.Gateway):
|
||
|
|
||
|
recursive = False
|
||
|
|
||
|
def respond(self):
|
||
|
req = self.req
|
||
|
try:
|
||
|
# Obtain a Request object from CherryPy
|
||
|
local = req.server.bind_addr
|
||
|
local = httputil.Host(local[0], local[1], '')
|
||
|
remote = req.conn.remote_addr, req.conn.remote_port
|
||
|
remote = httputil.Host(remote[0], remote[1], '')
|
||
|
|
||
|
scheme = req.scheme
|
||
|
sn = cherrypy.tree.script_name(req.uri or '/')
|
||
|
if sn is None:
|
||
|
self.send_response('404 Not Found', [], [''])
|
||
|
else:
|
||
|
app = cherrypy.tree.apps[sn]
|
||
|
method = req.method
|
||
|
path = req.path
|
||
|
qs = req.qs or ''
|
||
|
headers = req.inheaders.items()
|
||
|
rfile = req.rfile
|
||
|
prev = None
|
||
|
|
||
|
try:
|
||
|
redirections = []
|
||
|
while True:
|
||
|
request, response = app.get_serving(
|
||
|
local, remote, scheme, 'HTTP/1.1')
|
||
|
request.multithread = True
|
||
|
request.multiprocess = False
|
||
|
request.app = app
|
||
|
request.prev = prev
|
||
|
|
||
|
# Run the CherryPy Request object and obtain the
|
||
|
# response
|
||
|
try:
|
||
|
request.run(method, path, qs,
|
||
|
req.request_protocol, headers, rfile)
|
||
|
break
|
||
|
except cherrypy.InternalRedirect:
|
||
|
ir = sys.exc_info()[1]
|
||
|
app.release_serving()
|
||
|
prev = request
|
||
|
|
||
|
if not self.recursive:
|
||
|
if ir.path in redirections:
|
||
|
raise RuntimeError(
|
||
|
'InternalRedirector visited the same '
|
||
|
'URL twice: %r' % ir.path)
|
||
|
else:
|
||
|
# Add the *previous* path_info + qs to
|
||
|
# redirections.
|
||
|
if qs:
|
||
|
qs = '?' + qs
|
||
|
redirections.append(sn + path + qs)
|
||
|
|
||
|
# Munge environment and try again.
|
||
|
method = 'GET'
|
||
|
path = ir.path
|
||
|
qs = ir.query_string
|
||
|
rfile = io.BytesIO()
|
||
|
|
||
|
self.send_response(
|
||
|
response.output_status, response.header_list,
|
||
|
response.body)
|
||
|
finally:
|
||
|
app.release_serving()
|
||
|
except:
|
||
|
tb = format_exc()
|
||
|
# print tb
|
||
|
cherrypy.log(tb, 'NATIVE_ADAPTER', severity=logging.ERROR)
|
||
|
s, h, b = bare_error()
|
||
|
self.send_response(s, h, b)
|
||
|
|
||
|
def send_response(self, status, headers, body):
|
||
|
req = self.req
|
||
|
|
||
|
# Set response status
|
||
|
req.status = str(status or '500 Server Error')
|
||
|
|
||
|
# Set response headers
|
||
|
for header, value in headers:
|
||
|
req.outheaders.append((header, value))
|
||
|
if (req.ready and not req.sent_headers):
|
||
|
req.sent_headers = True
|
||
|
req.send_headers()
|
||
|
|
||
|
# Set response body
|
||
|
for seg in body:
|
||
|
req.write(seg)
|
||
|
|
||
|
|
||
|
class CPHTTPServer(wsgiserver.HTTPServer):
|
||
|
|
||
|
"""Wrapper for wsgiserver.HTTPServer.
|
||
|
|
||
|
wsgiserver has been designed to not reference CherryPy in any way,
|
||
|
so that it can be used in other frameworks and applications.
|
||
|
Therefore, we wrap it here, so we can apply some attributes
|
||
|
from config -> cherrypy.server -> HTTPServer.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, server_adapter=cherrypy.server):
|
||
|
self.server_adapter = server_adapter
|
||
|
|
||
|
server_name = (self.server_adapter.socket_host or
|
||
|
self.server_adapter.socket_file or
|
||
|
None)
|
||
|
|
||
|
wsgiserver.HTTPServer.__init__(
|
||
|
self, server_adapter.bind_addr, NativeGateway,
|
||
|
minthreads=server_adapter.thread_pool,
|
||
|
maxthreads=server_adapter.thread_pool_max,
|
||
|
server_name=server_name)
|
||
|
|
||
|
self.max_request_header_size = (
|
||
|
self.server_adapter.max_request_header_size or 0)
|
||
|
self.max_request_body_size = (
|
||
|
self.server_adapter.max_request_body_size or 0)
|
||
|
self.request_queue_size = self.server_adapter.socket_queue_size
|
||
|
self.timeout = self.server_adapter.socket_timeout
|
||
|
self.shutdown_timeout = self.server_adapter.shutdown_timeout
|
||
|
self.protocol = self.server_adapter.protocol_version
|
||
|
self.nodelay = self.server_adapter.nodelay
|
||
|
|
||
|
ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
|
||
|
if self.server_adapter.ssl_context:
|
||
|
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
|
||
|
self.ssl_adapter = adapter_class(
|
||
|
self.server_adapter.ssl_certificate,
|
||
|
self.server_adapter.ssl_private_key,
|
||
|
self.server_adapter.ssl_certificate_chain)
|
||
|
self.ssl_adapter.context = self.server_adapter.ssl_context
|
||
|
elif self.server_adapter.ssl_certificate:
|
||
|
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
|
||
|
self.ssl_adapter = adapter_class(
|
||
|
self.server_adapter.ssl_certificate,
|
||
|
self.server_adapter.ssl_private_key,
|
||
|
self.server_adapter.ssl_certificate_chain)
|