Beispiel #1
0
 def _read_body_until_close(
         self, delegate: httputil.HTTPMessageDelegate
 ) -> Generator[Any, Any, None]:
     body = yield self.stream.read_until_close()
     if not self._write_finished or self.is_client:
         with _ExceptionLoggingContext(app_log):
             delegate.data_received(body)
 async def _read_chunked_body(
         self, delegate: httputil.HTTPMessageDelegate) -> None:
     # TODO: "chunk extensions" http://tools.ietf.org/html/rfc2616#section-3.6.1
     total_size = 0
     while True:
         chunk_len_str = await self.stream.read_until(b"\r\n", max_bytes=64)
         chunk_len = int(chunk_len_str.strip(), 16)
         if chunk_len == 0:
             crlf = await self.stream.read_bytes(2)
             if crlf != b"\r\n":
                 raise httputil.HTTPInputError(
                     "improperly terminated chunked request")
             return
         total_size += chunk_len
         if total_size > self._max_body_size:
             raise httputil.HTTPInputError("chunked body too large")
         bytes_to_read = chunk_len
         while bytes_to_read:
             chunk = await self.stream.read_bytes(min(
                 bytes_to_read, self.params.chunk_size),
                                                  partial=True)
             bytes_to_read -= len(chunk)
             if not self._write_finished or self.is_client:
                 with _ExceptionLoggingContext(app_log):
                     ret = delegate.data_received(chunk)
                     if ret is not None:
                         await ret
         # chunk ends with \r\n
         crlf = await self.stream.read_bytes(2)
         assert crlf == b"\r\n"
Beispiel #3
0
 async def _read_chunked_body(self, delegate: httputil.HTTPMessageDelegate) -> None:
     # TODO: "chunk extensions" http://tools.ietf.org/html/rfc2616#section-3.6.1
     total_size = 0
     while True:
         chunk_len_str = await self.stream.read_until(b"\r\n", max_bytes=64)
         chunk_len = int(chunk_len_str.strip(), 16)
         if chunk_len == 0:
             crlf = await self.stream.read_bytes(2)
             if crlf != b"\r\n":
                 raise httputil.HTTPInputError(
                     "improperly terminated chunked request"
                 )
             return
         total_size += chunk_len
         if total_size > self._max_body_size:
             raise httputil.HTTPInputError("chunked body too large")
         bytes_to_read = chunk_len
         while bytes_to_read:
             chunk = await self.stream.read_bytes(
                 min(bytes_to_read, self.params.chunk_size), partial=True
             )
             bytes_to_read -= len(chunk)
             if not self._write_finished or self.is_client:
                 with _ExceptionLoggingContext(app_log):
                     ret = delegate.data_received(chunk)
                     if ret is not None:
                         await ret
         # chunk ends with \r\n
         crlf = await self.stream.read_bytes(2)
         assert crlf == b"\r\n"
 async def _read_body_until_close(
         self, delegate: httputil.HTTPMessageDelegate) -> None:
     body = await self.stream.read_until_close()
     if not self._write_finished or self.is_client:
         with _ExceptionLoggingContext(app_log):
             ret = delegate.data_received(body)
             if ret is not None:
                 await ret
Beispiel #5
0
 async def _read_body_until_close(
     self, delegate: httputil.HTTPMessageDelegate
 ) -> None:
     body = await self.stream.read_until_close()
     if not self._write_finished or self.is_client:
         with _ExceptionLoggingContext(app_log):
             ret = delegate.data_received(body)
             if ret is not None:
                 await ret
Beispiel #6
0
 def _read_fixed_body(self, content_length: int,
                      delegate: httputil.HTTPMessageDelegate) -> Generator[Any, Any, None]:
     while content_length > 0:
         body = yield self.stream.read_bytes(
             min(self.params.chunk_size, content_length), partial=True)
         content_length -= len(body)
         if not self._write_finished or self.is_client:
             with _ExceptionLoggingContext(app_log):
                 ret = delegate.data_received(body)
                 if ret is not None:
                     yield ret
Beispiel #7
0
 async def _read_fixed_body(
     self, content_length: int, delegate: httputil.HTTPMessageDelegate
 ) -> None:
     while content_length > 0:
         body = await self.stream.read_bytes(
             min(self.params.chunk_size, content_length), partial=True
         )
         content_length -= len(body)
         if not self._write_finished or self.is_client:
             with _ExceptionLoggingContext(app_log):
                 ret = delegate.data_received(body)
                 if ret is not None:
                     await ret
 async def _read_message(self,
                         delegate: httputil.HTTPMessageDelegate) -> bool:
     need_delegate_close = False
     try:
         header_future = self.stream.read_until_regex(
             b"\r?\n\r?\n", max_bytes=self.params.max_header_size)
         if self.params.header_timeout is None:
             header_data = await header_future
         else:
             try:
                 header_data = await gen.with_timeout(
                     self.stream.io_loop.time() +
                     self.params.header_timeout,
                     header_future,
                     quiet_exceptions=iostream.StreamClosedError,
                 )
             except gen.TimeoutError:
                 self.close()
                 return False
         start_line_str, headers = self._parse_headers(header_data)
         if self.is_client:
             resp_start_line = httputil.parse_response_start_line(
                 start_line_str)
             self._response_start_line = resp_start_line
             start_line = (
                 resp_start_line
             )  # type: Union[httputil.RequestStartLine, httputil.ResponseStartLine]
             # TODO: this will need to change to support client-side keepalive
             self._disconnect_on_finish = False
         else:
             req_start_line = httputil.parse_request_start_line(
                 start_line_str)
             self._request_start_line = req_start_line
             self._request_headers = headers
             start_line = req_start_line
             self._disconnect_on_finish = not self._can_keep_alive(
                 req_start_line, headers)
         need_delegate_close = True
         with _ExceptionLoggingContext(app_log):
             header_recv_future = delegate.headers_received(
                 start_line, headers)
             if header_recv_future is not None:
                 await header_recv_future
         if self.stream is None:
             # We've been detached.
             need_delegate_close = False
             return False
         skip_body = False
         if self.is_client:
             assert isinstance(start_line, httputil.ResponseStartLine)
             if (self._request_start_line is not None
                     and self._request_start_line.method == "HEAD"):
                 skip_body = True
             code = start_line.code
             if code == 304:
                 # 304 responses may include the content-length header
                 # but do not actually have a body.
                 # http://tools.ietf.org/html/rfc7230#section-3.3
                 skip_body = True
             if code >= 100 and code < 200:
                 # 1xx responses should never indicate the presence of
                 # a body.
                 if "Content-Length" in headers or "Transfer-Encoding" in headers:
                     raise httputil.HTTPInputError(
                         "Response code %d cannot have body" % code)
                 # TODO: client delegates will get headers_received twice
                 # in the case of a 100-continue.  Document or change?
                 await self._read_message(delegate)
         else:
             if headers.get(
                     "Expect"
             ) == "100-continue" and not self._write_finished:
                 self.stream.write(b"HTTP/1.1 100 (Continue)\r\n\r\n")
         if not skip_body:
             body_future = self._read_body(
                 resp_start_line.code if self.is_client else 0, headers,
                 delegate)
             if body_future is not None:
                 if self._body_timeout is None:
                     await body_future
                 else:
                     try:
                         await gen.with_timeout(
                             self.stream.io_loop.time() +
                             self._body_timeout,
                             body_future,
                             quiet_exceptions=iostream.StreamClosedError,
                         )
                     except gen.TimeoutError:
                         gen_log.info("Timeout reading body from %s",
                                      self.context)
                         self.stream.close()
                         return False
         self._read_finished = True
         if not self._write_finished or self.is_client:
             need_delegate_close = False
             with _ExceptionLoggingContext(app_log):
                 delegate.finish()
         # If we're waiting for the application to produce an asynchronous
         # response, and we're not detached, register a close callback
         # on the stream (we didn't need one while we were reading)
         if (not self._finish_future.done() and self.stream is not None
                 and not self.stream.closed()):
             self.stream.set_close_callback(self._on_connection_close)
             await self._finish_future
         if self.is_client and self._disconnect_on_finish:
             self.close()
         if self.stream is None:
             return False
     except httputil.HTTPInputError as e:
         gen_log.info("Malformed HTTP message from %s: %s", self.context, e)
         if not self.is_client:
             await self.stream.write(b"HTTP/1.1 400 Bad Request\r\n\r\n")
         self.close()
         return False
     finally:
         if need_delegate_close:
             with _ExceptionLoggingContext(app_log):
                 delegate.on_connection_close()
         header_future = None  # type: ignore
         self._clear_callbacks()
     return True
Beispiel #9
0
 async def _read_message(self, delegate: httputil.HTTPMessageDelegate) -> bool:
     need_delegate_close = False
     try:
         header_future = self.stream.read_until_regex(
             b"\r?\n\r?\n", max_bytes=self.params.max_header_size
         )
         if self.params.header_timeout is None:
             header_data = await header_future
         else:
             try:
                 header_data = await gen.with_timeout(
                     self.stream.io_loop.time() + self.params.header_timeout,
                     header_future,
                     quiet_exceptions=iostream.StreamClosedError,
                 )
             except gen.TimeoutError:
                 self.close()
                 return False
         start_line_str, headers = self._parse_headers(header_data)
         if self.is_client:
             resp_start_line = httputil.parse_response_start_line(start_line_str)
             self._response_start_line = resp_start_line
             start_line = (
                 resp_start_line
             )  # type: Union[httputil.RequestStartLine, httputil.ResponseStartLine]
             # TODO: this will need to change to support client-side keepalive
             self._disconnect_on_finish = False
         else:
             req_start_line = httputil.parse_request_start_line(start_line_str)
             self._request_start_line = req_start_line
             self._request_headers = headers
             start_line = req_start_line
             self._disconnect_on_finish = not self._can_keep_alive(
                 req_start_line, headers
             )
         need_delegate_close = True
         with _ExceptionLoggingContext(app_log):
             header_recv_future = delegate.headers_received(start_line, headers)
             if header_recv_future is not None:
                 await header_recv_future
         if self.stream is None:
             # We've been detached.
             need_delegate_close = False
             return False
         skip_body = False
         if self.is_client:
             assert isinstance(start_line, httputil.ResponseStartLine)
             if (
                 self._request_start_line is not None
                 and self._request_start_line.method == "HEAD"
             ):
                 skip_body = True
             code = start_line.code
             if code == 304:
                 # 304 responses may include the content-length header
                 # but do not actually have a body.
                 # http://tools.ietf.org/html/rfc7230#section-3.3
                 skip_body = True
             if code >= 100 and code < 200:
                 # 1xx responses should never indicate the presence of
                 # a body.
                 if "Content-Length" in headers or "Transfer-Encoding" in headers:
                     raise httputil.HTTPInputError(
                         "Response code %d cannot have body" % code
                     )
                 # TODO: client delegates will get headers_received twice
                 # in the case of a 100-continue.  Document or change?
                 await self._read_message(delegate)
         else:
             if headers.get("Expect") == "100-continue" and not self._write_finished:
                 self.stream.write(b"HTTP/1.1 100 (Continue)\r\n\r\n")
         if not skip_body:
             body_future = self._read_body(
                 resp_start_line.code if self.is_client else 0, headers, delegate
             )
             if body_future is not None:
                 if self._body_timeout is None:
                     await body_future
                 else:
                     try:
                         await gen.with_timeout(
                             self.stream.io_loop.time() + self._body_timeout,
                             body_future,
                             quiet_exceptions=iostream.StreamClosedError,
                         )
                     except gen.TimeoutError:
                         gen_log.info("Timeout reading body from %s", self.context)
                         self.stream.close()
                         return False
         self._read_finished = True
         if not self._write_finished or self.is_client:
             need_delegate_close = False
             with _ExceptionLoggingContext(app_log):
                 delegate.finish()
         # If we're waiting for the application to produce an asynchronous
         # response, and we're not detached, register a close callback
         # on the stream (we didn't need one while we were reading)
         if (
             not self._finish_future.done()
             and self.stream is not None
             and not self.stream.closed()
         ):
             self.stream.set_close_callback(self._on_connection_close)
             await self._finish_future
         if self.is_client and self._disconnect_on_finish:
             self.close()
         if self.stream is None:
             return False
     except httputil.HTTPInputError as e:
         gen_log.info("Malformed HTTP message from %s: %s", self.context, e)
         if not self.is_client:
             await self.stream.write(b"HTTP/1.1 400 Bad Request\r\n\r\n")
         self.close()
         return False
     finally:
         if need_delegate_close:
             with _ExceptionLoggingContext(app_log):
                 delegate.on_connection_close()
         header_future = None  # type: ignore
         self._clear_callbacks()
     return True