This is a tiny little note that can help with debugging in some situations.
We'll use netcat (nc
) to view the raw data sent to and returned from
an HTTP server1.
Capture a Request
We'll run a dummy listener via nc
and directly inspect the body of an
HTTP request. To run the listener on port 6426
via nc -l
(-l
for listen)
and in another terminal, use curl
to make a simple HTTP request
$ curl --max-time 1 http://localhost:6426
curl: (28) Operation timed out after 1005 milliseconds with 0 bytes received
In our other shell, we should see something like:
$ nc -l 6426
GET / HTTP/1.1
Host: localhost:6426
User-Agent: curl/7.64.1
Accept: */*
$
Let's write this to a file so we can keep it around later
$ nc -l 6426 > request.bin
$ hexdump -C request.bin
00000000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a |GET / HTTP/1.1..|
00000010 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a |Host: localhost:|
00000020 36 34 32 36 0d 0a 55 73 65 72 2d 41 67 65 6e 74 |6426..User-Agent|
00000030 3a 20 63 75 72 6c 2f 37 2e 36 34 2e 31 0d 0a 41 |: curl/7.64.1..A|
00000040 63 63 65 70 74 3a 20 2a 2f 2a 0d 0a 0d 0a |ccept: */*....|
0000004e
In particular note that the newline characters are 0d 0a
, i.e. \r\n
:
$ python
>>> b'\x0d\x0a'
b'\r\n'
Capture a Response
We'll use an Express server on port 6426
to capture an HTTP response:
const express = require("express");
function main(port) {
const app = express();
app.get("/", (_req, res) => {
res.send("Hello World!\n");
});
app.listen(port, () => {
console.log(`Listening on http://localhost:${port}`);
});
}
if (require.main === module) {
main(6426);
}
Running this via node index.js
, we can see what a "regular" curl
command
returns
$ curl http://localhost:6426
Hello World!
In order to see the raw TCP response instead of the parsed HTTP response, we can again use netcat:
$ cat request.bin | nc localhost 6426
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
ETag: W/"d-oLZZOWcLwsAQ9NXWoLPk5FkPuSs"
Date: Thu, 25 Feb 2021 04:47:12 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Hello World!
As with the request, we see familiar elements of an HTTP response other than the body. For example the first line has the status code and the next seven lines contain the headers.
- Note that HTTP/2 is a binary protocol so this only applies to HTTP/1.1. ↩