DEV Community

Raknaos
Raknaos

Posted on

My HTTP timing tool could not even start on Python 3.11

The first version of a small HTTP timing tool failed before it could measure a request.

That sounds like an ordinary syntax mistake, but it exposed exactly why I run a tool locally before I write about it. The README showed a useful command and a self-test. The repository looked like a tiny, dependency-free utility. On this machine, the advertised self-test did not reach the local HTTP server at all: Python stopped while parsing the file.

The failure was in the renderer, not in DNS, TCP, TLS, or HTTP. One f-string used a backslash escape inside an expression:

lines.append(f"  {'total':>10s}  {'\\u2588' * width}  {total*1000:7.1f}ms")
Enter fullscreen mode Exit fullscreen mode

Python 3.11 reports SyntaxError: f-string expression part cannot include a backslash. The README says the project requires Python 3.7+, so this is not a theoretical compatibility edge case. It is a startup failure on a supported interpreter.

That was the problem I wanted to solve in the first place: when an HTTP request is slow, “the server is slow” is not a diagnosis. The delay may be name resolution, the TCP handshake, TLS, waiting for the first response byte, or downloading the response. I wanted a small command that made those phases visible without installing a package or remembering a larger profiling stack.

The design is deliberately small

httpstat-lite is a standalone Python script. The repository contains httpstat_lite.py, and its implementation uses the standard library: argparse, socket, ssl, time, and urllib.parse, with the self-test importing http.server and threading when needed.

The core measurement function records a wall-clock start, resolves the host with socket.getaddrinfo, opens an IPv4 TCP socket, optionally wraps it with the default TLS context, sends a minimal HTTP/1.1 GET request, and measures the first received byte as TTFB. It then reads until the socket closes and records the download and total durations.

That decomposition is useful because each number answers a different question. DNS tells me whether resolving the host is expensive. Connect tells me whether reaching the address is expensive. TLS separates the secure handshake from the TCP connection. TTFB includes the wait after sending the request until the first byte arrives. Download measures the remaining read. Total is the complete elapsed time recorded by the script.

The request itself is intentionally direct:

req = f"GET {path} HTTP/1.1\\r\\nHost: {host}\\r\\nConnection: close\\r\\n\\r\\n"
sock.sendall(req.encode())
sock.recv(1)  # first byte
Enter fullscreen mode Exit fullscreen mode

There is no HTTP client dependency and no attempt to become a general-purpose HTTP implementation. The script is a timing probe, not a browser and not a response validator.

The output is an ASCII chart. Each phase receives a bar proportional to its share of the measured total, followed by milliseconds. A successful run is intended to look like this:

         dns  ████   12.3ms
     connect  ██████   25.1ms
         tls  ████   15.0ms
        ttfb  ██████████████████   80.2ms
    download  ███   10.5ms
       total  ████████████████████████████████████████  143.1ms
Enter fullscreen mode Exit fullscreen mode

The normal command is one line:

python3 httpstat_lite.py https://example.com
Enter fullscreen mode Exit fullscreen mode

The repository also defines an offline self-test. It starts a local http.server, measures a loopback URL, checks that DNS, connect, TTFB, download, and total timings exist, renders the chart, and prints self-test: PASS when those assertions succeed. Running that command is part of my verification routine, not a claim I can replace with a README reading.

On the checked-out source, however, the self-test currently fails earlier with the Python 3.11 syntax error shown above. That is the honest state of this revision. The measurement approach is easy to inspect, but the advertised compatibility claim and the executable source disagree until the f-string is rewritten.

The fix is smaller than the diagnosis

The problematic expression does not need to build the bar inside the f-string. The renderer already does this correctly for every other phase:

bar = "\\u2588" * bar_len
lines.append(f"  {phase:>10s}  {bar}  {ms:7.1f}ms")
Enter fullscreen mode Exit fullscreen mode

The total line should follow the same rule: create the bar before formatting the string, or use a literal character outside the expression. That keeps the code compatible with the stated Python 3.7+ requirement and makes the line easier to read.

This is also why I prefer a local self-test over a green-looking interface. A small CLI can have a simple design and still fail at the first byte of execution. In this case, the failure is not hidden in a rare network path; it is visible to the parser before any socket is opened.

What it does not do

This tool does not measure every possible HTTP detail. It uses IPv4 address resolution and the first address returned by getaddrinfo. It sends a GET request and reads until the connection closes. It does not expose response headers, status-code validation, redirects, HTTP/2, HTTP/3, proxy configuration, or a full response body report. Its TTFB number is the elapsed time until one byte is received, not a server-side processing measurement.

It also cannot tell me why a phase is slow. It shows where the time was observed. I still need logs, repeated samples, and knowledge of the network path to explain the cause.

The cost is the standard library already present in Python. There is no package installation and no network dependency for the self-test once the syntax issue is fixed. The normal URL measurement, of course, contacts the URL I provide.

Try it

The source, MIT license, usage example, and self-test are in the repository:

https://github.com/Raknaos/httpstat-lite

Download the script and run:

python3 httpstat_lite.py --self-test
python3 httpstat_lite.py https://example.com
Enter fullscreen mode Exit fullscreen mode

Run the first command on the exact revision you intend to use. That one habit caught this bug before the timing chart could pretend to be evidence.

Top comments (1)

Collapse
 
tidiane_stano_c6b88f8b685 profile image
Tidiane Stano

do you wanna try my website,api gateway,i wanna improve it.Can you give some advices to me?thank you