Вход


Главная страница >> Учебный процесс >> Конспекты >> Delphi. Delphi Book - сборник разных FAQ'ов >> Написание CGI скриптов (продолжение). >> Написание CGI скриптов (продолжение).

[Назад]    [Содержание ]    [Вперед]

  


Написание CGI скриптов (продолжение).


Написание CGI скриптов (продолжение)

        Эта статья продолжает рассмотрение вопроса по написанию CGI скриптов. Как и в предыдущей статье, в качестве системы разработки предполагается использование Inprise Delphi.

    Содержание:

    1. Отладка CGI скриптов
    2. Описание переменных среды, передаваемых скрипту.
    3. FAQ's по скриптам
    4. Ссылки

    1. Отладка CGI скриптов

        Отладка скриптов является достаточно сложной проблемой, так как он запускается сервером в отдельном процессе. По этому, для ее решения  необходимо прибегать ко всевозможным ухищрениям. Здесь Вам будет предложен один, на мой взгляд достаточно удобный, метод отладки, суть которого заключается во временном (на время отладки) преобразовании .exe модуля скрипта в динамическую библиотеку.

        Как Вы наверно помните, в предыдущей статье мы написали скрипт, проверяющий имя пользователя и пароль. Вспомним его:


// ******************************
// файл: CGI.dpr
// ******************************

program CGI;{$APPTYPE CONSOLE}uses
Windows,
CGIUN in 'CGIUN.pas';
begin
Main;
end.// ******************************
// файл: CGIUN.pas
// ******************************

unit CGIUN;
interface
uses SysUtils, Windows;
procedure Main;
implementation
var InParams: string;
// Читаем переданные параметры из переменной окружения
procedure InitParams;
var SS: string;
begin
SetLength(SS,10000);
GetEnvironmentVariable('QUERY_STRING',@SS[1],2000);
InParams:=PChar(@SS[1]);
end;
// Функция переводит шестнадчитиричный символ в число
function HexToInt(CH: char): integer;
begin
Result:=0;
case CH of
'0'..'9': Result:=Ord(CH)-Ord('0');
'A'..'F': Result:=Ord(CH)-Ord('A')+10;
'a'..'f': Result:=Ord(CH)-Ord('a')+10;
end;
end;
// Преобразует символы, записанные в виде %2B к правильному виду
function Decode(Value: string): string;
var I, L: integer;
begin
Result:='';
L:=0;
for I:=1 to Length(Value) do begin
if(Value[I]<>'%') and (Value[I]<>'+') and (L<1) then begin
Result:=Result+Value[I];
end else begin
if(Value[I]='+') then
Result:=Result+' '
else if(Value[I]='%') then begin
L:=2;
if(I<Length(Value)-1) then begin
Result:=Result+Chr(HexToInt(Value[I+1])*16+HexToInt(Value[I+2]));
end;
end else
Dec(L);
end;
end;
end;
// Возвращает значение параметра, заданного в Name
function ParamByName(Name: string): string;
var SS, ST: string;
K: integer;
begin
Result:='';
SS:=InParams;
while Length(SS)<>0 do begin
K:=Pos('&',SS);
if(K<>0) then begin
ST:=Copy(SS,1,K-1);
SS:=Copy(SS,K+1,10000);
end else begin
ST:=SS;
SS:='';
end;
K:=Pos('=',ST);
if(K<>0) then begin
if(Name=Copy(ST,1,K-1)) then begin
Result:=Decode(Copy(ST,K+1,6000));
end;
end;
end;
end;
procedure Main;
var UserId, Password: string;
begin
InitParams;
UserId:=ParamByName('USERID');
Password:=ParamByName('PASSWORD');
WriteLn('Content-Type: text/html');
WriteLn;
WriteLn('<html>');
WriteLn('<head>');
if(UserId='user1') and (Password='password') then begin
WriteLn('<title>СGI скрипт: доступ разрешен</title>');
WriteLn('</head>');
WriteLn('<body>');
WriteLn;
WriteLn('<h1>доступ разрешен</h1>');
end else begin
WriteLn('<title>СGI скрипт: ошибка авторизации</title>');
WriteLn('</head>');
WriteLn('<body>');
WriteLn;
WriteLn('<h1>ошибка авторизации</h1>');
end;
WriteLn(' UserID: <b>'+UserId+'</b>');
WriteLn(' Password: <b>'+Password+'</b>');
WriteLn('</body>');
WriteLn('</html>');
end;
end.

        Для достижения поставленной цели,
преобразуем файл cgi.dpr к следующему виду:



{$IFDEF debug}
library CGI;
{$ELSE}
program CGI;
{$ENDIF}
{$IFNDEF debug}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Windows,
CGIUN in 'CGIUN.pas',
Param in 'param.pas';
{$IFDEF debug}
procedure StartCGI(AES: integer; AEnvStr: PChar;
AGP: integer; AGetParam: PChar;
ASI: integer; AStdIn: PChar;
var ResLen: integer; var Res: PChar); cdecl; export;
begin
_StartCGI(AES,AEnvStr,AGP,AGetParam,ASI,AstdIn);
Main;
ResLen:=Length(ResVar);
if(ResLen>0) then begin
GetMem(Res,ResLen+1);
Move(ResVar[1],Res[0],ResLen+1);
end;
end;
exports StartCGI;
{$ENDIF}
begin
{$IFNDEF debug}
InitParams;
Main;
{$ENDIF}
end.

        Также создадим еще один модуль: param.pas


unit Param;
interface
procedure InitParams;
function ParamByName(Name: string): string;
function EnvironmentVariable(Name: string): string;
procedure WriteOut(SS: string);
{$IFDEF debug}
procedure _StartCGI(AES: integer; AEnvStr: PChar;
AGP: integer; AGetParam: PChar;
ASI: integer; AStdIn: PChar); cdecl;
var ResVar: string;
{$ENDIF}
implementation
uses Windows, SysUtils;
var InParams: string;
{$IFDEF debug}
EnviromentVar: string;
{$ENDIF}
procedure WriteOut(SS: string);
begin
{$IFNDEF debug}
WriteLn(SS);
{$ELSE}
ResVar:=ResVar+SS+#13#10;
{$ENDIF}
end;
{$IFDEF debug}
procedure _StartCGI(AES: integer; AEnvStr: PChar;
AGP: integer; AGetParam: PChar;
ASI: integer; AStdIn: PChar); cdecl;
var SS: string;
begin
ResVar:='';
SetLength(SS,AES+1);
Move(AEnvStr[0],SS[1],AES+1);
EnviromentVar:=SS;
if(AGP<>0) then begin
SetLength(SS,AGP+1);
Move(AGetParam[0],SS[1],AGP+1);
Param.InParams:=SS;
end else begin
SetLength(SS,ASI+1);
Move(AStdIn[0],SS[1],ASI+1);
Param.InParams:=SS;
end;
end;
{$ENDIF}
function EnvironmentVariable(Name: string): string;
var SS: string;
{$IFDEF debug}
ST: string;
K: integer;
{$ENDIF}
begin
{$IFDEF debug}
Result:='';
SS:=EnviromentVar;
while Length(SS)>0 do begin
K:=Pos(#0,SS);
if(K=0) then begin
ST:=SS;
SS:='';
end else begin
ST:=Copy(SS,1,K-1);
SS:=Copy(SS,K+1,650000);
end;
K:=Pos('=',ST);
if(K<>0) then begin
if(Copy(ST,1,K-1)=Name) then begin
Result:=Copy(ST,K+1,650000);
end;
end;
end;
{$ELSE}
SetLength(SS,2000);
GetEnvironmentVariable(PChar(Name),PChar(SS),2000);
Result:=Trim(SS);
{$ENDIF}
end;
// Читаем переданные параметры из переменной окружения
procedure InitParams;
{$IFNDEF debug}
var SS: string;
STR: string;
StdIn, Size, Actual: cardinal;
{$ENDIF}
begin
{$IFNDEF debug}
SetLength(SS,10000);
GetEnvironmentVariable('QUERY_STRING',@SS[1],2000);
InParams:=PChar(@SS[1]);
if(Trim(InParams)='') then begin
StdIn := GetStdHandle(STD_INPUT_HANDLE);
Size := SetFilePointer(StdIn, 0, nil, FILE_END);
SetFilePointer(StdIn, 0, nil, FILE_BEGIN);
SetLength(STR,Size+1);
if (Size <= 0) then Exit;
ReadFile(StdIn, STR[1], Size, Actual, nil);
STR[Size+1] := #0;
InParams:=PChar(@STR[1]);
end;
{$ENDIF}
end;
// Функция переводит шестнадчитиричный символ в число
function HexToInt(CH: char): integer;
begin
Result:=0;
case CH of
'0'..'9': Result:=Ord(CH)-Ord('0');
'A'..'F': Result:=Ord(CH)-Ord('A')+10;
'a'..'f': Result:=Ord(CH)-Ord('a')+10;
end;
end;
// Преобразует символы, записанные в виде %2B к правильному виду
function Decode(Value: string): string;
var I, L: integer;
begin
Result:='';
L:=0;
for I:=1 to Length(Value) do begin
if(Value[I]<>'%') and (Value[I]<>'+') and (L<1) then begin
Result:=Result+Value[I];
end else begin
if(Value[I]='+') then
Result:=Result+' '
else if(Value[I]='%') then begin
L:=2;
if(I<Length(Value)-1) then begin
Result:=Result+Chr(HexToInt(Value[I+1])*16+HexToInt(Value[I+2]));
end;
end else
Dec(L);
end;
end;
end;
// Возвращает значение параметра, заданного в Name function ParamByName(Name: string): string;
var SS, ST: string;
K: integer;
begin
Result:='';
SS:=InParams;
while Length(SS)<>0 do begin
K:=Pos('&',SS);
if(K<>0) then begin
ST:=Copy(SS,1,K-1);
SS:=Copy(SS,K+1,10000);
end else begin
ST:=SS;
SS:='';
end;
K:=Pos('=',ST);
if(K<>0) then begin
if(Name=Copy(ST,1,K-1)) then begin
Result:=Decode(Copy(ST,K+1,6000));
end;
end;
end;
end;
end.

        Теперь файл CGIUN.pas (содержащий функциональную часть скрипта) будет выглядить следующим образом:


// ******************************
// файл: CGIUN.pas
// ******************************
unit CGIUN;
interface
uses SysUtils, Windows, Params;
procedure Main;
implementation
procedure Main;
var UserId, Password: string;
begin
UserId:=ParamByName('USERID');
Password:=ParamByName('PASSWORD');
WriteOut('Content-Type: text/html');
WriteOut('');
WriteOut('<html>');
WriteOut('<head>');
if(UserId='user1') and (Password='password') then begin
WriteOut('<title>СGI скрипт: доступ разрешен</title>');
WriteOut('</head>');
WriteOut('<body>');
WriteOut('');
WriteOut('<h1>доступ разрешен</h1>');
end else begin
WriteOut('<title>СGI скрипт: ошибка авторизации</title>');
WriteOut('</head>');
WriteOut('<body>');
WriteOut('');
WriteOut('<h1>ошибка авторизации</h1>');
end;
WriteOut(' UserID: <b>'+UserId+'</b>');
WriteOut(' Password: <b>'+Password+'</b>');
WriteOut('</body>');
WriteOut('</html>');
end;
end.

        Давайте теперь разберемся в этом исходном коде. Проект состоит из трех файлов: CGI.dpr, Param.pas и CGIUN.pas. Рассмотрим их поподробнее:

    bol.gif (887 bytes)  CGI.dpr - это основной файл проекта. Его содержимое практически неизменно для всех скриптов. Если посмотреть на этот файл повнимательней, то можно заметить, что, определив директиву препроцессора debug, можно превратить выполняемый файл CGI скрипта в динамическую библиотеку. Именно этим свойством мы будем пользоваться при отладке скрипта.

    bol.gif (887 bytes)  Param.pas - в этот файл мною были вынесены некоторые функции, необходимые для всех CGI скриптов.

    bol.gif (887 bytes)  CGIUN.pas - в этом файле содержится функциональная часть CGI скрипта. Нужно помнить, что доступ к переменным окружения нужно осуществлять с помощью функции EnvironmnenVariable(VariableName), а выводить результат работы скрипта при помощи процедуры WriteOut(SS: string). Также больше нет необходимости в вызове функции InitParams в процедуре Main.

        Можно увидеть, что полученный проект практически не отличается от описанного в предыдущей статье. При его компиляции будет получен работоспособный скрипт.

        Теперь перейдем к сути вопроса. К отладке. Для этого нужно определить в проекте директиву препроцессора debug. Это делается следующим образом: в среду Delphi загружается наш проект, а затем в меню выбирается: Project -> Options -> Directories/Conditionals. В поле Conditionals defines пишем debug. Полностью пересобираем проект. Получим файл динамической библиотеки. Для ее отладки нам нужен Web сервер, способный работать с такими библиотеками. Его можно взять здесь.   Перепишите его в какую-либо директорию. В этой директории  создайте папку root. В root так-же создайте папку cgi-bin. В папку root необходимо поместить файл index.html (любого содержания)

        Опять возвратимся в Delphi. В Project -> Options -> Directories/Conditionals в поле Output Directory укажите путь к папке cgi-bin. Затем в Run -> Parameters в Host Applications укажите файл сервера (Tiny.exe), а в Parameters укажите путь к папке root. Теперь осталось поставить точку останова на функции Main в файле CGIUN.pas и нажать F9.

        Запустим броузер и укажем URL: http://localhost/cgi-bin/cgi.exe?password=pass&username=user Ура! Delphi выходит на точку останова. Теперь можно отлаживать скрипт.

        После отладки можно убрать директиву препроцессора debug, перекомпилировать проект и получить работоспособный CGI скрипт.

        Нужно заметить, что описанный выше способ отладки не будет работать с другими web серверами.

    2. Описание переменных среды, передаваемых скрипту.

        Как Вы наверно знаете, при запуске CGI скрипта ему передаются несколько параметров, позволяющих получить информацию о запросе. Здесь будут описаны некоторые из них.

PATH_INFO  
PATH_TRANSLATED  
REMOTE_HOST Имя компьютера, пославшего запрос
REMOTE_ADDR IP адресс компьютера, пославшего запрос
GATEWAY_INTERFACE Наименование интерфейса, по которому был вызван скрипт (CGI/1.1)
SCRIPT_NAME Выполняемый файл скрипта (/cgi-bin/cgi_t.exe)
REQUEST_METHOD Метод передачи параметров скрипту (GET или POST)
HTTP_ACCEPT Описывает результат какого типа может быть принят клиентом:
    text/html - html документ
    image/gif - картинка gif
    */* - любой
    и.т.д.
Может передаваться несколько типов через запятую
HTTP_ACCEPT_CHARSET Может содержать кодовую страницу, в которой клиент хотел-бы получить результат
HTTP_ACCEPT_ENCODING Может содержать тип кодировки, поддерживаемый клиентом (например gzip - результат может быть сжать gzip'ом)
HTTP_ACCEPT_LANGUAGE Может содержать язык на котором клиент желает получить результат (ru - русский)
HTTP_FROM  
HTTP_HOST Название хоста, к которому обратился клиент
HTTP_REFERER Содержит URL страницы, ссылающейся на скрипт
HTTP_USER_AGENT Идентификатор клиента, пославшего запрос
QUERY_STRING Если параметры передаются скрипту методом GET, то здесь содержится строка параметров запроса
SERVER_SOFTWARE Идентификатор WEB сервера
SERVER_NAME Название WEB сервера, вызвавшего скрипт
SERVER_PROTOCOL Протокол, по которому был получен запрос (HTTP/1.0, HTTP/1.1, ....)
SERVER_PORT Порт сервера, по которому был принят запрос
CONTENT_TYPE  
CONTENT_LENGTH  
USER_NAME имя пользователя
USER_PASSWORD пароль пользователя
AUTH_TYPE тип авторизации
HTTP_X_FORWARDED может содержать название прокси сервера, через который был произведен запрос
HTTP_X_FORWARDED_FOR может содержать IP адрес компьютера, пославшего запрос через прокси сервер (эту переменную устанавливают не анонимные прокси сервера)


    3. FAQ's по скриптам

    В этом разделе расположены наиболее чато задаваемые вопросы по CGI скриптам.

        1. Я написал скрипт и поместил его на Web сервер. Однако, вместо запуска, скрипт переписывается ко мне. Почему?

        Скорее всего, Вы поместили скрипт в директорию сервера, запуск скриптов из которой запрещен. В большинстве серверов директория со скриптами называется CGI-BIN или Scripts. Проверьте также, что используемый Вами сервер построен на основе Windows'95 или Windows'NT (сервера для Unix не поддерживаются). Также посмотрите следующий ответ.

        2. Написанный скрипт прекрастно работает под Internet Explorer, но при попытке обращения к нему с использованием Netscape Navigator открывается окно записи файла. Почему?

        Помните, что перед выдачей ответа, скрипт должен сообщить серверу тип документа, который он намерен вернуть в ответ на запрос. Это делается выводом перед началом документа строки вида: 'Context-Type: <type>/<subtype>', где <type>/<subtype> определяет тип документа. Наиболее часто используемый тип - text/html. Он определяет стандартный html документ.

        3. При вызове скрипта из браузера выдается ошибка 500 или появляется сообщение 'Access Violation....'

        Проверьте, что в файле проекта (*.dpr) присутствует строка {$APPTYPE CONSOLE}

    4. Ссылки

     Здесь Вы сможете найти более подробную информацию о написании CGI скриптов: http://hoohoo.ncsa.uiuc.edu/cgi/ . К сожалению, только на английском языке.




[Назад]    [Содержание ]    [Вперед]

  


  
За содержание страницы отвечает Гончарова М.Н.
©
Кафедра СПиКБ, 2002-2017