Internet Engineering
02
Introduction · Cookie · Proxy & Cache · Authentication
Fall 2026 ·
Amirkabir University of Technology
@1995parham
every URL is also a URI, but there are URIs which are not URLs.
<protocol(scheme)> :// <user> : <pass> @ <host> : < port> / <path> ? <query> # <frag>
/var/www/http://www.example.com/1.html →
/var/www/1.htmlhttp://www.example.com/1/2/3.jpg →
/var/www/1/2/3.jpghttp://www.example.com/1/2/../3.jpg
→ /var/www/1/3.jpg 🤨name=value formatA simple HTTP Request & Response Service.
This may be case sensitive or not.
~ → 126 → %7Ehttps://ganj.irandoc.ac.ir/api/v1/search/main?keywords=hellow%20world
# ce.aut.ac.ir resolves to 185.211.88.129
# connectify0 is the network interface
sudo tcpdump --interface connectify0 --number -n -v 'port 80 and dst host 185.211.88.129'
1 07:47:41.159469 IP (tos 0x0, ttl 64, id 59255, offset 0, flags [DF], proto TCP (6), length 60)
10.202.0.2.55340 > 185.211.88.129.80: Flags [S], cksum 0x5a35 (correct), seq 440330126, win 59040, options [mss 14760,sackOK,TS val 954835682 ecr 0,nop,wscale 7], length 0
2 07:47:41.159904 IP (tos 0x0, ttl 64, id 59256, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xf7e0 (correct), ack 35014, win 462, options [nop,nop,TS val 954835682 ecr 15081515], length 0
3 07:47:41.160032 IP (tos 0x0, ttl 64, id 59257, offset 0, flags [DF], proto TCP (6), length 137)
10.202.0.2.55340 > 185.211.88.129.80: Flags [P.], cksum 0x282f (correct), seq 0:85, ack 1, win 462, options [nop,nop,TS val 954835682 ecr 15081515], length 85: HTTP, length: 85
GET /~students/ HTTP/1.1
Host: ce.aut.ac.ir
User-Agent: curl/8.3.0
Accept: */*
4 07:47:41.218732 IP (tos 0x0, ttl 64, id 59258, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xe649 (correct), ack 4334, win 429, options [nop,nop,TS val 954835741 ecr 15081574], length 0
5 07:47:41.219358 IP (tos 0x0, ttl 64, id 59259, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xd5c5 (correct), ack 8592, win 397, options [nop,nop,TS val 954835742 ecr 15081575], length 0
6 07:47:41.224808 IP (tos 0x0, ttl 64, id 59260, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xcb33 (correct), ack 11288, win 397, options [nop,nop,TS val 954835747 ecr 15081580], length 0
7 07:47:41.226004 IP (tos 0x0, ttl 64, id 59261, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xc0a9 (correct), ack 13984, win 397, options [nop,nop,TS val 954835748 ecr 15081581], length 0
8 07:47:41.226548 IP (tos 0x0, ttl 64, id 59262, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xb61f (correct), ack 16680, win 397, options [nop,nop,TS val 954835749 ecr 15081582], length 0
9 07:47:41.227124 IP (tos 0x0, ttl 64, id 59263, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xb332 (correct), ack 17427, win 397, options [nop,nop,TS val 954835750 ecr 15081583], length 0
10 07:47:41.227371 IP (tos 0x0, ttl 64, id 59264, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [F.], cksum 0xb331 (correct), seq 85, ack 17427, win 397, options [nop,nop,TS val 954835750 ecr 15081583], length 0
11 07:47:41.227485 IP (tos 0x0, ttl 64, id 59265, offset 0, flags [DF], proto TCP (6), length 52)
10.202.0.2.55340 > 185.211.88.129.80: Flags [.], cksum 0xb330 (correct), ack 17428, win 397, options [nop,nop,TS val 954835750 ecr 15081583], length 0



GET /index.html HTTP/1.1
Host: www.aut.ac.ir
User-Agent: Mozilla/36.0
Accept-Language: en-us
Connection: keep-alive
Method<sp>Path<sp>Version<CRLF>
Header-Field:Header-Value<CRLF>
...
Header-Field:Header-Value<CRLF>
<CRLF>
Entity-Body
HTTP/1.1 200 OK
Date: Sun, 02 Oct 2018 20:30:40
Server: Apache/2.2.2
Last-Modified: Mon, 03 May 2017 10:20:22
Connection: keep-alive
Content-Length: 3000
data data data ...
Version<sp>Code<sp>Reason<CRLF>
Header-Field:Header-Value<CRLF>
...
Header-Field:Header-Value<CRLF>
<CRLF>
Entity-Body
GET, HEAD,
OPTIONS,
TRACEPOST is
not safe, even though it only
sends data
PUT and
DELETEPOST and
PATCH are notPOST — that is why you see do not press refreshon payment pages
GET that deletes something will be deleted by a crawlerA redirect says look over there
. The harder question is what the
client sends when it gets there — the same method and body, or a fresh
GET?
| Code | Lifetime | Method on the second request |
|---|---|---|
| 301 Moved Permanently | permanent | in practice becomes GET |
| 302 Found | temporary | in practice becomes GET |
| 303 See Other | temporary | always GET, by specification |
| 307 Temporary Redirect | temporary | preserved, body and all |
| 308 Permanent Redirect | permanent | preserved, body and all |
POST into
GET anyway, silently — and the web was
written against the browsers, not the documentfor historical reasons, a user agent MAY change the request method
301 and 302 are the ambiguous ones. After a POST, never use them — say what you mean with 303 or 307.

POST, the address bar still holds the URL you
posted to
POST /orders HTTP/1.1
Host: shop.example
Content-Type: application/x-www-form-urlencoded
item=book&qty=1
HTTP/1.1 303 See Other
Location: /orders/1042
GET /orders/1042 HTTP/1.1
Host: shop.example
POST /api/v1/orders HTTP/1.1
Host: api.example
Content-Type: application/json
{"item": "book"}
HTTP/1.1 308 Permanent Redirect
Location: /api/v2/orders
POST /api/v2/orders HTTP/1.1
Host: api.example
Content-Type: application/json
{"item": "book"}
GET/rNNN with that status and
Location: /target/target simply reports the method and body it received
curl -L -d 'name=parham' http://127.0.0.1:8232/r303
-d implies POST. Do
not add -X POST — that
forces the method on the second request too and hides the effect
301 → GET /target body=''
302 → GET /target body=''
303 → GET /target body=''
307 → POST /target body='name=parham'
308 → POST /target body='name=parham'
--post301 --post302, which is exactly the ambiguity those
two codes carryPOST? → 303,
pointing at the resultmessage received, still working — another response is coming
| Code | Meaning | Still used? |
|---|---|---|
| 100 Continue | go ahead and send the body | yes, for big uploads |
| 101 Switching Protocols | this connection is now something else | yes, WebSocket |
| 102 Processing | WebDAV keep-alive | deprecated |
| 103 Early Hints | start fetching these while I think | yes, and growing |
POST a 2 GB file. The server may reject it
anyway — wrong credentials, wrong content type, too large401 is a poor use of everyone's eveningExpect: 100-continue, and waits
$ curl -v -X POST -H 'Expect: 100-continue' --data-binary @big.txt \
http://127.0.0.1:8241/upload
> POST /upload HTTP/1.1
> Host: 127.0.0.1:8241
> Expect: 100-continue
> Content-Length: 3000
>
< HTTP/1.1 100 Continue <- interim: the body has not been sent yet
<
< HTTP/1.1 200 OK <- final, after the body went up
< Content-Length: 15
Upgrade request
header, naming the protocol the server is switching to
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Link: rel=preload headers, and send the
real response later
$ curl -v http://127.0.0.1:8241/hints
> GET /hints HTTP/1.1
> Host: 127.0.0.1:8241
>
< HTTP/1.1 103 Early Hints
< Link: </style.css>; rel=preload; as=style
< Link: </app.js>; rel=preload; as=script
<
<- server is still working here
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 27
from https://avatars1.githubusercontent.com/u/8181240?v=4
ssh$base_url, tokens — out of the
request itselfdemo/example.yml
name: Example
method: GET
url: https://example.com
timeout: 30000
followRedirects: true
maxRedirects: 5
and the same file runs without the TUI
$ noodle collection run demo
✓ GET example 200 OK 491.524791ms
https://example.com
Summary: 1 passed, 0 failed, 491.524791ms

Server-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via an HTTP connection, and describes how servers can initiate data transmission towards clients once an initial client connection has been established.
They are commonly used to send message updates or continuous data streams to a browser client and designed to enhance native, cross-browser streaming through a JavaScript API called EventSource, through which a client requests a particular URL in order to receive an event stream.
The EventSource API is standardized as part of HTML5 by the WHATWG. The media type for SSE is text/event-stream.
103 Early Hints: the
server answers early with
Link: </style.css>; rel=preload and lets the
browser decide what to fetch.| WebSocket | SSE | Polling | |
|---|---|---|---|
| Direction | Bidirectional | Server → Client | Client → Server |
| Protocol | ws:// or wss:// | HTTP | HTTP |
| Overhead | Low | Low | High |
| Use Case | Chat, Games | Notifications | Simple updates |
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
// Create WebSocket connection
const socket = new WebSocket('wss://example.com/socket');
// Connection opened
socket.addEventListener('open', (event) => {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', (event) => {
console.log('Message from server:', event.data);
});
// Handle errors
socket.addEventListener('error', (error) => {
console.error('WebSocket error:', error);
});
// Connection closed
socket.addEventListener('close', (event) => {
console.log('Connection closed:', event.code, event.reason);
});
// Send text
socket.send('Hello!');
// Send JSON
socket.send(JSON.stringify({ type: 'message', data: 'Hello!' }));
// Check connection state
if (socket.readyState === WebSocket.OPEN) {
socket.send('Connection is open');
}
// Close connection
socket.close(1000, 'Normal closure');
Use WebSocket when you need low-latency, bidirectional communication. For server-to-client only, consider SSE as a simpler alternative.
Expires and no Max-Age. The browser drops it
when it closesSessionhere means the browser session, not your application's login session — a login can be built on either
Set-Cookie: session=P4kQ2m9xR7tLv3Nb; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: user=parham; role=admin
role=admin is one DevTools click away
GET /cookies/set?name=parham&family=alvani HTTP/1.1
Host: httpbin.org
HTTP/1.1 302 FOUND
Date: Mon, 07 Sep 2020 05:19:50 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 223
Connection: keep-alive
Server: gunicorn/19.9.0
Location: /cookies
Set-Cookie: name=parham; Path=/
Set-Cookie: family=alvani; Path=/
GET /cookies HTTP/1.1
Host: httpbin.org
Cookie: name=parham; family=alvani
HTTP/1.1 200 OK
Date: Mon, 07 Sep 2020 05:23:53 GMT
Content-Type: application/json
Content-Length: 58
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"cookies": {
"family": "alvani",
"name": "parham"
}
}
document.cookiehttp versus https — that
is the Secure attribute aboveSameSite=None is set, the cookie
Secure
attribute must also be set (or the cookie will be blocked)..example.com) is
ignored — write
example.com.com or .ac.ir — otherwise one site could
set a cookie for every university in Iran

curl -D - -c jar.txt http://127.0.0.1:8210/login
HTTP/1.0 200 OK
Set-Cookie: session=abc123; Path=/
Set-Cookie: admin_token=xyz789; Path=/admin
$ curl -b jar.txt http://127.0.0.1:8210/
path=/ cookies=session=abc123
$ curl -b jar.txt http://127.0.0.1:8210/admin/panel
path=/admin/panel cookies=admin_token=xyz789; session=abc123
session goes everywhere, because its path
is /admin_token is sent
only under /admin, and the
server never even sees it elsewhereHttpOnly__Secure-: accepted only if it was set
with Secure, over HTTPS__Host-: the same, plus
Path=/ and no
Domain attribute at all
Set-Cookie: __Host-session=P4kQ2m9xR7tLv3Nb; Path=/; Secure; HttpOnly; SameSite=Lax
__Host- is what stops
evil.example.com from handing you a cookie that looks like
yoursSameSite=None; Secure exists: it is how you
opt in to a cross-site cookie, and it is exactly what is going
awaySameSite is the first defence, a per-session
token is the second
Covered properly in the Web Security lecture.
Neither Strict nor Lax are a complete solution for your site's security. Cookies are sent as part of the user's request and you should treat them the same as any other user input. That means sanitizing and validating the input. Never use a cookie to store data you consider a server-side secret.



Cache-Control HTTP/1.1
general-header field is used to specify
directives for caching mechanisms in
both requests and responses.the maximum amount of time a resource will be considered fresh.
Cache-Control: max-age=<seconds>
The cache should not store anything about the client request or server response.
Cache-Control: no-store
A cache will send the request to the origin server for validation before releasing a cached copy.
Cache-Control: no-cache
If-Modified-Since: current
last-modified timeIf-None-Match: the server will send back
the requested resource, with a 200 status, only if it doesn't have an
ETag matching the given ones.ETag HTTP response header is an
identifier for a specific version of a resource.
who are you?, authorization answers
what may you do?
user:pass
[The username itself cannot contain a colon]
An additional pad character is allocated which may be used to force the encoded output into an integer multiple of 4 characters (or equivalently when the unencoded binary text is not a multiple of 3 bytes) ; these padding characters must then be discarded when decoding but still allow the calculation of the effective length of the unencoded text, when its input binary length would not be not a multiple of 3 bytes (the last non-pad character is normally encoded so that the last 6-bit block it represents will be zero-padded on its least significant bits, at most two pad characters may occur at the end of the encoded stream).
If unpadded strings are concatenated, it's impossible to recover the original data because information about the number of odd bytes at the end of each individual sequence is lost

GET /basic-auth/admin/admin HTTP/1.1
Host: httpbin.org
Authorization: Basic YWRtaW46YWRtaW4=
HTTP/1.1 200 OK
Date: Mon, 07 Sep 2020 14:14:25 GMT
Content-Type: application/json
Content-Length: 48
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"authenticated": true,
"user": "admin"
}
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
response = MD5(HA1:nonce:HA2)
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
hash(pass, nonce)SHA-256 and deprecates
MD5; the example above is the classic MD5
one from RFC 2617

do not repeat the procedure for every requestis the cookie we met earlier
POST /login with the credentialsSet-Cookie: session=… on the responseCookie:
$ curl -sS -D - -o /dev/null http://127.0.0.1:8211/me
HTTP/1.1 401 Unauthorized
$ curl -sS -D - -c jar.txt -d 'user=parham&pass=s3cret' \
http://127.0.0.1:8211/login
HTTP/1.1 303 See Other
Location: /me
Set-Cookie: session=5r1dBZIscs2oNeux; Path=/; HttpOnly; SameSite=Lax
GET /me HTTP/1.1
Host: 127.0.0.1:8211
Cookie: session=5r1dBZIscs2oNeux
HTTP/1.1 200 OK
hello parham
session=5r1dBZIscs2oNeux means nothing on
its own — the name parhamlives in the server's session store
HttpOnly keeps an XSS payload from reading
it; SameSite keeps another site from
riding it
$ curl -sS -D - -b jar.txt -X POST http://127.0.0.1:8211/logout
HTTP/1.1 200 OK
Set-Cookie: session=; Path=/; Max-Age=0
$ curl -sS -b jar.txt http://127.0.0.1:8211/me # jar still has the cookie
unknown session
Max-Age=0 cookie is only a courtesy
to the browser| Session cookie | Bearer token | |
|---|---|---|
| State | On the server | In the token |
| Sent by | The browser, automatically | Your code, explicitly |
| Revoke | One delete | Wait for expiry |
| CSRF | Exposed, needs SameSite | Immune |
| Fits | A site with a browser in front of it | APIs, mobile, service to service |
give access to the bearer of this token.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlBhcmhhbSBBbHZhbmkiLCJpYXQiOjE1MTYyMzkwMjIsInByb2plY3QiOiJhbiBhd2Vzb21lIHByb2plY3QifQ.
gWWHu5Ps_F6lbqJRBXkNjEk_-0QdLhN9l2MNjWOcj90
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "Parham Alvani",
"iat": 1516239022,
"project": "an awesome project"
}
{
"alg": "RS512",
"kid": "z8a4l4oOFEqgehRYDBZP+fprPnLDLmabkslOxVVpLNE",
"typ": "JWT"
}
{
"aud": [
"passenger"
],
"email": "parhamalvani@gmail.com",
"exp": 1646469738,
"iat": 1645260138,
"iss": 1,
"jti": "2NFKm5FfEey65wIArBQAz289hDgf/E0gjnyXrNCM0v4",
"sid": "25JzmlUBAwtMfQvT7qmOalw5M7p",
"sub": "KpQxO5glyv04Ad1"
}

$ curl --connect-to example.com:80:127.0.0.1:9080 http://example.com/
$ curl --connect-to example.com:443:127.0.0.1:9443 https://example.com/
C->S 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a |GET / HTTP/1.1..|
C->S 48 6f 73 74 3a 20 65 78 61 6d 70 6c 65 2e 63 6f |Host: example.co|
C->S 6d 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 |m..User-Agent: c|
C->S 75 72 6c 2f 38 2e 37 2e 31 0d 0a 41 63 63 65 70 |url/8.7.1..Accep|
C->S 74 3a 20 2a 2f 2a 0d 0a 43 6f 6e 6e 65 63 74 69 |t: */*..Connecti|
C->S 6f 6e 3a 20 63 6c 6f 73 65 0d 0a 0d 0a |on: close....|
S->C 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
S->C 0a 44 61 74 65 3a 20 4d 6f 6e 2c 20 31 37 20 41 |.Date: Mon, 17 A|
C->S 17 03 03 00 6e b8 bc 67 ba 90 7c 40 2c e2 bb 5a |....n..g..|@,..Z|
C->S c0 1d 7b 7f 27 49 9e aa 81 81 cc 7f 10 d6 a1 40 |..{.'I.........@|
C->S 06 41 a7 a3 9f 94 ab 22 a9 40 4c 03 f2 25 4a b2 |.A.....".@L..%J.|
C->S ca 54 00 a3 38 89 bb cb 10 bf 2e a1 a8 24 03 94 |.T..8........$..|
C->S 8c f2 24 6b 71 19 40 f4 3b 24 d6 66 fe 1a e7 5c |..$kq.@.;$.f...\|
C->S 49 9d 2a 71 63 6d d4 b6 6a 53 30 33 bc ee b5 77 |I.*qcm..jS03...w|
C->S cf a2 30 c9 06 ff 4d 81 0c 8d d1 7e 5b 1c b4 54 |..0...M....~[..T|
C->S 7e 53 45 |~SE|
17 = application data,
03 03 = record version,
00 6e = 110 bytes follow
C->S 16 03 01 01 39 01 00 01 35 03 03 0f 96 b0 2f 41 |....9...5...../A|
... cipher suites, key share, 220 bytes ...
C->S 0e 00 00 0b 65 78 61 6d 70 6c 65 2e 63 6f 6d 00 |....example.com.|
ClientHello is sent
before any key exists, so the
SNI hostname goes out in the clear
