httpsvlt.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. {
  2. HTTP Servlet Classes
  3. Copyright (c) 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  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. unit HTTPSvlt;
  12. interface
  13. uses SysUtils, Classes, fpAsync, fpSock, HTTPBase, Servlets;
  14. resourcestring
  15. SErrUnknownMethod = 'Unknown HTTP method "%s" used';
  16. SErrUnsupportedMethod = 'HTTP method "%s" is not supported for this URL';
  17. type
  18. THttpSession = class
  19. public
  20. property Attributes[const AName: String]: TObject; // !!!: Implement this rw
  21. property CreationTime: TDateTime; // !!!: Implement this
  22. property ID: String; // !!!: Implement this
  23. property LastAccessedTime: TDateTime; // !!!: Implement this
  24. property MaxInactiveInterval: TDateTime; // !!!: Implement this rw
  25. property ServletContext: TServletContext; // !!!: Implement this
  26. property IsNew: Boolean; // !!!: Implement this
  27. // procedure Invalidate; // !!!: Implement this
  28. // procedure RemoveAttribute(const AName: String); // !!!: Implement this
  29. end;
  30. THttpServletRequest = class(TServletRequest)
  31. private
  32. RequestHeader: THTTPRequestHeader;
  33. protected
  34. function GetContentLength: Integer; override;
  35. function GetContentType: String; override;
  36. function GetProtocol: String; override;
  37. function GetMethod: String;
  38. function GetRequestURI: String;
  39. function GetQueryString: String;
  40. public
  41. constructor Create(ARequestHeader: THTTPRequestHeader; AInputStream: TStream;
  42. const AScheme, APathInfo: String);
  43. // GetSession
  44. // function IsRequestedSessionIdFromCookie: Boolean; // !!!: Implement this
  45. // function IsRequestedSessionIdFromURL: Boolean; // !!!: Implement this
  46. // function IsRequestedSessionIdValid: Boolean; // !!!: Implement this
  47. property AuthType: String; // !!!: How to implement?
  48. property ContextPath: String; // !!!: How to implement?
  49. property CookieCount: Integer; // !!!: How to implement?
  50. property Cookies[Index: Integer]: Pointer; // !!!: How to implement?
  51. property DateHeaders[const AName: String]: TDateTime; // !!!: Implement this
  52. property Headers[const AName: String]: String; // !!!: Implement this
  53. property IntHeaders[const AName: String]: Integer; // !!!: Implement this
  54. property Method: String read GetMethod;
  55. property PathInfo: String read FPathInfo;
  56. property PathTranslated: String; // !!!: How to implement?
  57. property QueryString: String read GetQueryString;
  58. property RemoteUser: String; // !!!: How to implement?
  59. property RequestedSessionID: String; // !!!: How to implement?
  60. property RequestURI: String read GetRequestURI;
  61. property RequestURL: String; // !!!: How to implement?
  62. property ServletPath: String; // !!!: How to implement?
  63. end;
  64. THttpServletResponse = class(TServletResponse)
  65. private
  66. ResponseHeader: THTTPResponseHeader;
  67. protected
  68. procedure SetContentType(const Value: String); override;
  69. procedure SetContentLength(Value: Int64); override;
  70. public
  71. constructor Create(AResponseHeader: THTTPResponseHeader;
  72. AOutputStream: TStream);
  73. // procedure AddCookie(Cookie: TCookie); // !!!: Implement this
  74. // procedure AddDateHeader(const AName: String; ADate: TDateTime); // !!!: Implement this
  75. // procedure AddHeader(const AName, AValue: String); // !!!: Implement this
  76. // procedure AddIntHeader(const AName: String; AValue: Int64); // !!!: Implement this
  77. // function ContainsHeader(const AName: String): Boolean; // !!!: Implement this
  78. // function EncodeRedirectURL(const URL: String): String; // !!!: Implement this
  79. // function EncodeURL(const URL: String): String; // !!!: Implement this
  80. // procedure SendError(StatusCode: Integer); // !!!: Implement this
  81. // procedure SendError(StatusCode: Integer; const Msg: String); // !!!: Implement this
  82. // procedure SendRedirect(const Location: String); // !!!: Implement this
  83. // procedure SetDateHeader(const AName: String; ADate: TDateTime); // !!!: Implement this
  84. // procedure SetHeader(const AName, AValue: String); // !!!: Implement this
  85. // procedure SetIntHeader(const AName: String; AValue: Int64); // !!!: Implement this
  86. // procedure SetStatus(StatusCode: Integer); // !!!: Implement this
  87. // procedure SetStatus(StatusCode: Integer; const Msg: String); // !!!: Implement this
  88. end;
  89. THttpServlet = class(TGenericServlet)
  90. protected
  91. // function GetLastModified(Req: THttpServletRequest): TDateTime;
  92. // Handlers for HTTP methods
  93. procedure DoDelete(Req: THttpServletRequest; Resp: THttpServletResponse);
  94. virtual; abstract;
  95. procedure DoGet(Req: THttpServletRequest; Resp: THttpServletResponse);
  96. virtual; abstract;
  97. procedure DoHead(Req: THttpServletRequest; Resp: THttpServletResponse);
  98. virtual; abstract;
  99. procedure DoOptions(Req: THttpServletRequest; Resp: THttpServletResponse);
  100. virtual; abstract;
  101. procedure DoPost(Req: THttpServletRequest; Resp: THttpServletResponse);
  102. virtual; abstract;
  103. procedure DoPut(Req: THttpServletRequest; Resp: THttpServletResponse);
  104. virtual; abstract;
  105. procedure DoTrace(Req: THttpServletRequest; Resp: THttpServletResponse);
  106. virtual; abstract;
  107. procedure Service(Req: THttpServletRequest; Resp: THttpServletResponse); virtual;
  108. end;
  109. // A simple file retreiving servlet
  110. TCustomFileServlet = class(THttpServlet)
  111. private
  112. FPath: String;
  113. protected
  114. procedure DoGet(Req: THttpServletRequest; Resp: THttpServletResponse); override;
  115. property Path: String read FPath write FPath;
  116. end;
  117. TFileServlet = class(TCustomFileServlet)
  118. published
  119. property Path;
  120. end;
  121. // HTTP server (servlet container)
  122. TServletMapping = class(TCollectionItem)
  123. private
  124. FServlet: TGenericServlet;
  125. FURLPattern: String;
  126. published
  127. property Servlet: TGenericServlet read FServlet write FServlet;
  128. property URLPattern: String read FURLPattern write FURLPattern;
  129. end;
  130. TServletMappings = class(TCollection)
  131. private
  132. function GetItem(Index: Integer): TServletMapping;
  133. procedure SetItem(Index: Integer; Value: TServletMapping);
  134. public
  135. property Items[Index: Integer]: TServletMapping read GetItem write SetItem;
  136. default;
  137. end;
  138. THttpServer = class(TCustomTCPServer)
  139. private
  140. Connections: TList; // List of THttpServerConnection objects
  141. FServletMappings: TServletMappings;
  142. protected
  143. procedure DoConnect(AStream: TSocketStream); override;
  144. public
  145. constructor Create(AOwner: TComponent); override;
  146. destructor Destroy; override;
  147. procedure AddServlet(AServlet: THttpServlet; const AURLPattern: String);
  148. // procedure RemoveServlet(const APathName: String);
  149. published
  150. property Active;
  151. property Port;
  152. property OnQueryConnect;
  153. property OnConnect;
  154. property ServletMappings: TServletMappings
  155. read FServletMappings write FServletMappings;
  156. end;
  157. { No, this one really doesn't belong to here - but as soon as we don't have a
  158. nice solution for platform-independent component streaming in the FCL classes
  159. unit, it will be left here. }
  160. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  161. implementation
  162. constructor THttpServletRequest.Create(ARequestHeader: THTTPRequestHeader;
  163. AInputStream: TStream; const AScheme, APathInfo: String);
  164. begin
  165. inherited Create(AInputStream, AScheme, APathInfo);
  166. RequestHeader := ARequestHeader;
  167. end;
  168. function THttpServletRequest.GetContentLength: Integer;
  169. begin
  170. Result := RequestHeader.ContentLength;
  171. end;
  172. function THttpServletRequest.GetContentType: String;
  173. begin
  174. Result := RequestHeader.ContentType;
  175. end;
  176. function THttpServletRequest.GetProtocol: String;
  177. begin
  178. Result := 'HTTP/' + RequestHeader.HttpVersion;
  179. end;
  180. function THttpServletRequest.GetMethod: String;
  181. begin
  182. Result := RequestHeader.Command;
  183. end;
  184. function THttpServletRequest.GetRequestURI: String;
  185. begin
  186. Result := RequestHeader.URI;
  187. end;
  188. function THttpServletRequest.GetQueryString: String;
  189. begin
  190. Result := RequestHeader.QueryString;
  191. end;
  192. constructor THttpServletResponse.Create(AResponseHeader: THTTPResponseHeader;
  193. AOutputStream: TStream);
  194. begin
  195. inherited Create(AOutputStream);
  196. ResponseHeader := AResponseHeader;
  197. end;
  198. procedure THttpServletResponse.SetContentType(const Value: String);
  199. begin
  200. ResponseHeader.ContentType := Value;
  201. end;
  202. procedure THttpServletResponse.SetContentLength(Value: Int64);
  203. begin
  204. ResponseHeader.ContentLength := Value;
  205. end;
  206. procedure THttpServlet.Service(Req: THttpServletRequest; Resp: THttpServletResponse);
  207. var
  208. Method: String;
  209. begin
  210. Method := Req.Method;
  211. try
  212. if Method = 'DELETE' then
  213. DoDelete(Req, Resp)
  214. else if Method = 'GET' then
  215. DoGet(Req, Resp)
  216. else if Method = 'HEAD' then
  217. DoHead(Req, Resp)
  218. else if Method = 'OPTIONS' then
  219. DoOptions(Req, Resp)
  220. else if Method = 'POST' then
  221. DoPost(Req, Resp)
  222. else if Method = 'PUT' then
  223. DoPut(Req, Resp)
  224. else if Method = 'TRACE' then
  225. DoTrace(Req, Resp)
  226. else
  227. raise EServlet.CreateFmt(SErrUnknownMethod, [Method]);
  228. except
  229. on e: EAbstractError do
  230. raise EServlet.CreateFmt(SErrUnsupportedMethod, [Method]);
  231. end;
  232. end;
  233. procedure TCustomFileServlet.DoGet(Req: THttpServletRequest;
  234. Resp: THttpServletResponse);
  235. var
  236. f: TStream;
  237. s: String;
  238. i, LastStart: Integer;
  239. begin
  240. s := Req.PathInfo;
  241. i := 1;
  242. LastStart := 1;
  243. while i <= Length(s) do
  244. begin
  245. if (s[i] = '/') or (s[i] = '\') then
  246. LastStart := i + 1
  247. else if (i = LastStart) and (s[i] = '.') and (i < Length(s)) and
  248. (s[i + 1] = '..') then
  249. exit; // !!!: are ".." allowed in URLs?
  250. Inc(i);
  251. end;
  252. if s = '' then
  253. s := 'index.html';
  254. f := TFileStream.Create(Path + '/' + s, fmOpenRead);
  255. try
  256. Resp.OutputStream.CopyFrom(f, f.Size);
  257. finally
  258. f.Free;
  259. end;
  260. end;
  261. // HTTP Server
  262. function TServletMappings.GetItem(Index: Integer): TServletMapping;
  263. begin
  264. Result := TServletMapping(inherited GetItem(Index));
  265. end;
  266. procedure TServletMappings.SetItem(Index: Integer; Value: TServletMapping);
  267. begin
  268. inherited SetItem(Index, Value);
  269. end;
  270. type
  271. THttpServerConnection = class
  272. private
  273. Server: THttpServer;
  274. Stream: TSocketStream;
  275. RequestHeader: THttpRequestHeader;
  276. RequestStream: TMemoryStream;
  277. ResponseHeader: THttpResponseHeader;
  278. ResponseStream: TMemoryStream;
  279. BytesToRead, BytesToWrite: Integer;
  280. DataAvailableNotifyHandle: Pointer;
  281. CanSendNotifyHandle: Pointer;
  282. SendBuffer: Pointer;
  283. procedure RequestHeaderReceived(Sender: TObject);
  284. procedure DataAvailable(Sender: TObject);
  285. procedure RequestStreamReceived;
  286. procedure ResponseHeaderSent(Sender: TObject);
  287. procedure CanSend(Sender: TObject);
  288. public
  289. constructor Create(AServer: THttpServer; AStream: TSocketStream);
  290. destructor Destroy; override;
  291. end;
  292. constructor THttpServerConnection.Create(AServer: THttpServer;
  293. AStream: TSocketStream);
  294. begin
  295. inherited Create;
  296. Server := AServer;
  297. Stream := AStream;
  298. RequestHeader := THttpRequestHeader.Create;
  299. RequestHeader.OnCompleted := @RequestHeaderReceived;
  300. RequestHeader.AsyncReceive(Server.EventLoop, Stream);
  301. end;
  302. destructor THttpServerConnection.Destroy;
  303. begin
  304. if Assigned(DataAvailableNotifyHandle) then
  305. Server.EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  306. if Assigned(CanSendNotifyHandle) then
  307. Server.EventLoop.ClearCanWriteNotify(CanSendNotifyHandle);
  308. RequestHeader.Free;
  309. RequestStream.Free;
  310. ResponseHeader.Free;
  311. ResponseStream.Free;
  312. Stream.Free;
  313. Server.Connections.Remove(Self);
  314. inherited Destroy;
  315. end;
  316. procedure THttpServerConnection.RequestHeaderReceived(Sender: TObject);
  317. var
  318. BytesInBuffer: Integer;
  319. NeedMoreData: Boolean;
  320. begin
  321. // WriteLn('HTTP-Header empfangen');
  322. BytesInBuffer:= RequestHeader.Reader.BytesInBuffer;
  323. BytesToRead := RequestHeader.ContentLength;
  324. // WriteLn('Content-Length: ', BytesToRead, ', noch im Puffer: ', BytesInBuffer);
  325. RequestStream := TMemoryStream.Create;
  326. NeedMoreData := RequestHeader.Command = 'POST';
  327. if BytesInBuffer > 0 then
  328. begin
  329. RequestStream.Write(RequestHeader.Reader.Buffer^, BytesInBuffer);
  330. if BytesToRead > 0 then
  331. Dec(BytesToRead, BytesInBuffer);
  332. if BytesInBuffer = RequestHeader.ContentLength then
  333. NeedMoreData := False;
  334. end;
  335. if NeedMoreData then
  336. DataAvailableNotifyHandle := Server.EventLoop.SetDataAvailableNotify(
  337. Stream.Handle, @DataAvailable, nil)
  338. else
  339. RequestStreamReceived;
  340. end;
  341. procedure THttpServerConnection.DataAvailable(Sender: TObject);
  342. var
  343. Buffer: array[0..4095] of Byte;
  344. ReadNow, BytesRead: Integer;
  345. begin
  346. ReadNow := SizeOf(Buffer);
  347. if (BytesToRead > 0) and (ReadNow > BytesToRead) then
  348. ReadNow := BytesToRead;
  349. BytesRead := Stream.Read(Buffer, ReadNow);
  350. // WriteLn('Sollte ', ReadNow, ' Bytes lesen, ', BytesRead, ' wurden gelesen');
  351. RequestStream.Write(Buffer, BytesRead);
  352. if BytesToRead > 0 then
  353. begin
  354. Dec(BytesToRead, BytesRead);
  355. if BytesToRead = 0 then
  356. begin
  357. Server.EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  358. DataAvailableNotifyHandle := nil;
  359. RequestStreamReceived;
  360. end;
  361. end;
  362. end;
  363. procedure THttpServerConnection.RequestStreamReceived;
  364. var
  365. i: Integer;
  366. s, URI: String;
  367. Servlet: TGenericServlet;
  368. Request: THttpServletRequest;
  369. Response: THttpServletResponse;
  370. begin
  371. // WriteLn('Stream received: ', RequestStream.Size, ' bytes');
  372. URI := UpperCase(RequestHeader.URI);
  373. for i := 0 to Server.ServletMappings.Count - 1 do
  374. begin
  375. s := UpperCase(Server.ServletMappings[i].URLPattern);
  376. if ((s[Length(s)] = '*') and (Copy(s, 1, Length(s) - 1) =
  377. Copy(URI, 1, Length(s) - 1))) or (s = URI) then
  378. break;
  379. end;
  380. if i < Server.ServletMappings.Count then
  381. Servlet := Server.ServletMappings[i].Servlet
  382. else
  383. Servlet := nil;
  384. if RequestHeader.ContentLength = 0 then
  385. RequestHeader.ContentLength := RequestStream.Size;
  386. RequestStream.Position := 0;
  387. if s[Length(s)] = '*' then
  388. s := Copy(s, 1, Length(s) - 1);
  389. Request := THttpServletRequest.Create(RequestHeader, RequestStream, 'http',
  390. Copy(RequestHeader.URI, Length(s) + 1, Length(RequestHeader.URI)));
  391. ResponseHeader := THTTPResponseHeader.Create;
  392. ResponseHeader.Connection := 'Keep-Alive';
  393. ResponseStream := TMemoryStream.Create;
  394. Response := THttpServletResponse.Create(ResponseHeader, ResponseStream);
  395. try
  396. try
  397. if Assigned(Servlet) then
  398. if Servlet.InheritsFrom(THttpServlet) then
  399. THttpServlet(Servlet).Service(Request, Response)
  400. else
  401. Servlet.Service(Request, Response)
  402. else
  403. begin
  404. ResponseHeader.ContentType := 'text/plain';
  405. s := 'Invalid URL';
  406. ResponseStream.Write(s[1], Length(s));
  407. end;
  408. except
  409. on e: Exception do
  410. begin
  411. s := 'An error occured: ' + ' ' + e.Message;
  412. ResponseHeader.ContentType := 'text/plain';
  413. ResponseStream.Write(s[1], Length(s));
  414. end;
  415. end;
  416. BytesToWrite := ResponseStream.Size;
  417. SendBuffer := ResponseStream.Memory;
  418. ResponseStream.Position := 0;
  419. ResponseHeader.ContentLength := BytesToWrite;
  420. ResponseHeader.OnCompleted := @ResponseHeaderSent;
  421. ResponseHeader.AsyncSend(Server.EventLoop, Stream);
  422. finally
  423. Response.Free;
  424. Request.Free;
  425. end;
  426. // WriteLn('Antwort wurde generiert');
  427. end;
  428. procedure THttpServerConnection.ResponseHeaderSent(Sender: TObject);
  429. begin
  430. // WriteLn('Antwortheader geschickt');
  431. if BytesToWrite > 0 then
  432. CanSendNotifyHandle := Server.EventLoop.SetCanWriteNotify(Stream.Handle,
  433. @CanSend, nil);
  434. end;
  435. procedure THttpServerConnection.CanSend(Sender: TObject);
  436. var
  437. BytesWritten: Integer;
  438. begin
  439. BytesWritten := Stream.Write(SendBuffer^, BytesToWrite);
  440. Dec(BytesToWrite, BytesWritten);
  441. Inc(SendBuffer, BytesWritten);
  442. if BytesToWrite = 0 then
  443. begin
  444. // WriteLn('Antwortdaten geschickt');
  445. Free;
  446. end;
  447. end;
  448. constructor THttpServer.Create(AOwner: TComponent);
  449. begin
  450. inherited Create(AOwner);
  451. ServletMappings := TServletMappings.Create(TServletMapping);
  452. end;
  453. destructor THttpServer.Destroy;
  454. var
  455. i: Integer;
  456. begin
  457. ServletMappings.Free;
  458. if Assigned(Connections) then
  459. begin
  460. for i := 0 to Connections.Count - 1 do
  461. THttpServerConnection(Connections[i]).Free;
  462. Connections.Free;
  463. end;
  464. inherited Destroy;
  465. end;
  466. procedure THttpServer.AddServlet(AServlet: THttpServlet;
  467. const AURLPattern: String);
  468. var
  469. Mapping: TServletMapping;
  470. begin
  471. Mapping := TServletMapping(ServletMappings.Add);
  472. Mapping.Servlet := AServlet;
  473. Mapping.URLPattern := AURLPattern;
  474. end;
  475. {procedure THttpServer.RemoveServlet(const APathName: String);
  476. var
  477. i: Integer;
  478. begin
  479. for i := 0 to Servlets.Count - 1 do
  480. if TServletInfo(Servlets[i]).PathName = APathName then
  481. begin
  482. TServletInfo(Servlets[i]).Free;
  483. Servlets.Delete(i);
  484. break;
  485. end;
  486. end;}
  487. procedure THttpServer.DoConnect(AStream: TSocketStream);
  488. begin
  489. // WriteLn('Incoming HTTP connection');
  490. if not Assigned(Connections) then
  491. Connections := TList.Create;
  492. Connections.Add(THttpServerConnection.Create(Self, AStream));
  493. end;
  494. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  495. function DoInitClass(ClassType: TClass): Boolean;
  496. var
  497. Filename: String;
  498. TextStream, BinStream: TStream;
  499. begin
  500. Result := False;
  501. if (ClassType <> TComponent) and (ClassType <> RootAncestor) then
  502. begin
  503. { Init the parent class first }
  504. Result := DoInitClass(ClassType.ClassParent);
  505. Filename := LowerCase(Copy(ClassType.ClassName, 2, 255)) + '.frm';
  506. TextStream := nil;
  507. BinStream := nil;
  508. try
  509. try
  510. TextStream := TFileStream.Create(Filename, fmOpenRead);
  511. except
  512. exit;
  513. end;
  514. BinStream := TMemoryStream.Create;
  515. ObjectTextToBinary(TextStream, BinStream);
  516. BinStream.Position := 0;
  517. BinStream.ReadComponent(Instance);
  518. Result := True;
  519. finally
  520. TextStream.Free;
  521. BinStream.Free;
  522. end;
  523. end;
  524. end;
  525. begin
  526. {!!!: GlobalNameSpace.BeginWrite;
  527. try}
  528. if (Instance.ComponentState * [csLoading, csInline]) = [] then
  529. begin
  530. BeginGlobalLoading;
  531. try
  532. Result := DoInitClass(Instance.ClassType);
  533. NotifyGlobalLoading;
  534. finally
  535. EndGlobalLoading;
  536. end;
  537. end else
  538. Result := DoInitClass(Instance.ClassType);
  539. {finally
  540. GlobalNameSpace.EndWrite;
  541. end;}
  542. end;
  543. end.