def generate_session_id(secret_key=settings.secret_key_bytes(), signed=settings.sign_sessions()): """Generate a random session ID. Typically, each browser tab connected to a Bokeh application has its own session ID. In production deployments of a Bokeh app, session IDs should be random and unguessable - otherwise users of the app could interfere with one another. If session IDs are signed with a secret key, the server can verify that the generator of the session ID was "authorized" (the generator had to know the secret key). This can be used to have a separate process, such as another web application, which generates new sessions on a Bokeh server. This other process may require users to log in before redirecting them to the Bokeh server with a valid session ID, for example. Args: secret_key (str, optional) : Secret key (default: value of 'BOKEH_SECRET_KEY' env var) signed (bool, optional) : Whether to sign the session ID (default: value of 'BOKEH_SIGN_SESSIONS' env var) """ secret_key = _ensure_bytes(secret_key) if signed: # note: '-' can also be in the base64 encoded signature base_id = _get_random_string(secret_key=secret_key) return base_id + '-' + _signature(base_id, secret_key) else: return _get_random_string(secret_key=secret_key)
def check_session_id_signature(session_id, secret_key=settings.secret_key_bytes(), signed=settings.sign_sessions()): """Check the signature of a session ID, returning True if it's valid. The server uses this function to check whether a session ID was generated with the correct secret key. If signed sessions are disabled, this function always returns True. Args: session_id (str) : The session ID to check secret_key (str, optional) : Secret key (default: value of 'BOKEH_SECRET_KEY' env var) signed (bool, optional) : Whether to check anything (default: value of 'BOKEH_SIGN_SESSIONS' env var) """ secret_key = _ensure_bytes(secret_key) if signed: pieces = session_id.split('-', 1) if len(pieces) != 2: return False base_id = pieces[0] provided_signature = pieces[1] expected_signature = _signature(base_id, secret_key) # hmac.compare_digest() uses a string compare algorithm that doesn't # short-circuit so we don't allow timing analysis # encode_utf8 is used to ensure that strings have same encoding return hmac.compare_digest(encode_utf8(expected_signature), encode_utf8(provided_signature)) else: return True
def _get_random_string(length=36, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', secret_key=settings.secret_key_bytes()): """ Return a securely generated random string. The default length of 12 with the a-z, A-Z, 0-9 character set returns a 71-bit value. log_2((26+26+10)^12) =~ 71 bits """ secret_key = _ensure_bytes(secret_key) _reseed_if_needed(using_sysrandom, secret_key) return ''.join(random.choice(allowed_chars) for i in range(length))
def _get_random_string(length: int = 44, allowed_chars: str = 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', secret_key: Optional[bytes] = settings.secret_key_bytes()) -> str: """ Return a securely generated random string. With the a-z, A-Z, 0-9 character set: Length 12 is a 71-bit value. log_2((26+26+10)^12) =~ 71 Length 44 is a 261-bit value. log_2((26+26+10)^44) = 261 """ secret_key = _ensure_bytes(secret_key) _reseed_if_needed(using_sysrandom, secret_key) return ''.join(random.choice(allowed_chars) for _ in range(length))
def generate_session_id(secret_key: Optional[bytes] = settings.secret_key_bytes(), signed: bool = settings.sign_sessions()) -> str: ''' Generate a random session ID. Typically, each browser tab connected to a Bokeh application has its own session ID. In production deployments of a Bokeh app, session IDs should be random and unguessable - otherwise users of the app could interfere with one another. ''' session_id = _get_random_string() if signed: session_id = '.'.join([session_id, _signature(session_id, secret_key)]) return session_id
def check_token_signature( token: str, secret_key: Optional[bytes] = settings.secret_key_bytes(), signed: Optional[bool] = settings.sign_sessions() ) -> bool: """Check the signature of a token and the contained signature. The server uses this function to check whether a token and the contained session id was generated with the correct secret key. If signed sessions are disabled, this function always returns True. Args: token (str) : The token to check secret_key (str, optional) : Secret key (default: value of BOKEH_SECRET_KEY environment variable) signed (bool, optional) : Whether to check anything (default: value of BOKEH_SIGN_SESSIONS environment variable) Returns: bool """ secret_key = _ensure_bytes(secret_key) if signed: token_pieces = token.split('.', 1) if len(token_pieces) != 2: return False base_token = token_pieces[0] provided_token_signature = token_pieces[1] expected_token_signature = _signature(base_token, secret_key) # hmac.compare_digest() uses a string compare algorithm that doesn't # short-circuit so we don't allow timing analysis token_valid = hmac.compare_digest(expected_token_signature, provided_token_signature) session_id = get_session_id(token) session_id_valid = check_session_id_signature(session_id, secret_key, signed) return token_valid and session_id_valid return True
def check_session_id_signature(session_id: str, secret_key: Optional[bytes] = settings.secret_key_bytes(), signed: Optional[bool] = settings.sign_sessions()) -> bool: """Check the signature of a session ID, returning True if it's valid. The server uses this function to check whether a session ID was generated with the correct secret key. If signed sessions are disabled, this function always returns True. """ secret_key = _ensure_bytes(secret_key) if signed: id_pieces = session_id.split('.', 1) if len(id_pieces) != 2: return False provided_id_signature = id_pieces[1] expected_id_signature = _signature(id_pieces[0], secret_key) return hmac.compare_digest( expected_id_signature, provided_id_signature ) return True
def generate_jwt_token( session_id: str, secret_key: Optional[bytes] = settings.secret_key_bytes(), signed: bool = settings.sign_sessions(), extra_payload: Optional[Dict[str, Any]] = None, expiration: int = 300) -> str: """Generates a JWT token given a session_id and additional payload. Args: session_id (str): The session id to add to the token secret_key (str, optional) : Secret key (default: value of BOKEH_SECRET_KEY environment varariable) signed (bool, optional) : Whether to sign the session ID (default: value of BOKEH_SIGN_SESSIONS envronment varariable) extra_payload (dict, optional) : Extra key/value pairs to include in the Bokeh session token expiration (int, optional) : Expiration time Returns: str """ now = calendar.timegm(dt.datetime.now().utctimetuple()) payload = {'session_id': session_id, 'session_expiry': now + expiration} if extra_payload: if "session_id" in extra_payload: raise RuntimeError( "extra_payload for session tokens may not contain 'session_id'" ) payload.update(extra_payload) token = _base64_encode(json.dumps(payload)) secret_key = _ensure_bytes(secret_key) if not signed: return token return token + '.' + _signature(token, secret_key)
def invoke(self, args): applications = build_single_handler_applications(args.files) log_level = getattr(logging, args.log_level.upper()) logging.basicConfig(level=log_level) if len(applications) == 0: # create an empty application by default, typically used with output_server applications['/'] = Application() if args.keep_alive is not None: if args.keep_alive == 0: log.info("Keep-alive ping disabled") else: log.info("Keep-alive ping configured every %d milliseconds", args.keep_alive) # rename to be compatible with Server args.keep_alive_milliseconds = args.keep_alive server_kwargs = { key: getattr(args, key) for key in ['port', 'address', 'allow_websocket_origin', 'host', 'prefix', 'develop', 'keep_alive_milliseconds', 'use_xheaders', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key.") server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) if args.develop: log.info("Using develop mode (do not enable --develop in production)") address_string = '' if server.address is not None and server.address != '': address_string = ' address ' + server.address log.info("Starting Bokeh server on port %d%s with applications at paths %r", server.port, address_string, sorted(applications.keys())) server.start()
def invoke(self, args: argparse.Namespace) -> None: ''' ''' basicConfig(format=args.log_format, filename=args.log_file) # This is a bit of a fudge. We want the default log level for non-server # cases to be None, i.e. we don't set a log level. But for the server we # do want to set the log level to INFO if nothing else overrides that. log_level = settings.py_log_level(args.log_level) if log_level is None: log_level = logging.INFO logging.getLogger('bokeh').setLevel(log_level) if args.use_config is not None: log.info("Using override config file: {}".format(args.use_config)) settings.load_config(args.use_config) # protect this import inside a function so that "bokeh info" can work # even if Tornado is not installed from bokeh.server.server import Server files = [] for f in args.files: if args.glob: files.extend(glob(f)) else: files.append(f) argvs = {f: args.args for f in files} applications = build_single_handler_applications(files, argvs) if len(applications) == 0: # create an empty application by default applications['/'] = Application() # rename args to be compatible with Server if args.keep_alive is not None: args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: args.stats_log_frequency_milliseconds = args.stats_log_frequency if args.mem_log_frequency is not None: args.mem_log_frequency_milliseconds = args.mem_log_frequency server_kwargs = { key: getattr(args, key) for key in [ 'port', 'address', 'allow_websocket_origin', 'num_procs', 'prefix', 'index', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'mem_log_frequency_milliseconds', 'use_xheaders', 'websocket_max_message_size', 'include_cookies', 'include_headers', 'exclude_cookies', 'exclude_headers', 'session_token_expiration', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['ssl_certfile'] = settings.ssl_certfile( getattr(args, 'ssl_certfile', None)) server_kwargs['ssl_keyfile'] = settings.ssl_keyfile( getattr(args, 'ssl_keyfile', None)) server_kwargs['ssl_password'] = settings.ssl_password() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError( "argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key." ) auth_module_path = settings.auth_module( getattr(args, 'auth_module', None)) if auth_module_path: server_kwargs['auth_provider'] = AuthModule(auth_module_path) else: server_kwargs['auth_provider'] = NullAuth() server_kwargs['xsrf_cookies'] = settings.xsrf_cookies( getattr(args, 'enable_xsrf_cookies', False)) server_kwargs['cookie_secret'] = settings.cookie_secret( getattr(args, 'cookie_secret', None)) server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect server_kwargs['autoreload'] = args.dev is not None def find_autoreload_targets(app_path: str) -> None: path = os.path.abspath(app_path) if not os.path.isdir(path): return for path, subdirs, files in os.walk(path): for name in files: if (fnmatch(name, '*.html') or fnmatch(name, '*.css') or fnmatch(name, '*.yaml')): log.info("Watching: " + os.path.join(path, name)) watch(os.path.join(path, name)) def add_optional_autoreload_files(file_list: List[str]) -> None: for filen in file_list: if os.path.isdir(filen): log.warning("Cannot watch directory " + filen) continue log.info("Watching: " + filen) watch(filen) if server_kwargs['autoreload']: if len(applications.keys()) != 1: die("--dev can only support a single app.") if server_kwargs['num_procs'] != 1: log.info("Running in --dev mode. --num-procs is limited to 1.") server_kwargs['num_procs'] = 1 find_autoreload_targets(args.files[0]) add_optional_autoreload_files(args.dev) with report_server_init_errors(**server_kwargs): server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback() -> None: for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) address_string = 'localhost' if server.address is not None and server.address != '': address_string = server.address for route in sorted(applications.keys()): url = "http://%s:%d%s%s" % (address_string, server.port, server.prefix, route) log.info("Bokeh app running at: %s" % url) log.info("Starting Bokeh server with process id: %d" % os.getpid()) server.run_until_shutdown()
def invoke(self, args): applications = build_single_handler_applications(args.files) log_level = getattr(logging, args.log_level.upper()) logging.basicConfig(level=log_level) if len(applications) == 0: # create an empty application by default, typically used with output_server applications["/"] = Application() if args.keep_alive is not None: if args.keep_alive == 0: log.info("Keep-alive ping disabled") else: log.info("Keep-alive ping configured every %d milliseconds", args.keep_alive) # rename to be compatible with Server args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: log.info("Check for unused sessions every %d milliseconds", args.check_unused_sessions) # rename to be compatible with Server args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: log.info("Unused sessions last for %d milliseconds", args.unused_session_lifetime) # rename to be compatible with Server args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: log.info("Log statistics every %d milliseconds", args.stats_log_frequency) # rename to be compatible with Server args.stats_log_frequency_milliseconds = args.stats_log_frequency server_kwargs = { key: getattr(args, key) for key in [ "port", "address", "allow_websocket_origin", "host", "prefix", "develop", "keep_alive_milliseconds", "check_unused_sessions_milliseconds", "unused_session_lifetime_milliseconds", "stats_log_frequency_milliseconds", "use_xheaders", ] if getattr(args, key, None) is not None } server_kwargs["sign_sessions"] = settings.sign_sessions() server_kwargs["secret_key"] = settings.secret_key_bytes() server_kwargs["generate_session_ids"] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == "unsigned": server_kwargs["sign_sessions"] = False elif args.session_ids == "signed": server_kwargs["sign_sessions"] = True elif args.session_ids == "external-signed": server_kwargs["sign_sessions"] = True server_kwargs["generate_session_ids"] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs["sign_sessions"] and not server_kwargs["secret_key"]: die( "To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key." ) server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) if args.develop: log.info("Using develop mode (do not enable --develop in production)") address_string = "" if server.address is not None and server.address != "": address_string = " address " + server.address log.info( "Starting Bokeh server on port %d%s with applications at paths %r", server.port, address_string, sorted(applications.keys()), ) server.start()
def invoke(self, args): argvs = { f : args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) logging.basicConfig(level=log_level, format=args.log_format) if len(applications) == 0: # create an empty application by default, typically used with output_server applications['/'] = Application() if args.keep_alive is not None: if args.keep_alive == 0: log.info("Keep-alive ping disabled") else: log.info("Keep-alive ping configured every %d milliseconds", args.keep_alive) # rename to be compatible with Server args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: log.info("Check for unused sessions every %d milliseconds", args.check_unused_sessions) # rename to be compatible with Server args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: log.info("Unused sessions last for %d milliseconds", args.unused_session_lifetime) # rename to be compatible with Server args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: log.info("Log statistics every %d milliseconds", args.stats_log_frequency) # rename to be compatible with Server args.stats_log_frequency_milliseconds = args.stats_log_frequency server_kwargs = { key: getattr(args, key) for key in ['port', 'address', 'allow_websocket_origin', 'host', 'prefix', 'develop', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'use_xheaders', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key.") server_kwargs['use_index'] = not args.disable_index server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) if args.develop: log.info("Using develop mode (do not enable --develop in production)") address_string = '' if server.address is not None and server.address != '': address_string = ' address ' + server.address log.info("Starting Bokeh server on port %d%s with applications at paths %r", server.port, address_string, sorted(applications.keys())) log.info("Starting Bokeh server with process id: %d" % getpid()) server.start()
def invoke(self, args): ''' ''' # protect this import inside a function so that "bokeh info" can work # even if Tornado is not installed from bokeh.server.server import Server argvs = { f : args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) basicConfig(level=log_level, format=args.log_format, filename=args.log_file) if len(applications) == 0: # create an empty application by default applications['/'] = Application() # rename args to be compatible with Server if args.keep_alive is not None: args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: args.stats_log_frequency_milliseconds = args.stats_log_frequency if args.mem_log_frequency is not None: args.mem_log_frequency_milliseconds = args.mem_log_frequency server_kwargs = { key: getattr(args, key) for key in ['port', 'address', 'allow_websocket_origin', 'num_procs', 'prefix', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'mem_log_frequency_milliseconds', 'use_xheaders', 'websocket_max_message_size', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key.") server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect with report_server_init_errors(**server_kwargs): server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) address_string = 'localhost' if server.address is not None and server.address != '': address_string = server.address for route in sorted(applications.keys()): url = "http://%s:%d%s%s" % (address_string, server.port, server.prefix, route) log.info("Bokeh app running at: %s" % url) log.info("Starting Bokeh server with process id: %d" % getpid()) server.run_until_shutdown()
def invoke(self, args): ''' ''' # protect this import inside a function so that "bokeh info" can work # even if Tornado is not installed from bokeh.server.server import Server argvs = { f : args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) basicConfig(level=log_level, format=args.log_format, filename=args.log_file) if len(applications) == 0: # create an empty application by default applications['/'] = Application() # rename args to be compatible with Server if args.keep_alive is not None: args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: args.stats_log_frequency_milliseconds = args.stats_log_frequency if args.mem_log_frequency is not None: args.mem_log_frequency_milliseconds = args.mem_log_frequency server_kwargs = { key: getattr(args, key) for key in ['port', 'address', 'allow_websocket_origin', 'num_procs', 'prefix', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'mem_log_frequency_milliseconds', 'use_xheaders', 'websocket_max_message_size', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key.") server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect server_kwargs['autoreload'] = args.dev is not None def find_autoreload_targets(app_path): path = os.path.abspath(app_path) if not os.path.isdir(path): return for path, subdirs, files in os.walk(path): for name in files: if (fnmatch(name, '*.html') or fnmatch(name, '*.css') or fnmatch(name, '*.yaml')): log.info("Watching: " + os.path.join(path, name)) watch(os.path.join(path, name)) def add_optional_autoreload_files(file_list): for filen in file_list: if os.path.isdir(filen): log.warning("Cannot watch directory " + filen) continue log.info("Watching: " + filen) watch(filen) if server_kwargs['autoreload']: if len(applications.keys()) != 1: die("--dev can only support a single app.") if server_kwargs['num_procs'] != 1: log.info("Running in --dev mode. --num-procs is limited to 1.") server_kwargs['num_procs'] = 1 find_autoreload_targets(args.files[0]) add_optional_autoreload_files(args.dev) with report_server_init_errors(**server_kwargs): server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) address_string = 'localhost' if server.address is not None and server.address != '': address_string = server.address for route in sorted(applications.keys()): url = "http://%s:%d%s%s" % (address_string, server.port, server.prefix, route) log.info("Bokeh app running at: %s" % url) log.info("Starting Bokeh server with process id: %d" % os.getpid()) server.run_until_shutdown()
def __init__(self, applications, prefix, hosts, extra_websocket_origins, io_loop=None, extra_patterns=None, secret_key=settings.secret_key_bytes(), sign_sessions=settings.sign_sessions(), generate_session_ids=True, # heroku, nginx default to 60s timeout, so well less than that keep_alive_milliseconds=37000, # how often to check for unused sessions check_unused_sessions_milliseconds=17000, # how long unused sessions last unused_session_lifetime_milliseconds=60*30*1000, # how often to log stats stats_log_frequency_milliseconds=15000, develop=False): self._prefix = prefix if io_loop is None: io_loop = IOLoop.current() self._loop = io_loop if keep_alive_milliseconds < 0: # 0 means "disable" raise ValueError("keep_alive_milliseconds must be >= 0") self._hosts = set(hosts) self._websocket_origins = self._hosts | set(extra_websocket_origins) self._resources = {} self._develop = develop self._secret_key = secret_key self._sign_sessions = sign_sessions self._generate_session_ids = generate_session_ids log.debug("Allowed Host headers: %r", list(self._hosts)) log.debug("These host origins can connect to the websocket: %r", list(self._websocket_origins)) # Wrap applications in ApplicationContext self._applications = dict() for k,v in applications.items(): self._applications[k] = ApplicationContext(v, self._develop, self._loop) extra_patterns = extra_patterns or [] all_patterns = [] for key in applications: app_patterns = [] for p in per_app_patterns: if key == "/": route = p[0] else: route = key + p[0] route = self._prefix + route app_patterns.append((route, p[1], { "application_context" : self._applications[key] })) websocket_path = None for r in app_patterns: if r[0].endswith("/ws"): websocket_path = r[0] if not websocket_path: raise RuntimeError("Couldn't find websocket path") for r in app_patterns: r[2]["bokeh_websocket_path"] = websocket_path all_patterns.extend(app_patterns) for p in extra_patterns + toplevel_patterns: prefixed_pat = (self._prefix+p[0],) + p[1:] all_patterns.append(prefixed_pat) for pat in all_patterns: _whitelist(pat[1]) log.debug("Patterns are: %r", all_patterns) super(BokehTornado, self).__init__(all_patterns) self._clients = set() self._executor = ProcessPoolExecutor(max_workers=4) self._loop.add_callback(self._start_async) self._stats_job = PeriodicCallback(self.log_stats, stats_log_frequency_milliseconds, io_loop=self._loop) self._unused_session_linger_seconds = unused_session_lifetime_milliseconds self._cleanup_job = PeriodicCallback(self.cleanup_sessions, check_unused_sessions_milliseconds, io_loop=self._loop) if keep_alive_milliseconds > 0: self._ping_job = PeriodicCallback(self.keep_alive, keep_alive_milliseconds, io_loop=self._loop) else: self._ping_job = None
def __init__(self, applications, prefix, hosts, extra_websocket_origins, io_loop=None, extra_patterns=None, secret_key=settings.secret_key_bytes(), sign_sessions=settings.sign_sessions(), generate_session_ids=True, # heroku, nginx default to 60s timeout, so well less than that keep_alive_milliseconds=37000, # how often to check for unused sessions check_unused_sessions_milliseconds=17000, # how long unused sessions last unused_session_lifetime_milliseconds=15000, # how often to log stats stats_log_frequency_milliseconds=15000, use_index=True, redirect_root=True): self._prefix = prefix self.use_index = use_index if keep_alive_milliseconds < 0: # 0 means "disable" raise ValueError("keep_alive_milliseconds must be >= 0") if check_unused_sessions_milliseconds <= 0: raise ValueError("check_unused_sessions_milliseconds must be > 0") if unused_session_lifetime_milliseconds <= 0: raise ValueError("check_unused_sessions_milliseconds must be > 0") if stats_log_frequency_milliseconds <= 0: raise ValueError("stats_log_frequency_milliseconds must be > 0") self._hosts = set(hosts) self._websocket_origins = self._hosts | set(extra_websocket_origins) self._resources = {} self._secret_key = secret_key self._sign_sessions = sign_sessions self._generate_session_ids = generate_session_ids log.debug("Allowed Host headers: %r", list(self._hosts)) log.debug("These host origins can connect to the websocket: %r", list(self._websocket_origins)) # Wrap applications in ApplicationContext self._applications = dict() for k,v in applications.items(): self._applications[k] = ApplicationContext(v) extra_patterns = extra_patterns or [] all_patterns = [] for key, app in applications.items(): app_patterns = [] for p in per_app_patterns: if key == "/": route = p[0] else: route = key + p[0] route = self._prefix + route app_patterns.append((route, p[1], { "application_context" : self._applications[key] })) websocket_path = None for r in app_patterns: if r[0].endswith("/ws"): websocket_path = r[0] if not websocket_path: raise RuntimeError("Couldn't find websocket path") for r in app_patterns: r[2]["bokeh_websocket_path"] = websocket_path all_patterns.extend(app_patterns) # add a per-app static path if requested by the application if app.static_path is not None: if key == "/": route = "/static/(.*)" else: route = key + "/static/(.*)" route = self._prefix + route all_patterns.append((route, StaticFileHandler, { "path" : app.static_path })) for p in extra_patterns + toplevel_patterns: if p[1] == RootHandler: if self.use_index: data = {"applications": self._applications, "prefix": self._prefix, "use_redirect": redirect_root} prefixed_pat = (self._prefix + p[0],) + p[1:] + (data,) all_patterns.append(prefixed_pat) else: prefixed_pat = (self._prefix + p[0],) + p[1:] all_patterns.append(prefixed_pat) for pat in all_patterns: _whitelist(pat[1]) log.debug("Patterns are:") for line in pformat(all_patterns, width=60).split("\n"): log.debug(" " + line) super(BokehTornado, self).__init__(all_patterns)
def invoke(self, args): argvs = { f : args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) logging.basicConfig(level=log_level, format=args.log_format) if len(applications) == 0: # create an empty application by default, typically used with output_server applications['/'] = Application() if args.keep_alive is not None: if args.keep_alive == 0: log.info("Keep-alive ping disabled") else: log.info("Keep-alive ping configured every %d milliseconds", args.keep_alive) # rename to be compatible with Server args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: log.info("Check for unused sessions every %d milliseconds", args.check_unused_sessions) # rename to be compatible with Server args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: log.info("Unused sessions last for %d milliseconds", args.unused_session_lifetime) # rename to be compatible with Server args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: log.info("Log statistics every %d milliseconds", args.stats_log_frequency) # rename to be compatible with Server args.stats_log_frequency_milliseconds = args.stats_log_frequency server_kwargs = { key: getattr(args, key) for key in ['port', 'address', 'allow_websocket_origin', 'host', 'num_procs', 'prefix', 'develop', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'use_xheaders', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError("argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key.") server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) if args.develop: log.info("Using develop mode (do not enable --develop in production)") address_string = '' if server.address is not None and server.address != '': address_string = ' address ' + server.address log.info("Starting Bokeh server on port %d%s with applications at paths %r", server.port, address_string, sorted(applications.keys())) log.info("Starting Bokeh server with process id: %d" % getpid()) event_handler = LoggingEventHandler() observer = Observer() path = r'C:\Users\jhu\Desktop' observer.schedule(event_handler, path, recursive=False) observer.start() server.start()
def invoke(self, args): argvs = {f: args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) basicConfig(level=log_level, format=args.log_format) # This should remain here until --host is removed entirely _fixup_deprecated_host_args(args) if len(applications) == 0: # create an empty application by default applications['/'] = Application() if args.keep_alive is not None: if args.keep_alive == 0: log.info("Keep-alive ping disabled") else: log.info("Keep-alive ping configured every %d milliseconds", args.keep_alive) # rename to be compatible with Server args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: log.info("Check for unused sessions every %d milliseconds", args.check_unused_sessions) # rename to be compatible with Server args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: log.info("Unused sessions last for %d milliseconds", args.unused_session_lifetime) # rename to be compatible with Server args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: log.info("Log statistics every %d milliseconds", args.stats_log_frequency) # rename to be compatible with Server args.stats_log_frequency_milliseconds = args.stats_log_frequency server_kwargs = { key: getattr(args, key) for key in [ 'port', 'address', 'allow_websocket_origin', 'num_procs', 'prefix', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'use_xheaders', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError( "argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key." ) server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect with report_server_init_errors(**server_kwargs): server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) address_string = 'localhost' if server.address is not None and server.address != '': address_string = server.address for route in sorted(applications.keys()): url = "http://%s:%d%s%s" % (address_string, server.port, server.prefix, route) log.info("Bokeh app running at: %s" % url) log.info("Starting Bokeh server with process id: %d" % getpid()) server.run_until_shutdown()
def invoke(self, args): ''' ''' # protect this import inside a function so that "bokeh info" can work # even if Tornado is not installed from bokeh.server.server import Server argvs = {f: args.args for f in args.files} applications = build_single_handler_applications(args.files, argvs) log_level = getattr(logging, args.log_level.upper()) basicConfig(level=log_level, format=args.log_format, filename=args.log_file) if len(applications) == 0: # create an empty application by default applications['/'] = Application() # rename args to be compatible with Server if args.keep_alive is not None: args.keep_alive_milliseconds = args.keep_alive if args.check_unused_sessions is not None: args.check_unused_sessions_milliseconds = args.check_unused_sessions if args.unused_session_lifetime is not None: args.unused_session_lifetime_milliseconds = args.unused_session_lifetime if args.stats_log_frequency is not None: args.stats_log_frequency_milliseconds = args.stats_log_frequency if args.mem_log_frequency is not None: args.mem_log_frequency_milliseconds = args.mem_log_frequency server_kwargs = { key: getattr(args, key) for key in [ 'port', 'address', 'allow_websocket_origin', 'num_procs', 'prefix', 'index', 'keep_alive_milliseconds', 'check_unused_sessions_milliseconds', 'unused_session_lifetime_milliseconds', 'stats_log_frequency_milliseconds', 'mem_log_frequency_milliseconds', 'use_xheaders', 'websocket_max_message_size', ] if getattr(args, key, None) is not None } server_kwargs['sign_sessions'] = settings.sign_sessions() server_kwargs['secret_key'] = settings.secret_key_bytes() server_kwargs['generate_session_ids'] = True if args.session_ids is None: # no --session-ids means use the env vars pass elif args.session_ids == 'unsigned': server_kwargs['sign_sessions'] = False elif args.session_ids == 'signed': server_kwargs['sign_sessions'] = True elif args.session_ids == 'external-signed': server_kwargs['sign_sessions'] = True server_kwargs['generate_session_ids'] = False else: raise RuntimeError( "argparse should have filtered out --session-ids mode " + args.session_ids) if server_kwargs['sign_sessions'] and not server_kwargs['secret_key']: die("To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; " + "the `bokeh secret` command can be used to generate a new key." ) server_kwargs['use_index'] = not args.disable_index server_kwargs['redirect_root'] = not args.disable_index_redirect server_kwargs['autoreload'] = args.dev is not None def find_autoreload_targets(app_path): path = os.path.abspath(app_path) if not os.path.isdir(path): return for path, subdirs, files in os.walk(path): for name in files: if (fnmatch(name, '*.html') or fnmatch(name, '*.css') or fnmatch(name, '*.yaml')): log.info("Watching: " + os.path.join(path, name)) watch(os.path.join(path, name)) def add_optional_autoreload_files(file_list): for filen in file_list: if os.path.isdir(filen): log.warning("Cannot watch directory " + filen) continue log.info("Watching: " + filen) watch(filen) if server_kwargs['autoreload']: if len(applications.keys()) != 1: die("--dev can only support a single app.") if server_kwargs['num_procs'] != 1: log.info("Running in --dev mode. --num-procs is limited to 1.") server_kwargs['num_procs'] = 1 find_autoreload_targets(args.files[0]) add_optional_autoreload_files(args.dev) with report_server_init_errors(**server_kwargs): server = Server(applications, **server_kwargs) if args.show: # we have to defer opening in browser until we start up the server def show_callback(): for route in applications.keys(): server.show(route) server.io_loop.add_callback(show_callback) address_string = 'localhost' if server.address is not None and server.address != '': address_string = server.address for route in sorted(applications.keys()): url = "http://%s:%d%s%s" % (address_string, server.port, server.prefix, route) log.info("Bokeh app running at: %s" % url) log.info("Starting Bokeh server with process id: %d" % os.getpid()) server.run_until_shutdown()