httpdefs.pp 40 KB

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