|
||||||||
|
Написание CGI скриптов (продолжение).Написание CGI скриптов (продолжение) Эта статья продолжает рассмотрение вопроса по написанию CGI скриптов. Как и в предыдущей статье, в качестве системы разработки предполагается использование Inprise Delphi. Содержание: 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. Для достижения поставленной цели, {$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. Рассмотрим их поподробнее: CGI.dpr - это основной файл проекта. Его содержимое практически неизменно для всех скриптов. Если посмотреть на этот файл повнимательней, то можно заметить, что, определив директиву препроцессора debug, можно превратить выполняемый файл CGI скрипта в динамическую библиотеку. Именно этим свойством мы будем пользоваться при отладке скрипта. Param.pas - в этот файл мною были вынесены некоторые функции, необходимые для всех CGI скриптов. 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 скрипта ему передаются несколько параметров, позволяющих получить информацию о запросе. Здесь будут описаны некоторые из них.
В этом разделе расположены наиболее чато задаваемые вопросы по 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} Здесь Вы сможете найти более подробную информацию о написании CGI скриптов: http://hoohoo.ncsa.uiuc.edu/cgi/ . К сожалению, только на английском языке.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
За содержание страницы отвечает Гончарова М.Н. © Кафедра СПиКБ, 2002-2017 |