cadlag dot org

Looking at an HTTP/1.1 Session

Our recent foray into TCP/IP led us to look at HTTP as a prime example of an application layer protocol. Today I thought it would be fun to look at an HTTP request/response interaction with Wireshark. Let’s break it down step by step and see exactly how HTTP/1.1 plays out over TCP.

When I run:

1curl -v http://google.com -H "Connection: close"

I see the following:

* Host google.com:80 was resolved.
* IPv6: (none)
* IPv4: 142.250.72.174
*   Trying 142.250.72.174:80...
* Connected to google.com (142.250.72.174) port 80
> GET / HTTP/1.1
> Host: google.com
> User-Agent: curl/8.7.1
> Accept: */*
> Connection: close
> 
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Location: http://www.google.com/
< Content-Type: text/html; charset=UTF-8
< Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-EB7GxjMVJsa5Mr_7PWB0yA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
< Date: Wed, 12 Feb 2025 01:43:25 GMT
< Expires: Fri, 14 Mar 2025 01:43:25 GMT
< Cache-Control: public, max-age=2592000
< Server: gws
< Content-Length: 219
< X-XSS-Protection: 0
< X-Frame-Options: SAMEORIGIN
< Connection: close
< 
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
* Closing connection

Looking at Wireshark, I see the following capture:

Wireshark Trace

The first few segments set up the TCP connection. You can see the “three-way handshake” in action:

TCP Connection Establishment

With the TCP connection established we make the HTTP GET request. The server acknowledges this before sending the response:

HTTP Get

In principle, the server could bundle the above ACK with the actual HTTP response, but doing so would introduce a delay if the response isn’t ready immediately. So it plays it safe: first, it sends an ACK, then it responds to the request.

The next segment has the actual HTTP response, which we acknowledge:

HTTP Response

Finally, the TCP connection is torn down. I ran this experiment twice, and the next screenshot is from the second run, but the behavior and relative sequence numbers are the same. I was expecting to see a three-way handshake, but there are four segments here:

TCP Teardown

Recall that I asked curl to use the Connection: close header in my request. This tells Google that it is free to close the connection once it has served a response. What we see in the above screenshot is that both sides of the TCP connection initiate a close simultaneously. Because of this, each FIN is sent before receiving the other’s. Since a FIN gets its own sequence number, it requires a separate ACK. So we need an extra acknowledgment to fully close this connection.

The takeaway here is that we can see HTTP riding on TCP just by sniffing packets. This gives a solid sanity check for what we have already learned about these protocols.

Reply to this post by email ↪