def get_app(self): app = Application() def request_callable(request): request.connection.write_headers( ResponseStartLine("HTTP/1.1", 200, "OK"), HTTPHeaders({"Content-Length": "2"})) request.connection.write(b"OK") request.connection.finish() router = CustomRouter() router.add_routes({ "/nested_handler": (app, _get_named_handler("nested_handler")) }) app.add_handlers(".*", [ (HostMatches("www.example.com"), [ (PathMatches("/first_handler"), "tornado.test.routing_test.SecondHandler", {}, "second_handler") ]), Rule(PathMatches("/.*handler"), router), Rule(PathMatches("/first_handler"), FirstHandler, name="first_handler"), Rule(PathMatches("/request_callable"), request_callable), ("/connection_delegate", ConnectionDelegate()) ]) return app
def make_web_app(self): # Start with web application routes app = Application([ (r"/assets/(.*)", StaticFileHandler, dict(path=tornado_settings['static_path'])), (r"/dashboard/(.*)", MainUIRequestHandler, dict( data_queues=self.data_queues, foreman=self.foreman, )), (r"/dashws", DashboardWebSocket, dict( data_queues=self.data_queues, foreman=self.foreman, )), (r"/history/(.*)", HistoryUIRequestHandler, dict(data_queues=self.data_queues, )), (r"/plugins/(.*)", PluginsUIRequestHandler, dict(data_queues=self.data_queues, )), (r"/settings/(.*)", SettingsUIRequestHandler, dict(data_queues=self.data_queues, )), (r"/filebrowser/(.*)", ElementFileBrowserUIRequestHandler, dict(data_queues=self.data_queues, )), (r"/(.*)", RedirectHandler, dict(url="/dashboard/")), ], **tornado_settings) # Add API routes app.add_handlers(r'.*', [ (PathMatches(r"/api/.*"), APIRequestRouter(app)), ]) return app
def make_app(host, apipath): if host is None: return Application([(apipath, WebPing)]) app = Application() app.add_handlers(host, [(apipath, WebPing)]) return app
def __init__(self, app: Application, pyctuator_impl: PyctuatorImpl) -> None: super().__init__(app, pyctuator_impl) custom_dumps = partial(json.dumps, default=self._custom_json_serializer) app.settings.setdefault("pyctuator_router", self) app.settings.setdefault("custom_dumps", custom_dumps) # Register a log-function that records request and response in traces and than delegates to the original func self.delegate_log_function = app.settings.get("log_function") app.settings.setdefault("log_function", self._intercept_request_and_response) app.add_handlers(".*$", [ (r"/pyctuator", PyctuatorHandler), (r"/pyctuator/env", EnvHandler), (r"/pyctuator/info", InfoHandler), (r"/pyctuator/health", HealthHandler), (r"/pyctuator/metrics", MetricsHandler), (r"/pyctuator/metrics/(?P<metric_name>.*$)", MetricsNameHandler), (r"/pyctuator/loggers", LoggersHandler), (r"/pyctuator/loggers/(?P<logger_name>.*$)", LoggersNameHandler), (r"/pyctuator/dump", ThreadDumpHandler), (r"/pyctuator/threaddump", ThreadDumpHandler), (r"/pyctuator/logfile", LogFileHandler), (r"/pyctuator/trace", HttpTraceHandler), (r"/pyctuator/httptrace", HttpTraceHandler), ])
def _add_handler(app: Application, options: Options, handlers: _RouteHandlerSpecs) -> None: prefixed_handlers: List[Any] = [ (urljoin(options.url_prefix, route_pattern), ) + tuple(handler_info) for route_pattern, *handler_info in handlers ] app.add_handlers(r".*", prefixed_handlers)
def get_app(self): app = Application() def request_callable(request): request.connection.write_headers( ResponseStartLine("HTTP/1.1", 200, "OK"), HTTPHeaders({"Content-Length": "2"})) request.connection.write(b"OK") request.connection.finish() router = CustomRouter() router.add_routes({ "/nested_handler": (app, _get_named_handler("nested_handler")) }) app.add_handlers(".*", [ (HostMatches("www.example.com"), [ (PathMatches("/first_handler"), "tornado.test.routing_test.SecondHandler", {}, "second_handler") ]), Rule(PathMatches("/.*handler"), router), Rule(PathMatches("/first_handler"), FirstHandler, name="first_handler"), Rule(PathMatches("/request_callable"), request_callable), ("/connection_delegate", ConnectionDelegate()) ]) return app
def _setup_application( self, config: Config, app: Application, ) -> None: base_url = config["base_url"] app.add_handlers( r".*", [ (urljoin(base_url, route_pattern), ) + tuple(handler_info) # type: ignore for route_pattern, *handler_info in self._create_route_handlers(config) ], )
def __call__(self, application=None): """ This is used to mount the handlers in the applet onto a new `tornado.web.Application` object or an existing one thats passed in. :arg tornado.web.Application application The application providing the mount point for the handlers in the applet. A new application object is created if this argument is `None`. """ if not application: application = Application() application.add_handlers(self.host_pattern, self.handlers) return application
def initWebServer(options={}): options.setdefault('port', 8081) options.setdefault('host', '0.0.0.0') options.setdefault('log_dir', None) options.setdefault('username', '') options.setdefault('password', '') options.setdefault('web_root', '/') assert isinstance(options['port'], int) assert 'data_root' in options # tornado setup # Load the app app = Application([], debug=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=' ) # Main Handler app.add_handlers(".*$", [ (r"%s" % options['web_root'], RedirectHandler, {'url': '%s/home/' % options['web_root']}), (r'%s/(.*)(/?)' % options['web_root'], webserve.MainHandler) ]) # Static Path Handler app.add_handlers(".*$", [ (r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'css')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'js')]}) ]) global server protocol = "http" server = HTTPServer(app, no_keep_alive=True) logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str( options['port']) + "/") server.listen(options['port'], options['host'])
def application( route_list, Application, xsrf_cookies=False, ): app = Application( xsrf_cookies=xsrf_cookies, debug=app_config.DEBUG, gzip=True ) for route in route_list: # print route.host, route.handlers app.add_handlers(route.host, route.handlers) return app
def get_app(self): app = Application() def request_callable(request): request.write(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK") request.finish() app.add_handlers(".*", [ (HostMatches("www.example.com"), [ (PathMatches("/first_handler"), "tornado.test.routing_test.SecondHandler", {}, "second_handler") ]), Rule(PathMatches("/first_handler"), FirstHandler, name="first_handler"), Rule(PathMatches("/request_callable"), request_callable), ("/connection_delegate", ConnectionDelegate()) ]) return app
def application(): """ 生成一个可用的应用程序,供http服务器调用 :return: Application """ settings = { "autoescape": "xhtml_escape", "websocket_ping_interval": None, # "static_path": "statics", # "static_url_prefix": "/static", "template_path": opt_template["template_path"], "cookie_secret": opt_secret["cookie_secret"], "xsrf_cookies": opt_secret["xsrf_cookies"] == "true", "ui_methods": ancient.uiMethod, "ui_modules": ancient.uiModule.registered_class, "debug": opt_debug["debug"] == "true", "autoreload": opt_debug["autoreload"] == "true", "compiled_template_cache": opt_debug["compiled_template_cache"] == "true", "static_hash_cache": opt_debug["static_hash_cache"] == "true", "serve_traceback": opt_debug["serve_traceback"] == "true", } domain_name = opt_server["domain"] if domain_name: app = Application(handlers=None, **settings) app.add_handlers(domain_name, routers()) print("Local access : [ http://127.0.0.1:{} ]".format(options.port)) print("Remote access : [ http://{0}:{1} ]".format( domain_name, options.port)) else: app = Application(handlers=routers(), **settings) print("Local access : [ http://127.0.0.1:{} ]".format(options.port)) network_index = int(opt_debug["network_index"]) index = 0 for ip in gethostbyname_ex(gethostname())[2]: index += 1 url = "http://{}:{}".format(ip, options.port) print("Remote access : [ {} ]".format(url)) if network_index == index: print_qrcode(url) return app
def get_app(self): app = Application() def request_callable(request): request.write(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK") request.finish() app.add_handlers( ".*", [(HostMatches("www.example.com"), [ (PathMatches("/first_handler"), "tornado.test.routing_test.SecondHandler", {}, "second_handler") ]), Rule(PathMatches("/first_handler"), FirstHandler, name="first_handler"), Rule(PathMatches("/request_callable"), request_callable), ("/connection_delegate", ConnectionDelegate())]) return app
def run(self, port=5000, host='127.0.0.1', **kwargs): if self.app.debug or kwargs.get('debug', False): # Attach debugger to Flask routes self.app.debug = True self.app.wsgi_app = DebuggedApplication(self.app.wsgi_app, True) # Attach debugger to Tornado routes self.debug_app = DebuggedApplication(WSGIAdapter(self), True) debug_container = WSGIContainer(self.debug_app) # Socket handlers must bypass debugger if hasattr(self, 'socket_handlers'): socket_app = Application([ (r".*", FallbackHandler, dict(fallback=WSGIContainer(self.debug_app))) ]) socket_app.add_handlers(r".*", self.socket_handlers) http_server = HTTPServer(socket_app) else: http_server = HTTPServer(debug_container) autoreload.start() else: if hasattr(self, 'socket_handlers'): self.add_handlers(r".*", self.socket_handlers) http_server = HTTPServer(self) http_server.listen(port, address=host) IOLoop.current().start()
class SRWebServer(threading.Thread): # pylint: disable=too-many-instance-attributes def __init__(self, options=None, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options or {} self.options.setdefault("port", 8081) self.options.setdefault("host", "0.0.0.0") self.options.setdefault("log_dir", None) self.options.setdefault("username", "") self.options.setdefault("password", "") self.options.setdefault("web_root", "/") assert isinstance(self.options["port"], int) assert "data_root" in self.options self.server = None # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split("|") self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options["web_root"]: sickbeard.WEB_ROOT = self.options["web_root"] = "/" + self.options["web_root"].lstrip("/").strip("/") # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options["api_root"] = r"{0}/api/{1}".format(sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options["enable_https"] self.https_cert = self.options["https_cert"] self.https_key = self.options["https_key"] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key) ): if not create_https_certificates(self.https_cert, self.https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url="{0}/login/".format(self.options["web_root"]), ) # Main Handlers self.app.add_handlers( ".*$", [ # webapi handler (r"{0}(/?.*)".format(self.options["api_root"]), ApiHandler), # webapi key retrieval (r"{0}/getkey(/?.*)".format(self.options["web_root"]), KeyHandler), # webapi builder redirect ( r"{0}/api/builder".format(self.options["web_root"]), RedirectHandler, {"url": self.options["web_root"] + "/apibuilder/"}, ), # webui login/logout handlers (r"{0}/login(/?)".format(self.options["web_root"]), LoginHandler), (r"{0}/logout(/?)".format(self.options["web_root"]), LogoutHandler), # Web calendar handler (Needed because option Unprotected calendar) (r"{0}/calendar".format(self.options["web_root"]), CalendarHandler), # webui handlers ] + route.get_routes(self.options["web_root"]), ) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon ( r"{0}/(favicon\.ico)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, self.options["data_root"], "images/ico/favicon.ico")}, ), # images ( r"{0}/images/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, self.options["data_root"], "images")}, ), # cached images ( r"{0}/cache/images/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, sickbeard.CACHE_DIR, "images")}, ), # css ( r"{0}/css/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, self.options["data_root"], "css")}, ), # javascript ( r"{0}/js/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, self.options["data_root"], "js")}, ), # fonts ( r"{0}/fonts/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": ek(os.path.join, self.options["data_root"], "fonts")}, ), # videos (r"{0}/videos/(.*)".format(self.options["web_root"]), StaticFileHandler, {"path": self.video_root}), ], ) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, ssl_options={"certfile": self.https_cert, "keyfile": self.https_key}) else: protocol = "http" self.server = HTTPServer(self.app) logger.log( u"Starting SickRage on " + protocol + "://" + str(self.options["host"]) + ":" + str(self.options["port"]) + "/" ) try: self.server.listen(self.options["port"], self.options["host"]) except Exception: if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser( "https" if sickbeard.ENABLE_HTTPS else "http", self.options["port"], sickbeard.WEB_ROOT ) logger.log(u"Launching browser and exiting") logger.log(u"Could not start webserver on port {0}, already in use!".format(self.options["port"])) os._exit(1) # pylint: disable=protected-access try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
class WebServer(threading.Thread): def __init__(self, options=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.io_loop = None self.server = None self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', None) assert isinstance(self.options['port'], int) assert 'data_root' in self.options # web root self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/')) if self.options['web_root'] else '' # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: make_cert = False update_cfg = False for (attr, ext) in [('https_cert', '.crt'), ('https_key', '.key')]: ssl_path = getattr(self, attr, None) if ssl_path and not os.path.isfile(ssl_path): if not ssl_path.endswith(ext): setattr(self, attr, os.path.join(ssl_path, 'server%s' % ext)) setattr(sickbeard, attr.upper(), 'server%s' % ext) make_cert = True # If either the HTTPS certificate or key do not exist, make some self-signed ones. if make_cert: if not create_https_certificates(self.https_cert, self.https_key): logger.log(u'Unable to create CERT/KEY files, disabling HTTPS') update_cfg |= False is not sickbeard.ENABLE_HTTPS sickbeard.ENABLE_HTTPS = False self.enable_https = False else: update_cfg = True if not (os.path.isfile(self.https_cert) and os.path.isfile(self.https_key)): logger.log(u'Disabled HTTPS because of missing CERT and KEY files', logger.WARNING) update_cfg |= False is not sickbeard.ENABLE_HTTPS sickbeard.ENABLE_HTTPS = False self.enable_https = False if update_cfg: sickbeard.save_config() # Load the app self.app = Application([], debug=True, serve_traceback=True, autoreload=False, compress_response=True, cookie_secret=sickbeard.COOKIE_SECRET, xsrf_cookies=True, login_url='%s/login/' % self.options['web_root']) re_host_pattern = re_valid_hostname() # webui login/logout handlers self.app.add_handlers(re_host_pattern, [ (r'%s/login(/?)' % self.options['web_root'], webserve.LoginHandler), (r'%s/logout(/?)' % self.options['web_root'], webserve.LogoutHandler), ]) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers(re_host_pattern, [ (r'%s/calendar' % self.options['web_root'], webserve.CalendarHandler), ]) # Static File Handlers self.app.add_handlers(re_host_pattern, [ # favicon (r'%s/(favicon\.ico)' % self.options['web_root'], webserve.BaseStaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'%s/images/(.*)' % self.options['web_root'], webserve.BaseStaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images')}), # cached images (r'%s/cache/images/(.*)' % self.options['web_root'], webserve.BaseStaticFileHandler, {'path': os.path.join(sickbeard.CACHE_DIR, 'images')}), # css (r'%s/css/(.*)' % self.options['web_root'], webserve.BaseStaticFileHandler, {'path': os.path.join(self.options['data_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options['web_root'], webserve.BaseStaticFileHandler, {'path': os.path.join(self.options['data_root'], 'js')}), (r'%s/kodi/(.*)' % self.options['web_root'], webserve.RepoHandler, {'path': os.path.join(sickbeard.CACHE_DIR, 'clients', 'kodi'), 'default_filename': 'index.html'}), ]) # Main Handler self.app.add_handlers(re_host_pattern, [ (r'%s/api/builder(/?)(.*)' % self.options['web_root'], webserve.ApiBuilder), (r'%s/api(/?.*)' % self.options['web_root'], webapi.Api), (r'%s/imagecache(/?.*)' % self.options['web_root'], webserve.CachedImages), (r'%s/cache(/?.*)' % self.options['web_root'], webserve.Cache), (r'%s/config/general(/?.*)' % self.options['web_root'], webserve.ConfigGeneral), (r'%s/config/search(/?.*)' % self.options['web_root'], webserve.ConfigSearch), (r'%s/config/providers(/?.*)' % self.options['web_root'], webserve.ConfigProviders), (r'%s/config/subtitles(/?.*)' % self.options['web_root'], webserve.ConfigSubtitles), (r'%s/config/postProcessing(/?.*)' % self.options['web_root'], webserve.ConfigPostProcessing), (r'%s/config/notifications(/?.*)' % self.options['web_root'], webserve.ConfigNotifications), (r'%s/config/anime(/?.*)' % self.options['web_root'], webserve.ConfigAnime), (r'%s/config(/?.*)' % self.options['web_root'], webserve.Config), (r'%s/errorlogs(/?.*)' % self.options['web_root'], webserve.ErrorLogs), (r'%s/history(/?.*)' % self.options['web_root'], webserve.History), (r'%s/home/is_alive(/?.*)' % self.options['web_root'], webserve.IsAliveHandler), (r'%s/home/addShows(/?.*)' % self.options['web_root'], webserve.NewHomeAddShows), (r'%s/home/postprocess(/?.*)' % self.options['web_root'], webserve.HomePostProcess), (r'%s/home(/?.*)' % self.options['web_root'], webserve.Home), (r'%s/manage/manageSearches(/?.*)' % self.options['web_root'], webserve.ManageSearches), (r'%s/manage/showProcesses(/?.*)' % self.options['web_root'], webserve.showProcesses), (r'%s/manage/(/?.*)' % self.options['web_root'], webserve.Manage), (r'%s/ui(/?.*)' % self.options['web_root'], webserve.UI), (r'%s/browser(/?.*)' % self.options['web_root'], webserve.WebFileBrowser), (r'%s(/?update_watched_state_kodi/?)' % self.options['web_root'], webserve.NoXSRFHandler), (r'%s(/?.*)' % self.options['web_root'], webserve.MainHandler), ]) def run(self): protocol, ssl_options = (('http', None), ('https', {'certfile': self.https_cert, 'keyfile': self.https_key}))[self.enable_https] logger.log(u'Starting SickGear on ' + protocol + '://' + str(self.options['host']) + ':' + str( self.options['port']) + '/') try: self.server = self.app.listen(self.options['port'], self.options['host'], ssl_options=ssl_options, xheaders=sickbeard.HANDLE_REVERSE_PROXY, protocol=protocol) except (StandardError, Exception): etype, evalue, etb = sys.exc_info() logger.log( 'Could not start webserver on %s. Excpeption: %s, Error: %s' % (self.options['port'], etype, evalue), logger.ERROR) return self.io_loop = IOLoop.current() try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shut_down(self): self.alive = False if None is not self.io_loop: self.io_loop.stop()
class PupyWebServer(object): def __init__(self, pupsrv, config): self.pupsrv = pupsrv self.config = config self.clients = {} self.mappings = {} self.ssl = False self.wwwroot = self.config.get( 'webserver', 'static_webroot_uri', None) or \ self.random_path() self.preserve_payloads = self.config.getboolean( 'webserver', 'preserve_payloads') self.root = self.config.get_folder('wwwroot') self.app = None self._thread = None self._ioloop = None self.listen = config.get('webserver', 'listen') if ':' in self.listen: hostname, port = self.listen.rsplit(':', 1) port = int(port) self.hostname, self.port = hostname, port else: self.hostname = self.listen self.port = 9000 self.served_files = set() self.aliases = {} self.show_requests = self.config.getboolean('webserver', 'log') def log(self, handler): if not self.show_requests: return message = 'Web: ' if handler.request.uri in self.aliases: message += '({}) '.format(self.aliases[handler.request.uri]) message += handler._request_summary() if handler.get_status() < 400: self.pupsrv.info(Success(message)) else: self.pupsrv.info(Error(message)) def start(self): webstatic = self.config.get_folder('webstatic', create=False) cert = self.config.get('webserver', 'cert', None) key = self.config.get('webserver', 'key', None) self.app = TornadoApplication([ (r'/', IndexHandler), (self.wwwroot + '/(.*)', PayloadsHandler, { 'path': self.root, 'mappings': self.mappings, }), (r'/static/(.*)', TornadoStaticFileHandler, { 'path': webstatic }), ], debug=False, template_path=webstatic, log_function=self.log, default_handler_class=ErrorHandler, default_handler_args={ 'status_code': 404, }) ssl_options = None if key and cert: ssl_options = create_default_context(certfile=cert, keyfile=key, server_side=True) self.ssl = True self.app.listen(self.port, address=self.hostname, ssl_options=ssl_options) self._ioloop = tornado.ioloop.IOLoop.instance() self._thread = threading.Thread(target=self._ioloop.start) self._thread.daemon = True self._thread.start() self._registered = {} def stop(self): self._ioloop.stop() self._ioloop = None self._thread = None for (_, _, cleanup) in self._registered.itervalues(): if cleanup: cleanup() self.mappings = {} self.aliases = {} if self.preserve_payloads: return for filepath in self.served_files: if path.isfile(filepath): unlink(filepath) def get_random_path_at_webroot(self): while True: filename = ''.join(random.choice( string.ascii_uppercase + \ string.ascii_lowercase + \ string.digits) for _ in range(10)) filepath = path.join(self.root, filename) if not path.isfile(filepath): return filepath, filename def random_path(self): return '/'+''.join( random.choice( string.ascii_uppercase + \ string.ascii_lowercase + \ string.digits) for _ in range(10)) def register_mapping(self, name): name = self.random_path() self.mappings[name] = path if name in self.mappings: del self.mappings[name] def is_registered(self, name): return self._registered.get(name, (None, None, None))[0] def serve_content(self, content, alias=None, as_file=True): uri = None if as_file: filepath, filename = self.get_random_path_at_webroot() try: with open(filepath, 'w') as out: out.write(content) self.served_files.add(filepath) except: if path.isfile(filepath): path.unlink(filepath) raise uri = self.wwwroot + '/' + filename else: uri = self.random_path() self.app.add_handlers('.*', [(uri, StaticTextHandler, { 'content': content })]) if alias: self.aliases[uri] = alias return uri def start_webplugin(self, name, web_handlers, cleanup=None): random_path = self.random_path() if name in self._registered: random_path, _, _ = self._registered[name] return self.port, random_path klasses = [] for tab in web_handlers: if len(tab) == 2: uri_path, handler = tab kwargs = {} else: uri_path, handler, kwargs = tab ends_with_slash = uri_path.endswith('/') uri_path = '/'.join(x for x in [random_path] + uri_path.split('/') if x) if ends_with_slash: uri_path += '/' klasses.append(handler) if issubclass(handler, (ErrorHandler, WebSocketHandler, RequestHandler, StaticTextHandler, PayloadsHandler, IndexHandler)): kwargs['config'] = self.config self.app.add_handlers(".*", [(uri_path, handler, kwargs)]) self.pupsrv.info('Register webhook for {} at {}'.format( name, uri_path)) self._registered[name] = random_path, klasses, cleanup return self.port, random_path def stop_webplugin(self, name): if name not in self._registered: return self.pupsrv.info('Unregister webhook for {}'.format(name)) random_path, klasses, cleanup = self._registered[name] removed = False to_remove = [] for rule in self.app.wildcard_router.rules: if rule.target in klasses: to_remove.append(rule) removed = True elif rule.matcher.regex.pattern.startswith(random_path): to_remove.append(rule) removed = True for rule in to_remove: self.app.wildcard_router.rules.remove(rule) to_remove = [] for rule in self.app.default_router.rules: if rule.target in klasses: to_remove.append(rule) removed = True elif rule.matcher.regex.pattern.startswith(random_path): to_remove.append(rule) removed = True if cleanup: cleanup() if removed: del self._registered[name] else: self.pupsrv.info('{} was not found [error]'.format(name))
def runCouchPotato(options, base_path, args, data_dir=None, log_dir=None, Env=None, desktop=None): try: locale.setlocale(locale.LC_ALL, "") encoding = locale.getpreferredencoding() except (locale.Error, IOError): encoding = None # for OSes that are poorly configured I'll just force UTF-8 if not encoding or encoding in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'): encoding = 'UTF-8' Env.set('encoding', encoding) # Do db stuff db_path = sp(os.path.join(data_dir, 'database')) old_db_path = os.path.join(data_dir, 'couchpotato.db') # Remove database folder if both exists if os.path.isdir(db_path) and os.path.isfile(old_db_path): db = SuperThreadSafeDatabase(db_path) db.open() db.destroy() # Check if database exists db = SuperThreadSafeDatabase(db_path) db_exists = db.exists() if db_exists: # Backup before start and cleanup old backups backup_path = sp(os.path.join(data_dir, 'db_backup')) backup_count = 5 existing_backups = [] if not os.path.isdir(backup_path): os.makedirs(backup_path) for root, dirs, files in os.walk(backup_path): # Only consider files being a direct child of the backup_path if root == backup_path: for backup_file in sorted(files): ints = re.findall('\d+', backup_file) # Delete non zip files if len(ints) != 1: os.remove(os.path.join(root, backup_file)) else: existing_backups.append((int(ints[0]), backup_file)) else: # Delete stray directories. shutil.rmtree(root) # Remove all but the last 5 for eb in existing_backups[:-backup_count]: os.remove(os.path.join(backup_path, eb[1])) # Create new backup new_backup = sp( os.path.join(backup_path, '%s.tar.gz' % int(time.time()))) zipf = tarfile.open(new_backup, 'w:gz') for root, dirs, files in os.walk(db_path): for zfilename in files: zipf.add(os.path.join(root, zfilename), arcname='database/%s' % os.path.join(root[len(db_path) + 1:], zfilename)) zipf.close() # Open last db.open() else: db.create() # Force creation of cachedir log_dir = sp(log_dir) cache_dir = sp(os.path.join(data_dir, 'cache')) python_cache = sp(os.path.join(cache_dir, 'python')) if not os.path.exists(cache_dir): os.mkdir(cache_dir) if not os.path.exists(python_cache): os.mkdir(python_cache) # Register environment settings Env.set('app_dir', sp(base_path)) Env.set('data_dir', sp(data_dir)) Env.set('log_path', sp(os.path.join(log_dir, 'CouchPotato.log'))) Env.set('db', db) Env.set('http_opener', requests.Session()) Env.set('cache_dir', cache_dir) Env.set('cache', FileSystemCache(python_cache)) Env.set('console_log', options.console_log) Env.set('quiet', options.quiet) Env.set('desktop', desktop) Env.set('daemonized', options.daemon) Env.set('args', args) Env.set('options', options) # Determine debug debug = options.debug or Env.setting('debug', default=False, type='bool') Env.set('debug', debug) # Development development = Env.setting('development', default=False, type='bool') Env.set('dev', development) # Disable logging for some modules for logger_name in [ 'enzyme', 'guessit', 'subliminal', 'apscheduler', 'tornado', 'requests' ]: logging.getLogger(logger_name).setLevel(logging.ERROR) for logger_name in ['gntp']: logging.getLogger(logger_name).setLevel(logging.WARNING) # Disable SSL warning disable_warnings() # Use reloader reloader = debug is True and development and not Env.get( 'desktop') and not options.daemon # Logger logger = logging.getLogger() formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') level = logging.DEBUG if debug else logging.INFO logger.setLevel(level) logging.addLevelName(19, 'INFO') # To screen if (debug or options.console_log) and not options.quiet and not options.daemon: hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(formatter) logger.addHandler(hdlr) # To file hdlr2 = handlers.RotatingFileHandler(Env.get('log_path'), 'a', 500000, 10, encoding=Env.get('encoding')) hdlr2.setFormatter(formatter) logger.addHandler(hdlr2) # Start logging & enable colors # noinspection PyUnresolvedReferences import color_logs from couchpotato.core.logger import CPLog log = CPLog(__name__) log.debug('Started with options %s', options) # Check available space try: total_space, available_space = getFreeSpace(data_dir) if available_space < 100: log.error( 'Shutting down as CP needs some space to work. You\'ll get corrupted data otherwise. Only %sMB left', available_space) return except: log.error('Failed getting diskspace: %s', traceback.format_exc()) def customwarn(message, category, filename, lineno, file=None, line=None): log.warning('%s %s %s line:%s', (category, message, filename, lineno)) warnings.showwarning = customwarn # Create app from couchpotato import WebHandler web_base = ('/' + Env.setting('url_base').lstrip('/') + '/') if Env.setting('url_base') else '/' Env.set('web_base', web_base) api_key = Env.setting('api_key') if not api_key: api_key = uuid4().hex Env.setting('api_key', value=api_key) api_base = r'%sapi/%s/' % (web_base, api_key) Env.set('api_base', api_base) # Basic config host = Env.setting('host', default='0.0.0.0') # app.debug = development config = { 'use_reloader': reloader, 'port': tryInt(Env.setting('port', default=5050)), 'host': host if host and len(host) > 0 else '0.0.0.0', 'ssl_cert': Env.setting('ssl_cert', default=None), 'ssl_key': Env.setting('ssl_key', default=None), } # Load the app application = Application( [], log_function=lambda x: None, debug=config['use_reloader'], gzip=True, cookie_secret=api_key, login_url='%slogin/' % web_base, ) Env.set('app', application) # Request handlers application.add_handlers( ".*$", [ (r'%snonblock/(.*)(/?)' % api_base, NonBlockHandler), # API handlers (r'%s(.*)(/?)' % api_base, ApiHandler), # Main API handler (r'%sgetkey(/?)' % web_base, KeyHandler), # Get API key (r'%s' % api_base, RedirectHandler, { "url": web_base + 'docs/' }), # API docs # Login handlers (r'%slogin(/?)' % web_base, LoginHandler), (r'%slogout(/?)' % web_base, LogoutHandler), # Catch all webhandlers (r'%s(.*)(/?)' % web_base, WebHandler), (r'(.*)', WebHandler), ]) # Static paths static_path = '%sstatic/' % web_base for dir_name in ['fonts', 'images', 'scripts', 'style']: application.add_handlers( ".*$", [('%s%s/(.*)' % (static_path, dir_name), StaticFileHandler, { 'path': sp(os.path.join(base_path, 'couchpotato', 'static', dir_name)) })]) Env.set('static_path', static_path) # Load configs & plugins loader = Env.get('loader') loader.preload(root=sp(base_path)) loader.run() # Fill database with needed stuff fireEvent('database.setup') if not db_exists: fireEvent('app.initialize', in_order=True) fireEvent('app.migrate') # Go go go! from tornado.ioloop import IOLoop from tornado.autoreload import add_reload_hook loop = IOLoop.current() # Reload hook def reload_hook(): fireEvent('app.shutdown') add_reload_hook(reload_hook) # Some logging and fire load event try: log.info('Starting server on port %(port)s', config) except: pass fireEventAsync('app.load') ssl_options = None if config['ssl_cert'] and config['ssl_key']: ssl_options = { 'certfile': config['ssl_cert'], 'keyfile': config['ssl_key'], } server = HTTPServer(application, no_keep_alive=True, ssl_options=ssl_options) try_restart = True restart_tries = 5 while try_restart: try: server.listen(config['port'], config['host']) loop.start() server.close_all_connections() server.stop() loop.close(all_fds=True) except Exception as e: log.error('Failed starting: %s', traceback.format_exc()) try: nr, msg = e if nr == 48: log.info( 'Port (%s) needed for CouchPotato is already in use, try %s more time after few seconds', (config.get('port'), restart_tries)) time.sleep(1) restart_tries -= 1 if restart_tries > 0: continue else: return except ValueError: return except: pass raise try_restart = False
class HTTPD(object): # noqa """ HTTP Server implementation that attaches to an event loop and socket, and is capable of handling mesos wire protocol messages. """ def __init__(self, sock, loop): """ Construct an HTTP server on a socket given an ioloop. """ self.loop = loop self.sock = sock self.app = Application(handlers=[(r'/.*$', Blackhole)]) self.server = HTTPServer(self.app, io_loop=self.loop) self.server.add_sockets([sock]) self.sock.listen(1024) def terminate(self): log.info('Terminating HTTP server and all connections') self.server.close_all_connections() self.sock.close() def mount_process(self, process): """ Mount a Process onto the http server to receive message callbacks. """ for route_path in process.route_paths: route = '/%s%s' % (process.pid.id, route_path) log.info('Mounting route %s' % route) self.app.add_handlers('.*$', [( re.escape(route), RoutedRequestHandler, dict(process=process, path=route_path) )]) for message_name in process.message_names: route = '/%s/%s' % (process.pid.id, message_name) log.info('Mounting message handler %s' % route) self.app.add_handlers('.*$', [( re.escape(route), WireProtocolMessageHandler, dict(process=process, name=message_name) )]) def unmount_process(self, process): """ Unmount a process from the http server to stop receiving message callbacks. """ # There is no remove_handlers, but .handlers is public so why not. server.handlers is a list of # 2-tuples of the form (host_pattern, [list of RequestHandler]) objects. We filter out all # handlers matching our process from the RequestHandler list for each host pattern. def nonmatching(handler): return 'process' not in handler.kwargs or handler.kwargs['process'] != process def filter_handlers(handlers): host_pattern, handlers = handlers return (host_pattern, list(filter(nonmatching, handlers))) self.app.handlers = [filter_handlers(handlers) for handlers in self.app.handlers]
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'%s/api/%s' % (sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=True, autoreload=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=', login_url='/login/', ) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'%s(/?.*)' % self.options['api_root'], ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % self.options['web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), # webui login/logout handlers (r'%s/login(/?.*)' % self.options['web_root'], LoginHandler), (r'%s/logout(/?.*)' % self.options['web_root'], LogoutHandler), # webui redirect (r'/', RedirectHandler, {"url": self.options['web_root'] + '/home/'}), # webui handlers ] + route.get_routes(self.options['web_root'])) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'%s/(favicon\.ico)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'%s/images/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'images')}), # cached images (r'%s/cache/images/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(sickbeard.CACHE_DIR, 'images')}), # css (r'%s/css/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'js')}), # videos ] + [(r'%s/videos/(.*)' % self.options['web_root'], StaticFileHandler, {"path": self.video_root})]) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, ssl_options={"certfile": self.https_cert, "keyfile": self.https_key}) else: protocol = "http" self.server = HTTPServer(self.app) logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str( self.options['port']) + "/") try: self.server.listen(self.options['port'], self.options['host']) except: etype, evalue, etb = sys.exc_info() logger.log( "Could not start webserver on %s. Excpeption: %s, Error: %s" % (self.options['port'], etype, evalue), logger.ERROR) return try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
def initWebServer(options={}): options.setdefault('port', 8081) options.setdefault('host', '0.0.0.0') options.setdefault('log_dir', None) options.setdefault('username', '') options.setdefault('password', '') options.setdefault('web_root', '/') assert isinstance(options['port'], int) assert 'data_root' in options def http_error_401_hander(status, message, traceback, version): """ Custom handler for 401 error """ if status != "401 Unauthorized": logger.log(u"Tornado caught an error: %s %s" % (status, message), logger.ERROR) logger.log(traceback, logger.DEBUG) return r'''<!DOCTYPE html> <html> <head> <title>%s</title> </head> <body> <br/> <font color="#0000FF">Error %s: You need to provide a valid username and password.</font> </body> </html> ''' % ('Access denied', status) def http_error_404_hander(status, message, traceback, version): """ Custom handler for 404 error, redirect back to main page """ return r'''<!DOCTYPE html> <html> <head> <title>404</title> <script type="text/javascript" charset="utf-8"> <!-- location.href = "%s/home/" //--> </script> </head> <body> <br/> </body> </html> ''' % options['web_root'] # tornado setup enable_https = options['enable_https'] https_cert = options['https_cert'] https_key = options['https_key'] if enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)): if not create_https_certificates(https_cert, https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False enable_https = False if not (os.path.exists(https_cert) and os.path.exists(https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False enable_https = False # Load the app app = Application([], debug=True, gzip=True, autoreload=True, xheaders=False, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=', login_url='/login' ) # Index Handler app.add_handlers(".*$", [ (r"/", RedirectHandler, {'url': '/home/'}), (r'/login', webserve.LoginHandler), (r'/api/(.*)(/?)', webapi.Api), (r'%s(.*)(/?)' % options['web_root'], webserve.IndexHandler) ]) # Static Path Handler app.add_handlers(".*$", [ ('%s/%s/(.*)([^/]*)' % (options['web_root'], 'images'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images'), os.path.join(sickbeard.CACHE_DIR, 'images'), os.path.join(sickbeard.CACHE_DIR, 'images', 'thumbnails')]}), ('%s/%s/(.*)([^/]*)' % (options['web_root'], 'css'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'css')]}), ('%s/%s/(.*)([^/]*)' % (options['web_root'], 'js'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'js')]}) ]) global server if enable_https: protocol = "https" server = HTTPServer(app, no_keep_alive=True, ssl_options={"certfile": https_cert, "keyfile": https_key}) else: protocol = "http" server = HTTPServer(app, no_keep_alive=True) logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str( options['port']) + "/") server.listen(options['port'], options['host'])
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.name = "TORNADO" self.alive = True self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options[b'port'], int) assert 'gui_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options[b'web_root']: sickbeard.WEB_ROOT = self.options[b'web_root'] = ('/' + self.options[b'web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options[b'api_root'] = r'%s/api/%s' % (sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options[b'enable_https'] self.https_cert = self.options[b'https_cert'] self.https_key = self.options[b'https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logging.info("Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and ek(os.path.exists, self.https_key)): logging.warning("Disabled HTTPS because of missing CERT and KEY files") sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=sickbeard.DEBUG, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='%s/login/' % self.options[b'web_root'], ) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'%s(/?.*)' % self.options[b'api_root'], ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % self.options[b'web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options[b'web_root'], RedirectHandler, {"url": self.options[b'web_root'] + '/apibuilder/'}), # webui login/logout handlers (r'%s/login(/?)' % self.options[b'web_root'], LoginHandler), (r'%s/logout(/?)' % self.options[b'web_root'], LogoutHandler), # webui handlers ] + route.get_routes(self.options[b'web_root'])) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % self.options[b'web_root'], CalendarHandler), ]) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'%s/(favicon\.ico)' % self.options[b'web_root'], StaticFileHandler, {"path": ek(os.path.join, self.options[b'gui_root'], 'images/ico/favicon.ico')}), # images (r'%s.*?/images/(.*)' % self.options[b'web_root'], StaticImageHandler, {"path": ek(os.path.join, self.options[b'gui_root'], 'images')}), # css (r'%s/css/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": ek(os.path.join, self.options[b'gui_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": ek(os.path.join, self.options[b'gui_root'], 'js')}), # videos ] + [(r'%s/videos/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": self.video_root})]) def run(self): protocol = 'http' self.server = HTTPServer(self.app) if self.enable_https: protocol = 'https' self.server.ssl_options={"certfile": self.https_cert, "keyfile": self.https_key} logging.info("Starting SiCKRAGE web server on [{}://{}:{}/]".format(protocol, self.options[b'host'], self.options[b'port'])) try: self.server.listen(self.options[b'port'], self.options[b'host']) except: logging.info("Could not start webserver on port %s, already in use!" % self.options[b'port']) os._exit(1) if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMONIZE: self.io_loop.add_callback(sickbeard.launchBrowser, protocol, sickbeard.WEB_PORT, sickbeard.WEB_ROOT) try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): pass def shutDown(self): self.alive = False self.io_loop.stop()
class SRWebServer(object): def __init__(self, **kwargs): self.running = True self.restart = False self.io_loop = IOLoop.instance() self.options = {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') self.options.setdefault('stop_timeout', 3) self.options.update(kwargs) # video root if sickrage.ROOT_DIRS: root_dirs = sickrage.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options[b'web_root']: sickrage.WEB_ROOT = self.options[b'web_root'] = ('/' + self.options[b'web_root'].lstrip('/').strip('/')) # api root if not sickrage.API_KEY: sickrage.API_KEY = generateApiKey() self.options[b'api_root'] = r'%s/api/%s' % (sickrage.WEB_ROOT, sickrage.API_KEY) # tornado setup self.enable_https = self.options[b'enable_https'] self.https_cert = self.options[b'https_cert'] self.https_key = self.options[b'https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): sickrage.LOGGER.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): sickrage.LOGGER.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=sickrage.DEBUG, autoreload=False, gzip=sickrage.WEB_USE_GZIP, xheaders=sickrage.HANDLE_REVERSE_PROXY, cookie_secret=sickrage.WEB_COOKIE_SECRET, login_url='%s/login/' % self.options[b'web_root'], ) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'%s(/?.*)' % self.options[b'api_root'], ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % self.options[b'web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options[b'web_root'], RedirectHandler, {"url": self.options[b'web_root'] + '/apibuilder/'}), # webui login/logout handlers (r'%s/login(/?)' % self.options[b'web_root'], LoginHandler), (r'%s/logout(/?)' % self.options[b'web_root'], LogoutHandler), # webui handlers ] + route.get_routes(self.options[b'web_root'])) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % self.options[b'web_root'], CalendarHandler), ]) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'%s/(favicon\.ico)' % self.options[b'web_root'], StaticFileHandler, {"path": os.path.join(self.options[b'gui_root'], 'images/ico/favicon.ico')}), # images (r'%s.*?/images/(.*)' % self.options[b'web_root'], StaticImageHandler, {"path": os.path.join(self.options[b'gui_root'], 'images')}), # css (r'%s/css/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": os.path.join(self.options[b'gui_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": os.path.join(self.options[b'gui_root'], 'js')}), # videos ] + [(r'%s/videos/(.*)' % self.options[b'web_root'], StaticFileHandler, {"path": self.video_root})]) # daemonize sickrage if sickrage.DAEMONIZE: import daemon ctx = daemon.DaemonContext() ctx.initgroups = False ctx.open() # write sickrage pidfile sickrage.PID = os.getpid() if sickrage.CREATEPID: with file(sickrage.PIDFILE, 'w+') as pf: pf.write(str(sickrage.PID)) self.io_loop.add_callback(sickrage.Scheduler.start) def start(self): threading.currentThread().setName("TORNADO") try: self.server = HTTPServer(self.app) if self.enable_https: self.server.ssl_options = {"certfile": self.https_cert, "keyfile": self.https_key} self.server.listen(self.options[b'port'], self.options[b'host']) # start tornado web server from sickrage.core.helpers import get_lan_ip sickrage.LOGGER.info("Starting SiCKRAGE web server on [{}://{}:{}/]".format( ('http', 'https')[sickrage.ENABLE_HTTPS], get_lan_ip(), sickrage.WEB_PORT)) # launch browser window if sickrage.LAUNCH_BROWSER and not any([sickrage.WEB_NOLAUNCH, sickrage.DAEMONIZE]): sickrage.LOGGER.info("Launching browser window") threading.Thread(None, lambda: launch_browser(('http', 'https')[sickrage.ENABLE_HTTPS], sickrage.WEB_PORT, sickrage.WEB_ROOT)).start() sickrage.STARTED = True self.io_loop.start() except (KeyboardInterrupt, SystemExit) as e: sickrage.LOGGER.info('PERFORMING SHUTDOWN') except Exception as e: sickrage.LOGGER.info("TORNADO failed to start: {}".format(e)) finally: self.server_shutdown() sickrage.LOGGER.shutdown() @staticmethod def remove_pid_file(): try: if os.path.exists(sickrage.PIDFILE): os.remove(sickrage.PIDFILE) except (IOError, OSError): pass def server_restart(self): sickrage.LOGGER.info('PERFORMING RESTART') import tornado.autoreload tornado.autoreload.add_reload_hook(self.server_shutdown) tornado.autoreload.start() tornado.autoreload._reload() def server_shutdown(self): self.server.stop() if self.running: self.io_loop.stop() # shutdown sickrage if sickrage.STARTED: sickrage.core.halt() sickrage.core.saveall() if sickrage.DAEMONIZE and sickrage.PIDFILE: self.remove_pid_file() sickrage.LOGGER.info('SHUTDOWN/RESTART COMPLETED!')
class srWebServer(object): def __init__(self): self.io_loop = IOLoop.instance() self.running = True self.restart = False self.open_browser = False self.port = sickrage.srConfig.WEB_PORT self.host = sickrage.srConfig.WEB_HOST # video root if sickrage.srConfig.ROOT_DIRS: root_dirs = sickrage.srConfig.ROOT_DIRS.split("|") self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if sickrage.srConfig.WEB_ROOT: sickrage.srConfig.WEB_ROOT = sickrage.srConfig.WEB_ROOT = "/" + sickrage.srConfig.WEB_ROOT.lstrip( "/" ).strip("/") # api root if not sickrage.srConfig.API_KEY: sickrage.srConfig.API_KEY = generateApiKey() self.api_root = r"%s/api/%s" % (sickrage.srConfig.WEB_ROOT, sickrage.srConfig.API_KEY) # tornado setup if sickrage.srConfig.ENABLE_HTTPS: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (sickrage.srConfig.HTTPS_CERT and os.path.exists(sickrage.srConfig.HTTPS_CERT)) or not ( sickrage.srConfig.HTTPS_KEY and os.path.exists(sickrage.srConfig.HTTPS_KEY) ): if not create_https_certificates(sickrage.srConfig.HTTPS_CERT, sickrage.srConfig.HTTPS_KEY): sickrage.srLogger.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.srConfig.ENABLE_HTTPS = False if not (os.path.exists(sickrage.srConfig.HTTPS_CERT) and os.path.exists(sickrage.srConfig.HTTPS_KEY)): sickrage.srLogger.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.srConfig.ENABLE_HTTPS = False # Load the app self.app = Application( [], debug=sickrage.srConfig.DEBUG, autoreload=False, gzip=sickrage.srConfig.WEB_USE_GZIP, xheaders=sickrage.srConfig.HANDLE_REVERSE_PROXY, cookie_secret=sickrage.srConfig.WEB_COOKIE_SECRET, login_url="%s/login/" % sickrage.srConfig.WEB_ROOT, ) # Main Handlers self.app.add_handlers( ".*$", [ # webapi handler (r"%s(/?.*)" % self.api_root, ApiHandler), # webapi key retrieval (r"%s/getkey(/?.*)" % sickrage.srConfig.WEB_ROOT, KeyHandler), # webapi builder redirect ( r"%s/api/builder" % sickrage.srConfig.WEB_ROOT, RedirectHandler, {"url": sickrage.srConfig.WEB_ROOT + "/apibuilder/"}, ), # webui login/logout handlers (r"%s/login(/?)" % sickrage.srConfig.WEB_ROOT, LoginHandler), (r"%s/logout(/?)" % sickrage.srConfig.WEB_ROOT, LogoutHandler), # webui handlers ] + Route.get_routes(sickrage.srConfig.WEB_ROOT), ) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers(".*$", [(r"%s/calendar" % sickrage.srConfig.WEB_ROOT, CalendarHandler)]) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon ( r"%s/(favicon\.ico)" % sickrage.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srConfig.GUI_DIR, "images/ico/favicon.ico")}, ), # images ( r"%s.*?/images/(.*)" % sickrage.srConfig.WEB_ROOT, StaticImageHandler, {"path": os.path.join(sickrage.srConfig.GUI_DIR, "images")}, ), # css ( r"%s/css/(.*)" % sickrage.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srConfig.GUI_DIR, "css")}, ), # javascript ( r"%s/js/(.*)" % sickrage.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srConfig.GUI_DIR, "js")}, ), # videos ] + [(r"%s/videos/(.*)" % sickrage.srConfig.WEB_ROOT, StaticFileHandler, {"path": self.video_root})], ) def start(self): threading.currentThread().setName("TORNADO") try: self.server = HTTPServer(self.app) if sickrage.srConfig.ENABLE_HTTPS: self.server.ssl_options = { "certfile": sickrage.srConfig.HTTPS_CERT, "keyfile": sickrage.srConfig.HTTPS_KEY, } self.server.listen(self.port, self.host) # launch browser window if self.open_browser: threading.Thread( None, lambda: launch_browser( ("http", "https")[sickrage.srConfig.ENABLE_HTTPS], sickrage.srConfig.WEB_PORT, sickrage.srConfig.WEB_ROOT, ), ).start() sickrage.srConfig.STARTED = True sickrage.srLogger.info( "SiCKRAGE STARTED :: VERSION:[{}] CONFIG:[{}] URL:[{}://{}:{}/]".format( sickrage.srCore.VERSION, sickrage.srConfig.CONFIG_FILE, ("http", "https")[sickrage.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srConfig.WEB_PORT, ) ) self.io_loop.start() except (KeyboardInterrupt, SystemExit) as e: sickrage.srLogger.info("PERFORMING SHUTDOWN") except Exception as e: sickrage.srLogger.info("TORNADO failed to start: {}".format(e.message)) finally: self.server_shutdown() sickrage.srLogger.shutdown() def server_restart(self): sickrage.srLogger.info("PERFORMING RESTART") import tornado.autoreload tornado.autoreload.add_reload_hook(self.server_shutdown) tornado.autoreload.start() tornado.autoreload._reload() def server_shutdown(self): self.server.stop() if self.running: self.io_loop.stop() # shutdown sickrage if sickrage.srCore.STARTED: sickrage.srCore.halt() sickrage.srCore.save_all() sickrage.srLogger.info("SHUTDOWN COMPLETED!")
class FlaskyApp(object): """The Bant object provides a convenient API to configure :class:`~tornado.web.Application` It acts as a container for all plugins, crosscutting concern functions, objects and settings. On invocation of run method, Bant instance registers :class:`~flasky.handler.DynamicHandler` classes for every endpoint that is defined by user. Bant instance might be created at the :file:`__init__.py` of your package. Another approach might be creating initialization closures to init API functions. For more information please refer to :ref: :: from bant import Bant app = Bant(**settings) :param ioloop: IOLoop that will be used by application. By default Bant application uses :class:`asyncio.BaseEventLoop` :param settings: Application specific settings. """ #: The class for used as object container #: See :class:`~flasky.DIContainer` #: TODO: This fill be deleted di_class = DIContainer #: The class for used as parameter resolver #: See :class: `~flasky.parameters.ParameterResolver` #: TODO: This will be deleted parameter_resolver_class = ParameterResolver #: The class for used as cache manager #: See :class: `~flasky.cache.CacheManager` #: TODO: This will be deleted cache_manager_class = CacheManager #: Default plugins which will be loaded at the initialization of FlaskyApp. #: Users might manipulate the value of this list and prevent the loading of #: module default_plugins = ["di", "parameters", "caches"] #: The name of the logger to use. logger_name = "flasky.logger" def __init__(self, ioloop=None, **settings): #: :class:`tornado.ioloop.IOLoop` which will be used to run #: application. Default ioloop class is #: :class:`tornado.asyncio.AsyncIOMainLoop` self.ioloop = ioloop or self._get_ioloop() #: Please look :meth:`~build_app` self.is_builded = False #: The debug flag. This flag will be passed to tornado.Application #: If the debug flag set True, Application will be reload on every #: if code changes detected and Application will spit detailed #: logging information. self.debug = False #: Configuration dictionary, there might be better implementation for #: this in future versions self.settings = settings #: :class:`tornado.web.Application` instance for this flaskyApp. #: This instance will be injected to :class:`handler.DynamicHandler` #: TODO: change this variable name to tornado_app self.app = None #: List of functions which will be runned before any request #: will be handled. This functions might be used #: for any initialization routine self.on_start_funcs = [] #: A list of functions which will be called at the beginning of the #: request. Before request functions can be used to perform common #: cross-cutting concerns (logging, authorization etc.) #: To register a function use the :meth:`before_request` decorator self.before_request_funcs = [] #: A list of functions which will be called after the request handled #: by handler function. Function should take 2 parameters, a #: :class:`~tornado.web.RequestHandler` instance and endpoint #: definition. #: #: **Warning**: These functions will not be called if any error occurs #: during the execution of handler. To run piece of code after request #: in every circumstances please please check :meth:`teardown_request` #: #: To register a function use the :meth:`after_request` decorator self.after_request_funcs = [] #: A list of functions which will be called after request is handled #: this function will be called even if any error exists on handler #: function. This method can :meth:`on_teardown_request` self.teardown_request_funcs = [] #: A error specific handler registry. Key will be #: type of error and None type will be used as #: default error handler. #: #: To register an error handler, use the :meth:`error_handler` #: decorator self.error_handlers = {None: default_error_handler_func} #: Registered handler definitions #: All handlers registered under a tree which path might be accessed #: like this:: #: #: handler_function = #: self.host_definitions['0.0.0.0']['/api/token']['POST'] #: self.host_definitions = OrderedDict() #: Registered static file handlers, on build time this definitions #: will be converted to :class:`tornado.web.StaticFileHandler`. You #: can serve static file like this:: #: #: from flasky.app import FlaskyApp #: #: app = FlaskyApp() #: app.serve_static_file("/static/([^/]+)", "path/to/static/file") #: self.static_file_handler_definitions = [] #: Executor which will be injected to handlers, max_worker_count of #: settings might be used to manage size of ThreadPoolExecutor. #: Default value is 1 self.executor = ThreadPoolExecutor( max_workers=(settings.get('max_worker_count', None) or 1)) #: Built-in plugins #: TODO: Implement plugin mechanisms self.di = self.di_class(self) self.parameter_resolver = self.parameter_resolver_class(self) self.cache = self.cache_manager_class(self) self.scheduler = Scheduler(self.ioloop) def _get_ioloop(self): from tornado.platform.asyncio import AsyncIOMainLoop if not hasattr(IOLoop, "_instance"): AsyncIOMainLoop().install() return IOLoop.current() def api(self, host='.*$', endpoint=None, method=None, **kwargs): """A decorator which will be used to register a handler for given endpoint. Parameters are default :class:`tornado.web.RequestHandler` parameters. Regexes can be used at endpoint definition and will be injected to as second parameter:: @app.api( endpoint="/api/user", method="POST" ) async def create_user(handler, *args, **kwargs): handler.write("hello world") @app.api( endpoint="api/user", method="GET" ) async def get_users(handler, *args, **kwargs): handler.write({"user": "******"}) :param host: Host Address in which this handler function will be registered. :param endpoint: Endpoint address for which this handler function will be executed. :param method: HTTP Method(POST, GET, PUT etc..) for which this handler function will be executed :param options:" Options that will be attached to handler function all the way throught middleware pipeline """ host_definition = self.host_definitions.get(host, None) if host_definition is None: host_definition = OrderedDict() self.host_definitions[host] = host_definition endpoint_definition = self.host_definitions[host].get(endpoint, None) if endpoint_definition is None: endpoint_definition = OrderedDict() for supported_method in DynamicHandler.SUPPORTED_METHODS: endpoint_definition[supported_method] = OrderedDict() host_definition[endpoint] = endpoint_definition def decorator(f): if not iscoroutinefunction(f): raise ConfigurationError( message="Function [{}] should be" "coroutine in order to use.".format(f.__name__)) if not endpoint: raise ConfigurationError( message='Endpoint should be provided.') if not method: raise ConfigurationError( message='Endpoint method(GET, POST etc..)' 'should be provided') if method not in DynamicHandler.SUPPORTED_METHODS: raise ConfigurationError( message='Unsuppoterted method {}'.format(method)) self.host_definitions[host][endpoint][method] = {'function': f} self.host_definitions[host][endpoint][method].update(kwargs) return f return decorator def on_start(self, f): """ Registers a function to be run on build time of application Function should take :class:BantApp as parameter. This is a good place to initialize the common objects, setting up logging vs:: @app.on_start async def initialize(app): app.logger = logging.get("application_logger") """ self.on_start_funcs.append(f) return f def before_request(self, f): """Registers a function to run before each request handler. Function should take 2 argument, an instance of :class:`~handler.DynamicHandler` and dictionary of endpoint definition:: @app.before_request async def check_authorization(handler, handler_definition): if not handler.request.headers.get('Authorization', None): raise UnauthorizedAccessError() """ self.before_request_funcs.append(f) return f def after_request(self, f): """Registers a function to run after each request handler. Function should take 2 argument an instance of :class:`handler.DynamicHandler` and :type:`dict` of endpoint definition:: @app.after_request async def add_cors_headers handler.set_header("Allow-Origin", "*") It's important to know that, if any error raised during pipeline, after request functions WILL NOT BE EXECUTED. To execute a function after pipeline in every circumstances, use :meth:`teardown_request` """ self.after_request_funcs.append(f) return f def error_handler(self, err_type=None): """Registers a function for given error type.:: @app.error_handler(MongoError) async def handle_mongo_error(handler, error, endpoint_definition): handler.clear() handler.write("Internal error occured") handler.set_status(500) :param err_type: an exception type which will be handled by this error handler function. If given parameter is None, handler will be used as default error handler. """ def decorator(f): self.error_handlers[err_type] = f return f return decorator def on_teardown_request(self, f): self.teardown_request_funcs.append(f) return f def serve_static_file(self, pattern, path): """Serves matched files from given path: app.serve_static_file("*.png", "/path/to/png/files") """ if not pattern: raise ValueError('Pattern should be specified...') if path is None: raise ValueError('Path should be specified.') self.static_file_handler_definitions.append((pattern, {'path': path})) def build_app(self, host="0.0.0.0"): """ Building application means, creation of :class:`tornado.web.Application` instance, creation of :class:`handler.DynamicHandler` routes, adding routes into tornado application, registration of :class:`tornado.web.StaticFileHandler`s """ self.app = Application(default_host=host, **self.settings) app_ctx = self._build_app_ctx() for host, host_definition in self.host_definitions.items(): handlers = [] for endpoint, endpoint_definition in host_definition.items(): handler = self._create_dynamic_handlers( host, endpoint, endpoint_definition, app_ctx) handlers.append(*handler[1]) self.app.add_handlers(host, handlers) for url_patttern, static_file_handler_settings \ in self.static_file_handler_definitions: self.app.add_handlers(".*$", [ (url_patttern, StaticFileHandler, static_file_handler_settings) ]) self.is_builded = True def _create_dynamic_handlers(self, host, endpoint, endpoint_definition, app_ctx): """ Creates dynamic handler and sets all pipeline functions as a dictionary which will be injected to dynamic handler. """ return host, [(endpoint, DynamicHandler, dict(endpoint_definition=endpoint_definition, endpoint=endpoint, after_request_funcs=self.after_request_funcs, error_handler_funcs=self.error_handlers, before_request_funcs=self.before_request_funcs, run_in_executor=self.run_in_executor, teardown_request_funcs=self.teardown_request_funcs, app_ctx=app_ctx))] def _build_app_ctx(self): """Creates application context object. This application context will be used by default plugins and will work as a namespace for external uses. Rationale behind this approach is that we don't want to clutter :class:`handler.DynamicHandler` instances. """ return ApplicationContext() def run(self, port=8888, host="0.0.0.0"): """Starts an HTTP Server on the given port and host. This function also executes :attr:`on_start_funcs` and calls run method of :class:`scheduler.Scheduler` """ if not self.is_builded: self.build_app(host=host) for on_start_func in self.on_start_funcs: self.ioloop.run_sync(functools.partial(on_start_func, self)) self.scheduler.run() self.app.listen(port) self.ioloop.start() def run_in_executor(self, func, *args): """runs given function in another thread. """ return futures.wrap_future(self.executor.submit( functools.partial(func, *args)), loop=self.ioloop.asyncio_loop) def add_tornado_handler(self, host_pattern, host_handlers): """ To add any handler which extends :class:`tornado.web.RequestHandler` class. :param host: Hostname to listen to :param handlers: list of tuples. Tuples are consist of URL pattern, handler class, handler settings. For more information please check :meth:`tornado.web.Application.add_handlers` """ self.app.add_handlers(host_pattern, host_handlers)
class WebServer(object): def __init__(self): super(WebServer, self).__init__() self.name = "TORNADO" self.daemon = True self.started = False self.video_root = None self.api_root = None self.app = None self.server = None def start(self): self.started = True # load languages tornado.locale.load_gettext_translations(sickrage.LOCALE_DIR, 'messages') # clear mako cache folder mako_cache = os.path.join(sickrage.app.cache_dir, 'mako') if os.path.isdir(mako_cache): shutil.rmtree(mako_cache) # video root if sickrage.app.config.root_dirs: root_dirs = sickrage.app.config.root_dirs.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] # web root if sickrage.app.config.web_root: sickrage.app.config.web_root = sickrage.app.config.web_root = ( '/' + sickrage.app.config.web_root.lstrip('/').strip('/')) # api root self.api_root = r'%s/api/%s' % (sickrage.app.config.web_root, sickrage.app.config.api_key) # tornado setup if sickrage.app.config.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not ( sickrage.app.config.https_cert and os.path.exists( sickrage.app.config.https_cert)) or not ( sickrage.app.config.https_key and os.path.exists(sickrage.app.config.https_key)): if not create_https_certificates(sickrage.app.config.https_cert, sickrage.app.config.https_key): sickrage.app.log.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.app.config.enable_https = False if not (os.path.exists(sickrage.app.config.https_cert) and os.path.exists( sickrage.app.config.https_key)): sickrage.app.log.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.app.config.enable_https = False # Load the app self.app = Application( debug=True, autoreload=False, gzip=sickrage.app.config.web_use_gzip, cookie_secret=sickrage.app.config.web_cookie_secret, login_url='%s/login/' % sickrage.app.config.web_root) # Websocket handler self.app.add_handlers(".*$", [ (r'%s/ws/ui' % sickrage.app.config.web_root, WebSocketUIHandler) ]) # Static File Handlers self.app.add_handlers('.*$', [ # api (r'%s(/?.*)' % self.api_root, ApiHandler), # redirect to home (r"(%s)" % sickrage.app.config.web_root, RedirectHandler, {"url": "%s/home" % sickrage.app.config.web_root}), # api key (r'%s/getkey(/?.*)' % sickrage.app.config.web_root, KeyHandler), # api builder (r'%s/api/builder' % sickrage.app.config.web_root, RedirectHandler, {"url": sickrage.app.config.web_root + '/apibuilder/'}), # login (r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler), # logout (r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler), # calendar (r'%s/calendar' % sickrage.app.config.web_root, CalendarHandler), # favicon (r'%s/(favicon\.ico)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images/favicon.ico')}), # images (r'%s/images/(.*)' % sickrage.app.config.web_root, StaticImageHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images')}), # css (r'%s/css/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'css')}), # scss (r'%s/scss/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'scss')}), # fonts (r'%s/fonts/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'fonts')}), # javascript (r'%s/js/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'js')}), # videos (r'%s/videos/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": self.video_root}), ]) # Web Handlers self.app.add_handlers('.*$', Route.get_routes(sickrage.app.config.web_root)) self.server = HTTPServer(self.app, xheaders=sickrage.app.config.handle_reverse_proxy) if sickrage.app.config.enable_https: self.server.ssl_options = { "certfile": sickrage.app.config.https_cert, "keyfile": sickrage.app.config.https_key } try: self.server.listen(sickrage.app.config.web_port) sickrage.app.log.info( "SiCKRAGE :: STARTED") sickrage.app.log.info( "SiCKRAGE :: VERSION:[{}]".format(sickrage.version())) sickrage.app.log.info( "SiCKRAGE :: CONFIG:[{}] [v{}]".format(sickrage.app.config_file, sickrage.app.config.config_version)) sickrage.app.log.info( "SiCKRAGE :: DATABASE:[v{}]".format(sickrage.app.main_db.version)) sickrage.app.log.info( "SiCKRAGE :: URL:[{}://{}:{}{}]".format(('http', 'https')[sickrage.app.config.enable_https], sickrage.app.config.web_host, sickrage.app.config.web_port, sickrage.app.config.web_root)) # launch browser window if all([not sickrage.app.no_launch, sickrage.app.config.launch_browser]) or sickrage.app.config.view_changelog: threading.Thread(None, lambda: launch_browser( ('http', 'https')[sickrage.app.config.enable_https], sickrage.app.config.web_host, sickrage.app.config.web_port ), name="LAUNCH-BROWSER").start() except socket.error as e: sickrage.app.log.warning(e.strerror) raise SystemExit def shutdown(self): if self.started: self.started = False self.server.close_all_connections() self.server.stop()
class srWebServer(object): def __init__(self): self.io_loop = IOLoop.instance() self.running = True self.restart = False self.open_browser = False self.port = sickrage.srConfig.WEB_PORT self.host = sickrage.srConfig.WEB_HOST # video root if sickrage.srConfig.ROOT_DIRS: root_dirs = sickrage.srConfig.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if sickrage.srConfig.WEB_ROOT: sickrage.srConfig.WEB_ROOT = sickrage.srConfig.WEB_ROOT = ( '/' + sickrage.srConfig.WEB_ROOT.lstrip('/').strip('/')) # api root if not sickrage.srConfig.API_KEY: sickrage.srConfig.API_KEY = generateApiKey() self.api_root = r'%s/api/%s' % (sickrage.srConfig.WEB_ROOT, sickrage.srConfig.API_KEY) # tornado setup if sickrage.srConfig.ENABLE_HTTPS: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (sickrage.srConfig.HTTPS_CERT and os.path.exists(sickrage.srConfig.HTTPS_CERT)) or not ( sickrage.srConfig.HTTPS_KEY and os.path.exists(sickrage.srConfig.HTTPS_KEY)): if not create_https_certificates(sickrage.srConfig.HTTPS_CERT, sickrage.srConfig.HTTPS_KEY): sickrage.srLogger.info( "Unable to create CERT/KEY files, disabling HTTPS") sickrage.srConfig.ENABLE_HTTPS = False if not (os.path.exists(sickrage.srConfig.HTTPS_CERT) and os.path.exists(sickrage.srConfig.HTTPS_KEY)): sickrage.srLogger.warning( "Disabled HTTPS because of missing CERT and KEY files") sickrage.srConfig.ENABLE_HTTPS = False # Load the app self.app = Application( [], debug=sickrage.srConfig.DEBUG, autoreload=False, gzip=sickrage.srConfig.WEB_USE_GZIP, xheaders=sickrage.srConfig.HANDLE_REVERSE_PROXY, cookie_secret=sickrage.srConfig.WEB_COOKIE_SECRET, login_url='%s/login/' % sickrage.srConfig.WEB_ROOT, ) # Main Handlers self.app.add_handlers( '.*$', [ # webapi handler (r'%s(/?.*)' % self.api_root, ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % sickrage.srConfig.WEB_ROOT, KeyHandler), # webapi builder redirect (r'%s/api/builder' % sickrage.srConfig.WEB_ROOT, RedirectHandler, { "url": sickrage.srConfig.WEB_ROOT + '/apibuilder/' }), # webui login/logout handlers (r'%s/login(/?)' % sickrage.srConfig.WEB_ROOT, LoginHandler), (r'%s/logout(/?)' % sickrage.srConfig.WEB_ROOT, LogoutHandler), # webui handlers ] + Route.get_routes(sickrage.srConfig.WEB_ROOT)) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % sickrage.srConfig.WEB_ROOT, CalendarHandler), ]) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon (r'%s/(favicon\.ico)' % sickrage.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srConfig.GUI_DIR, 'images/ico/favicon.ico') }), # images (r'%s.*?/images/(.*)' % sickrage.srConfig.WEB_ROOT, StaticImageHandler, { "path": os.path.join(sickrage.srConfig.GUI_DIR, 'images') }), # css (r'%s/css/(.*)' % sickrage.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srConfig.GUI_DIR, 'css') }), # javascript (r'%s/js/(.*)' % sickrage.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srConfig.GUI_DIR, 'js') }), # videos ] + [(r'%s/videos/(.*)' % sickrage.srConfig.WEB_ROOT, StaticFileHandler, { "path": self.video_root })]) def start(self): threading.currentThread().setName("TORNADO") try: self.server = HTTPServer(self.app) if sickrage.srConfig.ENABLE_HTTPS: self.server.ssl_options = { "certfile": sickrage.srConfig.HTTPS_CERT, "keyfile": sickrage.srConfig.HTTPS_KEY } self.server.listen(self.port, self.host) # launch browser window if self.open_browser: threading.Thread( None, lambda: launch_browser( ('http', 'https')[sickrage.srConfig.ENABLE_HTTPS], sickrage.srConfig.WEB_PORT, sickrage.srConfig.WEB_ROOT) ).start() sickrage.srConfig.STARTED = True sickrage.srLogger.info( "SiCKRAGE STARTED :: VERSION:[{}] CONFIG:[{}] URL:[{}://{}:{}/]" .format(sickrage.srCore.VERSION, sickrage.srConfig.CONFIG_FILE, ('http', 'https')[sickrage.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srConfig.WEB_PORT)) self.io_loop.start() except (KeyboardInterrupt, SystemExit) as e: sickrage.srLogger.info('PERFORMING SHUTDOWN') except Exception as e: sickrage.srLogger.info("TORNADO failed to start: {}".format( e.message)) finally: self.server_shutdown() sickrage.srLogger.shutdown() def server_restart(self): sickrage.srLogger.info('PERFORMING RESTART') import tornado.autoreload tornado.autoreload.add_reload_hook(self.server_shutdown) tornado.autoreload.start() tornado.autoreload._reload() def server_shutdown(self): self.server.stop() if self.running: self.io_loop.stop() # shutdown sickrage if sickrage.srCore.STARTED: sickrage.srCore.halt() sickrage.srCore.save_all() sickrage.srLogger.info('SHUTDOWN COMPLETED!')
class AppWebServer(threading.Thread): def __init__(self, options=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None self.io_loop = None # video root if app.ROOT_DIRS: root_dirs = app.ROOT_DIRS self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: app.WEB_ROOT = self.options['web_root'] = clean_url_path(self.options['web_root']) # Configure root to selected theme. app.WEB_ROOT = self.options['theme_path'] = clean_url_path(app.WEB_ROOT) # Configure the directory to the theme's data root. app.THEME_DATA_ROOT = self.options['theme_data_root'] = os.path.join(self.options['data_root'], app.THEME_NAME) # api root if not app.API_KEY: app.API_KEY = generate_api_key() self.options['api_root'] = r'{root}/api/(?:v1/)?{key}'.format(root=app.WEB_ROOT, key=app.API_KEY) self.options['api_v2_root'] = r'{root}/api/v2'.format(root=app.WEB_ROOT) # websocket root self.options['web_socket'] = r'{root}/ws'.format(root=app.WEB_ROOT) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): log.info('Unable to create CERT/KEY files, disabling HTTPS') app.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): log.warning('Disabled HTTPS because of missing CERT and KEY files') app.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=app.WEB_USE_GZIP, xheaders=app.HANDLE_REVERSE_PROXY, cookie_secret=app.WEB_COOKIE_SECRET, login_url=r'{root}/login/'.format(root=self.options['theme_path']), log_function=self.log_request, ) self.app.add_handlers('.*$', get_apiv2_handlers(self.options['api_v2_root'])) # Websocket handler self.app.add_handlers('.*$', [ (r'{base}/ui(/?.*)'.format(base=self.options['web_socket']), WebSocketUIHandler) ]) # Static File Handlers self.app.add_handlers('.*$', [ # favicon (r'{base}/favicon\.ico()'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'assets', 'img', 'ico', 'favicon.ico')}), # images (r'{base}/images/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'assets', 'img')}), # cached images (r'{base}/cache/images/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(app.CACHE_DIR, 'images')}), # css (r'{base}/css/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'assets', 'css')}), # javascript (r'{base}/js/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'assets', 'js')}), # fonts (r'{base}/fonts/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'assets', 'fonts')}), # videos (r'{base}/videos/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': self.video_root}), # vue dist (r'{base}/vue/dist/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'vue')}), # vue index.html (r'{base}/vue/?.*()'.format(base=self.options['theme_path']), AuthenticatedStaticFileHandler, {'path': os.path.join(self.options['theme_data_root'], 'index.html'), 'default_filename': 'index.html'}), ]) # Used for hot-swapping themes # This is the 2nd rule from the end, because the last one is always `self.app.wildcard_router` self.app.static_file_handlers = self.app.default_router.rules[-2] # API v1 handlers self.app.add_handlers('.*$', [ # Main handler (r'{base}(/?.*)'.format(base=self.options['api_root']), ApiHandler), # Key retrieval (r'{base}/getkey(/?.*)'.format(base=self.options['web_root']), KeyHandler), # Builder redirect (r'{base}/api/builder'.format(base=self.options['web_root']), RedirectHandler, {'url': '{base}/apibuilder/'.format(base=self.options['web_root'])}), # Webui login/logout handlers (r'{base}/login(/?)'.format(base=self.options['theme_path']), LoginHandler), (r'{base}/logout(/?)'.format(base=self.options['theme_path']), LogoutHandler), (r'{base}/token(/?)'.format(base=self.options['web_root']), TokenHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{base}/calendar'.format(base=self.options['web_root']), CalendarHandler), # webui handlers ] + self._get_webui_routes()) def _get_webui_routes(self): webroot = self.options['theme_path'] route._routes = list(reversed([url(webroot + u.regex.pattern, u.handler_class, u.kwargs, u.name) for u in route.get_routes()])) return route.get_routes() def run(self): # Start event loop in python3 if six.PY3: import asyncio asyncio.set_event_loop(asyncio.new_event_loop()) if self.enable_https: protocol = 'https' self.server = HTTPServer(self.app, ssl_options={'certfile': self.https_cert, 'keyfile': self.https_key}) else: protocol = 'http' self.server = HTTPServer(self.app) log.info('Starting Medusa on {scheme}://{host}:{port}{web_root}/', { 'scheme': protocol, 'host': self.options['host'], 'port': self.options['port'], 'web_root': self.options['theme_path'] }) try: self.server.listen(self.options['port'], self.options['host']) except Exception as ex: if app.LAUNCH_BROWSER and not self.daemon: app.instance.launch_browser('https' if app.ENABLE_HTTPS else 'http', self.options['port'], app.WEB_ROOT) log.info('Launching browser and exiting') log.info('Could not start the web server on port {port}. Exception: {ex}', { 'port': self.options['port'], 'ex': ex }) os._exit(1) # pylint: disable=protected-access try: self.io_loop = IOLoop.current() self.io_loop.start() except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop() def log_request(self, handler): """ Write a completed HTTP request to the logs. This method handles logging Tornado requests. """ if not app.WEB_LOG: return if handler.get_status() < 400: level = logging.INFO elif handler.get_status() < 500: # Don't log normal RESTful responses as warnings if isinstance(handler, BaseRequestHandler): level = logging.INFO else: level = logging.WARNING else: level = logging.ERROR log.log( level, '{status} {summary} {time:.2f}ms', { 'status': handler.get_status(), 'summary': handler._request_summary(), 'time': 1000.0 * handler.request.request_time() } )
def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, Env = None, desktop = None): try: locale.setlocale(locale.LC_ALL, "") encoding = locale.getpreferredencoding() except (locale.Error, IOError): encoding = None # for OSes that are poorly configured I'll just force UTF-8 if not encoding or encoding in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'): encoding = 'UTF-8' Env.set('encoding', encoding) # Do db stuff db_path = toUnicode(os.path.join(data_dir, 'couchpotato.db')) # Backup before start and cleanup old databases new_backup = toUnicode(os.path.join(data_dir, 'db_backup', str(int(time.time())))) # Create path and copy if not os.path.isdir(new_backup): os.makedirs(new_backup) src_files = [options.config_file, db_path, db_path + '-shm', db_path + '-wal'] for src_file in src_files: if os.path.isfile(src_file): dst_file = toUnicode(os.path.join(new_backup, os.path.basename(src_file))) shutil.copyfile(src_file, dst_file) # Try and copy stats seperately try: shutil.copystat(src_file, dst_file) except: pass # Remove older backups, keep backups 3 days or at least 3 backups = [] for directory in os.listdir(os.path.dirname(new_backup)): backup = toUnicode(os.path.join(os.path.dirname(new_backup), directory)) if os.path.isdir(backup): backups.append(backup) total_backups = len(backups) for backup in backups: if total_backups > 3: if tryInt(os.path.basename(backup)) < time.time() - 259200: for the_file in os.listdir(backup): file_path = os.path.join(backup, the_file) try: if os.path.isfile(file_path): os.remove(file_path) except: raise os.rmdir(backup) total_backups -= 1 # Register environment settings Env.set('app_dir', toUnicode(base_path)) Env.set('data_dir', toUnicode(data_dir)) Env.set('log_path', toUnicode(os.path.join(log_dir, 'CouchPotato.log'))) Env.set('db_path', toUnicode('sqlite:///' + db_path)) Env.set('cache_dir', toUnicode(os.path.join(data_dir, 'cache'))) Env.set('cache', FileSystemCache(toUnicode(os.path.join(Env.get('cache_dir'), 'python')))) Env.set('console_log', options.console_log) Env.set('quiet', options.quiet) Env.set('desktop', desktop) Env.set('daemonized', options.daemon) Env.set('args', args) Env.set('options', options) # Determine debug debug = options.debug or Env.setting('debug', default = False, type = 'bool') Env.set('debug', debug) # Development development = Env.setting('development', default = False, type = 'bool') Env.set('dev', development) # Disable logging for some modules for logger_name in ['enzyme', 'guessit', 'subliminal', 'apscheduler']: logging.getLogger(logger_name).setLevel(logging.ERROR) for logger_name in ['gntp', 'migrate']: logging.getLogger(logger_name).setLevel(logging.WARNING) # Use reloader reloader = debug is True and development and not Env.get('desktop') and not options.daemon # Logger logger = logging.getLogger() formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') level = logging.DEBUG if debug else logging.INFO logger.setLevel(level) logging.addLevelName(19, 'INFO') # To screen if (debug or options.console_log) and not options.quiet and not options.daemon: hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(formatter) logger.addHandler(hdlr) # To file hdlr2 = handlers.RotatingFileHandler(Env.get('log_path'), 'a', 500000, 10) hdlr2.setFormatter(formatter) logger.addHandler(hdlr2) # Start logging & enable colors import color_logs from couchpotato.core.logger import CPLog log = CPLog(__name__) log.debug('Started with options %s', options) def customwarn(message, category, filename, lineno, file = None, line = None): log.warning('%s %s %s line:%s', (category, message, filename, lineno)) warnings.showwarning = customwarn # Check if database exists db = Env.get('db_path') db_exists = os.path.isfile(toUnicode(db_path)) # Load migrations if db_exists: from migrate.versioning.api import version_control, db_version, version, upgrade repo = os.path.join(base_path, 'couchpotato', 'core', 'migration') latest_db_version = version(repo) try: current_db_version = db_version(db, repo) except: version_control(db, repo, version = latest_db_version) current_db_version = db_version(db, repo) if current_db_version < latest_db_version: if development: log.error('There is a database migration ready, but you are running development mode, so it won\'t be used. If you see this, you are stupid. Please disable development mode.') else: log.info('Doing database upgrade. From %d to %d', (current_db_version, latest_db_version)) upgrade(db, repo) # Configure Database from couchpotato.core.settings.model import setup setup() # Create app from couchpotato import WebHandler web_base = ('/' + Env.setting('url_base').lstrip('/') + '/') if Env.setting('url_base') else '/' Env.set('web_base', web_base) api_key = Env.setting('api_key') api_base = r'%sapi/%s/' % (web_base, api_key) Env.set('api_base', api_base) # Basic config host = Env.setting('host', default = '0.0.0.0') # app.debug = development config = { 'use_reloader': reloader, 'port': tryInt(Env.setting('port', default = 5050)), 'host': host if host and len(host) > 0 else '0.0.0.0', 'ssl_cert': Env.setting('ssl_cert', default = None), 'ssl_key': Env.setting('ssl_key', default = None), } # Load the app application = Application([], log_function = lambda x : None, debug = config['use_reloader'], gzip = True, cookie_secret = api_key, login_url = '%slogin/' % web_base, ) Env.set('app', application) # Request handlers application.add_handlers(".*$", [ (r'%snonblock/(.*)(/?)' % api_base, NonBlockHandler), # API handlers (r'%s(.*)(/?)' % api_base, ApiHandler), # Main API handler (r'%sgetkey(/?)' % web_base, KeyHandler), # Get API key (r'%s' % api_base, RedirectHandler, {"url": web_base + 'docs/'}), # API docs # Login handlers (r'%slogin(/?)' % web_base, LoginHandler), (r'%slogout(/?)' % web_base, LogoutHandler), # Catch all webhandlers (r'%s(.*)(/?)' % web_base, WebHandler), (r'(.*)', WebHandler), ]) # Static paths static_path = '%sstatic/' % web_base for dir_name in ['fonts', 'images', 'scripts', 'style']: application.add_handlers(".*$", [ ('%s%s/(.*)' % (static_path, dir_name), StaticFileHandler, {'path': toUnicode(os.path.join(base_path, 'couchpotato', 'static', dir_name))}) ]) Env.set('static_path', static_path) # Load configs & plugins loader = Env.get('loader') loader.preload(root = toUnicode(base_path)) loader.run() # Fill database with needed stuff if not db_exists: fireEvent('app.initialize', in_order = True) # Go go go! from tornado.ioloop import IOLoop loop = IOLoop.current() # Some logging and fire load event try: log.info('Starting server on port %(port)s', config) except: pass fireEventAsync('app.load') if config['ssl_cert'] and config['ssl_key']: server = HTTPServer(application, no_keep_alive = True, ssl_options = { "certfile": config['ssl_cert'], "keyfile": config['ssl_key'], }) else: server = HTTPServer(application, no_keep_alive = True) try_restart = True restart_tries = 5 while try_restart: try: server.listen(config['port'], config['host']) loop.start() except Exception, e: log.error('Failed starting: %s', traceback.format_exc()) try: nr, msg = e if nr == 48: log.info('Port (%s) needed for CouchPotato is already in use, try %s more time after few seconds', (config.get('port'), restart_tries)) time.sleep(1) restart_tries -= 1 if restart_tries > 0: continue else: return except: pass raise try_restart = False
class WebServer(threading.Thread): def __init__(self): super(WebServer, self).__init__() self.name = "TORNADO" self.daemon = True self.started = False self.video_root = None self.api_root = None self.app = None self.server = None self.io_loop = None def run(self): self.started = True self.io_loop = IOLoop() # load languages tornado.locale.load_gettext_translations(sickrage.LOCALE_DIR, 'messages') # Check configured web port is correct if sickrage.app.config.web_port < 21 or sickrage.app.config.web_port > 65535: sickrage.app.config.web_port = 8081 # clear mako cache folder mako_cache = os.path.join(sickrage.app.cache_dir, 'mako') if os.path.isdir(mako_cache): shutil.rmtree(mako_cache, ignore_errors=True) # video root if sickrage.app.config.root_dirs: root_dirs = sickrage.app.config.root_dirs.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] # web root if sickrage.app.config.web_root: sickrage.app.config.web_root = sickrage.app.config.web_root = ( '/' + sickrage.app.config.web_root.lstrip('/').strip('/')) # api root self.api_root = r'%s/api/%s' % (sickrage.app.config.web_root, sickrage.app.config.api_key) # tornado setup if sickrage.app.config.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not create_https_certificates(sickrage.app.config.https_cert, sickrage.app.config.https_key): sickrage.app.log.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.app.config.enable_https = False if not (os.path.exists(sickrage.app.config.https_cert) and os.path.exists(sickrage.app.config.https_key)): sickrage.app.log.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.app.config.enable_https = False # Load templates mako_lookup = TemplateLookup( directories=[sickrage.app.config.gui_views_dir], module_directory=os.path.join(sickrage.app.cache_dir, 'mako'), filesystem_checks=True, strict_undefined=True, input_encoding='utf-8', output_encoding='utf-8', encoding_errors='replace' ) templates = {} for root, dirs, files in os.walk(sickrage.app.config.gui_views_dir): path = root.split(os.sep) for x in sickrage.app.config.gui_views_dir.split(os.sep): if x in path: del path[path.index(x)] for file in files: filename = '{}/{}'.format('/'.join(path), file).lstrip('/') templates[filename] = mako_lookup.get_template(filename) # Load the app self.app = Application( debug=True, autoreload=False, gzip=sickrage.app.config.web_use_gzip, cookie_secret=sickrage.app.config.web_cookie_secret, login_url='%s/login/' % sickrage.app.config.web_root, templates=templates, default_handler_class=NotFoundHandler ) # Websocket handler self.app.add_handlers('.*$', [ (r'%s/ws/ui' % sickrage.app.config.web_root, WebSocketUIHandler) ]) # Static File Handlers self.app.add_handlers('.*$', [ # api (r'%s/api/(\w{32})(/?.*)' % sickrage.app.config.web_root, ApiHandler), # redirect to home (r"(%s)(/?)" % sickrage.app.config.web_root, RedirectHandler, {"url": "%s/home" % sickrage.app.config.web_root}), # api builder (r'%s/api/builder' % sickrage.app.config.web_root, RedirectHandler, {"url": sickrage.app.config.web_root + '/apibuilder/'}), # login (r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler), # logout (r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler), # favicon (r'%s/(favicon\.ico)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images/favicon.ico')}), # images (r'%s/images/(.*)' % sickrage.app.config.web_root, StaticImageHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images')}), # css (r'%s/css/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'css')}), # scss (r'%s/scss/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'scss')}), # fonts (r'%s/fonts/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'fonts')}), # javascript (r'%s/js/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'js')}), # videos (r'%s/videos/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": self.video_root}), ]) # Handlers self.app.add_handlers('.*$', [ (r'%s/robots.txt' % sickrage.app.config.web_root, RobotsDotTxtHandler), (r'%s/messages.po' % sickrage.app.config.web_root, MessagesDotPoHandler), (r'%s/quicksearch.json' % sickrage.app.config.web_root, QuicksearchDotJsonHandler), (r'%s/apibuilder(/?)' % sickrage.app.config.web_root, APIBulderHandler), (r'%s/setHomeLayout(/?)' % sickrage.app.config.web_root, SetHomeLayoutHandler), (r'%s/setPosterSortBy(/?)' % sickrage.app.config.web_root, SetPosterSortByHandler), (r'%s/setPosterSortDir(/?)' % sickrage.app.config.web_root, SetPosterSortDirHandler), (r'%s/setHistoryLayout(/?)' % sickrage.app.config.web_root, SetHistoryLayoutHandler), (r'%s/toggleDisplayShowSpecials(/?)' % sickrage.app.config.web_root, ToggleDisplayShowSpecialsHandler), (r'%s/toggleScheduleDisplayPaused(/?)' % sickrage.app.config.web_root, ToggleScheduleDisplayPausedHandler), (r'%s/setScheduleSort(/?)' % sickrage.app.config.web_root, SetScheduleSortHandler), (r'%s/forceSchedulerJob(/?)' % sickrage.app.config.web_root, ForceSchedulerJobHandler), (r'%s/announcements(/?)' % sickrage.app.config.web_root, AnnouncementsHandler), (r'%s/announcements/announcementCount(/?)' % sickrage.app.config.web_root, AnnouncementCountHandler), (r'%s/announcements/mark-seen(/?)' % sickrage.app.config.web_root, MarkAnnouncementSeenHandler), (r'%s/schedule(/?)' % sickrage.app.config.web_root, ScheduleHandler), (r'%s/setScheduleLayout(/?)' % sickrage.app.config.web_root, SetScheduleLayoutHandler), (r'%s/calendar(/?)' % sickrage.app.config.web_root, CalendarHandler), (r'%s/changelog(/?)' % sickrage.app.config.web_root, ChangelogHandler), (r'%s/account/link(/?)' % sickrage.app.config.web_root, AccountLinkHandler), (r'%s/account/unlink(/?)' % sickrage.app.config.web_root, AccountUnlinkHandler), (r'%s/account/is-linked(/?)' % sickrage.app.config.web_root, AccountIsLinkedHandler), (r'%s/history(/?)' % sickrage.app.config.web_root, HistoryHandler), (r'%s/history/clear(/?)' % sickrage.app.config.web_root, HistoryClearHandler), (r'%s/history/trim(/?)' % sickrage.app.config.web_root, HistoryTrimHandler), (r'%s/irc(/?)' % sickrage.app.config.web_root, IRCHandler), (r'%s/logs(/?)' % sickrage.app.config.web_root, LogsHandler), (r'%s/logs/errorCount(/?)' % sickrage.app.config.web_root, ErrorCountHandler), (r'%s/logs/warningCount(/?)' % sickrage.app.config.web_root, WarningCountHandler), (r'%s/logs/view(/?)' % sickrage.app.config.web_root, LogsViewHandler), (r'%s/logs/clearAll(/?)' % sickrage.app.config.web_root, LogsClearAllHanlder), (r'%s/logs/clearWarnings(/?)' % sickrage.app.config.web_root, LogsClearWarningsHanlder), (r'%s/logs/clearErrors(/?)' % sickrage.app.config.web_root, LogsClearErrorsHanlder), (r'%s/browser(/?)' % sickrage.app.config.web_root, WebFileBrowserHandler), (r'%s/browser/complete(/?)' % sickrage.app.config.web_root, WebFileBrowserCompleteHandler), (r'%s/home(/?)' % sickrage.app.config.web_root, HomeHandler), (r'%s/home/showProgress(/?)' % sickrage.app.config.web_root, ShowProgressHandler), (r'%s/home/is-alive(/?)' % sickrage.app.config.web_root, IsAliveHandler), (r'%s/home/testSABnzbd(/?)' % sickrage.app.config.web_root, TestSABnzbdHandler), (r'%s/home/testSynologyDSM(/?)' % sickrage.app.config.web_root, TestSynologyDSMHandler), (r'%s/home/testTorrent(/?)' % sickrage.app.config.web_root, TestTorrentHandler), (r'%s/home/testFreeMobile(/?)' % sickrage.app.config.web_root, TestFreeMobileHandler), (r'%s/home/testTelegram(/?)' % sickrage.app.config.web_root, TestTelegramHandler), (r'%s/home/testJoin(/?)' % sickrage.app.config.web_root, TestJoinHandler), (r'%s/home/testGrowl(/?)' % sickrage.app.config.web_root, TestGrowlHandler), (r'%s/home/testProwl(/?)' % sickrage.app.config.web_root, TestProwlHandler), (r'%s/home/testBoxcar2(/?)' % sickrage.app.config.web_root, TestBoxcar2Handler), (r'%s/home/testPushover(/?)' % sickrage.app.config.web_root, TestPushoverHandler), (r'%s/home/twitterStep1(/?)' % sickrage.app.config.web_root, TwitterStep1Handler), (r'%s/home/twitterStep2(/?)' % sickrage.app.config.web_root, TwitterStep2Handler), (r'%s/home/testTwitter(/?)' % sickrage.app.config.web_root, TestTwitterHandler), (r'%s/home/testTwilio(/?)' % sickrage.app.config.web_root, TestTwilioHandler), (r'%s/home/testSlack(/?)' % sickrage.app.config.web_root, TestSlackHandler), (r'%s/home/testAlexa(/?)' % sickrage.app.config.web_root, TestAlexaHandler), (r'%s/home/testDiscord(/?)' % sickrage.app.config.web_root, TestDiscordHandler), (r'%s/home/testKODI(/?)' % sickrage.app.config.web_root, TestKODIHandler), (r'%s/home/testPMC(/?)' % sickrage.app.config.web_root, TestPMCHandler), (r'%s/home/testPMS(/?)' % sickrage.app.config.web_root, TestPMSHandler), (r'%s/home/testLibnotify(/?)' % sickrage.app.config.web_root, TestLibnotifyHandler), (r'%s/home/testEMBY(/?)' % sickrage.app.config.web_root, TestEMBYHandler), (r'%s/home/testNMJ(/?)' % sickrage.app.config.web_root, TestNMJHandler), (r'%s/home/settingsNMJ(/?)' % sickrage.app.config.web_root, SettingsNMJHandler), (r'%s/home/testNMJv2(/?)' % sickrage.app.config.web_root, TestNMJv2Handler), (r'%s/home/settingsNMJv2(/?)' % sickrage.app.config.web_root, SettingsNMJv2Handler), (r'%s/home/getTraktToken(/?)' % sickrage.app.config.web_root, GetTraktTokenHandler), (r'%s/home/testTrakt(/?)' % sickrage.app.config.web_root, TestTraktHandler), (r'%s/home/loadShowNotifyLists(/?)' % sickrage.app.config.web_root, LoadShowNotifyListsHandler), (r'%s/home/saveShowNotifyList(/?)' % sickrage.app.config.web_root, SaveShowNotifyListHandler), (r'%s/home/testEmail(/?)' % sickrage.app.config.web_root, TestEmailHandler), (r'%s/home/testNMA(/?)' % sickrage.app.config.web_root, TestNMAHandler), (r'%s/home/testPushalot(/?)' % sickrage.app.config.web_root, TestPushalotHandler), (r'%s/home/testPushbullet(/?)' % sickrage.app.config.web_root, TestPushbulletHandler), (r'%s/home/getPushbulletDevices(/?)' % sickrage.app.config.web_root, GetPushbulletDevicesHandler), (r'%s/home/serverStatus(/?)' % sickrage.app.config.web_root, ServerStatusHandler), (r'%s/home/providerStatus(/?)' % sickrage.app.config.web_root, ProviderStatusHandler), (r'%s/home/shutdown(/?)' % sickrage.app.config.web_root, ShutdownHandler), (r'%s/home/restart(/?)' % sickrage.app.config.web_root, RestartHandler), (r'%s/home/updateCheck(/?)' % sickrage.app.config.web_root, UpdateCheckHandler), (r'%s/home/update(/?)' % sickrage.app.config.web_root, UpdateHandler), (r'%s/home/verifyPath(/?)' % sickrage.app.config.web_root, VerifyPathHandler), (r'%s/home/installRequirements(/?)' % sickrage.app.config.web_root, InstallRequirementsHandler), (r'%s/home/branchCheckout(/?)' % sickrage.app.config.web_root, BranchCheckoutHandler), (r'%s/home/displayShow(/?)' % sickrage.app.config.web_root, DisplayShowHandler), (r'%s/home/togglePause(/?)' % sickrage.app.config.web_root, TogglePauseHandler), (r'%s/home/deleteShow' % sickrage.app.config.web_root, DeleteShowHandler), (r'%s/home/refreshShow(/?)' % sickrage.app.config.web_root, RefreshShowHandler), (r'%s/home/updateShow(/?)' % sickrage.app.config.web_root, UpdateShowHandler), (r'%s/home/subtitleShow(/?)' % sickrage.app.config.web_root, SubtitleShowHandler), (r'%s/home/updateKODI(/?)' % sickrage.app.config.web_root, UpdateKODIHandler), (r'%s/home/updatePLEX(/?)' % sickrage.app.config.web_root, UpdatePLEXHandler), (r'%s/home/updateEMBY(/?)' % sickrage.app.config.web_root, UpdateEMBYHandler), (r'%s/home/syncTrakt(/?)' % sickrage.app.config.web_root, SyncTraktHandler), (r'%s/home/deleteEpisode(/?)' % sickrage.app.config.web_root, DeleteEpisodeHandler), (r'%s/home/testRename(/?)' % sickrage.app.config.web_root, TestRenameHandler), (r'%s/home/doRename(/?)' % sickrage.app.config.web_root, DoRenameHandler), (r'%s/home/searchEpisode(/?)' % sickrage.app.config.web_root, SearchEpisodeHandler), (r'%s/home/getManualSearchStatus(/?)' % sickrage.app.config.web_root, GetManualSearchStatusHandler), (r'%s/home/searchEpisodeSubtitles(/?)' % sickrage.app.config.web_root, SearchEpisodeSubtitlesHandler), (r'%s/home/setSceneNumbering(/?)' % sickrage.app.config.web_root, SetSceneNumberingHandler), (r'%s/home/retryEpisode(/?)' % sickrage.app.config.web_root, RetryEpisodeHandler), (r'%s/home/fetch_releasegroups(/?)' % sickrage.app.config.web_root, FetchReleasegroupsHandler), (r'%s/home/postprocess(/?)' % sickrage.app.config.web_root, HomePostProcessHandler), (r'%s/home/postprocess/processEpisode(/?)' % sickrage.app.config.web_root, HomeProcessEpisodeHandler), (r'%s/home/addShows(/?)' % sickrage.app.config.web_root, HomeAddShowsHandler), (r'%s/home/addShows/searchIndexersForShowName(/?)' % sickrage.app.config.web_root, SearchIndexersForShowNameHandler), (r'%s/home/addShows/massAddTable(/?)' % sickrage.app.config.web_root, MassAddTableHandler), (r'%s/home/addShows/newShow(/?)' % sickrage.app.config.web_root, NewShowHandler), (r'%s/home/addShows/traktShows(/?)' % sickrage.app.config.web_root, TraktShowsHandler), (r'%s/home/addShows/popularShows(/?)' % sickrage.app.config.web_root, PopularShowsHandler), (r'%s/home/addShows/addShowToBlacklist(/?)' % sickrage.app.config.web_root, AddShowToBlacklistHandler), (r'%s/home/addShows/existingShows(/?)' % sickrage.app.config.web_root, ExistingShowsHandler), (r'%s/home/addShows/addShowByID(/?)' % sickrage.app.config.web_root, AddShowByIDHandler), (r'%s/home/addShows/addNewShow(/?)' % sickrage.app.config.web_root, AddNewShowHandler), (r'%s/home/addShows/addExistingShows(/?)' % sickrage.app.config.web_root, AddExistingShowsHandler), (r'%s/manage(/?)' % sickrage.app.config.web_root, ManageHandler), (r'%s/manage/editShow(/?)' % sickrage.app.config.web_root, EditShowHandler), (r'%s/manage/showEpisodeStatuses(/?)' % sickrage.app.config.web_root, ShowEpisodeStatusesHandler), (r'%s/manage/episodeStatuses(/?)' % sickrage.app.config.web_root, EpisodeStatusesHandler), (r'%s/manage/changeEpisodeStatuses(/?)' % sickrage.app.config.web_root, ChangeEpisodeStatusesHandler), (r'%s/manage/setEpisodeStatus(/?)' % sickrage.app.config.web_root, SetEpisodeStatusHandler), (r'%s/manage/showSubtitleMissed(/?)' % sickrage.app.config.web_root, ShowSubtitleMissedHandler), (r'%s/manage/subtitleMissed(/?)' % sickrage.app.config.web_root, SubtitleMissedHandler), (r'%s/manage/downloadSubtitleMissed(/?)' % sickrage.app.config.web_root, DownloadSubtitleMissedHandler), (r'%s/manage/backlogShow(/?)' % sickrage.app.config.web_root, BacklogShowHandler), (r'%s/manage/backlogOverview(/?)' % sickrage.app.config.web_root, BacklogOverviewHandler), (r'%s/manage/massEdit(/?)' % sickrage.app.config.web_root, MassEditHandler), (r'%s/manage/massUpdate(/?)' % sickrage.app.config.web_root, MassUpdateHandler), (r'%s/manage/failedDownloads(/?)' % sickrage.app.config.web_root, FailedDownloadsHandler), (r'%s/manage/manageQueues(/?)' % sickrage.app.config.web_root, ManageQueuesHandler), (r'%s/manage/manageQueues/forceBacklogSearch(/?)' % sickrage.app.config.web_root, ForceBacklogSearchHandler), (r'%s/manage/manageQueues/forceDailySearch(/?)' % sickrage.app.config.web_root, ForceDailySearchHandler), (r'%s/manage/manageQueues/forceFindPropers(/?)' % sickrage.app.config.web_root, ForceFindPropersHandler), (r'%s/manage/manageQueues/pauseDailySearcher(/?)' % sickrage.app.config.web_root, PauseDailySearcherHandler), (r'%s/manage/manageQueues/pauseBacklogSearcher(/?)' % sickrage.app.config.web_root, PauseBacklogSearcherHandler), (r'%s/manage/manageQueues/pausePostProcessor(/?)' % sickrage.app.config.web_root, PausePostProcessorHandler), (r'%s/config(/?)' % sickrage.app.config.web_root, ConfigHandler), (r'%s/config/reset(/?)' % sickrage.app.config.web_root, ConfigResetHandler), (r'%s/config/anime(/?)' % sickrage.app.config.web_root, ConfigAnimeHandler), (r'%s/config/anime/saveAnime(/?)' % sickrage.app.config.web_root, ConfigSaveAnimeHandler), (r'%s/config/backuprestore(/?)' % sickrage.app.config.web_root, ConfigBackupRestoreHandler), (r'%s/config/backuprestore/backup(/?)' % sickrage.app.config.web_root, ConfigBackupHandler), (r'%s/config/backuprestore/restore(/?)' % sickrage.app.config.web_root, ConfigRestoreHandler), (r'%s/config/backuprestore/saveBackupRestore(/?)' % sickrage.app.config.web_root, SaveBackupRestoreHandler), (r'%s/config/general(/?)' % sickrage.app.config.web_root, ConfigGeneralHandler), (r'%s/config/general/generateApiKey(/?)' % sickrage.app.config.web_root, GenerateApiKeyHandler), (r'%s/config/general/saveRootDirs(/?)' % sickrage.app.config.web_root, SaveRootDirsHandler), (r'%s/config/general/saveAddShowDefaults(/?)' % sickrage.app.config.web_root, SaveAddShowDefaultsHandler), (r'%s/config/general/saveGeneral(/?)' % sickrage.app.config.web_root, SaveGeneralHandler), (r'%s/config/notifications(/?)' % sickrage.app.config.web_root, ConfigNotificationsHandler), (r'%s/config/notifications/saveNotifications(/?)' % sickrage.app.config.web_root, SaveNotificationsHandler), (r'%s/config/postProcessing(/?)' % sickrage.app.config.web_root, ConfigPostProcessingHandler), (r'%s/config/postProcessing/savePostProcessing(/?)' % sickrage.app.config.web_root, SavePostProcessingHandler), (r'%s/config/postProcessing/testNaming(/?)' % sickrage.app.config.web_root, TestNamingHandler), (r'%s/config/postProcessing/isNamingValid(/?)' % sickrage.app.config.web_root, IsNamingPatternValidHandler), (r'%s/config/postProcessing/isRarSupported(/?)' % sickrage.app.config.web_root, IsRarSupportedHandler), (r'%s/config/providers(/?)' % sickrage.app.config.web_root, ConfigProvidersHandler), (r'%s/config/providers/canAddNewznabProvider(/?)' % sickrage.app.config.web_root, CanAddNewznabProviderHandler), (r'%s/config/providers/canAddTorrentRssProvider(/?)' % sickrage.app.config.web_root, CanAddTorrentRssProviderHandler), (r'%s/config/providers/getNewznabCategories(/?)' % sickrage.app.config.web_root, GetNewznabCategoriesHandler), (r'%s/config/providers/saveProviders(/?)' % sickrage.app.config.web_root, SaveProvidersHandler), (r'%s/config/qualitySettings(/?)' % sickrage.app.config.web_root, ConfigQualitySettingsHandler), (r'%s/config/qualitySettings/saveQualities(/?)' % sickrage.app.config.web_root, SaveQualitiesHandler), (r'%s/config/search(/?)' % sickrage.app.config.web_root, ConfigSearchHandler), (r'%s/config/search/saveSearch(/?)' % sickrage.app.config.web_root, SaveSearchHandler), (r'%s/config/subtitles(/?)' % sickrage.app.config.web_root, ConfigSubtitlesHandler), (r'%s/config/subtitles/get_code(/?)' % sickrage.app.config.web_root, ConfigSubtitleGetCodeHandler), (r'%s/config/subtitles/wanted_languages(/?)' % sickrage.app.config.web_root, ConfigSubtitlesWantedLanguagesHandler), (r'%s/config/subtitles/saveSubtitles(/?)' % sickrage.app.config.web_root, SaveSubtitlesHandler), ]) # HTTPS Cert/Key object ssl_ctx = None if sickrage.app.config.enable_https: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(sickrage.app.config.https_cert, sickrage.app.config.https_key) # Web Server self.server = HTTPServer(self.app, ssl_options=ssl_ctx, xheaders=sickrage.app.config.handle_reverse_proxy) try: self.server.listen(sickrage.app.config.web_port) except socket.error as e: sickrage.app.log.warning(e.strerror) raise SystemExit self.io_loop.start() def shutdown(self): if self.started: self.started = False if self.server: self.server.close_all_connections() self.server.stop() if self.io_loop: self.io_loop.stop()
class WebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', None) assert isinstance(self.options['port'], int) assert 'data_root' in self.options # web root self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/')) if self.options[ 'web_root'] else '' # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log(u'Unable to create CERT/KEY files, disabling HTTPS') sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log(u'Disabled HTTPS because of missing CERT and KEY files', logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=True, autoreload=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.COOKIE_SECRET, login_url='%s/login/' % self.options['web_root'], ) # Main Handler self.app.add_handlers('.*$', [ (r'%s/api/builder(/?)(.*)' % self.options['web_root'], webserve.ApiBuilder), (r'%s/api(/?.*)' % self.options['web_root'], webapi.Api), (r'%s/cache(/?.*)' % self.options['web_root'], webserve.Cache), (r'%s/config/general(/?.*)' % self.options['web_root'], webserve.ConfigGeneral), (r'%s/config/search(/?.*)' % self.options['web_root'], webserve.ConfigSearch), (r'%s/config/providers(/?.*)' % self.options['web_root'], webserve.ConfigProviders), (r'%s/config/subtitles(/?.*)' % self.options['web_root'], webserve.ConfigSubtitles), (r'%s/config/postProcessing(/?.*)' % self.options['web_root'], webserve.ConfigPostProcessing), (r'%s/config/notifications(/?.*)' % self.options['web_root'], webserve.ConfigNotifications), (r'%s/config/anime(/?.*)' % self.options['web_root'], webserve.ConfigAnime), (r'%s/config(/?.*)' % self.options['web_root'], webserve.Config), (r'%s/errorlogs(/?.*)' % self.options['web_root'], webserve.ErrorLogs), (r'%s/history(/?.*)' % self.options['web_root'], webserve.History), (r'%s/home/is_alive(/?.*)' % self.options['web_root'], webserve.IsAliveHandler), (r'%s/home/addShows(/?.*)' % self.options['web_root'], webserve.NewHomeAddShows), (r'%s/home/postprocess(/?.*)' % self.options['web_root'], webserve.HomePostProcess), (r'%s/home(/?.*)' % self.options['web_root'], webserve.Home), (r'%s/manage/manageSearches(/?.*)' % self.options['web_root'], webserve.ManageSearches), (r'%s/manage/showQueueOverview(/?.*)' % self.options['web_root'], webserve.showQueueOverview), (r'%s/manage/(/?.*)' % self.options['web_root'], webserve.Manage), (r'%s/ui(/?.*)' % self.options['web_root'], webserve.UI), (r'%s/browser(/?.*)' % self.options['web_root'], webserve.WebFileBrowser), (r'%s(/?.*)' % self.options['web_root'], webserve.MainHandler), ]) # webui login/logout handlers self.app.add_handlers('.*$', [ (r'%s/login(/?)' % self.options['web_root'], webserve.LoginHandler), (r'%s/logout(/?)' % self.options['web_root'], webserve.LogoutHandler), ]) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % self.options['web_root'], webserve.CalendarHandler), ]) # Static File Handlers self.app.add_handlers('.*$', [ # favicon (r'%s/(favicon\.ico)' % self.options['web_root'], StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'%s/images/(.*)' % self.options['web_root'], StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images')}), # cached images (r'%s/cache/images/(.*)' % self.options['web_root'], StaticFileHandler, {'path': os.path.join(sickbeard.CACHE_DIR, 'images')}), # css (r'%s/css/(.*)' % self.options['web_root'], StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options['web_root'], StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'js')}), ]) def run(self): if self.enable_https: protocol = 'https' self.server = HTTPServer(self.app, ssl_options={'certfile': self.https_cert, 'keyfile': self.https_key}) else: protocol = 'http' self.server = HTTPServer(self.app) logger.log(u'Starting SickGear on ' + protocol + '://' + str(self.options['host']) + ':' + str( self.options['port']) + '/') try: self.server.listen(self.options['port'], self.options['host']) except: etype, evalue, etb = sys.exc_info() logger.log( 'Could not start webserver on %s. Excpeption: %s, Error: %s' % (self.options['port'], etype, evalue), logger.ERROR) return try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
def initWebServer(options={}): options.setdefault('port', 8081) options.setdefault('host', '0.0.0.0') options.setdefault('log_dir', None) options.setdefault('username', '') options.setdefault('password', '') options.setdefault('web_root', '/') assert isinstance(options['port'], int) assert 'data_root' in options # tornado setup enable_https = options['enable_https'] https_cert = options['https_cert'] https_key = options['https_key'] if enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)): if not create_https_certificates(https_cert, https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False enable_https = False if not (os.path.exists(https_cert) and os.path.exists(https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False enable_https = False # Load the app app = Application([], debug=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=' ) # Main Handler app.add_handlers(".*$", [ (r"%s" % options['web_root'], RedirectHandler, {'url': '%s/home/' % options['web_root']}), (r'%s/api/(.*)(/?)' % options['web_root'], webapi.Api), (r'%s/(.*)(/?)' % options['web_root'], webserve.MainHandler) ]) # Static Path Handler app.add_handlers(".*$", [ (r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images'), os.path.join(sickbeard.CACHE_DIR, 'images')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'css')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'js')]}) ]) global server if enable_https: protocol = "https" server = HTTPServer(app, no_keep_alive=True, ssl_options={"certfile": https_cert, "keyfile": https_key}) else: protocol = "http" server = HTTPServer(app, no_keep_alive=True) logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str( options['port']) + "/") try: server.listen(options['port'], options['host']) except: pass
def runCouchPotato(options, base_path, args, data_dir=None, log_dir=None, Env=None, desktop=None): try: locale.setlocale(locale.LC_ALL, "") encoding = locale.getpreferredencoding() except (locale.Error, IOError): encoding = None # for OSes that are poorly configured I'll just force UTF-8 if not encoding or encoding in ("ANSI_X3.4-1968", "US-ASCII", "ASCII"): encoding = "UTF-8" Env.set("encoding", encoding) # Do db stuff db_path = sp(os.path.join(data_dir, "database")) old_db_path = os.path.join(data_dir, "couchpotato.db") # Remove database folder if both exists if os.path.isdir(db_path) and os.path.isfile(old_db_path): db = SuperThreadSafeDatabase(db_path) db.open() db.destroy() # Check if database exists db = SuperThreadSafeDatabase(db_path) db_exists = db.exists() if db_exists: # Backup before start and cleanup old backups backup_path = sp(os.path.join(data_dir, "db_backup")) backup_count = 5 existing_backups = [] if not os.path.isdir(backup_path): os.makedirs(backup_path) for root, dirs, files in os.walk(backup_path): # Only consider files being a direct child of the backup_path if root == backup_path: for backup_file in sorted(files): ints = re.findall("\d+", backup_file) # Delete non zip files if len(ints) != 1: try: os.remove(os.path.join(root, backup_file)) except: pass else: existing_backups.append((int(ints[0]), backup_file)) else: # Delete stray directories. shutil.rmtree(root) # Remove all but the last 5 for eb in existing_backups[:-backup_count]: os.remove(os.path.join(backup_path, eb[1])) # Create new backup new_backup = sp(os.path.join(backup_path, "%s.tar.gz" % int(time.time()))) zipf = tarfile.open(new_backup, "w:gz") for root, dirs, files in os.walk(db_path): for zfilename in files: zipf.add( os.path.join(root, zfilename), arcname="database/%s" % os.path.join(root[len(db_path) + 1 :], zfilename), ) zipf.close() # Open last db.open() else: db.create() # Force creation of cachedir log_dir = sp(log_dir) cache_dir = sp(os.path.join(data_dir, "cache")) python_cache = sp(os.path.join(cache_dir, "python")) if not os.path.exists(cache_dir): os.mkdir(cache_dir) if not os.path.exists(python_cache): os.mkdir(python_cache) session = requests.Session() session.max_redirects = 5 # Register environment settings Env.set("app_dir", sp(base_path)) Env.set("data_dir", sp(data_dir)) Env.set("log_path", sp(os.path.join(log_dir, "CouchPotato.log"))) Env.set("db", db) Env.set("http_opener", session) Env.set("cache_dir", cache_dir) Env.set("cache", FileSystemCache(python_cache)) Env.set("console_log", options.console_log) Env.set("quiet", options.quiet) Env.set("desktop", desktop) Env.set("daemonized", options.daemon) Env.set("args", args) Env.set("options", options) # Determine debug debug = options.debug or Env.setting("debug", default=False, type="bool") Env.set("debug", debug) # Development development = Env.setting("development", default=False, type="bool") Env.set("dev", development) # Disable logging for some modules for logger_name in ["enzyme", "guessit", "subliminal", "apscheduler", "tornado", "requests"]: logging.getLogger(logger_name).setLevel(logging.ERROR) for logger_name in ["gntp"]: logging.getLogger(logger_name).setLevel(logging.WARNING) # Disable SSL warning disable_warnings() # Use reloader reloader = debug is True and development and not Env.get("desktop") and not options.daemon # Logger logger = logging.getLogger() formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s", "%m-%d %H:%M:%S") level = logging.DEBUG if debug else logging.INFO logger.setLevel(level) logging.addLevelName(19, "INFO") # To screen if (debug or options.console_log) and not options.quiet and not options.daemon: hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(formatter) logger.addHandler(hdlr) # To file hdlr2 = handlers.RotatingFileHandler(Env.get("log_path"), "a", 500000, 10, encoding=Env.get("encoding")) hdlr2.setFormatter(formatter) logger.addHandler(hdlr2) # Start logging & enable colors # noinspection PyUnresolvedReferences import color_logs from couchpotato.core.logger import CPLog log = CPLog(__name__) log.debug("Started with options %s", options) # Check soft-chroot dir exists: try: # Load Soft-Chroot soft_chroot = Env.get("softchroot") soft_chroot_dir = Env.setting("soft_chroot", section="core", default=None, type="unicode") soft_chroot.initialize(soft_chroot_dir) except SoftChrootInitError as exc: log.error(exc) return except: log.error("Unable to check whether SOFT-CHROOT is defined") return # Check available space try: total_space, available_space = getFreeSpace(data_dir) if available_space < 100: log.error( "Shutting down as CP needs some space to work. You'll get corrupted data otherwise. Only %sMB left", available_space, ) return except: log.error("Failed getting diskspace: %s", traceback.format_exc()) def customwarn(message, category, filename, lineno, file=None, line=None): log.warning("%s %s %s line:%s", (category, message, filename, lineno)) warnings.showwarning = customwarn # Create app from couchpotato import WebHandler web_base = ("/" + Env.setting("url_base").lstrip("/") + "/") if Env.setting("url_base") else "/" Env.set("web_base", web_base) api_key = Env.setting("api_key") if not api_key: api_key = uuid4().hex Env.setting("api_key", value=api_key) api_base = r"%sapi/%s/" % (web_base, api_key) Env.set("api_base", api_base) # Basic config host = Env.setting("host", default="0.0.0.0") host6 = Env.setting("host6", default="::") config = { "use_reloader": reloader, "port": tryInt(Env.setting("port", default=5050)), "host": host if host and len(host) > 0 else "0.0.0.0", "host6": host6 if host6 and len(host6) > 0 else "::", "ssl_cert": Env.setting("ssl_cert", default=None), "ssl_key": Env.setting("ssl_key", default=None), } # Load the app application = Application( [], log_function=lambda x: None, debug=config["use_reloader"], gzip=True, cookie_secret=api_key, login_url="%slogin/" % web_base, ) Env.set("app", application) # Request handlers application.add_handlers( ".*$", [ (r"%snonblock/(.*)(/?)" % api_base, NonBlockHandler), # API handlers (r"%s(.*)(/?)" % api_base, ApiHandler), # Main API handler (r"%sgetkey(/?)" % web_base, KeyHandler), # Get API key (r"%s" % api_base, RedirectHandler, {"url": web_base + "docs/"}), # API docs # Login handlers (r"%slogin(/?)" % web_base, LoginHandler), (r"%slogout(/?)" % web_base, LogoutHandler), # Catch all webhandlers (r"%s(.*)(/?)" % web_base, WebHandler), (r"(.*)", WebHandler), ], ) # Static paths static_path = "%sstatic/" % web_base for dir_name in ["fonts", "images", "scripts", "style"]: application.add_handlers( ".*$", [ ( "%s%s/(.*)" % (static_path, dir_name), StaticFileHandler, {"path": sp(os.path.join(base_path, "couchpotato", "static", dir_name))}, ) ], ) Env.set("static_path", static_path) # Load configs & plugins loader = Env.get("loader") loader.preload(root=sp(base_path)) loader.run() # Fill database with needed stuff fireEvent("database.setup") if not db_exists: fireEvent("app.initialize", in_order=True) fireEvent("app.migrate") # Go go go! from tornado.ioloop import IOLoop from tornado.autoreload import add_reload_hook loop = IOLoop.current() # Reload hook def reload_hook(): fireEvent("app.shutdown") add_reload_hook(reload_hook) # Some logging and fire load event try: log.info("Starting server on port %(port)s", config) except: pass fireEventAsync("app.load") ssl_options = None if config["ssl_cert"] and config["ssl_key"]: ssl_options = {"certfile": config["ssl_cert"], "keyfile": config["ssl_key"]} server = HTTPServer(application, no_keep_alive=True, ssl_options=ssl_options) try_restart = True restart_tries = 5 while try_restart: try: if config["host"].startswith("unix:"): server.add_socket(bind_unix_socket(config["host"][5:])) else: server.listen(config["port"], config["host"]) if Env.setting("ipv6", default=False): try: server.listen(config["port"], config["host6"]) except: log.info2("Tried to bind to IPV6 but failed") loop.start() server.close_all_connections() server.stop() loop.close(all_fds=True) except Exception as e: log.error("Failed starting: %s", traceback.format_exc()) try: nr, msg = e if nr == 48: log.info( "Port (%s) needed for CouchPotato is already in use, try %s more time after few seconds", (config.get("port"), restart_tries), ) time.sleep(1) restart_tries -= 1 if restart_tries > 0: continue else: return except ValueError: return except: pass raise try_restart = False
def initWebServer(options={}): options.setdefault('port', 8081) options.setdefault('host', '0.0.0.0') options.setdefault('log_dir', None) options.setdefault('username', '') options.setdefault('password', '') options.setdefault('web_root', '/') assert isinstance(options['port'], int) assert 'data_root' in options # tornado setup enable_https = options['enable_https'] https_cert = options['https_cert'] https_key = options['https_key'] if enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)): if not create_https_certificates(https_cert, https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False enable_https = False if not (os.path.exists(https_cert) and os.path.exists(https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False enable_https = False # Load the app app = Application([], debug=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=' ) # Main Handler app.add_handlers(".*$", [ (r"/", RedirectHandler, {'url': '%s/home/' % options['web_root']}), (r'%s/api/(.*)(/?)' % options['web_root'], webapi.Api), (r'%s/(.*)(/?)' % options['web_root'], webserve.IndexHandler) ]) # Static Path Handler app.add_handlers(".*$", [ (r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'images'), os.path.join(sickbeard.CACHE_DIR, 'images')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'css')]}), (r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler, {'paths': [os.path.join(options['data_root'], 'js')]}) ]) global server if enable_https: protocol = "https" server = HTTPServer(app, no_keep_alive=True, ssl_options={"certfile": https_cert, "keyfile": https_key}) else: protocol = "http" server = HTTPServer(app, no_keep_alive=True) logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str( options['port']) + "/") try: server.listen(options['port'], options['host']) except: pass
class AppWebServer(threading.Thread): def __init__(self, options=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None self.io_loop = None # video root if app.ROOT_DIRS: root_dirs = app.ROOT_DIRS self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: app.WEB_ROOT = self.options['web_root'] = clean_url_path( self.options['web_root']) # Configure root to selected theme. app.WEB_ROOT = self.options['theme_path'] = clean_url_path( app.WEB_ROOT) # Configure the directory to the theme's data root. app.THEME_DATA_ROOT = self.options['theme_data_root'] = os.path.join( self.options['data_root'], app.THEME_NAME) # api root if not app.API_KEY: app.API_KEY = generate_api_key() self.options['api_root'] = r'{root}/api/(?:v1/)?{key}'.format( root=app.WEB_ROOT, key=app.API_KEY) self.options['api_v2_root'] = r'{root}/api/v2'.format( root=app.WEB_ROOT) # websocket root self.options['web_socket'] = r'{root}/ws'.format(root=app.WEB_ROOT) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): log.info( 'Unable to create CERT/KEY files, disabling HTTPS') app.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): log.warning( 'Disabled HTTPS because of missing CERT and KEY files') app.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=app.WEB_USE_GZIP, xheaders=app.HANDLE_REVERSE_PROXY, cookie_secret=app.WEB_COOKIE_SECRET, login_url=r'{root}/login/'.format(root=self.options['theme_path']), log_function=self.log_request, ) self.app.add_handlers('.*$', get_apiv2_handlers(self.options['api_v2_root'])) # Websocket handler self.app.add_handlers('.*$', [(r'{base}/ui(/?.*)'.format( base=self.options['web_socket']), WebSocketUIHandler)]) # Static File Handlers self.app.add_handlers( '.*$', [ # favicon (r'{base}/favicon\.ico()'.format( base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'assets', 'img', 'ico', 'favicon.ico') }), # images (r'{base}/images/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'assets', 'img') }), # cached images (r'{base}/cache/images/(.*)'.format( base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(app.CACHE_DIR, 'images') }), # css (r'{base}/css/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'assets', 'css') }), # javascript (r'{base}/js/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'assets', 'js') }), # fonts (r'{base}/fonts/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'assets', 'fonts') }), # videos (r'{base}/videos/(.*)'.format(base=self.options['theme_path']), StaticFileHandler, { 'path': self.video_root }), # vue dist (r'{base}/vue/dist/(.*)'.format( base=self.options['theme_path']), StaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'vue') }), # vue index.html (r'{base}/vue/?.*()'.format(base=self.options['theme_path']), AuthenticatedStaticFileHandler, { 'path': os.path.join(self.options['theme_data_root'], 'index.html'), 'default_filename': 'index.html' }), ]) # Used for hot-swapping themes # This is the 2nd rule from the end, because the last one is always `self.app.wildcard_router` self.app.static_file_handlers = self.app.default_router.rules[-2] # API v1 handlers self.app.add_handlers( '.*$', [ # Main handler (r'{base}(/?.*)'.format(base=self.options['api_root']), ApiHandler), # Key retrieval (r'{base}/getkey(/?.*)'.format(base=self.options['web_root']), KeyHandler), # Builder redirect (r'{base}/api/builder'.format(base=self.options['web_root']), RedirectHandler, { 'url': '{base}/apibuilder/'.format(base=self.options['web_root']) }), # Webui login/logout handlers (r'{base}/login(/?)'.format(base=self.options['theme_path']), LoginHandler), (r'{base}/logout(/?)'.format(base=self.options['theme_path']), LogoutHandler), (r'{base}/token(/?)'.format(base=self.options['web_root']), TokenHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{base}/calendar'.format(base=self.options['web_root']), CalendarHandler), # webui handlers ] + self._get_webui_routes()) def _get_webui_routes(self): webroot = self.options['theme_path'] route._routes = list( reversed([ url(webroot + u.regex.pattern, u.handler_class, u.kwargs, u.name) for u in route.get_routes() ])) return route.get_routes() def run(self): # Start event loop in python3 if six.PY3: import asyncio asyncio.set_event_loop(asyncio.new_event_loop()) if self.enable_https: protocol = 'https' self.server = HTTPServer(self.app, ssl_options={ 'certfile': self.https_cert, 'keyfile': self.https_key }) else: protocol = 'http' self.server = HTTPServer(self.app) log.info( 'Starting Medusa on {scheme}://{host}:{port}{web_root}/', { 'scheme': protocol, 'host': self.options['host'], 'port': self.options['port'], 'web_root': self.options['theme_path'] }) try: self.server.listen(self.options['port'], self.options['host']) except Exception as ex: if app.LAUNCH_BROWSER and not self.daemon: app.instance.launch_browser( 'https' if app.ENABLE_HTTPS else 'http', self.options['port'], app.WEB_ROOT) log.info('Launching browser and exiting') log.info( 'Could not start the web server on port {port}. Exception: {ex}', { 'port': self.options['port'], 'ex': ex }) os._exit(1) # pylint: disable=protected-access try: self.io_loop = IOLoop.current() self.io_loop.start() except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop() def log_request(self, handler): """ Write a completed HTTP request to the logs. This method handles logging Tornado requests. """ if not app.WEB_LOG: return level = None if handler.get_status() < 400: level = logging.INFO elif handler.get_status() < 500: # Don't log normal APIv2 RESTful responses as warnings if isinstance(handler, BaseRequestHandler): level = logging.INFO else: level = logging.WARNING else: # If a real exception was raised in APIv2, # let `BaseRequestHandler.log_exception` handle the logging if not isinstance(handler, BaseRequestHandler): level = logging.ERROR if level is None: return log.log( level, '{status} {summary} {time:.2f}ms', { 'status': handler.get_status(), 'summary': handler._request_summary(), 'time': 1000.0 * handler.request.request_time() })
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', None) assert isinstance(self.options['port'], int) assert 'data_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/')) if self.options[ 'web_root'] else '' # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=True, autoreload=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=' ) # Main Handler self.app.add_handlers(".*$", [ (r'%s/api/(.*)(/?)' % self.options['web_root'], webapi.Api), (r'%s/(.*)(/?)' % self.options['web_root'], webserve.MainHandler), (r'(.*)', webserve.MainHandler) ]) # Static Path Handler self.app.add_handlers(".*$", [ (r'%s/(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler, {'paths': [os.path.join(self.options['data_root'], 'images/ico/favicon.ico')]}), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler, {'paths': [os.path.join(self.options['data_root'], 'images'), os.path.join(sickbeard.CACHE_DIR, 'images')]}), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler, {'paths': [os.path.join(self.options['data_root'], 'css')]}), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler, {'paths': [os.path.join(self.options['data_root'], 'js')]}), ]) # Static Videos Path if self.video_root: self.app.add_handlers(".*$", [ (r'%s/%s/(.*)' % (self.options['web_root'], 'videos'), MultiStaticFileHandler, {'paths': [self.video_root]}), ]) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, no_keep_alive=True, ssl_options={"certfile": self.https_cert, "keyfile": self.https_key}) else: protocol = "http" self.server = HTTPServer(self.app, no_keep_alive=True) logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str( self.options['port']) + "/") try: self.server.listen(self.options['port'], self.options['host']) except: etype, evalue, etb = sys.exc_info() logger.log( "Could not start webserver on %s. Excpeption: %s, Error: %s" % (self.options['port'], etype, evalue), logger.ERROR) return try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False if self.server: logger.log("Shutting down tornado") self.server.stop() self.io_loop.stop() self.join()
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'%s/api/%s' % (sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application([], debug=True, autoreload=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=', login_url='%s/login/' % self.options['web_root'], ) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'%s(/?.*)' % self.options['api_root'], ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % self.options['web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), # webui login/logout handlers (r'%s/login(/?)' % self.options['web_root'], LoginHandler), (r'%s/logout(/?)' % self.options['web_root'], LogoutHandler), # webui handlers ] + route.get_routes(self.options['web_root'])) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'%s/(favicon\.ico)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'%s/images/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'images')}), # cached images (r'%s/cache/images/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(sickbeard.CACHE_DIR, 'images')}), # css (r'%s/css/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'css')}), # javascript (r'%s/js/(.*)' % self.options['web_root'], StaticFileHandler, {"path": os.path.join(self.options['data_root'], 'js')}), # videos ] + [(r'%s/videos/(.*)' % self.options['web_root'], StaticFileHandler, {"path": self.video_root})]) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, ssl_options={"certfile": self.https_cert, "keyfile": self.https_key}) else: protocol = "http" self.server = HTTPServer(self.app) logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str( self.options['port']) + "/") try: self.server.listen(self.options['port'], self.options['host']) except: etype, evalue, etb = sys.exc_info() logger.log( "Could not start webserver on %s. Excpeption: %s, Error: %s" % (self.options['port'], etype, evalue), logger.ERROR) return try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
class srWebServer(object): def __init__(self): self.started = False def start(self): self.started = True # video root self.video_root = None if sickrage.srCore.srConfig.ROOT_DIRS: root_dirs = sickrage.srCore.srConfig.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] # web root if sickrage.srCore.srConfig.WEB_ROOT: sickrage.srCore.srConfig.WEB_ROOT = sickrage.srCore.srConfig.WEB_ROOT = ( '/' + sickrage.srCore.srConfig.WEB_ROOT.lstrip('/').strip('/')) # api root if not sickrage.srCore.srConfig.API_KEY: sickrage.srCore.srConfig.API_KEY = generateApiKey() self.api_root = r'%s/api/%s' % (sickrage.srCore.srConfig.WEB_ROOT, sickrage.srCore.srConfig.API_KEY) # tornado setup if sickrage.srCore.srConfig.ENABLE_HTTPS: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (sickrage.srCore.srConfig.HTTPS_CERT and os.path.exists( sickrage.srCore.srConfig.HTTPS_CERT)) or not ( sickrage.srCore.srConfig.HTTPS_KEY and os.path.exists( sickrage.srCore.srConfig.HTTPS_KEY)): if not create_https_certificates( sickrage.srCore.srConfig.HTTPS_CERT, sickrage.srCore.srConfig.HTTPS_KEY): sickrage.srCore.srLogger.info( "Unable to create CERT/KEY files, disabling HTTPS") sickrage.srCore.srConfig.ENABLE_HTTPS = False if not (os.path.exists(sickrage.srCore.srConfig.HTTPS_CERT) and os.path.exists(sickrage.srCore.srConfig.HTTPS_KEY)): sickrage.srCore.srLogger.warning( "Disabled HTTPS because of missing CERT and KEY files") sickrage.srCore.srConfig.ENABLE_HTTPS = False # Load the app self.app = Application( [], debug=False, autoreload=False, gzip=sickrage.srCore.srConfig.WEB_USE_GZIP, xheaders=sickrage.srCore.srConfig.HANDLE_REVERSE_PROXY, cookie_secret=sickrage.srCore.srConfig.WEB_COOKIE_SECRET, login_url='%s/login/' % sickrage.srCore.srConfig.WEB_ROOT) # Main Handlers self.app.add_handlers( '.*$', [ # webapi handler (r'%s(/?.*)' % self.api_root, ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % sickrage.srCore.srConfig.WEB_ROOT, KeyHandler), # webapi builder redirect (r'%s/api/builder' % sickrage.srCore.srConfig.WEB_ROOT, RedirectHandler, { "url": sickrage.srCore.srConfig.WEB_ROOT + '/apibuilder/' }), # webui login/logout handlers (r'%s/login(/?)' % sickrage.srCore.srConfig.WEB_ROOT, LoginHandler), (r'%s/logout(/?)' % sickrage.srCore.srConfig.WEB_ROOT, LogoutHandler), # webui handlers ] + Route.get_routes(sickrage.srCore.srConfig.WEB_ROOT)) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % sickrage.srCore.srConfig.WEB_ROOT, CalendarHandler), ]) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon (r'%s/(favicon\.ico)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'images/ico/favicon.ico') }), # images (r'%s.*?/images/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticImageHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'images') }), # css (r'%s/css/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'css') }), # scss (r'%s/scss/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'scss') }), # fonts (r'%s/fonts/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'fonts') }), # javascript (r'%s/js/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'js') }), # videos ] + [(r'%s/videos/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, { "path": self.video_root })]) self.server = HTTPServer(self.app, no_keep_alive=True) if sickrage.srCore.srConfig.ENABLE_HTTPS: self.server.ssl_options = { "certfile": sickrage.srCore.srConfig.HTTPS_CERT, "keyfile": sickrage.srCore.srConfig.HTTPS_KEY } try: self.server.listen(sickrage.srCore.srConfig.WEB_PORT, None) except socket.error as e: print(e.message) raise # launch browser window if all( [not sickrage.NOLAUNCH, sickrage.srCore.srConfig.LAUNCH_BROWSER]): threading.Thread( None, lambda: launch_browser( ('http', 'https')[sickrage.srCore.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srCore.srConfig.WEB_PORT)).start() # clear mako cache folder makocache = os.path.join(sickrage.srCore.srConfig.CACHE_DIR, 'mako') if os.path.isdir(makocache): shutil.rmtree(makocache) sickrage.srCore.srLogger.info("SiCKRAGE :: STARTED") sickrage.srCore.srLogger.info("SiCKRAGE :: VERSION:[{}]".format( sickrage.srCore.VERSIONUPDATER.updater.version)) sickrage.srCore.srLogger.info("SiCKRAGE :: CONFIG:[{}]".format( sickrage.CONFIG_FILE)) sickrage.srCore.srLogger.info("SiCKRAGE :: URL:[{}://{}:{}/]".format( ('http', 'https')[sickrage.srCore.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srCore.srConfig.WEB_PORT)) def shutdown(self): if self.started: self.server.close_all_connections() self.server.stop() self.started = False
class WebServer(object): def __init__(self): super(WebServer, self).__init__() self.name = "TORNADO" self.daemon = True self.started = False self.video_root = None self.api_root = None self.app = None self.server = None def start(self): self.started = True # load languages tornado.locale.load_gettext_translations(sickrage.LOCALE_DIR, 'messages') # clear mako cache folder mako_cache = os.path.join(sickrage.app.cache_dir, 'mako') if os.path.isdir(mako_cache): shutil.rmtree(mako_cache) # video root if sickrage.app.config.root_dirs: root_dirs = sickrage.app.config.root_dirs.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] # web root if sickrage.app.config.web_root: sickrage.app.config.web_root = sickrage.app.config.web_root = ( '/' + sickrage.app.config.web_root.lstrip('/').strip('/')) # api root self.api_root = r'%s/api/%s' % (sickrage.app.config.web_root, sickrage.app.config.api_key) # tornado setup if sickrage.app.config.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not ( sickrage.app.config.https_cert and os.path.exists( sickrage.app.config.https_cert)) or not ( sickrage.app.config.https_key and os.path.exists(sickrage.app.config.https_key)): if not create_https_certificates(sickrage.app.config.https_cert, sickrage.app.config.https_key): sickrage.app.log.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.app.config.enable_https = False if not (os.path.exists(sickrage.app.config.https_cert) and os.path.exists( sickrage.app.config.https_key)): sickrage.app.log.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.app.config.enable_https = False # Load the app self.app = Application( debug=True, autoreload=False, gzip=sickrage.app.config.web_use_gzip, cookie_secret=sickrage.app.config.web_cookie_secret, login_url='%s/login/' % sickrage.app.config.web_root) # Websocket handler self.app.add_handlers(".*$", [ (r'%s/ws/ui' % sickrage.app.config.web_root, WebSocketUIHandler) ]) # Static File Handlers self.app.add_handlers('.*$', [ # api (r'%s/api/(\w{32})(/?.*)' % sickrage.app.config.web_root, ApiHandler), # redirect to home (r"(%s)" % sickrage.app.config.web_root, RedirectHandler, {"url": "%s/home" % sickrage.app.config.web_root}), # api builder (r'%s/api/builder' % sickrage.app.config.web_root, RedirectHandler, {"url": sickrage.app.config.web_root + '/apibuilder/'}), # login (r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler), # logout (r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler), # calendar (r'%s/calendar' % sickrage.app.config.web_root, CalendarHandler), # favicon (r'%s/(favicon\.ico)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images/favicon.ico')}), # images (r'%s/images/(.*)' % sickrage.app.config.web_root, StaticImageHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'images')}), # css (r'%s/css/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'css')}), # scss (r'%s/scss/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'scss')}), # fonts (r'%s/fonts/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'fonts')}), # javascript (r'%s/js/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": os.path.join(sickrage.app.config.gui_static_dir, 'js')}), # videos (r'%s/videos/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler, {"path": self.video_root}), ]) # Web Handlers self.app.add_handlers('.*$', Route.get_routes(sickrage.app.config.web_root)) # HTTPS Cert/Key object ssl_ctx = None if sickrage.app.config.enable_https: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(sickrage.app.config.https_cert, sickrage.app.config.https_key) # Web Server self.server = HTTPServer(self.app, ssl_options=ssl_ctx, xheaders=sickrage.app.config.handle_reverse_proxy) try: self.server.listen(sickrage.app.config.web_port) sickrage.app.log.info( "SiCKRAGE :: STARTED") sickrage.app.log.info( "SiCKRAGE :: VERSION:[{}]".format(sickrage.version())) sickrage.app.log.info( "SiCKRAGE :: CONFIG:[{}] [v{}]".format(sickrage.app.config_file, sickrage.app.config.config_version)) sickrage.app.log.info( "SiCKRAGE :: DATABASE:[v{}]".format(sickrage.app.main_db.version)) sickrage.app.log.info( "SiCKRAGE :: URL:[{}://{}:{}{}]".format(('http', 'https')[sickrage.app.config.enable_https], sickrage.app.config.web_host, sickrage.app.config.web_port, sickrage.app.config.web_root)) except socket.error as e: sickrage.app.log.warning(e.strerror) raise SystemExit def shutdown(self): if self.started: self.started = False if self.server: self.server.close_all_connections() self.server.stop()
class HTTPD(object): # noqa """ HTTP Server implementation that attaches to an event loop and socket, and is capable of handling mesos wire protocol messages. """ def __init__(self, sock, loop): """ Construct an HTTP server on a socket given an ioloop. """ self.loop = loop self.sock = sock self.app = Application(handlers=[(r'/.*$', Blackhole)]) self.server = HTTPServer(self.app, io_loop=self.loop) self.server.add_sockets([sock]) self.sock.listen(1024) def terminate(self): log.info('Terminating HTTP server and all connections') self.server.close_all_connections() self.sock.close() def mount_process(self, process): """ Mount a Process onto the http server to receive message callbacks. """ for route_path in process.route_paths: route = '/%s%s' % (process.pid.id, route_path) log.info('Mounting route %s' % route) self.app.add_handlers('.*$', [(re.escape(route), RoutedRequestHandler, dict(process=process, path=route_path))]) for message_name in process.message_names: route = '/%s/%s' % (process.pid.id, message_name) log.info('Mounting message handler %s' % route) self.app.add_handlers( '.*$', [(re.escape(route), WireProtocolMessageHandler, dict(process=process, name=message_name))]) def unmount_process(self, process): """ Unmount a process from the http server to stop receiving message callbacks. """ # There is no remove_handlers, but .handlers is public so why not. server.handlers is a list of # 2-tuples of the form (host_pattern, [list of RequestHandler]) objects. We filter out all # handlers matching our process from the RequestHandler list for each host pattern. def nonmatching(handler): return 'process' not in handler.kwargs or handler.kwargs[ 'process'] != process def filter_handlers(handlers): host_pattern, handlers = handlers return (host_pattern, list(filter(nonmatching, handlers))) self.app.handlers = [ filter_handlers(handlers) for handlers in self.app.handlers ]
class SRWebServer(threading.Thread): # pylint: disable=too-many-instance-attributes def __init__(self, options=None, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'{0}/api/{1}'.format(sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log("Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)): logger.log("Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='{0}/login/'.format(self.options['web_root']), ) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'{0}/(favicon\.ico)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'{0}/images/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, self.options['data_root'], 'images')}), # cached images (r'{0}/cache/images/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, sickbeard.CACHE_DIR, 'images')}), # css (r'{0}/css/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, self.options['data_root'], 'css')}), # javascript (r'{0}/js/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, self.options['data_root'], 'js')}), # fonts (r'{0}/fonts/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, self.options['data_root'], 'fonts')}), # videos (r'{0}/videos/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": self.video_root}) ]) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'{0}(/?.*)'.format(self.options['api_root']), ApiHandler), # webapi key retrieval (r'{0}/getkey(/?.*)'.format(self.options['web_root']), KeyHandler), # webapi builder redirect (r'{0}/api/builder'.format(self.options['web_root']), RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), # webui login/logout handlers (r'{0}/login(/?)'.format(self.options['web_root']), LoginHandler), (r'{0}/logout(/?)'.format(self.options['web_root']), LogoutHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{0}/calendar'.format(self.options['web_root']), CalendarHandler), # webui handlers ] + route.get_routes(self.options['web_root'])) def run(self): if self.enable_https: protocol = "https" ssl_options = {"certfile": self.https_cert, "keyfile": self.https_key} else: protocol = "http" ssl_options = None logger.log("Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str( self.options['port']) + "/") try: self.server = self.app.listen(self.options['port'], self.options['host'], ssl_options=ssl_options, xheaders=sickbeard.HANDLE_REVERSE_PROXY, protocol=protocol) except SocketError as ex: err_msg = "" if ex.errno == errno.EADDRINUSE: # Address/port combination already in use if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser('https' if sickbeard.ENABLE_HTTPS else 'http', self.options['port'], sickbeard.WEB_ROOT) logger.log("Launching browser and exiting") err_msg = "already in use!" logger.log("Could not start webserver on port {0}: {1}".format(self.options['port'], err_msg or ex)) os._exit(1) # pylint: disable=protected-access except Exception as ex: logger.log("Could not start webserver on port {0}: {1}".format(self.options['port'], ex)) os._exit(1) # pylint: disable=protected-access try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutdown(self): self.alive = False self.io_loop.stop()
def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, Env = None, desktop = None): try: locale.setlocale(locale.LC_ALL, "") encoding = locale.getpreferredencoding() except (locale.Error, IOError): encoding = None # for OSes that are poorly configured I'll just force UTF-8 if not encoding or encoding in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'): encoding = 'UTF-8' Env.set('encoding', encoding) # Do db stuff db_path = sp(os.path.join(data_dir, 'database')) # Check if database exists db = SuperThreadSafeDatabase(db_path) db_exists = db.exists() if db_exists: # Backup before start and cleanup old backups backup_path = sp(os.path.join(data_dir, 'db_backup')) backup_count = 5 existing_backups = [] if not os.path.isdir(backup_path): os.makedirs(backup_path) for root, dirs, files in os.walk(backup_path): for backup_file in sorted(files): ints = re.findall('\d+', backup_file) # Delete non zip files if len(ints) != 1: os.remove(os.path.join(backup_path, backup_file)) else: existing_backups.append((int(ints[0]), backup_file)) # Remove all but the last 5 for eb in existing_backups[:-backup_count]: os.remove(os.path.join(backup_path, eb[1])) # Create new backup new_backup = sp(os.path.join(backup_path, '%s.tar.gz' % int(time.time()))) zipf = tarfile.open(new_backup, 'w:gz') for root, dirs, files in os.walk(db_path): for zfilename in files: zipf.add(os.path.join(root, zfilename), arcname = 'database/%s' % os.path.join(root[len(db_path) + 1:], zfilename)) zipf.close() # Open last db.open() else: db.create() # Force creation of cachedir log_dir = sp(log_dir) cache_dir = sp(os.path.join(data_dir, 'cache')) python_cache = sp(os.path.join(cache_dir, 'python')) if not os.path.exists(cache_dir): os.mkdir(cache_dir) if not os.path.exists(python_cache): os.mkdir(python_cache) # Register environment settings Env.set('app_dir', sp(base_path)) Env.set('data_dir', sp(data_dir)) Env.set('log_path', sp(os.path.join(log_dir, 'CouchPotato.log'))) Env.set('db', db) Env.set('cache_dir', cache_dir) Env.set('cache', FileSystemCache(python_cache)) Env.set('console_log', options.console_log) Env.set('quiet', options.quiet) Env.set('desktop', desktop) Env.set('daemonized', options.daemon) Env.set('args', args) Env.set('options', options) # Determine debug debug = options.debug or Env.setting('debug', default = False, type = 'bool') Env.set('debug', debug) # Development development = Env.setting('development', default = False, type = 'bool') Env.set('dev', development) # Disable logging for some modules for logger_name in ['enzyme', 'guessit', 'subliminal', 'apscheduler', 'tornado', 'requests']: logging.getLogger(logger_name).setLevel(logging.ERROR) for logger_name in ['gntp']: logging.getLogger(logger_name).setLevel(logging.WARNING) # Use reloader reloader = debug is True and development and not Env.get('desktop') and not options.daemon # Logger logger = logging.getLogger() formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') level = logging.DEBUG if debug else logging.INFO logger.setLevel(level) logging.addLevelName(19, 'INFO') # To screen if (debug or options.console_log) and not options.quiet and not options.daemon: hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(formatter) logger.addHandler(hdlr) # To file hdlr2 = handlers.RotatingFileHandler(Env.get('log_path'), 'a', 500000, 10, encoding = Env.get('encoding')) hdlr2.setFormatter(formatter) logger.addHandler(hdlr2) # Start logging & enable colors # noinspection PyUnresolvedReferences import color_logs from couchpotato.core.logger import CPLog log = CPLog(__name__) log.debug('Started with options %s', options) def customwarn(message, category, filename, lineno, file = None, line = None): log.warning('%s %s %s line:%s', (category, message, filename, lineno)) warnings.showwarning = customwarn # Create app from couchpotato import WebHandler web_base = ('/' + Env.setting('url_base').lstrip('/') + '/') if Env.setting('url_base') else '/' Env.set('web_base', web_base) api_key = Env.setting('api_key') if not api_key: api_key = uuid4().hex Env.setting('api_key', value = api_key) api_base = r'%sapi/%s/' % (web_base, api_key) Env.set('api_base', api_base) # Basic config host = Env.setting('host', default = '0.0.0.0') # app.debug = development config = { 'use_reloader': reloader, 'port': tryInt(Env.setting('port', default = 5050)), 'host': host if host and len(host) > 0 else '0.0.0.0', 'ssl_cert': Env.setting('ssl_cert', default = None), 'ssl_key': Env.setting('ssl_key', default = None), } # Load the app application = Application( [], log_function = lambda x: None, debug = config['use_reloader'], gzip = True, cookie_secret = api_key, login_url = '%slogin/' % web_base, ) Env.set('app', application) # Request handlers application.add_handlers(".*$", [ (r'%snonblock/(.*)(/?)' % api_base, NonBlockHandler), # API handlers (r'%s(.*)(/?)' % api_base, ApiHandler), # Main API handler (r'%sgetkey(/?)' % web_base, KeyHandler), # Get API key (r'%s' % api_base, RedirectHandler, {"url": web_base + 'docs/'}), # API docs # Login handlers (r'%slogin(/?)' % web_base, LoginHandler), (r'%slogout(/?)' % web_base, LogoutHandler), # Catch all webhandlers (r'%s(.*)(/?)' % web_base, WebHandler), (r'(.*)', WebHandler), ]) # Static paths static_path = '%sstatic/' % web_base for dir_name in ['fonts', 'images', 'scripts', 'style']: application.add_handlers(".*$", [ ('%s%s/(.*)' % (static_path, dir_name), StaticFileHandler, {'path': sp(os.path.join(base_path, 'couchpotato', 'static', dir_name))}) ]) Env.set('static_path', static_path) # Load configs & plugins loader = Env.get('loader') loader.preload(root = sp(base_path)) loader.run() # Fill database with needed stuff fireEvent('database.setup') if not db_exists: fireEvent('app.initialize', in_order = True) fireEvent('app.migrate') # Go go go! from tornado.ioloop import IOLoop from tornado.autoreload import add_reload_hook loop = IOLoop.current() # Reload hook def test(): fireEvent('app.shutdown') add_reload_hook(test) # Some logging and fire load event try: log.info('Starting server on port %(port)s', config) except: pass fireEventAsync('app.load') if config['ssl_cert'] and config['ssl_key']: server = HTTPServer(application, no_keep_alive = True, ssl_options = { 'certfile': config['ssl_cert'], 'keyfile': config['ssl_key'], }) else: server = HTTPServer(application, no_keep_alive = True) try_restart = True restart_tries = 5 while try_restart: try: server.listen(config['port'], config['host']) loop.start() except Exception as e: log.error('Failed starting: %s', traceback.format_exc()) try: nr, msg = e if nr == 48: log.info('Port (%s) needed for CouchPotato is already in use, try %s more time after few seconds', (config.get('port'), restart_tries)) time.sleep(1) restart_tries -= 1 if restart_tries > 0: continue else: return except: pass raise try_restart = False
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options[b'port'], int) assert 'data_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options[b'web_root']: sickbeard.WEB_ROOT = self.options[b'web_root'] = ( '/' + self.options[b'web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options[b'api_root'] = r'%s/api/%s' % (sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options[b'enable_https'] self.https_cert = self.options[b'https_cert'] self.https_key = self.options[b'https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logging.info( "Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and ek(os.path.exists, self.https_key)): logging.warning( "Disabled HTTPS because of missing CERT and KEY files") sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=sickbeard.DEBUG, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='%s/login/' % self.options[b'web_root'], ) # Main Handlers self.app.add_handlers( '.*$', [ # webapi handler (r'%s(/?.*)' % self.options[b'api_root'], ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % self.options[b'web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options[b'web_root'], RedirectHandler, { "url": self.options[b'web_root'] + '/apibuilder/' }), # webui login/logout handlers (r'%s/login(/?)' % self.options[b'web_root'], LoginHandler), (r'%s/logout(/?)' % self.options[b'web_root'], LogoutHandler), # webui handlers ] + route.get_routes(self.options[b'web_root'])) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % self.options[b'web_root'], CalendarHandler), ]) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon (r'%s/(favicon\.ico)' % self.options[b'web_root'], StaticFileHandler, { "path": ek(os.path.join, self.options[b'data_root'], 'images/ico/favicon.ico') }), # images (r'%s/images/(.*)' % self.options[b'web_root'], StaticFileHandler, { "path": ek(os.path.join, self.options[b'data_root'], 'images') }), # cached images (r'%s/cache/images/(.*)' % self.options[b'web_root'], StaticFileHandler, { "path": ek(os.path.join, sickbeard.CACHE_DIR, 'images') }), # css (r'%s/css/(.*)' % self.options[b'web_root'], StaticFileHandler, { "path": ek(os.path.join, self.options[b'data_root'], 'css') }), # javascript (r'%s/js/(.*)' % self.options[b'web_root'], StaticFileHandler, { "path": ek(os.path.join, self.options[b'data_root'], 'js') }), # videos ] + [(r'%s/videos/(.*)' % self.options[b'web_root'], StaticFileHandler, { "path": self.video_root })]) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, ssl_options={ "certfile": self.https_cert, "keyfile": self.https_key }) else: protocol = "http" self.server = HTTPServer(self.app) logging.info("Starting SiCKRAGE web server on [{}://{}:{}/]".format( protocol, self.options[b'host'], self.options[b'port'])) try: self.server.listen(self.options[b'port'], self.options[b'host']) except: if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser( 'https' if sickbeard.ENABLE_HTTPS else 'http', self.options[b'port'], sickbeard.WEB_ROOT) logging.info("Launching browser and exiting") logging.info( "Could not start webserver on port %s, already in use!" % self.options[b'port']) ek(os._exit, 1) try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
class SRWebServer(threading.Thread): # pylint: disable=too-many-instance-attributes def __init__(self, options=None, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ( '/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'{0}/api/{1}'.format( sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log( "Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)): logger.log( "Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='{0}/login/'.format(self.options['web_root']), ) # Main Handlers self.app.add_handlers( '.*$', [ # webapi handler (r'{0}(/?.*)'.format(self.options['api_root']), ApiHandler), # webapi key retrieval (r'{0}/getkey(/?.*)'.format( self.options['web_root']), KeyHandler), # webapi builder redirect (r'{0}/api/builder'.format( self.options['web_root']), RedirectHandler, { "url": self.options['web_root'] + '/apibuilder/' }), # webui login/logout handlers (r'{0}/login(/?)'.format( self.options['web_root']), LoginHandler), (r'{0}/logout(/?)'.format( self.options['web_root']), LogoutHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{0}/calendar'.format( self.options['web_root']), CalendarHandler), # webui handlers ] + route.get_routes(self.options['web_root'])) # Static File Handlers self.app.add_handlers( ".*$", [ # favicon (r'{0}/(favicon\.ico)'.format( self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'images/ico/favicon.ico') }), # images (r'{0}/images/(.*)'.format( self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'images') }), # locale (r'{0}/locale/messages\.json'.format( self.options['web_root']), LocaleFileHandler, { "path": ek(os.path.join, sickbeard.LOCALE_DIR, '{lang_code}/LC_MESSAGES') }), # cached images (r'{0}/cache/images/(.*)'.format( self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, sickbeard.CACHE_DIR, 'images') }), # css (r'{0}/css/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'css') }), # javascript (r'{0}/js/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'js') }), # fonts (r'{0}/fonts/(.*)'.format( self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'fonts') }), # videos (r'{0}/videos/(.*)'.format( self.options['web_root']), StaticFileHandler, { "path": self.video_root }) ]) def run(self): if self.enable_https: protocol = "https" ssl_options = { "certfile": self.https_cert, "keyfile": self.https_key } else: protocol = "http" ssl_options = None logger.log("Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str(self.options['port']) + "/") try: self.server = self.app.listen( self.options['port'], self.options['host'], ssl_options=ssl_options, xheaders=sickbeard.HANDLE_REVERSE_PROXY, protocol=protocol) except Exception: if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser( 'https' if sickbeard.ENABLE_HTTPS else 'http', self.options['port'], sickbeard.WEB_ROOT) logger.log("Launching browser and exiting") logger.log( "Could not start webserver on port {0}, already in use!". format(self.options['port'])) os._exit(1) # pylint: disable=protected-access try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
def make_web_app(self): # Start with web application routes from unmanic.webserver.websocket import UnmanicWebsocketHandler app = Application([ (r"/unmanic/websocket", UnmanicWebsocketHandler), (r"/unmanic/downloads/(.*)", DownloadsHandler), (r"/(.*)", RedirectHandler, dict( url="/unmanic/ui/dashboard/" )), ], **tornado_settings) # Add API routes from unmanic.webserver.api_request_router import APIRequestRouter app.add_handlers(r'.*', [ ( PathMatches(r"/unmanic/api/.*"), APIRequestRouter(app) ), ]) # Add frontend routes from unmanic.webserver.main import MainUIRequestHandler app.add_handlers(r'.*', [ (r"/unmanic/css/(.*)", StaticFileHandler, dict( path=tornado_settings['static_css'] )), (r"/unmanic/fonts/(.*)", StaticFileHandler, dict( path=tornado_settings['static_fonts'] )), (r"/unmanic/icons/(.*)", StaticFileHandler, dict( path=tornado_settings['static_icons'] )), (r"/unmanic/img/(.*)", StaticFileHandler, dict( path=tornado_settings['static_img'] )), (r"/unmanic/js/(.*)", StaticFileHandler, dict( path=tornado_settings['static_js'] )), ( PathMatches(r"/unmanic/ui/(.*)"), MainUIRequestHandler, ), ]) # Add widgets routes from unmanic.webserver.plugins import DataPanelRequestHandler from unmanic.webserver.plugins import PluginStaticFileHandler from unmanic.webserver.plugins import PluginAPIRequestHandler app.add_handlers(r'.*', [ ( PathMatches(r"/unmanic/panel/[^/]+(/(?!static/|assets$).*)?$"), DataPanelRequestHandler ), ( PathMatches(r"/unmanic/plugin_api/[^/]+(/(?!static/|assets$).*)?$"), PluginAPIRequestHandler ), (r"/unmanic/panel/.*/static/(.*)", PluginStaticFileHandler, dict( path=tornado_settings['static_img'] )), ]) if self.developer: self._log("API Docs - Updating...", level="debug") try: from unmanic.webserver.api_v2.schema.swagger import generate_swagger_file errors = generate_swagger_file() for error in errors: self._log(error, level="warn") else: self._log("API Docs - Updated successfully", level="debug") except Exception as e: self._log("Failed to reload API schema", message2=str(e), level="error") # Start the Swagger UI. Automatically generated swagger.json can also # be served using a separate Swagger-service. from swagger_ui import tornado_api_doc tornado_api_doc( app, config_path=os.path.join(os.path.dirname(__file__), "..", "webserver", "docs", "api_schema_v2.json"), url_prefix="/unmanic/swagger", title="Unmanic application API" ) return app
class SRWebServer(threading.Thread): # pylint: disable=too-many-instance-attributes def __init__(self, options=None, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.io_loop = io_loop or IOLoop.current() self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'{root}/api/{key}'.format(root=sickbeard.WEB_ROOT, key=sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log('Unable to create CERT/KEY files, disabling HTTPS') sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)): logger.log('Disabled HTTPS because of missing CERT and KEY files', logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=sickbeard.WEB_USE_GZIP, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url=r'{root}/login/'.format(root=self.options['web_root']), ) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'{base}(/?.*)'.format(base=self.options['api_root']), ApiHandler), # webapi key retrieval (r'{base}/getkey(/?.*)'.format(base=self.options['web_root']), KeyHandler), # webapi builder redirect (r'{base}/api/builder'.format(base=self.options['web_root']), RedirectHandler, {'url': '{base}/apibuilder/'.format(base=self.options['web_root'])}), # webui login/logout handlers (r'{base}/login(/?)'.format(base=self.options['web_root']), LoginHandler), (r'{base}/logout(/?)'.format(base=self.options['web_root']), LogoutHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{base}/calendar'.format(base=self.options['web_root']), CalendarHandler), # webui handlers ] + route.get_routes(self.options['web_root'])) # Static File Handlers self.app.add_handlers('.*$', [ # favicon (r'{base}/(favicon\.ico)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'{base}/images/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, self.options['data_root'], 'images')}), # cached images (r'{base}/cache/images/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, sickbeard.CACHE_DIR, 'images')}), # css (r'{base}/css/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, self.options['data_root'], 'css')}), # javascript (r'{base}/js/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, self.options['data_root'], 'js')}), # fonts (r'{base}/fonts/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': ek(os.path.join, self.options['data_root'], 'fonts')}), # videos (r'{base}/videos/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': self.video_root}) ]) def run(self): if self.enable_https: protocol = 'https' self.server = HTTPServer(self.app, ssl_options={'certfile': self.https_cert, 'keyfile': self.https_key}) else: protocol = 'http' self.server = HTTPServer(self.app) logger.log('Starting Medusa on {scheme}://{host}:{port}/'.format (scheme=protocol, host=self.options['host'], port=self.options['port'])) try: self.server.listen(self.options['port'], self.options['host']) except Exception: if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser('https' if sickbeard.ENABLE_HTTPS else 'http', self.options['port'], sickbeard.WEB_ROOT) logger.log('Launching browser and exiting') logger.log('Could not start the web server on port {port}, already in use!'.format(port=self.options['port'])) os._exit(1) # pylint: disable=protected-access try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
class SRWebServer(threading.Thread): def __init__(self, options=None): super().__init__() self.daemon = True self.alive = True self.name = "WEBSERVER" self.options = options if settings.WEB_HOST and settings.WEB_HOST != "0.0.0.0": web_host = settings.WEB_HOST else: web_host = ("0.0.0.0", "")[settings.WEB_IPV6] self.options.update({ "data_root": os.path.join(settings.PROG_DIR, "gui", settings.GUI_NAME), "web_root": settings.WEB_ROOT, "host": web_host, "enable_https": settings.ENABLE_HTTPS, "handle_reverse_proxy": settings.HANDLE_REVERSE_PROXY, "log_dir": (None, settings.LOG_DIR)[settings.WEB_LOG], # 'username': oldbeard.WEB_USERNAME or '', # 'password': oldbeard.WEB_PASSWORD or '', }) self.options.setdefault("port", settings.WEB_PORT or 8081) self.server = None # video root if settings.ROOT_DIRS: root_dirs = settings.ROOT_DIRS.split("|") self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options["web_root"]: settings.WEB_ROOT = self.options[ "web_root"] = "/" + self.options["web_root"].strip("/") # api root if not settings.API_KEY: settings.API_KEY = generateApiKey() self.options[ "api_root"] = f"{settings.WEB_ROOT}/api/{settings.API_KEY}" # tornado setup self.enable_https = self.options["enable_https"] self.https_cert = None if settings.HTTPS_CERT: self.https_cert = os.path.realpath(settings.HTTPS_CERT) if not os.path.exists(self.https_cert) and not os.path.isabs( self.https_cert): self.https_cert = os.path.realpath( os.path.join(settings.DATA_DIR, settings.HTTPS_CERT)) self.https_key = None if settings.HTTPS_KEY: self.https_key = os.path.realpath(settings.HTTPS_KEY) if not os.path.exists(self.https_key) and not os.path.isabs( self.https_key): self.https_key = os.path.realpath( os.path.join(settings.DATA_DIR, settings.HTTPS_KEY)) if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert) and self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.info( "Unable to create CERT/KEY files, disabling HTTPS") settings.ENABLE_HTTPS = self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.warning( "Disabled HTTPS because of missing CERT and KEY files") settings.ENABLE_HTTPS = self.enable_https = False asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) # Load the app self.app = Application( [], debug= False, # enables autoreload, compiled_template_cache, static_hash_cache, serve_traceback - This fixes the 404 page and fixes autoreload for # devs. We could now update without restart possibly if we check DB version hasnt changed! autoreload=False, gzip=settings.WEB_USE_GZIP, cookie_secret=settings.WEB_COOKIE_SECRET, login_url=f'{self.options["web_root"]}/login/', static_path=self.options["data_root"], static_url_prefix=f'{self.options["web_root"]}/', static_handler_class=SickChillStaticFileHandler # default_handler_class=Custom404Handler ) # Static File Handlers self.app.add_handlers( ".*$", [ url( rf'{self.options["web_root"]}/(favicon\.ico)', SickChillStaticFileHandler, { "path": os.path.join(self.options["data_root"], "images/ico") }, name="favicon", ), url( rf'{self.options["web_root"]}/images/(.*)', SickChillStaticFileHandler, { "path": os.path.join(self.options["data_root"], "images") }, name="images", ), url( rf'{self.options["web_root"]}/cache/images/(.*)', SickChillStaticFileHandler, {"path": os.path.join(settings.CACHE_DIR, "images")}, name="image_cache", ), url(rf'{self.options["web_root"]}/css/(.*)', SickChillStaticFileHandler, {"path": os.path.join(self.options["data_root"], "css")}, name="css"), url(rf'{self.options["web_root"]}/js/(.*)', SickChillStaticFileHandler, {"path": os.path.join(self.options["data_root"], "js")}, name="js"), url( rf'{self.options["web_root"]}/fonts/(.*)', SickChillStaticFileHandler, {"path": os.path.join(self.options["data_root"], "fonts")}, name="fonts", ) # TODO: WTF is this? # url(rf'{self.options["web_root"]}/videos/(.*)', SickChillStaticFileHandler, # {"path": self.video_root}, name='videos') ], ) # Main Handlers self.app.add_handlers( ".*$", [ url(rf'{self.options["api_root"]}(/?.*)', ApiHandler, name="api"), url(rf'{self.options["web_root"]}/getkey(/?.*)', KeyHandler, name="get_api_key"), url(rf'{self.options["web_root"]}/api/builder', RedirectHandler, {"url": self.options["web_root"] + "/apibuilder/"}, name="apibuilder"), url(rf'{self.options["web_root"]}/login(/?)', LoginHandler, name="login"), url(rf'{self.options["web_root"]}/logout(/?)', LogoutHandler, name="logout"), url(rf'{self.options["web_root"]}/calendar/?', CalendarHandler, name="calendar"), url(rf'{self.options["web_root"]}/movies/(?P<route>details)/(?P<slug>.*)/', MoviesHandler, name="movies-details"), url(rf'{self.options["web_root"]}/movies/(?P<route>remove)/(?P<pk>.*)/', MoviesHandler, name="movies-remove"), url(rf'{self.options["web_root"]}/movies/(?P<route>add)/', MoviesHandler, name="movies-add"), url(rf'{self.options["web_root"]}/movies/(?P<route>search)/', MoviesHandler, name="movies-search"), url(rf'{self.options["web_root"]}/movies/(?P<route>list)/', MoviesHandler, name="movies-list"), url(rf'{self.options["web_root"]}/movies/(.*)', MoviesHandler, name="movies"), # routes added by @route decorator # Plus naked index with missing web_root prefix ] + Route.get_routes(self.options["web_root"]), ) def run(self): if self.enable_https: protocol = "https" ssl_options = { "certfile": self.https_cert, "keyfile": self.https_key } else: protocol = "http" ssl_options = None logger.info("Starting SickChill on " + protocol + "://" + str(self.options["host"]) + ":" + str(self.options["port"]) + "/") try: self.server = self.app.listen( self.options["port"], self.options["host"], ssl_options=ssl_options, xheaders=settings.HANDLE_REVERSE_PROXY, protocol=protocol) except socket_error as ex: err_msg = "" if ex.errno == errno.EADDRINUSE: # Address/port combination already in use if settings.LAUNCH_BROWSER and not self.daemon: sickchill.start.launchBrowser( "https" if settings.ENABLE_HTTPS else "http", self.options["port"], settings.WEB_ROOT) logger.info("Launching browser and exiting") err_msg = "already in use!" logger.info( f"Could not start webserver on port {self.options['port']}: {err_msg or ex}" ) # noinspection PyProtectedMember os._exit(1) except Exception as ex: logger.info( f"Could not start webserver on port {self.options['port']}: {ex}" ) # noinspection PyProtectedMember os._exit(1) try: IOLoop.current().start() IOLoop.current().close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutdown(self): self.alive = False IOLoop.current().stop()
class AppWebServer(threading.Thread): # pylint: disable=too-many-instance-attributes def __init__(self, options=None, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = 'TORNADO' self.io_loop = io_loop or IOLoop.current() self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None # video root if app.ROOT_DIRS: root_dirs = app.ROOT_DIRS self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: app.WEB_ROOT = self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not app.API_KEY: app.API_KEY = generate_api_key() self.options['api_root'] = r'{root}/api/(?:v1/)?{key}'.format(root=app.WEB_ROOT, key=app.API_KEY) self.options['api_v2_root'] = r'{root}/api/v2'.format(root=app.WEB_ROOT) # websocket root self.options['web_socket'] = r'{root}/ws'.format(root=app.WEB_ROOT) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log('Unable to create CERT/KEY files, disabling HTTPS') app.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log('Disabled HTTPS because of missing CERT and KEY files', logger.WARNING) app.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=app.WEB_USE_GZIP, xheaders=app.HANDLE_REVERSE_PROXY, cookie_secret=app.WEB_COOKIE_SECRET, login_url=r'{root}/login/'.format(root=self.options['web_root']), ) self.app.add_handlers('.*$', get_apiv2_handlers(self.options['api_v2_root'])) # Websocket handler self.app.add_handlers(".*$", [ (r'{base}/ui(/?.*)'.format(base=self.options['web_socket']), MedusaWebSocketHandler.WebSocketUIHandler) ]) # Static File Handlers self.app.add_handlers('.*$', [ # favicon (r'{base}/(favicon\.ico)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}), # images (r'{base}/images/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'images')}), # cached images (r'{base}/cache/images/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(app.CACHE_DIR, 'images')}), # css (r'{base}/css/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'css')}), # javascript (r'{base}/js/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'js')}), # fonts (r'{base}/fonts/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['data_root'], 'fonts')}), # videos (r'{base}/videos/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': self.video_root}), # vue dist (r'{base}/vue/dist/(.*)'.format(base=self.options['web_root']), StaticFileHandler, {'path': os.path.join(self.options['vue_root'], 'dist')}), # vue index.html (r'{base}/vue/?.*()'.format(base=self.options['web_root']), AuthenticatedStaticFileHandler, {'path': os.path.join(self.options['vue_root'], 'index.html'), 'default_filename': 'index.html'}), ]) # API v1 handlers self.app.add_handlers('.*$', [ # Main handler (r'{base}(/?.*)'.format(base=self.options['api_root']), ApiHandler), # Key retrieval (r'{base}/getkey(/?.*)'.format(base=self.options['web_root']), KeyHandler), # Builder redirect (r'{base}/api/builder'.format(base=self.options['web_root']), RedirectHandler, {'url': '{base}/apibuilder/'.format(base=self.options['web_root'])}), # Webui login/logout handlers (r'{base}/login(/?)'.format(base=self.options['web_root']), LoginHandler), (r'{base}/logout(/?)'.format(base=self.options['web_root']), LogoutHandler), (r'{base}/token(/?)'.format(base=self.options['web_root']), TokenHandler), # Web calendar handler (Needed because option Unprotected calendar) (r'{base}/calendar'.format(base=self.options['web_root']), CalendarHandler), # webui handlers ] + self._get_webui_routes()) def _get_webui_routes(self): webroot = self.options['web_root'] route._routes = list(reversed([url(webroot + u.regex.pattern, u.handler_class, u.kwargs, u.name) for u in route.get_routes()])) return route.get_routes() def run(self): if self.enable_https: protocol = 'https' self.server = HTTPServer(self.app, ssl_options={'certfile': self.https_cert, 'keyfile': self.https_key}) else: protocol = 'http' self.server = HTTPServer(self.app) logger.log('Starting Medusa on {scheme}://{host}:{port}{web_root}/'.format (scheme=protocol, host=self.options['host'], port=self.options['port'], web_root=self.options['web_root'])) try: self.server.listen(self.options['port'], self.options['host']) except Exception: if app.LAUNCH_BROWSER and not self.daemon: app.instance.launch_browser('https' if app.ENABLE_HTTPS else 'http', self.options['port'], app.WEB_ROOT) logger.log('Launching browser and exiting') logger.log('Could not start the web server on port {port}, already in use!'.format(port=self.options['port'])) os._exit(1) # pylint: disable=protected-access try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload. pass def shutDown(self): self.alive = False self.io_loop.stop()
class SRWebServer(threading.Thread): def __init__(self, options={}, io_loop=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "TORNADO" self.io_loop = io_loop or IOLoop.current() self.options = options self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', None) assert isinstance(self.options['port'], int) assert 'data_root' in self.options # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/') ) if self.options['web_root'] else '' # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and os.path.exists(self.https_cert)) or not ( self.https_key and os.path.exists(self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log( u"Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)): logger.log( u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug=True, autoreload=False, gzip=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=') # Main Handler self.app.add_handlers( ".*$", [(r'%s/api/(.*)(/?)' % self.options['web_root'], webapi.Api), (r'%s/(.*)(/?)' % self.options['web_root'], webserve.MainHandler), (r'(.*)', webserve.MainHandler)]) # Static Path Handler self.app.add_handlers(".*$", [ (r'%s/(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler, { 'paths': [ os.path.join(self.options['data_root'], 'images/ico/favicon.ico') ] }), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler, { 'paths': [ os.path.join(self.options['data_root'], 'images'), os.path.join(sickbeard.CACHE_DIR, 'images') ] }), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler, { 'paths': [os.path.join(self.options['data_root'], 'css')] }), (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler, { 'paths': [os.path.join(self.options['data_root'], 'js')] }), ]) # Static Videos Path if self.video_root: self.app.add_handlers(".*$", [ (r'%s/%s/(.*)' % (self.options['web_root'], 'videos'), MultiStaticFileHandler, { 'paths': [self.video_root] }), ]) def run(self): if self.enable_https: protocol = "https" self.server = HTTPServer(self.app, ssl_options={ "certfile": self.https_cert, "keyfile": self.https_key }) else: protocol = "http" self.server = HTTPServer(self.app) logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str(self.options['port']) + "/") try: self.server.listen(self.options['port'], self.options['host']) except: etype, evalue, etb = sys.exc_info() logger.log( "Could not start webserver on %s. Excpeption: %s, Error: %s" % (self.options['port'], etype, evalue), logger.ERROR) return try: self.io_loop.start() self.io_loop.close(True) except (IOError, ValueError): # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutDown(self): self.alive = False if self.server: self.server.stop() self.io_loop.stop()
class SRWebServer(threading.Thread): def __init__(self, options=None): threading.Thread.__init__(self) self.daemon = True self.alive = True self.name = "WEBSERVER" self.options = options or {} self.options.setdefault('port', 8081) self.options.setdefault('host', '0.0.0.0') self.options.setdefault('log_dir', None) self.options.setdefault('username', '') self.options.setdefault('password', '') self.options.setdefault('web_root', '/') assert isinstance(self.options['port'], int) assert 'data_root' in self.options self.server = None # video root if sickbeard.ROOT_DIRS: root_dirs = sickbeard.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] else: self.video_root = None # web root if self.options['web_root']: sickbeard.WEB_ROOT = self.options['web_root'] = ( '/' + self.options['web_root'].lstrip('/').strip('/')) # api root if not sickbeard.API_KEY: sickbeard.API_KEY = generateApiKey() self.options['api_root'] = r'{0}/api/{1}'.format( sickbeard.WEB_ROOT, sickbeard.API_KEY) # tornado setup self.enable_https = self.options['enable_https'] self.https_cert = self.options['https_cert'] self.https_key = self.options['https_key'] if self.enable_https: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not ( self.https_key and ek(os.path.exists, self.https_key)): if not create_https_certificates(self.https_cert, self.https_key): logger.log( "Unable to create CERT/KEY files, disabling HTTPS") sickbeard.ENABLE_HTTPS = False self.enable_https = False if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)): logger.log( "Disabled HTTPS because of missing CERT and KEY files", logger.WARNING) sickbeard.ENABLE_HTTPS = False self.enable_https = False # Load the app self.app = Application( [], debug= False, # enables autoreload, compiled_template_cache, static_hash_cache, serve_traceback - This fixes the 404 page and fixes autoreload for # devs. We could now update without restart possibly if we check DB version hasnt changed! autoreload=False, gzip=sickbeard.WEB_USE_GZIP, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='{0}/login/'.format(self.options['web_root']), static_path=self.options['data_root'], static_url_prefix='{0}/'.format(self.options['web_root']) # default_handler_class=Custom404Handler ) # Static File Handlers self.app.add_handlers( ".*$", [ url(r'{0}/favicon.ico'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'images/ico/favicon.ico') }, name='favicon'), url(r'{0}/images/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'images') }, name='images'), url(r'{0}/cache/images/(.*)'.format(self.options['web_root']), StaticFileHandler, {"path": ek(os.path.join, sickbeard.CACHE_DIR, 'images')}, name='image_cache'), url(r'{0}/css/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'css') }, name='css'), url(r'{0}/js/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'js') }, name='js'), url(r'{0}/fonts/(.*)'.format(self.options['web_root']), StaticFileHandler, { "path": ek(os.path.join, self.options['data_root'], 'fonts') }, name='fonts') # TODO: WTF is this? # url(r'{0}/videos/(.*)'.format(self.options['web_root']), StaticFileHandler, # {"path": self.video_root}, name='videos') ]) # Main Handlers self.app.add_handlers( '.*$', [ url(r'{0}(/?.*)'.format(self.options['api_root']), ApiHandler, name='api'), url(r'{0}/getkey(/?.*)'.format(self.options['web_root']), KeyHandler, name='get_api_key'), url(r'{0}/api/builder'.format(self.options['web_root']), RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}, name='apibuilder'), url(r'{0}/login(/?)'.format(self.options['web_root']), LoginHandler, name='login'), url(r'{0}/logout(/?)'.format(self.options['web_root']), LogoutHandler, name='logout'), url(r'{0}/calendar/?'.format(self.options['web_root']), CalendarHandler, name='calendar'), # routes added by @route decorator # Plus naked index with missing web_root prefix ] + Route.get_routes(self.options['web_root'])) def run(self): if self.enable_https: protocol = "https" ssl_options = { "certfile": self.https_cert, "keyfile": self.https_key } else: protocol = "http" ssl_options = None logger.log("Starting SickChill on " + protocol + "://" + str(self.options['host']) + ":" + str(self.options['port']) + "/") try: self.server = self.app.listen( self.options['port'], self.options['host'], ssl_options=ssl_options, xheaders=sickbeard.HANDLE_REVERSE_PROXY, protocol=protocol) except socket_error as ex: err_msg = "" if ex.errno == errno.EADDRINUSE: # Address/port combination already in use if sickbeard.LAUNCH_BROWSER and not self.daemon: sickbeard.launchBrowser( 'https' if sickbeard.ENABLE_HTTPS else 'http', self.options['port'], sickbeard.WEB_ROOT) logger.log("Launching browser and exiting") err_msg = "already in use!" logger.log("Could not start webserver on port {0}: {1}".format( self.options['port'], err_msg or ex)) # noinspection PyProtectedMember os._exit(1) except Exception as ex: logger.log("Could not start webserver on port {0}: {1}".format( self.options['port'], ex)) # noinspection PyProtectedMember os._exit(1) try: IOLoop.current().start() IOLoop.current().close(True) except (IOError, ValueError) as e: # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass def shutdown(self): self.alive = False IOLoop.current().stop()
class APIManager(EService): """Service exposing the 5G-EmPOWER REST API This service exposes the 5G-EmPOWER REST API, the 'port' parameter specifies on which port the HTTP server should listen. Parameters: port: the port on which the HTTP server should listen (optional, default: 8888) """ HANDLERS = [IndexHandler, AuthLoginHandler, AuthLogoutHandler, DocHandler, AuthSwitchProjectHandler] accounts_manager = None projects_manager = None def __init__(self, context, service_id, webui, port): super().__init__(context=context, service_id=service_id, webui=webui, port=port) self.settings = { "static_path": self.webui + "static/", "cookie_secret": COOKIE_SECRET, "template_path": self.webui + "templates/", "login_url": LOGIN_URL, "debug": DEBUG, } self.application = Application([], **self.settings) self.http_server = tornado.httpserver.HTTPServer(self.application) @property def webui(self): """Return path to Web UI.""" return self.params["webui"] @webui.setter def webui(self, value): """Set path to Web UI.""" if "webui" in self.params and self.params["webui"]: raise ValueError("Param webui can not be changed") self.params["webui"] = value @property def port(self): """Return port.""" return self.params["port"] @port.setter def port(self, value): """Set port.""" if "port" in self.params and self.params["port"]: raise ValueError("Param port can not be changed") self.params["port"] = int(value) def start(self): """Start api manager.""" super().start() self.accounts_manager = srv_or_die("accountsmanager") self.projects_manager = srv_or_die("projectsmanager") self.http_server.listen(self.port) self.log.info("Listening on port %u", self.port) self.http_server.start() def register_handler(self, handler): """Add a new handler class.""" for url in handler.URLS: self.log.info("Registering URL: %s", url) self.application.add_handlers(r".*$", [(url, handler)])
class APIManager(EService): """Service exposing the 5G-EmPOWER REST API This service exposes the 5G-EmPOWER REST API, the 'port' parameter specifies on which port the HTTP server should listen. Parameters: port: the port on which the HTTP server should listen (optional, default: 8888) """ HANDLERS = [IndexHandler, AuthLoginHandler, AuthLogoutHandler, DocHandler] accounts_manager = None projects_manager = None def __init__(self, **kwargs): if 'port' not in kwargs: kwargs['port'] = DEFAULT_PORT super().__init__(**kwargs) self.settings = { "static_path": STATIC_PATH, "cookie_secret": COOKIE_SECRET, "template_path": TEMPLATE_PATH, "login_url": LOGIN_URL, "debug": DEBUG, } self.application = Application([], **self.settings) self.http_server = tornado.httpserver.HTTPServer(self.application) @property def port(self): """Return port.""" return self.params["port"] @port.setter def port(self, value): """Set port.""" if "port" in self.params and self.params["port"]: raise ValueError("Param port can not be changed") self.params["port"] = int(value) def start(self, load): """Start api manager.""" super().start(load) self.accounts_manager = \ srv_or_die("empower.managers.accountsmanager.accountsmanager") self.projects_manager = \ srv_or_die("empower.managers.projectsmanager.projectsmanager") self.http_server.listen(self.port) self.log.info("Listening on port %u", self.port) self.http_server.start() def register_handler(self, handler): """Add a new handler class.""" for url in handler.URLS: self.log.info("Registering URL: %s", url) self.application.add_handlers(r".*$", [(url, handler)])
class srWebServer(object): def __init__(self): self.started = False def start(self): self.started = True # video root self.video_root = None if sickrage.srCore.srConfig.ROOT_DIRS: root_dirs = sickrage.srCore.srConfig.ROOT_DIRS.split('|') self.video_root = root_dirs[int(root_dirs[0]) + 1] # web root if sickrage.srCore.srConfig.WEB_ROOT: sickrage.srCore.srConfig.WEB_ROOT = sickrage.srCore.srConfig.WEB_ROOT = ( '/' + sickrage.srCore.srConfig.WEB_ROOT.lstrip('/').strip('/')) # api root if not sickrage.srCore.srConfig.API_KEY: sickrage.srCore.srConfig.API_KEY = generateApiKey() self.api_root = r'%s/api/%s' % (sickrage.srCore.srConfig.WEB_ROOT, sickrage.srCore.srConfig.API_KEY) # tornado setup if sickrage.srCore.srConfig.ENABLE_HTTPS: # If either the HTTPS certificate or key do not exist, make some self-signed ones. if not ( sickrage.srCore.srConfig.HTTPS_CERT and os.path.exists( sickrage.srCore.srConfig.HTTPS_CERT)) or not ( sickrage.srCore.srConfig.HTTPS_KEY and os.path.exists(sickrage.srCore.srConfig.HTTPS_KEY)): if not create_https_certificates(sickrage.srCore.srConfig.HTTPS_CERT, sickrage.srCore.srConfig.HTTPS_KEY): sickrage.srCore.srLogger.info("Unable to create CERT/KEY files, disabling HTTPS") sickrage.srCore.srConfig.ENABLE_HTTPS = False if not (os.path.exists(sickrage.srCore.srConfig.HTTPS_CERT) and os.path.exists( sickrage.srCore.srConfig.HTTPS_KEY)): sickrage.srCore.srLogger.warning("Disabled HTTPS because of missing CERT and KEY files") sickrage.srCore.srConfig.ENABLE_HTTPS = False # Load the app self.app = Application([], debug=False, autoreload=False, gzip=sickrage.srCore.srConfig.WEB_USE_GZIP, xheaders=sickrage.srCore.srConfig.HANDLE_REVERSE_PROXY, cookie_secret=sickrage.srCore.srConfig.WEB_COOKIE_SECRET, login_url='%s/login/' % sickrage.srCore.srConfig.WEB_ROOT) # Main Handlers self.app.add_handlers('.*$', [ # webapi handler (r'%s(/?.*)' % self.api_root, ApiHandler), # webapi key retrieval (r'%s/getkey(/?.*)' % sickrage.srCore.srConfig.WEB_ROOT, KeyHandler), # webapi builder redirect (r'%s/api/builder' % sickrage.srCore.srConfig.WEB_ROOT, RedirectHandler, {"url": sickrage.srCore.srConfig.WEB_ROOT + '/apibuilder/'}), # webui login/logout handlers (r'%s/login(/?)' % sickrage.srCore.srConfig.WEB_ROOT, LoginHandler), (r'%s/logout(/?)' % sickrage.srCore.srConfig.WEB_ROOT, LogoutHandler), # webui handlers ] + Route.get_routes(sickrage.srCore.srConfig.WEB_ROOT)) # Web calendar handler (Needed because option Unprotected calendar) self.app.add_handlers('.*$', [ (r'%s/calendar' % sickrage.srCore.srConfig.WEB_ROOT, CalendarHandler), ]) # Static File Handlers self.app.add_handlers(".*$", [ # favicon (r'%s/(favicon\.ico)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'images/ico/favicon.ico')}), # images (r'%s.*?/images/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticImageHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'images')}), # css (r'%s/css/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'css')}), # scss (r'%s/scss/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'scss')}), # fonts (r'%s/fonts/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'fonts')}), # javascript (r'%s/js/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": os.path.join(sickrage.srCore.srConfig.GUI_DIR, 'js')}), # videos ] + [(r'%s/videos/(.*)' % sickrage.srCore.srConfig.WEB_ROOT, StaticFileHandler, {"path": self.video_root})]) self.server = HTTPServer(self.app, no_keep_alive=True) if sickrage.srCore.srConfig.ENABLE_HTTPS: self.server.ssl_options = { "certfile": sickrage.srCore.srConfig.HTTPS_CERT, "keyfile": sickrage.srCore.srConfig.HTTPS_KEY } try: self.server.listen(sickrage.srCore.srConfig.WEB_PORT, None) except socket.error as e: print(e.message) raise # launch browser window if all([not sickrage.NOLAUNCH, sickrage.srCore.srConfig.LAUNCH_BROWSER]): threading.Thread(None, lambda: launch_browser( ('http', 'https')[sickrage.srCore.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srCore.srConfig.WEB_PORT )).start() # clear mako cache folder makocache = os.path.join(sickrage.srCore.srConfig.CACHE_DIR, 'mako') if os.path.isdir(makocache): shutil.rmtree(makocache) sickrage.srCore.srLogger.info("SiCKRAGE :: STARTED") sickrage.srCore.srLogger.info("SiCKRAGE :: VERSION:[{}]".format(sickrage.srCore.VERSIONUPDATER.updater.version)) sickrage.srCore.srLogger.info("SiCKRAGE :: CONFIG:[{}]".format(sickrage.CONFIG_FILE)) sickrage.srCore.srLogger.info("SiCKRAGE :: URL:[{}://{}:{}/]".format( ('http', 'https')[sickrage.srCore.srConfig.ENABLE_HTTPS], get_lan_ip(), sickrage.srCore.srConfig.WEB_PORT)) def shutdown(self): if self.started: self.server.close_all_connections() self.server.stop() self.started = False