httpsvlt.pp 19 KB

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