IdCustomHTTPServer.pas 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10117: IdCustomHTTPServer.pas
  11. {
  12. { Rev 1.6 5/6/04 3:17:40 PM RLebeau
  13. { Updated TIdHTTPResponseInfo.WriteContent() to check the ContentText first
  14. { instead of the ContentStream
  15. }
  16. {
  17. { Rev 1.5 05.6.2003 ã. 11:03:56 DBondzhev
  18. { Socket exctions should not be stopped after DoCommandGet
  19. }
  20. {
  21. Rev 1.4 5/8/2003 4:51:40 PM BGooijen
  22. fixed av on FSessionList.PurgeStaleSessions(Terminated);
  23. }
  24. {
  25. Rev 1.3 2/25/2003 10:44:30 AM BGooijen
  26. The Serversoftware wasn't send to the client, because of duplicate properties
  27. (.Server and .ServerSoftware).
  28. }
  29. {
  30. { Rev 1.2 11.2.2003 13:33:30 TPrami
  31. { - Fixed URL get parameter handling (RFC 1866 section 8.2.1.)
  32. }
  33. {
  34. { Rev 1.1 5/12/2002 10:17:32 AM SGrobety
  35. }
  36. {
  37. { Rev 1.0 2002.11.12 10:34:42 PM czhower
  38. }
  39. unit IdCustomHTTPServer;
  40. interface
  41. uses
  42. Classes,
  43. IdAssignedNumbers,
  44. IdException, IdGlobal, IdHeaderList, IdTCPServer, IdThread, IdCookie,
  45. IdHTTPHeaderInfo, IdStackConsts,
  46. SyncObjs, SysUtils;
  47. const
  48. Id_TId_HTTPServer_KeepAlive = false;
  49. Id_TId_HTTPServer_ParseParams = True;
  50. Id_TId_HTTPServer_SessionState = False;
  51. {This probably should be something else but I don't know what
  52. I have fixed a problem which was caused by a timeout of 0 so I am extremely
  53. suspecious of this}
  54. Id_TId_HTTPSessionTimeOut = 0;
  55. Id_TId_HTTPAutoStartSession = False;
  56. GResponseNo = 200;
  57. GFContentLength = -1;
  58. GServerSoftware = gsIdProductName + '/' + gsIdVersion; {Do not Localize}
  59. GContentType = 'text/html'; {Do not Localize}
  60. GSessionIDCookie = 'IDHTTPSESSIONID'; {Do not Localize}
  61. type
  62. // Forwards
  63. TIdHTTPSession = Class;
  64. TIdHTTPCustomSessionList = Class;
  65. TIdHTTPRequestInfo = Class;
  66. TIdHTTPResponseInfo = Class;
  67. //events
  68. TOnSessionEndEvent = procedure(Sender: TIdHTTPSession) of object;
  69. TOnSessionStartEvent = procedure(Sender: TIdHTTPSession) of object;
  70. TOnCreateSession = procedure(ASender: TIdPeerThread;
  71. var VHTTPSession: TIdHTTPSession) of object;
  72. TOnCreatePostStream = procedure(ASender: TIdPeerThread;
  73. var VPostStream: TStream) of object;
  74. TIdHTTPGetEvent = procedure(AThread: TIdPeerThread;
  75. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo) of object;
  76. TIdHTTPOtherEvent = procedure(Thread: TIdPeerThread;
  77. const asCommand, asData, asVersion: string) of object;
  78. TIdHTTPInvalidSessionEvent = procedure(Thread: TIdPeerThread;
  79. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo;
  80. var VContinueProcessing: Boolean; const AInvalidSessionID: String) of object;
  81. //objects
  82. EIdHTTPServerError = class(EIdException);
  83. EIdHTTPHeaderAlreadyWritten = class(EIdHTTPServerError);
  84. EIdHTTPErrorParsingCommand = class(EIdHTTPServerError);
  85. EIdHTTPUnsupportedAuthorisationScheme = class(EIdHTTPServerError);
  86. EIdHTTPCannotSwitchSessionStateWhenActive = class(EIdHTTPServerError);
  87. TIdHTTPRequestInfo = class(TIdRequestHeaderInfo)
  88. protected
  89. FAuthExists: Boolean;
  90. FCookies: TIdServerCookies;
  91. FParams: TStrings;
  92. FPostStream: TStream;
  93. FRawHTTPCommand: string;
  94. FRemoteIP: string;
  95. FSession: TIdHTTPSession;
  96. FDocument: string;
  97. FCommand: string;
  98. FVersion: string;
  99. FAuthUsername: string;
  100. FAuthPassword: string;
  101. FUnparsedParams: string;
  102. FQueryParams: string;
  103. FFormParams: string;
  104. //
  105. procedure DecodeAndSetParams(const AValue: String);
  106. public
  107. constructor Create; override;
  108. destructor Destroy; override;
  109. property Session: TIdHTTPSession read FSession;
  110. //
  111. property AuthExists: Boolean read FAuthExists;
  112. property AuthPassword: string read FAuthPassword;
  113. property AuthUsername: string read FAuthUsername;
  114. property Command: string read FCommand;
  115. property Cookies: TIdServerCookies read FCookies;
  116. property Document: string read FDocument write FDocument; // writable for isapi compatibility. Use with care
  117. property Params: TStrings read FParams;
  118. property PostStream: TStream read FPostStream write FPostStream;
  119. property RawHTTPCommand: string read FRawHTTPCommand;
  120. property RemoteIP: String read FRemoteIP;
  121. property UnparsedParams: string read FUnparsedParams write FUnparsedParams; // writable for isapi compatibility. Use with care
  122. property FormParams: string read FFormParams write FFormParams; // writable for isapi compatibility. Use with care
  123. property QueryParams: string read FQueryParams write FQueryParams; // writable for isapi compatibility. Use with care
  124. property Version: string read FVersion;
  125. end;
  126. TIdHTTPResponseInfo = class(TIdResponseHeaderInfo)
  127. protected
  128. FAuthRealm: string;
  129. FContentType: string;
  130. FConnection: TIdTCPServerConnection;
  131. FResponseNo: Integer;
  132. FCookies: TIdServerCookies;
  133. FContentStream: TStream;
  134. FContentText: string;
  135. FCloseConnection: Boolean;
  136. FFreeContentStream: Boolean;
  137. FHeaderHasBeenWritten: Boolean;
  138. FResponseText: string;
  139. FSession: TIdHTTPSession;
  140. //
  141. procedure ReleaseContentStream;
  142. procedure SetCookies(const AValue: TIdServerCookies);
  143. procedure SetHeaders; override;
  144. procedure SetResponseNo(const AValue: Integer);
  145. procedure SetCloseConnection(const Value: Boolean);
  146. public
  147. procedure CloseSession;
  148. constructor Create(AConnection: TIdTCPServerConnection); reintroduce;
  149. destructor Destroy; override;
  150. procedure Redirect(const AURL: string);
  151. procedure WriteHeader;
  152. procedure WriteContent;
  153. //
  154. property AuthRealm: string read FAuthRealm write FAuthRealm;
  155. property CloseConnection: Boolean read FCloseConnection write SetCloseConnection;
  156. property ContentStream: TStream read FContentStream write FContentStream;
  157. property ContentText: string read FContentText write FContentText;
  158. property Cookies: TIdServerCookies read FCookies write SetCookies;
  159. property FreeContentStream: Boolean read FFreeContentStream write FFreeContentStream;
  160. // writable for isapi compatibility. Use with care
  161. property HeaderHasBeenWritten: Boolean read FHeaderHasBeenWritten write FHeaderHasBeenWritten;
  162. property ResponseNo: Integer read FResponseNo write SetResponseNo;
  163. property ResponseText: String read FResponseText write FResponseText;
  164. property ServerSoftware: string read FServer write FServer;
  165. property Session: TIdHTTPSession read FSession;
  166. end;
  167. TIdHTTPSession = Class(TObject)
  168. protected
  169. FContent: TStrings;
  170. FLastTimeStamp: TDateTime;
  171. FLock: TCriticalSection;
  172. FOwner: TIdHTTPCustomSessionList;
  173. FSessionID: string;
  174. FRemoteHost: string;
  175. //
  176. procedure SetContent(const Value: TStrings);
  177. function GetContent: TStrings;
  178. function IsSessionStale: boolean; virtual;
  179. procedure DoSessionEnd; virtual;
  180. public
  181. constructor Create(AOwner: TIdHTTPCustomSessionList); virtual;
  182. constructor CreateInitialized(AOwner: TIdHTTPCustomSessionList; const SessionID,
  183. RemoteIP: string); virtual;
  184. destructor Destroy; override;
  185. procedure Lock;
  186. procedure Unlock;
  187. //
  188. property Content: TStrings read GetContent write SetContent;
  189. property LastTimeStamp: TDateTime read FLastTimeStamp;
  190. property RemoteHost: string read FRemoteHost;
  191. property SessionID: String read FSessionID;
  192. end;
  193. TIdHTTPCustomSessionList = class(TComponent)
  194. private
  195. FSessionTimeout: Integer;
  196. FOnSessionEnd: TOnSessionEndEvent;
  197. FOnSessionStart: TOnSessionStartEvent;
  198. protected
  199. // remove a session from the session list. Called by the session on "Free"
  200. procedure RemoveSession(Session: TIdHTTPSession); virtual; abstract;
  201. public
  202. procedure Clear; virtual; abstract;
  203. procedure PurgeStaleSessions(PurgeAll: Boolean = false); virtual; abstract;
  204. function CreateUniqueSession(const RemoteIP: String): TIdHTTPSession; virtual; abstract;
  205. function CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession; virtual; abstract;
  206. function GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; virtual; abstract;
  207. procedure Add(ASession: TIdHTTPSession); virtual; Abstract;
  208. published
  209. property SessionTimeout: Integer read FSessionTimeout write FSessionTimeout;
  210. property OnSessionEnd: TOnSessionEndEvent read FOnSessionEnd write FOnSessionEnd;
  211. property OnSessionStart: TOnSessionStartEvent read FOnSessionStart write FOnSessionStart;
  212. end;
  213. TIdCustomHTTPServer = class(TIdTCPServer)
  214. protected
  215. FAutoStartSession: Boolean;
  216. FKeepAlive: Boolean;
  217. FParseParams: Boolean;
  218. FServerSoftware: string;
  219. FMIMETable: TIdMimeTable;
  220. FSessionList: TIdHTTPCustomSessionList;
  221. FSessionState: Boolean;
  222. FSessionTimeOut: Integer;
  223. FOkToProcessCommand : Boolean; // allow descendents to process requests without requiring FOnCommandGet to be assigned
  224. FOnCreatePostStream: TOnCreatePostStream;
  225. FOnCreateSession: TOnCreateSession;
  226. FOnInvalidSession: TIdHTTPInvalidSessionEvent;
  227. FOnSessionEnd: TOnSessionEndEvent;
  228. FOnSessionStart: TOnSessionStartEvent;
  229. FOnCommandGet: TIdHTTPGetEvent;
  230. FOnCommandOther: TIdHTTPOtherEvent;
  231. FSessionCleanupThread: TIdThread;
  232. //
  233. procedure DoOnCreateSession(AThread: TIdPeerThread; var VNewSession: TIdHTTPSession); virtual;
  234. procedure DoInvalidSession(AThread: TIdPeerThread;
  235. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo;
  236. var VContinueProcessing: Boolean; const AInvalidSessionID: String); virtual;
  237. procedure DoCommandOther(AThread: TIdPeerThread; const asCommand, asData
  238. , asVersion: string); virtual;
  239. procedure DoCommandGet(AThread: TIdPeerThread;
  240. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  241. virtual;
  242. procedure CreatePostStream(ASender: TIdPeerThread; var VPostStream: TStream); virtual;
  243. procedure DoCreatePostStream(ASender: TIdPeerThread;
  244. var VPostStream: TStream);
  245. function DoExecute(AThread: TIdPeerThread): Boolean; override;
  246. procedure SetActive(AValue: Boolean); override;
  247. procedure SetSessionState(const Value: Boolean);
  248. function GetSessionFromCookie(AThread: TIdPeerThread;
  249. AHTTPrequest: TIdHTTPRequestInfo; AHTTPResponse: TIdHTTPResponseInfo;
  250. var VContinueProcessing: Boolean): TIdHTTPSession;
  251. { to be published in TIdHTTPServer}
  252. property OnCreatePostStream: TOnCreatePostStream read FOnCreatePostStream
  253. write FOnCreatePostStream;
  254. property OnCommandGet: TIdHTTPGetEvent read FOnCommandGet
  255. write FOnCommandGet;
  256. public
  257. constructor Create(AOwner: TComponent); Override;
  258. function CreateSession(AThread: TIdPeerThread;
  259. HTTPResponse: TIdHTTPResponseInfo;
  260. HTTPRequest: TIdHTTPRequestInfo): TIdHTTPSession;
  261. destructor Destroy; override;
  262. function EndSession(const SessionName: string): boolean;
  263. function ServeFile(AThread: TIdPeerThread; ResponseInfo: TIdHTTPResponseInfo; aFile: TFileName): cardinal; virtual;
  264. //
  265. property MIMETable: TIdMimeTable read FMIMETable;
  266. property SessionList: TIdHTTPCustomSessionList read FSessionList;
  267. published
  268. property AutoStartSession: boolean read FAutoStartSession write FAutoStartSession default Id_TId_HTTPAutoStartSession;
  269. property DefaultPort default IdPORT_HTTP;
  270. property OnInvalidSession: TIdHTTPInvalidSessionEvent read FOnInvalidSession
  271. write FOnInvalidSession;
  272. property OnSessionStart: TOnSessionStartEvent read FOnSessionStart
  273. write FOnSessionStart;
  274. property OnSessionEnd: TOnSessionEndEvent read FOnSessionEnd
  275. write FOnSessionEnd;
  276. property OnCreateSession: TOnCreateSession read FOnCreateSession
  277. write FOnCreateSession;
  278. property KeepAlive: Boolean read FKeepAlive write FKeepAlive
  279. default Id_TId_HTTPServer_KeepAlive;
  280. property ParseParams: boolean read FParseParams write FParseParams
  281. default Id_TId_HTTPServer_ParseParams;
  282. property ServerSoftware: string read FServerSoftware write FServerSoftware;
  283. property SessionState: Boolean read FSessionState write SetSessionState
  284. default Id_TId_HTTPServer_SessionState;
  285. property SessionTimeOut: Integer read FSessionTimeOut write FSessionTimeOut
  286. default Id_TId_HTTPSessionTimeOut;
  287. property OnCommandOther: TIdHTTPOtherEvent read FOnCommandOther
  288. write FOnCommandOther;
  289. end;
  290. TIdHTTPDefaultSessionList = Class(TIdHTTPCustomSessionList)
  291. protected
  292. SessionList: TThreadList;
  293. procedure RemoveSession(Session: TIdHTTPSession); override;
  294. // remove a session surgically when list already locked down (prevent deadlock)
  295. procedure RemoveSessionFromLockedList(AIndex: Integer; ALockedSessionList: TList);
  296. public
  297. Constructor Create(AOwner: TComponent); override;
  298. destructor Destroy; override;
  299. procedure Clear; override;
  300. procedure Add(ASession: TIdHTTPSession); override;
  301. procedure PurgeStaleSessions(PurgeAll: Boolean = false); override;
  302. function CreateUniqueSession(const RemoteIP: String): TIdHTTPSession; override;
  303. function CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession; override;
  304. function GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; override;
  305. end;
  306. implementation
  307. uses
  308. IdCoderMIME, IdResourceStrings, IdURI, IdIOHandlerSocket, IdTCPConnection;
  309. const
  310. SessionCapacity = 128;
  311. // Calculate the number of MS between two TimeStamps
  312. function TimeStampInterval(StartStamp, EndStamp: TDateTime): integer;
  313. var
  314. days: Integer;
  315. hour, min, s, ms: Word;
  316. begin
  317. days := Trunc(EndStamp - StartStamp); // whole days
  318. DecodeTime(EndStamp - StartStamp, hour, min, s, ms);
  319. result := (((days * 24 + hour) * 60 + min) * 60 + s) * 1000 + ms;
  320. end;
  321. function GetRandomString(NumChar: cardinal): string;
  322. const
  323. CharMap='qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890'; {Do not Localize}
  324. var
  325. i: integer;
  326. MaxChar: cardinal;
  327. begin
  328. randomize;
  329. MaxChar := length(CharMap) - 1;
  330. for i := 1 to NumChar do
  331. begin
  332. // Add one because CharMap is 1-based
  333. Result := result + CharMap[Random(maxChar) + 1];
  334. end;
  335. end;
  336. type
  337. TIdHTTPSessionCleanerThread = Class(TIdThread)
  338. protected
  339. FSessionList: TIdHTTPCustomSessionList;
  340. public
  341. constructor Create(SessionList: TIdHTTPCustomSessionList); reintroduce;
  342. procedure AfterRun; override;
  343. procedure Run; override;
  344. end; // class
  345. { TIdCustomHTTPServer }
  346. constructor TIdCustomHTTPServer.Create(AOwner: TComponent);
  347. begin
  348. inherited Create(AOwner);
  349. FSessionState := Id_TId_HTTPServer_SessionState;
  350. DefaultPort := IdPORT_HTTP;
  351. ParseParams := Id_TId_HTTPServer_ParseParams;
  352. FSessionList := TIdHTTPDefaultSessionList.Create(Self);
  353. FMIMETable := TIdMimeTable.Create(True);
  354. FSessionTimeOut := Id_TId_HTTPSessionTimeOut;
  355. FAutoStartSession := Id_TId_HTTPAutoStartSession;
  356. FKeepAlive := Id_TId_HTTPServer_KeepAlive;
  357. FOkToProcessCommand := false;
  358. end;
  359. procedure TIdCustomHTTPServer.DoOnCreateSession(AThread: TIdPeerThread; Var VNewSession: TIdHTTPSession);
  360. begin
  361. VNewSession := nil;
  362. if Assigned(FOnCreateSession) then
  363. begin
  364. OnCreateSession(AThread, VNewSession);
  365. end;
  366. end;
  367. function TIdCustomHTTPServer.CreateSession(AThread: TIdPeerThread; HTTPResponse: TIdHTTPResponseInfo;
  368. HTTPRequest: TIdHTTPRequestInfo): TIdHTTPSession;
  369. begin
  370. if SessionState then begin
  371. DoOnCreateSession(AThread, Result);
  372. if not Assigned(result) then
  373. begin
  374. result := FSessionList.CreateUniqueSession(HTTPRequest.RemoteIP);
  375. end
  376. else begin
  377. FSessionList.Add(result);
  378. end;
  379. with HTTPResponse.Cookies.Add do
  380. begin
  381. CookieName := GSessionIDCookie;
  382. Value := result.SessionID;
  383. Path := '/'; {Do not Localize}
  384. MaxAge := -1; // By default the cookies wil be valid until the user has closed his browser window.
  385. // MaxAge := SessionTimeOut div 1000;
  386. end;
  387. HTTPResponse.FSession := result;
  388. HTTPRequest.FSession := result;
  389. end else begin
  390. result := nil;
  391. end;
  392. end;
  393. destructor TIdCustomHTTPServer.Destroy;
  394. begin
  395. Active := false; // Set Active to false in order to cloase all active sessions.
  396. FreeAndNil(FMIMETable);
  397. FreeAndNil(FSessionList);
  398. inherited Destroy;
  399. end;
  400. procedure TIdCustomHTTPServer.DoCommandGet(AThread: TIdPeerThread;
  401. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  402. begin
  403. if Assigned(FOnCommandGet) then begin
  404. FOnCommandGet(AThread, ARequestInfo, AResponseInfo);
  405. end;
  406. end;
  407. procedure TIdCustomHTTPServer.DoCommandOther(AThread: TIdPeerThread;
  408. const asCommand, asData, asVersion: string);
  409. begin
  410. if Assigned(FOnCommandOther) then begin
  411. OnCommandOther(AThread, asCommand, asData, asVersion);
  412. end;
  413. end;
  414. function TIdCustomHTTPServer.DoExecute(AThread: TIdPeerThread): boolean;
  415. var
  416. LRequestInfo: TIdHTTPRequestInfo;
  417. LResponseInfo: TIdHTTPResponseInfo;
  418. procedure ReadCookiesFromRequestHeader;
  419. var
  420. LRawCookies: TStringList;
  421. i: Integer;
  422. S: String;
  423. begin
  424. LRawCookies := TStringList.Create; try
  425. LRequestInfo.RawHeaders.Extract('cookie', LRawCookies); {Do not Localize}
  426. for i := 0 to LRawCookies.Count -1 do begin
  427. S := LRawCookies[i];
  428. while IndyPos(';', S) > 0 do begin {Do not Localize}
  429. LRequestInfo.Cookies.AddSrcCookie(Fetch(S, ';')); {Do not Localize}
  430. S := Trim(S);
  431. end;
  432. if S <> '' then
  433. LRequestInfo.Cookies.AddSrcCookie(S);
  434. end;
  435. finally LRawCookies.Free; end;
  436. end;
  437. var
  438. i: integer;
  439. s, sInputLine, sCmd, sVersion: String;
  440. LURI: TIdURI;
  441. LImplicitPostStream: Boolean;
  442. LRawHTTPCommand: string;
  443. ContinueProcessing: Boolean;
  444. LCloseConnection: Boolean;
  445. begin
  446. ContinueProcessing := True;
  447. Result := True;
  448. LCloseConnection := not KeepAlive;
  449. try
  450. try repeat
  451. with AThread.Connection do begin
  452. sInputLine := ReadLn;
  453. LRawHTTPCommand := sInputLine;
  454. i := idGlobal.RPos(' ', sInputLine, -1); {Do not Localize}
  455. if i = 0 then begin
  456. raise EIdHTTPErrorParsingCommand.Create(RSHTTPErrorParsingCommand);
  457. end;
  458. sVersion := Copy(sInputLine, i + 1, MaxInt);
  459. SetLength(sInputLine, i - 1);
  460. {TODO Check for 1.0 only at this point}
  461. sCmd := UpperCase(Fetch(sInputLine, ' ')); {Do not Localize}
  462. // These essentially all "retrieve" so they are all "Get"s
  463. if ((sCmd = 'GET') or (sCmd = 'POST') {Do not Localize}
  464. or (sCmd = 'HEAD')) and (Assigned(OnCommandGet) or FOkToProcessCommand) then begin {Do not Localize}
  465. LRequestInfo := TIdHTTPRequestInfo.Create; try
  466. LRequestInfo.FRawHTTPCommand := LRawHTTPCommand;
  467. LRequestInfo.FRemoteIP := (AThread.Connection.IOHandler as TIdIOHandlerSocket).Binding.PeerIP;
  468. LRequestInfo.FCommand := sCmd;
  469. // Retrieve the HTTP header
  470. LRequestInfo.RawHeaders.Clear;
  471. Capture(LRequestInfo.RawHeaders, ''); {Do not Localize}
  472. LRequestInfo.ProcessHeaders;
  473. // Grab Params so we can parse them
  474. // POSTed data - may exist with GETs also. With GETs, the action
  475. // params from the form element will be posted
  476. // TODO: Rune this is the area that needs fixed. Ive hacked it for now
  477. // Get data can exists with POSTs, but can POST data exist with GETs?
  478. // If only the first, the solution is easy. If both - need more
  479. // investigation.
  480. // i := StrToIntDef(LRequestInfo.Headers.Values['Content-Length'], -1); {Do not Localize}
  481. LRequestInfo.PostStream := nil;
  482. CreatePostStream(AThread, LRequestInfo.FPostStream);
  483. LImplicitPostStream := LRequestInfo.PostStream = nil;
  484. try
  485. if LImplicitPostStream then begin
  486. LRequestInfo.PostStream := TStringStream.Create(''); {Do not Localize}
  487. end;
  488. if LRequestInfo.ContentLength > 0 then begin
  489. AThread.Connection.ReadStream(LRequestInfo.PostStream
  490. , LRequestInfo.ContentLength);
  491. end else begin
  492. if sCmd = 'POST' then begin {Do not Localize}
  493. if not LRequestInfo.HasContentLength then
  494. AThread.Connection.ReadStream(LRequestInfo.PostStream, -1, True);
  495. {LResponseInfo := TIdHTTPResponseInfo.Create(AThread.Connection);
  496. try
  497. LResponseInfo.SetResponseNo(406);
  498. LResponseInfo.WriteHeader;
  499. LResponseInfo.WriteContent;
  500. raise EIdClosedSocket.Create(''); // Force the server to close the connection and to free all associated resources
  501. finally
  502. LResponseInfo.Free;
  503. end;
  504. {if LowerCase(LRequestInfo.ContentType) = 'application/x-www-form-urlencoded' then begin
  505. S := ReadLn;
  506. LRequestInfo.PostStream.Write(S[1], Length(S));
  507. end
  508. else}
  509. end;
  510. end;
  511. if LRequestInfo.PostStream is TStringStream then begin
  512. LRequestInfo.FormParams := TStringStream(LRequestInfo.PostStream).DataString;
  513. LRequestInfo.UnparsedParams := LRequestInfo.FormParams;
  514. end;
  515. finally
  516. if LImplicitPostStream then begin
  517. FreeAndNil(LRequestInfo.FPostStream);
  518. end;
  519. end;
  520. // GET data - may exist with POSTs also
  521. LRequestInfo.QueryParams := sInputLine;
  522. sInputLine := Fetch(LRequestInfo.FQueryParams, '?'); {Do not Localize}
  523. // glue together parameters passed in the URL and those
  524. //
  525. if Length(LRequestInfo.QueryParams) > 0 then begin
  526. if Length(LRequestInfo.UnparsedParams) = 0 then begin
  527. LRequestInfo.FUnparsedParams := LRequestInfo.QueryParams;
  528. end else begin
  529. LRequestInfo.FUnparsedParams := LRequestInfo.UnparsedParams + '&' {Do not Localize}
  530. + LRequestInfo.QueryParams;
  531. end;
  532. end;
  533. // Parse Params
  534. if ParseParams then begin
  535. if (LowerCase(LRequestInfo.ContentType) = 'application/x-www-form-urlencoded') then begin {Do not Localize}
  536. LRequestInfo.DecodeAndSetParams(LRequestInfo.UnparsedParams);
  537. end
  538. else begin
  539. // Parse only query params when content type is not 'application/x-www-form-urlencoded' {Do not Localize}
  540. LRequestInfo.DecodeAndSetParams(LRequestInfo.QueryParams);
  541. end;
  542. end;
  543. // Cookies
  544. ReadCookiesFromRequestHeader;
  545. // Host
  546. // LRequestInfo.FHost := LRequestInfo.Headers.Values['host']; {Do not Localize}
  547. LRequestInfo.FVersion := sVersion;
  548. // Parse the document input line
  549. if sInputLine = '*' then begin {Do not Localize}
  550. LRequestInfo.FDocument := '*'; {Do not Localize}
  551. end else begin
  552. LURI := TIdURI.Create(sInputLine);
  553. // SG 29/11/01: Per request of Doychin
  554. // Try to fill the "host" parameter
  555. LRequestInfo.FDocument := TIdURI.URLDecode(LURI.Path) + TIdURI.URLDecode(LURI.Document) + LURI.Params;
  556. if (Length(LURI.Host) > 0) and (Length(LRequestInfo.FHost) = 0) then begin
  557. LRequestInfo.FHost := LURI.Host;
  558. end;
  559. LURI.Free;
  560. end;
  561. s := LRequestInfo.RawHeaders.Values['Authorization']; {Do not Localize}
  562. LRequestInfo.FAuthExists := Length(s) > 0;
  563. if LRequestInfo.AuthExists then begin
  564. if AnsiCompareText(Fetch(s, ' '), 'Basic') = 0 then begin {Do not Localize}
  565. s := TIdDecoderMIME.DecodeString(s);
  566. LRequestInfo.FAuthUsername := Fetch(s, ':'); {Do not Localize}
  567. LRequestInfo.FAuthPassword := s;
  568. end else begin
  569. raise EIdHTTPUnsupportedAuthorisationScheme.Create(
  570. RSHTTPUnsupportedAuthorisationScheme);
  571. end;
  572. end;
  573. LResponseInfo := TIdHTTPResponseInfo.Create(AThread.Connection); try
  574. LResponseInfo.CloseConnection := not (FKeepAlive and
  575. AnsiSameText(LRequestInfo.Connection, 'Keep-alive')); {Do not Localize}
  576. // Session management
  577. GetSessionFromCookie(AThread, LRequestInfo, LResponseInfo
  578. , ContinueProcessing);
  579. // SG 05.07.99
  580. // Set the ServerSoftware string to what it's supposed to be. {Do not Localize}
  581. if Length(Trim(ServerSoftware)) > 0 then begin
  582. LResponseInfo.ServerSoftware := ServerSoftware;
  583. end;
  584. try
  585. if ContinueProcessing then begin
  586. DoCommandGet(AThread, LRequestInfo, LResponseInfo);
  587. end;
  588. except
  589. on E: EIdSocketError do begin
  590. raise;
  591. end;
  592. on E: Exception do begin
  593. LResponseInfo.ResponseNo := 500;
  594. LResponseInfo.ContentText := E.Message;
  595. end;
  596. end;
  597. // Write even though WriteContent will, may be a redirect or other
  598. if not LResponseInfo.HeaderHasBeenWritten then begin
  599. LResponseInfo.WriteHeader;
  600. end;
  601. // Always check ContentText first
  602. if (Length(LResponseInfo.ContentText) > 0)
  603. or Assigned(LResponseInfo.ContentStream) then begin
  604. LResponseInfo.WriteContent;
  605. end;
  606. finally
  607. LCloseConnection := LResponseInfo.CloseConnection;
  608. FreeAndNil(LResponseInfo);
  609. end;
  610. finally FreeAndNil(LRequestInfo); end;
  611. end else begin
  612. DoCommandOther(AThread, sCmd, sInputLine, sVersion);
  613. end;
  614. end;
  615. until LCloseConnection;
  616. except
  617. on E: EIdSocketError do begin
  618. if E.LastError <> Id_WSAECONNRESET then raise;
  619. end;
  620. on E: EIdClosedSocket do
  621. AThread.Connection.Disconnect;
  622. end;
  623. finally AThread.Connection.Disconnect; end;
  624. end;
  625. procedure TIdCustomHTTPServer.DoInvalidSession(AThread: TIdPeerThread;
  626. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo;
  627. var VContinueProcessing: Boolean; const AInvalidSessionID: String);
  628. begin
  629. if Assigned(FOnInvalidSession) then begin
  630. FOnInvalidSession(AThread, ARequestInfo, AResponseInfo, VContinueProcessing, AInvalidSessionID)
  631. end;
  632. end;
  633. function TIdCustomHTTPServer.EndSession(const SessionName: string): boolean;
  634. var
  635. ASession: TIdHTTPSession;
  636. begin
  637. ASession := SessionList.GetSession(SessionName, ''); {Do not Localize}
  638. result := Assigned(ASession);
  639. if result then
  640. begin
  641. ASession.free;
  642. end;
  643. end;
  644. function TIdCustomHTTPServer.GetSessionFromCookie(AThread: TIdPeerThread;
  645. AHTTPRequest: TIdHTTPRequestInfo; AHTTPResponse: TIdHTTPResponseInfo;
  646. var VContinueProcessing: Boolean): TIdHTTPSession;
  647. var
  648. CurrentCookieIndex: Integer;
  649. SessionId: String;
  650. begin
  651. Result := nil;
  652. VContinueProcessing := True;
  653. if SessionState then
  654. begin
  655. CurrentCookieIndex := AHTTPRequest.Cookies.GetCookieIndex(0, GSessionIDCookie);
  656. while (result = nil) and (CurrentCookieIndex >= 0) do
  657. begin
  658. SessionId := AHTTPRequest.Cookies.Items[CurrentCookieIndex].Value;
  659. Result := FSessionList.GetSession(SessionID, AHTTPrequest.RemoteIP);
  660. if not Assigned(Result) then
  661. DoInvalidSession(AThread, AHTTPRequest, AHTTPResponse, VContinueProcessing, SessionId);
  662. Inc(CurrentCookieIndex);
  663. CurrentCookieIndex := AHTTPRequest.Cookies.GetCookieIndex(CurrentCookieIndex, GSessionIDCookie);
  664. end; { while }
  665. // check if a session was returned. If not and if AutoStartSession is set to
  666. // true, Create a new session
  667. if (FAutoStartSession and VContinueProcessing) and (result = nil) then
  668. begin
  669. Result := CreateSession(AThread, AHTTPResponse, AHTTPrequest);
  670. end;
  671. end;
  672. AHTTPRequest.FSession := result;
  673. AHTTPResponse.FSession := result;
  674. end;
  675. function TIdCustomHTTPServer.ServeFile(AThread: TIdPeerThread; ResponseInfo: TIdHTTPResponseInfo;
  676. AFile: TFileName): Cardinal;
  677. begin
  678. if Length(ResponseInfo.ContentType) = 0 then begin
  679. ResponseInfo.ContentType := MIMETable.GetFileMIMEType(aFile);
  680. end;
  681. ResponseInfo.ContentLength := FileSizeByName(aFile);
  682. ResponseInfo.WriteHeader;
  683. //TODO: allow TransferFileEnabled function
  684. result := aThread.Connection.WriteFile(aFile);
  685. end;
  686. procedure TIdCustomHTTPServer.SetActive(AValue: Boolean);
  687. begin
  688. if (not (csDesigning in ComponentState)) and (FActive <> AValue)
  689. and (not (csLoading in ComponentState)) then begin
  690. if AValue then
  691. begin
  692. // starting server
  693. // set the session timeout and options
  694. if FSessionTimeOut <> 0 then
  695. FSessionList.FSessionTimeout := FSessionTimeOut
  696. else
  697. FSessionState := false;
  698. // Session events
  699. FSessionList.OnSessionStart := FOnSessionStart;
  700. FSessionList.OnSessionEnd := FOnSessionEnd;
  701. // If session handeling is enabled, create the housekeeper thread
  702. if SessionState then
  703. FSessionCleanupThread := TIdHTTPSessionCleanerThread.Create(FSessionList);
  704. end
  705. else
  706. begin
  707. // Stopping server
  708. // Boost the clear thread priority to give it a good chance to terminate
  709. if assigned(FSessionCleanupThread) then begin
  710. SetThreadPriority(FSessionCleanupThread, tpNormal);
  711. FSessionCleanupThread.TerminateAndWaitFor;
  712. FreeAndNil(FSessionCleanupThread);
  713. end;
  714. FSessionCleanupThread := nil;
  715. FSessionList.Clear;
  716. end;
  717. end;
  718. inherited;
  719. end;
  720. procedure TIdCustomHTTPServer.SetSessionState(const Value: Boolean);
  721. begin
  722. // ToDo: Add thread multiwrite protection here
  723. if (not ((csDesigning in ComponentState) or (csLoading in ComponentState))) and Active then
  724. raise EIdHTTPCannotSwitchSessionStateWhenActive.Create(RSHTTPCannotSwitchSessionStateWhenActive);
  725. FSessionState := Value;
  726. end;
  727. procedure TIdCustomHTTPServer.DoCreatePostStream(ASender: TIdPeerThread;
  728. var VPostStream: TStream);
  729. begin
  730. if Assigned(OnCreatePostStream) then begin
  731. OnCreatePostStream(ASender, VPostStream);
  732. end;
  733. end;
  734. procedure TIdCustomHTTPServer.CreatePostStream(ASender: TIdPeerThread;
  735. var VPostStream: TStream);
  736. begin
  737. DoCreatePostStream(ASender, VPostStream);
  738. end;
  739. { TIdHTTPSession }
  740. constructor TIdHTTPSession.Create(AOwner: TIdHTTPCustomSessionList);
  741. begin
  742. inherited Create;
  743. FLock := TCriticalSection.Create;
  744. FContent := TStringList.Create;
  745. FOwner := AOwner;
  746. if assigned( AOwner ) then
  747. begin
  748. if assigned(AOwner.OnSessionStart) then
  749. begin
  750. AOwner.OnSessionStart(self);
  751. end;
  752. end;
  753. end;
  754. {TIdSession}
  755. constructor TIdHTTPSession.CreateInitialized(AOwner: TIdHTTPCustomSessionList; const SessionID, RemoteIP: string);
  756. begin
  757. inherited Create;
  758. FSessionID := SessionID;
  759. FRemoteHost := RemoteIP;
  760. FLastTimeStamp := Now;
  761. FLock := TCriticalSection.Create;
  762. FContent := TStringList.Create;
  763. FOwner := AOwner;
  764. if assigned( AOwner ) then
  765. begin
  766. if assigned(AOwner.OnSessionStart) then
  767. begin
  768. AOwner.OnSessionStart(self);
  769. end;
  770. end;
  771. end;
  772. destructor TIdHTTPSession.Destroy;
  773. begin
  774. // code added here should also be reflected in
  775. // the TIdHTTPDefaultSessionList.RemoveSessionFromLockedList method
  776. // Why? It calls this function and this code gets executed?
  777. DoSessionEnd;
  778. FContent.Free;
  779. FLock.Free;
  780. if Assigned(FOwner) then begin
  781. FOwner.RemoveSession(self);
  782. end;
  783. inherited;
  784. end;
  785. procedure TIdHTTPSession.DoSessionEnd;
  786. begin
  787. if assigned(FOwner) and assigned(FOwner.FOnSessionEnd) then
  788. FOwner.FOnSessionEnd(self);
  789. end;
  790. function TIdHTTPSession.GetContent: TStrings;
  791. begin
  792. result := FContent;
  793. end;
  794. function TIdHTTPSession.IsSessionStale: boolean;
  795. begin
  796. result := TimeStampInterval(FLastTimeStamp, Now) > Integer(FOwner.SessionTimeout);
  797. end;
  798. procedure TIdHTTPSession.Lock;
  799. begin
  800. // ToDo: Add session locking code here
  801. FLock.Enter;
  802. end;
  803. procedure TIdHTTPSession.SetContent(const Value: TStrings);
  804. begin
  805. FContent.Assign(Value);
  806. end;
  807. procedure TIdHTTPSession.Unlock;
  808. begin
  809. // ToDo: Add session unlocking code here
  810. FLock.Leave;
  811. end;
  812. { TIdHTTPRequestInfo }
  813. constructor TIdHTTPRequestInfo.Create;
  814. begin
  815. inherited;
  816. FCookies := TIdServerCookies.Create(self);
  817. FParams := TStringList.Create;
  818. ContentLength := -1;
  819. end;
  820. procedure TIdHTTPRequestInfo.DecodeAndSetParams(const AValue: String);
  821. var
  822. p, p2: PChar;
  823. s: string;
  824. begin
  825. // Convert special characters
  826. // ampersand '&' separates values {Do not Localize}
  827. Params.BeginUpdate; try
  828. Params.Clear;
  829. p := PChar(AValue);
  830. p2 := p;
  831. while (p2 <> nil) and (p2[0] <> #0) do begin
  832. p2 := StrScan(p, '&'); {Do not Localize}
  833. if p2 = nil then begin
  834. p2 := StrEnd(p);
  835. end;
  836. SetString(s, p, p2 - p);
  837. // See RFC 1866 section 8.2.1. TP
  838. s := StringReplace(s, '+', ' ', [rfReplaceAll]); {do not localize}
  839. Params.Add(TIdURI.URLDecode(s));
  840. p := p2 + 1;
  841. end;
  842. finally Params.EndUpdate; end;
  843. end;
  844. destructor TIdHTTPRequestInfo.Destroy;
  845. begin
  846. FreeAndNil(FCookies);
  847. FreeAndNil(FParams);
  848. FreeAndNil(FPostStream);
  849. inherited;
  850. end;
  851. { TIdHTTPResponseInfo }
  852. procedure TIdHTTPResponseInfo.CloseSession;
  853. var
  854. i: Integer;
  855. begin
  856. i := Cookies.GetCookieIndex(0, GSessionIDCookie);
  857. if i > -1 then begin
  858. Cookies.Delete(i);
  859. end;
  860. Cookies.Add.CookieName := GSessionIDCookie;
  861. FreeAndNil(FSession);
  862. end;
  863. constructor TIdHTTPResponseInfo.Create(AConnection: TIdTCPServerConnection);
  864. begin
  865. inherited Create;
  866. FFreeContentStream := True;
  867. ContentLength := GFContentLength;
  868. {Some clients may not support folded lines}
  869. RawHeaders.FoldLines := False;
  870. FCookies := TIdServerCookies.Create(self);
  871. {TODO Specify version - add a class method dummy that calls version}
  872. ServerSoftware := GServerSoftware;
  873. ContentType := GContentType;
  874. FConnection := AConnection;
  875. ResponseNo := GResponseNo;
  876. end;
  877. destructor TIdHTTPResponseInfo.Destroy;
  878. begin
  879. FreeAndNil(FCookies);
  880. ReleaseContentStream;
  881. inherited Destroy;
  882. end;
  883. procedure TIdHTTPResponseInfo.Redirect(const AURL: string);
  884. begin
  885. ResponseNo := 302;
  886. Location := AURL;
  887. end;
  888. procedure TIdHTTPResponseInfo.ReleaseContentStream;
  889. begin
  890. if FreeContentStream then begin
  891. FreeAndNil(FContentStream);
  892. end else begin
  893. FContentStream := nil;
  894. end;
  895. end;
  896. procedure TIdHTTPResponseInfo.SetCloseConnection(const Value: Boolean);
  897. begin
  898. Connection := iif(Value, 'close', 'keep-alive'); {Do not Localize}
  899. FCloseConnection := Value;
  900. end;
  901. procedure TIdHTTPResponseInfo.SetCookies(const AValue: TIdServerCookies);
  902. begin
  903. FCookies.Assign(AValue);
  904. end;
  905. procedure TIdHTTPResponseInfo.SetHeaders;
  906. begin
  907. inherited SetHeaders;
  908. with RawHeaders do
  909. begin
  910. if Server <> '' then
  911. Values['Server'] := Server; {Do not Localize}
  912. if ContentType <> '' then
  913. Values['Content-Type'] := ContentType; {Do not Localize}
  914. if Location <> '' then
  915. begin
  916. Values['Location'] := Location; {Do not Localize}
  917. end;
  918. if ContentLength > -1 then
  919. begin
  920. Values['Content-Length'] := IntToStr(ContentLength); {Do not Localize}
  921. end;
  922. if FLastModified > 0 then
  923. begin
  924. Values['Last-Modified'] := DateTimeGMTToHttpStr(FLastModified); { do not localize}
  925. end;
  926. if AuthRealm <> '' then {Do not Localize}
  927. begin
  928. ResponseNo := 401;
  929. Values['WWW-Authenticate'] := 'Basic realm="' + AuthRealm + '"'; {Do not Localize}
  930. if ContentLength = -1 then begin
  931. FContentText := '<HTML><BODY><B>' + IntToStr(ResponseNo) + ' ' + RSHTTPUnauthorized + '</B></BODY></HTML>'; {Do not Localize}
  932. ContentLength := Length(FContentText);
  933. end;
  934. end;
  935. end;
  936. end;
  937. procedure TIdHTTPResponseInfo.SetResponseNo(const AValue: Integer);
  938. begin
  939. FResponseNo := AValue;
  940. case FResponseNo of
  941. 100: ResponseText := RSHTTPContinue;
  942. // 2XX: Success
  943. 200: ResponseText := RSHTTPOK;
  944. 201: ResponseText := RSHTTPCreated;
  945. 202: ResponseText := RSHTTPAccepted;
  946. 203: ResponseText := RSHTTPNonAuthoritativeInformation;
  947. 204: ResponseText := RSHTTPNoContent;
  948. 205: ResponseText := RSHTTPResetContent;
  949. 206: ResponseText := RSHTTPPartialContent;
  950. // 3XX: Redirections
  951. 301: ResponseText := RSHTTPMovedPermanently;
  952. 302: ResponseText := RSHTTPMovedTemporarily;
  953. 303: ResponseText := RSHTTPSeeOther;
  954. 304: ResponseText := RSHTTPNotModified;
  955. 305: ResponseText := RSHTTPUseProxy;
  956. // 4XX Client Errors
  957. 400: ResponseText := RSHTTPBadRequest;
  958. 401: ResponseText := RSHTTPUnauthorized;
  959. 403: ResponseText := RSHTTPForbidden;
  960. 404: begin
  961. ResponseText := RSHTTPNotFound;
  962. // Close connection
  963. CloseConnection := true;
  964. end;
  965. 405: ResponseText := RSHTTPMethodeNotAllowed;
  966. 406: ResponseText := RSHTTPNotAcceptable;
  967. 407: ResponseText := RSHTTPProxyAuthenticationRequired;
  968. 408: ResponseText := RSHTTPRequestTimeout;
  969. 409: ResponseText := RSHTTPConflict;
  970. 410: ResponseText := RSHTTPGone;
  971. 411: ResponseText := RSHTTPLengthRequired;
  972. 412: ResponseText := RSHTTPPreconditionFailed;
  973. 413: ResponseText := RSHTTPRequestEntityToLong;
  974. 414: ResponseText := RSHTTPRequestURITooLong;
  975. 415: ResponseText := RSHTTPUnsupportedMediaType;
  976. // 5XX Server errors
  977. 500: ResponseText := RSHTTPInternalServerError;
  978. 501: ResponseText := RSHTTPNotImplemented;
  979. 502: ResponseText := RSHTTPBadGateway;
  980. 503: ResponseText := RSHTTPServiceUnavailable;
  981. 504: ResponseText := RSHTTPGatewayTimeout;
  982. 505: ResponseText := RSHTTPHTTPVersionNotSupported;
  983. else
  984. ResponseText := RSHTTPUnknownResponseCode;
  985. end;
  986. {if ResponseNo >= 400 then
  987. // Force COnnection closing when there is error during the request processing
  988. CloseConnection := true;
  989. end;}
  990. end;
  991. procedure TIdHTTPResponseInfo.WriteContent;
  992. begin
  993. if not HeaderHasBeenWritten then begin
  994. WriteHeader;
  995. end;
  996. with FConnection do begin
  997. // Always check ContentText first
  998. if ContentText <> '' then begin
  999. Write(ContentText);
  1000. end else if Assigned(ContentStream) then begin
  1001. WriteStream(ContentStream);
  1002. end else begin
  1003. FConnection.WriteLn('<HTML><BODY><B>' + IntToStr(ResponseNo) + ' ' + ResponseText {Do not Localize}
  1004. + '</B></BODY></HTML>'); {Do not Localize}
  1005. end;
  1006. // Clear All - This signifies that WriteConent has been called.
  1007. ContentText := ''; {Do not Localize}
  1008. ReleaseContentStream;
  1009. end;
  1010. end;
  1011. procedure TIdHTTPResponseInfo.WriteHeader;
  1012. var
  1013. i: Integer;
  1014. begin
  1015. if HeaderHasBeenWritten then begin
  1016. raise EIdHTTPHeaderAlreadyWritten.Create(RSHTTPHeaderAlreadyWritten);
  1017. end;
  1018. FHeaderHasBeenWritten := True;
  1019. if ContentLength = -1 then
  1020. begin
  1021. // Always check ContentText first
  1022. if Length(ContentText) > 0 then begin
  1023. ContentLength := Length(ContentText)
  1024. end else if Assigned(ContentStream) then begin
  1025. ContentLength := ContentStream.Size;
  1026. end;
  1027. end;
  1028. SetHeaders;
  1029. with FConnection do
  1030. begin
  1031. OpenWriteBuffer; try
  1032. // Write HTTP status response
  1033. // Client will be forced to close the connection. We are not going to support
  1034. // keep-alive feature for now
  1035. WriteLn('HTTP/1.1 ' + IntToStr(ResponseNo) + ' ' + ResponseText); {Do not Localize}
  1036. // Write headers
  1037. for i := 0 to RawHeaders.Count -1 do begin
  1038. WriteLn(RawHeaders[i]);
  1039. end;
  1040. // Write cookies
  1041. for i := 0 to Cookies.Count - 1 do begin
  1042. WriteLn('Set-Cookie: ' + Cookies[i].ServerCookie); {Do not Localize}
  1043. end;
  1044. // HTTP headers ends with a double CR+LF
  1045. WriteLn;
  1046. finally CloseWriteBuffer; end;
  1047. end;
  1048. end;
  1049. { TIdHTTPDefaultSessionList }
  1050. procedure TIdHTTPDefaultSessionList.Add(ASession: TIdHTTPSession);
  1051. begin
  1052. SessionList.Add(ASession);
  1053. end;
  1054. procedure TIdHTTPDefaultSessionList.Clear;
  1055. var
  1056. ASessionList: TList;
  1057. i: Integer;
  1058. begin
  1059. ASessionList := SessionList.LockList;
  1060. try
  1061. for i := ASessionList.Count - 1 DownTo 0 do
  1062. if ASessionList[i] <> nil then
  1063. begin
  1064. TIdHTTPSession(ASessionList[i]).DoSessionEnd;
  1065. TIdHTTPSession(ASessionList[i]).FOwner := nil;
  1066. TIdHTTPSession(ASessionList[i]).Free;
  1067. end;
  1068. ASessionList.Clear;
  1069. ASessionList.Capacity := SessionCapacity;
  1070. finally
  1071. SessionList.UnlockList;
  1072. end;
  1073. end;
  1074. constructor TIdHTTPDefaultSessionList.Create(AOwner: TComponent);
  1075. begin
  1076. inherited;
  1077. SessionList := TThreadList.Create;
  1078. SessionList.LockList.Capacity := SessionCapacity;
  1079. SessionList.UnlockList;
  1080. end;
  1081. function TIdHTTPDefaultSessionList.CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession;
  1082. begin
  1083. result := TIdHTTPSession.CreateInitialized(Self, SessionID, RemoteIP);
  1084. SessionList.Add(result);
  1085. end;
  1086. function TIdHTTPDefaultSessionList.CreateUniqueSession(
  1087. const RemoteIP: String): TIdHTTPSession;
  1088. var
  1089. SessionID: String;
  1090. begin
  1091. SessionID := GetRandomString(15);
  1092. while GetSession(SessionID, RemoteIP) <> nil do
  1093. begin
  1094. SessionID := GetRandomString(15);
  1095. end; // while
  1096. result := CreateSession(RemoteIP, SessionID);
  1097. end;
  1098. destructor TIdHTTPDefaultSessionList.destroy;
  1099. begin
  1100. Clear;
  1101. SessionList.free;
  1102. inherited;
  1103. end;
  1104. function TIdHTTPDefaultSessionList.GetSession(const SessionID, RemoteIP: string): TIdHTTPSession;
  1105. var
  1106. ASessionList: TList;
  1107. i: Integer;
  1108. ASession: TIdHTTPSession;
  1109. begin
  1110. Result := nil;
  1111. ASessionList := SessionList.LockList;
  1112. try
  1113. // get current time stamp
  1114. for i := 0 to ASessionList.Count - 1 do
  1115. begin
  1116. ASession := TIdHTTPSession(ASessionList[i]);
  1117. Assert(ASession <> nil);
  1118. // the stale sessions check has been removed... the cleanup thread should suffice plenty
  1119. if AnsiSameText(ASession.FSessionID, SessionID) and ((length(RemoteIP) = 0) or AnsiSameText(ASession.RemoteHost, RemoteIP)) then
  1120. begin
  1121. // Session found
  1122. ASession.FLastTimeStamp := Now;
  1123. result := ASession;
  1124. break;
  1125. end;
  1126. end;
  1127. finally
  1128. SessionList.UnlockList;
  1129. end;
  1130. end;
  1131. procedure TIdHTTPDefaultSessionList.PurgeStaleSessions(PurgeAll: Boolean = false);
  1132. var
  1133. i: Integer;
  1134. aSessionList: TList;
  1135. begin
  1136. // S.G. 24/11/00: Added a way to force a session purge (Used when thread is terminated)
  1137. // Get necessary data
  1138. aSessionList := SessionList.LockList;
  1139. try
  1140. // Loop though the sessions.
  1141. for i := aSessionList.Count - 1 downto 0 do
  1142. begin
  1143. // Identify the stale sessions
  1144. if Assigned(ASessionList[i]) and
  1145. (PurgeAll or TIdHTTPSession(aSessionList[i]).IsSessionStale) then
  1146. begin
  1147. RemoveSessionFromLockedList(i, aSessionList);
  1148. end;
  1149. end;
  1150. finally
  1151. SessionList.UnlockList;
  1152. end;
  1153. end;
  1154. procedure TIdHTTPDefaultSessionList.RemoveSession(Session: TIdHTTPSession);
  1155. var
  1156. ASessionList: TList;
  1157. Index: integer;
  1158. begin
  1159. ASessionList := SessionList.LockList;
  1160. try
  1161. Index := ASessionList.IndexOf(TObject(Session));
  1162. if index > -1 then
  1163. begin
  1164. ASessionList.Delete(index);
  1165. end;
  1166. finally
  1167. SessionList.UnlockList;
  1168. end;
  1169. end;
  1170. procedure TIdHTTPDefaultSessionList.RemoveSessionFromLockedList(AIndex: Integer;
  1171. ALockedSessionList: TList);
  1172. begin
  1173. TIdHTTPSession(ALockedSessionList[AIndex]).DoSessionEnd;
  1174. // must set the owner to nil or the session will try to remove itself from the
  1175. // session list and deadlock
  1176. TIdHTTPSession(ALockedSessionList[AIndex]).FOwner := nil;
  1177. TIdHTTPSession(ALockedSessionList[AIndex]).Free;
  1178. ALockedSessionList.Delete(AIndex);
  1179. end;
  1180. { TIdHTTPSessionClearThread }
  1181. procedure TIdHTTPSessionCleanerThread.AfterRun;
  1182. begin
  1183. if Assigned(FSessionList) then
  1184. FSessionList.PurgeStaleSessions(true);
  1185. inherited AfterRun;
  1186. end;
  1187. constructor TIdHTTPSessionCleanerThread.Create(SessionList: TIdHTTPCustomSessionList);
  1188. begin
  1189. inherited Create(false);
  1190. SetThreadPriority(Self, tpIdle); // Set priority to the lowest possible
  1191. FSessionList := SessionList;
  1192. FreeOnTerminate := False;
  1193. end;
  1194. procedure TIdHTTPSessionCleanerThread.Run;
  1195. begin
  1196. Sleep(1000);
  1197. if Assigned(FSessionList) then begin
  1198. FSessionList.PurgeStaleSessions(Terminated);
  1199. end;
  1200. end;
  1201. end.