Foundationপ্রথম নীতি থেকে
LEVEL 7লেসন ১৮/২২কঠিন৫৫ মিনিট

HTTP/1.1, HTTP/2 — request/response-এর আসল খরচ

HTTP/1.1, HTTP/2 and HTTP/3 Evolution

Method-status-header-র semantics তিন দশক অক্ষত; বদলেছে transport-এর খরচ — HTTP/1.1-এর HOL blocking থেকে HTTP/2-এর multiplexing, আর সেই খরচ মাপার হাতিয়ার raw socket client।

এই লেসন শেষে আপনি পারবেন

  • HTTP-র version-independent semantics — methods, status class, headers, statelessness — বর্ণনা করে বলতে পারবেন কেন এই স্তরটা কোনো version-ই touch করেনি
  • HTTP/1.1-এর keep-alive আর pipelining-এর পার্থক্য দেখিয়ে ব্যাখ্যা করতে পারবেন head-of-line blocking কীভাবে pipelining-কে মৃত্যুদণ্ড দিল, আর browsers 6-connection workaround কেন নিল
  • HTTP/2-এর binary framing, streams ও multiplexing, HPACK, server-push (এবং তার মৃত্যু) — frame-level trace-এ চিহ্নিত করতে পারবেন
  • Cache-Control, ETag, If-None-Match-এর 304 dance পুরো flow-এ trace করে conditional request নিজের হাতে চালাতে পারবেন
  • Cookies-কে stateless protocol-এর উপরের thin state-layer হিসেবে analyze করতে পারবেন, এবং raw socket-এ HTTP parse করে দেখাতে পারবেন protocol আসলে কত ছোট

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

আজকের web-এ ঘটছে একটা অদ্ভুত বিষয়। একদিকে protocol-টার ভাষা — GET, 404, Content-Type — ১৯৯১ থেকে প্রায় অপরিবর্তিত। অন্যদিকে সেই ভাষা কীভাবে পরিবহণ হয়, তা তিনবার পুরোপুরি rewrite হয়েছে: HTTP/1.1 (1997) → HTTP/2 (2015) → HTTP/3 (2022)।

Paradox-টাই আজকের পাঠ: semantics আর transport দুইটা আলাদা engineering সমস্যা। Browser-এ link-click করলে ঠিক কী ঘটল — কয় packet, কয় connection, কোথায় অপেক্ষা, কোথায় একই তথ্য বারবার? খরচ মাপতে পারলে বুঝবেন কেন h2-এর জন্ম, কেন server-push মরল, আর TCP-র ভেতরের HOL কেন QUIC-র জন্ম দিল।

আজকের প্রশ্ন: এক page-load-এর আসল invoice কী, কোন line-item অপ্রয়োজনীয়?

মূল ধারণা

Semantics — যে স্তর কখনো বদলায়নি

RFC 9110 সাবধানে নাম রেখেছে Semantics — নির্দিষ্ট version নয়:

Methodsচরিত্রউদাহরণ
GET, HEADsafe — server-state বদলায় নাpage fetch
PUT, DELETEidempotent — কতবার পাঠালেও ফল একreplace/remove
POSTকিছুই guarantee নেইform submit, payment
PATCHpartial modifyfield update

Status-class গল্প বলে: 1xx informational, 2xx success (200, 204), 3xx redirect (301 permanent, 304 not-modified — আজকের hero!), 4xx client-দোষ (404, 429), 5xx server-দোষ (500, 503)।

Headers metadata-র ভাষা — আর এখানেই প্রথম খরচ:

GET /index.html HTTP/1.1            ← request line
Host: example.com                   ← 1.1-এ mandatory
User-Agent: Mozilla/5.0 ...
Accept: text/html,...
Accept-Encoding: gzip, deflate, br
                                    ← খালি লাইন = header শেষ

প্রতি request-এ ~500 byte প্রায়-অপরিবর্তিত plaintext। সংখ্যাটা মনে রাখুন — h2-এর HPACK ঠিক এটারই শিকারি।

HTTP/1.1 — keep-alive আর pipelining-এর মৃত্যু

1.0-এ প্রতি object-এ নতুন TCP connection (handshake+TLS+slow-start প্রতিবার)। 1.1-এর উত্তর: keep-alive — connection খোলা, পরের request একই pipe:

HTTP/1.0:        conn ─GET▶ ◀resp─ close    conn ─GET▶ ...
HTTP/1.1:        conn ─GET▶ ◀resp─ ─GET▶ ◀resp─ ─GET▶ ◀resp─ ...

কিন্তু ৩০ object = ৩০ serial round-trip। প্রস্তাব ছিল pipelining — উত্তর অপেক্ষা না করে সব request একসাথে পাঠানো। তাত্ত্বিকভাবে ১ RTT, বাস্তবে মৃত্যুদণ্ড — এক বাধ্যবাধকতার কারণে: response অবশ্যই request- order-এ আসবে।

Browser-দের workaround: parallel connections — per-host ৬টা। ৩০ object → ৬ lane-এ ৫টা করে। দাম: ৬× handshake, ৬× slow-start, server-ভিড়। এই compromise-ই 2015 পর্যন্ত web-এর বাস্তবতা।

HTTP/2 — framing-এর বিপ্লব

SPDY-বংশধর h2 (RFC 9113): এক connection-এ streams — প্রতিটা independent request/response-এর ID-বাহী লজিক্যাল পথ; wire-এ সব কিছু ছোট binary frame:

┌────────────────── one TCP connection ──────────────────┐
│  [HEADERS s=1] [HEADERS s=3] [DATA s=1] [DATA s=3]     │
│  [HEADERS s=5] [DATA s=1] [DATA s=5] [DATA s=3] ...    │
└────────────────────────────────────────────────────────┘
   frame ≈ 9-byte header + payload; type = DATA/HEADERS/
   SETTINGS/WINDOW_UPDATE/PING/RST_STREAM/...

Interleaved frames — stream-3 আটকে গেলেও 1, 5 এগোয়। Application-level HOL dead। Bonus দুটো: HPACK header-compression — static-table (৬১ common header) + per-connection dynamic-table + Huffman; আগের 500-byte header 20-30 byte-এ, repeated User-Agent একবারই যায়। আর server push — server অনুমানে resource ধাক্কা দেবে… যা ব্যর্থ: server জানত না cache-এ কী আছে, push-গুলো বেশিরভাগ wasted bytes। Chrome 106 (2022)-এ removed — protocol-feature-র dignified মৃত্যু; পাঠ: receiver-context- dependent speculation sender-এ নয়।

Caching — সবচেয়ে সস্তা request যেটা যায় না

প্রথম visit:
GET /logo.png  ▶
◀ 200 OK, Cache-Control: max-age=31536000, ETag: "abc123"

এক বছরের মধ্যে আবার:        ← request-ই যায় না! disk-cache

max-age শেষে:
GET /logo.png, If-None-Match: "abc123"  ▶
◀ 304 Not Modified              ← body নেই! ~200 byte বনাম ~50KB

ETag = representation-fingerprint। no-cache = cache করো কিন্তু প্রতিবার revalidate; no-store = কোনো স্মৃতি নয় (banking)।

Cookies — stateless-এর উপর পাতলা state

HTTP amnesiac — প্রতিটা request স্বাধীন। Login-state-এর দরকার মেটায় cookie: server Set-Cookie: session=xyz; HttpOnly; Secure; SameSite=Lax, client পরের প্রতিটা request-এ Cookie: session=xyz ফেরত। পুরো ‘login state’ = client-বাহিত string, server-session-store-এর চাবি। HttpOnly = JS পড়বে না (XSS-defense); Secure = HTTPS-only; SameSite = cross-site request নয় (CSRF-defense)।

ভেতরে কী ঘটছে

Frame-by-frame — h2-এ একটা GET-এর দেহ

→ SETTINGS          (empty)                    ← connection preamble
→ HEADERS (stream 1)
    :method         GET                        ← pseudo-headers ':' দিয়ে —
    :scheme         https                         request-line+Host-এর
    :authority      example.com                   উত্তরাধিকার
    :path           /
    user-agent      curl/8.4.0                 ← HPACK-indexed!
    accept          */*                        ← static-table index
→ SETTINGS ACK
← HEADERS (stream 1)  :status 200, content-type..., etag...
← DATA (stream 1)     [html bytes, END_STREAM]

লক্ষণীয়: (১) request-line+Host → চার pseudo-header — ambiguity-free parsing (h1-এর whitespace/newline parsing-bug-ইতিহাস: request smuggling!)। (২) প্রতিটা frame stream-ID বহন — interleaving-এর mechanism। (৩) flow-control frame-level (WINDOW_UPDATE credit) — TCP-র global-window-এর চেয়ে সূক্ষ্ম।

খরচ-এর anatomy — এক page, তিন যুগ

৩০-object page, 50ms RTT:

HTTP/1.1, 6 conns:  6×handshake(parallel)≈1RTT + 30÷6×1RTT≈5RTT ≈ 6 RTT = 300ms
                    (+ 6× congestion-window warm-up)

HTTP/2, 1 conn:     handshake 1RTT + 30 streams interleaved 1-2RTT ≈ 2-3 RTT
                    = 100-150ms (+ single slow-start)

HTTP/2 + loss:      যে-কোনো stream-এর packet-loss → সব-৩০ freeze +1 RTT

Shape সঠিক: h2-এর জয় connection-amortization-এ; vulnerability shared- bytestream-এ।

উদাহরণ

পূর্ণ exchange — conditional-request-এর dance

# ১. প্রথম fetch
→ GET /api/status HTTP/1.1
← HTTP/1.1 200 OK
← Cache-Control: max-age=60
← ETag: "v17-f3a9c2"
← (body: 4812 bytes)

# ২. ৯০ সেকেন্ড পর — max-age expired
→ GET /api/status HTTP/1.1
→ If-None-Match: "v17-f3a9c2"          ← আগের fingerprint ফেরত

← HTTP/1.1 304 Not Modified             ← body ZERO
← Cache-Control: max-age=60             ← নতুন freshness-টিকিট

# ৩. cache রাখল পুরনো body, max-age রিসেট — ৬০ সেকেন্ড আরো শান্তি

# ৪. content সত্যিই বদলালে
→ If-None-Match: "v17-f3a9c2"
← HTTP/1.1 200 OK, ETag: "v18-91bd04", (নতুন body)

Economics: step-2-এ গেল ~150 byte, এড়াল 4812 — 97% saving প্রতি repeat-visit-এ; CDN-scale-এ এই dance-ই origin-bandwidth-bill ভাগ করে। 304 নিজেই design-lesson: ‘no change’ একটা first-class উত্তর।

ছবিতে

তিন version, এক ছবি — object-গুলো কীভাবে যায়

HTTP/1.1 keep-alive (serial):           time ▶
conn: GET1▶ ◀R1  GET2▶ ◀R2  GET3▶ ◀R3  GET4▶ ◀R4  GET5▶ ◀R5  GET6▶ ◀R6
      └────── প্রতি object = 1 RTT ──────────────────────────────┘
                                                6 RTT total (1 conn)

HTTP/1.1 pipelining (dead dream):
conn: GET1 GET2 GET3 GET4 GET5 GET6 ▶   ◀R1..........◀R6
                                          ↑
                              R1 slow → পেছনের ৫ ready-ও আটকে
                              = application-level HOL → browsers বর্জন

HTTP/2 multiplexing (1 conn):
conn: H1 H3 H5 H2 H6 H4 D1 D3 D5 D2 D4 D6 ...
      stream-3 slow? 1,5,2,6 এগোচ্ছে                    ≈2 RTT

TCP underneath (সব version):
      loss on stream-3's data → retransmit →
      ★ h1: শুধু ওই conn থামে    ★ h2: সব stream একসাথে থামে ★
                                       (transport-HOL → QUIC)
একই ৬টা object তিন transport-এ। খেয়াল করুন pipelining-এ কেন সবাই R1-এর পেছনে বন্দি, আর h2-এর নিচে TCP-loss কাকে থামায়।

নিজে চালিয়ে দেখুন

EXPERIMENT

curl দিয়ে version-war আর header-forensics

macOS / Linux (curl built-in)· ১৫ মিনিট

ধাপ ১ — একই URL, দুই version:

curl -sv --http1.1 https://www.wikipedia.org -o /dev/null 2>&1 | grep '< HTTP'
curl -sv --http2     https://www.wikipedia.org -o /dev/null 2>&1 | grep 'ALPN\|< HTTP'
# প্রথমটা: < HTTP/1.1 200 OK
# দ্বিতীয়টা: * ALPN: offers h2 ...   < HTTP/2 200

TLS-lesson-এর ALPN মনে করুন — version negotiate হয়েছিল handshake-এর ভেতরেই।

ধাপ ২ — header-size comparison (HPACK-জাদু):

for v in http1.1 http2; do
  curl -sv --$v https://www.wikipedia.org -o /dev/null 2>&1 |
    awk -v V=$v '/> /{req+=length($0)} END{print V": request bytes ≈ "req}'
done
# h1-এ প্রতি request full headers; h2-তে compressed

ধাপ ৩ — response-header forensics (quiz-style):

curl -sI https://github.com | grep -i 'server\|cache\|strict\|etag'

প্রতিটা line-এর গল্প বের করুন: Server: field CDN-নাকি origin-software? Cache-Control: private, no-cache logged-in-site-এ কেন বাধ্যতামূলক? Strict-Transport-Security (HSTS) কীভাবে future-downgrade attack আগেই বন্ধ করে? x-served-by/x-cache থাকলে CDN/reverse-proxy signature — পরের-পরের লেসনের preview!

ধাপ ৪ — 304 dance live:

etag=$(curl -sI https://httpbin.org/etag/test | awk -F'"' 'NR==1{print $2}')
curl -sv -H "If-None-Match: \"$etag\"" https://httpbin.org/etag/test -o /dev/null 2>&1 | grep '< HTTP'
# < HTTP/2 304   ← নিজে হাতে conditional-request!
এটা কী প্রমাণ করে

একই URL দুই version-এ negotiation, header-size আর timing-এ পার্থক্য হাতে-নাগাল; popular-site-এর response-headers পড়ে production caching/security posture পড়া যায়।

নিজে বানান

BUILD IT

rawhttp.py — library-ছাড়া HTTP client: GET, parse, ETag-flow

Python · ●●○○○
  1. socket.create_connection দিয়ে TCP connect
  2. Request bytes হাতে: request line + Host + Connection: close + blank line
  3. recv loop — Content-Length পর্যন্ত জমাও
  4. Status line + headers parse — CRLF split, case-insensitive
  5. ETag ধরে If-None-Match revalidation — 304 যাচাই

HTTP আসলে কত ছোট — এই client প্রমাণ (~৮০ লাইন, stdlib):

#!/usr/bin/env python3
"""rawhttp.py — stdlib-socket-এ হাতে-লেখা HTTP/1.1 client"""
import socket


def request(host, path='/', extra_headers=None, port=80):
    sock = socket.create_connection((host, port), timeout=10)
    try:
        lines = [f'GET {path} HTTP/1.1', f'Host: {host}',
                 'Connection: close', '']
        for k, v in (extra_headers or {}).items():
            lines.insert(2, f'{k}: {v}')
        raw = ('\r\n'.join(lines) + '\r\n\r\n').encode()
        print(f'--- পাঠালাম ({len(raw)} bytes) ---')
        print(raw.decode(), end='')
        chunks = b''
        while True:
            part = sock.recv(65536)
            if not part:
                break
            chunks += part
    finally:
        sock.close()

    head, _, body = chunks.partition(b'\r\n\r\n')
    status_line, *header_lines = head.decode('latin1').split('\r\n')
    version, code, reason = status_line.split(' ', 2)
    headers = {}
    for h in header_lines:
        name, _, value = h.partition(':')
        headers[name.strip().lower()] = value.strip()
    clen = int(headers.get('content-length', len(body)))
    return {'code': int(code), 'headers': headers,
            'body': body[:clen], 'raw_size': len(chunks)}


if __name__ == '__main__':
    host, port = '127.0.0.1', 8000
    # fixture server চালু রাখুন: python3 fixture_server.py
    r1 = request(host, '/', port=port)
    print('--- উত্তর ---')
    print(f'status : {r1["code"]}')
    etag = r1['headers'].get('etag', '(etag নেই)')
    print(f'etag   : {etag}   size: {r1["raw_size"]} bytes')

    r2 = request(host, '/', port=port,
                 extra_headers={'If-None-Match': etag})
    print('revalidate with If-None-Match:')
    print(f'status : {r2["code"]}  size: {r2["raw_size"]} bytes')
    saved = r1['raw_size'] - r2['raw_size']
    pct = 100 * saved / max(r1['raw_size'], 1)
    print(f'304-dance savings: {saved} bytes ({pct:.0f}% less)')

Fixture server (ETag-সহ, ১৫ লাইন):

# fixture_server.py
import hashlib, http.server

BODY = b'hello from the fixture server - ' + bytes(range(256)) * 4

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        etag = hashlib.md5(BODY).hexdigest()
        if self.headers.get('If-None-Match') == etag:
            self.send_response(304); self.send_header('ETag', etag)
            self.end_headers(); return
        self.send_response(200)
        self.send_header('ETag', etag)
        self.send_header('Content-Length', str(len(BODY)))
        self.end_headers(); self.wfile.write(BODY)
    def log_message(self, *a): pass

http.server.HTTPServer(('127.0.0.1', 8000), Handler).serve_forever()

Output:

--- উত্তর ---
status : 200
etag   : 9a0364b9e99bb480dd25e1f0284c8555   size: 1489 bytes

revalidate with If-None-Match:
status : 304  size: 122 bytes
304-dance savings: 1367 bytes (92% less)

HTTPS-এ একই করতে socket-কে ssl.SSLContext().wrap_socket() — TLS-lesson- এর stack মনে করুন। নিজে বাড়ান: POST+Content-Length; chunked-decoding; duplicate-header এলে আপনার parser-এর কী হয় — নিজেই আবিষ্কার করুন।

বাস্তব সিস্টেমে

HTTP-র খরচ-হিসাব বাস্তবে

Domain-sharding-এর মৃত্যু। img1..img6.example.com ছিল h1-এর 6-connection cap ফাঁদ-পারের trick — h2-তে anti-pattern: এক multiplexed connection-ই optimal, sharding-এ HPACK dynamic-table ভাঙে। Migration checklist-গুলোতে এই line-ই সবচেয়ে ভুল-হওয়া।

Header-bloat সংখ্যায়। Modern-app request-এ cookie+tracking+security headers 2-8 KB — mobile-uplink-এ দামি। HPACK + cookie-trimming + Brotli — page-weight-war-এর প্রথম সারি; measurement: median request-header h1-এ ~450 byte, h2 HPACK-এ ~120।

Server-push-এর কবর। Chrome telemetry: pushed-resource ~41% কখনো ব্যবহৃত হয়নি। যে জিতল: 103 Early Hints — server hint দেয় (‘CSS এগুলো লাগবে’), content ধাক্কা দেয় না; client cache-aware ভাবে নিজে চেয়ে নেয়। Speculation→suggestion — textbook API-pivot।

Caching = billion-dollar। Wikipedia text-page hit-ratio 90%+ — origin-database কাজ করে কারণ edge ৯০% প্রশ্নের উত্তর দেয়। Cache-Control/ETag দুই header-ই সেই economics-এর control-panel — CDN-lesson-এ geo-scale।

যে ভুলগুলো সবাই করে

“HTTP/2 সবসময় HTTP/1.1-এর চেয়ে দ্রুত।”

Measure না করে দাবি বিপজ্জনক। Single-large-object, low-loss — পার্থক্য নগণ্য (multiplexing-লাভ শূন্য)। High-loss-এ h2 কখনো h1-এর চেয়ে খারাপও: h1-এর ৬ connection-এ loss এক connection থামায়, h2-এর এক connection-এ loss সবাইকে (transport-HOL)। ৬× parallel slow-start-ও উল্টো খেলতে পারে। Version-choice = performance-decision, dogma নয়।

“HTTP stateless মানে server কোনো state রাখতে পারে না।”

মানে সীমাবদ্ধ-precise: protocol-level-এ এক request অন্যটার উপর নির্ভর করে না — প্রতিটা self-contained। Server ভেতরে যা খুশি state (session-store, db) — শর্ত: সেই state-এর reference client-ই পাঠায় (cookie/token)। Fielding-এর design-goal: reliability+scalability — proxy/cache যে-কোনো replica-তে পাঠাতে পারে। Statelessness = visible-contract, memory-less-server নয়।

“404 মানে server ভেঙে পড়েছে।”

404 একটা সফল, সঠিক উত্তর: ‘চাওয়া resource এখানে নেই’ — processing নিখুঁত সম্পন্ন। Class-পড়া: 4xx = request-এর সমস্যা (server healthy), 5xx = server fail। Monitoring-এ এই ভাগ জীবন-মরণ: 404-rate বাড়া = broken-links/scanner; 500-rate বাড়া = rollback এখনই। Status-class-reading ops-literacy-র প্রথম অক্ষর।

“Cookie-ই একমাত্র state-mechanism — token আলাদা জিনিস।”

JWT/session-token যে-path-এই যাক — cookie বা Authorization: Bearer header — মূলনীতি এক: client-carried credential প্রতিটা self-contained request-এ। পার্থক্য policy-র: cookie automatic (SameSite নিয়ন্ত্রণে), CSRF-classic-surface; header explicit — XSS-ঝুঁকি বেশি, CSRF প্রায় শূন্য। Transport-choice; state-model দুটোতেই এক।

বুঝেছেন কি না দেখুন

1

Pipelining-এর ordering-requirement না থাকলে কী হতো? নিয়মটা ঠিক কাদের বাঁচায়?

যুক্তি

Response-গুলো request-এর সাথে match করার উপায় হারাত — h1-message-এ কোনো ID নেই, response identify হয় position-এই। h2-এর stream-ID-ই সেই সমাধান — interleaving-এর প্রথম শর্ত। নিয়মটা বাঁচায় simple-parser-দের: identity-by-position (simple, serial) বনাম explicit-identity (parallel, demux-complex) — design-tension-এর পাঠ।

2

max-age=3600 CSS deploy-এর পর বদলাল। User-cache-এ পুরনোটা — কতক্ষণে নতুন পাবে? Production-solution কী?

প্রয়োগ

সর্বোচ্চ ১ ঘণ্টা — max-age চলাকালীন request-ই যায় না। Solution: cache-busting via URL — app.a1b2c3.css (content-hash filename) + max-age=31536000, immutable; HTML-এ new-hash reference, HTML নিজে no-cache। Immutable-asset long-TTL + volatile-document no-cache — golden pattern; content-change instant (HTML বদলাতেই)।

3

HPACK dynamic-table কেন connection-scoped? Domain-sharding এর কী ক্ষতি?

যুক্তি

Table-এর মূল্য স্মৃতিতে — আগের request-এর header-value মনে রেখে index-reference। Connection-lifetime জুড়ে share হলে hit-rate বেশি; per-request reset-এ compression প্রায় শূন্য। Sharding = প্রতি hostname আলাদা connection = আলাদা empty-table = full-headers আবার — h1-era optimization h2-era-তে anti-pattern। Layer-assumption বদলালে optimization পুনঃমূল্যায়ন বাধ্যতামূলক।

4

Real-time dashboard — ৫ সেকেন্ডে update। Polling, long-polling, SSE, WebSocket-এর খরচ বিশ্লেষণ করুন; WS কবে জেতে?

ডিজাইন

Polling: প্রতি ৫-এ full-request; idle-ও খরচ; latency ≤5s; simplest। Long-polling: server ধরে রাখে — latency কমে, কিন্তু per-update reconnect+headers, hanging-request state। SSE: এক persistent server-push-stream, auto-reconnect built-in — one-way feed-এ perfect (dashboard!)। WS: bidirectional, ~6-byte frame — জেতে client-ও frequent পাঠালে (chat, game, cursors)। Dashboard read-heavy → SSE যথেষ্ট। প্রশ্নটা নিজেকে করুন: upstream-এ কি সত্যিই frequent traffic?

5

h2-এ এক stream-এর lifecycle-এ কোন frame-type, connection-setup-এ কোনগুলো?

স্মরণ

Connection-level: SETTINGS (+ACK)। Stream-level: HEADERS (pseudo-headers), DATA, ঐচ্ছিক WINDOW_UPDATE, cancel-এ RST_STREAM; keepalive PING, shutdown GOAWAY। সব binary frame, প্রতিটা stream-ID-বাহী (connection-mgmt frames বাদে)।

এরপর কী

এরপর কী

Application-level HOL মরেছে, কিন্তু TCP-র ordered-bytestream এক packet-loss- কেও সব-stream-stall বানায়। Kernel-TCP বদলানো প্রায় অসম্ভব — কিন্তু UDP- র উপর userspace-এ নতুন transport লেখা সম্ভব। পরের লেসনে QUIC — কেন TCP+TLS নতুন-করে-লেখাই একমাত্র পথ, connection-ID-র roam-magic, আর Google/Meta/Cloudflare কেন kernel bypass করল।

আরও পড়ুন

  • RFC 9110 — HTTP Semantics — R. Fielding, M. Nottingham, J. Reschke (eds.) · Methods, status codes, caching, cookies-র আনুষ্ঠানিক সংজ্ঞা — version-neutral document
  • RFC 9113 — HTTP/2 — M. Thomson, C. Benfield (eds.) · Binary framing layer, stream multiplexing, HPACK — wire format-এর specification
  • Architectural Styles and the Design of Network-based Software Architectures (Fielding dissertation, 2000) — Roy T. Fielding · REST-এর জন্ম — statelessness আর uniform interface-এর design rationale