BASH: Implementacja metod GET i POST protokołu HTTP
14.05.2013 20:30
System Linux, w bardzo łatwy sposób, umożliwia nam dostęp do protokołu TCP, za pomocą którego możemy zaimplementować wiele tekstowych protokołów wyższych warstw. W tym wpisie zaprezentuje jak uzyskać dostęp do zasobów HTTP przez metody GET i POST, za pomocą języka skryptowego BASH.
Zacznijmy od typowego zapytania GET (RFC 2616). Wygląda następująco:
GET / HTTP/1.1 Host: www.wp.pl
Pierwsza linia składa się z: metody - GET zasobu - / wersji - HTTP/1.1
W następnej linii określony został host (serwer), z którego pobieramy zasoby.
Standardowo każdy skrypt w Linuksie uruchamiany jest z trzema strumieniami stdin (0), stdout (1), strerr (2). Dostęp do protokołu TCP otrzymujemy przez wirtualne urządzenie /dev/tcp, HTTP jest protokołem wyższej warstwy pracującym na porcie 80. Aby nawiązać połączenie, należy utworzyć kolejny strumień, który zestawi nam transmisję klient-serwer na porcie 80, w tym celu wpisujemy
exec 3<>/dev/tcp/www.wp.pl/80
3 - oznacza nowy strumień, który będzie używany do wysyłania i odbierania komunikatów <> - to przekierowanie wejścia i wyjścia /dev/tcp/www.wp.pl/80 - to określenie serwera do jakiego uzyskujemy dostęp
W tym momencie mamy zestawione połączenie klient-serwer. Teraz wyślemy żądanie dostępu do zasobu. Wpiszmy sekwencję:
echo "GET / HTTP/1.1" >&3 echo "Host: www.wp.pl" >&3 echo "" >&3
>&3 oznacza przekierowanie danych do strumienia "3", który jest naszym połączeniem (klient-serwer). Pierwsze dwie linie zostały omówione wcześniej, trzecia (pusta) wg standardu stanowi separator/zakończenie komunikatu GET. Zapytanie zostało wysłane do serwera, a jego wynik oczekuje na nas w strumieniu "3". Aby go wyświetlić należy przekierować wyjście na ekran za pomocą polecania cat:
cat <&3
Uzyskamy w ten sposób zasób "/" z serwera "www.wp.pl", łącznie z nagłówkami GET/HTTP. Niektóre serwery (np. google.com) po wykonaniu zapytania nie zamkną automatycznie połączenia, dlatego też należy określić, że chcemy je zamknąć po pobraniu zasobu:
echo "GET / HTTP/1.1" >&3 echo "Host: www.google.com" >&3 echo "Connection: close" >&3 echo "" >&3
Jako podsumowanie GET zapiszmy całość jako funkcję bash:
http_get() { HOST=`echo $1 | sed "s/.*\:\/\///"` PARAM="/"`echo $HOST | sed "s/.*\///"` HOST=`echo $HOST | sed "s/\/.*//"` PORT="80" exec 3<>/dev/tcp/$HOST/$PORT if [ $? -ne 0 ]; then exit fi echo "GET $PARAM HTTP/1.1" >&3 echo "Host: $HOST" >&3 echo "Connection: close" >&3 echo "" >&3 cat <&3 } http_get "http://www.wp.pl/"
Przejdźmy teraz do metody POST, która umożliwia przekazywanie parametrów do serwera. Minimalna postać zapytania wygląda następująco:
POST / HTTP/1.1 Host: www.wp.pl Content-Length: 4 Content-Type: application/x-www-form-urlencoded dane
W pierwszej linii POST oznacza metodę przekazywania danych do serwera, reszta jest identycznie jak w GET. Podobnie jest w drugiej linii. Content-Length oznacza długość danych, wysłanych za separatorem (pusta linia), Content-Type - to format przesyłanych danych.
Implementacja w postaci funkcji:
http_post() { HOST=`echo $1 | sed "s/.*\:\/\///"` PARAM="/"`echo $HOST | sed "s/.*\///"` HOST=`echo $HOST | sed "s/\/.*//"` PORT="80" DATA="$2" SIZE=${#DATA} TYPE="$3" exec 3<>/dev/tcp/$HOST/$PORT if [ $? -ne 0 ]; then exit fi echo "POST $PARAM HTTP/1.1" >&3 echo "Host: $HOST" >&3 echo "Connection: close" >&3 echo "Content-Length: $SIZE" >&3 echo "Content-Type: $TYPE" >&3 echo "" >&3 echo "$DATA" >&3 cat <&3 }
Podobnie jak w metodzie GET wysyłamy dla pewności opcję zakończenia połączenia (Connection: close) po przesłaniu danych. Content-Length zliczany jest z długości przekazanych danych ${#DATA}. Pomiędzy danymi, a nagłówkiem zostawiamy linię wolną - oznacza to zakończenie przesyłania nagłówka i rozpoczęcie przesyłania danych.
Przykład zastosowania:
http_post "http://localhost/login.php" "username=nazwa&password=haslo" "application/x-www-form-urlencoded"
Znając podstawy można spróbować zaimplementować inne protokoły warstwy aplikacji.