source: trunk/Demo/Packages/CoolWeb/WebServer/UHTTPServer.pas

Last change on this file was 60, checked in by chronos, 12 years ago
File size: 12.7 KB
Line 
1unit UHTTPServer;
2
3{$mode Delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, UTCPServer, UCommon, UMemoryStreamEx, UMIMEType,
9 Synautil, SpecializedList, SpecializedDictionary;
10
11type
12 THTTPServer = class;
13 EEmptyHTTPHandler = class(Exception);
14
15 TQueryParameterList = class(TStringList)
16 procedure Parse(Text: string);
17 function Syntetize: string;
18 end;
19
20 { TCookieList }
21
22 TCookieList = class(TStringList)
23 procedure Parse(Text: string);
24 function Syntetize: string;
25 end;
26
27 { THTTPRequest }
28
29 THTTPRequest = class
30 ContentType: string;
31 Content: TMemoryStreamEx;
32 Query: TQueryParameterList;
33 Path: TListString;
34 Method: string;
35 Headers: TDictionaryStringString;
36 Cookies: TCookieList;
37 Post: TQueryParameterList;
38 procedure Assign(Source: THTTPRequest);
39 procedure Clear;
40 constructor Create;
41 destructor Destroy; override;
42 end;
43
44 { THTTPResponse }
45
46 THTTPResponse = class
47 ContentType: string;
48 Content: TMemoryStreamEx;
49 Headers: TDictionaryStringString;
50 Cookies: TCookieList;
51 procedure Assign(Source: THTTPResponse);
52 procedure Clear;
53 constructor Create;
54 destructor Destroy; override;
55 end;
56
57 { THTTPHandlerData }
58
59 THTTPHandlerData = class
60 Server: THTTPServer;
61 Request: THTTPRequest;
62 Response: THTTPResponse;
63 SessionId: string;
64 Session: TStringList;
65 procedure Assign(Source: THTTPHandlerData);
66 constructor Create; virtual;
67 destructor Destroy; override;
68 end;
69
70 TRequestEvent = procedure(HandlerData: THTTPHandlerData) of object;
71
72 TRequestHandler = class
73 Name: string;
74 Handler: TRequestEvent;
75 end;
76
77 { TRequestHandlerList }
78
79 TRequestHandlerList = class(TListObject)
80 procedure Add(AName: string; AHandler: TRequestEvent);
81 function IndexOfName(AName: string): TRequestHandler;
82 end;
83
84 { THTTPSessionStorage }
85
86 THTTPSessionStorage = class(TComponent)
87 public
88 procedure Load(HandlerData: THTTPHandlerData); virtual;
89 procedure Save(HandlerData: THTTPHandlerData); virtual;
90 constructor Create(AOwner: TComponent); override;
91 destructor Destroy; override;
92 end;
93
94 { THTTPServer }
95
96 THTTPServer = class(TComponent)
97 private
98 FDocumentRoot: string;
99 FOnRequest: TRequestEvent;
100 FSessionStorage: THTTPSessionStorage;
101 FShowExceptions: Boolean;
102 procedure SetShowExceptions(const AValue: Boolean);
103 public
104 procedure Run; virtual;
105 procedure ErrorResponse(HandlerData: THTTPHandlerData);
106 procedure FileResponse(HandlerData: THTTPHandlerData);
107 procedure ServerInfo(HandlerData: THTTPHandlerData); virtual;
108 constructor Create(AOwner: TComponent); override;
109 destructor Destroy; override;
110 published
111 property ShowExceptions: Boolean read FShowExceptions write SetShowExceptions;
112 property DocumentRoot: string read FDocumentRoot write FDocumentRoot;
113 property SessionStorage: THTTPSessionStorage read FSessionStorage
114 write FSessionStorage;
115 property OnRequest: TRequestEvent read FOnRequest write FOnRequest;
116 end;
117
118procedure HTTPExceptionShow(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
119procedure HTTPExceptionHide(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
120
121
122resourcestring
123 SEmptyHTTPHandler = 'No handler defined for HTTP server.';
124 SFileNotFound = 'File %s not found.';
125 SPageNotFound = 'Page %s not found.';
126
127implementation
128
129{ THTTPServer }
130
131procedure THTTPServer.ServerInfo(HandlerData: THTTPHandlerData);
132var
133 I: Integer;
134begin
135 with HandlerData, Response.Content do begin
136 //Response.Cookies.Values['Test'] := 'Halo';
137 //Response.Cookies.Values['Test2'] := 'Halo2';
138
139 //HTTPServer.SessionHandler.Variables.Values['Session1'] := 'Value1';
140 //HTTPServer.SessionHandler.Variables.Values['Session2'] := 'Value2';
141
142 WriteString('<a href="?ServerInfo">Refresh</a>');
143
144 WriteString('<h5>Request HTTP content:</h5>');
145 WriteStream(Request.Content, Request.Content.Size);
146
147 WriteString('<h5>Request HTTP headers</h5>');
148 for I := 0 to Request.Headers.Count - 1 do begin;
149 WriteString(Request.Headers.Items[I].Value + '<br/>');
150 end;
151
152 WriteString('<h5>Request HTTP cookies</h5>');
153 for I := 0 to Request.Cookies.Count - 1 do begin;
154 WriteString(Request.Cookies.Strings[I] + '<br/>');
155 end;
156
157 //WriteString('Session id: ' + SessionId);
158 WriteString('<h5>Session variables</h5>');
159 for I := 0 to Session.Count - 1 do begin;
160 WriteString(Session.Strings[I] + '<br/>');
161 end;
162
163 WriteString('<h5>Request HTTP POST</h5>');
164 for I := 0 to Request.Post.Count - 1 do begin;
165 WriteString(Request.Post.Strings[I] + '<br/>');
166 end;
167
168
169 WriteString('<h5>Response HTTP content:</h5>');
170 WriteStream(Response.Content, Response.Content.Size);
171
172 WriteString('<h5>Response HTTP headers</h5>');
173 with Response.Content do
174 for I := 0 to Response.Headers.Count - 1 do begin;
175 WriteString(Response.Headers.Items[I].Value + '<br/>');
176 end;
177
178 WriteString('<h5>Response HTTP cookies</h5>');
179 for I := 0 to Response.Cookies.Count - 1 do begin;
180 WriteString(Response.Cookies.Strings[I] + '<br/>');
181 end;
182 end;
183end;
184
185procedure THTTPServer.ErrorResponse(HandlerData: THTTPHandlerData);
186begin
187 with HandlerData, Response.Content do begin
188 WriteString('<html><body>' + Format(SPageNotFound, [Request.Path.Implode('/', StrToStr)]) + '</body></html>');
189 end;
190end;
191
192procedure THTTPServer.SetShowExceptions(const AValue: Boolean);
193begin
194 FShowExceptions := AValue;
195 if AValue then ExceptProc := HTTPExceptionShow
196 else ExceptProc := HTTPExceptionHide;
197end;
198
199procedure THTTPServer.Run;
200begin
201
202end;
203
204procedure THTTPServer.FileResponse(HandlerData: THTTPHandlerData);
205var
206 BinaryFile: TFileStream;
207 FileName: string;
208begin
209 with HandlerData do begin
210 FileName := DocumentRoot + DirectorySeparator + Request.Path.Implode('/', StrToStr);
211 if FileExists(FileName) then begin
212 Response.Headers.Values['Content-Type'] := GetMIMEType(Copy(ExtractFileExt(FileName), 2, 255));
213 try
214 BinaryFile := TFileStream.Create(FileName, fmOpenRead);
215 Response.Content.WriteStream(BinaryFile, BinaryFile.Size);
216 finally
217 BinaryFile.Free;
218 end;
219 end else
220 with Response.Content do begin
221 WriteLn(Format(SFileNotFound, [Request.Path.Implode('/', StrToStr)]));
222 WriteString('<html><body>' + Format(SFileNotFound, [Request.Path.Implode('/', StrToStr)]) + '</body></html>');
223 end;
224 end;
225end;
226
227constructor THTTPServer.Create(AOwner: TComponent);
228begin
229 inherited;
230 ShowExceptions := False;
231 DocumentRoot := './';
232end;
233
234destructor THTTPServer.Destroy;
235begin
236 inherited Destroy;
237end;
238
239{ THTTPResponse }
240
241procedure THTTPResponse.Assign(Source: THTTPResponse);
242begin
243 Content.Assign(Source.Content);
244 ContentType := Source.ContentType;
245 Cookies.Assign(Source.Cookies);
246 Headers.Assign(Source.Headers);
247end;
248
249procedure THTTPResponse.Clear;
250begin
251 Content.Clear;
252 Cookies.Clear;
253 Headers.Clear;
254end;
255
256constructor THTTPResponse.Create;
257begin
258 Content := TMemoryStreamEx.Create;
259 Cookies := TCookieList.Create;
260 Headers := TDictionaryStringString.Create;
261end;
262
263destructor THTTPResponse.Destroy;
264begin
265 Content.Free;
266 Headers.Free;
267 Cookies.Free;
268 inherited Destroy;
269end;
270
271{ TRequestHandlerList }
272
273procedure TRequestHandlerList.Add(AName: string; AHandler: TRequestEvent);
274begin
275 with TRequestHandler(Items[inherited Add(TRequestHandler.Create)]) do begin
276 Name := AName;
277 Handler := AHandler;
278 end;
279end;
280
281function TRequestHandlerList.IndexOfName(AName: string): TRequestHandler;
282var
283 I: Integer;
284begin
285 I := 0;
286 while (I < Count) and (TRequestHandler(Items[I]).Name <> AName) do Inc(I);
287 if I < Count then Result := TRequestHandler(Items[I]) else Result := nil;
288end;
289
290{ THTTPRequest }
291
292procedure THTTPRequest.Assign(Source: THTTPRequest);
293begin
294 Content.Assign(Source.Content);
295 ContentType := Source.ContentType;
296 Cookies.Assign(Source.Cookies);
297 Headers.Assign(Source.Headers);
298 Post.Assign(Source.Post);
299 Method := Source.Method;
300 Query.Assign(Source.Query);
301 Path.Assign(Source.Path);
302end;
303
304procedure THTTPRequest.Clear;
305begin
306 Post.Clear;
307 Content.Clear;
308 Path.Clear;
309 Cookies.Clear;
310 Headers.Clear;
311 Query.Clear;
312end;
313
314constructor THTTPRequest.Create;
315begin
316 Post := TQueryParameterList.Create;
317 Query := TQueryParameterList.Create;
318 Path := TListString.Create;
319 Headers := TDictionaryStringString.Create;
320 Cookies := TCookieList.Create;
321 Content := TMemoryStreamEx.Create;
322end;
323
324destructor THTTPRequest.Destroy;
325begin
326 Content.Free;
327 Post.Free;
328 Query.Free;
329 Path.Free;
330 Headers.Free;
331 Cookies.Free;
332 inherited Destroy;
333end;
334
335{ TQueryParameterList }
336
337procedure TQueryParameterList.Parse(Text: string);
338var
339 I: Integer;
340 Parts: TListString;
341 Pair: TListString;
342begin
343 try
344 Parts := TListString.Create;
345 Pair := TListString.Create;
346 Clear;
347 Parts.Explode(Text, '&', StrToStr);
348 for I := 0 to Parts.Count - 1 do begin
349 Pair.Explode(Parts[I], '=', StrToStr);
350 if Pair.Count >= 2 then
351 Values[Pair[0]] := Pair[1];
352 end;
353
354 finally
355 Parts.Free;
356 Pair.Free;
357 end;
358end;
359
360function TQueryParameterList.Syntetize: string;
361var
362 I: Integer;
363begin
364 Result := '';
365 for I := 0 to Count - 1 do
366 Result := Result + '&amp;' + Names[I] + '=' + ValueFromIndex[I];
367 Result := Copy(Result, 6, Length(Result));
368end;
369
370{ TCookieList }
371
372procedure TCookieList.Parse(Text: string);
373var
374 I: Integer;
375 Parts: TListString;
376 Pair: TListString;
377begin
378 try
379 Parts := TListString.Create;
380 Pair := TListString.Create;
381 Clear;
382 Parts.Explode(Text, ';', StrToStr);
383 for I := 0 to Parts.Count - 1 do begin
384 Pair.Explode(Parts[I], '=', StrToStr);
385 if Pair.Count >= 2 then
386 Values[Trim(Pair[0])] := Trim(Pair[1]);
387 end;
388 finally
389 Pair.Free;
390 Parts.Free;
391 end;
392end;
393
394function TCookieList.Syntetize: string;
395var
396 I: Integer;
397begin
398 Result := '';
399 for I := 0 to Count - 1 do
400 Result := Result + '; ' + Names[I] + '=' + ValueFromIndex[I];
401 Result := Copy(Result, 2, Length(Result));
402end;
403
404{ THTTPSessionStorage }
405
406procedure THTTPSessionStorage.Load(HandlerData: THTTPHandlerData);
407begin
408
409end;
410
411procedure THTTPSessionStorage.Save(HandlerData: THTTPHandlerData);
412begin
413
414end;
415
416constructor THTTPSessionStorage.Create;
417begin
418 inherited;
419end;
420
421destructor THTTPSessionStorage.Destroy;
422begin
423 inherited Destroy;
424end;
425
426{ THTTPHandlerData }
427
428procedure THTTPHandlerData.Assign(Source: THTTPHandlerData);
429begin
430 Request.Assign(Source.Request);
431 Response.Assign(Source.Response);
432 Session.Assign(Source.Session);
433 Server := Source.Server;
434 SessionId := Source.SessionId;
435end;
436
437constructor THTTPHandlerData.Create;
438begin
439 Request := THTTPRequest.Create;
440 Response := THTTPResponse.Create;
441 Session := TStringList.Create;
442end;
443
444destructor THTTPHandlerData.Destroy;
445begin
446 Request.Free;
447 Response.Free;
448 Session.Free;
449 inherited Destroy;
450end;
451
452procedure HTTPExceptionShow(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
453type
454 TArrayOfPointer = array of Pointer;
455var
456 Message: string;
457 i: LongInt;
458 hstdout: ^Text;
459begin
460 hstdout := @stdout;
461 WriteLn(hstdout^, 'Content-type: text/html');
462 WriteLn(hstdout^);
463 Writeln(hstdout^, 'An unhandled exception occurred at $', HexStr(PtrUInt(Addr), SizeOf(PtrUInt) * 2), ' :<br/>');
464 if Obj is exception then
465 begin
466 Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
467 Writeln(hstdout^, Message + '<br/>');
468 end
469 else
470 Writeln(hstdout^, 'Exception object ', Obj.ClassName, ' is not of class Exception.<br/>');
471 Writeln(hstdout^, BackTraceStrFunc(Addr) + '<br/>');
472 if (FrameCount > 0) then
473 begin
474 for i := 0 to FrameCount - 1 do
475 if I < Length(TArrayOfPointer(Frames)) then
476 Writeln(hstdout^, BackTraceStrFunc(TArrayOfPointer(Frames)[i]) + '<br/>');
477 end;
478 Writeln(hstdout^, '');
479end;
480
481procedure HTTPExceptionHide(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
482type
483 TArrayOfPointer = array of Pointer;
484var
485 Message: string;
486 i: LongInt;
487 hstdout: ^Text;
488 hstderr: ^Text;
489begin
490 hstdout := @stdout;
491 hstderr := @stderr;
492 WriteLn(hstdout^, 'Content-type: text/html');
493 WriteLn(hstdout^);
494 WriteLn(hstdout^, 'Error occured during page generation.');
495 Writeln(hstderr^, 'An unhandled exception occurred at $', HexStr(PtrUInt(Addr), SizeOf(PtrUInt) * 2), ' :');
496 if Obj is exception then
497 begin
498 Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
499 Writeln(hstderr^, Message);
500 end
501 else
502 Writeln(hstderr^, 'Exception object ', Obj.ClassName, ' is not of class Exception.');
503 Writeln(hstderr^, BackTraceStrFunc(Addr));
504 if (FrameCount > 0) then
505 begin
506 for i := 0 to FrameCount - 1 do
507 if I < Length(TArrayOfPointer(Frames)) then
508 Writeln(hstderr^, BackTraceStrFunc(TArrayOfPointer(Frames)[i]));
509 end;
510 Writeln(hstderr^, '');
511end;
512
513end.
514
Note: See TracBrowser for help on using the repository browser.