mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-05-26 07:08:10 +00:00
307 lines
9.8 KiB
Python
307 lines
9.8 KiB
Python
import os
|
|
import posixpath
|
|
import sys
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer, SimpleHTTPRequestHandler
|
|
from io import BytesIO
|
|
from urllib import parse as urlparse
|
|
from urllib.parse import unquote
|
|
|
|
from . import utils
|
|
from . import webapi as web
|
|
|
|
__all__ = ["runsimple"]
|
|
|
|
|
|
def runbasic(func, server_address=("0.0.0.0", 8080)):
|
|
"""
|
|
Runs a simple HTTP server hosting WSGI app `func`. The directory `static/`
|
|
is hosted statically.
|
|
|
|
Based on [WsgiServer][ws] from [Colin Stewart][cs].
|
|
|
|
[ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
|
|
[cs]: http://www.owlfish.com/
|
|
"""
|
|
# Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
|
|
# Modified somewhat for simplicity
|
|
# Used under the modified BSD license:
|
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
|
|
|
import errno
|
|
import socket
|
|
import traceback
|
|
|
|
import SocketServer
|
|
|
|
class WSGIHandler(SimpleHTTPRequestHandler):
|
|
def run_wsgi_app(self):
|
|
protocol, host, path, parameters, query, fragment = urlparse.urlparse(
|
|
"http://dummyhost%s" % self.path
|
|
)
|
|
|
|
# we only use path, query
|
|
env = {
|
|
"wsgi.version": (1, 0),
|
|
"wsgi.url_scheme": "http",
|
|
"wsgi.input": self.rfile,
|
|
"wsgi.errors": sys.stderr,
|
|
"wsgi.multithread": 1,
|
|
"wsgi.multiprocess": 0,
|
|
"wsgi.run_once": 0,
|
|
"REQUEST_METHOD": self.command,
|
|
"REQUEST_URI": self.path,
|
|
"PATH_INFO": path,
|
|
"QUERY_STRING": query,
|
|
"CONTENT_TYPE": self.headers.get("Content-Type", ""),
|
|
"CONTENT_LENGTH": self.headers.get("Content-Length", ""),
|
|
"REMOTE_ADDR": self.client_address[0],
|
|
"SERVER_NAME": self.server.server_address[0],
|
|
"SERVER_PORT": str(self.server.server_address[1]),
|
|
"SERVER_PROTOCOL": self.request_version,
|
|
}
|
|
|
|
for http_header, http_value in self.headers.items():
|
|
env["HTTP_%s" % http_header.replace("-", "_").upper()] = http_value
|
|
|
|
# Setup the state
|
|
self.wsgi_sent_headers = 0
|
|
self.wsgi_headers = []
|
|
|
|
try:
|
|
# We have there environment, now invoke the application
|
|
result = self.server.app(env, self.wsgi_start_response)
|
|
try:
|
|
try:
|
|
for data in result:
|
|
if data:
|
|
self.wsgi_write_data(data)
|
|
finally:
|
|
if hasattr(result, "close"):
|
|
result.close()
|
|
except OSError as socket_err:
|
|
# Catch common network errors and suppress them
|
|
if socket_err.args[0] in (errno.ECONNABORTED, errno.EPIPE):
|
|
return
|
|
except socket.timeout:
|
|
return
|
|
except:
|
|
print(traceback.format_exc(), file=web.debug)
|
|
|
|
if not self.wsgi_sent_headers:
|
|
# We must write out something!
|
|
self.wsgi_write_data(" ")
|
|
return
|
|
|
|
do_POST = run_wsgi_app
|
|
do_PUT = run_wsgi_app
|
|
do_DELETE = run_wsgi_app
|
|
|
|
def do_GET(self):
|
|
if self.path.startswith("/static/"):
|
|
SimpleHTTPRequestHandler.do_GET(self)
|
|
else:
|
|
self.run_wsgi_app()
|
|
|
|
def wsgi_start_response(self, response_status, response_headers, exc_info=None):
|
|
if self.wsgi_sent_headers:
|
|
raise Exception("Headers already sent and start_response called again!")
|
|
# Should really take a copy to avoid changes in the application....
|
|
self.wsgi_headers = (response_status, response_headers)
|
|
return self.wsgi_write_data
|
|
|
|
def wsgi_write_data(self, data):
|
|
if not self.wsgi_sent_headers:
|
|
status, headers = self.wsgi_headers
|
|
# Need to send header prior to data
|
|
status_code = status[: status.find(" ")]
|
|
status_msg = status[status.find(" ") + 1 :]
|
|
self.send_response(int(status_code), status_msg)
|
|
for header, value in headers:
|
|
self.send_header(header, value)
|
|
self.end_headers()
|
|
self.wsgi_sent_headers = 1
|
|
# Send the data
|
|
self.wfile.write(data)
|
|
|
|
class WSGIServer(SocketServer.ThreadingMixIn, HTTPServer):
|
|
def __init__(self, func, server_address):
|
|
HTTPServer.HTTPServer.__init__(self, server_address, WSGIHandler)
|
|
self.app = func
|
|
self.serverShuttingDown = 0
|
|
|
|
print("http://%s:%d/" % server_address)
|
|
WSGIServer(func, server_address).serve_forever()
|
|
|
|
|
|
# The WSGIServer instance.
|
|
# Made global so that it can be stopped in embedded mode.
|
|
server = None
|
|
|
|
|
|
def runsimple(func, server_address=("0.0.0.0", 8080)):
|
|
"""
|
|
Runs [CherryPy][cp] WSGI server hosting WSGI app `func`.
|
|
The directory `static/` is hosted statically.
|
|
|
|
[cp]: http://www.cherrypy.org
|
|
"""
|
|
global server
|
|
func = StaticMiddleware(func)
|
|
func = LogMiddleware(func)
|
|
|
|
server = WSGIServer(server_address, func)
|
|
|
|
if "/" in server_address[0]:
|
|
print("%s" % server_address)
|
|
else:
|
|
if server.ssl_adapter:
|
|
print("https://%s:%d/" % server_address)
|
|
else:
|
|
print("http://%s:%d/" % server_address)
|
|
|
|
try:
|
|
server.start()
|
|
except (KeyboardInterrupt, SystemExit):
|
|
server.stop()
|
|
server = None
|
|
|
|
|
|
def WSGIServer(server_address, wsgi_app):
|
|
"""Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`.
|
|
This function can be overwritten to customize the webserver or use a different webserver.
|
|
"""
|
|
from cheroot import wsgi
|
|
|
|
server = wsgi.Server(server_address, wsgi_app, server_name="localhost")
|
|
server.nodelay = not sys.platform.startswith(
|
|
"java"
|
|
) # TCP_NODELAY isn't supported on the JVM
|
|
return server
|
|
|
|
|
|
class StaticApp(SimpleHTTPRequestHandler):
|
|
"""WSGI application for serving static files."""
|
|
|
|
def __init__(self, environ, start_response):
|
|
self.headers = []
|
|
self.environ = environ
|
|
self.start_response = start_response
|
|
self.directory = os.getcwd()
|
|
|
|
def send_response(self, status, msg=""):
|
|
# the int(status) call is needed because in Py3 status is an enum.IntEnum and we need the integer behind
|
|
self.status = str(int(status)) + " " + msg
|
|
|
|
def send_header(self, name, value):
|
|
self.headers.append((name, value))
|
|
|
|
def end_headers(self):
|
|
pass
|
|
|
|
def log_message(*a):
|
|
pass
|
|
|
|
def __iter__(self):
|
|
environ = self.environ
|
|
|
|
self.path = environ.get("PATH_INFO", "")
|
|
self.client_address = (
|
|
environ.get("REMOTE_ADDR", "-"),
|
|
environ.get("REMOTE_PORT", "-"),
|
|
)
|
|
self.command = environ.get("REQUEST_METHOD", "-")
|
|
|
|
self.wfile = BytesIO() # for capturing error
|
|
|
|
try:
|
|
path = self.translate_path(self.path)
|
|
etag = '"%s"' % os.path.getmtime(path)
|
|
client_etag = environ.get("HTTP_IF_NONE_MATCH")
|
|
self.send_header("ETag", etag)
|
|
if etag == client_etag:
|
|
self.send_response(304, "Not Modified")
|
|
self.start_response(self.status, self.headers)
|
|
return
|
|
except OSError:
|
|
pass # Probably a 404
|
|
|
|
f = self.send_head()
|
|
self.start_response(self.status, self.headers)
|
|
|
|
if f:
|
|
block_size = 16 * 1024
|
|
while True:
|
|
buf = f.read(block_size)
|
|
if not buf:
|
|
break
|
|
yield buf
|
|
f.close()
|
|
else:
|
|
value = self.wfile.getvalue()
|
|
yield value
|
|
|
|
|
|
class StaticMiddleware:
|
|
"""WSGI middleware for serving static files."""
|
|
|
|
def __init__(self, app, prefix="/static/"):
|
|
self.app = app
|
|
self.prefix = prefix
|
|
|
|
def __call__(self, environ, start_response):
|
|
path = environ.get("PATH_INFO", "")
|
|
path = self.normpath(path)
|
|
|
|
if path.startswith(self.prefix):
|
|
return StaticApp(environ, start_response)
|
|
else:
|
|
return self.app(environ, start_response)
|
|
|
|
def normpath(self, path):
|
|
path2 = posixpath.normpath(unquote(path))
|
|
if path.endswith("/"):
|
|
path2 += "/"
|
|
return path2
|
|
|
|
|
|
class LogMiddleware:
|
|
"""WSGI middleware for logging the status."""
|
|
|
|
def __init__(self, app):
|
|
self.app = app
|
|
self.format = '%s - - [%s] "%s %s %s" - %s'
|
|
|
|
f = BytesIO()
|
|
|
|
class FakeSocket:
|
|
def makefile(self, *a):
|
|
return f
|
|
|
|
# take log_date_time_string method from BaseHTTPRequestHandler
|
|
self.log_date_time_string = BaseHTTPRequestHandler(
|
|
FakeSocket(), None, None
|
|
).log_date_time_string
|
|
|
|
def __call__(self, environ, start_response):
|
|
def xstart_response(status, response_headers, *args):
|
|
out = start_response(status, response_headers, *args)
|
|
self.log(status, environ)
|
|
return out
|
|
|
|
return self.app(environ, xstart_response)
|
|
|
|
def log(self, status, environ):
|
|
outfile = environ.get("wsgi.errors", web.debug)
|
|
req = environ.get("PATH_INFO", "_")
|
|
protocol = environ.get("ACTUAL_SERVER_PROTOCOL", "-")
|
|
method = environ.get("REQUEST_METHOD", "-")
|
|
host = "{}:{}".format(
|
|
environ.get("REMOTE_ADDR", "-"),
|
|
environ.get("REMOTE_PORT", "-"),
|
|
)
|
|
|
|
time = self.log_date_time_string()
|
|
|
|
msg = self.format % (host, time, protocol, method, req, status)
|
|
print(utils.safestr(msg), file=outfile)
|