Python3 Migrate
This commit is contained in:
210
venv/lib/python3.7/site-packages/mopidy/http/actor.py
Normal file
210
venv/lib/python3.7/site-packages/mopidy/http/actor.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import json
|
||||
import logging
|
||||
import secrets
|
||||
import threading
|
||||
|
||||
import pykka
|
||||
import tornado.httpserver
|
||||
import tornado.ioloop
|
||||
import tornado.netutil
|
||||
import tornado.web
|
||||
import tornado.websocket
|
||||
|
||||
from mopidy import exceptions, models, zeroconf
|
||||
from mopidy.core import CoreListener
|
||||
from mopidy.http import Extension, handlers
|
||||
from mopidy.internal import formatting, network
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
except ImportError:
|
||||
asyncio = None
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpFrontend(pykka.ThreadingActor, CoreListener):
|
||||
apps = []
|
||||
statics = []
|
||||
|
||||
def __init__(self, config, core):
|
||||
super().__init__()
|
||||
|
||||
self.hostname = network.format_hostname(config["http"]["hostname"])
|
||||
self.port = config["http"]["port"]
|
||||
tornado_hostname = config["http"]["hostname"]
|
||||
if tornado_hostname == "::":
|
||||
tornado_hostname = None
|
||||
|
||||
try:
|
||||
logger.debug("Starting HTTP server")
|
||||
sockets = tornado.netutil.bind_sockets(self.port, tornado_hostname)
|
||||
self.server = HttpServer(
|
||||
config=config,
|
||||
core=core,
|
||||
sockets=sockets,
|
||||
apps=self.apps,
|
||||
statics=self.statics,
|
||||
)
|
||||
except OSError as exc:
|
||||
raise exceptions.FrontendError(f"HTTP server startup failed: {exc}")
|
||||
|
||||
self.zeroconf_name = config["http"]["zeroconf"]
|
||||
self.zeroconf_http = None
|
||||
self.zeroconf_mopidy_http = None
|
||||
|
||||
def on_start(self):
|
||||
logger.info("HTTP server running at [%s]:%s", self.hostname, self.port)
|
||||
self.server.start()
|
||||
|
||||
if self.zeroconf_name:
|
||||
self.zeroconf_http = zeroconf.Zeroconf(
|
||||
name=self.zeroconf_name, stype="_http._tcp", port=self.port
|
||||
)
|
||||
self.zeroconf_mopidy_http = zeroconf.Zeroconf(
|
||||
name=self.zeroconf_name,
|
||||
stype="_mopidy-http._tcp",
|
||||
port=self.port,
|
||||
)
|
||||
self.zeroconf_http.publish()
|
||||
self.zeroconf_mopidy_http.publish()
|
||||
|
||||
def on_stop(self):
|
||||
if self.zeroconf_http:
|
||||
self.zeroconf_http.unpublish()
|
||||
if self.zeroconf_mopidy_http:
|
||||
self.zeroconf_mopidy_http.unpublish()
|
||||
|
||||
self.server.stop()
|
||||
|
||||
def on_event(self, name, **data):
|
||||
on_event(name, self.server.io_loop, **data)
|
||||
|
||||
|
||||
def on_event(name, io_loop, **data):
|
||||
event = data
|
||||
event["event"] = name
|
||||
message = json.dumps(event, cls=models.ModelJSONEncoder)
|
||||
handlers.WebSocketHandler.broadcast(message, io_loop)
|
||||
|
||||
|
||||
class HttpServer(threading.Thread):
|
||||
name = "HttpServer"
|
||||
|
||||
def __init__(self, config, core, sockets, apps, statics):
|
||||
super().__init__()
|
||||
|
||||
self.config = config
|
||||
self.core = core
|
||||
self.sockets = sockets
|
||||
self.apps = apps
|
||||
self.statics = statics
|
||||
|
||||
self.app = None
|
||||
self.server = None
|
||||
self.io_loop = None
|
||||
|
||||
def run(self):
|
||||
if asyncio:
|
||||
# If asyncio is available, Tornado uses it as its IO loop. Since we
|
||||
# start Tornado in a another thread than the main thread, we must
|
||||
# explicitly create an asyncio loop for the current thread.
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
|
||||
self.app = tornado.web.Application(
|
||||
self._get_request_handlers(),
|
||||
cookie_secret=self._get_cookie_secret(),
|
||||
)
|
||||
self.server = tornado.httpserver.HTTPServer(self.app)
|
||||
self.server.add_sockets(self.sockets)
|
||||
|
||||
self.io_loop = tornado.ioloop.IOLoop.current()
|
||||
self.io_loop.start()
|
||||
|
||||
logger.debug("Stopped HTTP server")
|
||||
|
||||
def stop(self):
|
||||
logger.debug("Stopping HTTP server")
|
||||
self.io_loop.add_callback(self.io_loop.stop)
|
||||
|
||||
def _get_request_handlers(self):
|
||||
request_handlers = []
|
||||
request_handlers.extend(self._get_app_request_handlers())
|
||||
request_handlers.extend(self._get_static_request_handlers())
|
||||
request_handlers.extend(self._get_default_request_handlers())
|
||||
|
||||
logger.debug(
|
||||
"HTTP routes from extensions: %s",
|
||||
formatting.indent(
|
||||
"\n".join(
|
||||
f"{path!r}: {handler!r}"
|
||||
for (path, handler, *_) in request_handlers
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return request_handlers
|
||||
|
||||
def _get_app_request_handlers(self):
|
||||
result = []
|
||||
for app in self.apps:
|
||||
try:
|
||||
request_handlers = app["factory"](self.config, self.core)
|
||||
except Exception:
|
||||
logger.exception("Loading %s failed.", app["name"])
|
||||
continue
|
||||
|
||||
result.append((f"/{app['name']}", handlers.AddSlashHandler))
|
||||
for handler in request_handlers:
|
||||
handler = list(handler)
|
||||
handler[0] = f"/{app['name']}{handler[0]}"
|
||||
result.append(tuple(handler))
|
||||
logger.debug("Loaded HTTP extension: %s", app["name"])
|
||||
return result
|
||||
|
||||
def _get_static_request_handlers(self):
|
||||
result = []
|
||||
for static in self.statics:
|
||||
result.append((f"/{static['name']}", handlers.AddSlashHandler))
|
||||
result.append(
|
||||
(
|
||||
f"/{static['name']}/(.*)",
|
||||
handlers.StaticFileHandler,
|
||||
{"path": static["path"], "default_filename": "index.html"},
|
||||
)
|
||||
)
|
||||
logger.debug("Loaded static HTTP extension: %s", static["name"])
|
||||
return result
|
||||
|
||||
def _get_default_request_handlers(self):
|
||||
sites = [app["name"] for app in self.apps + self.statics]
|
||||
|
||||
default_app = self.config["http"]["default_app"]
|
||||
if default_app not in sites:
|
||||
logger.warning(
|
||||
f"HTTP server's default app {default_app!r} not found"
|
||||
)
|
||||
default_app = "mopidy"
|
||||
logger.debug(f"Default webclient is {default_app}")
|
||||
|
||||
return [
|
||||
(
|
||||
r"/",
|
||||
tornado.web.RedirectHandler,
|
||||
{"url": f"/{default_app}/", "permanent": False},
|
||||
)
|
||||
]
|
||||
|
||||
def _get_cookie_secret(self):
|
||||
file_path = Extension.get_data_dir(self.config) / "cookie_secret"
|
||||
if not file_path.is_file():
|
||||
cookie_secret = secrets.token_hex(32)
|
||||
file_path.write_text(cookie_secret)
|
||||
else:
|
||||
cookie_secret = file_path.read_text().strip()
|
||||
if not cookie_secret:
|
||||
logging.error(
|
||||
f"HTTP server could not find cookie secret in {file_path}"
|
||||
)
|
||||
return cookie_secret
|
||||
Reference in New Issue
Block a user