#Niezbędnik Juniora. Protokół HTTP

By 30 June 2015Niezbędnik Juniora

Ten wpis poświę­cony będzie pro­tokołowi HTTP — czyli temu, jak przeglą­dar­ka komu­niku­je się z ser­w­erem.Pod­stawy pro­tokołu porusza­l­iśmy już w lekcji 9, omaw­ia­jąc adno­tac­je Spring MVC. Dzisi­aj powiemy sobie o nim znacznie więcej, dowiesz się także, co się dzieje po wpisa­niu w przeglą­darkę adresu www.kobietydokodu.pl ;)

Czym jest protokół HTTP

HTTP to skrót od Hyper­text Trans­fer Pro­to­col i jest to główny pro­tokół uży­wany współcześnie w przegladark­ach. Jest to pro­tokół bezs­tanowy, tzn. ani ser­w­er (ani klient) nie prze­chowu­je infor­ma­cji o tym, jakie były wcześniej zapy­ta­nia pomiędzy określonym ser­w­erem i klien­tem oraz nie posi­a­da stanu wewnętrznego. Powodu­je to, że każde zapy­tanie do ser­w­era trak­towane jest jako ‘nowe’, z punk­tu widzenia ser­w­era aplikacji niemożli­we do pow­iąza­nia z infor­ma­c­ja­mi np. o zal­o­gowanym użytkown­iku. Tą bezs­tanowość moż­na obe­jść, obec­nie głównie za pomocą tzw. ciasteczek (będzie o nich nieco dalej), należy jed­nak pamię­tać, że HTTP sam w sobie jest bezs­tanowy.

Zapytania HTTP

W zapy­ta­ni­ach HTTP może­my wyróżnić dwa ele­men­ty: nagłówek i ciało. Zajmi­jmy się najpierw pier­wszą częś­cią czyli nagłówkiem. Nagłówek ma min­i­mum 1 wier­sz, który określa metodę HTTP (poniżej omówimy sobie czym są metody, oraz do czego służy każ­da z nich), adres URI oraz wer­sję pro­tokołu HTTP (obec­nie korzys­tamy z wer­sji 1.1, oznaczanej w nagłówku jako HTTP/1.1 ). Przykład­owy pier­wszy wier­sz nagłówka wyglą­da nastepu­ją­co:

GET / HTTP/1.1

Gdzie:

  • GET — to nazwa metody HTTP
  • / — to tzw. URI, czyli ta część adresu, który wys­tepu­je po dome­nie
  • HTTP/1.1 — okresle­nie pro­tokołu, tutaj HTTP w wer­sji 1.1

Bezpośred­nio pod tą lin­ijką może­my (ale nie musimy) podać nagłów­ki HTTP, każdy w nowej linii. Uwa­ga! Nie może być tutaj żad­nej linii prz­er­wy! Wszys­tko, co zna­jdzie się po pier­wszej pustej linii (prz­er­wie) trak­towane jest jako ciało zapy­ta­nia.

Znim prze­jdziemy do ciała, wyjaśni­jmy sobie kil­ka kon­cep­tów.

Metoda HTTP

Metody w pro­tokole HTTP służą do roz­granicza­nia różnych czyn­noś­ci, które mamy zami­ar wykon­ać, poma­ga­ją także w pro­jek­towa­niu przeglą­darek inter­ne­towych i obsłudze zapy­tań. Początkowo o pro­tokole HTTP myślano jako o pro­tokole do obsłu­gi plików na zdal­nym ser­w­erze — taki pro­tokół pozwalał na pobranie zasobu, usunię­cie go, wysłanie na ser­w­er, jego aktu­al­iza­cję oraz pobranie metadanych. Takie znacze­nie nada­je się też metodom HTTP w przy­pad­ku API speł­ni­a­jącego założe­nia REST. Tyle z teorii i his­torii. W prak­tyce metody pozwala­ją nam ‘rozdzielać’ zapy­ta­nia trafi­a­jące pod ten sam adres — np. metody GET uży­wamy do wyświ­etle­nia for­mu­la­rza, a metody POST do jego przesła­nia — obie rzeczy może­my real­i­zować pod tym samym adresem url, np. www.kobietydokodu.pl/jakis/formularz . Poniżej zna­jdziesz pod­sumowanie metod pro­tokołu HTTP:

Meto­da Request body Response body Zas­tosowanie / opis
GET  niedoz­wolone  opcjon­al­nie Pobieranie zasobu lub jego wyświ­etle­nie, np. wyświ­etle­nie for­mu­la­rza lub strony. Para­me­try moż­na przekazywac jedynie poprzez adres (np. ?nazwa=wartosc&nazwa2=wartosc2)
POST  opcjon­alne  opcjon­al­nie Przesłanie danych zapisanych jako pary klucz-wartość do ser­w­era (np. wysłanie for­mu­la­rza, gdzie kluczem jest nazwa danego pola a wartoś­cią wpisana przez nas wartość). Meto­da ta pozwala przesyłać także pli­ki (a także wiele pli­ki oraz pary klucz-wartość jed­nocześnie). Para­me­try są przekazy­wane w ciele zapy­ta­nia, moż­na także przekazy­wać para­me­try poprzez adres (tak jak w metodzie GET)
PUT  opcjon­alne  opcjon­al­nie Przesyłanie ‘pacz­ki’ danych, np. jed­nego pliku. Meto­da ta ma pewne ograniczenia, np. nie ma możli­woś­ci łaczenia par klucz-wartość z inną przesyłaną treś­cią (np. plikiem). Obec­nie uży­wana głównie w przy­pad­ku RESTowych ser­wisów, gdzie ciałem jest np. for­mu­la­rz zapisany w postaci JSONa.
DELETE  opcjon­al­nie  opcjon­al­nie Usuwanie zasobu na ser­w­erze, z racji bez­pieczeńst­wa prak­ty­cznie zawsze jest wyłac­zona domyśl­nie. Obec­nie uży­wana głównie w przy­pad­ku RESTowych ser­wisów, wskazu­jąc, że dany zasób ma być usunię­ty (i obsługi­wany przez aplikację, a nie sam ser­w­er).
HEAD  niedoz­wolone  niedoz­wolone  Ana­log­iczny do zapy­ta­nia GET, z tym wyjątkiem, że nie zwraca ciała (zawartoś­ci). Służy do pobra­nia met­danych o zaso­bie w postaci nagłówków HTTP. Dla danego adresu zwraca same nagłów­ki.

Ist­nieją jeszcze metody OPTIONS, TRACE oraz CONNECT, ale nie mają one dużego zas­tosowa­nia z punk­tu widzenia pro­gramisty aplikacji webowych.

Pełną specy­fikację możesz znaleźć na stron­ie http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html, jest to for­mal­na definic­ja metod HTTP.

Nagłówki HTTP

Nagłów­ki HTTP spotkamy zarówno w zapy­ta­ni­ach, jak i w odpowiedzi­ach. Są one pier­wszymy lini­a­mi, odd­zielone od ciała jed­ną pustą lin­ią. Nagłów­ki są opcjon­alne — pro­tokół nie wyma­ga ich obec­noś­ci. Nagłów­ki to pewnego rodza­ju metadane i polece­nia wymieni­ane przez przeglą­darkę i ser­w­er — mogą się w nich znaleźć infor­ma­c­je takie jak rodzaj przesyłanych treś­ci (np. czy jest to obrazek czy plik JSON), sug­es­tia doty­czą­ca trak­towa­nia zawartoś­ci (czy przeglą­dar­ka ma wyświ­etlić daną treść, czy np. potrak­tować to jako pobieranie), jaki jest rozmi­ar przesyłanych danych, kiedy były mody­fikowane, jakiego rodza­ju odpowiedzi dru­ga strona się spodziewa itp.

Nagłów­ki przyj­mu­ją postać klucz-wartość, zapisy­wane w postaci:

Klucz: wartość

Najpierw jest klucz (prze­ważnie zaczy­na się dużą literą, ale nie jest to wyma­gane), następ­nie dwukropek, spac­ja oraz wartość.

W prze­waża­jącej więk­szoś­ci nagłów­ki są automaty­cznie ustaw­iane przez ser­w­er i aplikac­ja nie musi ich mody­fikować / uzu­peł­ni­ać. Są jed­nak sytu­acje, w których może­my chcieć wysłać określony nagłówek, mamy wtedy taką możli­wość.

Pełną listę stan­dar­d­owych nagłówków możesz znaleźć np. na Wikipedii, my omówimy sobie tylko te najważniejsze. Co ważne — nagłówek może mieć dowol­ną nazwę (klucz), nie musi ona być spośród tych określonych stan­dar­d­a­mi. Dzię­ki temu możli­we jest imple­men­towanie dodatkowej funkcjon­al­noś­ci pomiędzy ser­w­era­mi, które się ze sobą komu­niku­ją. Może­my też dołaczać dowolne nagłów­ki — jeśli odbior­ca wiado­moś­ci nie wie, jak je zin­ter­pre­tować, po pros­tu je zig­noru­je.

Poniższa tabela pod­sumowu­je najczęś­ciej uży­wane nagłów­ki

Nagłówek Opis Przykład
Con­tent-Type W zapy­ta­niu oraz odpowiedzi określa, jakiego typu dane są przesyłane Con­tent-Type: application/json
Con­tent-Length W zapy­ta­niu oraz odpowiedzi zaw­iera infor­ma­c­je ile danych jest przesyłanych Con­tent-Length: 20
Cook­ie W zapy­ta­niu przesyła zawartość Cook­ies prze­chowywanych dla danej wit­ryny. Może prze­chowywać wiele wartoś­ci w postaci klucz=wartość, pary odd­zielane są od siebie śred­nika­mi. Cook­ie: AcceptedCookiePolicy=1; Country=Poland;
Set-Cook­ie W odpowiedzi jest to polece­nie ser­w­era, aby przeglą­dar­ka ustaw­iła wartoś­ci Cook­ie; podob­nie jak nagłówek Cook­ie może zaw­ier­ać wiele par postaci klucz=wartość odd­zielonych śred­nika­mi Set-Cook­ie: UserID=JanNowak; SeenTutorial=1
Loca­tion W odpowiedzi instu­u­je przeglą­darkę o tym, że ma wykon­ać zapy­tanie pod inny adres. W ten sposób (w połacze­niu ze sta­tusem np. 302) w aplikacji może­my przekierowywać pod inny adres Loca­tion: http://calieminnaaplikacja.com.pl/nowawersja
Last-Mod­i­fied W odpowiedzi ser­w­er może poin­for­mować, kiedy nastąpiła ostat­nia zmi­ana zawartoś­ci. For­mat daty jest specy­ficzny dla pro­tokołu HTTP i określony w doku­men­cie RCF 7231 Last-Mod­i­fied: Tue, 15 May 2015 12:45:26 GMT
Con­tent-Dis­po­si­tion W odpowiedzi ser­w­er może poinstuować przeglą­darkę, aby zami­ast wyświelać treść, pobrała ją. Moż­na też określić nazwę, pod jaką przeglą­dar­ka powin­na zasug­erować zapisanie pliku Con­tent-Dis­po­si­tion: attach­ment; filename=“raport_roczny.pdf”
Host W zapy­ta­niu jest to nagłówek obow­iązkowy, infor­mu­je ser­w­er pod jaki adres dome­ny chce­my wysłać zapy­tanie (może to być też adres IP). Poma­ga to ser­werom obsługu­ją­cym wiele domen praw­idłowo przekierowywać zapy­ta­nia Host: www.kobietydokodu.pl
Accept W zapy­ta­niu klient może poin­for­mować ser­w­er, jakiego typu odpowiedzi akcep­tu­je. Dzię­ki temu ser­w­er może zade­cy­dować o wysła­niu odpowiedzi np. w XML a nie JSON, co ma zas­tosowanie w wielu API Accept: application/xml

Statusy HTTP (kody odpowiedzi)

Jed­nym z ele­men­tów pro­tokołu HTTP są kody odpowiedzi, zwany­mi też sta­tusa­mi. To numeryczne, trzy­cyfrowe kody, które są dołączane do odpowiedzi i syg­nal­izu­ją sta­tus odpowiedzi. Najpop­u­larniejsze i najczęś­ciej spo­tykane kody to 200 (OK, czyli wszys­tko jest w porząd­ku, zapy­tanie jest obsłużone), 302 (przekierowanie), 403 (brak dostępu) oraz 404 (nie znaleziono — stąd licz­ba 404 częs­to pojaw­ia się na stronach z infor­ma­cją, że wpisany adres nie ist­nieje).

Kodów jest mnóst­wo, najczęś­ciej wystar­czy jed­nak zna­jo­mość tych pod­sta­wowych. Co ważne, nawet nie zna­jąc kodu moż­na określić jego przy­bliżone znacze­nie na pod­staw­ie numeru. Kody są bowiem podzielone na grupy, pier­wsza cyfra kodu mówi nam, z której jest on grupy. I tak:

  • 1xx — infor­ma­cyjne, nieczęs­to moż­na spotkać, doty­czą bardziej środowiska niż samej aplikacji (np. 111 — ser­w­er odrzu­cił połacze­nie)
  • 2xx — zapy­tanie się powiodło
  • 3xx — przekierowanie, zapy­tanie należy kierować pod inny adres / ser­w­er
  • 4xx — błąd aplikacji spowodowany dzi­ałaniem użytkown­i­ka (np. wspom­ni­any 404 — nie znaleziono — czy 403 — brak dostępu lub 400 — niepoprawnie zapy­tanie)
  • 5xx — błąd ser­w­era (np. nieob­służony wyjątek w Javie)

Listę kodów moż­na znaleźć m.in. na Wikipedii. Są one także opisane i zdefin­iowane w doku­men­cie RFC 2616 z późniejszy­mi zmi­ana­mi.

W ramach cieka­wost­ki pole­cam poczy­tać o kodzie odpowiedzi 418, który jest częś­cią wielu imple­men­tacji, znalazł się także w Spring Frame­work.

Bezstanowość HTTP a ciasteczka

Jak wspom­i­nal­iśmy, pro­tokół HTTP jest bezs­tanowy, tzn nie ‘prze­chowu­je’ infor­ma­cji o tym, co dzi­ało się wcześniej. Jest to oczy­wisty prob­lem w więk­szoś­ci przy­pad­ków, kiedy korzys­tamy z narzędzi wyma­ga­ją­cych zal­o­gowa­nia się — infor­ma­c­ja o tym, jaki użytkown­ik jest zal­o­gowany musi być prze­chowywana w jak­iś sposób.

Rozwiązaniem stosowanym obec­nie na sze­ro­ka skalę są tzw. ciastecz­ka — pier­wot­nie mające postać plików tek­stowych w for­ma­cie klucz=wartość, obec­nie prze­chowywane w wewnętrznej bazie danych przeglą­dar­ki.

For­mal­nie ciastecz­ka (ang. cook­ies) to zbiór par klucz-wartość przyp­isanych do danej dome­ny (w ogól­nym przy­pad­ku; możli­we jest też utworze­nie cook­ies dla ścież­ki, np. kobietydokodu.pl/jednaaplikacja) które są wysyłane do ser­w­era z każdym zapy­taniem. Oczy­wiś­cie ze względów bez­pieczeńst­wa nie prze­chowu­je się w nich danych użytkown­i­ka, do tego służą tzw. ses­je, czyli kolekc­je danych prze­chowywane po stron­ie ser­w­era. W ciasteczkach najczęś­ciej zapisu­je się tzw. klucz sesji — unikalny iden­ty­fika­tor, na pod­staw­ie którego ser­w­er ma możli­wość pow­iąza­nia jed­nego ze zbiorów danych które prze­chowu­je z wysyła­ją­cym zapy­tanie klien­tem. Najczęś­ciej w zależnoś­ci od tech­nologii klucz ten ma określoną nazwę — w przy­pad­ku Javy stan­dard JavaEE defini­u­je go jako JSESSIONID (w Servlet API 3, które jest częś­cią stan­dar­du Java EE 6 moż­na zmienić to w pliku web.xml).

Oczy­wiś­cie więk­szość współczes­nych ser­w­erów ma dodatkowe zabez­pieczenia, sprawdza np. adres IP klien­ta pow­iązanego z sesją, żeby upewnić się że nie ma miejs­ca pró­ba przech­wyce­nia sesji (tzw. atak typu Ses­sion Hijack­ing). Ogól­nie jed­nak ciastecz­ka i jed­na zapisana w nich wartość jest pod­stawą wystar­cza­jącą do wprowadzenia stanowoś­ci w aplikac­jach webowych.

Jeśli chodzi o inne infor­ma­c­je prze­chowywane w ciasteczkach to tutaj jest mnóst­wo możli­woś­ci — od śledzenia zachowań użytkown­i­ka (np. Google Ana­lyt­ics prze­chowu­je w ciasteczku info­mac­je, żeby pow­iązać kil­ka zachowań danego klien­ta z tym samym użytkown­ikiem w ich sys­temie) poprzez prze­chowywanie infor­ma­cji o pref­er­enc­jach (np. kole­jność sor­towa­nia listy ele­men­tów aby następ­nym razem od razu ją uporząd­kować w ten sposób) po zachowa­nia jed­no­ra­zowe (np. czy użytkown­ik zaak­cep­tował poli­tykę cook­ies albo ‘ukrył’ jakąś część inter­fe­j­su).

To, o czym trze­ba jeszcze pamię­tać, to fakt, że ciastecz­ka zwyk­le ‘żyją’ dłużej niż ses­ja — w teorii infor­ma­c­je tam zapisane mogą być wieczne (w prak­tyce, żyją do kole­jnego ‘wyczyszczenia’ pamię­ci podręcznej przeglą­dar­ki przez użytkown­i­ka). Z tego powodu jest to dobre miejsce do prze­chowywa­nia infor­ma­cji jawnych, niezwiązanych z dany­mi które nasz sys­tem przetwarza, np. pref­er­encji dot. wyglą­du inter­fe­j­su użytkown­i­ka, wszel­kich infor­ma­cji poma­ga­ją­cych nam zwięk­szyć kom­fort użytkown­i­ka w korzys­ta­niu z naszego sys­te­mu.

Z punk­tu widzenia bez­pieczeńst­wa, infor­ma­c­je w ciasteczkach są przesyłane jawnie w pro­tokole HTTP, bez szyfrowa­nia i moż­na nimi sto­sunkowo łat­wo manip­u­lować po stron­ie klien­ta. Dlat­ego należy bezwzględ­nie unikać prze­chowywa­nia tam infor­ma­cji nie­jawnych (przede wszys­tkim loginów/haseł, ale też danych jak adres email, jakiekol­wiek dane, do których dostęp w sys­temie wyma­ga auto­ryza­cji) a także po stron­ie aplikacji trak­tować wszys­tkie infor­ma­c­je z ciasteczek jako nieza­u­fane (koniecz­na jest każ­do­ra­zowa wal­i­dac­ja i wery­fikac­ja, jeśli z nich korzys­tamy bezpośred­nio). Z tego powodu poza sesją i śledze­niem zachowań użytkown­ików, ciastecz­ka mają znacznie szer­sze zas­tosowanie w tech­nolo­giach fron­tendowych (zapisy­wanie pref­er­encji użytkown­i­ka dot. układu strony, wyświ­et­la­nia ele­men­tów itp), gdzie służą wygodzie i nieiry­towa­nia użytkown­i­ka pow­tarzal­ny­mi pyta­ni­a­mi za każdym razem.

Bonus: co się dzieję, kiedy wpiszesz w przeglądarce adres www.kobietydokodu.pl ?

To pytanie ma drugie dno — poza tym, że jest pewnego rodza­ju pod­sumowaniem powyższych infor­ma­cji, pada ono bard­zo częs­to na roz­mowach rekru­ta­cyjnych :) Zaczni­jmy więc od początku.

Pier­wsze co się wydarzy to przeglą­dar­ka spróbu­je przetłu­maczyć adres url (www.kobietydokodu.pl) na adres IP (np. 188.128.171.252) . Aby to zro­bić, najpierw sprawdzana jest pamęć podręcz­na. Tam przeglą­dar­ka (lub sys­tem oper­a­cyjny) prze­chowu­je m.in. ostat­nie wyni­ki takiego tłu­maczenia. Jeśli nie zna­jdzie go w pamię­ci podręcznej, wysyła zapy­tanie do ser­w­era DNS. Ser­w­ery DNS służą właśnie do tego, aby tłu­maczyć nazwy domen na adresy IP. Po otrzy­ma­niu odpowiedzi przeglą­dar­ka zna już adres IP (czyli adres ser­w­era w sieci) pod jaki ma wysłać zapy­ta­nia.

Drugim krok­iem jest wysłanie zapy­ta­nia do ser­w­era dzi­ała­jącego pod wskazanym adresem. Zapy­tanie to wysyłane jest na port 80 (jest to domyśl­ny port pro­tokołu HTTP) i jest to zapy­tanie typu GET. Ser­w­er przetwarza zapy­tanie i jeśli wszys­tko jest w porząd­ku odpowia­da treś­cią (np.  stroną HTML) oraz sta­tusem 200 — OK.

Jeśli chodzi o to, co dzieje się po stron­ie ser­w­era, to zależy od użytej tech­nologii. My przeanal­izu­je­my oczy­wiś­cie ser­w­er aplikacji Java EE.

Wstępem jest zami­ana zapy­ta­nia HTTP na obiekt typu HttpServle­tRe­quest. Dopiero wtedy ma miejsce właś­ci­we przetwarzanie.
Na początku ser­w­er aplikacji anal­izu­je adres URI — bierze jego pier­wszy seg­ment (do pier­wszego /) wery­fiku­jąc, czy ist­nieje aplikac­ja uru­chomiona pod takim adresem. Jeśli tak, ta część jest usuwana z adresu URI, a zapy­tanie jest przekazy­wane do tej aplikacji, jeśli nie to zapy­tanie jest kierowane do aplikacji o ścieżce / (jeśli oczy­wiś­cie ist­nieje). Następ­nie ser­w­er aplikacji na pod­staw­ie infor­ma­cji z pliku XML kieru­je zapy­tanie do odpowied­niego obiek­tu, który imple­men­tu­je inter­fe­js Servlet. Tutaj już przy­chodzi czas na naszą aplikację — Servle­ty to obiek­ty, których imple­men­tac­ja jest częś­cią aplikacji.

W przy­pad­ku aplikacji napisanych z uży­ciem Spring’a, wszys­tkie zapy­ta­nia (najczęś­ciej) trafi­a­ją do jed­nego obiek­tu typu Dis­patch­erServlet, który na pod­staw­ie adno­tacji nad kon­trol­era­mi, plików kon­fig­u­racji oraz plików XML wywołu­je metody odpowied­nich klas.

To trochę uproszc­zony obraz, w grę wchodzą jeszcze fil­try, intr­cep­to­ry, potenc­jal­nie mogą też aspek­ty, jed­nak nie zmieni­a­ją one idei i dokład­niejszym ich omówie­niem zajmiemy się przy innej okazji.

Podsumowanie

 

Dzisi­aj omówiliśmy jak dzi­ała pro­tokół HTTP, z rzeczy, które musisz wiedzieć, to:

  • jest bezs­tanowy, opar­ty o architek­ture klient-ser­w­er i dzi­ała­ją­cy w try­bie zapy­tani-odpowiedź
  • co to są nagłów­ki i gdzie się je przekazu­je
  • jak wyglą­da zapy­tanie
  • co to są kody odpowiedzi (sta­tusy) i jakie wyróż­ni­amy grupy
  • co to są ciastecz­ka i do czego służą

Ta wiedza wystar­czy do tego, aby zrozu­mieć więk­szość mate­ri­ałów związanych z samym pro­tokołem HTTP, na stanowisku Junior devel­opera także powin­na być wystar­cza­ją­ca. Zachę­cam jed­nak do dal­szego zgłębia­nia i doczy­ta­nia o praw­dopodob­nie najbardziej zasłużonym pro­tokole na świecie.

  •  
  •  
  •  
  •  
  •