1
Rozdział ten poświęcony jest programowaniu aplikacji serwerów WWW opartych na technologiach CGI/WinCGI oraz ISAPI/NSAPI z wykorzystaniem technologii WebBroker i InternetExpress.
Reprezentantami technologii WebBroker na gruncie C++Buildera są: specjalizowany moduł danych, zwany modułem danych WWW lub krótko modułem WWW (ang. WebModule), bogaty zestaw komponentów i dwa kreatory, z których najczęściej używanym jest Web Server Application Wizard. Drugi z kreatorów – Database Web Application Wizard – umożliwia integrację tabeli danych z modułem WWW.
Wspomniane komponenty podzielone są na dwie grupy. Pierwsza z nich to tzw. komponenty-producenci, m.in.: TPageProducer, TDataSetPageProducer, TDataSetTableProducer, TQueryTableProducer; C++Builder 5 w wersji Enterprise wprowadza dodatkowo dwa komponenty: TMidasPageProducer i TReconcilePageProducer. Komponenty drugiej grupy to podstawowe zręby aplikacji WWW: TWebModule, TWebDispatcher, TWebRequest i TWebResponse.
Przed uruchomieniem przykładowego projektu, ilustrującego zagadnienia poruszane w tym rozdziale, konieczne jest dokonanie pewnych ustawień w środowisku C++Buildera, w szczególności instalacja dodatkowych pakietów i komponentów; szczegółowe instrukcje na ten temat znajdują się w pliku README.TXT umieszczonym na załączonej płycie CD-ROM wraz ze wspomnianym projektem w katalogu WebServer.
Określenia „WebBroker” i „Moduł WWW” często używane są zamiennie. W rzeczywistości WebBroker stanowi element modułu WWW (tzw. dyspozytor akcji – action dispatcher) wzbogacający „zwykły” moduł danych (datamodule) o elementy charakterystyczne dla modułu WWW. Innymi słowy, stanowi on centralną część mechanizmów, umożliwiających budowę aplikacji opartych na protokołach ISAPI/NSAPI, CGI lub WinCGI bez troszczenia się o różnice pomiędzy tymi protokołami. Dodatkowo Web Bridge umożliwia wykorzystanie tej samej aplikacji zarówno dla protokołu ISAPI (wszystkie wersje), jak i NSAPI (Netscape do wersji 3.6 włącznie).
Aplikacje serwerów WWW są aplikacjami niewizualnymi – interfejs użytkownika zapewniają im aplikacje klienta. Moduły WWW nie mogą więc zawierać komponentów wizualnych, ich konstruowanie odbywa się poza tym w sposób identyczny z konstruowaniem formularzy.
Standardowy kreator wspomagający tworzenie aplikacji serwerów WWW – Web Server Application Wizard – dostępny jest na karcie New okna New Items (otwieranego za pomocą opcji File|New menu głównego IDE). Bezpośrednio po jego uruchomieniu wyświetlane jest okno, umożliwiające wybór protokołu tworzonej aplikacji (rys. 10.1):
Rysunek 10.1. Wybór protokołu aplikacji serwera WWW
Aplikacja typu CGI (Common Gateway Interface) jest aplikacją konsolową, ładowaną przez serwer na żądanie użytkownika i rozładowywaną po zrealizowaniu tego żądania. Żądanie klienta przekazywane jest aplikacji poprzez standardowe wejście (StdIn), natomiast odpowiedź – zazwyczaj w formacie HTML – wypisywana jest do standardowego wyjścia (StdOut).
WinCGI stanowi specyficzną dla Windows implementację protokołu CGI. W przeciwieństwie do standardowego CGI aplikacja WinCGI jest aplikacją typu GUI, chociaż nie manifestuje swej obecności w postaci wizualnej. Rolę standardowych strumieni StdIn i StOut przejął pojedynczy plik INI, za pośrednictwem którego odbywa się wymiana danych pomiędzy serwerem i użytkownikiem.
Rozszerzenia serwera typu ISAPI (Microsoft IIS) i NSAPI (Netscape) tym różnią się od aplikacji CGI/WinCGI, iż w przeciwieństwie do tych ostatnich są bibliotekami DLL. Realizacja żądań odbywa się tu szybciej, ponieważ biblioteki te, ładowane jednorazowo w efekcie pierwszego żądania, pozostają w pamięci serwera.
Jako że przyszłe wersje Netscape z pewnością obsługiwać będą protokół ISAPI, a w chwili obecnej możliwe jest „mapowanie” odwołań do ISAPI na odwołania do NSAPI, w dalszej części rozdziału używać będziemy dla uproszczenia pojedynczej nazwy ISAPI na określenie obydwu protokołów.
Biblioteki DLL, stanowiące rozszerzenia typu ISAPI, mają tę niezaprzeczalną przewagę nad aplikacjami CGI, iż są szybsze i efektywniejsze w działaniu – wszystkie żądania dotyczące danej biblioteki obsługiwane są przez jej pojedynczy egzemplarz, oszczędza się więc zarówno czas, jak i pamięć. Owa „integracja” biblioteki z serwerem ma jednak swe ujemne strony: po pierwsze, jej „podmiana” na nową wersję wymaga zatrzymania serwera i jego ponownego uruchomienia; po drugie i ważniejsze – błędna biblioteka może spowodować załamanie całego serwera, tak więc przed „poważnym” zastosowaniem musi ona zostać przetestowana znacznie gruntowniej niż zwykła aplikacja. Należy zdawać sobie sprawę z faktu, iż biblioteka taka pracuje w warunkach powtarzalnego wykorzystywania (ang. serial reusability), co stwarza okazję do popełnienia wielu typowych błędów, jak np. brak inicjowania zmiennych roboczych przy każdym żądaniu – niezależne aplikacje CGI wolne są od tego problemu.
Niezależnie jednak od tego, czy finalnym produktem danego projektu ma być aplikacja CGI, czy biblioteka ISAPI/NSAPI, jego moduł WWW jest dokładnie taki sam. Stwarza to możliwość przetestowania przyszłej aplikacji CGI w tymczasowej postaci biblioteki DLL (śledzenie bibliotek DLL w C++Builderze jest znacznie łatwiejsze niż śledzenie zewnętrznych procesów). Należy w tym celu utworzyć (w ramach jednej grupy) dwa projekty: zasadniczy, tworzący aplikację CGI, i pomocniczy, tworzący bibliotekę ISAPI. Następnie, używając Menedżera Projektu, należy usunąć moduł WWW z projektu zasadniczego (CGI), a następnie dodać do tegoż projektu moduł WWW z projektu pomocniczego (ISAPI) – w ten sposób obydwa projekty dzielić będą ten sam moduł WWW.
Po wybraniu typu aplikacji z okna dialogowego pokazanego na rysunku 10.1 nastąpi utworzenie pustego modułu danych i związanego z nim modułu źródłowego. Używając opcji File|Save All z menu głównego IDE, należy zapisać ów projekt w oddzielnym katalogu (np. WebServer), nadając modułowi nazwę WebMod, zaś projektowi nazwę WebShow (tych właśnie nazw używać będziemy w dalszej części rozdziału).
Przed przystąpieniem do budowy aplikacji należy wpierw zapewnić, by nie była on zależna od żadnych zewnętrznych pakietów. W tym celu należy przejść na kartę Linker opcji projektu (Shift+Ctrl+F11) i wyłączyć opcję Use dynamic RTL, a następnie na kartę Packages i wyłączyć opcję Build with runtime packages.
Moduł WWW jest już gotowy do tego, by umieszczać na nim stosowne komponenty. Komponenty te znajdują się w palecie na stronie Internet (patrz rysunek 10.2) oraz na stronie InternetExpress, którą zajmiemy się nieco później.
Rysunek 10.2. Strona Internet palety komponentów
Spoglądając na rysunek 10.2, widzimy (kolejno od lewej) ikony komponentów TClientSocket i TServerSocket (nie mają one związku z WebBrokerem), a następnie komponenty: TWebDispatcher, TPageProducer, TQueryTableProducer, TDataSetTableProducer, TDataSetPageProducer i TCppWebBrowser – na bazie tego ostatniego zbudowano aplikację, umożliwiającą śledzenie aplikacji WWW z poziomu IDE, którą to aplikację wykorzystywać będziemy w dalszej części rozdziału.
Niezależnie od komponentów znajdujących się w Palecie opiszemy komponenty kluczowe dla modułów WWW: TWebModule, TWebRequest i TWebResponse.
Komponent TWebDispatcher jest tym elementem modułu WWW, który odróżnia ów moduł od „zwykłego” modułu danych (tak więc TDataModule + TWebDispatcher = TWebModule). Jego zadaniem jest przekazywanie żądań użytkowników do odpowiednich komponentów TWebActionItem zgrupowanych w kolekcję reprezentowaną przez właściwość Actions modułu WWW.
W danej aplikacji może wystąpić tylko jeden komponent TWebDispatcher – co oznacza, iż aplikacja ta może posiadać tylko jeden moduł WWW. C++Builder nie zezwoli na umieszczenie dodatkowego komponentu TWebDispatcher w module WWW utworzonym przez kreator – zagadkowy więc wydaje się fakt, iż nie zabrania on tego w stosunku do zwykłego modułu TDataModule.
Moduł WWW, reprezentowany przez komponent TWebModule, może być uważany za „wrażliwą” na Internet odmianę modułu danych (TDataModule). Najważniejszą jego właściwością jest właściwość Actions, wskazująca kolekcję, grupującą komponenty TWebActionItem. Każdy z tych komponentów reprezentuje pewną akcję, stanowiącą odpowiednik żądania użytkownika – ich właściwość PathInfo jest mianowicie tożsama z łańcuchem występującym z treści żądania bezpośrednio po URL; za odnalezienie komponentu TWebActionItem odpowiadającego danemu żądaniu odpowiedzialny jest TWebDispatcher.
Dodawanie i usuwanie komponentów TWebActionItem realizowane jest przez tzw. edytor akcji, stanowiący specjalizowany edytor właściwości Actions. Można go uruchomić na kilka sposobów – na przykład klikając wielokropek w wierszu właściwości Actions lub wybierając opcję Action Editor z menu kontekstowego modułu WWW. W wersji 5 C++Buildera można także posłużyć się projektantem modułu danych (Visual Data Module Designer), w którego drzewie (widocznym w lewej części modułu) przedmiotowe komponenty ukazane są jako potomne w stosunku do właściwości Actions modułu, a ich zestaw zmieniać można za pomocą menu kontekstowego.
Rysunek 10.3 przedstawia uruchomiony edytor akcji, w oknie którego widoczne są pozycje reprezentujące osiem akcji odpowiadających ośmiu różnych żądaniom użytkownika: /hello, /page, /dataset, /table, /query, /login, /browse i /final. Zwróć uwagę, iż pierwszy z komponentów akcji – WebActionItem1 – jest komponentem domyślnym – jego właściwość Default ma wartość true. Jeżeli żądanie użytkownika nie odpowiada właściwości PathInfo w żadnym z komponentów akcji, wybierany jest właśnie komponent domyślny.
Rysunek 10.3. Edytor akcji modułu WWW
W kontekście wybranego przez TWebDispatcher komponentu akcji generowane jest następnie zdarzenie OnAction, w ramach obsługi którego dokonać ma się realizacja żądania użytkownika. Szkielet funkcji obsługującej to zdarzenie wygląda następująco:
void __fastcall TWebModule1::WebModule1WebActionItem1Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
}
Zanim jednak przystąpimy do oprogramowania poszczególnych akcji, konieczne jest przyjrzenie się pewnym szczegółom związanym z parametrami Request i Response.
Odpowiedź na żądanie użytkownika, generowana w ramach zdarzenia OnAction, reprezentowana jest przez parametr Response funkcji zdarzeniowej. Parametr ten posiada kilka właściwości, z których najważniejszą jest Content, zawierająca treść odpowiedzi w postaci łańcucha HTML – wykonanie poniższej funkcji spowoduje wygenerowanie odpowiedzi „Hello, world!”:
Response–>Content = "<H1>Hello, world!</H1>"
Format informacji przypisywanej właściwości Content określony jest przez właściwość ContentType. Musi mieć ona postać zgodną z jednym z formatów MIME (w postaci „typ/podtyp”) – wartością domyślną jest text/html. Odpowiedzi w formie binarnej (np. obrazki) nie mogą być jednak przekazywane wprost przez właściwość Content – do ich przekazania służy właściwość ContentStream.
Pozostałe właściwości klasy TWebResponse odpowiedzialne są za kodowanie odpowiedzi (ContendEncoding), zapisywanie cookies w komputerze klienta (Cookies), bezpieczeństwo (StatusCode, ReasonString, WWWAuthenticate, Realm), czas życia dynamicznie generowanego dokumentu (Date, Expires) i informacje diagnostyczne (LogMessage).
Parametr Request funkcji zdarzeniowej określa różnorodne aspekty żądania użytkownika. Zależnie od metody wysłania żądania (GET lub POST – patrz niżej) określonej przez właściwość Method jego treść dostępna jest albo pod właściwościami Query i QueryFields, albo pod właściwościami Content i ContentFields. Oto prosty przykład wykorzystania tej informacji w generowanej odpowiedzi:
Response->Content = "<H1>Hello, world!</H1>";
if (Request->Method == "GET")
Response->Content = Response->Content + "<B>GET</B>" +
"<BR>Query: " + Request->Query;
else
if (Request->Method == "POST")
Response->Content = Response->Content + "<B>POST</B>" +
"<BR>Content: " + Request->Content;
Do innych użytecznych właściwości klasy TWebRequest należą m.in.: CookieField (reprezentująca treść cookie odczytanego z komputera klienta), Authorization (odpowiedzialna za bezpieczeństwo) oraz Referrer i UserAgent (związane bezpośrednio z metodą wysyłania żądania).
Wysłanie żądania wg metody GET polega na umieszczeniu jego treści wprost w URL. Jest to metoda względnie szybka, lecz rozmiar przesyłanych danych jest ograniczony do co najwyżej kilku kilobajtów.
W przypadku metody POST żądanie przesyłane jest za pomocą standardowych mechanizmów wejścia-wyjścia (lub przez plik INI w przypadku WinCGI). Przesyłanie jest wolniejsze, lecz rozmiar żądania ograniczony jest tylko wielkością dostępnej przestrzeni dyskowej. Ponadto nie jest widoczna treść przesyłanych danych, nie są więc one narażone na (przypadkowe) uszkodzenie przez użytkownika.
Pora przystąpić do przetestowania pierwszej aplikacji serwera WWW. Do działania wymaga ona funkcjonującego serwera WWW, którym może być Personal Web Server (dla Windows 95/98) albo Microsoft Internet Information Server (IIS) dla Windows NT/2000. Firmowy podręcznik użytkownika (Borland C++Builder 5 Developer’s Guide), dostarczany wraz z kopią C++Buildera 5, zawiera w rozdziale 30. szczegółowe wskazówki co do skonfigurowania Personal Web Servera tak, by można było na nim testować i śledzić aplikacje CGI i biblioteki ISAPI.
Należy przypomnieć, iż biblioteki ISAPI raz załadowane przez serwer nie są rozładowywane aż do zakończenia jego pracy. Przed utworzeniem nowej wersji biblioteki należy więc zatrzymać serwer (oraz usługę administracyjną IIS), a po zakończeniu kompilacji uruchomić go ponownie (co automatycznie uruchomi również usługę administracyjną IIS). Błędnie skonstruowana biblioteka DLL może ponadto spowodować kompletne załamanie serwera, wymagające jego zatrzymania i ponownego uruchomienia. Problem ten złagodzony został w najnowszej wersji IIS, oferującej opcję Run in Separate Memory Space, powodującą odizolowanie wątpliwego procesu od pozostałych procesów serwera. Możliwe jest także rozładowywanie biblioteki ISAPI po zrealizowaniu każdego żądania i ponowne jej ładowanie po nadejściu kolejnego, co umożliwia jej aktualizację bez zatrzymywania serwera (należy w tym celu wyłączyć opcję Cache ISAPI Application dla danej biblioteki w oknie dialogowym Menedżera IIS); uczynienie tego w „rzeczywistym” serwerze spowoduje jednak utratę korzyści wynikających z szybkości protokołu ISAPI.
W charakterze narzędzia wspomagającego śledzenie aplikacji serwera WWW można użyć freeware’owego programu IntraBob, stworzonego przez jednego z autorów amerykańskiego wydania niniejszej książki. Program ten znajduje się na załączonej płycie CD-ROM w katalogu Software\IntraBob, można go także pobrać ze strony http://www.drbob42.com. Zastępuje on serwer WWW i umożliwia śledzenie bibliotek ISAPI z poziomu IDE C++Buildera lub Delphi. Należy przekopiować jego pliki do katalogu zawierającego przedmiotową bibliotekę ISAPI i użyć pliku IntraBob.exe w charakterze aplikacji nadrzędnej (Host Application), jak pokazuje to rysunek 10.4.
Rysunek 10.4. IntraBob jako aplikacja nadrzędna dla śledzonej biblioteki ISAPI
Śledzenie biblioteki DLL będzie jednakże możliwe tylko wtedy, gdy w jej kodzie źródłowym ustanowiony zostanie punkt przerwania (breakpoint). Ustawmy go więc w jednym z początkowych wierszy funkcji zdarzeniowej komponentu WebActionItem1 (rys. 10.5):
Rysunek 10.5. Ustawienie punktu przerwania w kodzie źródłowym biblioteki DLL
Aby „uruchomić” śledzoną bibliotekę ISAPI, należy wpierw przygotować formularz HTML zawierający odpowiednie żądanie skierowane do tej biblioteki, na przykład:
<HTML>
...
hansgolastyk