lwebserver.pp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. { Web server component, built on the HTTP server component
  2. Copyright (C) 2006 Micha Nelissen
  3. This library is Free software; you can redistribute it and/or modify it
  4. under the terms of the GNU Library General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at your
  6. option) any later version.
  7. This program is diStributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  10. for more details.
  11. You should have received a Copy of the GNU Library General Public License
  12. along with This library; if not, Write to the Free Software Foundation,
  13. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. This license has been modified. See file LICENSE.ADDON for more information.
  15. Should you find these sources without a LICENSE File, please contact
  16. me at [email protected]
  17. }
  18. unit lwebserver;
  19. {$mode objfpc}{$h+}
  20. {$inline on}
  21. interface
  22. uses
  23. sysutils, classes, lhttp, lhttputil, lmimetypes, levents,
  24. lprocess, process, lfastcgi, fastcgi;
  25. type
  26. TLMultipartParameter = (mpContentType, mpContentDisposition, mpContentTransferEncoding,
  27. mpContentID, mpContentDescription);
  28. TLMultipartState = (msStart, msBodypartHeader, msBodypartData);
  29. const
  30. URIParamSepChar: char = '&';
  31. CookieSepChar: char = ';';
  32. FormURLContentType: pchar = 'application/x-www-form-urlencoded';
  33. MultipartContentType: pchar = 'multipart/form-data';
  34. MPParameterStrings: array[TLMultipartParameter] of string =
  35. ('Content-Type', 'Content-Disposition', 'Content-Transfer-Encoding',
  36. 'Content-ID', 'Content-Discription');
  37. type
  38. TDocumentHandler = class;
  39. TFileHandler = class;
  40. TFileOutput = class(TBufferOutput)
  41. protected
  42. FFile: file;
  43. function GetSize: integer;
  44. function FillBuffer: TWriteBlockStatus; override;
  45. public
  46. constructor Create(ASocket: TLHTTPSocket);
  47. destructor Destroy; override;
  48. function Open(const AFileName: string): boolean;
  49. property Size: integer read GetSize;
  50. end;
  51. TCGIOutput = class(TBufferOutput)
  52. protected
  53. FParsePos: pchar;
  54. FReadPos: integer;
  55. FParsingHeaders: boolean;
  56. FDocumentRoot: string;
  57. FExtraPath: string;
  58. FEnvPath: string;
  59. FScriptFileName: string;
  60. FScriptName: string;
  61. procedure AddEnvironment(const AName, AValue: string); virtual; abstract;
  62. procedure AddHTTPParam(const AName: string; AParam: TLHTTPParameter);
  63. function ParseHeaders: boolean;
  64. procedure CGIOutputError; virtual; abstract;
  65. procedure WriteCGIBlock;
  66. function WriteCGIData: TWriteBlockStatus; virtual; abstract;
  67. public
  68. constructor Create(ASocket: TLHTTPSocket);
  69. destructor Destroy; override;
  70. function FillBuffer: TWriteBlockStatus; override;
  71. procedure StartRequest; virtual;
  72. property DocumentRoot: string read FDocumentRoot write FDocumentRoot;
  73. property EnvPath: string read FEnvPath write FEnvPath;
  74. property ExtraPath: string read FExtraPath write FExtraPath;
  75. property ScriptFileName: string read FScriptFileName write FScriptFileName;
  76. property ScriptName: string read FScriptName write FScriptName;
  77. end;
  78. TSimpleCGIOutput = class(TCGIOutput)
  79. protected
  80. FProcess: TLProcess;
  81. procedure AddEnvironment(const AName, AValue: string); override;
  82. procedure CGIProcNeedInput(AHandle: TLHandle);
  83. procedure CGIProcHasOutput(AHandle: TLHandle);
  84. procedure CGIProcHasStderr(AHandle: TLHandle);
  85. procedure DoneInput; override;
  86. function HandleInput(ABuffer: pchar; ASize: integer): integer; override;
  87. procedure CGIOutputError; override;
  88. function WriteCGIData: TWriteBlockStatus; override;
  89. public
  90. constructor Create(ASocket: TLHTTPSocket);
  91. destructor Destroy; override;
  92. procedure StartRequest; override;
  93. property Process: TLProcess read FProcess;
  94. end;
  95. TFastCGIOutput = class(TCGIOutput)
  96. protected
  97. FRequest: TLFastCGIRequest;
  98. procedure AddEnvironment(const AName, AValue: string); override;
  99. procedure CGIOutputError; override;
  100. procedure DoneInput; override;
  101. procedure RequestEnd(ARequest: TLFastCGIRequest);
  102. procedure RequestNeedInput(ARequest: TLFastCGIRequest);
  103. procedure RequestHasOutput(ARequest: TLFastCGIRequest);
  104. procedure RequestHasStderr(ARequest: TLFastCGIRequest);
  105. function HandleInput(ABuffer: pchar; ASize: integer): integer; override;
  106. function WriteCGIData: TWriteBlockStatus; override;
  107. function WriteBlock: TWriteBlockStatus; override;
  108. public
  109. constructor Create(ASocket: TLHTTPSocket);
  110. destructor Destroy; override;
  111. procedure StartRequest; override;
  112. property Request: TLFastCGIRequest read FRequest write FRequest;
  113. end;
  114. TCGIHandler = class(TURIHandler)
  115. protected
  116. FScriptPathPrefix: string;
  117. FCGIRoot: string;
  118. FDocumentRoot: string;
  119. FEnvPath: string;
  120. function HandleURI(ASocket: TLHTTPServerSocket): TOutputItem; override;
  121. public
  122. property CGIRoot: string read FCGIRoot write FCGIRoot;
  123. property DocumentRoot: string read FDocumentRoot write FDocumentRoot;
  124. property EnvPath: string read FEnvPath write FEnvPath;
  125. property ScriptPathPrefix: string read FScriptPathPrefix write FScriptPathPrefix;
  126. end;
  127. TDocumentRequest = record
  128. Socket: TLHTTPServerSocket;
  129. Document: string;
  130. URIPath: string;
  131. ExtraPath: string;
  132. Info: TSearchRec;
  133. InfoValid: boolean;
  134. end;
  135. TDocumentHandler = class(TObject)
  136. private
  137. FNext: TDocumentHandler;
  138. protected
  139. FFileHandler: TFileHandler;
  140. procedure RegisterWithEventer(AEventer: TLEventer); virtual;
  141. public
  142. function HandleDocument(const ARequest: TDocumentRequest): TOutputItem; virtual; abstract;
  143. property FileHandler: TFileHandler read FFileHandler;
  144. end;
  145. { TFileHandler }
  146. TFileHandler = class(TURIHandler)
  147. protected
  148. FDocHandlerList: TDocumentHandler;
  149. FDirIndexList: TStrings;
  150. protected
  151. FDocumentRoot: string;
  152. FMimeTypeFile: string;
  153. procedure SetMimeTypeFile(const AValue: string);
  154. function HandleFile(const ARequest: TDocumentRequest): TOutputItem;
  155. function HandleURI(ASocket: TLHTTPServerSocket): TOutputItem; override;
  156. procedure RegisterWithEventer(AEventer: TLEventer); override;
  157. public
  158. constructor Create;
  159. destructor Destroy; override;
  160. procedure RegisterHandler(AHandler: TDocumentHandler);
  161. property DirIndexList: TStrings read FDirIndexList;
  162. property DocumentRoot: string read FDocumentRoot write FDocumentRoot;
  163. property MimeTypeFile: string read FMimeTypeFile write SetMimeTypeFile;
  164. end;
  165. TPHPCGIHandler = class(TDocumentHandler)
  166. protected
  167. FAppName: string;
  168. FEnvPath: string;
  169. public
  170. function HandleDocument(const ARequest: TDocumentRequest): TOutputItem; override;
  171. property AppName: string read FAppName write FAppName;
  172. property EnvPath: string read FEnvPath write FEnvPath;
  173. end;
  174. TPHPFastCGIHandler = class(TDocumentHandler)
  175. protected
  176. FPool: TLFastCGIPool;
  177. FEnvPath: string;
  178. function GetAppEnv: string;
  179. function GetAppName: string;
  180. function GetHost: string;
  181. function GetPort: integer;
  182. procedure RegisterWithEventer(AEventer: TLEventer); override;
  183. procedure SetAppEnv(NewEnv: string);
  184. procedure SetAppName(NewName: string);
  185. procedure SetHost(NewHost: string);
  186. procedure SetPort(NewPort: integer);
  187. public
  188. constructor Create;
  189. destructor Destroy; override;
  190. function HandleDocument(const ARequest: TDocumentRequest): TOutputItem; override;
  191. property AppEnv: string read GetAppEnv write SetAppEnv;
  192. property AppName: string read GetAppName write SetAppName;
  193. property EnvPath: string read FEnvPath write FEnvPath;
  194. property Host: string read GetHost write SetHost;
  195. property Pool: TLFastCGIPool read FPool;
  196. property Port: integer read GetPort write SetPort;
  197. end;
  198. { Forms }
  199. TFormOutput = class;
  200. TFillBufferEvent = procedure(AFormOutput: TFormOutput; var AStatus: TWriteBlockStatus);
  201. THandleInputMethod = function(ABuffer: pchar; ASize: integer): integer of object;
  202. TFormOutput = class(TBufferOutput)
  203. protected
  204. FBoundary: pchar;
  205. FRequestVars: TStrings;
  206. FMPParameters: array[TLMultipartParameter] of pchar;
  207. FMPState: TLMultipartState;
  208. FOnExtraHeaders: TNotifyEvent;
  209. FOnFillBuffer: TFillBufferEvent;
  210. FHandleInput: THandleInputMethod;
  211. procedure DoneInput; override;
  212. function FillBuffer: TWriteBlockStatus; override;
  213. function FindBoundary(ABuffer: pchar): pchar;
  214. function HandleInput(ABuffer: pchar; ASize: integer): integer; override;
  215. function HandleInputDiscard(ABuffer: pchar; ASize: integer): integer;
  216. function HandleInputFormURL(ABuffer: pchar; ASize: integer): integer;
  217. function HandleInputMultipart(ABuffer: pchar; ASize: integer): integer;
  218. procedure ParseMultipartHeader(ABuffer, ALineEnd: pchar);
  219. public
  220. constructor Create(ASocket: TLHTTPSocket);
  221. destructor Destroy; override;
  222. function AddVariables(Variables: pchar; ASize: integer; SepChar: char): integer;
  223. procedure DeleteCookie(const AName: string; const APath: string = '/';
  224. const ADomain: string = '');
  225. procedure SetCookie(const AName, AValue: string; const AExpires: TDateTime;
  226. const APath: string = '/'; const ADomain: string = '');
  227. property OnExtraHeaders: TNotifyEvent read FOnExtraHeaders write FOnExtraHeaders;
  228. property OnFillBuffer: TFillBufferEvent read FOnFillBuffer write FOnFillBuffer;
  229. end;
  230. THandleURIEvent = function(ASocket: TLHTTPServerSocket): TFormOutput;
  231. TFormHandler = class(TURIHandler)
  232. protected
  233. FOnHandleURI: THandleURIEvent;
  234. function HandleURI(ASocket: TLHTTPServerSocket): TOutputItem; override;
  235. procedure SelectMultipart(AFormOutput: TFormOutput; AContentType: pchar);
  236. public
  237. property OnHandleURI: THandleURIEvent read FOnHandleURI write FOnHandleURI;
  238. end;
  239. var
  240. EnableWriteln: Boolean = True;
  241. implementation
  242. uses
  243. lstrbuffer;
  244. { Example handlers }
  245. const
  246. InputBufferEmptyToWriteStatus: array[boolean] of TWriteBlockStatus =
  247. (wsPendingData, wsWaitingData);
  248. procedure InternalWrite(const s: string);
  249. begin
  250. if EnableWriteln then
  251. Writeln(s);
  252. end;
  253. procedure TDocumentHandler.RegisterWithEventer(AEventer: TLEventer);
  254. begin
  255. end;
  256. function TCGIHandler.HandleURI(ASocket: TLHTTPServerSocket): TOutputItem;
  257. var
  258. lOutput: TSimpleCGIOutput;
  259. lExecPath: string;
  260. begin
  261. if StrLComp(ASocket.RequestInfo.Argument, PChar(ScriptPathPrefix),
  262. Length(ScriptPathPrefix)) = 0 then
  263. begin
  264. lOutput := TSimpleCGIOutput.Create(ASocket);
  265. lOutput.DocumentRoot := FDocumentRoot;
  266. lOutput.EnvPath := FEnvPath;
  267. lOutput.Process.CurrentDirectory := FCGIRoot;
  268. lExecPath := (ASocket.RequestInfo.Argument+Length(ScriptPathPrefix));
  269. DoDirSeparators(lExecPath);
  270. lExecPath := FCGIRoot+lExecPath;
  271. if SeparatePath(lExecPath, lOutput.ExtraPath, faAnyFile and not faDirectory) then
  272. begin
  273. lOutput.Process.CommandLine := lExecPath;
  274. lOutput.ScriptFileName := lExecPath;
  275. lOutput.ScriptName := Copy(lExecPath, Length(FCGIRoot),
  276. Length(lExecPath)-Length(FCGIRoot)+1);
  277. lOutput.StartRequest;
  278. end else
  279. ASocket.ResponseInfo.Status := hsNotFound;
  280. Result := lOutput;
  281. end else
  282. Result := nil;
  283. end;
  284. constructor TFileHandler.Create;
  285. begin
  286. inherited;
  287. FDirIndexList := TStringList.Create;
  288. end;
  289. destructor TFileHandler.Destroy;
  290. begin
  291. FreeAndNil(FDirIndexList);
  292. inherited;
  293. end;
  294. procedure TFileHandler.RegisterWithEventer(AEventer: TLEventer);
  295. var
  296. lHandler: TDocumentHandler;
  297. begin
  298. lHandler := FDocHandlerList;
  299. while lHandler <> nil do
  300. begin
  301. lHandler.RegisterWithEventer(AEventer);
  302. lHandler := lHandler.FNext;
  303. end;
  304. end;
  305. procedure TFileHandler.SetMimeTypeFile(const AValue: string);
  306. begin
  307. FMimeTypeFile:=AValue;
  308. InitMimeList(aValue);
  309. end;
  310. function TFileHandler.HandleFile(const ARequest: TDocumentRequest): TOutputItem;
  311. var
  312. lFileOutput: TFileOutput;
  313. lReqInfo: PRequestInfo;
  314. lRespInfo: PResponseInfo;
  315. lHeaderOut: PHeaderOutInfo;
  316. lIndex: integer;
  317. begin
  318. Result := nil;
  319. if ARequest.InfoValid then
  320. begin
  321. lReqInfo := @ARequest.Socket.RequestInfo;
  322. lRespInfo := @ARequest.Socket.ResponseInfo;
  323. lHeaderOut := @ARequest.Socket.HeaderOut;
  324. if not (lReqInfo^.RequestType in [hmHead, hmGet]) then
  325. begin
  326. lRespInfo^.Status := hsNotAllowed;
  327. end else begin
  328. lFileOutput := TFileOutput.Create(ARequest.Socket);
  329. if lFileOutput.Open(ARequest.Document) then
  330. begin
  331. lRespInfo^.Status := hsOK;
  332. lHeaderOut^.ContentLength := ARequest.Info.Size;
  333. lRespInfo^.LastModified := LocalTimeToGMT(FileDateToDateTime(ARequest.Info.Time));
  334. lIndex := MimeList.IndexOf(ExtractFileExt(ARequest.Document));
  335. if lIndex >= 0 then
  336. lRespInfo^.ContentType := TStringObject(MimeList.Objects[lIndex]).Str;
  337. Result := lFileOutput;
  338. ARequest.Socket.StartResponse(lFileOutput);
  339. end else
  340. lFileOutput.Free;
  341. end;
  342. end;
  343. end;
  344. function TFileHandler.HandleURI(ASocket: TLHTTPServerSocket): TOutputItem;
  345. var
  346. lDocRequest: TDocumentRequest;
  347. lHandler: TDocumentHandler;
  348. lTempDoc: string;
  349. lDirIndexFound: boolean;
  350. I: integer;
  351. begin
  352. Result := nil;
  353. lDocRequest.Socket := ASocket;
  354. lDocRequest.URIPath := ASocket.RequestInfo.Argument;
  355. lDocRequest.Document := lDocRequest.URIPath;
  356. DoDirSeparators(LDocRequest.Document);
  357. lDocRequest.Document := IncludeTrailingPathDelimiter(FDocumentRoot)+lDocRequest.Document;
  358. lDocRequest.InfoValid := SeparatePath(lDocRequest.Document,lDocRequest.ExtraPath,
  359. faAnyFile, @lDocRequest.Info);
  360. if not lDocRequest.InfoValid then
  361. exit;
  362. if (lDocRequest.Info.Attr and faDirectory) <> 0 then
  363. begin
  364. lDirIndexFound := false;
  365. { if non-trivial ExtraPath, then it's not a pure directory request, so do
  366. not show default directory document }
  367. if lDocRequest.ExtraPath = PathDelim then
  368. begin
  369. lDocRequest.Document := IncludeTrailingPathDelimiter(lDocRequest.Document);
  370. for I := 0 to FDirIndexList.Count - 1 do
  371. begin
  372. lTempDoc := lDocRequest.Document + FDirIndexList.Strings[I];
  373. lDocRequest.InfoValid := FindFirst(lTempDoc,
  374. faAnyFile and not faDirectory, lDocRequest.Info) = 0;
  375. FindClose(lDocRequest.Info);
  376. if lDocRequest.InfoValid and ((lDocRequest.Info.Attr and faDirectory) = 0) then
  377. begin
  378. lDocRequest.Document := lTempDoc;
  379. lDirIndexFound := true;
  380. break;
  381. end;
  382. end;
  383. end;
  384. { requested a directory, but no source to show }
  385. if not lDirIndexFound then exit;
  386. end;
  387. lHandler := FDocHandlerList;
  388. while lHandler <> nil do
  389. begin
  390. Result := lHandler.HandleDocument(lDocRequest);
  391. if Result <> nil then exit;
  392. if ASocket.ResponseInfo.Status <> hsOK then exit;
  393. lHandler := lHandler.FNext;
  394. end;
  395. { no dynamic handler, see if it's a plain file }
  396. Result := HandleFile(lDocRequest);
  397. end;
  398. procedure TFileHandler.RegisterHandler(AHandler: TDocumentHandler);
  399. begin
  400. if AHandler = nil then exit;
  401. AHandler.FFileHandler := Self;
  402. AHandler.FNext := FDocHandlerList;
  403. FDocHandlerList := AHandler;
  404. end;
  405. function TPHPCGIHandler.HandleDocument(const ARequest: TDocumentRequest): TOutputItem;
  406. var
  407. lOutput: TSimpleCGIOutput;
  408. begin
  409. if ExtractFileExt(ARequest.Document) = '.php' then
  410. begin
  411. lOutput := TSimpleCGIOutput.Create(ARequest.Socket);
  412. lOutput.DocumentRoot := FFileHandler.DocumentRoot;
  413. lOutput.Process.CommandLine := FAppName;
  414. lOutput.ScriptName := ARequest.URIPath;
  415. lOutput.ScriptFileName := ARequest.Document;
  416. lOutput.ExtraPath := ARequest.ExtraPath;
  417. lOutput.EnvPath := FEnvPath;
  418. lOutput.StartRequest;
  419. Result := lOutput;
  420. end else
  421. Result := nil;
  422. end;
  423. constructor TPHPFastCGIHandler.Create;
  424. begin
  425. inherited;
  426. FPool := TLFastCGIPool.Create;
  427. end;
  428. destructor TPHPFastCGIHandler.Destroy;
  429. begin
  430. inherited;
  431. FPool.Free;
  432. end;
  433. function TPHPFastCGIHandler.GetAppEnv: string;
  434. begin
  435. Result := FPool.AppEnv;
  436. end;
  437. function TPHPFastCGIHandler.GetAppName: string;
  438. begin
  439. Result := FPool.AppName;
  440. end;
  441. function TPHPFastCGIHandler.GetHost: string;
  442. begin
  443. Result := FPool.Host;
  444. end;
  445. function TPHPFastCGIHandler.GetPort: integer;
  446. begin
  447. Result := FPool.Port;
  448. end;
  449. procedure TPHPFastCGIHandler.SetAppEnv(NewEnv: string);
  450. begin
  451. FPool.AppEnv := NewEnv;
  452. end;
  453. procedure TPHPFastCGIHandler.SetAppName(NewName: string);
  454. begin
  455. FPool.AppName := NewName;
  456. end;
  457. procedure TPHPFastCGIHandler.SetHost(NewHost: string);
  458. begin
  459. FPool.Host := NewHost;
  460. end;
  461. procedure TPHPFastCGIHandler.SetPort(NewPort: integer);
  462. begin
  463. FPool.Port := NewPort;
  464. end;
  465. procedure TPHPFastCGIHandler.RegisterWithEventer(AEventer: TLEventer);
  466. begin
  467. FPool.Eventer := AEventer;
  468. end;
  469. function TPHPFastCGIHandler.HandleDocument(const ARequest: TDocumentRequest): TOutputItem;
  470. var
  471. lOutput: TFastCGIOutput;
  472. fcgiRequest: TLFastCGIRequest;
  473. begin
  474. if ExtractFileExt(ARequest.Document) = '.php' then
  475. begin
  476. fcgiRequest := FPool.BeginRequest(FCGI_RESPONDER);
  477. if fcgiRequest <> nil then
  478. begin
  479. lOutput := TFastCGIOutput.Create(ARequest.Socket);
  480. lOutput.DocumentRoot := FFileHandler.DocumentRoot;
  481. lOutput.ScriptName := ARequest.URIPath;
  482. lOutput.ScriptFileName := ARequest.Document;
  483. lOutput.ExtraPath := ARequest.ExtraPath;
  484. lOutput.EnvPath := FEnvPath;
  485. lOutput.Request := fcgiRequest;
  486. ARequest.Socket.SetupEncoding(lOutput);
  487. lOutput.StartRequest;
  488. Result := lOutput;
  489. end else begin
  490. ARequest.Socket.ResponseInfo.Status := hsInternalError;
  491. ARequest.Socket.StartResponse(nil);
  492. Result := nil;
  493. end;
  494. end else
  495. Result := nil;
  496. end;
  497. { Output Items }
  498. constructor TFileOutput.Create(ASocket: TLHTTPSocket);
  499. begin
  500. inherited;
  501. FEof := true;
  502. end;
  503. destructor TFileOutput.Destroy;
  504. begin
  505. inherited;
  506. if not FEof then
  507. Close(FFile);
  508. end;
  509. function TFileOutput.Open(const AFileName: string): boolean;
  510. begin
  511. {$I-}
  512. FileMode := 0;
  513. Assign(FFile, AFileName);
  514. Reset(FFile,1);
  515. {$I+}
  516. Result := IOResult = 0;
  517. FEof := false;
  518. end;
  519. function TFileOutput.GetSize: integer; inline;
  520. begin
  521. Result := FileSize(FFile);
  522. end;
  523. function TFileOutput.FillBuffer: TWriteBlockStatus;
  524. var
  525. lRead: integer;
  526. begin
  527. if FEof then
  528. exit(wsDone);
  529. BlockRead(FFile, FBuffer[FBufferPos], FBufferSize-FBufferPos, lRead);
  530. Inc(FBufferPos, lRead);
  531. if lRead = 0 then
  532. begin
  533. { EOF reached }
  534. Close(FFile);
  535. exit(wsDone);
  536. end;
  537. Result := wsPendingData;
  538. end;
  539. constructor TCGIOutput.Create(ASocket: TLHTTPSocket);
  540. begin
  541. inherited;
  542. end;
  543. destructor TCGIOutput.Destroy;
  544. begin
  545. inherited;
  546. end;
  547. procedure TCGIOutput.AddHTTPParam(const AName: string; AParam: TLHTTPParameter);
  548. var
  549. lValue: pchar;
  550. begin
  551. lValue := FSocket.Parameters[AParam];
  552. if lValue = nil then exit;
  553. AddEnvironment(AName, lValue);
  554. end;
  555. procedure TCGIOutput.StartRequest;
  556. var
  557. lServerSocket: TLHTTPServerSocket absolute FSocket;
  558. tempStr: string;
  559. begin
  560. {
  561. FProcess.Environment.Add('SERVER_ADDR=');
  562. FProcess.Environment.Add('SERVER_ADMIN=');
  563. FProcess.Environment.Add('SERVER_NAME=');
  564. FProcess.Environment.Add('SERVER_PORT=');
  565. }
  566. tempStr := TLHTTPServer(lServerSocket.Creator).ServerSoftware;
  567. if Length(tempStr) > 0 then
  568. AddEnvironment('SERVER_SOFTWARE', tempStr);
  569. AddEnvironment('GATEWAY_INTERFACE', 'CGI/1.1');
  570. AddEnvironment('SERVER_PROTOCOL', lServerSocket.RequestInfo.VersionStr);
  571. AddEnvironment('REQUEST_METHOD', lServerSocket.RequestInfo.Method);
  572. AddEnvironment('REQUEST_URI', '/'+lServerSocket.RequestInfo.Argument);
  573. if Length(FExtraPath) > 0 then
  574. begin
  575. AddEnvironment('PATH_INFO', FExtraPath);
  576. { do not set PATH_TRANSLATED: bug in PHP }
  577. // AddEnvironment('PATH_TRANSLATED', FDocumentRoot+FExtraPath);
  578. end;
  579. AddEnvironment('SCRIPT_NAME', FScriptName);
  580. AddEnvironment('SCRIPT_FILENAME', FScriptFileName);
  581. AddEnvironment('QUERY_STRING', lServerSocket.RequestInfo.QueryParams);
  582. AddHTTPParam('CONTENT_TYPE', hpContentType);
  583. AddHTTPParam('CONTENT_LENGTH', hpContentLength);
  584. AddEnvironment('REMOTE_ADDR', FSocket.PeerAddress);
  585. AddEnvironment('REMOTE_PORT', IntToStr(FSocket.LocalPort));
  586. { used when user has authenticated in some way to server }
  587. // AddEnvironment('AUTH_TYPE='+...);
  588. // AddEnvironment('REMOTE_USER='+...);
  589. AddEnvironment('DOCUMENT_ROOT', FDocumentRoot);
  590. AddEnvironment('REDIRECT_STATUS', '200');
  591. AddHTTPParam('HTTP_HOST', hpHost);
  592. AddHTTPParam('HTTP_COOKIE', hpCookie);
  593. AddHTTPParam('HTTP_CONNECTION', hpConnection);
  594. AddHTTPParam('HTTP_REFERER', hpReferer);
  595. AddHTTPParam('HTTP_USER_AGENT', hpUserAgent);
  596. AddHTTPParam('HTTP_ACCEPT', hpAccept);
  597. AddEnvironment('PATH', FEnvPath);
  598. FParsingHeaders := true;
  599. FReadPos := FBufferPos;
  600. FParsePos := FBuffer+FReadPos;
  601. end;
  602. function TCGIOutput.ParseHeaders: boolean;
  603. var
  604. lHttpStatus: TLHTTPStatus;
  605. iEnd, lCode: integer;
  606. lStatus, lLength: dword;
  607. pLineEnd, pNextLine, pValue: pchar;
  608. lServerSocket: TLHTTPServerSocket absolute FSocket;
  609. procedure AddExtraHeader;
  610. begin
  611. AppendString(lServerSocket.HeaderOut.ExtraHeaders, FParsePos + ': ' + pValue + #13#10);
  612. end;
  613. begin
  614. repeat
  615. iEnd := IndexByte(FParsePos^, @FBuffer[FReadPos]-FParsePos, 10);
  616. if iEnd = -1 then exit(false);
  617. pNextLine := FParsePos+iEnd+1;
  618. if (iEnd > 0) and (FParsePos[iEnd-1] = #13) then
  619. dec(iEnd);
  620. pLineEnd := FParsePos+iEnd;
  621. pLineEnd^ := #0;
  622. if pLineEnd = FParsePos then
  623. begin
  624. { empty line signals end of headers }
  625. FParsingHeaders := false;
  626. FBufferOffset := pNextLine-FBuffer;
  627. FBufferPos := FReadPos;
  628. FReadPos := 0;
  629. lServerSocket.StartResponse(Self, true);
  630. exit(false);
  631. end;
  632. iEnd := IndexByte(FParsePos^, iEnd, ord(':'));
  633. if (iEnd = -1) or (FParsePos[iEnd+1] <> ' ') then
  634. break;
  635. FParsePos[iEnd] := #0;
  636. pValue := FParsePos+iEnd+2;
  637. if StrIComp(FParsePos, 'Content-type') = 0 then
  638. begin
  639. lServerSocket.ResponseInfo.ContentType := pValue;
  640. end else
  641. if StrIComp(FParsePos, 'Location') = 0 then
  642. begin
  643. if StrLIComp(pValue, 'http://', 7) = 0 then
  644. begin
  645. lServerSocket.ResponseInfo.Status := hsMovedPermanently;
  646. { add location header as-is to response }
  647. AddExtraHeader;
  648. end else
  649. InternalWrite('WARNING: unimplemented ''Location'' response received from CGI script');
  650. end else
  651. if StrIComp(FParsePos, 'Status') = 0 then
  652. begin
  653. { sometimes we get '<status code> space <reason>' }
  654. iEnd := IndexByte(pValue^, pLineEnd-pValue, ord(' '));
  655. if iEnd <> -1 then
  656. pValue[iEnd] := #0;
  657. Val(pValue, lStatus, lCode);
  658. if lCode <> 0 then
  659. break;
  660. for lHttpStatus := Low(TLHTTPStatus) to High(TLHTTPStatus) do
  661. if HTTPStatusCodes[lHttpStatus] = lStatus then
  662. lServerSocket.ResponseInfo.Status := lHttpStatus;
  663. end else
  664. if StrIComp(FParsePos, 'Content-Length') = 0 then
  665. begin
  666. Val(pValue, lLength, lCode);
  667. if lCode <> 0 then
  668. break;
  669. lServerSocket.HeaderOut.ContentLength := lLength;
  670. end else
  671. if StrIComp(FParsePos, 'Last-Modified') = 0 then
  672. begin
  673. if not TryHTTPDateStrToDateTime(pValue,
  674. lServerSocket.ResponseInfo.LastModified) then
  675. InternalWrite('WARNING: unable to parse last-modified string from CGI script: ' + pValue);
  676. end else
  677. AddExtraHeader;
  678. FParsePos := pNextLine;
  679. until false;
  680. { error happened }
  681. lServerSocket.ResponseInfo.Status := hsInternalError;
  682. exit(true);
  683. end;
  684. function TCGIOutput.FillBuffer: TWriteBlockStatus;
  685. begin
  686. if not FParsingHeaders then
  687. FReadPos := FBufferPos;
  688. Result := WriteCGIData;
  689. if FParsingHeaders then
  690. begin
  691. if ParseHeaders then
  692. begin
  693. { error while parsing }
  694. FEof := true;
  695. exit(wsDone);
  696. end;
  697. end else
  698. FBufferPos := FReadPos;
  699. end;
  700. procedure TCGIOutput.WriteCGIBlock;
  701. begin
  702. { CGI process has output pending, we can write a block to socket }
  703. if FParsingHeaders then
  704. begin
  705. if (FillBuffer = wsDone) and FParsingHeaders then
  706. begin
  707. { still parsing headers ? something's wrong }
  708. FParsingHeaders := false;
  709. CGIOutputError;
  710. TLHTTPServerSocket(FSocket).StartResponse(Self);
  711. end;
  712. end;
  713. if not FParsingHeaders then
  714. FSocket.WriteBlock;
  715. end;
  716. { TSimpleCGIOutput }
  717. constructor TSimpleCGIOutput.Create(ASocket: TLHTTPSocket);
  718. begin
  719. inherited;
  720. FProcess := TLProcess.Create(nil);
  721. FProcess.Options := FProcess.Options + [poUsePipes];
  722. FProcess.OnNeedInput := @CGIProcNeedInput;
  723. FProcess.OnHasOutput := @CGIProcHasOutput;
  724. FProcess.OnHasStderr := @CGIProcHasStderr;
  725. end;
  726. destructor TSimpleCGIOutput.Destroy;
  727. begin
  728. inherited;
  729. FProcess.Free;
  730. end;
  731. function TSimpleCGIOutput.WriteCGIData: TWriteBlockStatus;
  732. var
  733. lRead: integer;
  734. begin
  735. lRead := FProcess.Output.Read(FBuffer[FReadPos], FBufferSize-FReadPos);
  736. if lRead = 0 then exit(wsDone);
  737. Inc(FReadPos, lRead);
  738. Result := InputBufferEmptyToWriteStatus[lRead = 0];
  739. end;
  740. procedure TSimpleCGIOutput.AddEnvironment(const AName, AValue: string);
  741. begin
  742. FProcess.Environment.Add(AName+'='+AValue);
  743. end;
  744. procedure TSimpleCGIOutput.DoneInput;
  745. begin
  746. FProcess.CloseInput;
  747. end;
  748. function TSimpleCGIOutput.HandleInput(ABuffer: pchar; ASize: integer): integer;
  749. begin
  750. if ASize > 0 then
  751. Result := FProcess.Input.Write(ABuffer^, ASize)
  752. else
  753. Result := 0;
  754. FProcess.InputEvent.IgnoreWrite := ASize = 0;
  755. end;
  756. procedure TSimpleCGIOutput.StartRequest;
  757. begin
  758. inherited;
  759. FProcess.Eventer := FSocket.Eventer;
  760. FProcess.Execute;
  761. end;
  762. procedure TSimpleCGIOutput.CGIOutputError;
  763. var
  764. ServerSocket: TLHTTPServerSocket absolute FSocket;
  765. begin
  766. if FProcess.ExitStatus = 127 then
  767. ServerSocket.ResponseInfo.Status := hsNotFound
  768. else
  769. ServerSocket.ResponseInfo.Status := hsInternalError;
  770. end;
  771. procedure TSimpleCGIOutput.CGIProcNeedInput(AHandle: TLHandle);
  772. begin
  773. FProcess.InputEvent.IgnoreWrite := true;
  774. FSocket.ParseBuffer;
  775. end;
  776. procedure TSimpleCGIOutput.CGIProcHasOutput(AHandle: TLHandle);
  777. begin
  778. WriteCGIBlock;
  779. end;
  780. procedure TSimpleCGIOutput.CGIProcHasStderr(AHandle: TLHandle);
  781. var
  782. lBuf: array[0..1023] of char;
  783. lRead: integer;
  784. begin
  785. lRead := FProcess.Stderr.Read(lBuf, sizeof(lBuf)-1);
  786. lBuf[lRead] := #0;
  787. write(pchar(@lBuf[0]));
  788. end;
  789. { TFastCGIOutput }
  790. constructor TFastCGIOutput.Create(ASocket: TLHTTPSocket);
  791. begin
  792. inherited;
  793. end;
  794. destructor TFastCGIOutput.Destroy;
  795. begin
  796. if FRequest <> nil then
  797. begin
  798. FRequest.OnInput := nil;
  799. FRequest.OnOutput := nil;
  800. FRequest.OnStderr := nil;
  801. FRequest.OnEndRequest := nil;
  802. FRequest.AbortRequest;
  803. end;
  804. inherited;
  805. end;
  806. procedure TFastCGIOutput.AddEnvironment(const AName, AValue: string);
  807. begin
  808. FRequest.SendParam(AName, AValue);
  809. end;
  810. procedure TFastCGIOutput.CGIOutputError;
  811. begin
  812. TLHTTPServerSocket(FSocket).ResponseInfo.Status := hsInternalError;
  813. end;
  814. procedure TFastCGIOutput.DoneInput;
  815. begin
  816. if FRequest <> nil then
  817. FRequest.DoneInput;
  818. end;
  819. procedure TFastCGIOutput.RequestEnd(ARequest: TLFastCGIRequest);
  820. begin
  821. FRequest.OnEndRequest := nil;
  822. FRequest.OnInput := nil;
  823. FRequest.OnOutput := nil;
  824. FRequest := nil;
  825. { trigger final write, to flush output to socket }
  826. WriteCGIBlock;
  827. end;
  828. procedure TFastCGIOutput.RequestNeedInput(ARequest: TLFastCGIRequest);
  829. begin
  830. FSocket.ParseBuffer;
  831. end;
  832. procedure TFastCGIOutput.RequestHasOutput(ARequest: TLFastCGIRequest);
  833. begin
  834. WriteCGIBlock;
  835. end;
  836. procedure TFastCGIOutput.RequestHasStderr(ARequest: TLFastCGIRequest);
  837. var
  838. lBuf: array[0..1023] of char;
  839. lRead: integer;
  840. begin
  841. lRead := ARequest.Get(lBuf, sizeof(lBuf)-1);
  842. lBuf[lRead] := #0;
  843. write(pchar(@lBuf[0]));
  844. end;
  845. function TFastCGIOutput.HandleInput(ABuffer: pchar; ASize: integer): integer;
  846. begin
  847. Result := FRequest.SendInput(ABuffer, ASize);
  848. end;
  849. function TFastCGIOutput.WriteCGIData: TWriteBlockStatus;
  850. var
  851. lRead: integer;
  852. begin
  853. if FRequest = nil then exit(wsDone);
  854. if FRequest.OutputDone then exit(wsDone);
  855. lRead := FRequest.Get(@FBuffer[FReadPos], FBufferSize-FReadPos);
  856. Inc(FReadPos, lRead);
  857. Result := InputBufferEmptyToWriteStatus[lRead = 0];
  858. end;
  859. function TFastCGIOutput.WriteBlock: TWriteBlockStatus;
  860. begin
  861. if (FRequest <> nil) and FRequest.OutputPending then
  862. begin
  863. FRequest.ParseClientBuffer;
  864. Result := wsWaitingData;
  865. end else
  866. Result := inherited;
  867. end;
  868. procedure TFastCGIOutput.StartRequest;
  869. begin
  870. FRequest.OnEndRequest := @RequestEnd;
  871. FRequest.OnInput := @RequestNeedInput;
  872. FRequest.OnOutput := @RequestHasOutput;
  873. FRequest.OnStderr := @RequestHasStderr;
  874. inherited;
  875. FRequest.DoneParams;
  876. end;
  877. { TFormOutput }
  878. constructor TFormOutput.Create(ASocket: TLHTTPSocket);
  879. begin
  880. inherited;
  881. FRequestVars := TStringList.Create;
  882. end;
  883. destructor TFormOutput.Destroy;
  884. var
  885. I: integer;
  886. tmpObj: TObject;
  887. begin
  888. for I := 0 to FRequestVars.Count - 1 do
  889. begin
  890. tmpObj := FRequestVars.Objects[I];
  891. Finalize(string(tmpObj));
  892. FRequestVars.Objects[I] := nil;
  893. end;
  894. FRequestVars.Free;
  895. inherited;
  896. end;
  897. function TFormOutput.AddVariables(Variables: pchar; ASize: integer; SepChar: char): integer;
  898. var
  899. varname, sep, next: pchar;
  900. strName, strValue: string;
  901. tmpObj: TObject;
  902. i: integer;
  903. begin
  904. if Variables = nil then
  905. exit(0);
  906. if ASize = -1 then
  907. ASize := StrLen(Variables);
  908. varname := Variables;
  909. repeat
  910. sep := varname + IndexChar(varname^, ASize, '=');
  911. if sep < varname then
  912. break;
  913. dec(ASize, sep-varname);
  914. next := sep + IndexChar(sep^, ASize, SepChar);
  915. if next < sep then
  916. begin
  917. next := sep + ASize;
  918. ASize := 0;
  919. end else
  920. dec(ASize, next+1-sep);
  921. if sep > varname then
  922. begin
  923. setlength(strName, sep-varname);
  924. move(varname[0], strName[1], sep-varname);
  925. setlength(strValue, next-sep-1);
  926. move(sep[1], strValue[1], next-sep-1);
  927. i := FRequestVars.Add(strName);
  928. tmpObj := nil;
  929. string(tmpObj) := strValue;
  930. FRequestVars.Objects[i] := tmpObj;
  931. end;
  932. varname := next+1;
  933. until false;
  934. Result := ASize;
  935. end;
  936. procedure TFormOutput.DoneInput;
  937. begin
  938. if Assigned(FOnExtraHeaders) then
  939. FOnExtraHeaders(Self);
  940. TLHTTPServerSocket(FSocket).StartResponse(Self);
  941. end;
  942. function TFormOutput.HandleInputFormURL(ABuffer: pchar; ASize: integer): integer;
  943. begin
  944. Result := ASize-AddVariables(ABuffer, ASize, URIParamSepChar)
  945. end;
  946. procedure TFormOutput.ParseMultipartHeader(ABuffer, ALineEnd: pchar);
  947. var
  948. I: TLMultipartParameter;
  949. len: integer;
  950. begin
  951. for I := Low(TLMultipartParameter) to High(TLMultipartParameter) do
  952. begin
  953. len := Length(MPParameterStrings[I]);
  954. if ABuffer+len >= ALineEnd then
  955. continue;
  956. if (ABuffer[len] = ':')
  957. and (StrLIComp(ABuffer, PChar(MPParameterStrings[I]), len) = 0) then
  958. begin
  959. Inc(ABuffer, len+2);
  960. repeat
  961. if ABuffer = ALineEnd then exit;
  962. if ABuffer^ <> ' ' then break;
  963. inc(ABuffer);
  964. until false;
  965. FMPParameters[I] := ABuffer;
  966. if I = mpContentType then
  967. begin
  968. repeat
  969. if ABuffer = ALineEnd then exit;
  970. if ABuffer = ';' then break;
  971. inc(ABuffer);
  972. until false;
  973. end;
  974. break;
  975. end;
  976. end;
  977. end;
  978. function TFormOutput.FindBoundary(ABuffer: pchar): pchar;
  979. begin
  980. {$warning TODO}
  981. Result := nil;
  982. end;
  983. function TFormOutput.HandleInputMultipart(ABuffer: pchar; ASize: integer): integer;
  984. var
  985. pos, next, endline: pchar;
  986. begin
  987. pos := ABuffer;
  988. repeat
  989. case FMPState of
  990. msStart:
  991. begin
  992. { discard until first boundary }
  993. next := FindBoundary(pos);
  994. if next = nil then
  995. exit(ASize);
  996. FMPState := msBodypartHeader;
  997. end;
  998. msBodypartHeader:
  999. begin
  1000. endline := pos + IndexChar(pos, ASize, #10);
  1001. if endline < pos then
  1002. exit(pos-ABuffer);
  1003. next := endline+1;
  1004. if (endline > pos) and ((endline-1)^ = #13) then
  1005. dec(endline);
  1006. endline^ := #0;
  1007. if endline > pos then
  1008. ParseMultipartHeader(pos, endline)
  1009. else
  1010. FMPState := msBodypartData;
  1011. end;
  1012. msBodypartData:
  1013. begin
  1014. { decode based on content-transfer-encoding ? }
  1015. { CRLF before boundary, belongs to boundary, not data! }
  1016. next := FindBoundary(ABuffer);
  1017. end;
  1018. else
  1019. exit(ASize);
  1020. end;
  1021. dec(ASize, next-pos);
  1022. pos := next;
  1023. until false;
  1024. end;
  1025. function TFormOutput.HandleInputDiscard(ABuffer: pchar; ASize: integer): integer;
  1026. begin
  1027. Result := ASize;
  1028. end;
  1029. function TFormOutput.HandleInput(ABuffer: pchar; ASize: integer): integer;
  1030. begin
  1031. Result := FHandleInput(ABuffer, ASize);
  1032. end;
  1033. function TFormOutput.FillBuffer: TWriteBlockStatus;
  1034. begin
  1035. Result := wsDone;
  1036. if Assigned(FOnFillBuffer) then
  1037. FOnFillBuffer(Self, Result);
  1038. end;
  1039. procedure TFormOutput.DeleteCookie(const AName: string; const APath: string = '/';
  1040. const ADomain: string = '');
  1041. begin
  1042. { cookies expire when expires is in the past, duh }
  1043. SetCookie(AName, '', Now - 7.0, APath, ADomain);
  1044. end;
  1045. procedure TFormOutput.SetCookie(const AName, AValue: string; const AExpires: TDateTime;
  1046. const APath: string = '/'; const ADomain: string = '');
  1047. var
  1048. headers: PStringBuffer;
  1049. begin
  1050. headers := @TLHTTPServerSocket(FSocket).HeaderOut.ExtraHeaders;
  1051. AppendString(headers^, 'Set-Cookie: ' + HTTPEncode(AName) + '=' + HTTPEncode(AValue));
  1052. AppendString(headers^, ';path=' + APath + ';expires=' + FormatDateTime(HTTPDateFormat, AExpires));
  1053. if Length(ADomain) > 0 then
  1054. begin
  1055. AppendString(headers^, ';domain=');
  1056. AppendString(headers^, ADomain);
  1057. end;
  1058. AppendString(headers^, #13#10);
  1059. end;
  1060. { TFormHandler }
  1061. procedure TFormHandler.SelectMultipart(AFormOutput: TFormOutput; AContentType: pchar);
  1062. var
  1063. boundary, endquote: pchar;
  1064. begin
  1065. boundary := StrScan(AContentType, '=');
  1066. if boundary <> nil then
  1067. begin
  1068. Inc(boundary);
  1069. if boundary^ = '"' then
  1070. begin
  1071. Inc(boundary);
  1072. endquote := StrScan(boundary, '"');
  1073. if endquote <> nil then
  1074. endquote^ := #0;
  1075. end;
  1076. end;
  1077. AFormOutput.FBoundary := boundary;
  1078. AFormOutput.FHandleInput := @AFormOutput.HandleInputMultipart;
  1079. end;
  1080. function TFormHandler.HandleURI(ASocket: TLHTTPServerSocket): TOutputItem;
  1081. var
  1082. newFormOutput: TFormOutput;
  1083. contentType: pchar;
  1084. begin
  1085. if not Assigned(FOnHandleURI) then
  1086. exit(nil);
  1087. newFormOutput := FOnHandleURI(ASocket);
  1088. if newFormOutput = nil then
  1089. exit(nil);
  1090. newFormOutput.AddVariables(ASocket.RequestInfo.QueryParams, -1, URIParamSepChar);
  1091. newFormOutput.AddVariables(ASocket.Parameters[hpCookie], -1, CookieSepChar);
  1092. contentType := TLHTTPServerSocket(ASocket).Parameters[hpContentType];
  1093. if StrIComp(contentType, FormURLContentType) = 0 then
  1094. newFormOutput.FHandleInput := @newFormOutput.HandleInputFormURL
  1095. else if StrIComp(contentType, MultipartContentType) = 0 then
  1096. SelectMultipart(newFormOutput, contentType)
  1097. else
  1098. newFormOutput.FHandleInput := @newFormOutput.HandleInputDiscard;
  1099. Result := newFormOutput;
  1100. end;
  1101. end.