Web Client
Everything that asks a server for something.
Chrome is a web client, and so is a single line of curl. In this chapter you type an HTTP request by hand, wrap it into a shell script, learn curl properly, and then open Chrome's developer tools to watch the very same messages fly past.
A client is whatever asks first
Client and server are roles, not machines. Whichever side opens the conversation and asks for something is the client; the side that waits and answers is the server. Your browser is a client, so is curl, so is a phone app, so is another server calling an API.
That is why you can write a web client in one line. It does not need a window, a rendering engine, or a mouse — it needs to connect, send a request, and read what comes back.
| Question | Client | Server |
|---|---|---|
| Who speaks first | Opens the connection and sends the request. | Waits on a port, silent until someone connects. |
| Who needs an address | Needs to know where the server is — a domain or an IP, and a port. | Needs a stable address, which is why servers get domain names and clients usually do not. |
| How many at once | Usually one conversation at a time, though a browser opens several to load a page faster. | Many at once, which is most of what makes real server software complicated. |
A web server that fetches a map or charges a card is acting as a client for that request. Roles swap by the second, so ask which direction a particular request travels rather than what kind of machine is at each end.
Making a web client in the terminal
The quickest way to believe that HTTP is only text is to type a request yourself. Start the shell server from the previous topic, then open a second terminal and talk to it by hand.
Typing a request by hand with nc
netcat connects to a port and passes your keystrokes straight down the wire. Type the two lines, press Enter twice, and the server answers — you have just done a browser's job manually.
# connect, then type the two lines below and press Enter once more
nc localhost 8084
GET / HTTP/1.1
Host: localhost
# or send it all at once (printf's \r\n is HTTP's line break)
printf 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc localhost 8084The blank line at the end matters. It tells the server the request is finished, which is why you press Enter one extra time. HTTP requires carriage-return-and-newline at the end of each line. Typing works because most servers are forgiving, but the printf version sends exactly what the standard asks for. The Host line is required by HTTP/1.1. Leave it out against a real site and you will usually get an error back rather than a page.
Wrapping it into a script
Once the request is a single printf, a reusable client is a few lines. This one takes a host, a port and a path, sends the request, and splits the headers from the body so you can see the two parts.
#!/bin/bash
# usage: bash client.sh localhost 8084 /
HOST=${1:-localhost}
PORT=${2:-8084}
PATH_=${3:-/}
printf 'GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' "$PATH_" "$HOST" \
| nc "$HOST" "$PORT" \
| tr -d '\r' \
| awk 'BODY { print; next } /^$/ { BODY = 1; next } { print "[header] " $0 }'The tr command strips the carriage returns that HTTP leaves at the end of every line, and awk marks each line as a header until it meets the empty line — after that, everything is the body. This is exactly the split a browser does before it starts drawing.
Start the shell server in one terminal and this client in another, and you own both ends of a conversation you wrote yourself. Change the response in the server and the client sees it immediately — there is nothing else in between.
curl: the client you will use every day
curl does properly what our script does roughly: redirects, HTTPS, headers, uploads, timeouts, cookies. It is on almost every machine, and it is the standard way to test a server without a browser in the way.
# body only
curl http://localhost:8084
# body plus the response headers
curl -i http://localhost:8084
# the whole exchange, request headers included
curl -v http://localhost:8084The options that matter
| Option | What it does |
|---|---|
| -i | Show the response headers as well as the body. The first thing to reach for when a page is not what you expected. |
| -v | Verbose: show the request you sent and the response you got, line by line, including the connection itself. |
| -L | Follow redirects. Without it, a 301 gives you an almost empty response instead of the page you wanted. |
| -o FILE | Save the response to a file instead of printing it. This is curl as a download tool. |
| -H "Name: value" | Add a request header — a content type, an authorisation token, a language preference. |
| -X POST -d "data" | Send data instead of just asking. Adding data implies POST, and this is how you test a form or an API from the terminal. |
| -s | Silent: no progress meter. Use it whenever the output feeds into another command or a script. |
| -A "name" | Pretend to be a particular browser. Some sites answer differently depending on who is asking. |
Calling an API
Most of what a modern app talks to is not a page but an API returning JSON. curl fetches it, and jq makes it readable — together they are how you check an endpoint before writing a single line of application code.
# fetch JSON and pick one field out of it with jq
curl -s https://api.github.com/repos/sveltejs/svelte | jq '.stargazers_count'
# send JSON with POST
curl -X POST https://httpbin.org/post \
-H 'Content-Type: application/json' \
-d '{"name":"PES","level":1}'
# download to a file
curl -L -o page.html https://getpes.comGet into the habit of testing with curl first. If curl gets the right answer, the server is fine and the bug is in your application; if curl gets the wrong answer, you have saved yourself an afternoon of looking in the wrong place.
Chrome is the same client, at a different scale
Chrome sends the request your nc command sent. Then it does the enormous amount of extra work that turns the answer into something a person can look at — and that extra work is the only real difference.
It is worth knowing the steps, because every one of them is a place where a page can go wrong. When something does not appear, the question is always which step failed.
From address bar to picture
- Resolve the name The domain name is turned into an IP address by DNS. If this fails you never reach the server at all, and the error mentions the name rather than the page.
- Connect and secure It opens a connection and, for https, checks the certificate before anything else is sent. A warning here means the encryption could not be trusted, not that the page is missing.
- Send the request The same handful of lines you typed by hand, with rather more headers: cookies, accepted formats, preferred languages.
- Parse and fetch the rest It reads the HTML, builds a tree of elements from your tags, and requests everything the page refers to — stylesheets, images, fonts, scripts — usually several at once.
- Style, lay out, paint It applies the CSS, works out where every box goes, draws the result, and runs the JavaScript that can change all of it again afterwards.
Terminal client or browser
Chrome, Edge, Brave and Opera are all built on Chromium, so a page that works in one usually works in the others. Safari and Firefox use different engines, which is exactly why a site should be opened in at least two of them before you call it finished.
DevTools: watching the messages go by
Chrome ships with the best web debugging tool there is, and it is already installed. Everything you sent by hand with nc is listed here, request by request, with the response beside it.
Press F12, or Command-Option-I on a Mac and Control-Shift-I elsewhere. Right-clicking any part of a page and choosing Inspect opens it with that element already selected — the fastest way in.
The panels
Three things to try right now
- Find your own request Open the Network panel, reload this page, and click the very first entry. The request and response headers there are the same lines you typed into nc.
- Copy it as curl Right-click any request and choose Copy as cURL. Paste it into a terminal and you have reproduced exactly what the browser did — headers, cookies and all.
- Break it on purpose In the Elements panel, delete a heading and watch it vanish; in Network, throttle the speed to slow and reload. Both are safe, and both teach more than reading about them.
Open the HTML file you wrote in the first topic, inspect it, and you will recognise every tag in the tree. That loop — write it, serve it, request it, inspect it — is the whole of web development in miniature.
You mark this yourself. Nothing is graded here.