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()
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), ")"))
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), ")"))
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)
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)
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
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)
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)
def __init__(self, status, headers=None): self.status = status self.headers = Headers() self.headers.update(headers)
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()
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""