-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhttp_interactive.html
More file actions
601 lines (572 loc) · 40.5 KB
/
Copy pathhttp_interactive.html
File metadata and controls
601 lines (572 loc) · 40.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTTP — Guide Interactif</title>
<style>
:root{--bg:#f8fafc;--card:#fff;--border:#e2e8f0;--text:#1e293b;--muted:#64748b;--acc:#0284c7;--acc2:#0369a1;--blue600:#2563eb;--green:#16a34a;--red:#dc2626;--orange:#ea580c;--purple:#7c3aed;--grad1:#001a2e;--grad2:#002952}
@media(prefers-color-scheme:dark){:root{--bg:#0d0d0d;--card:#1a1a1a;--border:#2a2a2a;--text:#e2e8f0;--muted:#94a3b8}}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6}
header{background:linear-gradient(135deg,var(--grad1),var(--grad2));padding:2rem 1rem;text-align:center;border-bottom:2px solid var(--acc)}
header h1{font-size:2rem;font-weight:800;color:#fff}
header p{color:#7dd3fc;margin-top:.4rem;font-size:.95rem}
nav{position:sticky;top:0;z-index:100;background:var(--card);border-bottom:1px solid var(--border);padding:.5rem 1rem;display:flex;gap:.5rem;flex-wrap:wrap;justify-content:center}
.tab-btn{padding:.4rem 1rem;border:none;border-radius:999px;cursor:pointer;font-size:.85rem;font-weight:600;background:var(--border);color:var(--muted);transition:all .2s}
.tab-btn.active{background:var(--blue600);color:#fff}
.tab-btn:hover:not(.active){background:var(--acc);color:#fff}
section{display:none;max-width:1100px;margin:0 auto;padding:1.5rem 1rem}
section.active{display:block}
h2{font-size:1.4rem;font-weight:700;margin-bottom:1rem;color:var(--acc)}
h3{font-size:1.1rem;font-weight:600;margin:1.2rem 0 .6rem}
.grid{display:grid;gap:1rem}.g2{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}.g3{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.card{background:var(--card);border:1px solid var(--border);border-radius:.75rem;padding:1.2rem}
.card h4{font-size:1rem;font-weight:700;margin-bottom:.5rem;color:var(--acc)}
.badge{display:inline-block;padding:.2rem .6rem;border-radius:999px;font-size:.75rem;font-weight:600;margin:.1rem}
.badge-green{background:#dcfce7;color:#15803d}.badge-blue{background:#dbeafe;color:#1d4ed8}.badge-red{background:#fee2e2;color:#b91c1c}.badge-yellow{background:#fef3c7;color:#92400e}.badge-purple{background:#ede9fe;color:#6d28d9}.badge-orange{background:#ffedd5;color:#c2410c}
@media(prefers-color-scheme:dark){.badge-green{background:#14532d;color:#86efac}.badge-blue{background:#1e3a5f;color:#93c5fd}.badge-red{background:#450a0a;color:#fca5a5}.badge-yellow{background:#451a03;color:#fcd34d}.badge-purple{background:#2e1065;color:#c4b5fd}.badge-orange{background:#431407;color:#fdba74}}
.codeblk{background:#1e1e2e;color:#cdd6f4;border-radius:.5rem;padding:1rem;font-family:'Cascadia Code','Fira Code',monospace;font-size:.82rem;overflow-x:auto;margin:.5rem 0;line-height:1.7;white-space:pre-wrap}
.c-kw{color:#89b4fa}.c-str{color:#a6e3a1}.c-comment{color:#6c7086;font-style:italic}.c-val{color:#f9e2af}.c-fn{color:#cba6f7}.c-op{color:#89dceb}
table{width:100%;border-collapse:collapse;font-size:.85rem;margin:.5rem 0}
th{background:var(--acc);color:#fff;padding:.5rem .75rem;text-align:left}
td{padding:.45rem .75rem;border-bottom:1px solid var(--border)}
tr:hover td{background:var(--border)}
.info-box{background:var(--card);border-left:4px solid var(--acc);border-radius:.5rem;padding:.8rem 1rem;margin:.5rem 0}
.info-box.green{border-color:var(--green)}.info-box.red{border-color:var(--red)}.info-box.blue{border-color:var(--blue600)}.info-box.purple{border-color:var(--purple)}.info-box.orange{border-color:var(--orange)}
.clickable{cursor:pointer;transition:all .2s;border:2px solid transparent}
.clickable:hover{border-color:var(--acc);transform:translateY(-2px);box-shadow:0 4px 12px rgba(2,132,199,.2)}
.clickable.selected{border-color:var(--acc);background:rgba(2,132,199,.05)}
.detail-panel{display:none;margin-top:.75rem;padding:.8rem;background:var(--bg);border-radius:.5rem;border:1px solid var(--border);font-size:.88rem}
.detail-panel.open{display:block}
.method-card{padding:.6rem;border-radius:.5rem;text-align:center;font-weight:700;cursor:pointer;transition:all .2s;color:#fff}
.method-card:hover{transform:translateY(-3px);filter:brightness(1.1)}
.status-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:.4rem}
.status-cell{padding:.5rem;border-radius:.4rem;text-align:center;font-weight:700;color:#fff;cursor:pointer;font-size:.85rem;transition:all .2s}
.status-cell:hover{transform:scale(1.05)}
.status-1xx{background:#6366f1}.status-2xx{background:#16a34a}.status-3xx{background:#06b6d4}.status-4xx{background:#ea580c}.status-5xx{background:#dc2626}
.protocol-comparison{overflow-x:auto}
.timeline{position:relative;padding-left:2rem;margin:1rem 0}
.timeline::before{content:'';position:absolute;left:.6rem;top:0;bottom:0;width:2px;background:var(--acc)}
.tl-item{position:relative;padding:.5rem 0 .5rem .5rem}
.tl-item::before{content:'';position:absolute;left:-1.55rem;top:.9rem;width:.7rem;height:.7rem;border-radius:50%;background:var(--acc);box-shadow:0 0 0 3px var(--card)}
.tl-year{font-weight:700;color:var(--acc);font-size:.85rem}
.quiz-q{background:var(--card);border:1px solid var(--border);border-radius:.75rem;padding:1.2rem;margin:.8rem 0}
.quiz-q p{font-weight:600;margin-bottom:.7rem}
.quiz-opt{padding:.5rem .8rem;border:1px solid var(--border);border-radius:.4rem;margin:.3rem 0;cursor:pointer;transition:all .2s}
.quiz-opt:hover{border-color:var(--acc);background:rgba(2,132,199,.05)}
.quiz-opt.correct{background:#dcfce7;border-color:var(--green);color:#166534}
.quiz-opt.wrong{background:#fee2e2;border-color:var(--red);color:#991b1b}
@media(prefers-color-scheme:dark){.quiz-opt.correct{background:#14532d;color:#86efac}.quiz-opt.wrong{background:#450a0a;color:#fca5a5}}
#quiz-score{display:none;text-align:center;padding:1.5rem;background:var(--card);border-radius:.75rem;margin-top:1rem;font-size:1.1rem;font-weight:700}
</style>
</head>
<body>
<header>
<h1>HTTP</h1>
<p>HTTP/1.1, HTTP/2, HTTP/3 (QUIC) — protocole de communication du web</p>
</header>
<nav>
<button class="tab-btn active" onclick="showTab('overview')">Vue d'ensemble</button>
<button class="tab-btn" onclick="showTab('message')">Format des messages</button>
<button class="tab-btn" onclick="showTab('methods')">Méthodes</button>
<button class="tab-btn" onclick="showTab('status')">Codes de statut</button>
<button class="tab-btn" onclick="showTab('headers')">Headers clés</button>
<button class="tab-btn" onclick="showTab('h2')">HTTP/2</button>
<button class="tab-btn" onclick="showTab('h3')">HTTP/3 & QUIC</button>
<button class="tab-btn" onclick="showTab('quiz')">Quiz</button>
</nav>
<!-- VUE D'ENSEMBLE -->
<section id="overview" class="active">
<h2>Vue d'ensemble</h2>
<div class="grid g2">
<div class="card">
<h4>HTTP — HyperText Transfer Protocol</h4>
<p style="font-size:.9rem;margin-bottom:.7rem">Protocole de niveau application (couche 7 OSI) pour échanger des ressources hypermédia. Stateless, request-response, basé sur des URLs.</p>
<div class="info-box">
<strong>Caractéristiques :</strong><br>
• Stateless (chaque requête est indépendante)<br>
• Client-serveur<br>
• Texte (HTTP/1.x) ou binaire (HTTP/2, HTTP/3)<br>
• Extensible via les headers<br>
• Cacheable<br>
• Couche application au-dessus de TCP (1.x, 2) ou QUIC/UDP (3)
</div>
</div>
<div class="card">
<h4>Évolution historique</h4>
<div class="timeline">
<div class="tl-item"><div class="tl-year">1991 — HTTP/0.9</div>Une seule méthode (GET), pas de headers, pas de versions. Tim Berners-Lee.</div>
<div class="tl-item"><div class="tl-year">1996 — HTTP/1.0</div>RFC 1945. Headers, versions, codes de statut, POST.</div>
<div class="tl-item"><div class="tl-year">1997 — HTTP/1.1</div>RFC 2068 → 7230. Connexions persistantes, pipelining, host header obligatoire.</div>
<div class="tl-item"><div class="tl-year">2015 — HTTP/2</div>RFC 7540. Binaire, multiplexing, server push, HPACK, basé sur SPDY de Google.</div>
<div class="tl-item"><div class="tl-year">2022 — HTTP/3</div>RFC 9114. Sur QUIC (UDP), 0-RTT, supprime head-of-line blocking TCP.</div>
</div>
</div>
</div>
<div class="card protocol-comparison" style="margin-top:1rem">
<h4>Comparatif HTTP/1.1 vs HTTP/2 vs HTTP/3</h4>
<table>
<tr><th>Aspect</th><th>HTTP/1.1</th><th>HTTP/2</th><th>HTTP/3</th></tr>
<tr><td>Transport</td><td>TCP</td><td>TCP</td><td>QUIC/UDP</td></tr>
<tr><td>Format</td><td>Texte</td><td>Binaire</td><td>Binaire</td></tr>
<tr><td>Multiplexing</td><td>Non (1 req/conn)</td><td>Oui (frames)</td><td>Oui (streams QUIC)</td></tr>
<tr><td>Head-of-line blocking</td><td>HTTP + TCP</td><td>TCP seul</td><td>Aucun</td></tr>
<tr><td>Compression headers</td><td>Non</td><td>HPACK</td><td>QPACK</td></tr>
<tr><td>Server push</td><td>Non</td><td>Oui (déprécié)</td><td>Non</td></tr>
<tr><td>Chiffrement</td><td>Optionnel (TLS)</td><td>Quasi obligatoire (h2c rare)</td><td>Obligatoire (TLS 1.3 intégré)</td></tr>
<tr><td>Handshake</td><td>TCP (1-RTT) + TLS (1-2 RTT)</td><td>TCP + TLS + ALPN</td><td>QUIC 1-RTT, 0-RTT possible</td></tr>
<tr><td>Migration connexion</td><td>Non</td><td>Non</td><td>Oui (Connection ID)</td></tr>
<tr><td>Negotiation</td><td>Version dans request line</td><td>ALPN ou Upgrade</td><td>Alt-Svc header / DNS HTTPS RR</td></tr>
</table>
</div>
</section>
<!-- FORMAT DES MESSAGES -->
<section id="message">
<h2>Format des messages</h2>
<div class="grid g2">
<div class="card">
<h4>Requête HTTP/1.1</h4>
<div class="codeblk"><span class="c-kw">GET</span> /api/users/42 <span class="c-val">HTTP/1.1</span>
<span class="c-kw">Host</span>: api.example.com
<span class="c-kw">User-Agent</span>: Mozilla/5.0 (X11; Linux x86_64)
<span class="c-kw">Accept</span>: application/json
<span class="c-kw">Accept-Language</span>: fr-FR,fr;q=0.9
<span class="c-kw">Accept-Encoding</span>: gzip, deflate, br
<span class="c-kw">Authorization</span>: Bearer eyJhbGc...
<span class="c-kw">Cookie</span>: session=abc123; theme=dark
<span class="c-kw">Cache-Control</span>: no-cache
<span class="c-kw">Connection</span>: keep-alive
<span class="c-kw">If-None-Match</span>: <span class="c-str">"etag-xyz"</span>
<span class="c-comment">[corps vide pour GET]</span></div>
<div class="info-box" style="margin-top:.5rem;font-size:.82rem">
<strong>Structure :</strong><br>
1. Request-line : <code>METHOD path HTTP/version</code><br>
2. Headers (un par ligne, <code>name: value</code>)<br>
3. Ligne vide (CRLF)<br>
4. Body (optionnel)
</div>
</div>
<div class="card">
<h4>Réponse HTTP/1.1</h4>
<div class="codeblk"><span class="c-val">HTTP/1.1</span> <span class="c-kw">200</span> OK
<span class="c-kw">Date</span>: Sun, 12 May 2026 10:30:00 GMT
<span class="c-kw">Server</span>: nginx/1.25
<span class="c-kw">Content-Type</span>: application/json; charset=utf-8
<span class="c-kw">Content-Length</span>: 142
<span class="c-kw">Cache-Control</span>: public, max-age=3600
<span class="c-kw">ETag</span>: <span class="c-str">"etag-xyz"</span>
<span class="c-kw">Vary</span>: Accept-Encoding
<span class="c-kw">Strict-Transport-Security</span>: max-age=31536000
<span class="c-kw">Content-Encoding</span>: gzip
<span class="c-kw">Connection</span>: keep-alive
{
<span class="c-str">"id"</span>: <span class="c-val">42</span>,
<span class="c-str">"name"</span>: <span class="c-str">"Alice"</span>,
<span class="c-str">"email"</span>: <span class="c-str">"alice@example.com"</span>
}</div>
<div class="info-box blue" style="margin-top:.5rem;font-size:.82rem">
<strong>Status-line :</strong> <code>HTTP/version code reason</code><br>
<strong>Reason phrase</strong> (ex: "OK") supprimée en HTTP/2+ (économie de bytes).
</div>
</div>
</div>
<div class="grid g2" style="margin-top:1rem">
<div class="card">
<h4>Encodage du corps</h4>
<table>
<tr><th>Content-Type</th><th>Usage</th></tr>
<tr><td>application/json</td><td>API REST</td></tr>
<tr><td>application/xml</td><td>SOAP, legacy</td></tr>
<tr><td>application/x-www-form-urlencoded</td><td>Formulaires simples</td></tr>
<tr><td>multipart/form-data</td><td>Upload de fichiers</td></tr>
<tr><td>text/html; charset=utf-8</td><td>Page web</td></tr>
<tr><td>application/octet-stream</td><td>Binaire générique</td></tr>
<tr><td>application/grpc</td><td>gRPC sur HTTP/2</td></tr>
<tr><td>text/event-stream</td><td>Server-Sent Events</td></tr>
</table>
</div>
<div class="card">
<h4>Transfer-Encoding & Chunked</h4>
<p style="font-size:.88rem;margin-bottom:.5rem">Permet de streamer la réponse sans connaître sa taille à l'avance.</p>
<div class="codeblk"><span class="c-kw">HTTP/1.1</span> <span class="c-kw">200</span> OK
<span class="c-kw">Transfer-Encoding</span>: chunked
<span class="c-kw">Content-Type</span>: text/plain
<span class="c-val">1a</span> <span class="c-comment"># 26 bytes en hex</span>
Premier morceau de texte
<span class="c-val">10</span> <span class="c-comment"># 16 bytes</span>
deuxième morceau
<span class="c-val">0</span> <span class="c-comment"># fin</span></div>
<div class="info-box red" style="margin-top:.5rem;font-size:.82rem">
<strong>HTTP/2 & 3 :</strong> chunked supprimé — le multiplexing rend ça inutile, le stream lui-même gère la fin.
</div>
</div>
</div>
</section>
<!-- MÉTHODES -->
<section id="methods">
<h2>Méthodes HTTP</h2>
<p style="color:var(--muted);margin-bottom:1rem">Cliquez sur une méthode pour voir le détail</p>
<div class="grid g3">
<div class="card method-card clickable" style="background:#16a34a" onclick="showMethod('get')">GET</div>
<div class="card method-card clickable" style="background:#2563eb" onclick="showMethod('post')">POST</div>
<div class="card method-card clickable" style="background:#ea580c" onclick="showMethod('put')">PUT</div>
<div class="card method-card clickable" style="background:#a855f7" onclick="showMethod('patch')">PATCH</div>
<div class="card method-card clickable" style="background:#dc2626" onclick="showMethod('delete')">DELETE</div>
<div class="card method-card clickable" style="background:#64748b" onclick="showMethod('head')">HEAD</div>
<div class="card method-card clickable" style="background:#0891b2" onclick="showMethod('options')">OPTIONS</div>
<div class="card method-card clickable" style="background:#7c3aed" onclick="showMethod('connect')">CONNECT</div>
<div class="card method-card clickable" style="background:#be185d" onclick="showMethod('trace')">TRACE</div>
</div>
<div id="method-detail" style="display:none;margin-top:1rem;padding:1rem;background:var(--card);border:2px solid var(--acc);border-radius:.75rem"></div>
<div class="card" style="margin-top:1rem">
<h4>Propriétés des méthodes</h4>
<table>
<tr><th>Méthode</th><th>Safe</th><th>Idempotent</th><th>Cacheable</th><th>Body request</th><th>Body response</th></tr>
<tr><td>GET</td><td>✓</td><td>✓</td><td>✓</td><td>Non</td><td>Oui</td></tr>
<tr><td>HEAD</td><td>✓</td><td>✓</td><td>✓</td><td>Non</td><td>Non</td></tr>
<tr><td>OPTIONS</td><td>✓</td><td>✓</td><td>Non</td><td>Optionnel</td><td>Oui</td></tr>
<tr><td>POST</td><td>✗</td><td>✗</td><td>Rarement</td><td>Oui</td><td>Oui</td></tr>
<tr><td>PUT</td><td>✗</td><td>✓</td><td>Non</td><td>Oui</td><td>Oui</td></tr>
<tr><td>PATCH</td><td>✗</td><td>✗</td><td>Non</td><td>Oui</td><td>Oui</td></tr>
<tr><td>DELETE</td><td>✗</td><td>✓</td><td>Non</td><td>Optionnel</td><td>Optionnel</td></tr>
</table>
<div class="info-box blue" style="margin-top:.5rem;font-size:.82rem">
<strong>Safe</strong> = aucun effet de bord (lecture seule). <strong>Idempotent</strong> = N fois = 1 fois (état final identique).
</div>
</div>
</section>
<!-- CODES DE STATUT -->
<section id="status">
<h2>Codes de statut</h2>
<p style="color:var(--muted);margin-bottom:1rem">Cliquez sur un code pour son explication</p>
<h3>1xx — Informational</h3>
<div class="status-grid">
<div class="status-cell status-1xx" onclick="showStatus('100')">100 Continue</div>
<div class="status-cell status-1xx" onclick="showStatus('101')">101 Switching</div>
<div class="status-cell status-1xx" onclick="showStatus('103')">103 Early Hints</div>
</div>
<h3>2xx — Success</h3>
<div class="status-grid">
<div class="status-cell status-2xx" onclick="showStatus('200')">200 OK</div>
<div class="status-cell status-2xx" onclick="showStatus('201')">201 Created</div>
<div class="status-cell status-2xx" onclick="showStatus('202')">202 Accepted</div>
<div class="status-cell status-2xx" onclick="showStatus('204')">204 No Content</div>
<div class="status-cell status-2xx" onclick="showStatus('206')">206 Partial</div>
</div>
<h3>3xx — Redirection</h3>
<div class="status-grid">
<div class="status-cell status-3xx" onclick="showStatus('301')">301 Moved</div>
<div class="status-cell status-3xx" onclick="showStatus('302')">302 Found</div>
<div class="status-cell status-3xx" onclick="showStatus('304')">304 Not Modified</div>
<div class="status-cell status-3xx" onclick="showStatus('307')">307 Temp Redirect</div>
<div class="status-cell status-3xx" onclick="showStatus('308')">308 Perm Redirect</div>
</div>
<h3>4xx — Client Error</h3>
<div class="status-grid">
<div class="status-cell status-4xx" onclick="showStatus('400')">400 Bad Request</div>
<div class="status-cell status-4xx" onclick="showStatus('401')">401 Unauthorized</div>
<div class="status-cell status-4xx" onclick="showStatus('403')">403 Forbidden</div>
<div class="status-cell status-4xx" onclick="showStatus('404')">404 Not Found</div>
<div class="status-cell status-4xx" onclick="showStatus('405')">405 Method NA</div>
<div class="status-cell status-4xx" onclick="showStatus('409')">409 Conflict</div>
<div class="status-cell status-4xx" onclick="showStatus('410')">410 Gone</div>
<div class="status-cell status-4xx" onclick="showStatus('418')">418 Teapot</div>
<div class="status-cell status-4xx" onclick="showStatus('422')">422 Unprocessable</div>
<div class="status-cell status-4xx" onclick="showStatus('429')">429 Too Many</div>
</div>
<h3>5xx — Server Error</h3>
<div class="status-grid">
<div class="status-cell status-5xx" onclick="showStatus('500')">500 Internal</div>
<div class="status-cell status-5xx" onclick="showStatus('501')">501 Not Implem</div>
<div class="status-cell status-5xx" onclick="showStatus('502')">502 Bad Gateway</div>
<div class="status-cell status-5xx" onclick="showStatus('503')">503 Unavailable</div>
<div class="status-cell status-5xx" onclick="showStatus('504')">504 Gateway TO</div>
</div>
<div id="status-detail" style="display:none;margin-top:1rem;padding:1rem;background:var(--card);border:2px solid var(--acc);border-radius:.75rem"></div>
</section>
<!-- HEADERS -->
<section id="headers">
<h2>Headers HTTP clés</h2>
<div class="grid g2">
<div class="card">
<h4>Headers de requête courants</h4>
<table>
<tr><th>Header</th><th>Rôle</th></tr>
<tr><td>Host</td><td>Domaine cible (obligatoire HTTP/1.1)</td></tr>
<tr><td>User-Agent</td><td>Identifie le client</td></tr>
<tr><td>Accept</td><td>Types MIME acceptés en réponse</td></tr>
<tr><td>Accept-Language</td><td>Langues préférées</td></tr>
<tr><td>Accept-Encoding</td><td>Compression supportée (gzip, br)</td></tr>
<tr><td>Authorization</td><td>Credentials (Bearer, Basic…)</td></tr>
<tr><td>Cookie</td><td>Cookies envoyés par le serveur</td></tr>
<tr><td>Referer</td><td>URL d'origine du lien</td></tr>
<tr><td>Origin</td><td>Origine pour CORS</td></tr>
<tr><td>If-Modified-Since</td><td>Cache conditionnel par date</td></tr>
<tr><td>If-None-Match</td><td>Cache conditionnel par ETag</td></tr>
<tr><td>Range</td><td>Demander une portion (bytes=0-999)</td></tr>
</table>
</div>
<div class="card">
<h4>Headers de réponse courants</h4>
<table>
<tr><th>Header</th><th>Rôle</th></tr>
<tr><td>Content-Type</td><td>Type MIME du corps</td></tr>
<tr><td>Content-Length</td><td>Taille en octets</td></tr>
<tr><td>Content-Encoding</td><td>Compression appliquée</td></tr>
<tr><td>Cache-Control</td><td>Directives de cache</td></tr>
<tr><td>ETag</td><td>Tag unique de la version</td></tr>
<tr><td>Last-Modified</td><td>Date de dernière modification</td></tr>
<tr><td>Set-Cookie</td><td>Cookie à stocker</td></tr>
<tr><td>Location</td><td>URL de redirection</td></tr>
<tr><td>WWW-Authenticate</td><td>Méthode d'auth requise</td></tr>
<tr><td>Vary</td><td>Headers influençant le cache</td></tr>
<tr><td>Retry-After</td><td>Délai avant retry</td></tr>
<tr><td>Server</td><td>Logiciel serveur</td></tr>
</table>
</div>
</div>
<div class="grid g2" style="margin-top:1rem">
<div class="card">
<h4>Cache-Control — directives</h4>
<div class="codeblk"><span class="c-comment"># Directives serveur (réponse)</span>
Cache-Control: <span class="c-kw">public</span> <span class="c-comment"># cacheable par tous</span>
Cache-Control: <span class="c-kw">private</span> <span class="c-comment"># cache navigateur seul</span>
Cache-Control: <span class="c-kw">no-store</span> <span class="c-comment"># jamais cacher</span>
Cache-Control: <span class="c-kw">no-cache</span> <span class="c-comment"># revalider à chaque fois</span>
Cache-Control: <span class="c-kw">max-age</span>=<span class="c-val">3600</span> <span class="c-comment"># 1h de fraîcheur</span>
Cache-Control: <span class="c-kw">s-maxage</span>=<span class="c-val">86400</span> <span class="c-comment"># spécifique CDN/proxy</span>
Cache-Control: <span class="c-kw">immutable</span> <span class="c-comment"># jamais ne change</span>
Cache-Control: <span class="c-kw">stale-while-revalidate</span>=<span class="c-val">60</span>
<span class="c-comment"># sert le stale 60s pendant qu'on refresh en background</span>
<span class="c-comment"># Directives client (requête)</span>
Cache-Control: <span class="c-kw">no-cache</span> <span class="c-comment"># force revalidation</span>
Cache-Control: <span class="c-kw">only-if-cached</span> <span class="c-comment"># offline mode</span></div>
</div>
<div class="card">
<h4>Headers de sécurité</h4>
<div class="codeblk"><span class="c-kw">Strict-Transport-Security</span>: max-age=<span class="c-val">31536000</span>;
includeSubDomains; preload
<span class="c-kw">Content-Security-Policy</span>: default-src <span class="c-str">'self'</span>;
script-src <span class="c-str">'self'</span> <span class="c-str">'nonce-RANDOM'</span>
<span class="c-kw">X-Content-Type-Options</span>: nosniff
<span class="c-kw">X-Frame-Options</span>: DENY
<span class="c-kw">Referrer-Policy</span>: strict-origin-when-cross-origin
<span class="c-kw">Permissions-Policy</span>: geolocation=(), camera=()
<span class="c-kw">Cross-Origin-Opener-Policy</span>: same-origin
<span class="c-kw">Cross-Origin-Embedder-Policy</span>: require-corp
<span class="c-kw">Cross-Origin-Resource-Policy</span>: same-site</div>
</div>
</div>
</section>
<!-- HTTP/2 -->
<section id="h2">
<h2>HTTP/2</h2>
<div class="grid g2">
<div class="card">
<h4>Apports majeurs</h4>
<div class="info-box green"><strong>Multiplexing :</strong> plusieurs requêtes/réponses concurrentes sur une seule connexion TCP, sans head-of-line blocking au niveau HTTP</div>
<div class="info-box blue" style="margin-top:.4rem"><strong>Binary framing :</strong> chaque message est découpé en frames typées (HEADERS, DATA, SETTINGS, GOAWAY…)</div>
<div class="info-box purple" style="margin-top:.4rem"><strong>HPACK :</strong> compression des headers avec table dynamique côté client et serveur</div>
<div class="info-box orange" style="margin-top:.4rem"><strong>Server Push :</strong> serveur envoie des ressources liées sans attendre la demande (déprécié dans Chrome car peu efficace)</div>
<div class="info-box" style="margin-top:.4rem"><strong>Priorisation :</strong> stream priorities pour indiquer l'importance relative des requêtes</div>
</div>
<div class="card">
<h4>Types de frames</h4>
<table>
<tr><th>Frame</th><th>Type</th><th>Rôle</th></tr>
<tr><td>DATA</td><td>0x0</td><td>Payload du message</td></tr>
<tr><td>HEADERS</td><td>0x1</td><td>Métadonnées</td></tr>
<tr><td>PRIORITY</td><td>0x2</td><td>Priorisation</td></tr>
<tr><td>RST_STREAM</td><td>0x3</td><td>Annuler un stream</td></tr>
<tr><td>SETTINGS</td><td>0x4</td><td>Paramètres de connexion</td></tr>
<tr><td>PUSH_PROMISE</td><td>0x5</td><td>Annonce un push</td></tr>
<tr><td>PING</td><td>0x6</td><td>Keepalive, RTT</td></tr>
<tr><td>GOAWAY</td><td>0x7</td><td>Fermeture gracieuse</td></tr>
<tr><td>WINDOW_UPDATE</td><td>0x8</td><td>Flow control</td></tr>
<tr><td>CONTINUATION</td><td>0x9</td><td>Suite de HEADERS</td></tr>
</table>
</div>
</div>
<div class="grid g2" style="margin-top:1rem">
<div class="card">
<h4>HTTP/2 over TLS — négociation ALPN</h4>
<div class="codeblk"><span class="c-comment"># ClientHello TLS contient l'extension ALPN</span>
ClientHello:
extension: application_layer_protocol_negotiation
protocols: <span class="c-str">"h2"</span>, <span class="c-str">"http/1.1"</span>
<span class="c-comment"># Le serveur choisit dans sa ServerHello</span>
ServerHello:
extension: alpn
protocol: <span class="c-str">"h2"</span>
<span class="c-comment"># Une fois TLS établi, démarrage HTTP/2</span>
Client → Server: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
(magic preface 24 bytes)
Both ↔ Both: SETTINGS frames</div>
</div>
<div class="card">
<h4>Limitations HTTP/2</h4>
<div class="info-box red">
<strong>Head-of-line blocking au niveau TCP :</strong> bien que HTTP/2 multiplex au niveau application, si un paquet TCP est perdu, TOUS les streams bloquent jusqu'à retransmission.
</div>
<div class="info-box orange" style="margin-top:.4rem">
<strong>Server Push :</strong> peu utilisé en pratique car difficile à gérer correctement (le serveur ne sait pas ce que le client a déjà en cache). Remplacé par <strong>103 Early Hints</strong>.
</div>
<div class="info-box" style="margin-top:.4rem">
<strong>Coalescing :</strong> HTTP/2 peut réutiliser une connexion pour plusieurs origines si elles partagent le même certificat et IP. Optimisation invisible.
</div>
</div>
</div>
</section>
<!-- HTTP/3 & QUIC -->
<section id="h3">
<h2>HTTP/3 & QUIC</h2>
<div class="grid g2">
<div class="card">
<h4>QUIC — le protocole sous-jacent</h4>
<p style="font-size:.88rem;margin-bottom:.5rem">QUIC (Quick UDP Internet Connections, RFC 9000) est un protocole de transport conçu par Google, désormais standardisé par l'IETF. Bâti sur UDP, il intègre la sécurité TLS 1.3 et le multiplexing.</p>
<div class="info-box green">
<strong>Avantages clés :</strong><br>
• Handshake combiné TCP+TLS en 1-RTT (ou 0-RTT pour les sessions reprises)<br>
• Streams indépendants : la perte d'un paquet ne bloque qu'un stream<br>
• Migration de connexion : changer de Wi-Fi → 4G sans casser la session (Connection ID)<br>
• Chiffrement obligatoire du transport (même les headers de transport)<br>
• Évolutif sans coordination kernel (userspace)
</div>
<div class="info-box red" style="margin-top:.4rem">
<strong>Inconvénients :</strong> CPU usage plus élevé (chiffrement intégré), middleboxes parfois bloquent UDP, debug plus complexe.
</div>
</div>
<div class="card">
<h4>HTTP/3 — quoi de neuf vs HTTP/2</h4>
<table>
<tr><th>Aspect</th><th>HTTP/2</th><th>HTTP/3</th></tr>
<tr><td>Transport</td><td>TCP</td><td>QUIC/UDP</td></tr>
<tr><td>TLS</td><td>Séparé</td><td>Intégré dans QUIC</td></tr>
<tr><td>HoL blocking</td><td>Possible (TCP)</td><td>Aucun</td></tr>
<tr><td>Compression headers</td><td>HPACK</td><td>QPACK</td></tr>
<tr><td>Streams</td><td>Logique HTTP/2</td><td>Natifs QUIC</td></tr>
<tr><td>0-RTT</td><td>Via TLS 1.3</td><td>Natif</td></tr>
<tr><td>Connection ID</td><td>Non (tuple 4)</td><td>Oui (mobile-friendly)</td></tr>
</table>
<div class="info-box blue" style="margin-top:.5rem;font-size:.82rem">
<strong>QPACK</strong> évite la dépendance ordonnée de HPACK : streams indépendants peuvent décoder leurs headers sans attendre les autres.
</div>
</div>
</div>
<div class="grid g2" style="margin-top:1rem">
<div class="card">
<h4>Découverte HTTP/3 — Alt-Svc</h4>
<div class="codeblk"><span class="c-comment"># Le serveur HTTP/2 annonce HTTP/3 dispo</span>
HTTP/2 200 OK
Alt-Svc: h3=<span class="c-str">":443"</span>; ma=<span class="c-val">86400</span>,
h3-29=<span class="c-str">":443"</span>; ma=<span class="c-val">86400</span>
<span class="c-comment"># Le client retient et passe à HTTP/3 pour les
# prochaines requêtes (selon UDP non bloqué)</span>
<span class="c-comment"># Alternative : DNS HTTPS Resource Record (RFC 9460)</span>
example.com. <span class="c-val">3600</span> IN HTTPS <span class="c-val">1</span> . alpn=<span class="c-str">"h3,h2"</span>
ipv4hint=<span class="c-val">192.0.2.1</span>
ipv6hint=<span class="c-val">2001:db8::1</span></div>
</div>
<div class="card">
<h4>0-RTT</h4>
<div class="info-box">
<strong>0-RTT (Zero Round-Trip Time)</strong> : un client qui a déjà parlé au serveur peut envoyer ses données dans le tout premier paquet, sans attendre la fin du handshake.
</div>
<div class="info-box red" style="margin-top:.4rem;font-size:.82rem">
<strong>Risque replay :</strong> les paquets 0-RTT peuvent être rejoués. Limiter aux requêtes idempotentes (GET, HEAD), jamais aux POST de paiement par exemple.
</div>
<div class="info-box green" style="margin-top:.4rem;font-size:.82rem">
<strong>Gain :</strong> jusqu'à -200 ms sur la première requête sur mobile. Énorme pour le P50 de chargement.
</div>
</div>
</div>
<div class="card" style="margin-top:1rem">
<h4>Configuration nginx pour HTTP/3</h4>
<div class="codeblk"><span class="c-kw">server</span> {
<span class="c-kw">listen</span> <span class="c-val">443</span> <span class="c-kw">ssl</span>; <span class="c-comment"># HTTP/1.1 et HTTP/2 sur TCP/443</span>
<span class="c-kw">listen</span> <span class="c-val">443</span> <span class="c-kw">quic reuseport</span>; <span class="c-comment"># HTTP/3 sur UDP/443</span>
<span class="c-kw">http2</span> <span class="c-kw">on</span>;
<span class="c-kw">ssl_certificate</span> /etc/nginx/cert.pem;
<span class="c-kw">ssl_certificate_key</span> /etc/nginx/key.pem;
<span class="c-kw">ssl_protocols</span> TLSv1.3;
<span class="c-comment"># Annoncer HTTP/3 aux clients en HTTP/2</span>
<span class="c-kw">add_header</span> Alt-Svc <span class="c-str">'h3=":443"; ma=86400'</span>;
<span class="c-kw">server_name</span> example.com;
}</div>
</div>
</section>
<!-- QUIZ -->
<section id="quiz">
<h2>Quiz — HTTP</h2>
<div id="quiz-container"></div>
<div id="quiz-score"></div>
</section>
<script>
function showTab(id){document.querySelectorAll('section').forEach(s=>s.classList.remove('active'));document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));document.getElementById(id).classList.add('active');event.target.classList.add('active')}
const methodData={
get:{name:'GET',color:'#16a34a',desc:'Récupère une ressource. Sans effet de bord (safe), idempotente, cacheable. Le corps de requête est techniquement autorisé mais ignoré en pratique.',ex:'GET /api/users/42 HTTP/1.1'},
post:{name:'POST',color:'#2563eb',desc:'Crée une nouvelle ressource ou exécute une action. Non idempotente : POST appelé 2 fois crée potentiellement 2 ressources.',ex:'POST /api/users\\nContent-Type: application/json\\n\\n{"name":"Alice"}'},
put:{name:'PUT',color:'#ea580c',desc:'Remplace entièrement une ressource. Idempotente : PUT N fois = PUT 1 fois (même état final). Le client fournit l\'URL complète.',ex:'PUT /api/users/42 HTTP/1.1'},
patch:{name:'PATCH',color:'#a855f7',desc:'Modification partielle. Non idempotente par défaut (sauf si la patch est elle-même idempotente). RFC 6902 JSON Patch ou JSON Merge Patch (RFC 7396).',ex:'PATCH /api/users/42\\nContent-Type: application/json-patch+json'},
delete:{name:'DELETE',color:'#dc2626',desc:'Supprime la ressource. Idempotente : supprimer 2 fois reste "supprimé" (404 au 2e appel mais état final identique).',ex:'DELETE /api/users/42 HTTP/1.1'},
head:{name:'HEAD',color:'#64748b',desc:'Comme GET mais retourne uniquement les headers. Utile pour vérifier l\'existence, la taille (Content-Length) ou la fraîcheur (Last-Modified) sans télécharger.',ex:'HEAD /large-file.zip HTTP/1.1'},
options:{name:'OPTIONS',color:'#0891b2',desc:'Demande les capacités du serveur (méthodes supportées). Utilisé par CORS pour les requêtes preflight (avant POST/PUT cross-origin).',ex:'OPTIONS /api/users HTTP/1.1\\nAccess-Control-Request-Method: POST'},
connect:{name:'CONNECT',color:'#7c3aed',desc:'Établit un tunnel TCP via un proxy. Utilisé pour HTTPS à travers un proxy HTTP (transformation en proxy TCP transparent).',ex:'CONNECT example.com:443 HTTP/1.1'},
trace:{name:'TRACE',color:'#be185d',desc:'Diagnostique. Le serveur renvoie la requête reçue (echo). Désactivée partout en production pour des raisons de sécurité (Cross-Site Tracing).',ex:'TRACE / HTTP/1.1'}
};
function showMethod(m){const d=methodData[m];const p=document.getElementById('method-detail');p.style.display='block';p.innerHTML=`<h3 style="color:${d.color}">${d.name}</h3><p style="margin:.5rem 0">${d.desc}</p><div class="codeblk">${d.ex.replace(/\\n/g,'\n')}</div>`}
const statusData={
'100':'Continue : le serveur accepte la requête après Expect: 100-continue. Permet à un client de tester avant d\'envoyer un gros body.',
'101':'Switching Protocols : passage à un autre protocole (typiquement WebSocket via Upgrade).',
'103':'Early Hints : pendant que le serveur prépare la vraie réponse, il envoie des hints (Link preload). Remplace le server push HTTP/2.',
'200':'OK : la requête a réussi. Réponse standard pour GET.',
'201':'Created : ressource créée avec succès (POST, PUT). Devrait inclure Location: vers la nouvelle ressource.',
'202':'Accepted : la requête est acceptée mais traitée de façon asynchrone. Réponse pour les jobs en file.',
'204':'No Content : succès sans corps. Idéal pour DELETE ou PUT silencieux.',
'206':'Partial Content : réponse à une requête Range. Utilisé pour le streaming, reprise de téléchargement.',
'301':'Moved Permanently : la ressource a changé d\'URL définitivement. Les moteurs de recherche transfèrent le PageRank.',
'302':'Found : redirection temporaire (legacy). Préférer 307 pour la sémantique stricte.',
'304':'Not Modified : la ressource n\'a pas changé depuis If-Modified-Since / If-None-Match. Pas de body retourné.',
'307':'Temporary Redirect : redirection temporaire. Contrairement à 302, la méthode HTTP est préservée (un POST reste POST).',
'308':'Permanent Redirect : 301 mais préserve la méthode.',
'400':'Bad Request : la requête est mal formée (JSON invalide, syntaxe incorrecte).',
'401':'Unauthorized : pas authentifié OU credentials invalides. Doit inclure WWW-Authenticate.',
'403':'Forbidden : authentifié mais pas autorisé. Ne pas confondre avec 401.',
'404':'Not Found : la ressource n\'existe pas. Choix sémantique : utiliser 403 si on ne veut pas révéler l\'existence.',
'405':'Method Not Allowed : la ressource existe mais ne supporte pas cette méthode. Doit inclure Allow: GET, POST.',
'409':'Conflict : conflit avec l\'état actuel (ex: tentative de créer une ressource qui existe déjà).',
'410':'Gone : ressource intentionnellement supprimée définitivement. Plus fort que 404.',
'418':'I\'m a teapot : RFC 2324 (poisson d\'avril). Certains serveurs l\'utilisent pour signaler "je refuse cette requête" sans donner de détail.',
'422':'Unprocessable Entity : syntaxe OK mais sémantique invalide (validation métier échoue).',
'429':'Too Many Requests : rate limiting dépassé. Doit inclure Retry-After.',
'500':'Internal Server Error : erreur générique côté serveur. Le client n\'a pas à connaître les détails.',
'501':'Not Implemented : le serveur ne reconnaît pas la méthode HTTP utilisée.',
'502':'Bad Gateway : un proxy/reverse-proxy a reçu une réponse invalide d\'un upstream.',
'503':'Service Unavailable : surcharge ou maintenance. Doit inclure Retry-After.',
'504':'Gateway Timeout : un proxy a attendu trop longtemps un upstream.'
};
function showStatus(code){const p=document.getElementById('status-detail');p.style.display='block';p.innerHTML=`<h3>${code}</h3><p style="margin-top:.5rem">${statusData[code]||''}</p>`}
const quizData=[
{q:"Sur quel protocole de transport repose HTTP/3 ?",opts:["TCP","UDP via QUIC","SCTP","TCP+TLS"],a:1},
{q:"Qu'est-ce que le head-of-line blocking évité par HTTP/3 ?",opts:["Une attaque DDoS","La perte d'un paquet bloque tous les streams en HTTP/2 (TCP)","Un type de cache","Une limite de connexion"],a:1},
{q:"Quelle méthode HTTP est SAFE et IDEMPOTENTE ?",opts:["POST","PUT","GET","PATCH"],a:2},
{q:"À quoi sert le header Alt-Svc ?",opts:["À chiffrer la réponse","À annoncer qu'une autre version (ex: HTTP/3) est disponible","À configurer le cache","À gérer les cookies"],a:1},
{q:"Quel code de statut indique une redirection permanente qui préserve la méthode HTTP ?",opts:["301","302","307","308"],a:3},
{q:"Quelle compression de headers est utilisée par HTTP/2 ?",opts:["gzip","HPACK","QPACK","brotli"],a:1},
{q:"Quelle compression de headers est utilisée par HTTP/3 ?",opts:["HPACK","QPACK","Snappy","zstd"],a:1},
{q:"Le 0-RTT de QUIC...",opts:["Élimine totalement le chiffrement","Permet d'envoyer des données dans le premier paquet (session reprise) mais est vulnérable au replay","Améliore uniquement TCP","N'existe pas"],a:1},
{q:"Que fait le statut HTTP 103 Early Hints ?",opts:["Annule la requête","Envoie des hints de preload pendant la préparation de la vraie réponse","Indique une redirection","Demande l'authentification"],a:1},
{q:"Quelle est la différence entre 401 et 403 ?",opts:["Aucune","401 = pas authentifié, 403 = authentifié mais pas autorisé","401 = client, 403 = serveur","401 = GET, 403 = POST"],a:1},
{q:"Pourquoi la méthode TRACE est-elle désactivée en production ?",opts:["Trop lente","Risque de Cross-Site Tracing (CST)","Pas supportée par HTTP/2","Réservée au debug du serveur"],a:1},
{q:"Quel header indique le type MIME de la réponse ?",opts:["Accept","Content-Type","User-Agent","Vary"],a:1},
{q:"L'extension ALPN sert à quoi ?",opts:["Chiffrer les paquets","Négocier la version HTTP (h2, http/1.1) durant le handshake TLS","Compresser les headers","Authentifier le client"],a:1},
{q:"Que signifie 'idempotent' pour une méthode HTTP ?",opts:["Sans effet de bord","Le même appel répété N fois produit le même état final","Cacheable","Ne nécessite pas d'authentification"],a:1}
];
let answers=new Array(quizData.length).fill(null);
function buildQuiz(){const c=document.getElementById('quiz-container');c.innerHTML=quizData.map((q,i)=>`<div class="quiz-q"><p>${i+1}. ${q.q}</p>${q.opts.map((o,j)=>`<div class="quiz-opt" onclick="answer(this,${i},${j})">${o}</div>`).join('')}</div>`).join('')}
function answer(el,qi,oi){const parent=el.parentElement;if(parent.querySelector('.correct,.wrong'))return;answers[qi]=oi;const correct=quizData[qi].a;parent.querySelectorAll('.quiz-opt').forEach((o,j)=>{if(j===correct)o.classList.add('correct');else if(j===oi&&oi!==correct)o.classList.add('wrong')});if(answers.every(a=>a!==null)){const score=answers.filter((a,i)=>a===quizData[i].a).length;const sc=document.getElementById('quiz-score');sc.style.display='block';sc.innerHTML=`Score : ${score}/${quizData.length} ${score>=10?'Excellent !':score>=7?'Bien !':'À revoir'}`}}
buildQuiz();
</script>
</body>
</html>