Esempio n. 1
0
    def __init__(self, reader):
        """Initialize a ``ChunkedBodyReader``.

        Parameters:
            :reader: The ``StreamReader`` used to read data.
        """
        self._reader = reader
        self._current_chunk = 0
        self._is_complete = False

        self._body_size = 0
        self._headers = Headers()
Esempio n. 2
0
class Response:
    """Structure used to store server responses."""

    __slots__ = ("status", "headers")

    def __init__(self, status, headers=None):
        self.status = status
        self.headers = Headers()
        self.headers.update(headers)

    def __repr__(self):
        fields = ("{0}: {1!r}".format(name, getattr(self, name))
                  for name in self.__slots__)
        return "".join(("Response(", ", ".join(fields), ")"))
Esempio n. 3
0
class Response:
    """Structure used to store server responses."""

    __slots__ = ("status", "headers")

    def __init__(self, status, headers=None):
        self.status = status
        self.headers = Headers()
        self.headers.update(headers)

    def __repr__(self):
        fields = (
            "{0}: {1!r}".format(name, getattr(self, name))
            for name in self.__slots__
        )
        return "".join(("Response(", ", ".join(fields), ")"))
Esempio n. 4
0
    async def send_response(self, status, headers=None, body=None):
        """Send an HTTP response.

        This function will add the date, server and connection header
        fields to the given hedaer fields.
        """
        assert self._response is None

        reason = HTTP_STATUSES[status]
        status_line = "HTTP/1.1 {0} {1}\r\n".format(status, reason)

        # User defined "connection" header for closing connection
        # after response.
        if headers:
            if self._keep_alive and "close" in headers.pop("connection", []):
                self._keep_alive = False

        response_headers = Headers(
            date=datetime.utcnow(),
            server=self._server.server_agent,
            connection="keep-alive" if self._keep_alive else "close")

        if headers is not None:
            response_headers.update(headers)

        if body is None:
            response_headers.set("content-length", 0)
        elif isinstance(body, bytes):
            response_headers.set("content-length", len(body))
        else:
            raise Exception("body must be bytes or None")

        response_header = bytearray(status_line.encode("ascii"))

        for name, content in response_headers.fields():
            field_line = "{0}: {1}\r\n".format(name.title(), content)
            response_header.extend(field_line.encode("ascii"))

        # HTTP header trailing blank line
        response_header.extend(b"\r\n")

        self._logger.debug(response_header.decode("ascii"))

        self._writer.write(response_header)

        if body is not None:
            self._writer.write(body)

        await self._writer.drain()

        if status >= 200:
            self._response = Response(status, response_headers)
Esempio n. 5
0
    async def send_response(self, status, headers=None, body=None):
        """Send an HTTP response.

        This function will add the date, server and connection header
        fields to the given hedaer fields.
        """
        assert self._response is None

        reason = HTTP_STATUSES[status]
        status_line = "HTTP/1.1 {0} {1}\r\n".format(status, reason)

        # User defined "connection" header for closing connection
        # after response.
        if headers:
            if self._keep_alive and "close" in headers.pop("connection", []):
                self._keep_alive = False

        response_headers = Headers(
            date=datetime.utcnow(),
            server=self._server.server_agent,
            connection="keep-alive" if self._keep_alive else "close"
        )

        if headers is not None:
            response_headers.update(headers)

        if body is None:
            response_headers.set("content-length", 0)
        elif isinstance(body, bytes):
            response_headers.set("content-length", len(body))
        else:
            raise Exception("body must be bytes or None")

        response_header = bytearray(status_line.encode("ascii"))

        for name, content in response_headers.fields():
            field_line = "{0}: {1}\r\n".format(name.title(), content)
            response_header.extend(field_line.encode("ascii"))

        # HTTP header trailing blank line
        response_header.extend(b"\r\n")

        self._logger.debug(response_header.decode("ascii"))

        self._writer.write(response_header)

        if body is not None:
            self._writer.write(body)

        await self._writer.drain()

        if status >= 200:
            self._response = Response(status, response_headers)
Esempio n. 6
0
    def __init__(self, reader):
        """Initialize a ``ChunkedBodyReader``.

        Parameters:
            :reader: The ``StreamReader`` used to read data.
        """
        self._reader = reader
        self._current_chunk = 0
        self._is_complete = False

        self._body_size = 0
        self._headers = Headers()
Esempio n. 7
0
 def __init__(self,
              url,
              method="GET",
              header_fields=None,
              body=None,
              body_streaming_callback=None,
              timeout=None):
     self.url = url
     self.method = method
     self.header_fields = header_fields or Headers()
     self.body = body
     self.body_streaming_callback = body_streaming_callback
     self.timeout = timeout
     self.redirect_count = 0
Esempio n. 8
0
    async def _handle_request(self):
        """This coroutine, called by ``run``, will route the request
        to the associated  request handler.
        """
        #-----------------#
        # Request routing #
        #-----------------#

        method = self._request.method

        try:
            tmp = self._server.router.find_route(self._request.path)
            request_handler_factory, args, kwargs = tmp
        except RoutingError as error:
            # No route finded, send 404 not find error
            self._logger.info("route not find")
            raise HttpError(404) from error

        allowed_methods = request_handler_factory.allowed_methods()
        if method not in allowed_methods:
            # Method not implemented, send error 405 not implemented
            self._logger.info("method not implemented")
            error_headers = Headers(allowed=allowed_methods)
            raise HttpError(405, error_headers)

        #-------------------------#
        # Request handler calling #
        #-------------------------#

        self._handler = request_handler_factory(self)
        method_handler = getattr(self._handler, method.lower())

        can_continue = await self.handler.can_continue()

        if not can_continue:
            if "100-continue" in self._request.headers.get("except", []):
                if not self._response:
                    # if the can_continue method does not send an error,
                    # send a 417 error.
                    self._logger.info("expectation failed")
                    raise HttpError(417)

        if "100-continue" in self._request.headers.get("except", []):
            await self._handler.send_response(100)

        tmp = method_handler(*args, **kwargs)
        await self._loop.create_task(tmp)
Esempio n. 9
0
class ChunkedBodyReader:
    """This class is used to handle the chunked transfert encoding.
    
    Attributes:
        :body_size: The current number of bytes read since the beginning.
        :headers: Contains the trailing headers, if any.
    """
    def __init__(self, reader):
        """Initialize a ``ChunkedBodyReader``.

        Parameters:
            :reader: The ``StreamReader`` used to read data.
        """
        self._reader = reader
        self._current_chunk = 0
        self._is_complete = False

        self._body_size = 0
        self._headers = Headers()

    @property
    def is_complete(self):
        return self._is_complete

    @property
    def body_size(self):
        return self._body_size

    @property
    def headers(self):
        return self._headers

    async def __aiter__(self):
        return self

    async def __anext__(self):
        """Returns the next chunk of data."""
        if self._is_complete:
            raise StopAsyncIteration

        chunk_header = await self._reader.read_until(b"\r\n")
        chunk_size = int(chunk_header, base=16)

        if chunk_size == 0:
            await self._parse_trailer_headers()
            self._is_complete = True
            raise StopAsyncIteration

        chunk = await self._reader.read_until(b"\r\n")

        if len(chunk) != chunk_size:
            raise InvalidChunkError("chunk has a wrong size")

        self._body_size += len(chunk)
        self._current_chunk += 1

        return chunk

    async def _parse_trailer_headers(self):
        """When the last chunk of data is received, this method parse
        the trailing headers, if present.
        """
        trailer_field = await self._reader.read_until(b"\r\n")
        while trailer_field:
            name, content = self._headers.parse_line(trailer_field)
            self._headers.add(name, content)
Esempio n. 10
0
 def __init__(self, status, headers=None):
     self.status = status
     self.headers = Headers()
     self.headers.update(headers)
Esempio n. 11
0
 def __init__(self, method="GET", path="/", query=None, headers=None):
     self.method = method
     self.path = path
     self.query = query or {}
     self.headers = headers or Headers()
Esempio n. 12
0
class ChunkedBodyReader:
    """This class is used to handle the chunked transfert encoding.
    
    Attributes:
        :body_size: The current number of bytes read since the beginning.
        :headers: Contains the trailing headers, if any.
    """

    def __init__(self, reader):
        """Initialize a ``ChunkedBodyReader``.

        Parameters:
            :reader: The ``StreamReader`` used to read data.
        """
        self._reader = reader
        self._current_chunk = 0
        self._is_complete = False

        self._body_size = 0
        self._headers = Headers()

    @property
    def is_complete(self):
        return self._is_complete

    @property
    def body_size(self):
        return self._body_size

    @property
    def headers(self):
        return self._headers

    async def __aiter__(self):
        return self

    async def __anext__(self):
        """Returns the next chunk of data."""
        if self._is_complete:
            raise StopAsyncIteration

        chunk_header = await self._reader.read_until(b"\r\n")
        chunk_size = int(chunk_header, base=16)

        if chunk_size == 0:
            await self._parse_trailer_headers()
            self._is_complete = True
            raise StopAsyncIteration

        chunk = await self._reader.read_until(b"\r\n")

        if len(chunk) != chunk_size:
            raise InvalidChunkError("chunk has a wrong size")

        self._body_size += len(chunk)
        self._current_chunk += 1

        return chunk

    async def _parse_trailer_headers(self):
        """When the last chunk of data is received, this method parse
        the trailing headers, if present.
        """
        trailer_field = await self._reader.read_until(b"\r\n")
        while trailer_field:
            name, content = self._headers.parse_line(trailer_field)
            self._headers.add(name, content)
Esempio n. 13
0
 def __init__(self, status, header_fields=None, request=None):
     self.status = status
     self.header_fields = header_fields or Headers()
     self.request = request
     self.body = b""
Esempio n. 14
0
 def __init__(self, status, headers=None):
     self.status = status
     self.headers = Headers()
     self.headers.update(headers)