====== Hypertext Transfer Protocol ====== === Zanim zaczniesz... === //Uwaga:// Dla ułatwienia zajęć w tych materiałach kliknięcie na przykładowe polecenia, tekst pisany ''czcionką stałej szerokości'' i taki tekst zaznaczy cały ich tekst. Przypominam że w Linuksie ostatni zaznaczony tekst można wkleić naciskając środkowy przycisk myszy. //Uwaga:// jeżeli chcesz wykonywać te polecenia spoza PP, zaloguj się i wykonuj je z serwera //polluks// lub pytaj o adres inny niż //lindev.cs.put.poznan.pl// (z którego treść stron jest dostępna tylko z PP). //Uwaga:// wbudowana w bash komenda **''fc''** tworzy tymczasowy plik i otwiera go w domyślnym edytorze tekstu (wskazany przez zmienną ''EDITOR''). Po zakończeniu edycji pliku bash wstawia treść pliku do linii poleceń i wykonuje tę linię. ~~Zadanie.#0.#~~ Skopiuj i wklej w konsolę i wykonaj poniższe polecenie: tr 'a-z' 'd-za-c' << EOF kilka przykladowych linii tekstu EOF Następnie wpisz ''fc'', zmień treść tekstu przekazanego do komendy ''tr'', zapisz plik i wyjdź z niego. \\ Domyślny edytor możesz zmienić np. na ''nano'' bądź ''mousepad'' wpisując ''export EDITOR=nano'' bądź ''export EDITOR=mousepad''. ==== "Hello World" ==== ~~Zadanie.#1.#1~~ Wyślij zapytanie HTTP GET w wersji 1.0 bez podawania nazwy domenowej: nc lindev.cs.put.poznan.pl 80 << KONIEC GET / HTTP/1.0 KONIEC
~~Zadanie.#.#~~ Wyślij zapytanie HTTP GET w wersji 1.1 bez podawania nazwy domenowej: nc lindev.cs.put.poznan.pl 80 << KONIEC GET / HTTP/1.1 KONIEC Dlaczego dostajesz taką a nie inną odpowiedź? ~~Zadanie.#.#~~ Wyślij zapytanie HTTP GET w wersji 1.1 podając nazwę domenową: nc lindev.cs.put.poznan.pl 80 << KONIEC GET / HTTP/1.1 Host: lindev.cs.put.poznan.pl KONIEC Dlaczego połączenie się nie zamknęło? ~~Zadanie.#.#~~ Wyślij dwa zapytania HTTP GET w wersji 1.1 prosząc o zakończenie połączenia po odpowiedzi na ostatnie z nich: nc lindev.cs.put.poznan.pl 80 << KONIEC GET /var/ HTTP/1.1 Host: lindev.cs.put.poznan.pl GET /SK/ HTTP/1.1 Host: lindev.cs.put.poznan.pl Connection: close KONIEC
~~Zadanie.#.#~~ Wyślij zapytanie HTTP GET do innego serwera: nc nghttp2.org 80 << KONIEC GET / HTTP/1.0 KONIEC Dlaczego dostajesz odpowiedź //Bad Request//? ~~Zadanie.#.#~~ Zmień (w ten czy inny sposób) znak ''\n'' na ''\r\n'' i wyślij tę samą treść jak w poprzednim zadaniu: unix2dos << KONIEC | nc nghttp2.org 80 GET / HTTP/1.0 KONIEC sed 's/$/\r/' << KONIEC | nc nghttp2.org 80 GET / HTTP/1.0 KONIEC Jakie znasz sposoby zapisywania końca linii? ~~Zadanie.#.#~~ Zapytaj kolejno o http://www.cs.put.poznan.pl/, http://www.cs.put.poznan.pl/jkonczak/ oraz http://www.cs.put.poznan.pl/jkonczak/sk2: nc www.cs.put.poznan.pl 80 << KONIEC | head -n 20 GET / HTTP/1.1 Host: www.cs.put.poznan.pl Connection: close KONIEC nc www.cs.put.poznan.pl 80 << KONIEC | head -n 20 GET /jkonczak/ HTTP/1.1 Host: www.cs.put.poznan.pl Connection: close KONIEC nc www.cs.put.poznan.pl 80 << KONIEC | head -n 20 GET /jkonczak/sk2 HTTP/1.1 Host: www.cs.put.poznan.pl Connection:close KONIEC ==== Zmiana na , HTTPS, percent encoding ==== Narzędzie ''nc'' / ''netcat''((A przynajmniej popularniejsze z jego implementacji.)) nie umie zamieniać końców linii na ''\r\n''. \\ Narzędzie ''socat'' potrafi to zrobić po podaniu odpowiedniej opcji. Narzędzie ''nc'' / ''netcat'' zamyka połączenie jeśli z obu stron pojawi się koniec pliku (tzn. odczyt i na //stdin// i na gnieździe zwróci 0). \\ Domyślnie ''socat'' zamyka połączenie kiedy z którejkolwiek strony pojawi się koniec pliku. ~~Zadanie.#2.#1~~ Zobacz jak wskazuje się programowi ''socat'' zmianę wspomnianych wyżej zachowań: unix2dos << KONIEC | nc nghttp2.org 80 GET / HTTP/1.0 KONIEC socat tcp:nghttp2.org:80,crlf stdio,ignoreeof << KONIEC GET / HTTP/1.0 KONIEC
HTTPS (oficjalnie //HTTP Over TLS//) jest w podsumowującym go [[https://datatracker.ietf.org/doc/html/rfc2818#section-2|standardzie]] krótko scharakteryzowany następująco:
Conceptually, HTTP/TLS is very simple. Simply use HTTP over TLS precisely as you would use HTTP over TCP.
Dla przypomnienia: ''socat'' pozwala używać połączeń szyfrowanych, podając ''ssl'' bądź ''openssl'' jako protokół. ''openssl s_client'' służy do testowania połączeń szyfrowanych. Oba te narzędzia pozwalają testować szyfrowany transport wiadomości HTTP. ~~Zadanie.#.#~~ Porównaj wysłanie zapytania HTTP używając szyfrowanego transportu używając narzędzia ''socat'' i ''openssl''. socat ssl:cat.put.poznan.pl:443,crlf stdio,ignoreeof << KONIEC | head -n 40 GET / HTTP/1.1 Host: cat.put.poznan.pl Connection: close KONIEC openssl s_client -quiet -verify_quiet -crlf cat.put.poznan.pl:443 << KONIEC | head -n 40 GET / HTTP/1.1 Host: cat.put.poznan.pl Connection: close KONIEC
//Uwaga:// Jeśli poda się programowi ''socat'' opcję '',ignoreeof'' dla któregoś strumienia, zamienia ''\r\n'' zarówno w wysyłanych jak i odbieranych przez ten strumień danych. Z kolei ''openssl s_client'' z opcją ''-crlf'' poprawia jedynie wysyłane znaki nowej linii. Możesz to sprawdzić porównując wynik komend: printf 'GET / HTTP/1.0\nHost:sirius.cs.put.poznan.pl\n\n' | socat ssl:sirius.cs.put.poznan.pl:443,crlf -,ignoreeof | cat -v printf 'GET / HTTP/1.0\nHost:sirius.cs.put.poznan.pl\n\n' | openssl s_client -quiet -verify_quiet -crlf sirius.cs.put.poznan.pl:443 | cat -v Jeżeli w odpowiedzi będą dane binarne, ''socat'' może je "zepsuć" zmieniając ''\r\n'' na ''\n''. W żądaniu HTTP powinien być używany [[https://datatracker.ietf.org/doc/html/rfc9112#section-2.2|tylko]] zestaw znaków ASCII. Dodatkowo w linii żądania, w adresie żądanego zasobu nie może znajdować się spacja. Do zakodowania znaków spoza ASCII i białych znaków w linii żądania używa się tzw. [[https://datatracker.ietf.org/doc/html/rfc3986#section-2.1|Percent-Encoding]]. ~~Zadanie.#.#~~ Sprawdź jak zostanie zrozumiana nazwa zasobu %C5%BB%C3%B3%C5%82%C4%87 i d%C4%85%C4%87+w+r%C3%B3g: openssl s_client -crlf -quiet -verify_quiet pl.wikipedia.org:https << EOF | grep 'title>' GET /wiki/%C5%BB%C3%B3%C5%82%C4%87 HTTP/1.0 Host: pl.wikipedia.org EOF openssl s_client -crlf -quiet -verify_quiet www.google.pl:https << EOF | grep -o '.*' GET /search?q=d%C4%85%C4%87+w+r%C3%B3g HTTP/1.0 Host: www.google.pl User-Agent: Mozilla/5.0 Firefox/100 EOF Do zmiany ze zwykłego utf-8 na urlencode i z powrotem możesz użyć np. gotowej komendy w perlu czy php: openssl s_client -crlf -quiet -verify_quiet www.google.pl:https << EOF | grep -o '.*' GET /search?q=$(perl -Mutf8 -MURI::Escape -e 'print uri_escape_utf8 "żółta żaba żarła żur"') HTTP/1.0 Host: www.google.pl User-Agent: Mozilla/5.0 Firefox/100 EOF openssl s_client -crlf -quiet -verify_quiet www.google.pl:https << EOF | grep -o '.*' GET /search?q=$(php -r 'echo urlencode("żółta żaba żarła żur");') HTTP/1.0 Host: www.google.pl User-Agent: Mozilla/5.0 Firefox/100 EOF perl -Mutf8 -MURI::Escape -e 'print uri_unescape("%C5%B9d%C5%BAb%C5%82o") . "\n"' php -r 'echo urldecode("%C5%B9d%C5%BAb%C5%82o") . "\n";' ==== Nagłówki HTTP (1/2) ==== === Nagłówek "Host" === Ten sam serwer HTTP może obsługiwać strony dla wielu nazw domenowych. Żeby to umożliwić, wprowadzono do HTTP wymaganie podania nagłówka //Host// wskazującego dla jakiej nazwy hosta podany zasób ma zostać zwrócony. ~~Zadanie.#3.#1~~ Używając poniższych komend(( W potoku ''awk'' przerzuca nagłówki do strumienia standardowego błędu, a ''w3m'' rysuje otrzymany kod HTML w konsoli. Zamiast ''| w3m …'' możesz użyć też ''| elinks -dump /dev/sdtin'' lub ''| html2text''. )) połącz się na port 443 adresu IP 150.254.186.176 i podaj różne wartości nagłówka //Host//: raz www.mpu.pl, raz www.mpk.poznan.pl. Porównaj odpowiedzi. openssl s_client -quiet -verify_quiet -crlf 150.254.186.176:https << EOF \ | awk '{if(p)print;else print>"/dev/stderr"};/^\r$/{p=1}' | w3m -T text/html -dump - GET / HTTP/1.0 Host: www.mpu.pl EOF openssl s_client -quiet -verify_quiet -crlf 150.254.186.176:https << EOF \ | awk '{if(p)print;else print>"/dev/stderr"};/^\r$/{p=1}' | w3m -T text/html -dump - GET / HTTP/1.0 Host: www.mpk.poznan.pl EOF
~~Zadanie.#.#~~ Przy użyciu szyfrowanego transport komunikatów HTTP – tzn. HTTPS – treść żądania, w tym nagłówek //Host// jest wysyłany po nawiązaniu połączenia. Jednak już w trakcie nawiązywania połączenia TLS serwer musi przedstawić certyfikat dla właściwej domeny. A co jeśli serwer HTTPS obsługuje strony dla różnych domen? Dowiedz się skąd serwer wie który certyfikat przedstawić klientowi. Jak takie rozwiązanie wpływa na prywatność komunikacji HTTPS? Czy zaproponowano rozwiązania tej kwestii? === Ciastеczka === W protokole HTTP serwer może poprosić klienta o zapamiętanie wskazanych danych i przesłanie ich z powrotem przy każdym kolejnym żądaniu. \\ Szczegóły składni odpowiednich nagłówków znajdziesz np. [[https://datatracker.ietf.org/doc/html/rfc6265#section-4|tutaj]]. ~~Zadanie.#.#~~ Ściągnij treść strony http://www2.cs.put.poznan.pl/ i znajdź w nagłówkach odpowiedzi ten proszący o zapamiętanie ciasteczka: unix2dos << EOF | nc www2.cs.put.poznan.pl 80 | head -n18 GET / HTTP/1.0 Host: www2.cs.put.poznan.pl EOF
~~Zadanie.#.#~~ Ponownie ściągnij treść strony http://www2.cs.put.poznan.pl/, ale tym razem podaj w nagłówkach ciasteczko o treści qtrans_front_language=en. Czy różni się odpowiedź jeśli w żądaniu znalazło się wskazane ciasteczko? unix2dos << EOF | nc www2.cs.put.poznan.pl 80 | head -n18 GET / HTTP/1.0 Host: www2.cs.put.poznan.pl Cookie: qtrans_front_language=en EOF === [ekstra] Nagłówki "Accept..." === ~~Zadanie.#.#~~ Nagłówek //Accept-Language// jest często wykorzystywany (i respektowany) do wskazywania przez przeglądarkę preferowanego języka. Porównaj wynik zapytań najpierw zostawiając, potem usuwając ten nagłówek z poniższego zapytania: socat ssl:duckduckgo.com:443 stdio,ignoreeof << EOF GET / HTTP/1.0 Host: duckduckgo.com Accept-Language: pl EOF
~~Zadanie.#.#~~ Nagłówek //Accept-Encoding// jest powszechnie używany do określania przez klienta jakie sposoby kompresowania treści odpowiedzi rozumie. Jeśli taki nagłówek zostanie podany i serwer zna którąś ze wskazanych metod kompresji, to dla zaoszczędzenia transferu wyśle odpowiedź skompresowaną najkorzystniejszym dla siebie sposobem. Porównaj wynik poniższych zapytań bez oraz z tym nagłówkiem: openssl s_client -quiet -verify_quiet -crlf dcs.cs.put.poznan.pl:443 << EOF GET / HTTP/1.0 Host: dcs.cs.put.poznan.pl Accept-Encoding: gzip EOF Dodając na koniec linii polecenia ''openssl'' potok ''| sed '1,/^\r$/d' | gunzip'' możesz zdekompresować odpowiedź. ~~Zadanie.#.#~~ Porównaj ile przepustowości pozwalają zaoszczędzić różne sposoby kompresowania danych. M=; for ENC in identity gzip zstd br; do S=$(openssl s_client -quiet -verify_quiet -crlf www.facebook.com:443 << EOF 2>/dev/null | wc -c GET /Politechnika.Poznanska HTTP/1.0 Host: www.facebook.com Accept-Encoding: $ENC User-Agent: czlowiek EOF ); M=${M:-$S}; printf "%s %d'%03d %d.%03d%%\n" $ENC $((S/1000)) $((S%1000)) $((100*S/M)) $(((100000*S/M)%1000)); done | column -tR1-3 Jak duża jest różnica między metodą //gzip// a specjalnie napisaną w firmie Facebook dla oszczędzenia transferu i czasu kompresji metodą [[https://github.com/facebook/zstd|zstd]] i specjalnie napisaną w firmie Google dla oszczędzenia transferu i czasu kompresji metodą [[https://github.com/google/brotli|brotli]]? Zauważ też że w HTTP/1.x nagłówki nie są kompresowane. ~~Zadanie.#.#~~ Nagłówek //Accept// pozwala w żądaniu wskazać jaki typ MIME klient chce otrzymać. Zwykle serwer ma dany zasób tylko w jednym formacie i niezależnie co chciał klient, zwraca zawsze to samo. Zdarzają się scenariusze w których ten nagłówek jest jednak używany; np. przy ściąganiu obrazów przeglądarki ustawiają w //Accept// preferowane formaty, z których serwer może wybrać najkorzystniejszy. Porównaj: socat ssl:img.onet.pl:443,crlf -,ignoreeof << EOF | head -n 5 GET /1/NOek9lBaHR0cHM6Ly9vY2RuLmV1L3B1bHNjbXMvTURBXy8yYzYzMWQ4NWZlZDExMmFjNmU1YjRmZjMyYmRiMmMwNi5qcGeSlQMAzQHJzQIvzQE6kwXMvnbeAAKhMAehMQQ HTTP/1.0 Host: img.onet.pl Accept: image/avif,image/webp,image/png,image/jpeg,image/* EOF socat ssl:img.onet.pl:443,crlf -,ignoreeof << EOF | head -n 5 GET /1/NOek9lBaHR0cHM6Ly9vY2RuLmV1L3B1bHNjbXMvTURBXy8yYzYzMWQ4NWZlZDExMmFjNmU1YjRmZjMyYmRiMmMwNi5qcGeSlQMAzQHJzQIvzQE6kwXMvnbeAAKhMAehMQQ HTTP/1.0 Host: img.onet.pl Accept: image/png,image/jpeg,image/* EOF
=== Nagłówki opisujące treść === Jeżeli żądanie bądź odpowiedź zawierają treść, wśród nagłówków musi znaleźć się informacja o tym jakiego rodzaju jest to treść i kiedy odbiorca ma uznać że cała treść już do niego dotarła. ~~Zadanie.#.#~~ Porównaj użycie nagłówka //Content-Length// i //Transfer-Encoding: chunked// w HTTP/1.1: nc lindev.cs.put.poznan.pl 80 << KONIEC GET /ZSK/Win7_Samba3DomainMember.reg HTTP/1.1 Host: lindev.cs.put.poznan.pl Connection: close KONIEC nc lindev.cs.put.poznan.pl 80 << KONIEC GET /Windows/Photo/ HTTP/1.1 Host: lindev.cs.put.poznan.pl Connection: close KONIEC Praktycznie każda wersja HTTP ma inny pomysł na przesyłanie danych o nieokreślonym rozmiarze: HTTP/1.0 nie podawał długości danych i zamykał połączenie, HTTP/2 i HTTP/3 wprowadzają własne nagłówki transportujące wiele równoległych żądań (strumienie) i w nich jest zaznaczone czy to ostatni fragment treści. ~~Zadanie.#.#~~ Nagłówek //Content-Type// informuje o typie MIME danych stanowiących treść. Porównaj typy i zwracaną treść odpowiedzi na następujące zapytania: socat ssl:danepubliczne.imgw.pl:443 stdio,ignoreeof << EOF | grep -iC99 'content-.*:' GET /api/data/synop/id/12330/format/xml HTTP/1.0 Host: danepubliczne.imgw.pl EOF socat ssl:danepubliczne.imgw.pl:443 stdio,ignoreeof << EOF | grep -iC99 'content-.*:' GET /api/data/synop/id/12330/format/json HTTP/1.0 Host: danepubliczne.imgw.pl EOF socat ssl:danepubliczne.imgw.pl:443 stdio,ignoreeof << EOF | grep -iC99 'content-.*:' GET /api/data/synop/id/12330/format/html HTTP/1.0 Host: danepubliczne.imgw.pl EOF === [ekstra] Cache === Dla HTTP opracowano kilka mechanizmów pozwalających klientowi sprawdzić czy podany zasób zmienił się od poprzedniego pobrania go. Pozwala to używać wcześniej zapamiętanych wyników zapytań HTTP będąc pewnym, że zapamiętana wersja jest dalej aktualna. Klient może też podług własnego widzimisię użyć wcześniej zapamiętanej odpowiedzi bez sprawdzania czy zasób na serwerze się zmienił. Prawidłowe cache'owanie odpowiedzi HTTP jest dość złożone, patrz [[https://datatracker.ietf.org/doc/html/rfc9111|odpowiednie RFC]]. ~~Zadanie.#.#~~ Odpowiedź HTTP może wskazywać w nagłówku //Cache-Control// czy należy ją w ogóle cache'ować, czy i jak długo można ją używać bez sprawdzania czy dalej jest aktualna, jak również czy może być używana też przez innych użytkowników. Porównaj treść tego nagłówka w wynikach zapytań: openssl s_client -quiet -verify_quiet put.poznan.pl:443 << EOF | grep -iC25 cache-control: GET / HTTP/1.0 Host: put.poznan.pl EOF openssl s_client -crlf -quiet -verify_quiet www.pap.pl:443 << EOF | grep -iC25 cache-control: GET / HTTP/1.1 Host: www.pap.pl Connection: close EOF
~~Zadanie.#.#~~ Pobierz stronę www.wikipedia.org i znajdź nagłówek //Last-Modified//: openssl s_client -quiet -verify_quiet www.wikipedia.org:https << EOF GET / HTTP/1.0 Host: www.wikipedia.org EOF Następnie ponów zapytanie ustawiając nagłówek //If-Modified-Since// na datę z poprzedniej odpowiedzi, np: openssl s_client -quiet -verify_quiet www.wikipedia.org:https << EOF GET / HTTP/1.0 Host: www.wikipedia.org If-Modified-Since: Mon, 06 Jan 2025 16:45:09 GMT EOF
~~Zadanie.#.#~~ Poza nagłówkiem //Last-Modified//, odpowiedź na pierwsze zapytanie z poprzedniego zadania zawierała też nagłówek //ETag//. Ustawiając w żądaniu nagłówek //If-None-Match// na poprzednio widziany etag można określić jaką wersją odpowiedzi dysponuje klient: openssl s_client -quiet -verify_quiet www.wikipedia.org:https << EOF GET / HTTP/1.0 Host: www.wikipedia.org If-None-Match: W/"133d4-62b0c59f38f40" EOF
==== Metody HTTP ==== === GET i HEAD === Metoda HEAD powinna zwracać wynik identyczny jak GET dla podanego zasobu, w tym, nagłówek //Content-Type//, ale bez treści (i nagłówków mówiących jak odebrać treść, takich jak //Content-Length//, //Transfer-Encoding// czy //Content-Encoding//). ~~Zadanie.#4.#1~~ Porównaj wynik żądania HEAD i GET o tą samą stonę: printf "HEAD /jkonczak/ HTTP/1.0\r\nHost: www.cs.put.poznan.pl\r\n\r\n" | socat ssl:www.cs.put.poznan.pl:https -,ignoreeof printf "GET /jkonczak/ HTTP/1.0\r\nHost: www.cs.put.poznan.pl\r\n\r\n" | socat ssl:www.cs.put.poznan.pl:https -,ignoreeof === POST === Metoda POST powinna być używana do zmiany stanu zasobu, w przeglądarkach jest używana jako metoda ogólnego przeznaczenia w której klient wysyła dane do serwera. \\ Powszechnie używana do przesyłania formularzy (zbudowanych w oparciu o tag HTML ''%%
%%''), jak i np. w formularzach wyszukiwania jeśli informacje o tym co jest wyszukiwane mogłyby nie zmieścić się w URI (linia żądania ma limit długości, treść żądania nie ma). ~~Zadanie.#.#~~ Porównaj odpowiedź na żądanie o ten sam zasób metodą GET i metodą POST z treścią searchquery=sieci typu application/x-www-form-urlencoded używając przykładowych komend(( W potoku ''awk'' przerzuca nagłówki do strumienia standardowego błędu, a ''w3m'' rysuje otrzymany kod HTML w konsoli. Zamiast ''| w3m …'' możesz użyć też ''| elinks -dump /dev/sdtin'' lub ''| html2text''. )): openssl s_client -verify_quiet -quiet pomoc.put.poznan.pl:https << EOF | awk '{if(p)print;else print>"/dev/stderr"};/^\r$/{p=1}' | w3m -T text/html -dump - GET /index.php?/Base/Search/Index HTTP/1.0 Host: pomoc.put.poznan.pl EOF openssl s_client -verify_quiet -quiet pomoc.put.poznan.pl:https << EOF | awk '{if(p)print;else print>"/dev/stderr"};/^\r$/{p=1}' | w3m -T text/html -dump - POST /index.php?/Base/Search/Index HTTP/1.0 Host: pomoc.put.poznan.pl Content-Type: application/x-www-form-urlencoded Content-Length: 17 searchquery=sieci EOF === [ekstra] Inne metody === Poza używanymi przez przeglądarki GET i POST, standard HTTP definiuje też inne metody, np. PUT, DELETE, PATCH, OPTIONS. Te metody są wykorzystywane w powszechnie stosowanych w usługach internetowych z REST API (więcej o REST znajdziesz np. [[https://aws.amazon.com/what-is/restful-api/|tutaj]]). WebDAV jest rozszerzeniem HTTP o dodatkowe metody pozwalające na zarządzanie plikami, w tym zakładanie blokad. Pozwala to np. podłączyć katalog znajdujący się na zdalnym serwerze jako udział sieciowy (i jest to natywnie wspierane w systemach Windows i MacOS), oczywiście pod warunkiem podłączenia do przygotowanego na to serwera (webdav jest wspierany m. inn. przez ownCloud, Nextcloud i Seafile). ==== Kody odpowiedzi ==== Przypomnienie z wykładu: w odpowiedzi znajduje się numeryczny kod informujący o stanie wykonania żądania – np. czy się powiodło (2xx), czy nastąpiło przekierowanie (3xx), bądź czy żądanie nie może zostać obsłużone (4xx) lub wystąpił błąd po stronie serwera (5xx). \\ Listę oficjalnych kodów znajdziesz [[https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml|na stronie organizacji IANA]]. \\ Część programów używa też dodatkowych kodów, częściowo zebranych na [[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes|wikipedii]]. ==== Przeglądarka + zbadaj/inspect ==== W tej chwili praktycznie wszystkie przeglądarki internetowe mają wbudowane narzędzia deweloperskie (zwykle dostępne pod skrótem //Ctrl//+//Shift//+//I// lub z menu kontekstowego każdej strony – //ppm/[Narzędzia deweloperskie/]Zbadaj//). \\ Wśród tych narzędzi jest narzędzie //Sieć// które pokazuje wszystkie żądania HTTP wraz z odpowiedziami. ~~Zadanie.#5.#1~~ Znajdź w przeglądarce narzędzie wyświetlające wykonane żądania HTTP. Odśwież stronę, żeby zobaczyć listę wykonanych żądań. ~~Zadanie.#.#~~ Znajdź żądanie typu GET na które przyszła odpowiedź z treścią. Znajdź nagłówki odpowiedzi i treść odpowiedzi. ~~Zadanie.#.#~~ Znajdź żądanie typu POST na liście (jeśli nie widzisz żadnego, odwiedź inne strony). Znajdź nagłówki tego żądania i jego treść, zarówno w przetworzonej i surowej formie. ~~Zadanie.#.#~~ Domyślnie otwarcie nowej strony czyści dziennik/log żądań. Odpowiedzią na żądanie POST (np. wygenerowanego przez przycisk //wyślij// na formularzu) często jest [[https://en.wikipedia.org/wiki/Post/Redirect/Get|przekierowanie]], którego wykonanie czyści listę żądań. Znajdź jak ustawić żeby lista żądań była tylko czyszczona na żądanie użytkownika (opcja nazywa się np. //trwałe dzienniki// / //persistent log//). ~~Zadanie.#.#~~ Znajdź jak włączyć/wyłączyć działanie cache na czas korzystania z narzędzia deweloperskiego //Sieć//. Odśwież stronę i zobacz jaką część przeglądarka wzięła z pamięci podręcznej bądź wykonała zapytanie i dostała w odpowiedzi //304 Not Modified//. ==== Narzędzia curl i wget ==== ''[[https://curl.se/|curl]]'' i ''[[https://www.gnu.org/software/wget/|wget]]'' to szeroko rozpowszechnione narzędzia pozwalające m. inn. wysyłać zapytania HTTP. \\ ''curl //url//'' dla schematu URL //%%http://%%//, //%%https://%%// lub bez podania schematu wysyła zapytanie GET o wskazany URL i wypisuje treść na standardowe wyjście. \\ ''wget //url//'' dla schematu URL //%%http://%%//, //%%https://%%// lub bez podania schematu wysyła zapytanie GET o wskazany URL i zapisuje odpowiedź do pliku. |ściągnij podany URL do pliku o nazwie nazwy ostatnim członem URLa, podążając za przekierowaniami | ''wget //url//...'' | ''curl -LO //url//...'' | |jak wyżej, ale nazwę pliku weź z nagłówka //Content-Disposition// odpowiedzi HTTP | ''wget --content-disposition //url//...'' | ''curl -LOJ //url//...'' | |ściągnij podany URL do pliku o nazwie //nazwa_docelowa// | ''wget -O //nazwa_docelowa// //url//'' | ''curl -o //nazwa_docelowa1// //url1// [-o //nazwa_docelowa2// //url2//]...'' | |kontynuuj (wcześniej przerwane) ściąganie pliku | ''wget -c …'' | ''curl -C- …'' | |ściągnij podany URL i wypisz treść odpowiedzi na standardowe wyjście, (nie podążając za przekierowaniami) | ''wget -q -O- --max-redirect=0 //url//'' | ''curl //url//'' | |pokaż nagłówki odpowiedzi | ''wget -S …'' | ''curl -i …'' | |pokaż szczegóły (w tym nagłówki żądania i odpowiedzi) | ''wget -d …'' | ''curl -v …'' | |wybierz metodę //METODA// protokołu HTTP | ''wget --method=//METODA// …'' | ''curl --request //METODA// …'' | |ustaw nagłówek //User-Agent// na //agent// | ''wget -U //agent// …'' | ''curl -A //agent// …'' | |ustaw dowolny nagłówek //Tresc: wartosc// | ''wget --header "//Tresc: wartosc//" …'' | ''curl --header "//Tresc: wartosc//" …'' | |podaj treść zapytania //nazwa=wartosc// | ''wget --post-data=//nazwa=wartosc// …'' \\ patrz też: ''--post-file'' / ''-body-data'' / ''-body-file'' | ''curl -d //nazwa=wartosc// …'' \\ patrz też: ''--data-…'' / ''--form'' / ''--json'' | ''wget'' potrafi też ściągać rekurencyjnie strony, patrz: ''wget {-m|-r} [-np] [-k] [-E]'' ~~Zadanie.#6.#1~~ Pobierz kod HTML wybranej strony za pomocą narzędzia ''curl'' i ''wget'' do pliku. \\ Wyświetl za pomocą narzędzia ''curl'' i ''wget'' kod HTML strony http://put.poznan.pl. ~~Zadanie.#.#~~ Wykonaj żądanie o stronę http://lindev.cs.put.poznan.pl/ narzędziem ''curl'' lub ''wget'' w taki sposób, żeby na ekranie pojawiły się również nagłówki. ~~Zadanie.#.#~~ Wykonaj żądanie POST na URLu https://httpbin.org/post podając w treści przynajmniej dwie pary klucz-wartość zakodowane jako application/x-www-form-urlencoded (zarówno ''wget'' jak i ''curl'' domyślnie ustawi ten typ MIME dla żądania POST). \\ Dla przykładowego kodu HTML: ''%%%%'' przykładowa treść żądania to ''imie=Jan&nazwisko=Kowalski''.
==== [ekstra] Nagłówki HTTP (2/2) ==== === User-Agent === Nagłówek //User-Agent// określa oprogramowanie użyte do wykonania zapytania. Przeglądarki (jak i wiele innych klientów HTTP) pozwalają zwykle ustawić własną wartość tego nagłówka. //User-Agent// bywa używany do wysyłania strony w wersji zoptymalizowanej pod przedstawiającą się przeglądarkę bądź do nieudolnego blokowania dostępu do strony z czegoś innego niż przeglądarka. ~~Zadanie.#7.#1~~ Sprawdź jaką wartość //User-Agent// mają twoje przeglądarki i jaką wartość ustawia ''wget'' i ''curl''. Następnie wykonaj zapytanie programem ''wget'' lub ''curl'' przedstawiając się jako przeglądarka. === Referer === Klient HTTP wpisuje do nagłówka //Referer//(( Nazwa nagłówka jest zapisanym z błędem angielskim słowem //[[https://www.m-w.com/dictionary/referrer|referrer]]//. Zauważ że nagłówek który mówi czy i kiedy klient ma podawać nagłówek //Referer// nazywa się (już bez literówki) //Referrer-Policy//. )) URL który polecił wykonać bieżące żądanie. \\ Przykładowo jeśli przeglądarka pobrała stronę HTML z adresu //%%http://example.com/my/site/html%%// i na niej znalazł się znacznik //%%%%//, to pobierając obrazek ze wskazanego źródła przeglądarka w żądaniu o niego ustawi //%%Referer: http://example.com/my/site/html%%// (lub URL obcięty wedle uznania przeglądarki, np. //%%Referer: http://example.com/%%//). Podobnie dzieje się jeśli będąc na tej stronie użytkownik kliknie na hiperłącze. \\ Poza zbieraniem informacji kto linkuje daną stronę, //Referer// bywa używany do blokowania wykorzystania zasobów przez strony z innych domen (tzn. serwer odmówi odpowiedzi jeśli //Referer// nie będzie miał oczekiwanej wartości). Podobnie działa nagłówek //[[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing|Origin]]//, ustawiany jeśli żądanie spełnia konkretne warunki, tylko z definicji zawiera URL bez nazwy zasobu. ~~Zadanie.#.#~~ W narzędziach deweloperskich sprawdź wartość nagłówka //Referer// np. przechodząc z wyników wyszukiwarki do znalezionej strony. === HTTP Authentication === Strony korzystające z uwierzytelniania HTTP wykorzystują do tego odpowiednie nagłówki - //WWW-Authenticate// i //Authorization//. ~~Zadanie.#.#~~ Korzystając z narzędziach deweloperskich zobacz jak wygląda HTTP Auth na przykładzie strony z [[https://www.cs.put.poznan.pl/mkalewski/edu/sk/sk-2024.html|materiałami do wykładów z sieci]]. === Range i Content-Range === Protokół HTTP pozwala na pobranie wskazanej części (zakresu bajtów) podanego zasobu. Pozwala to np. sensownie wznawiać wcześniej przerwane pobieranie plików. ~~Zadanie.#.#~~ Na poniższym przykładzie zobacz jak wygląda sprawdzanie czy dla podanego zasobu można zażądać jego części i jak wygląda żądanie jego części: socat ssl:www.cs.put.poznan.pl:https,crlf stdio,ignoreeof << EOF | grep -C99 '.*Range.*' HEAD /jkonczak/_media/sk2:sockets.svg HTTP/1.1 Host: www.cs.put.poznan.pl GET /jkonczak/_media/sk2:sockets.svg HTTP/1.1 Host: www.cs.put.poznan.pl Range: bytes=291076-291131 Connection: close EOF === X-… === Przez dłuższy czas [[https://datatracker.ietf.org/doc/html/rfc6648|używano]] konwencji by nazwy nieustandaryzowanych nagłówków HTTP zaczynać od //X-…//. Chociaż nie jest do już zalecane, wciąż wiele (szczególnie od dłuższego czasu używających tej konwencji) aplikacji tak zaczyna własne nieustandaryzowane nagłówki. ~~Zadanie.#.#~~ Znajdź nagłówki zaczynające się od X-… na https://cat.put.poznan.pl/ lub https://put.poznan.pl/. ==== Nowsze wersje HTTP ==== === [ekstra] Przypomienie z wykładu === Zarówno HTTP/2 jak i HTTP/3 nie przesyłają bezpośrednio danych aplikacyjnych przez warstwę transportu (TCP lub TLS dla HTTP/2 czy UDP dla HTTP/3), tylko przesyłają na warstwie transportu własne ramki w których dopiero są dane.
              HTTP/1.x              ┃                            HTTP/2                          ┃            ┌┬────────────────────┬─────────────┬┐        HTTP/3
                                    ┃                                                            ┃            ││ :status: 200       │ tresc odpo  ││
                                    ┃                                                            ┃            ││ Content-Length: 16 │             ││
                                    ┃                                                            ┃            │├────────────────────┼─────────────┤│
                                    ┃                                                            ┃   Ramka  / ││ NAGŁÓWKI           │ TREŚĆ       ││  ┌┬───────────────┬┐
                                    ┃                                                            ┃   HTTP/3 \ ││ Długość: 6         │ Długość: 10 ││  ││ wiedzi        ││
                                    ┃  ┌────────────────────┬─────────────┐                      ┃   (3B)     │├────────────────────┴─────────────┤│  │├───────────────┤│
                                    ┃  │ :status: 200       │ tresc odpo  │ ┌───────────────┐    ┃           /││ Strumień: 3                      ││  ││ Strumień: 3   ││
                                    ┃  │ Content-Length: 16 │             │ │ wiedzi        │    ┗━━━━━━━┓  | ││ Długość: 22                      ││  ││ Długość: 9    ││
┌────────────────────┐              ┃  ├────────────────────┼─────────────┤ ├───────────────┤            ┃ Q| ││ Offset: (nieobecne)              ││  ││ Offset: 22    ││
│ HTTP/1.1 200 OK    │              ┃  │ Strumień: 3        │ Strumień: 3 │ │ Strumień: 3   │ \  ramka   ┃ U| ││ Koniec: nie                      ││  ││ Koniec: tak   ││
│ Content-Length: 16 │              ┃  │ NAGŁÓWKI           │ TREŚĆ       │ │ TREŚĆ         │  | HTTP/2  ┃ I| │└──────── TLS ─────────────────────┘│  │└──── TLS ──────┘│
│                    │ ┌─────────┐  ┃  │ Długość: 6         │ Długość: 10 │ │ Długość: 6    │  | (ma 9   ┃ C| │ Flagi:… , Numer pakietu:… ,        │  │ Flagi:…   ...   │
│ tresc odpo         │ │ wiedzi  │  ┃  │ Flagi: koniec      │ Flagi: 0    │ │ Flagi: koniec │ /  bajtów) ┃   \│ Identyfikator połączenia:… ,  ...  │  │ ...       ...   │
├────────────────────┤ ├─────────┤  ┃  ├────────────────────┴─────────────┤ ├───────────────┤            ┃    ├────────────────────────────────────┤  ├─────────────────┤
│     TCP/TLS        │ │ TCP/TLS │  ┃  │             TCP/TLS              │ │   TCP/TLS     │            ┃    │                 UDP                │  │       UDP       │
└────────────────────┘ └─────────┘  ┃  └──────────────────────────────────┘ └───────────────┘            ┃    └────────────────────────────────────┘  └─────────────────┘
Każda ramka HTTP/2 wskazuje strumień którego dotyczy – dla uproszczenia można przyjąć że strumienie działają jak osobne połączenia. HTTP/3 oficjalnie używa protokołu [[https://quicwg.org/|QUIC]] który używa protokołu UDP, ale w praktyce QUIC powstał na potrzeby HTTP/3 i mimo lobbowania za upowszechnieniem go trudno spotkać jego użycie poza HTTP/3. \\ W HTTP/3 szyfrowaniem komunikacji i dzieleniem na strumienie zajmuje się QUIC, a ramki HTTP/3 wskazują tylko typ (nagłówki/dane/…) i długość ich PDU. Nagłówki w [[https://datatracker.ietf.org/doc/html/rfc7541|HTTP/2]] i [[https://datatracker.ietf.org/doc/html/rfc9204|HTTP/3]] są wydajnie kompresowane w sposób skrojony pod HTTP, co istotnie zmniejsza ilość przesyłanych danych. HTTP/2 i HTTP/3 używa tych samych nagłówków z tym samym znaczeniem co HTTP/1.1(( Z nielicznymi wyjątkami wynikającymi z różnić w działaniu tych protokołów, np. //Transfer-Encoding: chunked// ma zastosowanie tylko w HTTP/1.1.)). To co wprowadzają nowe wersje HTTP to jedynie zmiana sposobu przesyłania żądań i odpowiedzi. === HTTP/2 === ~~Zadanie.#8.#1~~ Używając wiresharka zobacz jak wyglądają dane wymieniane przez TCP między klientem i serwerem HTTP w wyniku poleceń: curl -v --http1.1 --head -o/dev/null nghttp2.org curl -v --http2 --head -o/dev/null nghttp2.org curl -v --http2-prior-knowledge --head -o/dev/null nghttp2.org
~~Zadanie.#.#~~ Używając wiresharka zobacz jak wygląda sekwencyjne i równoległe wykonanie żądań w HTTP/2 spowodowane odpowiednio komendami: curl -v --http2 nghttp2.org nghttp2.org/documentation/ nghttp2.org/blog/ curl -v --http2 --parallel nghttp2.org nghttp2.org/documentation/ nghttp2.org/blog/ === HTTP/3 === ~~Zadanie.#.#~~ Poniższe polecenia ściągną trzy strony po HTTP/2 i HTTP/3, zapiszą komunikaty diagnostyczne ''curl''a do plików ''http_2'' i ''http_3'' i porównają te pliki. Sprawdź czy nagłówki zapytań / odpowiedzi różnią się. curl -sv --http2 --parallel https://nghttp2.org https://nghttp2.org/documentation/ https://nghttp2.org/blog/ >/dev/null 2> http_2 curl -sv --http3-only --parallel https://nghttp2.org https://nghttp2.org/documentation/ https://nghttp2.org/blog/ >/dev/null 2> http_3 git diff --color-words --no-index http_2 http_3
Gotowy wynik możesz obejrzeć [[sk2:http:z8.3|tutaj]].
~~Zadanie.#.#~~ Stwórz pusty plik o nazwie ''sslkey.log'' w katalogu ''/tmp'' np. komendą: touch /tmp/sslkey.log Następnie w wiresharku wejdź, wybierając z menu //Edycja// pozycję //Preferencje//, do ustawień wiresharka. \\ Tam kliknij na //Protocols// i znajdź na długiej liście pozycję //TLS// (możesz wpisać pierwsze litery po ustawieniu tej części GUI jako aktywne). \\ W ustawieniach TLS w pole //(Pre)-Master-Secret log filename// wpisz ''/tmp/sslkey.log''. \\ Zmienna środowiskowa SSLKEYLOGFILE respektowana przez ''curl'' (ale też przeglądarki internetowe) wskazuje nazwę pliku do którego te narzędzia będą wpisywać dane potrzebne do rozszyfrowania ruchu TLS. \\ Rozpocznij nasłuchiwanie w wiresharku i wykonaj komendy: SSLKEYLOGFILE=/tmp/sslkey.log curl -v --http2 --parallel https://nghttp2.org/documentation/ https://nghttp2.org/blog/ SSLKEYLOGFILE=/tmp/sslkey.log curl -v --http3-only --parallel https://nghttp2.org/documentation/ https://nghttp2.org/blog/ Zastosuj kolejno filtry ''http2'' i ''quic'' i prześledź żądanie o podstronę //%%/blog/%%// w HTTP/2 i HTTP/3. Użyj do tego //Podążaj / Strumień HTTP/2// oraz //Podążaj / Strumień QUIC// z menu kontekstowego odpowiednich pakietów.
Gotowe pakiety możesz pobrać stąd: {{sk2:http:z8_4.pcap}}
==== Misc ==== Sensowne, dużo obszerniejsze materiały o HTTP znajdziesz tutaj: https://developer.mozilla.org/en-US/docs/Web/HTTP