def adapt_prepare(self): """Prepares the special trip parameters.""" parsed = urlsplit(self.url) if parsed.scheme not in ('http', 'https'): raise ValueError('Unsupported url scheme: %s' % self.url) netloc = parsed.netloc if '@' in netloc: userpass, _, netloc = netloc.rpartition('@') self.host, self.port = split_host_and_port(netloc) if self.port is None: self.port = 443 if parsed.scheme == 'https' else 80 self.af = AF_INET self.decompress = 'gzip' in \ self.headers.get('Accept-Encoding', '') req_path = ((parsed.path or '/') + (('?' + parsed.query) if parsed.query else '')) self.start_line = RequestStartLine(self.method, req_path, '') self.headers = HTTPHeaders(self.headers) if 'Connection' not in self.headers: self.headers['Connection'] = 'close' if 'Host' not in self.headers: self.headers['Host'] = self.host
def connect(self): logging.info('start connect to %s from %s:%d', self.request.uri, self.request.connection.context.address[0], self.request.connection.context.address[1]) host, port = httputil.split_host_and_port(self.request.uri) self.start_tunnel(host, port)
def __init__(self, io_loop, client, request, release_callback, final_callback, max_buffer_size, tcp_client, max_header_size, max_body_size): self.start_time = io_loop.time() self.io_loop = io_loop self.client = client self.request = request self.release_callback = release_callback self.final_callback = final_callback self.max_buffer_size = max_buffer_size self.tcp_client = tcp_client self.max_header_size = max_header_size self.max_body_size = max_body_size self.code = None self.headers = None self.chunks = [] self._decompressor = None # Timeout handle returned by IOLoop.add_timeout self._timeout = None self._sockaddr = None with stack_context.ExceptionStackContext(self._handle_exception): self.parsed = urlparse.urlsplit(_unicode(self.request.url)) af = socket.AF_UNSPEC netloc = self.parsed.netloc host, port = httputil.split_host_and_port(netloc) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, stack_context.wrap(self._on_timeout)) self.tcp_client.connect(host,port, af=af, max_buffer_size=self.max_buffer_size, callback=self._on_connect)
def _transform_tornado_request(self, request): if isinstance(request, HTTPRequest): raise ValueError('param tRequest should be \ HTTPRequest instance from tornado package.') # from tornado.simple_httpclient L214-L242 self.parsed = urlparse.urlsplit(_unicode(self.request.url)) if self.parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % self.request.url) # urlsplit results have hostname and port results, but they # didn't support ipv6 literals until python 2.7. netloc = self.parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = split_host_and_port(netloc) if port is None: port = 443 if self.parsed.scheme == "https" else 80 if re.match(r'^\[.*\]$', host): # raw ipv6 addresses in urls are enclosed in brackets host = host[1:-1] self.parsed_hostname = host # save final host for _on_connect if request.allow_ipv6 is False: af = AF_INET else: af = AF_UNSPEC ssl_options = self._get_ssl_options(self.parsed.scheme) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, stack_context.wrap(functools.partial(self._on_timeout, "while connecting")))
def __init__(self, io_loop, client, request, release_callback, final_callback, max_buffer_size, tcp_client, max_header_size, max_body_size): self.start_time = io_loop.time() self.io_loop = io_loop self.client = client self.request = request self.release_callback = release_callback self.final_callback = final_callback self.max_buffer_size = max_buffer_size self.tcp_client = tcp_client self.max_header_size = max_header_size self.max_body_size = max_body_size self.code = None self.headers = None self.chunks = [] self._decompressor = None # Timeout handle returned by IOLoop.add_timeout self._timeout = None self._sockaddr = None with stack_context.ExceptionStackContext(self._handle_exception): self.parsed = urlparse.urlsplit(_unicode(self.request.url)) if self.parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % self.request.url) # urlsplit results have hostname and port results, but they # didn't support ipv6 literals until python 2.7. netloc = self.parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = httputil.split_host_and_port(netloc) if port is None: port = 443 if self.parsed.scheme == "https" else 80 if re.match(r'^\[.*\]$', host): # raw ipv6 addresses in urls are enclosed in brackets host = host[1:-1] self.parsed_hostname = host # save final host for _on_connect if request.allow_ipv6 is False: af = socket.AF_INET else: af = socket.AF_UNSPEC ssl_options = self._get_ssl_options(self.parsed.scheme) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, stack_context.wrap( functools.partial(self._on_timeout, "while connecting"))) self.tcp_client.connect(host, port, af=af, ssl_options=ssl_options, max_buffer_size=self.max_buffer_size, callback=self._on_connect)
def get_host_and_port(url): """ Get host and port from url.""" parsed = urlsplit(url) netloc = parsed.netloc if '@' in netloc: userpass, _, netloc = netloc.rpartition('@') host, port = split_host_and_port(netloc) if port is None: port = 443 if parsed.scheme == 'https' else 80 return (host, port)
def match(self, request: HTTPServerRequest) -> Optional[dict]: host, port = split_host_and_port(request.host) if (self.protocol is not None and request.protocol != self.protocol): return None if (self.host is not None and host != self.host): return None if (self.port is not None and (port or DEFAULT_PORTS.get(request.protocol, None)) != self.port): return None return {}
def __init__(self, io_loop, client, request, release_callback, final_callback, max_buffer_size, tcp_client, max_header_size, max_body_size): self.start_time = io_loop.time() self.io_loop = io_loop self.client = client self.request = request self.release_callback = release_callback self.final_callback = final_callback self.max_buffer_size = max_buffer_size self.tcp_client = tcp_client self.max_header_size = max_header_size self.max_body_size = max_body_size self.code = None self.headers = None self.chunks = [] self._decompressor = None # Timeout handle returned by IOLoop.add_timeout self._timeout = None self._sockaddr = None with stack_context.ExceptionStackContext(self._handle_exception): self.parsed = urlparse.urlsplit(_unicode(self.request.url)) if self.parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % self.request.url) # urlsplit results have hostname and port results, but they # didn't support ipv6 literals until python 2.7. netloc = self.parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = httputil.split_host_and_port(netloc) if port is None: port = 443 if self.parsed.scheme == "https" else 80 if re.match(r'^\[.*\]$', host): # raw ipv6 addresses in urls are enclosed in brackets host = host[1:-1] self.parsed_hostname = host # save final host for _on_connect if request.allow_ipv6 is False: af = socket.AF_INET else: af = socket.AF_UNSPEC ssl_options = self._get_ssl_options(self.parsed.scheme) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, stack_context.wrap(self._on_timeout)) self.tcp_client.connect(host, port, af=af, ssl_options=ssl_options, max_buffer_size=self.max_buffer_size, callback=self._on_connect)
def _get_host_handlers(self, request): host = split_host_and_port(request.host.lower())[0] matches = [] for pattern, handlers in self.handlers: if pattern.match(host): matches.extend(handlers) # Look for default host if not behind load balancer (for debugging) if not matches and "X-Real-Ip" not in request.headers: for pattern, handlers in self.handlers: if pattern.match(self.default_host): matches.extend(handlers) return matches or None
def prepare_tornado_request(self, request): dataDict = {} for key in request.arguments: dataDict[key] = request.arguments[key][0].decode('utf-8') result = { 'https': 'on' if request == 'https' else 'off', 'http_host': split_host_and_port(request.host)[0], 'script_name': request.path, 'server_port': split_host_and_port(request.host)[1], 'get_data': dataDict, 'post_data': dataDict, 'query_string': request.query } if self.force_https: result['https'] = self.force_https self.log.debug("The values used for the tornado request are: %r" % result) return result
async def CAPTCHA_check(self,CAPTCHA,request_params): try: client = tornado.httpclient.AsyncHTTPClient() request = { k:v for k,v in request_params.items() if (v != None) } request = copy.deepcopy(request) if "url_params" in request: request["url_params"] = { k:v.replace("__front_end_response__",CAPTCHA) for k,v in request["url_params"].items() } request["url"] = request["url"] + "?" + urlencode(request["url_params"]) del request["url_params"] if "body_params" in request: request["body_params"] = { k:v.replace("__front_end_response__",CAPTCHA) for k,v in request["body_params"].items() } request["body"] = urlencode(request["body_params"]) del request["body_params"] url = request["url"] del request["url"] if self.block_SSRF == True: parsed = urllib.parse.urlsplit(_unicode(url)) if parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % url) netloc = parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = httputil.split_host_and_port(netloc) if port is None: port = 443 if parsed.scheme == "https" else 80 if re.match(r"^\[.*\]$", host): host = host[1:-1] host_ips = list(filter(lambda x:x[1]==socket.SOCK_STREAM,socket.getaddrinfo(host,port))) host_ips_global = list(filter(lambda x:ipaddress.ip_address(x[-1][0]).is_global,host_ips)) host_ips_connectable = [x for x in host_ips_global if check_sock(x)] if len(host_ips_global) == 0: raise self.generateError(400,"SSRF blocked","Request to local network are blocked due to SSRF protection enabled") if port not in self.block_SSRF_port_whitelist: raise self.generateError(400,"SSRF blocked","Request port are not in block_SSRF_port_whitelist.") if len(host_ips_connectable) == 0: raise self.generateError(500,"CAPTCHA API fetch error","Not connectable") if "follow_redirects" in request and request["follow_redirects"] == True: raise self.generateError(400,"SSRF blocked","follow_redirects are not available if SSRF protection enabled") request["follow_redirects"] = False host_ip = host_ips_connectable[0] client = tornado.httpclient.AsyncHTTPClient(hostname_mapping={host:host_ip}) response = await client.fetch(url,**request) self._CAPTCHA_api_response_example = response return response except Exception as e: raise self.generateError(400,"CAPTCHA API fetch error",e,error_url="https://www.tornadoweb.org/en/stable/httpclient.html#tornado.httpclient.AsyncHTTPClient.fetch")
def get_sinatra_request(self, request: HTTPServerRequest): host, port = split_host_and_port(request.host) return { "env": { "PATH_INFO": request.path, "QUERY_STRING": request.query, "REMOTE_ADDR": request.remote_ip, "REMOTE_HOST": request.host, "REQUEST_METHOD": request.method, "REQUEST_URI": f"{request.protocol}://{request.host}{request.uri}", "SCRIPT_NAME": "", "SERVER_NAME": host, "SERVER_PORT": port, "SERVER_PROTOCOL": request.version, "HTTP_HOST": request.host, "HTTP_ACCEPT": "*/*", "HTTP_COOKIE": ";".join([v.OutputString() for v in request.cookies.values()]), "HTTPS": "on" if request.protocol == "https" else "off", "HTTP_VERSION": request.version, "REQUEST_PATH": request.path, "rack.input": request.body.decode("utf8"), } }
def request_environment(request): # This creates a WSGI environ dictionary from a Tornado request. environ = {} environ['REQUEST_URI'] = request.uri environ['QUERY_STRING'] = request.query environ['REQUEST_METHOD'] = request.method for header, value in request.headers.items(): if header in ['Content-Type', 'Content-Length']: wsgi_name = header.replace('-', '_').upper() else: wsgi_name = 'HTTP_' + header.replace('-', '_').upper() environ[wsgi_name] = value from tornado.httputil import split_host_and_port port = split_host_and_port(request.host.lower())[1] if port: environ['SERVER_PORT'] = port return environ
def _getHostSubDomain(cls): """ returns the subdomain portion of the request hostname eg something.copyrighthub.org would return "something" """ subDomain = "" host = cls.request.headers.get('Host') host, port = httputil.split_host_and_port(host) # get the subdomain part hostparts = host.split('.') # match subdomain, but only if not in list of ignore_subdomains if len(hostparts) == 3: if not hostparts[0] in options.ignored_subdomains \ and hostparts[1] == 'copyrighthub' \ and hostparts[2] == 'org': subDomain = hostparts[0] return subDomain
def get_sinatra_request(request: HTTPServerRequest): host, port = split_host_and_port(request.host) return {"env": { "PATH_INFO": request.path, "QUERY_STRING": request.query, "REMOTE_ADDR": request.remote_ip, "REMOTE_HOST": request.host, "REQUEST_METHOD": request.method, "REQUEST_URI": f"{request.protocol}://{request.host}{request.uri}", "SCRIPT_NAME": "", "SERVER_NAME": host, "SERVER_PORT": port, "SERVER_PROTOCOL": request.version, "HTTP_HOST": request.host, "HTTP_ACCEPT": "*/*", "HTTP_COOKIE": ";".join([ v.OutputString() for v in request.cookies.values() ]), "HTTPS": "on" if request.protocol == "https" else "off", "HTTP_VERSION": request.version, "REQUEST_PATH": request.path, "rack.input": request.body.decode("utf8"), }}
def _get_host(self): from tornado import httputil return httputil.split_host_and_port(self.request.host)[0]
async def get(self, path=None): # if the handler got a notebook_path argument, always serve that notebook_path = self.notebook_path or path if self.notebook_path and path: # when we are in single notebook mode but have a path self.redirect_to_file(path) return if self.voila_configuration.enable_nbextensions: # generate a list of nbextensions that are enabled for the classical notebook # a template can use that to load classical notebook extensions, but does not have to notebook_config = self.config_manager.get('notebook') # except for the widget extension itself, since Voilà has its own load_extensions = notebook_config.get('load_extensions', {}) if 'jupyter-js-widgets/extension' in load_extensions: load_extensions['jupyter-js-widgets/extension'] = False if 'voila/extension' in load_extensions: load_extensions['voila/extension'] = False nbextensions = [ name for name, enabled in load_extensions.items() if enabled ] else: nbextensions = [] notebook = await self.load_notebook(notebook_path) if not notebook: return self.cwd = os.path.dirname(notebook_path) path, basename = os.path.split(notebook_path) notebook_name = os.path.splitext(basename)[0] # Adding request uri to kernel env self.kernel_env = os.environ.copy() self.kernel_env['SCRIPT_NAME'] = self.request.path self.kernel_env[ 'PATH_INFO'] = '' # would be /foo/bar if voila.ipynb/foo/bar was supported self.kernel_env['QUERY_STRING'] = str(self.request.query) self.kernel_env['SERVER_SOFTWARE'] = 'voila/{}'.format(__version__) self.kernel_env['SERVER_PROTOCOL'] = str(self.request.version) host, port = split_host_and_port(self.request.host.lower()) self.kernel_env['SERVER_PORT'] = str(port) if port else '' self.kernel_env['SERVER_NAME'] = host # we can override the template via notebook metadata or a query parameter template_override = None if 'voila' in notebook.metadata and self.voila_configuration.allow_template_override in [ 'YES', 'NOTEBOOK' ]: template_override = notebook.metadata['voila'].get('template') if self.voila_configuration.allow_template_override == 'YES': template_override = self.get_argument("voila-template", template_override) if template_override: self.template_paths = collect_template_paths( ['voila', 'nbconvert'], template_override) template_name = template_override or self.voila_configuration.template theme = self.voila_configuration.theme if 'voila' in notebook.metadata and self.voila_configuration.allow_theme_override in [ 'YES', 'NOTEBOOK' ]: theme = notebook.metadata['voila'].get('theme', theme) if self.voila_configuration.allow_theme_override == 'YES': theme = self.get_argument("voila-theme", theme) # render notebook to html resources = { 'base_url': self.base_url, 'nbextensions': nbextensions, 'theme': theme, 'template': template_name, 'metadata': { 'name': notebook_name } } # include potential extra resources extra_resources = self.voila_configuration.config.VoilaConfiguration.resources # if no resources get configured from neither the CLI nor a config file, # extra_resources is a traitlets.config.loader.LazyConfigValue object # This seems to only happy with the notebook server and traitlets 5 # Note that we use string checking for backward compatibility if 'DeferredConfigString' in str(type(extra_resources)): from .configuration import VoilaConfiguration extra_resources = VoilaConfiguration.resources.from_string( extra_resources) if not isinstance(extra_resources, dict): extra_resources = extra_resources.to_dict() if extra_resources: recursive_update(resources, extra_resources) self.exporter = VoilaExporter( template_paths=self.template_paths, template_name=template_name, config=self.traitlet_config, contents_manager=self.contents_manager, # for the image inlining theme=theme, # we now have the theme in two places base_url=self.base_url, ) if self.voila_configuration.strip_sources: self.exporter.exclude_input = True self.exporter.exclude_output_prompt = True self.exporter.exclude_input_prompt = True # These functions allow the start of a kernel and execution of the notebook after (parts of) the template # has been rendered and send to the client to allow progressive rendering. # Template should first call kernel_start, and then decide to use notebook_execute # or cell_generator to implement progressive cell rendering extra_context = { 'kernel_start': self._jinja_kernel_start, 'cell_generator': self._jinja_cell_generator, 'notebook_execute': self._jinja_notebook_execute, } # Compose reply self.set_header('Content-Type', 'text/html') self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate') self.set_header('Pragma', 'no-cache') self.set_header('Expires', '0') # render notebook in snippets, and flush them out to the browser can render progresssively async for html_snippet, resources in self.exporter.generate_from_notebook_node( notebook, resources=resources, extra_context=extra_context): self.write(html_snippet) self.flush( ) # we may not want to consider not flusing after each snippet, but add an explicit flush function to the jinja context # yield # give control back to tornado's IO loop, so it can handle static files or other requests self.flush()
async def run(self) -> None: try: self.parsed = urllib.parse.urlsplit(_unicode(self.request.url)) if self.parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % self.request.url) # urlsplit results have hostname and port results, but they # didn't support ipv6 literals until python 2.7. netloc = self.parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = httputil.split_host_and_port(netloc) if port is None: port = 443 if self.parsed.scheme == "https" else 80 if re.match(r"^\[.*\]$", host): # raw ipv6 addresses in urls are enclosed in brackets host = host[1:-1] self.parsed_hostname = host # save final host for _on_connect if self.request.allow_ipv6 is False: af = socket.AF_INET else: af = socket.AF_UNSPEC ssl_options = self._get_ssl_options(self.parsed.scheme) source_ip = None if self.request.network_interface: if is_valid_ip(self.request.network_interface): source_ip = self.request.network_interface else: raise ValueError( "Unrecognized IPv4 or IPv6 address for network_interface, got %r" % (self.request.network_interface, )) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, functools.partial(self._on_timeout, "while connecting"), ) stream = await self.tcp_client.connect( host, port, af=af, ssl_options=ssl_options, max_buffer_size=self.max_buffer_size, source_ip=source_ip, ) if self.final_callback is None: # final_callback is cleared if we've hit our timeout. stream.close() return self.stream = stream self.stream.set_close_callback(self.on_connection_close) self._remove_timeout() if self.final_callback is None: return if self.request.request_timeout: self._timeout = self.io_loop.add_timeout( self.start_time + self.request.request_timeout, functools.partial(self._on_timeout, "during request"), ) if (self.request.method not in self._SUPPORTED_METHODS and not self.request.allow_nonstandard_methods): raise KeyError("unknown method %s" % self.request.method) for key in ( "proxy_host", "proxy_port", "proxy_username", "proxy_password", "proxy_auth_mode", ): if getattr(self.request, key, None): raise NotImplementedError("%s not supported" % key) if "Connection" not in self.request.headers: self.request.headers["Connection"] = "close" if "Host" not in self.request.headers: if "@" in self.parsed.netloc: self.request.headers[ "Host"] = self.parsed.netloc.rpartition("@")[-1] else: self.request.headers["Host"] = self.parsed.netloc username, password = None, None if self.parsed.username is not None: username, password = self.parsed.username, self.parsed.password elif self.request.auth_username is not None: username = self.request.auth_username password = self.request.auth_password or "" if username is not None: assert password is not None if self.request.auth_mode not in (None, "basic"): raise ValueError("unsupported auth_mode %s", self.request.auth_mode) self.request.headers[ "Authorization"] = "Basic " + _unicode( base64.b64encode( httputil.encode_username_password( username, password))) if self.request.user_agent: self.request.headers[ "User-Agent"] = self.request.user_agent if not self.request.allow_nonstandard_methods: # Some HTTP methods nearly always have bodies while others # almost never do. Fail in this case unless the user has # opted out of sanity checks with allow_nonstandard_methods. body_expected = self.request.method in ("POST", "PATCH", "PUT") body_present = (self.request.body is not None or self.request.body_producer is not None) if (body_expected and not body_present) or (body_present and not body_expected): raise ValueError( "Body must %sbe None for method %s (unless " "allow_nonstandard_methods is true)" % ("not " if body_expected else "", self.request.method)) if self.request.expect_100_continue: self.request.headers["Expect"] = "100-continue" if self.request.body is not None: # When body_producer is used the caller is responsible for # setting Content-Length (or else chunked encoding will be used). self.request.headers["Content-Length"] = str( len(self.request.body)) if (self.request.method == "POST" and "Content-Type" not in self.request.headers): self.request.headers[ "Content-Type"] = "application/x-www-form-urlencoded" if self.request.decompress_response: self.request.headers["Accept-Encoding"] = "gzip" req_path = (self.parsed.path or "/") + ( ("?" + self.parsed.query) if self.parsed.query else "") self.connection = self._create_connection(stream) start_line = httputil.RequestStartLine(self.request.method, req_path, "") self.connection.write_headers(start_line, self.request.headers) if self.request.expect_100_continue: await self.connection.read_response(self) else: await self._write_body(True) except Exception: if not self._handle_exception(*sys.exc_info()): raise
def run(self): try: self.parsed = urlparse.urlsplit(_unicode(self.request.url)) if self.parsed.scheme not in ("http", "https"): raise ValueError("Unsupported url scheme: %s" % self.request.url) # urlsplit results have hostname and port results, but they # didn't support ipv6 literals until python 2.7. netloc = self.parsed.netloc if "@" in netloc: userpass, _, netloc = netloc.rpartition("@") host, port = httputil.split_host_and_port(netloc) if port is None: port = 443 if self.parsed.scheme == "https" else 80 if re.match(r'^\[.*\]$', host): # raw ipv6 addresses in urls are enclosed in brackets host = host[1:-1] self.parsed_hostname = host # save final host for _on_connect if self.request.allow_ipv6 is False: af = socket.AF_INET else: af = socket.AF_UNSPEC ssl_options = self._get_ssl_options(self.parsed.scheme) timeout = min(self.request.connect_timeout, self.request.request_timeout) if timeout: self._timeout = self.io_loop.add_timeout( self.start_time + timeout, stack_context.wrap(functools.partial(self._on_timeout, "while connecting"))) stream = yield self.tcp_client.connect( host, port, af=af, ssl_options=ssl_options, max_buffer_size=self.max_buffer_size) if self.final_callback is None: # final_callback is cleared if we've hit our timeout. stream.close() return self.stream = stream self.stream.set_close_callback(self.on_connection_close) self._remove_timeout() if self.final_callback is None: return if self.request.request_timeout: self._timeout = self.io_loop.add_timeout( self.start_time + self.request.request_timeout, stack_context.wrap(functools.partial(self._on_timeout, "during request"))) if (self.request.method not in self._SUPPORTED_METHODS and not self.request.allow_nonstandard_methods): raise KeyError("unknown method %s" % self.request.method) for key in ('network_interface', 'proxy_host', 'proxy_port', 'proxy_username', 'proxy_password', 'proxy_auth_mode'): if getattr(self.request, key, None): raise NotImplementedError('%s not supported' % key) if "Connection" not in self.request.headers: self.request.headers["Connection"] = "close" if "Host" not in self.request.headers: if '@' in self.parsed.netloc: self.request.headers["Host"] = self.parsed.netloc.rpartition('@')[-1] else: self.request.headers["Host"] = self.parsed.netloc username, password = None, None if self.parsed.username is not None: username, password = self.parsed.username, self.parsed.password elif self.request.auth_username is not None: username = self.request.auth_username password = self.request.auth_password or '' if username is not None: if self.request.auth_mode not in (None, "basic"): raise ValueError("unsupported auth_mode %s", self.request.auth_mode) self.request.headers["Authorization"] = ( b"Basic " + base64.b64encode( httputil.encode_username_password(username, password))) if self.request.user_agent: self.request.headers["User-Agent"] = self.request.user_agent if not self.request.allow_nonstandard_methods: # Some HTTP methods nearly always have bodies while others # almost never do. Fail in this case unless the user has # opted out of sanity checks with allow_nonstandard_methods. body_expected = self.request.method in ("POST", "PATCH", "PUT") body_present = (self.request.body is not None or self.request.body_producer is not None) if ((body_expected and not body_present) or (body_present and not body_expected)): raise ValueError( 'Body must %sbe None for method %s (unless ' 'allow_nonstandard_methods is true)' % ('not ' if body_expected else '', self.request.method)) if self.request.expect_100_continue: self.request.headers["Expect"] = "100-continue" if self.request.body is not None: # When body_producer is used the caller is responsible for # setting Content-Length (or else chunked encoding will be used). self.request.headers["Content-Length"] = str(len( self.request.body)) if (self.request.method == "POST" and "Content-Type" not in self.request.headers): self.request.headers["Content-Type"] = "application/x-www-form-urlencoded" if self.request.decompress_response: self.request.headers["Accept-Encoding"] = "gzip" req_path = ((self.parsed.path or '/') + (('?' + self.parsed.query) if self.parsed.query else '')) self.connection = self._create_connection(stream) start_line = httputil.RequestStartLine(self.request.method, req_path, '') self.connection.write_headers(start_line, self.request.headers) if self.request.expect_100_continue: yield self.connection.read_response(self) else: yield self._write_body(True) except Exception: if not self._handle_exception(*sys.exc_info()): raise
async def get_generator(self, path=None): # if the handler got a notebook_path argument, always serve that notebook_path = self.notebook_path or path if (self.notebook_path and path): # when we are in single notebook mode but have a path self.redirect_to_file(path) return cwd = os.path.dirname(notebook_path) # Adding request uri to kernel env kernel_env = os.environ.copy() kernel_env[ENV_VARIABLE.SCRIPT_NAME] = self.request.path kernel_env[ ENV_VARIABLE. PATH_INFO] = '' # would be /foo/bar if voila.ipynb/foo/bar was supported kernel_env[ENV_VARIABLE.QUERY_STRING] = str(self.request.query) kernel_env[ENV_VARIABLE.SERVER_SOFTWARE] = 'voila/{}'.format( __version__) kernel_env[ENV_VARIABLE.SERVER_PROTOCOL] = str(self.request.version) host, port = split_host_and_port(self.request.host.lower()) kernel_env[ENV_VARIABLE.SERVER_PORT] = str(port) if port else '' kernel_env[ENV_VARIABLE.SERVER_NAME] = host # Add HTTP Headers as env vars following rfc3875#section-4.1.18 if len(self.voila_configuration.http_header_envs) > 0: for header_name in self.request.headers: config_headers_lower = [ header.lower() for header in self.voila_configuration.http_header_envs ] # Use case insensitive comparison of header names as per rfc2616#section-4.2 if header_name.lower() in config_headers_lower: env_name = f'HTTP_{header_name.upper().replace("-", "_")}' kernel_env[env_name] = self.request.headers.get( header_name) template_arg = self.get_argument("voila-template", None) theme_arg = self.get_argument("voila-theme", None) # Compose reply self.set_header('Content-Type', 'text/html') self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate') self.set_header('Pragma', 'no-cache') self.set_header('Expires', '0') try: current_notebook_data: Dict = self.kernel_manager.notebook_data.get( notebook_path, {}) pool_size: int = self.kernel_manager.get_pool_size(notebook_path) except AttributeError: # For server extenstion case. current_notebook_data = {} pool_size = 0 # Check if the conditions for using pre-heated kernel are satisfied. if self.should_use_rendered_notebook( current_notebook_data, pool_size, template_arg, theme_arg, self.request.arguments, ): # Get the pre-rendered content of notebook, the result can be all rendered cells # of the notebook or some rendred cells and a generator which can be used by this # handler to continue rendering calls. render_task, rendered_cache, kernel_id = await self.kernel_manager.get_rendered_notebook( notebook_name=notebook_path, ) QueryStringSocketHandler.send_updates({ 'kernel_id': kernel_id, 'payload': self.request.query }) # Send rendered cell to frontend if len(rendered_cache) > 0: yield ''.join(rendered_cache) # Wait for current running cell finish and send this cell to # frontend. rendered, rendering = await render_task if len(rendered) > len(rendered_cache): html_snippet = ''.join(rendered[len(rendered_cache):]) yield html_snippet # Continue render cell from generator. async for html_snippet, _ in rendering: yield html_snippet else: # All kernels are used or pre-heated kernel is disabled, start a normal kernel. supported_file_extensions = ['.ipynb'] supported_file_extensions.extend([ x.lower() for x in self.voila_configuration.extension_language_mapping.keys() ]) file_extenstion = Path(notebook_path).suffix.lower() if file_extenstion not in supported_file_extensions: self.redirect_to_file(path) return gen = NotebookRenderer( voila_configuration=self.voila_configuration, traitlet_config=self.traitlet_config, notebook_path=notebook_path, template_paths=self.template_paths, config_manager=self.config_manager, contents_manager=self.contents_manager, base_url=self.base_url, kernel_spec_manager=self.kernel_spec_manager, ) await gen.initialize(template=template_arg, theme=theme_arg) def time_out(): """If not done within the timeout, we send a heartbeat this is fundamentally to avoid browser/proxy read-timeouts, but can be used in a template to give feedback to a user """ return '<script>voila_heartbeat()</script>\n' kernel_env[ENV_VARIABLE.VOILA_PREHEAT] = 'False' kernel_env[ENV_VARIABLE.VOILA_BASE_URL] = self.base_url kernel_id = await ensure_async((self.kernel_manager.start_kernel( kernel_name=gen.notebook.metadata.kernelspec.name, path=cwd, env=kernel_env, ))) kernel_future = self.kernel_manager.get_kernel(kernel_id) queue = asyncio.Queue() async def put_html(): async for html_snippet, _ in gen.generate_content_generator( kernel_id, kernel_future): await queue.put(html_snippet) await queue.put(None) asyncio.ensure_future(put_html()) # If not done within the timeout, we send a heartbeat # this is fundamentally to avoid browser/proxy read-timeouts, but # can be used in a template to give feedback to a user while True: try: html_snippet = await asyncio.wait_for( queue.get(), self.voila_configuration.http_keep_alive_timeout) except asyncio.TimeoutError: yield time_out() else: if html_snippet is None: break yield html_snippet