#Niezbędnik Juniora. Protokół HTTP

By 30 czerwca 2015Niezbędnik Juniora

Ten wpis poświęcony będzie protokołowi HTTP – czyli temu, jak przeglądarka komunikuje się z serwerem.Podstawy protokołu poruszaliśmy już w lekcji 9, omawiając adnotacje Spring MVC. Dzisiaj powiemy sobie o nim znacznie więcej, dowiesz się także, co się dzieje po wpisaniu w przeglądarkę adresu www.kobietydokodu.pl ;)

Czym jest protokół HTTP

HTTP to skrót od Hypertext Transfer Protocol i jest to główny protokół używany współcześnie w przegladarkach. Jest to protokół bezstanowy, tzn. ani serwer (ani klient) nie przechowuje informacji o tym, jakie były wcześniej zapytania pomiędzy określonym serwerem i klientem oraz nie posiada stanu wewnętrznego. Powoduje to, że każde zapytanie do serwera traktowane jest jako ‚nowe’, z punktu widzenia serwera aplikacji niemożliwe do powiązania z informacjami np. o zalogowanym użytkowniku. Tą bezstanowość można obejść, obecnie głównie za pomocą tzw. ciasteczek (będzie o nich nieco dalej), należy jednak pamiętać, że HTTP sam w sobie jest bezstanowy.

Zapytania HTTP

W zapytaniach HTTP możemy wyróżnić dwa elementy: nagłówek i ciało. Zajmijmy się najpierw pierwszą częścią czyli nagłówkiem. Nagłówek ma minimum 1 wiersz, 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 wersję protokołu HTTP (obecnie korzystamy z wersji 1.1, oznaczanej w nagłówku jako HTTP/1.1 ). Przykładowy pierwszy wiersz nagłówka wygląda nastepująco:

GET / HTTP/1.1

Gdzie:

  • GET – to nazwa metody HTTP
  • / – to tzw. URI, czyli ta część adresu, który wystepuje po domenie
  • HTTP/1.1 – okreslenie protokołu, tutaj HTTP w wersji 1.1

Bezpośrednio pod tą linijką możemy (ale nie musimy) podać nagłówki HTTP, każdy w nowej linii. Uwaga! Nie może być tutaj żadnej linii przerwy! Wszystko, co znajdzie się po pierwszej pustej linii (przerwie) traktowane jest jako ciało zapytania.

Znim przejdziemy do ciała, wyjaśnijmy sobie kilka konceptów.

Metoda HTTP

Metody w protokole HTTP służą do rozgraniczania różnych czynności, które mamy zamiar wykonać, pomagają także w projektowaniu przeglądarek internetowych i obsłudze zapytań. Początkowo o protokole HTTP myślano jako o protokole do obsługi plików na zdalnym serwerze – taki protokół pozwalał na pobranie zasobu, usunięcie go, wysłanie na serwer, jego aktualizację oraz pobranie metadanych. Takie znaczenie nadaje się też metodom HTTP w przypadku API spełniającego założenia REST. Tyle z teorii i historii. W praktyce metody pozwalają nam ‚rozdzielać’ zapytania trafiające pod ten sam adres – np. metody GET używamy do wyświetlenia formularza, a metody POST do jego przesłania – obie rzeczy możemy realizować pod tym samym adresem url, np. www.kobietydokodu.pl/jakis/formularz . Poniżej znajdziesz podsumowanie metod protokołu HTTP:

Metoda Request body Response body Zastosowanie / opis
GET  niedozwolone  opcjonalnie Pobieranie zasobu lub jego wyświetlenie, np. wyświetlenie formularza lub strony. Parametry można przekazywac jedynie poprzez adres (np. ?nazwa=wartosc&nazwa2=wartosc2)
POST  opcjonalne  opcjonalnie Przesłanie danych zapisanych jako pary klucz-wartość do serwera (np. wysłanie formularza, gdzie kluczem jest nazwa danego pola a wartością wpisana przez nas wartość). Metoda ta pozwala przesyłać także pliki (a także wiele pliki oraz pary klucz-wartość jednocześnie). Parametry są przekazywane w ciele zapytania, można także przekazywać parametry poprzez adres (tak jak w metodzie GET)
PUT  opcjonalne  opcjonalnie Przesyłanie ‚paczki’ danych, np. jednego pliku. Metoda ta ma pewne ograniczenia, np. nie ma możliwości łaczenia par klucz-wartość z inną przesyłaną treścią (np. plikiem). Obecnie używana głównie w przypadku RESTowych serwisów, gdzie ciałem jest np. formularz zapisany w postaci JSONa.
DELETE  opcjonalnie  opcjonalnie Usuwanie zasobu na serwerze, z racji bezpieczeństwa praktycznie zawsze jest wyłaczona domyślnie. Obecnie używana głównie w przypadku RESTowych serwisów, wskazując, że dany zasób ma być usunięty (i obsługiwany przez aplikację, a nie sam serwer).
HEAD  niedozwolone  niedozwolone  Analogiczny do zapytania GET, z tym wyjątkiem, że nie zwraca ciała (zawartości). Służy do pobrania metdanych o zasobie w postaci nagłówków HTTP. Dla danego adresu zwraca same nagłówki.

Istnieją jeszcze metody OPTIONS, TRACE oraz CONNECT, ale nie mają one dużego zastosowania z punktu widzenia programisty aplikacji webowych.

Pełną specyfikację możesz znaleźć na stronie http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html, jest to formalna definicja metod HTTP.

Nagłówki HTTP

Nagłówki HTTP spotkamy zarówno w zapytaniach, jak i w odpowiedziach. Są one pierwszymy liniami, oddzielone od ciała jedną pustą linią. Nagłówki są opcjonalne – protokół nie wymaga ich obecności. Nagłówki to pewnego rodzaju metadane i polecenia wymieniane przez przeglądarkę i serwer – mogą się w nich znaleźć informacje takie jak rodzaj przesyłanych treści (np. czy jest to obrazek czy plik JSON), sugestia dotycząca traktowania zawartości (czy przeglądarka ma wyświetlić daną treść, czy np. potraktować to jako pobieranie), jaki jest rozmiar przesyłanych danych, kiedy były modyfikowane, jakiego rodzaju odpowiedzi druga strona się spodziewa itp.

Nagłówki przyjmują postać klucz-wartość, zapisywane w postaci:

Klucz: wartość

Najpierw jest klucz (przeważnie zaczyna się dużą literą, ale nie jest to wymagane), następnie dwukropek, spacja oraz wartość.

W przeważającej większości nagłówki są automatycznie ustawiane przez serwer i aplikacja nie musi ich modyfikować / uzupełniać. Są jednak sytuacje, w których możemy chcieć wysłać określony nagłówek, mamy wtedy taką możliwość.

Pełną listę standardowych 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ć dowolną nazwę (klucz), nie musi ona być spośród tych określonych standardami. Dzięki temu możliwe jest implementowanie dodatkowej funkcjonalności pomiędzy serwerami, które się ze sobą komunikują. Możemy też dołaczać dowolne nagłówki – jeśli odbiorca wiadomości nie wie, jak je zinterpretować, po prostu je zignoruje.

Poniższa tabela podsumowuje najczęściej używane nagłówki

Nagłówek Opis Przykład
Content-Type W zapytaniu oraz odpowiedzi określa, jakiego typu dane są przesyłane Content-Type: application/json
Content-Length W zapytaniu oraz odpowiedzi zawiera informacje ile danych jest przesyłanych Content-Length: 20
Cookie W zapytaniu przesyła zawartość Cookies przechowywanych dla danej witryny. Może przechowywać wiele wartości w postaci klucz=wartość, pary oddzielane są od siebie średnikami. Cookie: AcceptedCookiePolicy=1; Country=Poland;
Set-Cookie W odpowiedzi jest to polecenie serwera, aby przeglądarka ustawiła wartości Cookie; podobnie jak nagłówek Cookie może zawierać wiele par postaci klucz=wartość oddzielonych średnikami Set-Cookie: UserID=JanNowak; SeenTutorial=1
Location W odpowiedzi instuuje przeglądarkę o tym, że ma wykonać zapytanie pod inny adres. W ten sposób (w połaczeniu ze statusem np. 302) w aplikacji możemy przekierowywać pod inny adres Location: http://calieminnaaplikacja.com.pl/nowawersja
Last-Modified W odpowiedzi serwer może poinformować, kiedy nastąpiła ostatnia zmiana zawartości. Format daty jest specyficzny dla protokołu HTTP i określony w dokumencie RCF 7231 Last-Modified: Tue, 15 May 2015 12:45:26 GMT
Content-Disposition W odpowiedzi serwer może poinstuować przeglądarkę, aby zamiast wyświelać treść, pobrała ją. Można też określić nazwę, pod jaką przeglądarka powinna zasugerować zapisanie pliku Content-Disposition: attachment; filename=”raport_roczny.pdf”
Host W zapytaniu jest to nagłówek obowiązkowy, informuje serwer pod jaki adres domeny chcemy wysłać zapytanie (może to być też adres IP). Pomaga to serwerom obsługującym wiele domen prawidłowo przekierowywać zapytania Host: www.kobietydokodu.pl
Accept W zapytaniu klient może poinformować serwer, jakiego typu odpowiedzi akceptuje. Dzięki temu serwer może zadecydować o wysłaniu odpowiedzi np. w XML a nie JSON, co ma zastosowanie w wielu API Accept: application/xml

Statusy HTTP (kody odpowiedzi)

Jednym z elementów protokołu HTTP są kody odpowiedzi, zwanymi też statusami. To numeryczne, trzycyfrowe kody, które są dołączane do odpowiedzi i sygnalizują status odpowiedzi. Najpopularniejsze i najczęściej spotykane kody to 200 (OK, czyli wszystko jest w porządku, zapytanie jest obsłużone), 302 (przekierowanie), 403 (brak dostępu) oraz 404 (nie znaleziono – stąd liczba 404 często pojawia się na stronach z informacją, że wpisany adres nie istnieje).

Kodów jest mnóstwo, najczęściej wystarczy jednak znajomość tych podstawowych. Co ważne, nawet nie znając kodu można określić jego przybliżone znaczenie na podstawie numeru. Kody są bowiem podzielone na grupy, pierwsza cyfra kodu mówi nam, z której jest on grupy. I tak:

  • 1xx – informacyjne, nieczęsto można spotkać, dotyczą bardziej środowiska niż samej aplikacji (np. 111 – serwer odrzucił połaczenie)
  • 2xx – zapytanie się powiodło
  • 3xx – przekierowanie, zapytanie należy kierować pod inny adres / serwer
  • 4xx – błąd aplikacji spowodowany działaniem użytkownika (np. wspomniany 404 – nie znaleziono – czy 403 – brak dostępu lub 400 – niepoprawnie zapytanie)
  • 5xx – błąd serwera (np. nieobsłużony wyjątek w Javie)

Listę kodów można znaleźć m.in. na Wikipedii. Są one także opisane i zdefiniowane w dokumencie RFC 2616 z późniejszymi zmianami.

W ramach ciekawostki polecam poczytać o kodzie odpowiedzi 418, który jest częścią wielu implementacji, znalazł się także w Spring Framework.

Bezstanowość HTTP a ciasteczka

Jak wspominaliśmy, protokół HTTP jest bezstanowy, tzn nie ‚przechowuje’ informacji o tym, co działo się wcześniej. Jest to oczywisty problem w większości przypadków, kiedy korzystamy z narzędzi wymagających zalogowania się – informacja o tym, jaki użytkownik jest zalogowany musi być przechowywana w jakiś sposób.

Rozwiązaniem stosowanym obecnie na szeroka skalę są tzw. ciasteczka – pierwotnie mające postać plików tekstowych w formacie klucz=wartość, obecnie przechowywane w wewnętrznej bazie danych przeglądarki.

Formalnie ciasteczka (ang. cookies) to zbiór par klucz-wartość przypisanych do danej domeny (w ogólnym przypadku; możliwe jest też utworzenie cookies dla ścieżki, np. kobietydokodu.pl/jednaaplikacja) które są wysyłane do serwera z każdym zapytaniem. Oczywiście ze względów bezpieczeństwa nie przechowuje się w nich danych użytkownika, do tego służą tzw. sesje, czyli kolekcje danych przechowywane po stronie serwera. W ciasteczkach najczęściej zapisuje się tzw. klucz sesji – unikalny identyfikator, na podstawie którego serwer ma możliwość powiązania jednego ze zbiorów danych które przechowuje z wysyłającym zapytanie klientem. Najczęściej w zależności od technologii klucz ten ma określoną nazwę – w przypadku Javy standard JavaEE definiuje go jako JSESSIONID (w Servlet API 3, które jest częścią standardu Java EE 6 można zmienić to w pliku web.xml).

Oczywiście większość współczesnych serwerów ma dodatkowe zabezpieczenia, sprawdza np. adres IP klienta powiązanego z sesją, żeby upewnić się że nie ma miejsca próba przechwycenia sesji (tzw. atak typu Session Hijacking). Ogólnie jednak ciasteczka i jedna zapisana w nich wartość jest podstawą wystarczającą do wprowadzenia stanowości w aplikacjach webowych.

Jeśli chodzi o inne informacje przechowywane w ciasteczkach to tutaj jest mnóstwo możliwości – od śledzenia zachowań użytkownika (np. Google Analytics przechowuje w ciasteczku infomacje, żeby powiązać kilka zachowań danego klienta z tym samym użytkownikiem w ich systemie) poprzez przechowywanie informacji o preferencjach (np. kolejność sortowania listy elementów aby następnym razem od razu ją uporządkować w ten sposób) po zachowania jednorazowe (np. czy użytkownik zaakceptował politykę cookies albo ‚ukrył’ jakąś część interfejsu).

To, o czym trzeba jeszcze pamiętać, to fakt, że ciasteczka zwykle ‚żyją’ dłużej niż sesja – w teorii informacje tam zapisane mogą być wieczne (w praktyce, żyją do kolejnego ‚wyczyszczenia’ pamięci podręcznej przeglądarki przez użytkownika). Z tego powodu jest to dobre miejsce do przechowywania informacji jawnych, niezwiązanych z danymi które nasz system przetwarza, np. preferencji dot. wyglądu interfejsu użytkownika, wszelkich informacji pomagających nam zwiększyć komfort użytkownika w korzystaniu z naszego systemu.

Z punktu widzenia bezpieczeństwa, informacje w ciasteczkach są przesyłane jawnie w protokole HTTP, bez szyfrowania i można nimi stosunkowo łatwo manipulować po stronie klienta. Dlatego należy bezwzględnie unikać przechowywania tam informacji niejawnych (przede wszystkim loginów/haseł, ale też danych jak adres email, jakiekolwiek dane, do których dostęp w systemie wymaga autoryzacji) a także po stronie aplikacji traktować wszystkie informacje z ciasteczek jako niezaufane (konieczna jest każdorazowa walidacja i weryfikacja, jeśli z nich korzystamy bezpośrednio). Z tego powodu poza sesją i śledzeniem zachowań użytkowników, ciasteczka mają znacznie szersze zastosowanie w technologiach frontendowych (zapisywanie preferencji użytkownika dot. układu strony, wyświetlania elementów itp), gdzie służą wygodzie i nieirytowania użytkownika powtarzalnymi pytaniami 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 rodzaju podsumowaniem powyższych informacji, pada ono bardzo często na rozmowach rekrutacyjnych :) Zacznijmy więc od początku.

Pierwsze co się wydarzy to przeglądarka spróbuje przetłumaczyć adres url (www.kobietydokodu.pl) na adres IP (np. 188.128.171.252) . Aby to zrobić, najpierw sprawdzana jest pamęć podręczna. Tam przeglądarka (lub system operacyjny) przechowuje m.in. ostatnie wyniki takiego tłumaczenia. Jeśli nie znajdzie go w pamięci podręcznej, wysyła zapytanie do serwera DNS. Serwery DNS służą właśnie do tego, aby tłumaczyć nazwy domen na adresy IP. Po otrzymaniu odpowiedzi przeglądarka zna już adres IP (czyli adres serwera w sieci) pod jaki ma wysłać zapytania.

Drugim krokiem jest wysłanie zapytania do serwera działającego pod wskazanym adresem. Zapytanie to wysyłane jest na port 80 (jest to domyślny port protokołu HTTP) i jest to zapytanie typu GET. Serwer przetwarza zapytanie i jeśli wszystko jest w porządku odpowiada treścią (np.  stroną HTML) oraz statusem 200 – OK.

Jeśli chodzi o to, co dzieje się po stronie serwera, to zależy od użytej technologii. My przeanalizujemy oczywiście serwer aplikacji Java EE.

Wstępem jest zamiana zapytania HTTP na obiekt typu HttpServletRequest. Dopiero wtedy ma miejsce właściwe przetwarzanie.
Na początku serwer aplikacji analizuje adres URI – bierze jego pierwszy segment (do pierwszego /) weryfikując, czy istnieje aplikacja uruchomiona pod takim adresem. Jeśli tak, ta część jest usuwana z adresu URI, a zapytanie jest przekazywane do tej aplikacji, jeśli nie to zapytanie jest kierowane do aplikacji o ścieżce / (jeśli oczywiście istnieje). Następnie serwer aplikacji na podstawie informacji z pliku XML kieruje zapytanie do odpowiedniego obiektu, który implementuje interfejs Servlet. Tutaj już przychodzi czas na naszą aplikację – Servlety to obiekty, których implementacja jest częścią aplikacji.

W przypadku aplikacji napisanych z użyciem Spring’a, wszystkie zapytania (najczęściej) trafiają do jednego obiektu typu DispatcherServlet, który na podstawie adnotacji nad kontrolerami, plików konfiguracji oraz plików XML wywołuje metody odpowiednich klas.

To trochę uproszczony obraz, w grę wchodzą jeszcze filtry, intrceptory, potencjalnie mogą też aspekty, jednak nie zmieniają one idei i dokładniejszym ich omówieniem zajmiemy się przy innej okazji.

Podsumowanie

 

Dzisiaj omówiliśmy jak działa protokół HTTP, z rzeczy, które musisz wiedzieć, to:

  • jest bezstanowy, oparty o architekture klient-serwer i działający w trybie zapytani-odpowiedź
  • co to są nagłówki i gdzie się je przekazuje
  • jak wygląda zapytanie
  • co to są kody odpowiedzi (statusy) i jakie wyróżniamy grupy
  • co to są ciasteczka i do czego służą

Ta wiedza wystarczy do tego, aby zrozumieć większość materiałów związanych z samym protokołem HTTP, na stanowisku Junior developera także powinna być wystarczająca. Zachęcam jednak do dalszego zgłębiania i doczytania o prawdopodobnie najbardziej zasłużonym protokole na świecie.

  •  
  •  
  •  
  • 2
  •  
  • Paweł Sołtysiak

    „4xx – błąd aplikacji”

    Zmieniłbym na „błąd po stronie klienta”

    „Bonus: co się dzieję, kiedy wpiszesz w przeglądarce adres http://www.kobietydokodu.pl ?” – jeżeli kogoś interesuje bardziej szczegółowy opis to polecam przeczytać też to: https://github.com/alex/what-happens-when

    • Jak najbardziej, choć nadal jest to błąd aplikacji – uzupełniliśmy opis :) Dzięki za czujność!

  • Artur leśniak

    „Parametry można przekazywac jedynie poprzez adres (np. ?nazwa=wartosc&nazwa2=wartosc2)” – metoda get.
    Możemy również przesyłać dane przez nagłówek. Popraw mnie jeśli się mylę.

    http://stackoverflow.com/a/11876907/4120427
    http://stackoverflow.com/questions/10907954/http-requests-and-querystring-vs-headers

    Pozdrawiam Artur

    • Teoretycznie tak, można modyfikować nagłówki tak, aby umieszczać w nich dane. Zwróć jednak uwagę, że nie służą one do przekazywania danych od użytkownika, a parametrów związanych z samą komunikacją HTTP. Niektóre serwery proxy mogą także modyfikować nagłówki, filtrować itp, zakładając, że są one zgodne z protokołem HTTP (ten określa dokładnie jakie pola mogą występować w nagłówkach i co oznaczają – zobacz http://www.iana.org/assignments/message-headers/message-headers.xml#perm-headers), co mogłoby wpłynąć na działanie takiej aplikacji.

  • Tomasz Potocki

    Czegoś tu nie rozumiem. Najpierw jest informacja „Zajmijmy się najpierw pierwszą częścią czyli nagłówkiem. Nagłówek ma minimum 1 wiersz…” a pod tabelką jest „Nagłówki HTTP spotkamy zarówno w zapytaniach, jak i w odpowiedziach. Są one pierwszymi liniami, oddzielone od ciała jedną pustą linią. Nagłówki są opcjonalne – protokół nie wymaga ich obecności” – jeżeli coś ma mieć minimum jeden wiersz, to jak może być opcjonalne?