httpsvlt.pp 19 KB

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