HTTP

Adam.Pilorz

1 Obsługa protokołu HTTP
     1.1 Sposób użycia
          1.1.1 POST
          1.1.2 GET
          1.1.3 Przesyłanie plików
          1.1.4 Cookie
          1.1.5 HTTP-Auth
     1.2 Uwaga

Obsługa protokołu HTTP

Jako, że niejednokrotnie spotyka się problemy z wygodną obsługą protokołu HTTP, chciałem zaprezentować moje małe dzieło. Możnaby teoretycznie uznać, że po co komuś coś więcej niż to, co oferuje INDY? Odpowiedź jest dość prosta: INDY oparte jest na VCL. A do tego jest strasznie zagmatwane (ale to już prywatna, subiektywna opinia). Fakt faktem, że nie udało mi się w INDY wysłać pliku serwerowi. Na czystych gniazdkach zrobiłem to w kilka godzin (wraz z paroma innymi możliwościami). Ale czyste gniazdka (w sensie komponentów TClientSocket i TServerSocket) też przestają być wystarczające, gdy chcemy zminimalizować swoją aplikację: Również opierają się na VCL. Jeśli piszemy aplikację w WinAPI, to takie podejście to jest gratis 300KB. Idąc w kierunku minimalizacji, jaką chciałem osiągnąć przy pisaniu CubeCVS zacząłem szukać czegoś lepszego. Dotarłem do Synapse. Jest to całkiem potężny darmowy zamiennik dla znanego wszystkim INDY. Tyle tylko, że dwa razy mniejszy (po kompilacji). W związku z tym, że sama aplikacja ma się sporo zmieniać postanowiłem zrobić krok dalej - wyrzucić stałą obsługę protokołu i całą obsługę sieci do niezależnej biblioteki DLL i napisanie do niej maksymalnie prostego interfejsu. Tak powstała biblioteka http.dll obsługująca praktycznie wszystko, czego można się spodziewać po wysokopoziomowej obsłudze tego protokołu. Nie jest to czyste Synapse wklejone do DLL'ki, ale napisana przezemnie obsługa tego protokołu ograniczająca wymaganą pracę programisty, który chce ją wykorzystać do dodania pól formularzy, plików, skonfigurowanie Proxy (jeśli jest taka potrzeba), ewentualne dodanie ciasteczek i podanie adresu URL. W zamian otrzymujemy rekord złożony z nagłówka HTTP, dokumentu oraz dostarczonych Cookies.

Sposób użycia

Najlepszym sposobem prezentacji użycia jakiegoś gotowca jest chyba przykład :). W związku z tym zamieszczę kilka drobnych przykładów:

POST

program testhttp;

uses SysUtils, Windows, http;

var
  s: String;

begin
  Write('Podaj wartość parametru ''test'' przekazywanego metodą POST: ');
  ReadLn(s);
  AddField('test', PChar(s));
  Write('Podaj adres (URL) strony, do której chcesz ten parametr wysłać: ');
  ReadLn(s);
  WriteLn(String(Post(PChar(s)).Document)); //Wyświetlamy zwrócony dokument
  end;

GET

Można też wykorzystać metodę GET. Wtedy zamiast używać AddField po prostu dopisujemy do adresu URL coś na kształt '?test=wartość', oraz wywołujemy funkcję Get zamiast Post z takim samym efektem.

Przesyłanie plików

Jeśli chcemy wysłać plik nie ma nic prostszego: Używamy procedury AddFile albo AddLocalFile, w zależności od tego, czy chcemy ładować zawartość pliku z pamięci (opcja pierwsza), czy wprost z dysku (opcja druga). Używa się ich w następujący sposób:

AddFile('test', 'testowy.html', 'text/plain', 'Treść pliku :P');
AddLocalFile('test', 'testowy.exe', 'application/unknown', 'c:\windows\notepad.exe');

Gdzie 'test' jest nazwą pola (odpowiednik atrybutu name dla znacznika $lt;input type="file" /> w HTML oraz XHTML), 'testowy.html'/'testowy.exe' jest nazwą pliku, jaką otrzyma serwer, 'text/plain'/'application/unknown' są typami MIME, zaś ostatni argument odróżnia te dwie procedury. W pierwszej podajemy bezpośrednio treść pliku, zaś w drugiej ścieżkę do pliku, który ma zostać załadowany. Teoretycznie można zastąpić jedną opcję drugą (zapisywanie pliku tymczasowego, albo ładowanie pliku do pamięci i przekazywanie w postaci treści), lecz jest to zwyczajnie wolniejsze rozwiązanie.

Cookie

Warto zaznaczyć różnicę między dwiema operacjami na Cookie - dodawniem cookie do zapytania i odczytywaniem cookie po jego wykonaniu.

Pierwsza z nich odbywa się w ten sam sposób jak dodawanie pól do przesłania metodą POST, tylko procedure AddField zastępujemy procedurą AddCookie, natomiast odczytywanie cookie wymaga trochę innej konstrukcji zapytania. Wygląda to mniej więcej tak:

var
  HttpResult: THttpResult;
begin
  {jakieś tam pola, pliki, cookiesy dodawane}
  HttpResult:=Post('http://adres.serwera/adres/pliku?zmienne=GET');
  WriteLn('Cookie:');
  WriteLn(String(HttpResult.Cookies));
  WriteLn('Dokument:');
  WriteLn(String(HttpResult.Document));
  end;

Oczywiście jeśli interesują nas tylko ciasteczka, a dokument nie, to nic nie stoi na przeszkodzie zastosowania takiego rozwiązania jak w pierwszym przykładzie, tylko zastępując "Document" przez "Cookies".

HttpResult.Cookies jest zmienną PChar wskazującą na stringa, w którym poszczególne ciastka oddzielone są znakami łamania linii, zaś nazwa od wartości znakiem rówości.

HTTP-Auth

Czasami strona lub serwer wymagają autoryzacji. Część z tych autoryzacji przeprowadzana jest na bazie autorskich metod wykorzystujących formularze i język skryptowy po stronie serwera, czasem dodatkowo ciastka i/lub skrypty javascript. Protokół HTTP oferuje jednak wbudowany system autoryzacji zwany czasem HTTP-Auth. Dziś (tj. 09-07-2006) dodałem obsługę tej autoryzacji również w mojej bibliotece http.dll. Użycie jest banalnie proste. Do pierwszego przykładu dodajmy więc autoryzację HTTP:

program testhttp;

uses SysUtils, Windows, http;

var
  s, user, pass: String;

begin
  Write('Podaj nazwę użytkownika: ');
  ReadLn(user);
  Write('Podaj hasło użytkownika: ');
  ReadLn(pass); // <-- Ostatnie cztery linijki służą wczytywaniu informacji o nazwie i haśle użytkownika
  Write('Podaj wartość parametru ''test'' przekazywanego metodą POST: ');
  ReadLn(s);
  AddField('test', PChar(s));
  Write('Podaj adres (URL) strony, do której chcesz ten parametr wysłać: ');
  ReadLn(s);
  AddAuthData(PChar(user), PChar(pass)); //<-- Ta linijka dodaje informacje o użytkowniku i haśle
  WriteLn(String(Post(PChar(s)).Document)); //Wyświetlamy zwrócony dokument
  end;

Prawda, że proste? Dokładnie ta sama zasada tyczy się metody GET. Oczywiście można by stwierdzić, że wczytywanie hasła przez ReadLn jest niebezpieczne, bo hasło wyświetlane jest na ekranie. Bo tak jest, jest to niebezpieczna metoda - do Was należy ewentualna inwencja w tworzeniu metod bezpieczniejszych, ale nie o tym jest ten tekst.

Uwaga

Warte podkreślenia jest, iż do przekazywania wartości do DLL stosowany jest typ PChar zamiast String. Tak więc aby otrzymać stringa należy użyć konstrukcji String(zmienna), a aby stringa przekazać jako parametr należy użyć PChar(zmienna).

9 komentarzy

Linki chyba wygasły

Próbowałem użyć tego w pluginie do Tlena, ale co jakiś czas wyskakują mi pewne dwa errory- błąd aplikacji i Application Error z "Exception EInvalidPointer in module http.dll ".. itp :>

"Warto zaznaczyć różnicę między dwiema operacjami na Cookie - dodawniem cookie do zapytania i odczytywaniem cookie po jego wykonaniu."
Wg mnie "Cookie" wystarczy raz podlinkować - co za dużo to nie zdrowo. To samo z innymi. Po co w artykule (ba, w jednym zdaniu!) kilka identycznych linków.

To tylko moja ocena.

Wystąpił błąd powodujący, że nie da się ściągnąć załączonego pliku. Zgłoszone do bugtrackera Coyote. Jak szybko to nie zostanie poprawione, wrzucę gdzieś niezależnie ten plik i podam linka.
//Już poprawione - można ściągać.

Hmm... Niestety nie mam żadnego serwera, na którym mógłbym to przetestować. Jak znasz jakiś, podziel się wiedzą, potestuję i napiszę co i jak...

heh a na polaczenia szyfrowane nie dziala lub nie potrafie tego zastosowac :/

Właśnie starałem się możliwie gdzie się da zrobić linki itp. Wszystkie artykuły odnośnie dodawania nowych tekstów w nowym Coyote jakie istniały w momencie pisania tego tekstu i które udało mi się znaleźć przeczytałem i starałem się stosować w praktyce. A przy okazji to całkiem fajny sposób na sprawdzenie, jak to wszystko działa :)

Fajne pod wzgledem formatowania (przyznam sie ze nie czytalem calego) :) Ciesze sie ze poswieciles minutke i przeczytales poradnik jak formatowac tekst - o to chodzi! Tak trzymac!

Jak coś jest źle to poprawcie albo dajcie znać. Jak coś nie jest jasne, to dajcie znać, postaram się lepiej opisać.