예제 #1
0
async def test_http1_request(event_loop: asyncio.AbstractEventLoop) -> None:
    server = TCPServer(
        sanity_framework,
        event_loop,
        Config(),
        WorkerContext(),
        MemoryReader(),
        MemoryWriter()  # type: ignore  # noqa: E501
    )
    task = event_loop.create_task(server.run())
    client = h11.Connection(h11.CLIENT)
    await server.reader.send(  # type: ignore
        client.send(
            h11.Request(
                method="POST",
                target="/",
                headers=[
                    (b"host", b"hypercorn"),
                    (b"connection", b"close"),
                    (b"content-length", b"%d" % len(SANITY_BODY)),
                ],
            )))
    await server.reader.send(client.send(h11.Data(data=SANITY_BODY))
                             )  # type: ignore
    await server.reader.send(client.send(h11.EndOfMessage()))  # type: ignore
    events = []
    while True:
        event = client.next_event()
        if event == h11.NEED_DATA:
            data = await server.writer.receive()  # type: ignore
            client.receive_data(data)
        elif isinstance(event, h11.ConnectionClosed):
            break
        else:
            events.append(event)

    assert events == [
        h11.Response(
            status_code=200,
            headers=[
                (b"content-length", b"15"),
                (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"),
                (b"server", b"hypercorn-h11"),
                (b"connection", b"close"),
            ],
            http_version=b"1.1",
            reason=b"",
        ),
        h11.Data(data=b"Hello & Goodbye"),
        h11.EndOfMessage(headers=[]),  # type: ignore
    ]
    server.reader.close()  # type: ignore
    await task
예제 #2
0
async def test_client_sends_chunked(
    event_loop: asyncio.AbstractEventLoop, ) -> None:
    connection = MockConnection(event_loop)
    chunked_headers = [('transfer-encoding', 'chunked'),
                       ('expect', '100-continue')]
    await connection.send(
        h11.Request(method='POST',
                    target='/echo',
                    headers=BASIC_HEADERS + chunked_headers), )
    await connection.transport.updated.wait()
    informational_response = connection.get_events()[0]
    assert isinstance(informational_response, h11.InformationalResponse)
    assert informational_response.status_code == 100
    connection.transport.clear()
    for chunk in [b'chunked ', b'data']:
        await connection.send(
            h11.Data(data=chunk, chunk_start=True, chunk_end=True))
    await connection.send(h11.EndOfMessage())
    await connection.transport.closed.wait()
    response, *data, end = connection.get_events()
    assert isinstance(response, h11.Response)
    assert response.status_code == 200
    assert all(isinstance(datum, h11.Data) for datum in data)
    data = json.loads(b''.join(datum.data for datum in data).decode())
    assert data['request_body'] == 'chunked data'  # type: ignore
    assert isinstance(end, h11.EndOfMessage)
예제 #3
0
파일: mock.py 프로젝트: vit0r/turq
    def _send_fatal_error(self, exc):
        status_code = getattr(exc, 'error_status_hint', 500)
        self._logger.debug('sending error response, status %d', status_code)
        try:
            self.send_event(
                h11.Response(
                    status_code=status_code,
                    reason=turq.util.http.default_reason(status_code).encode(),
                    headers=[
                        (b'Date', turq.util.http.date().encode()),
                        (b'Content-Type', b'text/plain'),
                        (b'Connection', b'close'),
                    ],
                ))
            self.send_event(h11.Data(data=('Error: %s\r\n' % exc).encode()))
            self.send_event(h11.EndOfMessage())
        except Exception as e:
            self._logger.debug('cannot send error response: %s', e)

        # A crude way to avoid the TCP reset problem (RFC 7230 Section 6.6).
        try:
            self._socket.shutdown(socket.SHUT_WR)
            while self._socket.recv(1024):
                self._logger.debug('discarding data from client')
        except OSError:  # The client may have already closed the connection
            pass
예제 #4
0
    async def handle_client_http(self, stream, event, conn):
        # TODO: right now we handle a single request then close the connection
        # hence HTTP 1.1 keep-alive is not supported
        req = HTTPRequest.from_h11_req(event)
        rep = await self.http.handle_request(req)

        if self.config.debug:
            server_header = f"guardata/{guardata_version} {h11.PRODUCT_ID}"
        else:
            server_header = "guardata"
        rep.headers.append(("server", server_header))
        # Tell no support for keep-alive (h11 will know what to do from there)
        rep.headers.append(("connection", "close"))

        try:
            response_data = bytearray(
                conn.send(
                    h11.Response(
                        status_code=rep.status_code, headers=rep.headers, reason=rep.reason
                    )
                )
            )
            if rep.data:
                response_data += conn.send(h11.Data(data=rep.data))
            response_data += conn.send(h11.EndOfMessage())
            await stream.send_all(response_data)
        except trio.BrokenResourceError:
            # Peer is already gone, nothing to do
            pass
예제 #5
0
 def end_of_body(self):
     """
     This method marks the end of the body. It should be called once all the
     body data has been sent (if there is any), and may potentially emit
     more bytes.
     """
     return self._conn.send(h11.EndOfMessage())
예제 #6
0
 def handle(self):
     with closing(self.request) as s:
         c = h11.Connection(h11.SERVER)
         request = None
         while True:
             event = c.next_event()
             if event is h11.NEED_DATA:
                 # Use a small read buffer to make things more challenging
                 # and exercise more paths :-)
                 c.receive_data(s.recv(10))
                 continue
             if type(event) is h11.Request:
                 request = event
             if type(event) is h11.EndOfMessage:
                 break
         info = json.dumps({
             "method": request.method.decode("ascii"),
             "target": request.target.decode("ascii"),
             "headers": {
                 name.decode("ascii"): value.decode("ascii")
                 for (name, value) in request.headers
             },
         })
         s.sendall(c.send(h11.Response(status_code=200, headers=[])))
         s.sendall(c.send(h11.Data(data=info.encode("ascii"))))
         s.sendall(c.send(h11.EndOfMessage()))
예제 #7
0
 async def send(self, wrapper):
     headers = wrapper.server.create_headers() + self.headers
     await wrapper.send(
         h11.Response(status_code=self.status_code, headers=headers))
     if self.body:
         await wrapper.send(h11.Data(data=self.body))
     await wrapper.send(h11.EndOfMessage())
예제 #8
0
async def test_client_sends_chunked(
    event_loop: asyncio.AbstractEventLoop, ) -> None:
    connection = MockConnection(event_loop)
    chunked_headers = [("transfer-encoding", "chunked"),
                       ("expect", "100-continue")]
    await connection.send(
        h11.Request(method="POST",
                    target="/echo",
                    headers=BASIC_HEADERS + chunked_headers))
    await connection.transport.updated.wait()
    informational_response = connection.get_events()[0]
    assert isinstance(informational_response, h11.InformationalResponse)
    assert informational_response.status_code == 100
    connection.transport.clear()
    for chunk in [b"chunked ", b"data"]:
        await connection.send(
            h11.Data(data=chunk, chunk_start=True, chunk_end=True))
    await connection.send(h11.EndOfMessage())
    await connection.transport.closed.wait()
    response, *data, end = connection.get_events()
    assert isinstance(response, h11.Response)
    assert response.status_code == 200
    assert all(isinstance(datum, h11.Data) for datum in data)
    data = json.loads(b"".join(datum.data for datum in data).decode())
    assert data["request_body"] == "chunked data"  # type: ignore
    assert isinstance(end, h11.EndOfMessage)
예제 #9
0
    def send_error(self, event: h11.Request, status: HTTPStatus, msg: str = None, explain: str = None):
        try:
            short_msg, long_msg = responses[status]
        except KeyError:
            short_msg, long_msg = '???', '???'

        if msg is None:
            msg = short_msg

        if explain is None:
            explain = long_msg

        headers = []

        self.log.error('code {}, message {}'.format(status, msg))

        body = None
        if status >= 200 and status not in (HTTPStatus.NO_CONTENT, HTTPStatus.RESET_CONTENT, HTTPStatus.NOT_MODIFIED):
            body = self.error_message_format.format(code=status, message=msg, explain=explain).encode('UTF-8',
                                                                                                      'replace')
            headers.extend([('Content-Type', self.error_content_type),
                            ('Content-Length', str(len(body)))])

        headers.append(('Connection', 'close'))

        response = h11.Response(status_code=status, headers=headers)
        self.send(response)

        if event.method != 'HEAD' and body:
            self.send(h11.Data(data=body))

        self.send(h11.EndOfMessage())
예제 #10
0
    async def connect(self, data=None, _json=None, headers=[]):
        self.stream = await NetworkStream.connect(self.hostname, self.port)
        if self.scheme == 'https':
            self.stream = SSLStream(self.stream, self.hostname)

        outheaders = [
            ('Host', self.hostname),
            ('Connection', 'close'),
            ('Accept-Encoding', 'gzip'),
        ]
        if _json is not None:
            # overrides data
            data = json.dumps(_json)
            outheaders.append(('Content-Type', 'application/json'))
        if data is not None:
            if hasattr(data, 'items'):
                data = urllib.parse.urlencode(data)
                outheaders.append(
                    ('Content-Type', 'application/x-www-form-urlencoded'))
            if hasattr(data, 'encode'):
                data = data.encode('UTF-8')
            outheaders.append(('Content-Length', str(len(data))))
        outheaders.extend(headers)
        await self.send(
            h11.Request(method=self.method, target=self.qs,
                        headers=outheaders))
        if data is not None:
            await self.send(h11.Data(data=data))
        await self.send(h11.EndOfMessage())
        self.connected = True
        self.log.debug('%s', f'{self.url}: connected to {self.stream!r}')
예제 #11
0
파일: _server.py 프로젝트: mgrrx/aioros
async def handle_request(
    server_handle: ServerHandle,
    wrapper: ClientWrapper,
    request: h11.Request,
) -> None:
    body = b""
    while True:
        event = await wrapper.next_event()
        if isinstance(event, h11.EndOfMessage):
            break
        body += cast(h11.Data, event).data
    method_name, args = parse_request(body, dict(request.headers))
    try:
        method = lookup_method(server_handle, method_name)
        result = await method(*args)
        root = format_success(result)
        status_code = 200
    except Exception as exc:
        root = format_error(exc)
        status_code = 500

    data = build_xml(root)
    headers = wrapper.basic_headers()
    headers.append((b"Content-length", str(len(data)).encode()))
    response = h11.Response(status_code=status_code, headers=headers)
    await wrapper.send(response)
    await wrapper.send(h11.Data(data=data))
    await wrapper.send(h11.EndOfMessage())
예제 #12
0
def test_response_framing_1_content_length(example):
    resp, data, _ = example.send(
        h11.Request(method='GET', target='/', headers=[('Host', 'example')]),
        h11.EndOfMessage())
    assert (b'content-length', b'14') in resp.headers
    assert b'transfer-encoding' not in dict(resp.headers)
    assert data.data == b'Hello world!\r\n'
예제 #13
0
def test_handshake_rejection_with_body() -> None:
    events = _make_handshake_rejection(400, body=b"Hello")
    assert events == [
        h11.Response(headers=[(b"content-length", b"5")], status_code=400),
        h11.Data(data=b"Hello"),
        h11.EndOfMessage(),
    ]
예제 #14
0
 async def _send_http_reply(
     self,
     stream: Stream,
     conn: h11.Connection,
     status_code: int,
     headers: Dict[bytes, bytes] = {},
     data: Optional[bytes] = None,
 ) -> None:
     reason = HTTPStatus(status_code).phrase
     headers = list({
         **headers,
         # Add default headers
         b"server": self.server_header,
         b"date": format_date_time(None).encode("ascii"),
         b"content-Length": str(len(data or b"")).encode("ascii"),
         # Inform we don't support keep-alive (h11 will know what to do from there)
         b"connection": b"close",
     }.items())
     try:
         await stream.send_all(
             conn.send(
                 h11.Response(status_code=status_code,
                              headers=headers,
                              reason=reason)))
         if data:
             await stream.send_all(conn.send(h11.Data(data=data)))
         await stream.send_all(conn.send(h11.EndOfMessage()))
     except trio.BrokenResourceError:
         # Given we don't support keep-alive, the connection is going to be
         # shutdown anyway, so we can safely ignore the fact peer has left
         pass
예제 #15
0
파일: h11.py 프로젝트: tharvik/quart
 def _handle_error(self) -> None:
     self._send(h11.Response(
         status_code=400, headers=chain(
             [('content-length', '0'), ('connection', 'close')], self.response_headers(),
         ),
     ))
     self._send(h11.EndOfMessage())
예제 #16
0
async def test_http1_keep_alive_during(
    client_stream: trio.testing._memory_streams.MemorySendStream, ) -> None:
    client = h11.Connection(h11.CLIENT)
    await client_stream.send_all(client.send(REQUEST))
    await trio.sleep(2 * KEEP_ALIVE_TIMEOUT)
    # Key is that this doesn't error
    await client_stream.send_all(client.send(h11.EndOfMessage()))
예제 #17
0
 def _send_body(self):
     if self._response.body:
         self.chunk(self._response.body)
     self._log_headers(self._response.raw_headers)
     self._handler.send_event(h11.EndOfMessage(
         headers=_encode_headers(self._response.raw_headers),
     ))
예제 #18
0
 async def _handle_websocket(self, websocket: Websocket) -> None:
     response = await self.app.handle_websocket(websocket)
     if response is not None:
         if self.active:
             self.connection.close(
                 wsproto.connection.CloseReason.INTERNAL_ERROR)
             self.write(self.connection.bytes_to_send())
         else:
             headers = chain(
                 ((key, value) for key, value in response.headers.items()),
                 self.response_headers(),
             )
             self.write(
                 self.connection._upgrade_connection.send(
                     h11.Response(status_code=response.status_code,
                                  headers=headers), ), )
             if not suppress_body('GET', response.status_code):
                 async for data in response.response:
                     self.write(
                         self.connection._upgrade_connection.send(
                             h11.Data(data=data)), )
                     await self.drain()
             self.write(
                 self.connection._upgrade_connection.send(
                     h11.EndOfMessage()), )
     self.close()
예제 #19
0
    async def asgi_send(self, message: dict) -> None:
        """Called by the ASGI instance to send a message."""
        if message["type"] == "http.response.start" and self.state == ASGIHTTPState.REQUEST:
            self.response = message
        elif message["type"] == "http.response.body" and self.state in {
            ASGIHTTPState.REQUEST,
            ASGIHTTPState.RESPONSE,
        }:
            if self.state == ASGIHTTPState.REQUEST:
                headers = chain(
                    (
                        (bytes(key).strip(), bytes(value).strip())
                        for key, value in self.response["headers"]
                    ),
                    self.response_headers(),
                )
                await self.asend(
                    h11.Response(status_code=int(self.response["status"]), headers=headers)
                )
                self.state = ASGIHTTPState.RESPONSE

            if (
                not suppress_body(self.scope["method"], int(self.response["status"]))
                and message.get("body", b"") != b""
            ):
                await self.asend(h11.Data(data=bytes(message["body"])))

            if not message.get("more_body", False):
                if self.state != ASGIHTTPState.CLOSED:
                    await self.asend(h11.EndOfMessage())
                    await self.asgi_put({"type": "http.disconnect"})
                    self.state = ASGIHTTPState.CLOSED
        else:
            raise UnexpectedMessage(self.state, message["type"])
예제 #20
0
파일: hydra.py 프로젝트: seh9000/rsyscall
 async def post(self, target: str, body: bytes) -> bytes:
     # send request
     data = self.connection.send(
         h11.Request(method="POST",
                     target=target,
                     headers=[
                         *self.get_headers(),
                         ("Content-Length", str(len(body)))
                     ]))
     data += self.connection.send(h11.Data(data=body))
     data += self.connection.send(h11.EndOfMessage())
     await self.write(data)
     # get response
     response = await self.next_event()
     data = await self.next_event()
     eom = await self.next_event()
     print(response)
     print(data)
     print(eom)
     set_cookie = lookup_alist(response.headers, b"set-cookie")
     if set_cookie is not None:
         self.cookie = set_cookie
     if response.status_code >= 300:
         raise Exception("error posting", data.data)
     self.connection.start_next_cycle()
     return data.data
예제 #21
0
 async def send_events(connection, stream, events):
     for event in events:
         data = connection.send(event)
         await stream.send_all(data)
     if not isinstance(event, h11.EndOfMessage):
         data = connection.send(h11.EndOfMessage())
         await stream.send_all(data)
예제 #22
0
 async def respond_to_h11(self, h11_conn, event):
     """
     Most generic response to an h11 connection possible.
     """
     stream = H11Stream(h11_conn, event, (None, None))
     print("stream:", stream)
     handler = self.router.match(stream)
     print("handler:", handler)
     print("htype:", type(handler))
     handler_result = await handler(stream)
     if handler_result is not None:
         status, response = handler_result
     else:
         status, response = None, None
     print("response:", response)
     if not stream._headers_sent:
         content_type, response = response_to_bytes(handler, response)
         content_length = str(len(response))
         headers = h11_conn.basic_headers()
         headers.append(('Content-Type', content_type))
         headers.append(('Content-Length', content_length))
         resp = h11.Response(status_code=status, headers=headers)
         await h11_conn.send(resp)
     if response:
         await h11_conn.send(h11.Data(data=response))
     await h11_conn.send(h11.EndOfMessage())
     await h11_conn.close()
예제 #23
0
 async def stream_send(self, event: StreamEvent) -> None:
     if isinstance(event, Response):
         if event.status_code >= 200:
             await self._send_h11_event(
                 h11.Response(
                     headers=chain(event.headers,
                                   self.config.response_headers("h11")),
                     status_code=event.status_code,
                 ))
         else:
             await self._send_h11_event(
                 h11.InformationalResponse(
                     headers=chain(event.headers,
                                   self.config.response_headers("h11")),
                     status_code=event.status_code,
                 ))
     elif isinstance(event, Body):
         await self._send_h11_event(h11.Data(data=event.data))
     elif isinstance(event, EndBody):
         await self._send_h11_event(h11.EndOfMessage())
     elif isinstance(event, Data):
         await self.send(RawData(data=event.data))
     elif isinstance(event, EndData):
         pass
     elif isinstance(event, StreamClosed):
         await self._maybe_recycle()
예제 #24
0
 async def asgi_send(self, message: dict) -> None:
     """Called by the ASGI instance to send a message."""
     if message['type'] == 'http.response.start' and self.state == ASGIState.REQUEST:
         self.response = message
     elif (
             message['type'] == 'http.response.body'
             and self.state in {ASGIState.REQUEST, ASGIState.RESPONSE}
     ):
         if self.state == ASGIState.REQUEST:
             headers = chain(
                 (
                     (bytes(key).strip(), bytes(value).strip())
                     for key, value in self.response['headers']
                 ),
                 self.response_headers(),
             )
             self.send(h11.Response(status_code=int(self.response['status']), headers=headers))
             self.state = ASGIState.RESPONSE
         if (
                 not suppress_body(self.scope['method'], int(self.response['status']))
                 and message.get('body', b'') != b''
         ):
             self.send(h11.Data(data=bytes(message['body'])))
             await self.drain()
         if not message.get('more_body', False):
             if self.state != ASGIState.CLOSED:
                 self.send(h11.EndOfMessage())
                 self.app_queue.put_nowait({'type': 'http.disconnect'})
             self.state = ASGIState.CLOSED
     else:
         raise Exception(
             f"Unexpected message type, {message['type']} given the state {self.state}",
         )
예제 #25
0
def test_h11_as_client():
    with socket_server(SingleMindedRequestHandler) as httpd:
        with closing(socket.create_connection(httpd.server_address)) as s:
            c = h11.Connection(h11.CLIENT)

            s.sendall(
                c.send(
                    h11.Request(method="GET",
                                target="/foo",
                                headers=[("Host", "localhost")])))
            s.sendall(c.send(h11.EndOfMessage()))

            data = bytearray()
            while True:
                event = c.next_event()
                print(event)
                if event is h11.NEED_DATA:
                    # Use a small read buffer to make things more challenging
                    # and exercise more paths :-)
                    c.receive_data(s.recv(10))
                    continue
                if type(event) is h11.Response:
                    assert event.status_code == 200
                if type(event) is h11.Data:
                    data += event.data
                if type(event) is h11.EndOfMessage:
                    break
            assert bytes(data) == test_file_data
예제 #26
0
async def make_h11_request(socket):
    conn = h11.Connection(our_role=h11.CLIENT)
    req = h11.Request(
        method="GET",
        target="/",
        headers=[
            ("Host", "localhost"),
            ("Connection", "close"),
        ],
    )
    data = conn.send(req)
    await socket.sendall(data)
    req = h11.EndOfMessage()
    data = conn.send(req)
    await socket.sendall(data)

    async def next_event():
        while True:
            event = conn.next_event()
            if event is h11.NEED_DATA:
                data = await socket.recv(2 * 16)
                conn.receive_data(data)
                continue
            return event

    while True:
        print("getting event")
        event = await next_event()
        print(event)
        if type(event) is h11.EndOfMessage:
            break
예제 #27
0
async def test_http1_request(nursery: trio._core._run.Nursery) -> None:
    client_stream, server_stream = trio.testing.memory_stream_pair()
    server_stream.socket = MockSocket()
    server = TCPServer(sanity_framework, Config(), WorkerContext(),
                       server_stream)
    nursery.start_soon(server.run)
    client = h11.Connection(h11.CLIENT)
    await client_stream.send_all(
        client.send(
            h11.Request(
                method="POST",
                target="/",
                headers=[
                    (b"host", b"hypercorn"),
                    (b"connection", b"close"),
                    (b"content-length", b"%d" % len(SANITY_BODY)),
                ],
            )))
    await client_stream.send_all(client.send(h11.Data(data=SANITY_BODY)))
    await client_stream.send_all(client.send(h11.EndOfMessage()))
    events = []
    while True:
        event = client.next_event()
        if event == h11.NEED_DATA:
            # bytes cast is key otherwise b"" is lost
            data = bytes(await client_stream.receive_some(1024))
            client.receive_data(data)
        elif isinstance(event, h11.ConnectionClosed):
            break
        else:
            events.append(event)

    assert events == [
        h11.Response(
            status_code=200,
            headers=[
                (b"content-length", b"15"),
                (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"),
                (b"server", b"hypercorn-h11"),
                (b"connection", b"close"),
            ],
            http_version=b"1.1",
            reason=b"",
        ),
        h11.Data(data=b"Hello & Goodbye"),
        h11.EndOfMessage(headers=[]),  # type: ignore
    ]
예제 #28
0
def test_basics_1_not_found(example):
    resp, data, _ = example.send(
        h11.Request(method='GET', target='/', headers=[('Host', 'example')]),
        h11.EndOfMessage())
    assert resp.status_code == 404
    assert resp.reason == b'Not Found'
    assert (b'content-type', b'text/plain; charset=utf-8') in resp.headers
    assert b'Error! ' in data.data
예제 #29
0
def test_basics_1_head(example):
    resp, _ = example.send(
        h11.Request(method='HEAD',
                    target='/hello',
                    headers=[('Host', 'example')]), h11.EndOfMessage())
    assert resp.status_code == 200
    assert resp.reason == b'OK'
    assert (b'content-type', b'text/plain') in resp.headers
예제 #30
0
async def test_close_on_framework_error(
        event_loop: asyncio.AbstractEventLoop) -> None:
    connection = MockConnection(event_loop, framework=ErrorFramework)
    await connection.send(
        h11.Request(method='GET', target='/', headers=BASIC_HEADERS))
    await connection.send(h11.EndOfMessage())
    await connection.transport.closed.wait(
    )  # This is the key part, must close on error