Сервер - статьи

       

CGI


Все это, конечно, хорошо - но что мы с этого имеем? Практически ничего - ведь точно такой же результат мы бы получили, если бы просто набрали в браузере адрес: D:\Projects\HTMLProjects\MySite\Index.html. И, естественно, тот аргумент, что www.test.com/index.html набирать быстрее, устроит не всех (вернее, всех не устроит). К счастью, разрабатываем сервер мы сами - значит, можем внедрять в него все, что нам угодно: Standalone CGI, WinCGI, ISAPI (NSAPI), Apache CGI, PHP, Perl, Python, MySQL…

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

Итак. StandaloneCGI - программа, работающая под DOS или Windows (и не только, можно и под Linux, только для этого придется перекомпилировать наш сервер), которая при запуске выдает в устройство стандартного вывода требующуюся информацию. Все необходимые параметры передаются ей посредством переменных окружения.

Принцип работы сервера с такими программами таков:

  • задать необходимые переменные окружения;
  • запустить программу;
  • перенаправить результат из стандартного вывода на другой объект (например, в файл);
  • закрытие программы: Закрывается автоматически после вывода всей информации;
  • передать содержимое созданного файла клиенту;


  • удалить файл.

Для того чтобы не засорять память ненужными переменными окружения, воспользуемся функцией запуска приложений CreateProcess, которая перед запуском приложения создает для него частное адресное пространство со своими переменными окружения, которое освобождается после завершения процесса.

Прототип этой функции выглядит так:

function CreateProcess (lpApplicationName: PChar; lpCommandLine: PChar; lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): BOOL; stdcall;

Где:

  • LpApplicationName:PChar - название приложения (плюс полный путь к нему);
  • LpCommandLine:PChar - командная строка приложения (все параметры, которые передаются приложению через командную строку);
  • LpProcessAttributes, lpThreadAttributes - в нашем случае NIL (подробнее об этих параметрах читайте Win32 help);
  • BInheritHandles:Bool - в наше случае False;
  • DwCreationFlags - CREATE_NEW_PROCESS_GROUP или DETACHED_PROCESS;
  • LpEnvirounment:Pointer - указатель на строку, которая содержит переменные окружения, необходимые нашей программе;
  • LpCurrentDirectory:PChar - рабочий каталог нашей программы;
  • LpStartupInfo:TStartupInfo - параметры запуска приложения;
  • LpProcessInformation:TProcessInformation - переменная, в которую помещаются все дескрипторы запущенного приложения.

Результат: True - если приложение нормально напустилось, и False - в противном случае.


Для переадресации устройства вывода воспользуемся параметром lpStartupInfo, который имеет следующую структуру (см. таблицу 1).

Таблица 1. Параметры функции CreateProcess
Параметр Описание
cb: DWORD; Размер данной структуры
lpReserved: Pointer; Зарезервировано
lpDesktop: Pointer; Для NT - указатель на строку, которая содержит название дисплея, на который выводится информация приложения (здесь не используется)
lpTitle: Pointer; Для консольных приложений - строка, которая отображается на панели задач
dwX: DWORD; X-координата приложения (нам она не нужна - 0)
dwY: DWORD; Y-координата приложения (нам она не нужна - 0)
dwXSize: DWORD; Ширина окна приложения (нам она не нужна - 0)
dwYSize: DWORD; Высота окна приложения (нам она не нужна - 0)
dwXCountChars: DWORD; Для консольных приложений задает ширину в "текстовых единицах" (для нас - 0)
dwYCountChars: DWORD; Для консольных приложений задает высоту в "текстовых единицах" (для нас - 0)
dwFillAttribute: DWORD; Задает сочетание цвета фона и цвета символа (это не для нас - опять 0)
dwFlags: DWORD; См. таблицу 2
wShowWindow: Word; Одна из констант SW_ - режим отображения приложения (это не для нас)
cbReserved2: Word; Зарезервировано
lpReserved2: PByte; Зарезервировано
hStdInput: THandle; Дескриптор стандартного ввода
hStdOutput: THandle; Дескриптор стандартного вывода
HStdError: THandle; Дескриптор стандартного устройства вывода ошибки
Таблица 2. Описание флагов запускаемого процесса
Значение Описание
STARTF_USESHOWWINDOW Если не задан, wShowWindow игнорируется
STARTF_USEPOSITION Если не задан,, dwX, dwY игнорируются
STARTF_USESIZE Если не задан dwXSize, dwYSize игнорируются
STARTF_USECOUNTCHARS Если не задан, dwXCountChars, dwYCountChars игнорируются
STARTF_USEFILLATTRIBUTE Если не задан, dwFillAttribute игнорируется
STARTF_FORCEONFEEDBACK Очень много написано, все равно не использую
STARTF_FORCEOFFFEEDBACK Очень много написано, все равно не использую
STARTF_USESTDHANDLES Если не задан, hStdInput, hStdOutput, hStdError не используются
Как же все это будет выглядеть в программе? Для начала приведу функцию, которая возвращает в параметре Result:TStringList значения переменных окружения:



procedure CreateServerVariables (RequestInfo:TIdHttpRequestInfo;var Result:TStringList); begin if not Assigned (Result) then Result:=TStringList.Create; Result.Add ('HTTP_HOST='+RequestInfo.Host); Result.Add ('REQUEST_METHOD='+RequestInfo.Command); Result.Add ('URL='+RequestInfo.Document); Result.Add ('QUERY_STRING='+RequestInfo.UnparsedParams); Result.Add ('REMOTE_ADDR='+RequestInfo.RemoteIP); Result.Add ('HTTP_ACCEPT='+RequestInfo.Headers.Values ['Accept']); Result.Add ('HTTP_USER_AGENT='+RequestInfo.Headers.Values ['User-Agent']); Result.Add ('SERVER_PROTOCOL='+sServerProtocol); Result.Add ('SERVER_SOFTWARE='+sServerSoftware); end; Но просто передать значения Result в CreateProcess нельзя - для этого используем еще одну сервисную функцию:

function FormEnv (Data:TStringList):String; var i:integer; begin Result:=''; if Data<>nil then begin For i:=0 to Data.Count-1 do Result:=Result+Data [i]+#0; Result:=Result+#0; end; end; Нам осталось сделать переадресацию со стандартного устройства вывода в наш файл и запустить приложение:

function RunCGI (Command:PChar;Data:TStrings):PChar; var FS:TFileStream; SI:TStartupInfo; PI:TProcessInformation; SL:TStringList; Env:Pointer; EnvStr:String; begin Result:=PChar (sNoErrorNoResult); FS:=TFilestream.Create (ExtractFileDir (ParamStr (0))+'\temp.html',fmCreate); try FillChar (SI,SizeOf (SI),0); SI.cb:=SizeOf (SI); SI.dwFlags:=STARTF_USESTDHANDLES; SI.hStdOutput:=FS.Handle; SI.hStdInput:=GetStdHandle (STD_INPUT_HANDLE); SI.hStdError:=GetStdHandle (STD_ERROR_HANDLE); EnvStr:=FormEnv (Data); if not CreateProcess (Command,'',nil,nil,False, CREATE_NEW_PROCESS_GROUP or DETACHED_PROCESS,Pointer (EnvStr),PChar (ExtractFileDir (ParamStr (0))),SI,PI) then Result:=PChar (sCGIStartError) else begin if WaitForSingleObject (PI.hThread,5000)=WAIT_FAILED then begin Result:=PChar (sTimeoutError); exit; end; SL:=TStringList.Create; try FS.Position:=0; SL.LoadFromStream (FS); Result:=PChar (SL.Text); finally SL.Free; end; end; finally FS.Free; if FileExists (ExtractFileDir (ParamStr (0))+'\temp.html') then DeleteFile (ExtractFileDir (ParamStr (0))+'\temp.html'); end; end; Порядок работы:

  • сначала мы создаем файл (temp.html), в который будем переадресовывать информацию из приложения, и обнуляем переменную SI;
  • заполняем необходимые поля SI;
  • заполняем строку с переменными окружения;
  • запускаем наш CGI;
  • ждем конца выполнения (5 с);
  • передаем результат выполнения в SL, а тот, в свою очередь,- в переменную Result;
  • удаляем файл temp.html.
После выполнения этой функции возвращаемое значение передаем в ResponseInfo.ContentText.


Содержание раздела







Forekc.ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий