# HTTP

## Hvad det er

HTTP, Hypertext Transfer Protocol, er de regler, en browser, en app eller et
script følger, når den beder en server om noget og får et svar tilbage. Kører
samtalen inde i en krypteret TLS-forbindelse, hedder den samme protokol HTTPS.
En udveksling består altid af to dele. Klienten sender en forespørgsel med en
metode som `GET` eller `POST`, det mål, den vil have fat i, en række
headerfelter og nogle gange en krop. Serveren svarer med en statuskode på tre
cifre, sine egne headerfelter og som regel en krop. Når en side indlæses, et
billede hentes eller ét program kalder et andet, er det den samme udveksling,
der gentages.

Specifikationen kalder protokollen tilstandsløs. Hver forespørgsel skal
indeholde alt, hvad serveren behøver for at forstå den, og protokollen husker
intet fra den ene udveksling til den næste. Login, indkøbskurve og sessioner
bygges ovenpå, typisk med cookies: et headerfelt, serveren sætter én gang, og
som klienten sender med på alle senere forespørgsler.

Ordforrådet er lille, men præcist. `GET` og `HEAD` er sikre metoder; de beder om
en repræsentation og forventes ikke at ændre noget. `PUT` og `DELETE` er
idempotente, så den samme forespørgsel sendt to gange efterlader serveren i
samme tilstand som én gang. `POST` lover ingen af delene. Statuskoderne er
grupperet efter første ciffer: 2xx betyder succes, 3xx omdirigering, 4xx en fejl
i forespørgslen og 5xx en fejl på serveren. En klient, der møder en kode, den
ikke kender, ved derfor alligevel nogenlunde, hvad der skete.

Betydningen har ligget fast, mens formatet på ledningen er skiftet ud to gange.
HTTP/1.1 skriver beskederne som tekstlinjer over en TCP-forbindelse, og hver
forbindelse leverer sine svar ét ad gangen og i rækkefølge. HTTP/2 beholder
metoder og felter, men pakker dem i binære rammer og fletter mange
forespørgsler sammen på én forbindelse. HTTP/3 sender de samme rammer over
QUIC, en transport bygget på UDP, så en tabt pakke kun forsinker den strøm, den
hørte til. Siden 2022 har IETF udgivet semantikken i ét dokument og hver
versions trådformat i et andet.

HTTP eller HTTPS er altså ikke et valg mellem to protokoller. HTTPS er HTTP med
TLS nedenunder, som krypterer udvekslingen og beviser serverens navn. WebSocket
begynder som en almindelig HTTP-forespørgsel og opgraderer derefter forbindelsen
til en kanal, hvor begge parter kan sende, og gRPC er en konvention for
fjernkald, der kører oven på HTTP/2.

## Hvorfor det betyder noget

Stort set alle systemer, der taler med andre over internettet, bruger allerede
HTTP. Det interessante valg er derfor, om man bruger det, protokollen
definerer, eller behandler den som et rør og bygger resten selv.

Der er god grund til at bruge den fuldt ud, for en lang kæde af software forstår
allerede ordforrådet. Et klientbibliotek må gerne prøve en `GET` igen, når den
løber tør for tid, men skal lade en `POST` være, og det samme gælder en proxy
undervejs. En cache må gemme et svar mærket `max-age=600` i ti minutter og
give det videre til den næste læser uden at spørge serveren. Overvågning tæller
5xx-svar som fejl uden at få det at vide. Et API, der sender hvert kald som en
`POST` til én adresse og melder fejl inde i et 200-svar, giver afkald på alt
det, og hver klient må lære netop det API's private regler.

Modellen har sine grænser. Forespørgsel og svar passer til en klient, der ved,
hvad den vil have, og langt dårligere til en server, der har nyt at fortælle.
Derfor går live-opdateringer som regel over WebSocket, server-sent events eller
gentagne forespørgsler.

Ukrypteret HTTP på det åbne internet er et kortere spørgsmål. Browsere markerer
det som usikkert, alle på vejen kan læse og ændre indholdet, og certifikater er
gratis, så offentlig trafik hører hjemme på HTTPS. Valget mellem HTTP/1.1,
HTTP/2 og HTTP/3 er mest en indstilling på serveren eller kanten foran den.
Applikationens kode forbliver den samme, fordi hver forespørgsel betyder det
samme i alle tre.

## Sådan ser det ud i praksis

Det tydeligste eksempel her på sitet er et API, der blev flyttet over på et
framework, som er bygget om HTTP's egen form.
[HTTP-API'et flyttede fra Fiber til Huma v2](/da/portfolio/migrated-the-http-api-from-fiber-to-huma-60/),
der kører oven på Fiber. Hvert endpoint erklæres med structs for input og
output, og valideringen kommer fra typerne i stedet for fra håndskrevne tjek.
Ud af de samme erklæringer kommer en OpenAPI-beskrivelse, og et tjek i CI
sammenligner de ruter, serveren registrerer, med dem, beskrivelsen annoncerer.
Ved 649 paths og 760 operationer er pariteten 100 %, og den holdes, fordi en
rute uden beskrivelse får buildet til at fejle. Præcis to endpoints blev på rå
Fiber, nemlig WebSocket-endpointene, hvor Humas model med forespørgsel og svar
ikke passer.

Sitet bruger selv indholdsforhandling. Hver side udgives både som HTML og som
Markdown, og én måde at få Markdown-udgaven på er at
[sende `Accept: text/markdown` og lade serveren vælge](/da/cli/). Samme side
forklarer, hvorfor onion-adressen med vilje er almindelig `http://`: adressen
er tjenestens egen offentlige nøgle, så kredsløbet er godkendt og krypteret,
før der overhovedet bliver talt HTTP.

HTTP dukker også op som værktøj i infrastrukturkode. Da en ekstra server blev
sat op,
[blev begge udbyderes firewalls skrevet direkte mod deres REST-API'er](/da/portfolio/provisioned-a-second-server-from-code-123/)
gennem en generisk HTTP-opgave i stedet for en leverandørsamling. Det fjernede
en afhængighed og dens versionsdrift mod at skrive hver forespørgsels form i
hånden. Siden nævner også en anden pris: den generiske HTTP-opgave erklærer
ingen understøttelse af check-tilstand, så en prøvekørsel af det play, der
opretter maskinen, springer hver opgave i det over. Prøven køres derfor mod
firewall-playet.

## Hvor det går galt

De fleste HTTP-fejl opstår, når et ord i ordforrådet bruges til at betyde noget
andet end det, resten af kæden hører.

### En fejl, der ligner en succes

Et API svarer 200 og lægger `"error": "card declined"` i kroppen. En cache kan
gemme svaret, dashboards tæller det som en succes, og klientens logik for nye
forsøg går aldrig i gang. Send den statuskode, der passer, 4xx eller 5xx, og
beskriv problemet i kroppen. RFC 9457 definerer en JSON-form til netop det,
leveret som `application/problem+json`.

### Et link, der sletter

En `GET` ændrer tilstand, for eksempel et afmeldings- eller slettelink i en
mail. Crawlere, linkforhåndsvisninger i chatapps, sikkerhedsscannere og
browserens forhåndsindlæsning følger alle links på egen hånd, og hver af dem
kan udløse handlingen. Det, der ændrer data, hører hjemme bag `POST`, `PUT`
eller `DELETE`, eventuelt med en bekræftelsesside, hvis linket skal findes.

### En betaling, der trækkes to gange

Klienten sender en `POST`, forbindelsen ryger, før svaret kommer, og klienten
prøver igen. Serveren havde allerede behandlet den første. HTTP giver klienten
ingen måde at skelne de to tilfælde på, så API'et må gøre det: tag imod en
idempotensnøgle i et headerfelt og returnér det gemte resultat, når nøglen
kommer igen, eller lad klienten vælge den nye ressources id og oprette den med
`PUT`.

### En formular, der bliver til en GET

Serveren svarer på en `POST` med 301 eller 302 for at sende den videre. Af
historiske grunde tillader specifikationen, at brugeragenten skifter metoden til
`GET` ved netop de to koder, og mange gør det, så kroppen forsvinder undervejs.
Skal metoden overleve omdirigeringen, er 307 og 308 de rigtige koder.

### Den forkerte udgave fra en cache

Én URL leverer HTML eller Markdown, dansk eller engelsk, afhængigt af
forespørgslens headerfelter. En cache, der intet har fået at vide, gemmer den
udgave, den så først, og giver den videre til den næste læser. Svaret skal
nævne de felter, det afhænger af, for eksempel `Vary: Accept`, så cachen
gemmer én kopi pr. udgave frem for én pr. URL.

## Læs mere

- [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110) — metoder,
  statuskoder og headerfelter, defineret én gang for alle versioner.
- [RFC 9111: HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111) — hvornår
  et gemt svar må genbruges, og hvad `Cache-Control` fortæller en cache.
- [RFC 9112: HTTP/1.1](https://www.rfc-editor.org/rfc/rfc9112) — tekstformatet
  på ledningen, forbindelser og hvordan en besked afgrænses.
- [RFC 9113: HTTP/2](https://www.rfc-editor.org/rfc/rfc9113) — binære rammer og
  mange strømme over én forbindelse.
- [RFC 9114: HTTP/3](https://www.rfc-editor.org/rfc/rfc9114) — den samme
  semantik lagt oven på QUIC-strømme.
- [RFC 9457: Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457)
  — en fast JSON-krop til at beskrive, hvad der gik galt.

<https://engineer.company/da/glossary/http/>
