question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

httptools.parser.errors.HttpParserInvalidURLError on fragmented first line of the HTTP request.

See original GitHub issue

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

I’ve ran into issue with uvicorn+httptools, HttpParserInvalidURLError is raised and the server closes connection when the first line of the http/1.* request is split into several tcp packets. It also seems like everything is fine if h11 used instead of httptools.

To reproduce

import socket
import threading as th
from time import sleep

import uvicorn

########## HELPERS ##########
def receive_all(sock):
    chunks = []
    while True:
        chunk = sock.recv(1024)
        if not chunk:
            break

        chunks.append(chunk)

    return b''.join(chunks)


async def stub_app(scope, receive, send):
    event = await receive()
    d = b'whatever'
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'Content-Length', str(len(d)).encode()],
            [b'Content-Type', b'text/plain'],
        ]
    })
    await send({'type': 'http.response.body', 'body': d, 'more_body': False})
#############################


def send_fragmented_req(path):

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('127.0.0.1', 5000))
    d = (
        f'GET {path} HTTP/1.1\r\n'
        'Host: localhost\r\n'
        'Connection: close\r\n\r\n'
    ).encode()

    split = len(path) // 2
    # send first part
    sock.sendall(d[:split])
    sleep(0.01)  # important, ~100% error, without this the chances of reproducing the error are pretty low

    # send second part
    sock.sendall(d[split:])
    
    # read response
    resp = receive_all(sock)
    sock.shutdown(socket.SHUT_RDWR)
    sock.close()
    return resp


def test():
    t = th.Thread(target=lambda: uvicorn.run(stub_app, host='127.0.0.1', port=5000, http='httptools'))
    t.daemon = True
    t.start()
    sleep(1)    # wait for unicorn to start
    
    path = '/?param=' + 'q' * 10
    print(send_fragmented_req(path).decode())


if __name__ == '__main__':
    test()


# Console output:
# INFO:     Started server process [748603]
# INFO:     Waiting for application startup.
# INFO:     ASGI 'lifespan' protocol appears unsupported.
# INFO:     Application startup complete.
# INFO:     Uvicorn running on http://127.0.0.1:5000 (Press CTRL+C to quit)
# WARNING:  Invalid HTTP request received.
# Traceback (most recent call last):
#   File "httptools/parser/parser.pyx", line 258, in httptools.parser.parser.cb_on_url
#   File "/_/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 193, in on_url
#     parsed_url = httptools.parse_url(url)
#   File "httptools/parser/url_parser.pyx", line 105, in httptools.parser.url_parser.parse_url
# httptools.parser.errors.HttpParserInvalidURLError: invalid url b'am=qqqqqqqqqq'
#
# During handling of the above exception, another exception occurred:
#
# Traceback (most recent call last):
#   File "/_/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 131, in data_received
#     self.parser.feed_data(data)
#   File "httptools/parser/parser.pyx", line 212, in httptools.parser.parser.HttpParser.feed_data
# httptools.parser.errors.HttpParserCallbackError: `on_url` callback error

Expected behavior

HTTP response:

HTTP/1.1 200 OK
date: Tue, 23 Nov 2021 09:36:10 GMT
server: uvicorn
content-length: 8
content-type: text/plain

whatever

Actual behavior

Server closes connection without a response.

Environment

Running uvicorn 0.15.0 with CPython 3.8.2 on Linux Dependencies: uvicorn==0.15.0 h11==0.12.0 httptools==0.3.0

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
euri10commented, Nov 23, 2021

I’ve understood your solution idea. If it’s ok I’d like to try. I’d like to take some time for it though, to look aroud codebase since i know little about it at the moment.

sure ! take #1263 if you want as a baseline, I did that quickly so I’m not sure there are no edge cases, would be good to add tests for that : in the protocols module that should be the best, not sure how to handle the fragmentation in a test though but in that module we deal with several bare http requests so that would be a good place to add your snippet adapted for our tests format.

1reaction
div1001commented, Nov 23, 2021

want to takle it @div1001 ?

I’ve understood your solution idea. If it’s ok I’d like to try. I’d like to take some time for it though, to look aroud codebase since i know little about it at the moment.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to use the httptools.parser.errors.HttpParserError function ...
HttpParserError function in httptools. To help you get started, we've selected a few httptools examples, based on popular ways it is used in...
Read more >
Http ParserInvalidURLError on Rasa X running on server
I'm running Rasa X on an Ubuntu 18 server running in Alibaba cloud and it's throwing these errors from time to time… note...
Read more >
Invalid HTTP method in Traceback: Uvicorn - Stack Overflow
I was getting the same arcane WARNING: Invalid HTTP request received. error with an unhelpful stack trace. I tried all of the environment ......
Read more >
Invalid method encountered or duplicate "Content-Length ...
HttpParser.feed_data httptools.parser.errors.HttpParserInvalidMethodError: Invalid method encountered WARNING: Invalid HTTP request received.
Read more >
httptools - PyPI
httptools is a Python binding for the nodejs HTTP parser. ... bool: """Return ``True`` if the parsed request is a valid Upgrade request....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found