Exemple #1
0
    def handle_parser_exception(self, exc: Exception):
        """
        Handles an exception when parsing.

        This will not call into the app (hence why it is a normal function, and not a coroutine).
        It will also close the connection when it's done.

        :param exc: The exception to handle.
        """
        if isinstance(exc, httptools.HttpParserInvalidMethodError):
            # 405 method not allowed
            r = MethodNotAllowed()
        elif isinstance(exc, httptools.HttpParserError):
            # 400 bad request
            r = BadRequest()
        elif isinstance(exc, httptools.HttpParserUpgrade):
            r = BadRequest(description="Invalid upgrade header.")
        else:
            # internal server error
            r = InternalServerError()

        # Make a fake environment.
        new_environ = to_wsgi_environment(headers=self.headers, method="", path="/",
                                          http_version="1.0", body=None)
        new_environ["SERVER_NAME"] = self.component.get_server_name()
        new_environ["SERVER_PORT"] = str(self.server_port)
        new_environ["REMOTE_ADDR"] = self.ip

        self.raw_write(get_formatted_response(r, new_environ))
        self.parser = httptools.HttpRequestParser(self)
        self.close()
Exemple #2
0
    def handle_parser_exception(self, exc: Exception):
        """
        Handles an exception when parsing.

        This will not call into the app (hence why it is a normal function, and not a coroutine).
        It will also close the connection when it's done.

        :param exc: The exception to handle.
        """
        if isinstance(exc, httptools.HttpParserInvalidMethodError):
            # 405 method not allowed
            r = MethodNotAllowed()
        elif isinstance(exc, (httptools.HttpParserError, httptools.HttpParserUpgrade)):
            # 400 bad request
            r = BadRequest()
        else:
            # internal server error
            r = InternalServerError()

        # Make a fake environment.
        new_environ = to_wsgi_environment(headers=self.headers, method="", path="/",
                                          http_version="1.0", body=None)
        new_environ["SERVER_NAME"] = self.component.get_server_name()
        new_environ["SERVER_PORT"] = str(self.server_port)
        new_environ["REMOTE_ADDR"] = self.ip

        self.raw_write(get_formatted_response(r, new_environ))
        self.parser = httptools.HttpRequestParser(self)
        self.close()
Exemple #3
0
    async def inject_request(self,
                             headers: dict,
                             url: str,
                             method: str = "GET",
                             body: str = None) -> Response:
        """
        Injects a request into the test client.

        This will automatically create the correct context.

        :param headers: The headers to use.
        :param body: The body to use.
        :param url: The URL to use.
        :param method: The method to use.
        :return: The result.
        """
        if body is not None:
            body = BytesIO(body.encode())

        e = to_wsgi_environment(headers,
                                method,
                                url,
                                http_version="1.1",
                                body=body)
        e["SERVER_NAME"] = ""
        e["SERVER_PORT"] = ""

        r = Request(e)

        # for testing blueprints, etc
        # slow but it's a test so oh well
        self.finalize()

        return await self.process_request(r, self.base_context)
Exemple #4
0
    async def _wait(self):
        """
        The main core of the protocol.

        This constructs a new Werkzeug request from the headers.
        """
        # Event is set, construct the new fake WSGI environment

        # Check if the body has data in it by asking it to tell us what position it's seeked to.
        # If it's > 0, it has data, so we can use it. Otherwise, it doesn't, so it's useless.
        told = self.body.tell()
        if told:
            self.logger.debug("Read {} bytes of data from the connection".format(told))
            self.body.seek(0)
            body = self.body
        else:
            body = None

        version = self.parser.get_http_version()
        method = self.parser.get_method().decode()

        new_environ = to_wsgi_environment(headers=self.headers, method=method, path=self.full_url,
                                          http_version=version, body=body)

        new_environ["kyoukai.protocol"] = self
        new_environ["SERVER_NAME"] = self.component.get_server_name()
        new_environ["SERVER_PORT"] = str(self.server_port)
        new_environ["REMOTE_ADDR"] = self.ip
        new_environ["REMOTE_PORT"] = self.client_port

        # Construct a Request object.
        new_r = self.app.request_class(new_environ, False)

        # Invoke the app.
        async with self.lock:
            try:
                result = await self.app.process_request(new_r, self.parent_context)
            except Exception:
                # not good!
                # write the scary exception text
                self.logger.exception("Error in Kyoukai request handling!")
                self._raw_write(CRITICAL_ERROR_TEXT.encode("utf-8"))
                return
            else:
                # Write the response.
                self.write_response(result, new_environ)
            finally:
                if not self.parser.should_keep_alive():
                    self.close()
                # unlock the event and remove the waiter
                self.parser = httptools.HttpRequestParser(self)
Exemple #5
0
async def test_get_formatted_response():
    """
    Tests getting a formatted werkzeug Response.
    """
    with app.testing_bp() as bp:
        @bp.route("/")
        def root(ctx: HTTPRequestContext):
            return Response("Hello, world!")

        r = await app.inject_request({}, "/")

        environ = to_wsgi_environment({}, "GET", "/", "1.1")
        response = get_formatted_response(r, environ)

        # Pre-built response
        assert response == b'HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: ' \
                           b'13\r\nServer: Kyoukai/%s\r\nX-Powered-By: Kyoukai/%s\r\n\r\nHello, world!' % \
                           (__version__.encode(), __version__.encode())
Exemple #6
0
def test_route_adding():
    with app.testing_bp() as bp:
        # fake route
        def _inner(ctx: HTTPRequestContext):
            pass

        rtt = bp.wrap_route(_inner)
        assert rtt.get_endpoint_name(bp) == bp.name + "._inner"

        rtt = bp.add_route(rtt, "/")
        bp.finalize()
        assert bp.finalized
        assert len(bp.routes) == 1

        # test matching of the route
        environ = to_wsgi_environment({}, "GET", "/", "1.1")
        environ["SERVER_NAME"] = ""
        environ["SERVER_PORT"] = "4444"
        assert bp.match(environ)[0] == rtt

    assert len(app.root.routes) == 0
Exemple #7
0
    async def _wait(self):
        """
        The main core of the protocol.

        This constructs a new Werkzeug request from the headers.
        """
        # Check if the body has data in it by asking it to tell us what position it's seeked to.
        # If it's > 0, it has data, so we can use it. Otherwise, it doesn't, so it's useless.
        told = self.body.tell()
        if told:
            self.logger.debug("Read {} bytes of body data from the connection".format(told))
            self.body.seek(0)
            body = self.body
        else:
            body = None

        version = self.parser.get_http_version()
        method = self.parser.get_method().decode()

        for header, value in self.headers:
            # check if a content-encoding has been passed
            if header == "Content-Encoding" and body is not None:
                # no special encoding
                if value == "identity":
                    pass

                # gzip, decompress as such
                elif value == "gzip":
                    self.logger.debug("Decoding body data as gzip.")
                    try:
                        decompressed_data = gzip.decompress(body.read())
                    except zlib.error:
                        self.write(HTTP_INVALID_COMPRESSION)
                        self.close()
                        return

                    body = BytesIO(decompressed_data)

                # deflate, decompress as such
                elif value == "deflate":
                    z = zlib.decompressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0)
                    try:
                        decompressed_data = z.decompress(body.read())
                    except zlib.error:
                        self.write(HTTP_INVALID_COMPRESSION)
                        self.close()
                        return

                    body = BytesIO(decompressed_data)
                else:
                    self.logger.error("Unknown Content-Encoding sent by client: {}".format(value))

        new_environ = to_wsgi_environment(headers=self.headers, method=method, path=self.full_url,
                                          http_version=version, body=body)

        new_environ["kyoukai.protocol"] = self
        new_environ["SERVER_NAME"] = self.component.get_server_name()
        new_environ["SERVER_PORT"] = str(self.server_port)
        new_environ["REMOTE_ADDR"] = self.ip
        new_environ["REMOTE_PORT"] = self.client_port

        # Construct a Request object.
        new_r = self.app.request_class(new_environ, False)

        # Invoke the app.
        async with self.lock:
            try:
                result = await self.app.process_request(new_r, self.parent_context)
            except Exception:
                # not good!
                # write the scary exception text
                self.logger.exception("Error in Kyoukai request handling!")
                self._raw_write(CRITICAL_ERROR_TEXT.encode("utf-8"))
                return
            else:
                # Write the response.
                self.write_response(result, new_environ)
            finally:
                if not self.parser.should_keep_alive():
                    self.close()
                # unlock the event and remove the waiter
                self.parser = httptools.HttpRequestParser(self)