Vad är cookies?

20 april 2021

Tags
http history

En kort historik om cookies med en personlig touch

När HTTP skapades för runt 30 år sedan, dvs webben/nätet, utgick man från en tämligen enkel modell. En webbläsare bad en server att skicka innehållet i en (HTML) fil och sen var det inte mer med det — dvs operationen vara helt state-less. Varje anrop från klient till server kom som en "blixt från klar himmel", om man säger så.

Det här att HTTP var helt state-less, blev snabbt ganska kruxigt när man behövde spara information mellan en serie anrop, såsom innehållet i en varukorg. Här fanns det två separata, men relaterade, tekniska problem att lösa. Det första var att särskilja i en skur av anrop, vilka av dessa som kom från en viss användare, dvs rent tekniskt en viss webb(läsar)klient. Det andra problemet gällde var och hur skulle denna information sparas.

Våren 1995 implementerade jag en av de allra första bokhandlarna på nätet. Jag programmerade detta i Perl på en PC med Slackware Linux och driftade på en inhyrd SUN Sparcstation hos en av de tidigt ute ISP:erna i Stockholm. Webbservern var Apache HTTPd med CGI koppling till Perl skriptet i fråga.

För varje HTTP anrop (request) fanns meta-data, såsom avsändade IP-nummer. Detta kunde jag använda för att para ihop olika anrop med en viss användare i andra änden. På denna tid satt de flesta som hade åtkomst till internet vid en Unix workstation med publikt IP-nummer. Tanken svindlar med dagens perspektiv, varje dator på nätet var direkt anropbar!

När en användare lade en bok i systemets varukorg, så skapades en textfil på servern med namnet baserat på IP-numret (och en tidsstämpel). För varje anrop som kom in, så matchades IP-numret mot samtliga filer i varukorgskatalogen på servern. Fanns det en matchade fil, så infogades innehållet i den HTML som genererades för responsen tillbaka.

Tack och lov hade man inte ännu börjat med NAT (Network Address Translation). NAT är det självklara nu för tiden och innebär att alla datorer i ett lokalt nätverk delar på en enda publik IP-adress och det är routerns uppgift att översätta mellan publik och internt/osynligt IP-nummer.

Ett år senare skapade Netscape ett nytt sub-API för HTTP, som innebar att servern kunde skicka en response header i sitt svar (Set-Cookie: xyz). Webbläsaren kunde sen spara information på användarens dator. För varje följande anrop till samma server, bifogade klienten den sparade information (Cookie: xyz) och det var sen upp till denne att agera.

I början sparade man hela varukorgs-innehållet i en cookie, vilket givetvis förr eller senare, innebar att maxstorleken för en cookie överskreds — ooops. Lösningen blev att låta servern generera ett unikt ID som cookie-data och för varje anrop para ihop detta ID med sparad information på servern. Detta förfaringssätt kom med tiden att kallas för HTTP Session

Det här fungerade hur bra som helst under andra halvan av 1990-talet. Emellertid, blev internet alltmer populärt och vissa servrar/tjänster blev överbelastade av anrop. Då skaffade man ytterligare servrar och fördelade trafiken jämt mellan servrarna. Kruxet som nu uppstod, var att HTTP-Session data sparades på en viss server. Så om server #1 skapade en cookie och nästa anrop från samma klient skickades till server #2, så fanns inte cookie:n där.

Det här problemet kom att hanteras på tre helt olika sätt och det var upp till systemarkitekten att välja vilken lösning som skulle tillämpas. Låt oss kalla dem lösning A, B respektive C.

Lösning A; innebar att spara http-session data i en för servrarna i ett kluster gemensam databas. Nackdelen var att varje anrop bromsades upp av ett databas-anrop, vilket med tiden banade väg för olika cache-server lösningar som memcached och andra.

Lösning B; innebar att servrarna replikerade http-session data mellan varandra. Nackdelen var att webbtjänster med många och täta anrop, så blev det också kolossalt mycket intern trafik av replikerade http-session data. Som lök på laxen fanns också race-condition problematik då en server inte hunnit med att få den senaste uppdateringen av en viss http-session instans. Och, ja visst ja..., den interna replikeringstrafiken växte med kvadraten på antalet servrar i klustret.

Lösning C; innebar att man byggde en smartare last-balanserare, som inspekterade varje http request och skickade alla anrop för en viss cookie (dvs http-session ID) till samma server. Det här kallas för sticky session. Nackdelen var att om en server föll ifrån, så fanns det plötsligt ingen som svarade i andra änden. Så då behövde man implementera fail-over baserat på en lågintensiv variant av lösning A och/eller B.

Så ingen av de tre lösningarna är optimal utan man får väga för- och nackdelar. Med tiden utvecklades helt andra system-arkitekturer baserade på AJAX och sedermera SPA, för vilka jag får orsak att återkomma till i ett annat inlägg.