Beispiel #1
0
def parse_streams(inbound, outbound, scheme=None):
    """Parse one or two HTTP/1.x streams.

    Note that parsing an outbound stream without an inbound stream
    is unreliable, because response framing depends on the request.

    :param inbound:
        The inbound (request) stream as a :class:`~httpolice.stream.Stream`,
        or `None`.
    :param outbound:
        The outbound (response) stream as a :class:`~httpolice.stream.Stream`,
        or `None`.
    :param scheme:
        The scheme of the request URI, as a Unicode string,
        or `None` if unknown.
    :return:
        An iterable of :class:`Exchange` objects.
        Some of the exchanges may be "empty" (aka "complaint boxes"):
        containing neither request nor responses,
        but only a notice that indicates some general problem with the streams.
    """
    while inbound and inbound.good:
        (req, req_box) = _parse_request(inbound, scheme)
        (resps, resp_box) = ([], None)
        if req:
            if outbound and outbound.good:
                (resps, resp_box) = _parse_responses(outbound, req)
                if resps:
                    if resps[-1].status == st.switching_protocols:
                        inbound.sane = False
                    if req.method == m.CONNECT and resps[-1].status.successful:
                        inbound.sane = False
            yield Exchange(req, resps)
        if req_box:
            yield req_box
        if resp_box:
            yield resp_box

    if inbound and not inbound.eof:
        # Some data remains on the inbound stream, but we can't parse it.
        yield complaint_box(1007, stream=inbound, offset=inbound.tell())

    if outbound and outbound.good:
        if inbound:
            # We had some requests, but we ran out of them.
            # We'll still try to parse the remaining responses on their own.
            yield complaint_box(1008, stream=outbound)
        while outbound.good:
            (resps, resp_box) = _parse_responses(outbound, None)
            if resps:
                yield Exchange(None, resps)
            if resp_box:
                yield resp_box

    if outbound and not outbound.eof:
        # Some data remains on the outbound stream, but we can't parse it.
        yield complaint_box(1010, stream=outbound, offset=outbound.tell())
Beispiel #2
0
def _parse_request(stream, scheme=None):
    req = _parse_request_heading(stream, scheme)
    if req is Unavailable:
        box = Exchange(None, [])
        stream.dump_complaints(box.complain, place=u'request heading')
        return (None, box)
    else:
        _parse_request_body(req, stream)
        return (req, None)
Beispiel #3
0
def test_construct_exchange():
    req = Request(u'http', u'GET', u'/', u'HTTP/1.1',
                  [(u'Host', b'example.com')], None)
    assert repr(req) == '<Request GET>'
    resp1 = Response(u'HTTP/1.1', 123, u'Please wait', [], None)
    assert repr(resp1) == '<Response 123>'
    resp2 = Response(u'HTTP/1.1', 200, u'OK', [(u'Content-Length', b'14')],
                     b'Hello world!\r\n', None)
    exch = Exchange(req, [resp1, resp2])
    assert repr(exch) == \
        'Exchange(<Request GET>, [<Response 123>, <Response 200>])'
    assert isinstance(exch.request.method, Method)
    assert isinstance(exch.request.version, HTTPVersion)
    assert isinstance(exch.request.header_entries[0].name, FieldName)
    assert isinstance(exch.responses[0].version, HTTPVersion)
    assert isinstance(exch.responses[0].status, StatusCode)
    assert isinstance(exch.responses[1].header_entries[0].name, FieldName)
Beispiel #4
0
def _parse_responses(stream, req):
    resps = []
    while stream.sane:
        # Parse all responses corresponding to one request.
        # RFC 7230 section 3.3.
        resp = _parse_response_heading(req, stream)
        if resp is Unavailable:
            box = Exchange(None, [])
            stream.dump_complaints(box.complain, place=u'response heading')
            return (resps, box)
        else:
            resps.append(resp)
            _parse_response_body(resp, stream)
            if (not resp.status.informational) or \
                    (resp.status == st.switching_protocols):
                # This is the final response for this request.
                break
    return (resps, None)
Beispiel #5
0
def _process_entry(data, creator, path):
    req = _process_request(data['request'], creator, path)
    resp = _process_response(data['response'], req, creator, path)
    return Exchange(req, [resp] if resp is not None else [])