Computer Networks Module 1 – How the Internet works
What happens when you load a web page
Follow one real page load through DNS, TCP or QUIC, TLS 1.3 and HTTP, with measured timings that show why a first visit is slower than the next one.
What you will learn
- Trace one page load through DNS, a transport connection, TLS and HTTP using recorded command output
- Name the identifier used at each step (MAC address, IP address, port, domain name) and the layer that uses it
- Explain why a first visit is slower than a repeat visit, using measured phase timings
What you’ll learn
What you will learn
- Trace one page load through DNS, a transport connection, TLS and HTTP using recorded command output
- Name the identifier used at each step (MAC address, IP address, port, domain name) and the layer that uses it
- Explain why a first visit is slower than a repeat visit, using measured phase timings
On this page
When you open https://www.python.org/, the browser cannot just ask for the page. First it turns the name into an
IP address with the Domain Name System (DNS). Then it sends packets towards that address through your router, opens
a transport connection (TCP, or QUIC for HTTP/3), and runs a TLS 1.3 handshake to agree on encryption keys and check
the server’s certificate. Only then does the HTTP request leave. Almost every one of these steps has to wait for an
answer from the other side, so the time until the first byte of the page arrives is mostly a count of network round
trips multiplied by how far away the server is.
This lesson follows one real page load step by step, with each command’s output recorded on a real computer, and then measures why the second visit is so much faster than the first.
The steps at a glance
On a first visit over TCP, five things happen before the page starts to arrive. The diagram shows the messages of the steps that cross the network, and the list below it describes all five.
The four round trips of a first visit over TCP and TLS 1.3
Text description of the diagram
The diagram shows four boxes from top to bottom, one for each round trip, each holding a message and its answer.
- Round trip 1, DNS: the browser asks the resolver for the address of www.python.org, and the resolver answers with a CNAME that points to the content delivery network (CDN), then an A record with an IPv4 address.
- Round trip 2, the TCP handshake: the browser sends SYN to the server, and the server answers with SYN-ACK.
- Round trip 3, the TLS 1.3 handshake: the browser sends the final TCP ACK together with the TLS ClientHello, and the server answers with its ServerHello, its certificate and its Finished message.
- Round trip 4, the request: the browser sends its TLS Finished message with the HTTP request GET /, and the server answers with the HTTP 200 status line, the headers and the first bytes of the page.
- Resolve the name with DNS:
www.python.orgbecomes an IP address. That costs about one round trip to your resolver, and more when its cache has no answer. - Choose the first hop: the routing table says “send it to the router”, and ARP (IPv4) or Neighbor Discovery (IPv6) finds the router’s hardware address. Usually no round trip at all, because your computer already knows its router.
- Open a connection with TCP: SYN, SYN-ACK, ACK, one round trip.
- Agree on keys with TLS 1.3: ClientHello, then ServerHello to Finished, and the certificate is checked. One round trip; QUIC does steps 3 and 4 together, in one.
- Ask for the page with HTTP/2 or HTTP/3:
GET /, then the status line, headers and body. One round trip, plus the time the server takes.
Steps 1 to 5 cost about four round trips, and the request itself is only the last of them. The rest of the lesson looks at each step and then measures them.
Every layer has its own kind of address
Each step uses its own identifier, and each identifier is valid in a different place. Keeping them apart makes every later topic of this track easier.
- Domain name, such as
www.python.org: valid everywhere, since it is the name people type. DNS turns it into addresses, TLS checks it against the server’s certificate, and HTTP sends it so that one server can host many sites. - IP address, such as
167.82.24.223: valid from end to end, across every network on the path, although network address translation (NAT) may rewrite it on the way. Every router forwards a packet by its destination address. - Port, such as
443: valid on one host, where it picks the program, here the HTTPS server. TCP and UDP carry it. - MAC address, such as
00:00:5e:00:53:01, an address reserved for documentation: valid on one link only, such as your Wi-Fi network. Ethernet and Wi-Fi frames carry it.
The MAC address explains step 2. A frame that leaves your laptop carries the server’s IP address but the router’s MAC address, because the router is the only machine on your link that will pass it on. Your computer learns that MAC address with ARP for IPv4 (RFC 826) or Neighbor Discovery for IPv6 (RFC 4861), and keeps it in a cache. Each router then takes the packet out of the frame and puts it in a new frame for the next link, with new MAC addresses; the IP addresses inside stay the same.
Step 1: turning the name into addresses
DNS answers carry a time to live (TTL) in seconds, and every resolver along the way may keep the answer for that
long (RFC 1034). Here is the same question asked twice, five seconds
apart, of a public resolver at 8.8.8.8 with dig:
$ dig @8.8.8.8 www.python.org A +noall +answer +stats
; <<>> DiG 9.10.6 <<>> @8.8.8.8 www.python.org A +noall +answer +stats
; (1 server found)
;; global options: +cmd
www.python.org. 18576 IN CNAME dualstack.python.map.fastly.net.
dualstack.python.map.fastly.net. 60 IN A 167.82.24.223
;; Query time: 39 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; MSG SIZE rcvd: 104
$ dig @8.8.8.8 www.python.org A +noall +answer +stats
; <<>> DiG 9.10.6 <<>> @8.8.8.8 www.python.org A +noall +answer +stats
; (1 server found)
;; global options: +cmd
www.python.org. 18570 IN CNAME dualstack.python.map.fastly.net.
dualstack.python.map.fastly.net. 54 IN A 167.82.24.223
;; Query time: 16 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; MSG SIZE rcvd: 104 Three things are worth reading in that output:
- The name is an alias.
www.python.orgis aCNAME(a canonical-name record) for a name of the site’s content delivery network (CDN), and only that name has anArecord with an IPv4 address. Many large sites hand their traffic to a CDN this way. - The TTLs count down. The first answer gave the
Arecord a TTL of 60 seconds; five seconds later the same resolver answered with 54, so the second answer came from its cache, and the first had most likely just been fetched. - The cache was faster. The query time fell from 39 ms to 16 ms. A lookup that misses every cache can take much longer, because the resolver must ask other servers first; the DNS module of this track follows that path.
Your browser and your operating system keep caches of their own, so a name you used a moment ago is usually answered without asking the network at all.
About these recordings
Every recorded output in this lesson comes from one Mac (macOS 26.7.1 on Apple silicon) on a home fibre connection in India, made with dig 9.10.6, Homebrew curl 8.22.0 and OpenSSL 4.0.3. The lines that showed the date and time of a run are left out. Your numbers will be different, because they depend on where you are and on your network.
Steps 3 and 4: an encrypted connection
TCP opens a connection with a three-way handshake: the client sends SYN, the server replies SYN-ACK, and the client sends ACK (RFC 9293). That costs one round trip before any data can move. The client’s ACK can travel together with its first data, which for HTTPS is the TLS ClientHello.
TLS 1.3 then needs one more round trip (RFC 9846). The ClientHello already carries the client’s key share and the name of the site it wants (the server name indication), so the server can answer with its own key share, its certificate and its Finished message in one flight. After that reply the client checks the certificate, sends its Finished message and can send the HTTP request with it.
openssl s_client shows what a handshake agreed on:
$ openssl s_client -brief -connect www.python.org:443 -servername www.python.org < /dev/null
Connecting to 167.82.24.223
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_128_GCM_SHA256
Peer certificate: CN=www.python.org
Hash used: SHA256
Signature type: rsa_pss_rsae_sha256
Verification: OK
Negotiated TLS1.3 group: X25519MLKEM768
DONE
$ openssl s_client -brief -connect www.gnu.org:443 -servername www.gnu.org < /dev/null
Connecting to 209.51.188.116
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=wildebeest1p.gnu.org
Hash used: SHA256
Signature type: rsa_pss_rsae_sha256
Verification: OK
Peer Temp Key: X25519, 253 bits
DONE Both servers chose TLS 1.3 with AES in GCM mode, and both certificates verified. They differ in the key exchange.
www.gnu.org used X25519, a classical elliptic-curve exchange. The CDN in front of www.python.org used
X25519MLKEM768, which combines X25519 with ML-KEM-768, a post-quantum key encapsulation. The shared secret stays
safe as long as either of the two holds, which protects recorded traffic against a future quantum computer able to
break X25519 (RFC 10024). The number of round trips is the same
either way.
QUIC (RFC 9000) runs over UDP and builds the TLS 1.3 handshake into its own connection setup: where TCP and TLS take two round trips before the request can go, QUIC takes one. HTTP/3 is HTTP carried over QUIC (RFC 9114).
Step 5: the request, and who answers it
The request names the method and the resource (GET /) and the site, and the response starts with a status code
and headers (RFC 9110). A few of the response’s headers show who
actually answered:
$ curl -sI https://www.python.org/
HTTP/2 200
content-type: text/html; charset=utf-8
server: nginx
via: 1.1 varnish, 1.1 varnish, 1.1 varnish
age: 2244
x-served-by: cache-iad-kcgs7200042-IAD, cache-iad-kcgs7200042-IAD, cache-iad-kcgs7270046-IAD, cache-bom-vanm7210087-BOM
x-cache: MISS, HIT, HIT
x-cache-hits: 0, 4, 25
strict-transport-security: max-age=63072000; includeSubDomains; preload
content-length: 51995 x-served-bylists the cache servers that handled the response; in this CDN’s naming each entry ends with the code of its data centre (Fastly). The last one ends inBOM, its data centre in Mumbai (POP list), and the others inIAD, near Washington.x-cacheends withHIT: the cache that answered, the last one in the list, already had a copy and did not need to ask the origin server.age: 2244says the cached copy was about 37 minutes old (RFC 9111).
So the browser did not talk to the server where python.org’s pages are made. It talked to a CDN cache a few milliseconds away, which had a copy. That is normal for popular sites: a CDN edge, a load balancer or a reverse proxy usually ends your TCP and TLS connection, and the origin may be on another continent.
Measuring a real page load
curl can print when each phase of a request finished, counted in seconds from the start of the request
(curl manual): time_namelookup when the name was resolved, time_connect
when the TCP connection was open, time_appconnect when the TLS handshake was done (for HTTP/3, when the QUIC
connection with its built-in TLS was ready), and time_starttransfer when the first byte arrived. num_connects
says how many new connections the request needed. The program below reads a recorded session of six requests and
turns those moments into phases:
"""Turns curl's recorded timings into the phases of a page load, then counts round trips.
Each curl -w variable is a moment, measured from the start of the transfer:
namelookup the name has been resolved to an address (DNS)
connect the TCP connection is open
appconnect the TLS handshake is finished (for HTTP/3: the QUIC connection, which includes TLS, is ready)
starttransfer the first byte of the response has arrived
The input is a recorded session: each "$ curl ..." line is followed by the line(s) it printed.
"""
import sys
runs = []
for line in sys.stdin:
line = line.strip()
if line.startswith("$ curl"):
host = line.split()[-1].split("/")[2] # https://www.python.org/ -> www.python.org
http3 = "--http3-only" in line
elif line.startswith("http="):
fields = dict(field.split("=") for field in line.split())
t = {name: float(value) * 1000 for name, value in fields.items() if name != "http"}
reused = fields["new"] == "0" # curl opened no new connection for this request
runs.append({
"run": host + (", same connection" if reused else ", HTTP/3" if http3 else ""),
"tcp": not reused and not http3,
"dns": t["namelookup"],
"handshakes": t["appconnect"] - t["namelookup"] if not reused else 0.0,
"tcp_handshake": t["connect"] - t["namelookup"],
"tls_handshake": t["appconnect"] - t["connect"],
"first_byte": t["starttransfer"] - t["appconnect"],
"total": t["total"],
})
print(f"{'run (milliseconds)':32} {'dns':>6} {'handshakes':>11} {'to 1st byte':>12} {'total':>8}")
for r in runs:
print(f"{r['run']:32} {r['dns']:6.1f} {r['handshakes']:11.1f} {r['first_byte']:12.1f} {r['total']:8.1f}")
print()
print("A TCP handshake is one round trip, so connect - namelookup estimates the round-trip time (RTT).")
print(f"{'run over TCP':32} {'RTT ms':>7} {'TLS in RTTs':>12} {'to 1st byte in RTTs':>20}")
for r in runs:
if r["tcp"]:
rtt = r["tcp_handshake"]
print(f"{r['run']:32} {rtt:7.1f} {r['tls_handshake'] / rtt:12.1f} {r['first_byte'] / rtt:20.1f}") Input (standard input)
$ W='http=%{http_version} new=%{num_connects} namelookup=%{time_namelookup} connect=%{time_connect} appconnect=%{time_appconnect} starttransfer=%{time_starttransfer} total=%{time_total}\n'
$ curl -s -o /dev/null -w "$W" https://www.python.org/
http=2 new=1 namelookup=0.003606 connect=0.014310 appconnect=0.032947 starttransfer=0.051835 total=0.064813
$ curl -s -o /dev/null -w "$W" https://www.python.org/
http=2 new=1 namelookup=0.003525 connect=0.012868 appconnect=0.026678 starttransfer=0.043239 total=0.054140
$ curl -s -o /dev/null -o /dev/null -w "$W" https://www.python.org/ https://www.python.org/
http=2 new=1 namelookup=0.003212 connect=0.014600 appconnect=0.030988 starttransfer=0.048812 total=0.061206
http=2 new=0 namelookup=0.000000 connect=0.000000 appconnect=0.000000 starttransfer=0.011838 total=0.012697
$ curl --http3-only -s -o /dev/null -w "$W" https://www.python.org/
http=3 new=1 namelookup=0.003159 connect=0.015318 appconnect=0.020182 starttransfer=0.035968 total=0.054281
$ curl -s -o /dev/null -w "$W" https://www.gnu.org/
http=1.1 new=1 namelookup=0.006509 connect=0.247481 appconnect=0.500135 starttransfer=0.758298 total=1.225121
Output
run (milliseconds) dns handshakes to 1st byte total www.python.org 3.6 29.3 18.9 64.8 www.python.org 3.5 23.2 16.6 54.1 www.python.org 3.2 27.8 17.8 61.2 www.python.org, same connection 0.0 0.0 11.8 12.7 www.python.org, HTTP/3 3.2 17.0 15.8 54.3 www.gnu.org 6.5 493.6 258.2 1225.1 A TCP handshake is one round trip, so connect - namelookup estimates the round-trip time (RTT). run over TCP RTT ms TLS in RTTs to 1st byte in RTTs www.python.org 10.7 1.7 1.8 www.python.org 9.3 1.5 1.8 www.python.org 11.4 1.4 1.6 www.gnu.org 241.0 1.0 1.1
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 round_trips.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Read the two tables together:
- A nearby edge. For
www.python.orgthe TCP handshake took 9 to 11 ms, so the CDN’s edge is about one 10 ms round trip away. The TLS handshake took 1.4 to 1.7 round trips, most likely because computing the keys and sending the certificate add a few milliseconds, which shows when the distance is this short. - The same connection again. When curl sent a second request on the connection it had just opened, the name
lookup and both handshakes cost nothing (
new=0), and the first byte came back in 11.8 ms, about one round trip. - HTTP/3. The QUIC connection, with TLS built in, was ready 17.0 ms after the name lookup, against 23 to 29 ms for TCP and TLS: about one round trip saved.
- A distant server.
www.gnu.organswered from a server 241 ms away. Here the TLS handshake took almost exactly one round trip and the first byte one more, so the first byte arrived 758 ms after the start. The whole page took 1.2 seconds, because sending a larger body takes more round trips, as the TCP module explains.
The pattern is the same everywhere: when the server is close, computing and the server’s own work show up; when it is far, the number of round trips decides almost everything.
A model: round trips decide the first byte
That pattern can be written as a formula. With the DNS answer cached, the first byte arrives after the handshake round trips, plus one round trip for the request, plus the server’s own time. This model compares four kinds of connection at three distances:
"""A model of the time to first byte (TTFB) of an HTTPS request whose DNS answer is already cached.
Every handshake costs whole round trips before the request can leave, then the request and
the start of the response cost one more round trip, plus the time the server needs to answer.
"""
SERVER_MS = 20 # an assumption: the server needs 20 ms to start its response
HANDSHAKE_ROUND_TRIPS = {
"TCP + TLS 1.2": 3, # TCP's handshake, then TLS 1.2's full handshake of two round trips
"TCP + TLS 1.3": 2, # TCP's handshake, then TLS 1.3's one round trip
"QUIC, new connection": 1, # QUIC sets up the transport and TLS 1.3 together
"QUIC, 0-RTT resumption": 0, # the request travels in the very first flight
}
RTTS_MS = (30, 85, 250) # a nearby edge, another region, another continent
def ttfb(handshakes, rtt):
return (handshakes + 1) * rtt + SERVER_MS
print(f"{'TTFB (ms) at a round-trip time of:':34}" + "".join(f"{f'{rtt} ms':>9}" for rtt in RTTS_MS))
for setup, handshakes in HANDSHAKE_ROUND_TRIPS.items():
print(f"{setup:34}" + "".join(f"{ttfb(handshakes, rtt):>9}" for rtt in RTTS_MS))
tls13 = HANDSHAKE_ROUND_TRIPS["TCP + TLS 1.3"]
quic = HANDSHAKE_ROUND_TRIPS["QUIC, new connection"]
print()
print(f"On the 250 ms path, QUIC instead of TCP + TLS 1.3 saves {ttfb(tls13, 250) - ttfb(quic, 250)} ms.")
print(f"Serving from a 30 ms edge instead saves {ttfb(tls13, 250) - ttfb(tls13, 30)} ms, with no protocol change.") Output
TTFB (ms) at a round-trip time of: 30 ms 85 ms 250 ms TCP + TLS 1.2 140 360 1020 TCP + TLS 1.3 110 275 770 QUIC, new connection 80 190 520 QUIC, 0-RTT resumption 50 105 270 On the 250 ms path, QUIC instead of TCP + TLS 1.3 saves 250 ms. Serving from a 30 ms edge instead saves 660 ms, with no protocol change.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 ttfb_model.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Two results stand out. Each handshake round trip is worth a lot on a long path: on a 250 ms path, QUIC’s single handshake saves 250 ms. But moving the server closer saves more than any protocol change, which is why popular sites pay for CDNs.
The last row needs care. With 0-RTT resumption, a client that has talked to the server before sends its
request in the very first flight, encrypted with a key from the earlier connection. TLS cannot stop an attacker
from replaying such early data, so a client must only send early data that is safe to repeat
(RFC 9846, section 8), and a server can refuse an
early request with the status 425 Too Early (RFC 8470). Fetching a
page is fine; placing an order is not.
Why the second visit is faster
A repeat visit skips work at almost every step:
- DNS answers are cached by the browser, the operating system and the resolver until their TTL runs out.
- Connections are reused. HTTP/1.1 keeps a connection open for further requests
(RFC 9112), and HTTP/2 and HTTP/3 send many requests over one
connection at the same time, so the handshakes are paid once per site, not once per file.
The
same connectionrow above shows the saving. - TLS sessions resume. After a handshake, the server can give the client a ticket for a later connection, which shortens the next handshake and makes 0-RTT possible.
- HTTP caching can remove the request entirely. A response with
Cache-Control: max-age=3600may be reused for an hour without asking the server; after that, the browser can ask whether its copy is still valid and receive a short304 Not Modifiedinstead of the whole file (RFC 9111).
Your browser’s developer tools show the same phases for every request of a page, and can save them as a HAR file.
HAR File Analyzer & Sanitizer Load a HAR file saved from your browser's developer tools to see the DNS, connection, TLS and waiting time of every request. DNS Lookup Look up the A, AAAA and CNAME records of any site and see whether it sits behind a CDN. SSL Certificate Checker See which certificate a site presents in its TLS handshake and when it expires.Interview questions
Fresher · Name the steps between entering an HTTPS address in a browser and receiving the first byte of the page.
The browser resolves the domain name to an IP address with DNS, usually from a cache. It sends its packets to the default router, whose hardware address it knows from ARP or Neighbor Discovery. It opens a TCP connection with a three-way handshake (one round trip), then runs the TLS 1.3 handshake (one more round trip), checking that the server’s certificate matches the name. Only then does it send the HTTP request, and the first byte arrives about one round trip later, plus the server’s time. With HTTP/3, QUIC combines the transport and TLS handshakes into one round trip. Mention that the server answering is often a CDN edge rather than the origin.
Key takeaways
- A first HTTPS visit over TCP costs about four round trips before the first byte: DNS, TCP, TLS and the request. QUIC removes one, and 0-RTT resumption one more, for requests that are safe to repeat.
- Each layer has its own address: the domain name for people, the IP address from end to end, the port for the program and the MAC address for one link only.
- Distance multiplies every round trip. In the recordings, the same steps took 52 ms to a CDN edge about 10 ms away and 758 ms to an origin 241 ms away.
- Repeat visits are faster because DNS answers, connections, TLS sessions and responses are all cached or reused.
- The server you reach is often a CDN edge or a proxy; headers such as
age,x-cacheorviatell you so.
Exercise
Exercise · Easy · Python
Split curl's timings into the phases of a page load
The lesson's curl command prints one line per request, such as:
http=2 new=1 namelookup=0.003606 connect=0.014310 appconnect=0.032947 starttransfer=0.051835 total=0.064813
Each value is a moment in seconds, counted from the start of the request. Write two functions in phases.py.
phases(line) returns how long each phase took, in milliseconds, as a dictionary with four keys:
"dns": from the start until the name was resolved (namelookup);"connect": from then until the transport connection was open (connect);"tls": from then until the TLS handshake was done (appconnect);"wait": from then until the first byte of the response arrived (starttransfer).
Two cases need care. A plain http:// request has no TLS, and curl prints appconnect=0.000000: its "tls" phase is 0 and its wait starts when the connection opens. A request on a reused connection prints zeros for the first three moments, so only its wait is more than 0.
dominant(durations) takes such a dictionary and returns the key of the longest phase, for example "wait".
The sample tests use lines recorded in the lesson and accept answers within 1 ms.
Starter code · phases.py
def phases(line):
"""Split one line printed by curl -w into phase durations in milliseconds.
Returns a dictionary with the keys "dns", "connect", "tls" and "wait".
"""
# Replace this line with your code.
return {"dns": 0.0, "connect": 0.0, "tls": 0.0, "wait": 0.0}
def dominant(durations):
"""The key of the longest phase, such as "wait"."""
# Replace this line with your code.
return "" The sample tests · test_phases.py
import math
from phases import dominant, phases
FIRST_VISIT = "http=2 new=1 namelookup=0.003606 connect=0.014310 appconnect=0.032947 starttransfer=0.051835 total=0.064813"
FAR_ORIGIN = "http=1.1 new=1 namelookup=0.006509 connect=0.247481 appconnect=0.500135 starttransfer=0.758298 total=1.225121"
SLOW_SERVER = "http=2 new=1 namelookup=0.028254 connect=0.036660 appconnect=0.054341 starttransfer=0.349055 total=0.351304"
PLAIN_HTTP = "http=1.1 new=1 namelookup=0.018120 connect=0.257654 appconnect=0.000000 starttransfer=0.515179 total=0.758801"
REUSED = "http=2 new=0 namelookup=0.000000 connect=0.000000 appconnect=0.000000 starttransfer=0.011838 total=0.012697"
def close(actual, expected):
"""True when every phase is within 1 ms of the expected value."""
return set(actual) == set(expected) and all(math.isclose(actual[k], expected[k], abs_tol=1.0) for k in expected)
def test_first_visit():
"""splits a first visit to a nearby CDN edge"""
assert close(phases(FIRST_VISIT), {"dns": 3.6, "connect": 10.7, "tls": 18.6, "wait": 18.9})
def test_far_origin():
"""splits a visit to a server on another continent"""
assert close(phases(FAR_ORIGIN), {"dns": 6.5, "connect": 241.0, "tls": 252.7, "wait": 258.2})
def test_slow_server():
"""splits a request whose server took almost 0.3 s to answer"""
assert close(phases(SLOW_SERVER), {"dns": 28.3, "connect": 8.4, "tls": 17.7, "wait": 294.7})
def test_plain_http():
"""gives a plain http:// request no TLS phase"""
assert close(phases(PLAIN_HTTP), {"dns": 18.1, "connect": 239.5, "tls": 0.0, "wait": 257.5})
def test_reused_connection():
"""gives a reused connection only a wait"""
assert close(phases(REUSED), {"dns": 0.0, "connect": 0.0, "tls": 0.0, "wait": 11.8})
def test_dominant():
"""names the longest phase"""
assert dominant({"dns": 412.0, "connect": 19.0, "tls": 21.0, "wait": 18.0}) == "dns"
assert dominant(phases(SLOW_SERVER)) == "wait" A hint
Split the line on spaces, then each field on =: name, value = field.split("="). Multiply each value by 1,000 to get milliseconds (skip the http field, which is a version, not a time). Each phase is the difference between two moments, such as connect - namelookup. For dominant, max(durations, key=durations.get) returns the key with the largest value.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Check yourself
5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
References
- RFC 9110: HTTP Semantics (RFC Editor (IETF))
- RFC 9111: HTTP Caching (RFC Editor (IETF))
- RFC 9112: HTTP/1.1 (RFC Editor (IETF))
- RFC 9293: Transmission Control Protocol (TCP) (RFC Editor (IETF))
- RFC 9846: The Transport Layer Security (TLS) Protocol Version 1.3 (RFC Editor (IETF))
- RFC 10024: Post-Quantum Traditional (PQ/T) Hybrid Key Agreement Mechanisms for TLS 1.3 (RFC Editor (IETF))
- RFC 9000: QUIC, a UDP-Based Multiplexed and Secure Transport (RFC Editor (IETF))
- RFC 9114: HTTP/3 (RFC Editor (IETF))
- RFC 8470: Using Early Data in HTTP (RFC Editor (IETF))
- RFC 1034: Domain Names, Concepts and Facilities (RFC Editor (IETF))
- RFC 826: An Ethernet Address Resolution Protocol (RFC Editor (IETF))
- RFC 4861: Neighbor Discovery for IP version 6 (IPv6) (RFC Editor (IETF))
- curl manual page (--write-out variables) (curl project)
- openssl-s_client manual page (OpenSSL Project)
- X-Served-By (HTTP header reference) (Fastly)
- Points of presence (POP list) (Fastly)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress