httpdefs.pp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {
  12. HTTPDefs: Basic HTTP protocol declarations and classes
  13. See the file COPYING.FPC, included in this distribution,
  14. for details about the copyright.
  15. This program is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  18. }
  19. {$mode objfpc}
  20. {$H+}
  21. {$DEFINE CGIDEBUG}
  22. unit HTTPDefs;
  23. interface
  24. uses Classes,Sysutils;
  25. const
  26. fieldAccept = 'Accept';
  27. fieldAcceptCharset = 'Accept-Charset';
  28. fieldAcceptEncoding = 'Accept-Encoding';
  29. fieldAcceptLanguage = 'Accept-Language';
  30. fieldAuthorization = 'Authorization';
  31. fieldConnection = 'Connection';
  32. fieldContentEncoding = 'Content-Encoding';
  33. fieldContentLanguage = 'Content-Language';
  34. fieldContentLength = 'Content-Length';
  35. fieldContentType = 'Content-Type';
  36. fieldCookie = 'Cookie';
  37. fieldDate = 'Date';
  38. fieldExpires = 'Expires';
  39. fieldFrom = 'From';
  40. fieldIfModifiedSince = 'If-Modified-Since';
  41. fieldLastModified = 'Last-Modified';
  42. fieldLocation = 'Location';
  43. fieldPragma = 'Pragma';
  44. fieldReferer = 'Referer';
  45. fieldRetryAfter = 'Retry-After';
  46. fieldServer = 'Server';
  47. fieldSetCookie = 'Set-Cookie';
  48. fieldUserAgent = 'User-Agent';
  49. fieldWWWAuthenticate = 'WWW-Authenticate';
  50. NoHTTPFields = 24;
  51. HTTPDateFmt = '"%s", dd "%s" yyyy hh:mm:ss'; // For use in FormatDateTime
  52. SCookieExpire = ' "expires="'+HTTPDateFmt+' "GMT;"';
  53. SCookieDomain = ' domain=%s;';
  54. SCookiePath = ' path=%s;';
  55. SCookieSecure = ' secure';
  56. HTTPMonths: array[1..12] of string[3] = (
  57. 'Jan', 'Feb', 'Mar', 'Apr',
  58. 'May', 'Jun', 'Jul', 'Aug',
  59. 'Sep', 'Oct', 'Nov', 'Dec');
  60. HTTPDays: array[1..7] of string[3] = (
  61. 'Sun', 'Mon', 'Tue', 'Wed',
  62. 'Thu', 'Fri', 'Sat');
  63. Type
  64. TTHttpFields = Array[1..NoHTTPFields] of string;
  65. Const
  66. HTTPFieldNames : TTHttpFields
  67. = (fieldAccept, fieldAcceptCharset, fieldAcceptEncoding,
  68. fieldAcceptLanguage, fieldAuthorization, fieldConnection,
  69. fieldContentEncoding, fieldContentLanguage, fieldContentLength,
  70. fieldContentType, fieldCookie, fieldDate, fieldExpires,
  71. fieldFrom, fieldIfModifiedSince, fieldLastModified, fieldLocation,
  72. fieldPragma, fieldReferer, fieldRetryAfter, fieldServer,
  73. fieldSetCookie, fieldUserAgent, fieldWWWAuthenticate);
  74. type
  75. { TCookie }
  76. TCookie = class(TCollectionItem)
  77. private
  78. FName: string;
  79. FValue: string;
  80. FPath: string;
  81. FDomain: string;
  82. FExpires: TDateTime;
  83. FSecure: Boolean;
  84. protected
  85. Function GetAsString: string;
  86. public
  87. constructor Create(ACollection: TCollection); override;
  88. procedure Assign(Source: TPersistent); override;
  89. property Name: string read FName write FName;
  90. property Value: string read FValue write FValue;
  91. property Domain: string read FDomain write FDomain;
  92. property Path: string read FPath write FPath;
  93. property Expires: TDateTime read FExpires write FExpires;
  94. property Secure: Boolean read FSecure write FSecure;
  95. Property AsString : String Read GetAsString;
  96. end;
  97. { TCookies }
  98. TCookies = class(TCollection)
  99. private
  100. protected
  101. function GetCookie(Index: Integer): TCookie;
  102. procedure SetCookie(Index: Integer; Value: TCookie);
  103. public
  104. function Add: TCookie;
  105. Function CookieByName(AName : String) : TCookie;
  106. Function FindCookie(AName : String): TCookie;
  107. Function IndexOfCookie(AName : String) : Integer;
  108. property Items[Index: Integer]: TCookie read GetCookie write SetCookie; default;
  109. end;
  110. { TUploadedFile }
  111. TUploadedFile = Class(TCollectionItem)
  112. Private
  113. FContentType: String;
  114. FDisposition: String;
  115. FFieldName: String;
  116. FFileName: String;
  117. FLocalFileName: String;
  118. FSize: Int64;
  119. FStream : TStream;
  120. Protected
  121. function GetStream: TStream; virtual;
  122. Public
  123. Destructor Destroy; override;
  124. Property FieldName : String Read FFieldName Write FFieldName;
  125. Property FileName : String Read FFileName Write FFileName;
  126. Property Stream : TStream Read GetStream;
  127. Property Size : Int64 Read FSize Write FSize;
  128. Property ContentType : String Read FContentType Write FContentType;
  129. Property Disposition : String Read FDisposition Write FDisposition;
  130. Property LocalFileName : String Read FLocalFileName Write FLocalFileName;
  131. end;
  132. { TUploadedFiles }
  133. TUploadedFiles = Class(TCollection)
  134. private
  135. function GetFile(Index : Integer): TUploadedFile;
  136. procedure SetFile(Index : Integer; const AValue: TUploadedFile);
  137. public
  138. Function IndexOfFile(AName : String) : Integer;
  139. Function FileByName(AName : String) : TUploadedFile;
  140. Function FindFile(AName : String) : TUploadedFile;
  141. Property Files[Index : Integer] : TUploadedFile read GetFile Write SetFile; default;
  142. end;
  143. { THTTPHeader }
  144. THTTPHeader = class(TObject)
  145. private
  146. FContentFields: TStrings;
  147. FCookieFields: TStrings;
  148. FHTTPVersion : String;
  149. FFields : TTHttpFields;
  150. FQueryFields: TStrings;
  151. function GetSetField(AIndex: Integer): String;
  152. function GetSetFieldName(AIndex: Integer): String;
  153. procedure SetCookieFields(const AValue: TStrings);
  154. Function GetFieldCount : Integer;
  155. Function GetFieldName(Index : Integer) : String;
  156. Function GetContentLength : Integer;
  157. Procedure SetContentLength(Value : Integer);
  158. Function GetFieldIndex(AIndex : Integer) : Integer;
  159. Function GetServerPort : Word;
  160. Function GetSetFieldValue(Index : Integer) : String; virtual;
  161. Protected
  162. Function GetFieldValue(Index : Integer) : String; virtual;
  163. Procedure SetFieldValue(Index : Integer; Value : String); virtual;
  164. procedure ParseFirstHeaderLine(const line: String);virtual;
  165. Procedure ParseCookies; virtual;
  166. public
  167. constructor Create; virtual;
  168. destructor Destroy; override;
  169. procedure SetFieldByName(const AName, AValue: String);
  170. function GetFieldByName(const AName: String): String;
  171. Function LoadFromStream(Stream : TStream; IncludeCommand : Boolean) : integer;
  172. Function LoadFromStrings(Strings: TStrings; IncludeCommand : Boolean) : integer; virtual;
  173. // Common access
  174. property FieldCount: Integer read GetFieldCount;
  175. property Fields[AIndex: Integer]: String read GetSetField;
  176. property FieldNames[AIndex: Integer]: String read GetSetFieldName;
  177. property FieldValues[AIndex: Integer]: String read GetSetFieldValue;
  178. // Various properties.
  179. Property HttpVersion : String Index 0 Read GetFieldValue Write SetFieldValue;
  180. Property ProtocolVersion : String Index 0 Read GetFieldValue Write SetFieldValue;
  181. property Accept: String Index 1 read GetFieldValue write SetFieldValue;
  182. property AcceptCharset: String Index 2 Read GetFieldValue Write SetFieldValue;
  183. property AcceptEncoding: String Index 3 Read GetFieldValue Write SetFieldValue;
  184. property AcceptLanguage: String Index 4 Read GetFieldValue Write SetFieldValue;
  185. property Authorization: String Index 5 Read GetFieldValue Write SetFieldValue;
  186. property Connection: String Index 6 Read GetFieldValue Write SetFieldValue;
  187. property ContentEncoding: String Index 7 Read GetFieldValue Write SetFieldValue;
  188. property ContentLanguage: String Index 8 Read GetFieldValue Write SetFieldValue;
  189. property ContentLength: Integer Read GetContentLength Write SetContentLength;
  190. property ContentType: String Index 10 Read GetFieldValue Write SetFieldValue;
  191. property Cookie: String Index 11 Read GetFieldValue Write SetFieldValue;
  192. property Date: String Index 12 Read GetFieldValue Write SetFieldValue;
  193. property Expires: String Index 13 Read GetFieldValue Write SetFieldValue;
  194. property From: String Index 14 Read GetFieldValue Write SetFieldValue;
  195. property IfModifiedSince: String Index 15 Read GetFieldValue Write SetFieldValue;
  196. property LastModified: String Index 16 Read GetFieldValue Write SetFieldValue;
  197. property Location: String Index 17 Read GetFieldValue Write SetFieldValue;
  198. property Pragma: String Index 18 Read GetFieldValue Write SetFieldValue;
  199. property Referer: String Index 19 Read GetFieldValue Write SetFieldValue;
  200. property RetryAfter: String Index 20 Read GetFieldValue Write SetFieldValue;
  201. property Server: String Index 21 Read GetFieldValue Write SetFieldValue;
  202. property SetCookie: String Index 22 Read GetFieldValue Write SetFieldValue;
  203. property UserAgent: String Index 23 Read GetFieldValue Write SetFieldValue;
  204. property WWWAuthenticate: String Index 24 Read GetFieldValue Write SetFieldValue;
  205. // Various aliases, for compatibility
  206. Property PathInfo : String index 25 read GetFieldValue Write SetFieldValue;
  207. Property PathTranslated : String Index 26 read GetFieldValue Write SetFieldValue;
  208. Property RemoteAddress : String Index 27 read GetFieldValue Write SetFieldValue;
  209. Property RemoteHost : String Index 28 read GetFieldValue Write SetFieldValue;
  210. Property ScriptName : String Index 29 read GetFieldValue Write SetFieldValue;
  211. Property ServerPort : Word Read GetServerPort; // Index 30
  212. Property HTTPAccept : String Index 1 read GetFieldValue Write SetFieldValue;
  213. Property HTTPAcceptCharset : String Index 2 read GetFieldValue Write SetFieldValue;
  214. Property HTTPAcceptEncoding : String Index 3 read GetFieldValue Write SetFieldValue;
  215. Property HTTPIfModifiedSince : String Index 15 read GetFieldValue Write SetFieldValue; // Maybe change to TDateTime ??
  216. Property HTTPReferer : String Index 19 read GetFieldValue Write SetFieldValue;
  217. Property HTTPUserAgent : String Index 23 read GetFieldValue Write SetFieldValue;
  218. // Lists
  219. Property CookieFields : TStrings Read FCookieFields Write SetCookieFields;
  220. Property ContentFields: TStrings read FContentFields;
  221. property QueryFields : TStrings read FQueryFields;
  222. end;
  223. { TRequest }
  224. TRequest = class(THttpHeader)
  225. private
  226. FCommand: String;
  227. FCommandLine: String;
  228. FQuery: String;
  229. FURI: String;
  230. FFiles : TUploadedFiles;
  231. FReturnedPathInfo : String;
  232. procedure ParseFirstHeaderLine(const line: String);override;
  233. function GetFirstHeaderLine: String;
  234. Protected
  235. Procedure ProcessMultiPart(Stream : TStream; Const Boundary : String); virtual;
  236. Procedure ProcessQueryString(Const FQueryString : String); virtual;
  237. procedure ProcessURLEncoded(Stream : TStream); virtual;
  238. Function GetTempUploadFileName : String; virtual;
  239. public
  240. constructor Create; override;
  241. destructor destroy; override;
  242. Function GetNextPathInfo : String;
  243. Property CommandLine : String Read FCommandLine;
  244. Property Command : String read FCommand;
  245. Property URI : String read FURI; // Uniform Resource Identifier
  246. Property Query : String Read FQuery;
  247. Property QueryString : String Read FQuery; // Alias
  248. Property HeaderLine : String read GetFirstHeaderLine;
  249. Property Files : TUploadedFiles Read FFiles;
  250. end;
  251. { TResponse }
  252. TResponse = class(THttpHeader)
  253. private
  254. FContents: TStrings;
  255. FContentStream : TStream;
  256. FCode: Integer;
  257. FCodeText: String;
  258. FHeadersSent: Boolean;
  259. FContentSent: Boolean;
  260. FRequest : TRequest;
  261. FCookies : TCookies;
  262. function GetContent: String;
  263. procedure SetContent(const AValue: String);
  264. procedure SetContents(AValue: TStrings);
  265. procedure SetContentStream(const AValue: TStream);
  266. procedure SetFirstHeaderLine(const line: String);
  267. function GetFirstHeaderLine: String;
  268. procedure ContentsChanged(Sender : TObject);
  269. Protected
  270. Procedure DoSendHeaders(Headers : TStrings); virtual; abstract;
  271. Procedure DoSendContent; virtual; abstract;
  272. Procedure CollectHeaders(Headers : TStrings); virtual;
  273. public
  274. constructor Create(ARequest : TRequest);
  275. destructor destroy; override;
  276. Procedure SendContent;
  277. Procedure SendHeaders;
  278. Procedure SendResponse; // Delphi compatibility
  279. Property Request : TRequest Read FRequest;
  280. Property Code: Integer Read FCode Write FCode;
  281. Property CodeText: String Read FCodeText Write FCodeText;
  282. Property FirstHeaderLine : String Read GetFirstHeaderLine Write SetFirstHeaderLine;
  283. Property ContentStream : TStream Read FContentStream Write SetContentStream;
  284. Property Content : String Read GetContent Write SetContent;
  285. property Contents : TStrings read FContents Write SetContents;
  286. Property HeadersSent : Boolean Read FHeadersSent;
  287. Property ContentSent : Boolean Read FContentSent;
  288. property Cookies: TCookies read FCookies;
  289. end;
  290. { TSessionVariable }
  291. { TCustomSession }
  292. TCustomSession = Class(TComponent)
  293. Private
  294. FTimeOut: Integer;
  295. Protected
  296. Function GetSessionID : String; virtual;
  297. Function GetSessionVariable(VarName : String) : String; Virtual; abstract;
  298. procedure SetSessionVariable(VarName : String; const AValue: String);Virtual;abstract;
  299. Public
  300. Constructor Create(AOwner : TComponent); override;
  301. // Init session from request.
  302. Procedure InitSession(ARequest : TRequest; OnNewSession,OnExpired : TNotifyEvent); virtual;
  303. // Init response from session (typically, add cookie to response).
  304. Procedure InitResponse(AResponse : TResponse); virtual;
  305. // Update response from session (typically, change cookie to response and write session data).
  306. Procedure UpdateResponse(AResponse : TResponse); virtual; Abstract;
  307. Procedure RemoveVariable(VariableName : String); virtual; abstract;
  308. Procedure Terminate; virtual; abstract;
  309. Property TimeOutMinutes : Integer Read FTimeOut Write FTimeOut;
  310. Property SessionID : String Read GetSessionID;
  311. Property Variables[VarName : String] : String Read GetSessionVariable Write SetSessionVariable;
  312. end;
  313. TRequestEvent = Procedure (Sender: TObject; ARequest : TRequest) of object;
  314. TResponseEvent = Procedure (Sender: TObject; AResponse : TResponse) of object;
  315. HTTPError = Class(Exception);
  316. Function HTTPDecode(const AStr: String): String;
  317. Function HTTPEncode(const AStr: String): String;
  318. implementation
  319. {$ifdef CGIDEBUG}
  320. uses dbugintf;
  321. {$endif}
  322. Resourcestring
  323. SErrContentAlreadySent = 'HTTP Response content was already sent';
  324. SErrHeadersAlreadySent = 'HTTP headers were already sent';
  325. SErrInternalUploadedFileError = 'Internal uploaded file configuration error';
  326. SErrNoSuchUploadedFile = 'No such uploaded file : "%s"';
  327. SErrUnknownCookie = 'Unknown cookie: "%s"';
  328. const
  329. hexTable = '0123456789ABCDEF';
  330. { ---------------------------------------------------------------------
  331. Auxiliary functions
  332. ---------------------------------------------------------------------}
  333. Function GetFieldNameIndex(AName : String) : Integer;
  334. var
  335. Name: String;
  336. begin
  337. Name := UpperCase(AName);
  338. Result:=NoHTTPFields;
  339. While (Result>0) and (UpperCase(HTTPFieldNames[Result])<>Name) do
  340. Dec(Result);
  341. end;
  342. function HTTPDecode(const AStr: String): String;
  343. var
  344. S,SS, R : PChar;
  345. H : String[3];
  346. L,C : Integer;
  347. begin
  348. L:=Length(Astr);
  349. SetLength(Result,L);
  350. If (L=0) then
  351. exit;
  352. S:=PChar(AStr);
  353. SS:=S;
  354. R:=PChar(Result);
  355. while (S-SS)<L do
  356. begin
  357. case S^ of
  358. '+': R^ := ' ';
  359. '%': begin
  360. Inc(S);
  361. if ((S-SS)<L) then
  362. begin
  363. if (S^='%') then
  364. R^:='%'
  365. else
  366. begin
  367. H:='$00';
  368. H[2]:=S^;
  369. Inc(S);
  370. If (S-SS)<L then
  371. begin
  372. H[3]:=S^;
  373. Val(H,PByte(R)^,C);
  374. If (C<>0) then
  375. R^:=' ';
  376. end;
  377. end;
  378. end;
  379. end;
  380. else
  381. R^ := S^;
  382. end;
  383. Inc(R);
  384. Inc(S);
  385. end;
  386. SetLength(Result,R-PChar(Result));
  387. end;
  388. function HTTPEncode(const AStr: String): String;
  389. const
  390. HTTPAllowed = ['A'..'Z','a'..'z',
  391. '*','@','.','_','-',
  392. '0'..'9',
  393. '$','!','''','(',')'];
  394. var
  395. SS,S,R: PChar;
  396. H : String[2];
  397. L : Integer;
  398. begin
  399. L:=Length(AStr);
  400. SetLength(Result,L*3); // Worst case scenario
  401. if (L=0) then
  402. exit;
  403. R:=PChar(Result);
  404. S:=PChar(AStr);
  405. SS:=S; // Avoid #0 limit !!
  406. while ((S-SS)<L) do
  407. begin
  408. if S^ in HTTPAllowed then
  409. R^:=S^
  410. else if (S^=' ') then
  411. R^:='+'
  412. else
  413. begin
  414. R^:='%';
  415. H:=HexStr(Ord(S^),2);
  416. Inc(R);
  417. R^:=H[1];
  418. Inc(R);
  419. R^:=H[2];
  420. end;
  421. Inc(R);
  422. Inc(S);
  423. end;
  424. SetLength(Result,R-PChar(Result));
  425. end;
  426. { ---------------------------------------------------------------------
  427. THTTPHeader
  428. ---------------------------------------------------------------------}
  429. function THttpHeader.GetFieldCount: Integer;
  430. Var
  431. I : Integer;
  432. begin
  433. Result:=0;
  434. For I:=1 to NoHTTPFields do
  435. If (FFields[i]<>'') then
  436. Inc(Result);
  437. end;
  438. function THTTPHeader.GetContentLength: Integer;
  439. begin
  440. Result:=StrToIntDef(FFields[9],0);
  441. end;
  442. procedure THTTPHeader.SetContentLength(Value: Integer);
  443. begin
  444. FFields[9]:=IntToStr(Value);
  445. end;
  446. Function THttpHeader.GetFieldIndex(AIndex : Integer) : Integer;
  447. var
  448. I : Integer;
  449. begin
  450. I:=1;
  451. While (I<=NoHTTPFields) and (AIndex>=0) do
  452. begin
  453. If (FFields[i]<>'') then
  454. Dec(AIndex);
  455. Inc(I);
  456. end;
  457. If (AIndex=-1) then
  458. Result:=I-1
  459. else
  460. Result:=-1;
  461. end;
  462. function THTTPHeader.GetServerPort: Word;
  463. begin
  464. Result:=StrToIntDef(GetFieldValue(30),0);
  465. end;
  466. function THTTPHeader.GetSetFieldValue(Index: Integer): String;
  467. Var
  468. I : Integer;
  469. begin
  470. I:=GetFieldIndex(Index);
  471. If (I<>-1) then
  472. Result:=FFields[I];
  473. end;
  474. function THTTPHeader.GetSetField(AIndex: Integer): String;
  475. var
  476. I : Integer;
  477. begin
  478. I:=GetFieldIndex(AIndex);
  479. If (I<>-1) then
  480. Result := HTTPFieldNames[I] + ': ' + FFields[I];
  481. end;
  482. function THTTPHeader.GetSetFieldName(AIndex: Integer): String;
  483. var
  484. I : Integer;
  485. begin
  486. I:=GetFieldIndex(AIndex);
  487. if (I<>-1) then
  488. Result:=HTTPFieldNames[I];
  489. end;
  490. function THttpHeader.GetFieldName(Index: Integer): String;
  491. Var
  492. I : Integer;
  493. begin
  494. I:=GetFieldIndex(Index);
  495. If (I<>-1) then
  496. Result := HTTPFieldNames[i];
  497. end;
  498. Function THttpHeader.GetFieldValue(Index : Integer) : String;
  499. begin
  500. if (Index>1) and (Index<NoHTTPFields) then
  501. Result:=FFields[Index]
  502. else
  503. case Index of
  504. 0 : Result:=FHTTPVersion;
  505. 25 : Result:=''; // Property PathInfo
  506. 26 : Result:=''; // Property PathTranslated
  507. 27 : Result:=''; // Property RemoteAddress
  508. 28 : Result:=''; // Property RemoteHost
  509. 29 : Result:=''; // Property ScriptName
  510. 30 : Result:=''; // Property ServerPort
  511. end;
  512. end;
  513. procedure THTTPHeader.SetCookieFields(const AValue: TStrings);
  514. begin
  515. FCookieFields.Assign(AValue);
  516. end;
  517. Procedure THttpHeader.SetFieldValue(Index : Integer; Value : String);
  518. begin
  519. if (Index>1) and (Index<NoHTTPFields) then
  520. begin
  521. FFields[Index]:=Value;
  522. If (Index=11) then
  523. ParseCookies;
  524. end
  525. else
  526. case Index of
  527. 0 : FHTTPVersion:=Value;
  528. 25 : ; // Property PathInfo : String index 25 read GetFieldValue Write SetFieldValue;
  529. 26 : ; // Property PathTranslated : String Index 26 read GetFieldValue Write SetFieldValue;
  530. 27 : ; // Property RemoteAddress : String Index 27 read GetFieldValue Write SetFieldValue;
  531. 28 : ; // Property RemoteHost : String Index 28 read GetFieldValue Write SetFieldValue;
  532. 29 : ; // Property ScriptName : String Index 29 read GetFieldValue Write SetFieldValue;
  533. 30 : ; // Property ServerPort : Word Read GetServerPort; // Index 30
  534. end;
  535. end;
  536. procedure THTTPHeader.ParseFirstHeaderLine(const line: String);
  537. begin
  538. // Do nothing.
  539. end;
  540. procedure THTTPHeader.ParseCookies;
  541. Var
  542. P : Integer;
  543. S,C : String;
  544. begin
  545. {$ifdef cgidebug} SendMethodEnter('Parsecookies');{$endif}
  546. S:=Cookie;
  547. While (S<>'') do
  548. begin
  549. P:=Pos(';',S);
  550. If (P=0) then
  551. P:=length(S)+1;
  552. C:=Copy(S,1,P-1);
  553. While (P<Length(S)) and (S[P+1]=' ') do
  554. Inc(P);
  555. System.Delete(S,1,P);
  556. FCookieFields.Add(HTTPDecode(C));
  557. end;
  558. {$ifdef cgidebug} SendMethodExit('Parsecookies done');{$endif}
  559. end;
  560. constructor THttpHeader.Create;
  561. begin
  562. FCookieFields:=TStringList.Create;
  563. FQueryFields:=TStringList.Create;
  564. FHttpVersion := '1.1';
  565. end;
  566. destructor THttpHeader.Destroy;
  567. begin
  568. FreeAndNil(FCookieFields);
  569. FreeAndNil(FQueryFields);
  570. inherited Destroy;
  571. end;
  572. function THttpHeader.GetFieldByName(const AName: String): String;
  573. var
  574. i: Integer;
  575. begin
  576. I:=GetFieldNameIndex(AName);
  577. If (I<>0) then
  578. Result:=FFields[i];
  579. end;
  580. Function THTTPHeader.LoadFromStream(Stream: TStream; IncludeCommand : Boolean) : Integer;
  581. Var
  582. S : TStrings;
  583. begin
  584. S:=TStringList.Create;
  585. Try
  586. S.LoadFromStream(Stream);
  587. Result:=LoadFromStrings(S,IncludeCommand);
  588. Finally
  589. S.Free;
  590. end;
  591. end;
  592. Function THTTPHeader.LoadFromStrings(Strings: TStrings; IncludeCommand : Boolean) : integer;
  593. Var
  594. P : Integer;
  595. S,VN : String;
  596. begin
  597. Result:=0;
  598. if (Strings.Count>0) then
  599. begin
  600. if IncludeCommand then
  601. begin
  602. ParseFirstHeaderLine(Strings[0]);
  603. Inc(Result);
  604. end;
  605. While (Result<Strings.Count) and (Strings[Result]<>'') do
  606. begin
  607. S:=Strings[Result];
  608. P:=Pos(':',S);
  609. if (P<>0) then
  610. begin
  611. VN:=Copy(S,1,P-1);
  612. Delete(S,1,P);
  613. P:=GetFieldNameIndex(VN);
  614. If (P<>-1) then
  615. SetFieldValue(P,S);
  616. end;
  617. Inc(Result);
  618. end;
  619. end;
  620. end;
  621. procedure THttpHeader.SetFieldByName(const AName, AValue: String);
  622. var
  623. i: Integer;
  624. begin
  625. I:=GetFieldNameIndex(AName);
  626. If (I<>0) then
  627. SetFieldValue(i,AValue);
  628. end;
  629. { -------------------------------------------------------------------
  630. TFormItem, used by TRequest to process Multipart-encoded data.
  631. -------------------------------------------------------------------}
  632. Type
  633. TFormItem = Class(TObject)
  634. Disposition : String;
  635. Name : String;
  636. IsFile : Boolean;
  637. FileName : String;
  638. ContentType : String;
  639. DLen : Integer;
  640. Data : String;
  641. Procedure Process;
  642. end;
  643. Procedure TFormItem.Process;
  644. Function GetLine(Var S : String) : String;
  645. Var
  646. P : Integer;
  647. begin
  648. P:=Pos(#13#10,S);
  649. If (P<>0) then
  650. begin
  651. Result:=Copy(S,1,P-1);
  652. Delete(S,1,P+1);
  653. end;
  654. end;
  655. Function GetWord(Var S : String) : String;
  656. Var
  657. I,len : Integer;
  658. Quoted : Boolean;
  659. C : Char;
  660. begin
  661. len:=length(S);
  662. quoted:=false;
  663. Result:='';
  664. for i:=1 to len do
  665. Begin
  666. c:=S[i];
  667. if (c='"') then
  668. Quoted:=Not Quoted
  669. else
  670. begin
  671. if not (c in [' ','=',';',':']) or Quoted then
  672. Result:=Result+C;
  673. if (c in [';',':','=']) and (not quoted) then
  674. begin
  675. Delete(S,1,I);
  676. Exit;
  677. end;
  678. end;
  679. end;
  680. S:='';
  681. end;
  682. Var
  683. Line : String;
  684. len : integer;
  685. S : string;
  686. begin
  687. Line:=GetLine(Data);
  688. While (Line<>'') do
  689. begin
  690. S:=GetWord(Line);
  691. While (S<>'') do
  692. begin
  693. If CompareText(S,'Content-Disposition')=0 then
  694. Disposition:=GetWord(Line)
  695. else if CompareText(S,'name')=0 Then
  696. Name:=GetWord(Line)
  697. else if CompareText(S,'filename')=0 then
  698. begin
  699. FileName:=GetWord(Line);
  700. isFile:=True;
  701. end
  702. else if CompareText(S,'Content-Type')=0 then
  703. ContentType:=GetWord(Line);
  704. S:=GetWord(Line);
  705. end;
  706. Line:=GetLine(Data);
  707. end;
  708. // Now Data contains the rest of the data, plus a CR/LF. Strip the CR/LF
  709. Len:=Length(Data);
  710. If (len>2) then
  711. Data:=Copy(Data,1,Len-2);
  712. end;
  713. {
  714. This needs MASSIVE improvements for large files.
  715. Best would be to do this directly from the input stream
  716. and save the files at once if needed. (e.g. when a
  717. certain size is reached.)
  718. }
  719. procedure FormSplit(var Cnt : String; boundary: String; List : TList);
  720. // Splits the form into items
  721. var
  722. Sep : string;
  723. Clen,slen, p:longint;
  724. FI : TFormItem;
  725. begin
  726. Sep:='--'+boundary+#13+#10;
  727. Slen:=length(Sep);
  728. CLen:=Pos('--'+Boundary+'--',Cnt);
  729. // Cut last marker
  730. Cnt:=Copy(Cnt,1,Clen-1);
  731. // Cut first marker
  732. Delete(Cnt,1,Slen);
  733. Clen:=Length(Cnt);
  734. While Clen>0 do
  735. begin
  736. Fi:=TFormItem.Create;
  737. List.Add(Fi);
  738. P:=pos(Sep,Cnt);
  739. If (P=0) then
  740. P:=CLen+1;
  741. FI.Data:=Copy(Cnt,1,P-1);
  742. delete(Cnt,1,P+SLen-1);
  743. CLen:=Length(Cnt);
  744. end;
  745. end;
  746. { -------------------------------------------------------------------
  747. TRequest
  748. -------------------------------------------------------------------}
  749. constructor TRequest.create;
  750. begin
  751. inherited create;
  752. FFiles:=TUploadedFiles.Create(TUPloadedFile);
  753. end;
  754. destructor TRequest.destroy;
  755. begin
  756. FreeAndNil(FFiles);
  757. inherited destroy;
  758. end;
  759. function TRequest.GetNextPathInfo: String;
  760. Var
  761. P : String;
  762. i : Integer;
  763. begin
  764. P:=PathInfo;
  765. If (P<>'') and (P[1]='/') then
  766. Delete(P,1,1);
  767. Delete(P,1,Length(FReturnedPathInfo));
  768. I:=Pos('/',P);
  769. If (I=0) then
  770. I:=Length(P)+1;
  771. Result:=Copy(P,1,I-1);
  772. FReturnedPathInfo:=FReturnedPathInfo+'/'+Result;
  773. end;
  774. procedure TRequest.ParseFirstHeaderLine(const line: String);
  775. var
  776. i: Integer;
  777. begin
  778. FCommandLine := line;
  779. i := Pos(' ', line);
  780. FCommand := UpperCase(Copy(line, 1, i - 1));
  781. FURI := Copy(line, i + 1, Length(line));
  782. // Extract HTTP version
  783. i := Pos(' ', URI);
  784. if i > 0 then
  785. begin
  786. FHttpVersion := Copy(URI, i + 1, Length(URI));
  787. FURI := Copy(URI, 1, i - 1);
  788. FHttpVersion := Copy(HttpVersion, Pos('/', HttpVersion) + 1, Length(HttpVersion));
  789. end;
  790. // Extract query string
  791. i := Pos('?', URI);
  792. if i > 0 then
  793. begin
  794. FQuery:= Copy(URI, i + 1, Length(URI));
  795. FURI := Copy(URI, 1, i - 1);
  796. end;
  797. end;
  798. function TRequest.GetFirstHeaderLine: String;
  799. begin
  800. Result := Command + ' ' + URI;
  801. if Length(HttpVersion) > 0 then
  802. Result := Result + ' HTTP/' + HttpVersion;
  803. end;
  804. Procedure TRequest.ProcessQueryString(Const FQueryString : String);
  805. var
  806. queryItem : String;
  807. delimiter : Char;
  808. aString : String;
  809. aSepStr : String;
  810. aPos : Integer;
  811. aLenStr : Integer;
  812. aLenSep : Integer;
  813. function hexConverter(h1, h2 : Char) : Char;
  814. var
  815. B : Byte;
  816. begin
  817. B:=(Pos(upcase(h1),hexTable)-1)*16;
  818. B:=B+Pos(upcase(h2),hexTable)-1;
  819. Result:=chr(B);
  820. end;
  821. procedure InitToken(aStr, aSep : String);
  822. begin
  823. aString := aStr;
  824. aSepStr := aSep;
  825. aPos := 1;
  826. aLenStr := Length(aString);
  827. aLenSep := Length(aSepStr);
  828. end;
  829. function NextToken(var aToken : String; out aSepChar : Char) : Boolean;
  830. var
  831. i : Integer;
  832. j : Integer;
  833. BoT : Integer;
  834. EoT : Integer;
  835. isSep : Boolean;
  836. begin
  837. BoT:=aPos;
  838. EoT:=aPos;
  839. for i:=aPos to aLenStr do
  840. begin
  841. IsSep := false;
  842. for j := 1 to aLenSep do
  843. begin
  844. if aString[i] = aSepStr[j] then
  845. begin
  846. IsSep := true;
  847. Break;
  848. end;
  849. end;
  850. if IsSep then
  851. begin
  852. EoT := i;
  853. aPos := i + 1;
  854. aSepChar := aString[i];
  855. Break;
  856. end
  857. else
  858. begin
  859. if i = aLenStr then
  860. begin
  861. EoT := i;
  862. aPos := i;
  863. Break;
  864. end;
  865. end;
  866. end;
  867. if aPos < aLenStr then
  868. begin
  869. aToken := Copy(aString, BoT, EoT - BoT);
  870. Result := true;
  871. end
  872. else
  873. begin
  874. if aPos = aLenStr then
  875. begin
  876. aToken := Copy(aString, BoT, EoT - BoT + 1);
  877. Result := true;
  878. aPos := aPos + 1;
  879. end
  880. else
  881. begin
  882. Result := false;
  883. end;
  884. end;
  885. end;
  886. begin
  887. {$ifdef CGIDEBUG}SendMethodEnter('ProcessQueryString');{$endif CGIDEBUG}
  888. InitToken(FQueryString, '&');
  889. while NextToken(QueryItem, delimiter) do
  890. begin
  891. if (QueryItem<>'') then
  892. begin
  893. QueryItem:=HTTPDecode(QueryItem);
  894. FQueryFields.Add(QueryItem);
  895. end;
  896. end;
  897. {$ifdef CGIDEBUG}SendMethodExit('ProcessQueryString');{$endif CGIDEBUG}
  898. end;
  899. function TRequest.GetTempUploadFileName: String;
  900. begin
  901. Result:=GetTempFileName('/tmp/','CGI')
  902. end;
  903. Procedure TRequest.ProcessMultiPart(Stream : TStream; Const Boundary : String);
  904. Var
  905. L : TList;
  906. B : String;
  907. I : Integer;
  908. S,FF,key, Value : String;
  909. FI : TFormItem;
  910. F : TStream;
  911. begin
  912. {$ifdef CGIDEBUG} SendMethodEnter('ProcessMultiPart');{$endif CGIDEBUG}
  913. i:=Pos('=',Boundary);
  914. B:=Copy(Boundary,I+1,Length(Boundary)-I);
  915. I:=Length(B);
  916. If (I>0) and (B[1]='"') then
  917. B:=Copy(B,2,I-2);
  918. L:=TList.Create;
  919. Try
  920. SetLength(S,Stream.Size);
  921. If Length(S)>0 then
  922. if Stream is TCustomMemoryStream then
  923. // Faster.
  924. Move(TCustomMemoryStream(Stream).Memory^,S[1],Length(S))
  925. else
  926. begin
  927. Stream.Read(S[1],Length(S));
  928. Stream.Position:=0;
  929. end;
  930. FormSplit(S,B,L);
  931. For I:=L.Count-1 downto 0 do
  932. begin
  933. FI:=TFormItem(L[i]);
  934. FI.Process;
  935. If (FI.Name='') then
  936. Raise Exception.CreateFmt('Invalid multipart encoding: %s',[FI.Data]);
  937. {$ifdef CGIDEBUG}
  938. With FI Do
  939. begin
  940. SendSeparator;
  941. SendDebug ('PMP item Name : '+Name);
  942. SendDebug ('PMP item Disposition : '+Disposition);
  943. SendDebug ('PMP item FileName : '+FileName);
  944. SendBoolean('PMP item IsFile : ',IsFile);
  945. SendDebug ('PMP item ContentType : '+ContentType);
  946. SendInteger('PMP item DLen : ',DLen);
  947. SendDebug ('PMP item Data : '+Data);
  948. end;
  949. {$endif CGIDEBUG}
  950. Key:=FI.Name;
  951. If Not FI.IsFile Then
  952. Value:=FI.Data
  953. else
  954. begin
  955. Value:=FI.FileName;
  956. if Length(FI.Data)=0 then
  957. FF:=''
  958. else
  959. begin
  960. FF:=GetTempUploadFileName;
  961. F:=TFileStream.Create(FF,fmCreate);
  962. Try
  963. F.Write(FI.Data[1],Length(FI.Data));
  964. finally
  965. F.Free;
  966. end;
  967. end;
  968. With Files.Add as TUploadedFile do
  969. begin
  970. FieldName:=FI.Name;
  971. FileName:=FI.FileName;
  972. ContentType:=FI.ContentType;
  973. Disposition:=FI.Disposition;
  974. Size:=FI.DLen;
  975. LocalFileName:=FF;
  976. end;
  977. end;
  978. FI.Free;
  979. L[i]:=Nil;
  980. QueryFields.Add(Key+'='+Value)
  981. end;
  982. Finally
  983. For I:=0 to L.Count-1 do
  984. TObject(L[i]).Free;
  985. L.Free;
  986. end;
  987. {$ifdef CGIDEBUG} SendMethodExit('ProcessMultiPart');{$endif CGIDEBUG}
  988. end;
  989. Procedure TRequest.ProcessURLEncoded(Stream: TStream);
  990. var
  991. S : String;
  992. begin
  993. {$ifdef CGIDEBUG} SendMethodEnter('ProcessURLEncoded');{$endif CGIDEBUG}
  994. SetLength(S,Stream.Size); // Skip added Null.
  995. Stream.ReadBuffer(S[1],Stream.Size);
  996. {$ifdef CGIDEBUG}SendDebugFmt('Query string : %s',[s]);{$endif CGIDEBUG}
  997. ProcessQueryString(S);
  998. {$ifdef CGIDEBUG} SendMethodEnter('ProcessURLEncoded');{$endif CGIDEBUG}
  999. end;
  1000. { ---------------------------------------------------------------------
  1001. TUploadedFiles
  1002. ---------------------------------------------------------------------}
  1003. function TUploadedFiles.GetFile(Index : Integer): TUploadedFile;
  1004. begin
  1005. Result:=TUPloadedFile(Items[Index]);
  1006. end;
  1007. procedure TUploadedFiles.SetFile(Index : Integer; const AValue: TUploadedFile);
  1008. begin
  1009. Items[Index]:=AValue;
  1010. end;
  1011. function TUploadedFiles.IndexOfFile(AName: String): Integer;
  1012. begin
  1013. Result:=Count-1;
  1014. While (Result>=0) and (CompareText(Files[Result].FieldName,AName)<>0) do
  1015. Dec(Result);
  1016. end;
  1017. function TUploadedFiles.FileByName(AName: String): TUploadedFile;
  1018. begin
  1019. Result:=FindFile(AName);
  1020. If (Result=Nil) then
  1021. Raise HTTPError.CreateFmt(SErrNoSuchUploadedFile,[AName]);
  1022. end;
  1023. Function TUploadedFiles.FindFile(AName: String): TUploadedFile;
  1024. Var
  1025. I : Integer;
  1026. begin
  1027. I:=IndexOfFile(AName);
  1028. If (I=-1) then
  1029. Result:=Nil
  1030. else
  1031. Result:=Files[I];
  1032. end;
  1033. { ---------------------------------------------------------------------
  1034. TUploadedFile
  1035. ---------------------------------------------------------------------}
  1036. function TUploadedFile.GetStream: TStream;
  1037. begin
  1038. If (FStream=Nil) then
  1039. begin
  1040. If (FLocalFileName='') then
  1041. Raise HTTPError.Create(SErrInternalUploadedFileError);
  1042. FStream:=TFileStream.Create(FLocalFileName,fmOpenRead);
  1043. end;
  1044. Result:=FStream;
  1045. end;
  1046. destructor TUploadedFile.Destroy;
  1047. begin
  1048. FreeAndNil(FStream);
  1049. Inherited;
  1050. end;
  1051. { ---------------------------------------------------------------------
  1052. TResponse
  1053. ---------------------------------------------------------------------}
  1054. constructor TResponse.Create(ARequest : TRequest);
  1055. begin
  1056. inherited Create;
  1057. FRequest:=ARequest;
  1058. FCode := 200;
  1059. FCodeText := 'OK';
  1060. ContentType:='text/html';
  1061. FContents:=TStringList.Create;
  1062. TStringList(FContents).OnChange:=@ContentsChanged;
  1063. FCookies:=TCookies.Create(TCookie);
  1064. end;
  1065. destructor TResponse.destroy;
  1066. begin
  1067. FreeAndNil(FContents);
  1068. inherited destroy;
  1069. end;
  1070. procedure TResponse.SendContent;
  1071. begin
  1072. if ContentSent then
  1073. Raise HTTPError.Create(SErrContentAlreadySent);
  1074. if Not HeadersSent then
  1075. SendHeaders;
  1076. DoSendContent;
  1077. FContentSent:=True;
  1078. end;
  1079. procedure TResponse.SendHeaders;
  1080. Var
  1081. FHeaders : TStringList;
  1082. begin
  1083. if HeadersSent then
  1084. Raise HTTPError.Create(SErrHeadersAlreadySent);
  1085. FHeaders:=TStringList.Create;
  1086. CollectHeaders(FHeaders);
  1087. With Fheaders do
  1088. If (Count>0) and (Strings[Count-1]<>'') then
  1089. Add('');
  1090. Try
  1091. DoSendHeaders(FHeaders);
  1092. FHeadersSent:=True;
  1093. Finally
  1094. FHeaders.Free;
  1095. end;
  1096. end;
  1097. procedure TResponse.SendResponse;
  1098. begin
  1099. SendContent;
  1100. end;
  1101. procedure TResponse.SetFirstHeaderLine(const line: String);
  1102. var
  1103. i: Integer;
  1104. s: String;
  1105. begin
  1106. i := Pos('/', line);
  1107. s := Copy(line, i + 1, Length(line));
  1108. i := Pos(' ', s);
  1109. FHttpVersion := Copy(s, 1, i - 1);
  1110. s := Copy(s, i + 1, Length(s));
  1111. i := Pos(' ', s);
  1112. if i > 0 then begin
  1113. FCodeText := Copy(s, i + 1, Length(s));
  1114. s := Copy(s, 1, i - 1);
  1115. end;
  1116. FCode := StrToInt(s);
  1117. end;
  1118. procedure TResponse.SetContents(AValue: TStrings);
  1119. begin
  1120. FContentStream:=Nil;
  1121. FContents.Assign(AValue);
  1122. end;
  1123. function TResponse.GetContent: String;
  1124. begin
  1125. Result:=Contents.Text;
  1126. end;
  1127. procedure TResponse.SetContent(const AValue: String);
  1128. begin
  1129. FContentStream:=Nil;
  1130. FContents.Text:=AValue;
  1131. end;
  1132. procedure TResponse.SetContentStream(const AValue: TStream);
  1133. begin
  1134. If (FContentStream<>AValue) then
  1135. begin
  1136. FContentStream:=AValue;
  1137. If (FContentStream<>Nil) then
  1138. ContentLength:=FContentStream.Size
  1139. else
  1140. ContentLength:=0;
  1141. end;
  1142. end;
  1143. function TResponse.GetFirstHeaderLine: String;
  1144. begin
  1145. Result := Format('HTTP/%s %d %s', [HttpVersion, Code, CodeText]);
  1146. end;
  1147. procedure TResponse.ContentsChanged(Sender: TObject);
  1148. Var
  1149. I,L,LE : Integer;
  1150. begin
  1151. L:=0;
  1152. LE:=Length(LineEnding);
  1153. For I:=0 to FContents.Count-1 do
  1154. L:=L+Length(FContents[i])+LE;
  1155. ContentLength:=L;
  1156. end;
  1157. procedure TResponse.CollectHeaders(Headers: TStrings);
  1158. Var
  1159. I : Integer;
  1160. begin
  1161. Headers.add(Format('Status: %d %s',[Code,CodeText]));
  1162. {$ifdef cgidebug}
  1163. SendMethodEnter('Collectheaders');
  1164. If Not Assigned(FCookies) then
  1165. SendDebug('No cookies')
  1166. else
  1167. SendInteger('Nr of cookies',FCookies.Count);
  1168. {$endif}
  1169. For I:=0 to FCookies.Count-1 do
  1170. Headers.Add('Set-Cookie: '+FCookies[i].AsString);
  1171. For I:=0 to FieldCount-1 do
  1172. Headers.Add(Fields[i]);
  1173. Headers.Add('');
  1174. {$ifdef cgidebug} SendMethodExit('Collectheaders');{$endif}
  1175. end;
  1176. { TCookie }
  1177. function TCookie.GetAsString: string;
  1178. Var
  1179. Y,M,D : Word;
  1180. begin
  1181. {$ifdef cgidebug}SendMethodEnter('TCookie.GetAsString');{$endif}
  1182. try
  1183. Result:=Format('%s=%s;',[HTTPEncode(FName),HTTPEncode(FValue)]);
  1184. if (Length(FDomain)>0) then
  1185. Result:=Result+Format(SCookieDomain,[FDomain]);
  1186. if (Length(FPath)>0) then
  1187. Result:=Result+Format(SCookiePath,[FPath]);
  1188. if (FExpires>-1) then
  1189. begin
  1190. DecodeDate(Expires,Y,M,D);
  1191. Result:=Result+Format(FormatDateTime(SCookieExpire,Expires),
  1192. [HTTPDays[DayOfWeek(Expires)],HTTPMonths[M]]);
  1193. end;
  1194. if Secure then
  1195. Result:=Result+SCookieSecure;
  1196. except
  1197. {$ifdef cgidebug}
  1198. On E : Exception do
  1199. SendDebug('Exception in cookie asstring : '+E.Message)
  1200. {$endif}
  1201. end;
  1202. {$ifdef cgidebug}SendMethodExit('TCookie.GetAsString');{$endif}
  1203. end;
  1204. constructor TCookie.Create(ACollection: TCollection);
  1205. begin
  1206. inherited Create(ACollection);
  1207. FExpires:=-1;
  1208. end;
  1209. procedure TCookie.Assign(Source: TPersistent);
  1210. begin
  1211. if Source is TCookie then
  1212. with TCookie(Source) do
  1213. begin
  1214. Self.FName:=Name;
  1215. Self.FValue:=Value;
  1216. Self.FDomain:=Domain;
  1217. Self.FPath:=Path;
  1218. Self.FExpires:=Expires;
  1219. Self.FSecure:=Secure;
  1220. end
  1221. else
  1222. inherited Assign(Source);
  1223. end;
  1224. { TCookieCollection }
  1225. function TCookies.GetCookie(Index: Integer): TCookie;
  1226. begin
  1227. {$ifdef cgidebug}SendMethodExit('TCookies.GetCookie');{$endif}
  1228. Result:=TCookie(inherited Items[Index]);
  1229. {$ifdef cgidebug}SendMethodExit('TCookies.GetCookie');{$endif}
  1230. end;
  1231. procedure TCookies.SetCookie(Index: Integer; Value: TCookie);
  1232. begin
  1233. Items[Index]:=Value
  1234. end;
  1235. function TCookies.Add: TCookie;
  1236. begin
  1237. Result:=TCookie(Inherited Add);
  1238. end;
  1239. function TCookies.CookieByName(AName: String): TCookie;
  1240. begin
  1241. Result:=FindCookie(AName);
  1242. If (Result=Nil) then
  1243. Raise HTTPError.CreateFmt(SErrUnknownCookie,[AName]);
  1244. end;
  1245. function TCookies.FindCookie(AName: String): TCookie;
  1246. Var
  1247. I : Integer;
  1248. begin
  1249. I:=IndexOfCookie(AName);
  1250. If (I=-1) then
  1251. Result:=Nil
  1252. else
  1253. Result:=GetCookie(I);
  1254. end;
  1255. function TCookies.IndexOfCookie(AName: String): Integer;
  1256. begin
  1257. Result:=Count-1;
  1258. While (Result>=0) and (CompareText(GetCookie(Result).Name,AName)<>0) do
  1259. Dec(Result);
  1260. end;
  1261. { TCustomSession }
  1262. function TCustomSession.GetSessionID: String;
  1263. Var
  1264. G : TGUID;
  1265. begin
  1266. CreateGUID(G);
  1267. Result:=GuiDToString(G);
  1268. end;
  1269. constructor TCustomSession.Create(AOwner: TComponent);
  1270. begin
  1271. FTimeOut:=15;
  1272. inherited Create(AOwner);
  1273. end;
  1274. procedure TCustomSession.InitResponse(AResponse: TResponse);
  1275. begin
  1276. // do nothing
  1277. end;
  1278. procedure TCustomSession.InitSession(ARequest: TRequest; OnNewSession,OnExpired : TNotifyEvent);
  1279. begin
  1280. // Do nothing
  1281. end;
  1282. end.