home_site

Lab06 - Technologia CGI [ ver. TI.2025.11.6.005 ]

Zawartość strony

Plan zajęć

Zmienne środowiskowe CGI

CGI (Common Gateway Intreface) jest to standard interfejsu opracowanego do wymiany informacji pomiędzy serwerami WWW a zewnętrznymi programami. Jest to najstarszy sposób tworzenia interaktywnych stron WWW. Standard CGI definiuje komunikację pomiędzy graficznym interfejsem użytkownika (np. stroną WWW), serwerem WWW oraz zewnętrznym programem obsługującym interfejs CGI. Aplikacja ta może być uruchomiona na dowolnym komputerze i mieć dostęp do różnych zasobów (np. baz danych). Do obsługi standardu CGI wymagana jest odpowiednia implementacja tej technologii na serwerze WWW oraz umiejętność tworzenia aplikacji z obsługą standardu CGI. Wymiana danych pomiędzy serwerem WWW a aplikacją realizowana jest poprzez udostępnienie aplikacji zmiennych środowiskowych serwera WWW oraz obsługę operacji plikowych: STDIN - przekazanie danych z serwera WWW do aplikacji, STDOUT - odbiór danych z aplikacji przez serwer WWW oraz plik STDERR - plik zawierający komunikaty diagnostyczne tworzone przez aplikację (plik ten nie jest obsługiwany przez większość serwerów WWW lub komunitaty dołączane są do logu serwera WWW). Na rys. 1 przedstawiono transefer danych pomiędzy serwerem WWW a aplikacją zgodną z standardem CGI.

Lab05_img01
Rys.1 Technologia CGI

W ramach standardu serwer WWW staje się bramą (ang. gateway) - pośrednikiem do danego źródła informacji. Poniżej przedstawiono przykładowe zmienne środowiskowe dostępne w aplikacjach wykorzystujących interfejs CGI.

Zmienna środowiskowaOpis
SCRIPT_NAMEŚcieżka URL wykonywanego skryptu
DOCUMENT_ROOTKatalog z którego pobierane są dokumenty statyczne
CONTENT_TYPTyp treści żądania
CONTENT_LENGTHDługość danych (w bajtach) przekazanych do programu CGI poprzez wejście standardowe.
REQUEST_METHODEMetoda żądania HTTP użyta w danym żądaniu.
QUERY_STRINGZapytanie zawarte w żądaniu URL.
REMOTE_IDENTJeżeli serwer HTTP wspiera identyfikację opisaną w raporcie RFC 931, zmienna zawiera nazwę użytkownika zgłaszającego żądanie.
REMOTE_USERNazwa użytkownika, uwierzytelniona przez serwer WWW.
REMOTE_HOSTNazwa zdalnego hosta klienta zgłaszającego żądanie.
REMOTE_ADDRAdres IP zdalnego hosta klienta zgłaszającego żądanie.
SERVER_NAMENazwa hosta serwera lub jego adres IP.
SERVER_SOFTWARENazwa i wersja oprogramowania na serwerze WWW.
SERVER_PROTOCOLNazwa i wersja protokołu żądania.
SERVER_PORTNumer portu hosta, na którym serwer nasłuchuje.
GATEWAY_INTERFACEWersja interfejsu CGI wykorzystywanego przez serwer
PATH_INFODodatkowa informacja o ścieżce przekazywana do programu CGI.
PATH_TRANSLATEDPrzesunięta wersja ścieżki podanej w zmiennej PATH_INFO.
AUTH_TYPEMetoda uwierzytelniania użyta do zidentyfikowania użytkownika.
HTTP_USER_AGENTNazwa i wersja przeglądarki klienta.
HTTP_ACCEPTLista typów nośnika, które klient może zaakceptować.
HTTP_ACCEPT_LANGUAGELista języków, które klient może zaakceptować.
HTTP_ACCEPT_ENCODINGLista sposobów kodowania, które klient może zaakceptować.
HTTP_REFERERURL dokumentu, który skierował użytkownika do danego programu CGI (hiperłącze, formularz)
HTTP_COOKIEPara nazwa-wartość określona przez serwer.

Skrypty CGI na serwerze Pascal

Skrypty CGI uruchamiane są przez serwer WWW, a więc najczęściej wykonują się z uprawnieniami, które zostały przyznane serwerowi WWW. Rodzi to różne niebezpieczeństwa dla systemu operacyjnego w których działa zarówno serwer WWW jak i skrypt CGI. Z tego też powodu, administratorzy zezwalają na uruchamianie aplikacji CGI z odpowiednich katalogów dla których przygotowano odpowiednie zabezpieczenia. Na serwerze Pascal skrypty CGI mogą być uruchamiane z katalogu "cgi-bin" który znajduje się w katalogu "public_html" użytkownika. Dodatkowo należy nadać plikom uprawnienia dające możliwość uruchomienia przez serwer WWW.

Przykładowe skrypty CGI

Zadaniem aplikacji (skryptu) działającego poprzez interfejs CGI jest przygotowanie pliku zawierającego dokument HTML oraz informacyjne wiersze nagłówkowe protokołu HTTP. Informacja ta jest przesyłana poprzez plik STDOUT z aplikacji do serwera WWW. Serwer WWW po odebraniu informacji dodaje pozostałe wiersze nagłówkowe łącznie z pierwszym wierszem informującym o wersji protokołu HTTP i kodzie odpowiedzi. Przykładowa odpowiedź generowana przez aplikację CGI przedstawiona została poniżej.

Content-type: text/html

<html>
<head><title>Pierwszy skrypt CGI</title></head>
<body>
   <p>Skrypt CGI</p>
</body>
<html>

Konstrukcja powyższej odpowiedzi jest następująca. Po wymaganych wierszach nagłówkowych występuje jedna linia wolna (CR,LF) a następnie dane wynikowe wygenerowane przez aplikację CGI (np. kod HTML, kod dokumentu PDF lub kod zawierający rysunek w standardzie BMP). W przypadku niepoprawnej konstrukcji odpowiedzi wygenerowanej przez aplikację serwer WWW wyśle do przeglądarki komunikat o wewnętrznym błędzie serwera (kod 500 - Intrernal Server Error).

Podsumowując, tworząc aplikację wykorzystującą interfejs CGI należy:

Programy lub skrypty CGI można tworzyć przy pomocy popularnych języków - najczęściej jest to C/C++, Java, Visual Basic, Fortran oraz języków skryptowych python, perl, TCL czy poleceń powłoki uniksowej. Warunkiem jest jednak, aby dane były pobierane i wysyłane z zachowaniem reguł określonych przez standard CGI oraz aby kod wynikowy (w przypadku programów kompilowanych) lub skrypt można było uruchomić w systemie pod kontrolą którego pracuje serwis.

Na początek przedstawiony zostanie skrypt CGI wykorzystujący polecenia powłoki systemu Linux.

#!/bin/sh
echo Content-type: text/html
echo
echo "<html><head><title>Skrypt w shell'u</title></head>"
echo "<body>"
echo "<h2>Zalogowani uzytkownicy w systemie:</h2>"
echo "<pre>"
who
echo "</pre>"
echo "</body>"
echo "</html>"

Skrypt przedstawiony poniżej drukuje zmienne środowiskowe dostępne z poziomu powłoki bash (dla serwisu WWW).

#!/bin/bash
echo "Content-type: text/html"
echo ""
echo "<html>"
echo "<head>"
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">"
echo "<title>Environment Variables</title>"
echo "</head>"
echo "<body>"
echo "Environment Variables:"
echo "<pre>"
/usr/bin/env
echo "</pre>"
echo "</body>"
echo "</html>"
exit 0

Duże możliwości przed projektantami stron WWW daje możliwość wykorzystania języka C/C++ w serwisach WWW. Dostęp do zmiennych środowiskowych w języku C otrzymuje programista poprzez funkcję getenv() zawartą w bibliotece "stdlib.h", natomiast poprzez obsługę standardowego pliku wejściowego "STDIN" otrzymujemy dostęp do ciała żądania (metoda POST).

W przykładzie umieszczonym poniżej zaprezentowano odczyt zmiennych środowiskowych zrealizowane w języku C.

/* Program nalezy skompilowac i udostepnic jego wersje wykonywalna
   gcc -o env_c.cgi env_c.c */
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
/* generaja naglowka czastowego */
printf("Content-type: text/html\n\n");
/* Wydruk kodu HTML na STDOUT */
printf("<html><head><title>Przyklad 1</title></head>\n");
printf("<body><h2>Przyklad 1</h2><br>\n");
/* Pobranie informacji o kliencie */
printf("Nazwa serwera klienta: %s<br>\n", getenv("REMOTE_HOST") );
printf("Adres IP klienta: %s<p>\n", getenv("REMOTE_ADDR") );
/* dane serwera przechowywane w zmiennych srodowiskowych */
printf("Nazwa serwera: %s<br>\n", getenv("SERVER_NAME") );
printf("Oprogramowanie na serwerze: %s<br>\n", getenv("SERVER_SOFTWARE") );
printf("Protokol serwera WWW: %s<br>\n", getenv("SERVER_PROTOCOL") );
printf("Numer portu na serwerze: %s<br>\n", getenv("SERVER_PORT") );
printf("</body>\n</html>\n");
return(0);
}

Na koniec zostanie zaprezentowany skrypt odczytujacy zmienne środowiskowe zrealizowany w języku python.

#!/usr/bin/python3 
import sys 
sys.stderr = sys.stdout 
import os 
from html import escape 
 
print ("Content-type: text/html") 
print ()
print ("<html><head><title>CGI ENV from python</title></head><body><p>") 
print ("Running:") 
print ("<b>Python %s</b><br><br>" %(sys.version) )
print ("Environmental variables:<br>") 
print ("<ul>") 
for k in sorted(os.environ): 
  print ("<li><b>%s:</b>\t\t%s<br>" %(escape(k), escape(os.environ[k]))  )
print ("</ul></p></body></html>")

Obsługa formularzy w technologii CGI

Formularze HTML jest to interfejs użytkownika, który umożliwia wprowadzanie danych i przekazywanie ich do serwera WWW. W ramach aplikacji WWW formularze realizują następujące funkcjonalności: przekazywanie danych użytkownika do aplikacji i sterowanie działaniem aplikacji. Dane z formularza przeglądarka umieszcza w żądaniach do serwera WWW. I tak w żądaniu GET dane są zawarte w łańcuchu zapytania będącego cześcią URL'a w wierszu żądania. W przypadku metody POST dane są zawarte w treści żądania HTTP. Przesyłane dane są kodowane. Domyślnym sposobem kodowania danych jest "application/x-www-form-urlencoded". Możliwe jest też kodowanie "multiform/form-data", stosowane w przypadku formularzy, które umożliwiają przesyłanie plików na serwer WWW.

Przesyłane dane z formularza przeglądarka łączy w pary nazwa=wartość, natomiast poszczególne pary łączone są przy pomocy łącznika & i przesyłane do serwera WWW w następujący sposób:

get1=aaa&get2=bbb&get3=ccc

Uwaga. W czasie kodowania znaki specjane zastępowane znakiem % i dwucyfrową liczbą szesnatkową. Znaki spacji zastępowane są znakami plus.

Na początek przygotujemy odpowiedni formularz, ktory wykorzystamy do przesłania danych na serwer zgodnie z metodą GET i POST protokołu HTTP.

Poniżej zaprezentowano przykład formularza.

<html> <head> <title>Test CGI</title></head>
<body> 
<fieldset> <legend>Form - method=get</legend> 
	<form action="cgi-bin/read.cgi" method="get"> 
	<input type="text" name="get1"><br>
 	<input type="text" name="get2"><br> 
	<input type="password" name="get3" ><br> 
	<input type="submit"> </form> </fieldset> 
<fieldset> <legend>Form - method=post</legend> 
	<form action="cgi-bin/read.cgi" method="post">
	<input type="text" name="post1"><br> 
	<input type="text" name="post2"><br> 
	<input type="password" name="post3" ><br> 
	<input type="submit"> </form> </fieldset> 
</body> </html> 

Obsługa formularzy z poziomu poleceń powłoki systemu Linux

Prosty przykład prezentujący odbiór danych przesłanych z formularza w ramach skryptu powłoki systemu Linux.

#!/bin/bash
full="$QUERY_STRING"
echo Content-type: text/html
echo 
echo "<html>"
echo "<head><title>HTML Form - GET</title></head>"
echo "<body>"
echo "$full"
echo "<h2>Environment</h2>"
echo "<pre>$(env)</pre>"
echo "</body>"
echo "</html>"

Obsługa formularzy w języku C.

W kolejnym przykładzie zaprezentowano odczyt danych z wiersza żądania dla metody GET zrealizowane w języku C.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( void ) 
{
char *ptr; 
/* Tworzenie naglowka odpowiedzi */
printf("Content-type: text/html\n\n");
/* Drukowanie kodu HTML na STDOUT */
printf("<html><head><title>Jezyk C</title></head>\n");
printf("<body><h2>Obsluga zadania GET</h2>\n");
/* Czytanie zmiennej srodowiskowej - QUERY_STRING */
printf("Informacja przeslana w zadaniu: %s<p>\n", getenv("QUERY_STRING") );
/* Wyszukanie zawartosci zmiennej */
ptr = strstr( getenv("QUERY_STRING"), "=" );
ptr = ptr + 1;
printf("Hello %s!<br>\n", ptr );
printf("</body>\n</html>\n");
return(0); 
}

Kolejny przykład prezentuje kod aplikacji w języku C przetwarzający dane przesyłane przy pomocy metody POST.

#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 80

int main(void)
{
  char *lenstr;
  char input[MAXLEN];
  long len;
  printf("%s%c%c\n","Content-Type:text/html;charset=iso-8859-2",13,10);
  printf("<TITLE>Response</TITLE>\n");
  lenstr = getenv("CONTENT_LENGTH");
  if(lenstr == NULL || sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)
     printf("<p>Error in invocation - wrong FORM probably.</p>");
  else {
     fgets(input, len+1, stdin);
     printf("\n %s",input);
  }
return 0;
}

Obsługa formularzy z wykorzystaniem języka python.

Poniżej skrypty w języku html i języku python.

<html> <head> <title>Test CGI - jezyk python</title></head>
<body> 
<fieldset> <legend>Form - method=get</legend> 
	<form action="cgi-bin/form_py.py" method="get"> 
	<input type="text" name="data1"><br>
 	<input type="text" name="data2"><br> 
	<input type="password" name="data3" ><br> 
	<input type="submit"> </form> </fieldset> 
<fieldset> <legend>Form - method=post</legend> 
	<form action="cgi-bin/read_py.py" method="post">
	<input type="text" name="data1"><br> 
	<input type="text" name="data2"><br> 
	<input type="password" name="data3" ><br> 
	<input type="submit"> </form> </fieldset> 
</body> </html> 
#!/usr/bin/env python3
import cgi
form = cgi.FieldStorage()

text1 = form.getvalue("data1","(no data)")
text2 = form.getvalue("data2","(no data)")
text3 = form.getvalue("data3","(no data)")

# print HTTP/HTML headers
print ("Content-type: text/html")
print ()

print ("""<!DOCTYPE html>
<html><head>
<title>A CGI Script</title>
</head><body>
""")

# print HTML body using form data
print ("<p>Zawartosc pola data1 - " + text1 + ".</p>")
print ("<p>Zawartosc pola data2 - " + text2 + ".</p>")
print ("<p>Zawartosc pola data3 - " + text3 + ".</p>")
#print "<p>TEST OK"
print ("</body></html>")

Obsługa skryptów CGI w języku perl.

Na początek odczyt zmiennych środowiskowych przy pomocy skryptu w języku perl. Zmienne środowiskowe dostępne są w tablicy asocjacyjnej $ENV.

#!/usr/bin/perl -wT
print "Content-type: text/html";
print "\n\n";
print <<KONIEC_HTML;
<html>
<head><title>Informacje o serwerze</title></head>
<body>
<h1>Parametry serwera</h1>
<hr><br />
<table>
<tr><td>Nazwa serwera</td>         <td>$ENV{SERVER_NAME}</td></tr>
<tr><td>Oprogramowanie serwera</td><td>$ENV{SERVER_SOFTWARE}</td></tr>
<tr><td>Protokol serwera</td>      <td>$ENV{SERVER_PROTOCOL}</td></tr>
<tr><td>Wersja CGI</td>            <td>$ENV{GATEWAY_INTERFACE}</td></tr>
</table>
</body>
<html>
KONIEC_HTML

Kolejny skrypt w języku perl wyświetli wszystkie zmienne środowiskowe serwera WWW zawarte w tabeli $ENV.

#!/usr/bin/perl
print "Content-type: text/plain\n\n";
foreach $var (sort(keys(%ENV))) {
    $val = $ENV{$var};
    $val =~ s|\n|\\n|g;
    $val =~ s|"|\\"|g;
    print "${var}=\"${val}\"\n";
}

Na koniec zaprezenotowny zostanie skrypt w języku perl do przetwarzania danych przesłanych na serwer zgodnie z metodą GET i POST protokołu HTTP.

Przykładowy formularz i odpowiedni skrypt w języku perl przetwarzający przesłane przedstawiono poniżej.

<html> <head> <title>Test CGI</title></head>
<body> 
<fieldset> <legend>Form - method=get</legend> 
	<form action="cgi-bin/read.cgi" method="get"> 
	<input type="text" name="get1"><br>
 	<input type="text" name="get2"><br> 
	<input type="password" name="get3" ><br> 
	<input type="submit"> </form> </fieldset> 
<fieldset> <legend>Form - method=post</legend> 
	<form action="cgi-bin/read.cgi" method="post">
	<input type="text" name="post1"><br> 
	<input type="text" name="post2"><br> 
	<input type="password" name="post3" ><br> 
	<input type="submit"> </form> </fieldset> 
</body> </html> 

Skrypt w perlu dekodujący przesłane dane z formularza.

#!/usr/bin/perl
# Program przetwarzajacy formularz
# Generowanie naglowka HTTP
print "Content-type: text/html\n\n";
# Generowanie dokumentu HTML
print "<html><head>\n";
print "<title> Display query string data  </title></head> \n";
print "<body>\n";
# Rozpoznanie metody przeslania danych przez formularz
$request_method = $ENV{'REQUEST_METHOD'};
if ($request_method eq "GET")
  {  $query_string = $ENV{'QUERY_STRING'};
     print " Metoda przeslania formularza - GET <br><hr>" ;  }
elsif ($request_method eq "POST")
  {  read(STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
     print " Metoda przeslania formularza - post <br><hr>" ;  }
else
  {  print "Error - request method is illegal \n";   }
# Podzial "query string" na pare wartosci "name=value"
@name_value_pairs = split(/&/, $query_string);
foreach $name_value (@name_value_pairs)
{
    ($name, $value) = split (/=/, $name_value);
    print "Para  'name=value' zawiera nastepujace dane: $name, $value \n<br>";
}
print "</body> </html> \n";