1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 2019 by the Free Pascal development team
4
5    TCustomMicroHTTPApplication class.
6
7    See the file COPYING.FPC, included in this distribution,
8    for details about the copyright.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 **********************************************************************}
15{ $define CGIDEBUG}
16{$mode objfpc}
17{$H+}
18
19unit custmicrohttpapp;
20
21Interface
22
23uses
24  Classes, SysUtils, httpprotocol, httpdefs, custweb, libmicrohttpd;
25
26Type
27  TCustomMicroHTTPApplication = Class;
28  TMicroServerOption = (
29     mcoDebug,
30     mcoSSL,
31     mcoThreadPerConnection,
32     mcoSelectInternally,
33     mcoIPV6,
34     mcoPedanticChecks,
35     mcoPoll,
36     mcoSuppressDateNoClock,
37     mcoNoListenSocket,
38     mcoEPollLinuxOnly,
39     mcoPipeForShutdown,
40     mcoDualStack,
41     mcoEPollTurbo,
42     mcoSuspendResume,
43     mcoTCPFastOpen
44  );
45  TMicroServerOptions = Set of TMicroServerOption;
46
47  TMicroHTTPHandler = Class;
48  TRequestHandler = Class;
49
50  { TMicroRequest }
51
52  TMicroRequest = Class(TRequest)
53  Private
54    FHandler : TRequestHandler;
55    FMyQueryString : String;
56    // Return amount of data handled
57    Procedure DoSetHeader(K,V : String);
58    Procedure AddQueryField(K,V : String);
59    Function AddData(Data: PAnsiChar; DataSize: Size_t) : Size_t;
60    Procedure Initialize(const aUrl, aMethod, aVersion: String);
61    procedure InitRequestVars; override;
62  Protected
63    Property Handler : TRequestHandler Read FHandler;
64  end;
65
66  { TMicroResponse }
67
68  TMicroResponse = Class(TResponse)
69  Private
70    FHandler : TRequestHandler;
71    FResponse : PMHD_Response;
72  Protected
73    Procedure MaybeAllocateResponse; virtual;
74    Procedure DoSendHeaders(Headers: TStrings); override;
75    Procedure DoSendContent; override;
76    Property Handler : TRequestHandler Read FHandler;
77    Property Response : PMHD_Response Read FResponse;
78  Public
79    Destructor Destroy; override;
80  end;
81
82  { TRequestHandler }
83
84  TRequestHandler = Class
85    FConnection : PMHD_Connection;
86    FWebHandler : TMicroHTTPHandler;
87    FRequest : TMicroRequest;
88    FResponse : TMicroResponse;
89  Public
90    Constructor Create(aHandler : TMicroHTTPHandler; aConnection :PMHD_Connection);
91    Destructor Destroy; override;
92    Procedure Initialize(const aUrl, aMethod, aVersion: String);
93    Function ContinueRequest(Data: PAnsiChar; var DataSize: Size_t) : Cint;
94    Property Connection : PMHD_Connection Read FConnection;
95    Property WebHandler : TMicroHTTPHandler Read FWebHandler;
96    Property Request : TMicroRequest Read FRequest;
97    Property Response : TMicroResponse Read FResponse;
98  end;
99
100  { TMicroHTTPHandler }
101
102  TAcceptHandler = Procedure (Sender : TObject; Addr : PSockAddr; addrLen : socklen_t; var Allow : Boolean) of object;
103  TRequestErrorHandler = Procedure (Sender : TObject; E : Exception) of object;
104
105  TMicroHTTPHandler = class(TWebHandler)
106  Private
107    FAcceptHandler: TAcceptHandler;
108    FExtraHeaders: TStrings;
109    FOnRequestError: TRequestErrorHandler;
110    FPort : Word;
111    FOptions: TMicroServerOptions;
112    FServer: PMHD_Daemon;
113    FHostName : string;
114    procedure MaybeStopServer;
115    procedure RequestCompleted(aRequest: TRequestHandler);
116    function DoRequest(connection: PMHD_Connection; const aUrl, aMethod, aVersion: String; Data: PAnsiChar; var DataSize: Size_t): TRequestHandler;
117    procedure SetExtraHeaders(AValue: TStrings);
118    procedure SetHostName(AValue: String);
119    procedure SetOptions(AValue: TMicroServerOptions);
120    procedure SetPort(const AValue: Word);
121  protected
122    function OptionsToFlags : Integer;
123    Function DoAcceptConnection(Addr : PSockAddr; addrLen : socklen_t) : Boolean;
124    procedure CheckInactive;
125    function CreateServer: PMHD_Daemon; virtual;
126    procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
127    Procedure InitRequest(ARequest : TRequest); override;
128    Procedure InitResponse(AResponse : TResponse); override;
129    function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
130    Property Daemon : PMHD_Daemon Read FServer;
131  Public
132    Procedure Run; override;
133    constructor Create(AOwner: TComponent); override;
134    destructor Destroy; override;
135    // Port to listen on.
136    Property Port : Word Read FPort Write SetPort Default 80;
137    // HostName to use when using SSL
138    Property HostName : String Read FHostName Write SetHostName;
139    // ServerOPtions
140    Property Options : TMicroServerOptions Read FOptions Write SetOptions;
141    // On Accept handler
142    Property OnAccept : TAcceptHandler Read FAcceptHandler Write FAcceptHandler;
143    // Handle On Request error. If not set, error is logged.
144    Property OnRequestError : TRequestErrorHandler Read FOnRequestError Write FOnRequestError;
145    // Extra non-standard headers which can be accepted as part of requests
146    Property ExtraHeaders : TStrings Read FExtraHeaders Write SetExtraHeaders;
147  end;
148
149  { TCustomMicroHTTPApplication }
150
151  TCustomMicroHTTPApplication = Class(TCustomWebApplication)
152  private
153    function GetExtraHeaders: TStrings;
154    function GetHostName: String;
155    function GetOptions: TMicroServerOptions;
156    function GetPort: Word;
157    function GetUseSSL: Boolean;
158    procedure SetExtraHeaders(AValue: TStrings);
159    procedure SetHostName(AValue: String);
160    procedure SetOptions(AValue: TMicroServerOptions);
161    procedure SetPort(AValue: Word);
162    procedure SetUseSSL(AValue: Boolean);
163  protected
164    function InitializeWebHandler: TWebHandler; override;
165    Function HTTPHandler : TMicroHTTPHandler;
166  Public
167    constructor Create(aOwner : TComponent); override;
168    destructor Destroy; override;
169    Property Port : Word Read GetPort Write SetPort Default 80;
170    // Hostname to use when using SSL
171    Property HostName : String Read GetHostName Write SetHostName;
172    // ServerOptions
173    Property Options : TMicroServerOptions Read GetOptions Write SetOptions;
174    // For compatibility
175    Property UseSSL : Boolean Read GetUseSSL Write SetUSeSSL;
176    // Extra non-standard headers which can be accepted as part of requests
177    Property ExtraHeaders : TStrings Read GetExtraHeaders Write SetExtraHeaders;
178  end;
179
180
181Implementation
182
183Resourcestring
184  SErrServerActive = 'Operation cannot be performed while server is active';
185  SErrFailedToStartServer = 'Failed to start server';
186
187
188Const
189  BoolToYesNo : Array[Boolean] of Integer = (MHD_NO,MHD_YES);
190
191Const
192  OptionFlags :   Array[TMicroServerOption] of Integer = (
193  MHD_USE_DEBUG,
194  MHD_USE_SSL,
195  MHD_USE_THREAD_PER_CONNECTION,
196  MHD_USE_SELECT_INTERNALLY,
197  MHD_USE_IPv6,
198  MHD_USE_PEDANTIC_CHECKS,
199  MHD_USE_POLL,
200  MHD_SUPPRESS_DATE_NO_CLOCK,
201  MHD_USE_NO_LISTEN_SOCKET,
202  MHD_USE_EPOLL_LINUX_ONLY,
203  MHD_USE_PIPE_FOR_SHUTDOWN,
204  MHD_USE_DUAL_STACK,
205  MHD_USE_EPOLL_TURBO,
206  MHD_USE_SUSPEND_RESUME,
207  MHD_USE_TCP_FASTOPEN);
208
209{ ---------------------------------------------------------------------
210  libmicrohttp Callbacks
211  ---------------------------------------------------------------------}
212
213Function MaybeS(p : pchar) : String;
214begin
215  if Assigned(P) then Result:=P else Result:='';
216end;
217
218function GetRequestData(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; value: Pcchar): cint; cdecl;
219
220var
221  K,V : String;
222
223
224begin
225  K:=MaybeS(key);
226  V:=MaybeS(Value);
227  if kind=MHD_HEADER_KIND then
228    TMicroRequest(Cls).DoSetHeader(K,V)
229  else if kind=MHD_GET_ARGUMENT_KIND then
230    TMicroRequest(Cls).AddQueryField(K,V);
231  Result:=MHD_YES;
232end;
233
234
235procedure DoPanic(cls: Pointer; &file: Pcchar; line: cuint; reason: Pcchar); cdecl;
236
237begin
238  if Assigned(cls) then
239    TCustomMicroHTTPApplication(Cls).Log(etError,Format('Panic at %s(%d): %s ',[MaybeS(&File),line,MaybeS(reason)]))
240  else if IsConsole then
241    writeln('Panic: File ',MaybeS(&File),'(',line,')',MaybeS(Reason));
242end;
243
244
245function DoReadResponse(cls: pointer; pos: cuint64; buf: Pcchar; max: size_t): ssize_t; cdecl;
246
247Var
248  Resp : TMicroResponse;
249
250begin
251  Resp:=TMicroResponse(cls);
252  if Pos<>Resp.ContentStream.Position then
253    Resp.ContentStream.Position:=Pos;
254  Result:=Resp.ContentStream.Read(buf^,max);
255end;
256
257
258function AcceptCallBack(cls: Pointer; addr: psockaddr; addrlen: socklen_t): cint; cdecl;
259begin
260  Result:=BoolToYesNo[TMicroHTTPHandler(Cls).DoAcceptConnection(addr,addrlen)];
261end;
262
263
264function DoMHDRequest(cls: Pointer; connection: PMHD_Connection; url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
265  upload_data_size: pSize_t; con_cls: PPointer): cint; cdecl;
266
267Var
268  aURL : String;
269  aMethod : String;
270  aVersion : String;
271  H : TMicroHTTPHandler;
272
273begin
274  aURL:=URl;
275  aMethod:=Method;
276  aVersion:=Version;
277  if (Con_cls^=Nil) then
278    begin
279    H:=TMicroHTTPHandler(Cls);
280    Con_cls^:=H.DoRequest(connection,aURL,aMethod,aVersion,Upload_Data,Upload_data_size^);
281    Result:=BoolToYesNo[con_cls^<>Nil];
282    end
283  else
284    Result:=TRequestHandler(Con_cls^).ContinueRequest(Upload_Data,Upload_data_size^);
285end;
286
287procedure HandleRequestCompleted(ACls: Pointer; AConnection: PMHD_Connection; AConCls: PPointer; AToe: MHD_RequestTerminationCode); cdecl;
288var
289  Req: TRequestHandler;
290  H :  TMicroHTTPHandler;
291begin
292  Req:=TRequestHandler(AConCls^);
293  if not Assigned(Req) then
294    Exit;
295  H:=TMicroHTTPHandler(aCls);
296  if not Assigned(H) then
297    H:=Req.WebHandler;
298  if Assigned(H) then
299    H.RequestCompleted(Req)
300  else
301    Req.Free;
302  AConCls^ := nil;
303end;
304
305
306{ ---------------------------------------------------------------------
307  TMicroRequest
308  ---------------------------------------------------------------------}
309
310
311procedure TMicroRequest.DoSetHeader(K, V: String);
312
313Var
314  H :  THeader;
315
316begin
317  H:=HeaderType(K);
318  if hdRequest in HTTPHeaderDirections[h] then
319    SetHeader(H,V)
320  else
321    SetCustomHeader(K,V);
322end;
323
324procedure TMicroRequest.AddQueryField(K, V: String);
325
326Var
327  S : String;
328
329begin
330  if V<>'' then
331    QueryFields.Values[K]:=V
332  else
333    QueryFields.Add(K+'=');
334  S:=FMyQueryString;
335  if S<>'' then
336    S:=S+'&';
337  FMyQueryString:=S+K+'='+HTTPEncode(V);
338end;
339
340function TMicroRequest.AddData(Data: PAnsiChar; DataSize: Size_t): Size_t;
341
342Var
343  C : String;
344  L : Integer;
345
346begin
347  C:=Content;
348  L:=Length(C);
349  SetLength(C,L+Datasize);
350  Move(Data^,C[L+1],DataSize);
351  InitContent(C);
352  Result:=Datasize;
353end;
354
355procedure TMicroRequest.Initialize(const aUrl, aMethod, aVersion: String);
356
357begin
358  SetHTTPVariable(hvURL,aURL);
359  SetHTTPVariable(hvMethod,aMethod);
360  SetHTTPVariable(hvHTTPVersion,aVersion);
361  InitRequestVars;
362end;
363
364procedure TMicroRequest.InitRequestVars;
365
366Var
367  P : Pchar;
368  N,S  : String;
369  I : integer;
370
371begin
372  MHD_get_connection_values(FHandler.FConnection, MHD_GET_ARGUMENT_KIND,@GetRequestData,Self);
373  MHD_get_connection_values(FHandler.FConnection, MHD_HEADER_KIND,@GetRequestData,Self);
374  for N in FHandler.WebHandler.ExtraHeaders do
375    begin
376    P:=MHD_lookup_connection_value(FHandler.FConnection, MHD_HEADER_KIND,Pchar(N));
377    If P<>Nil then
378      SetCustomHeader(N,P);
379    end;
380  S:=URL;
381  I:=Pos('?',S);
382  if (I>0) then
383    S:=Copy(S,1,I-1);
384  If (Length(S)>1) and (S[1]<>'/') then
385    S:='/'+S
386  else if S='/' then
387    S:='';
388  PathInfo:=S;
389  Inherited;
390  // We set this afterwards, otherwise double processing
391  if FMyQueryString<>'' then
392    SetHTTPVariable(hvQuery,FMyQueryString)
393end;
394
395{ ---------------------------------------------------------------------
396  TMicroResponse
397  ---------------------------------------------------------------------}
398
399procedure TMicroResponse.MaybeAllocateResponse;
400
401Var
402  L : Integer;
403  P : PChar;
404  B : TBytes;
405
406begin
407  if FResponse<>Nil then exit;
408  if Assigned(ContentStream) then
409    begin
410    ContentStream.Position:=0;
411    L:=ContentStream.Size;
412    if FreeContentStream then
413      FResponse:=MHD_create_response_from_callback(L,4096,@DoReadResponse,Self,Nil)
414    else
415      // We must copy the bytes, because we don't know when the stream is freed.
416      begin
417      SetLength(B,L);
418      ContentStream.ReadBuffer(B[0],L);
419      P:=Pchar(B);
420      FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
421      end;
422    end
423  else
424    begin
425    L:=Length(Content);
426    P:=PChar(Content);
427    FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
428    end;
429end;
430
431procedure TMicroResponse.DoSendHeaders(Headers: TStrings);
432
433Var
434  I : Integer;
435  N,V : String;
436
437begin
438  // Note that if the response is allocated, then you cannot set the content stream any more...
439  MaybeAllocateResponse;
440  Headers.NameValueSeparator:=':';
441  For I:=0 to Headers.Count-1 do
442    begin
443    Headers.GetNameValue(I,N,V);
444    MHD_add_response_header(FResponse,PAnsiChar(N),PAnsiChar(V));
445    end;
446end;
447
448procedure TMicroResponse.DoSendContent;
449begin
450  MaybeAllocateResponse;
451  MHD_queue_response(FHandler.FConnection,Self.Code,FResponse);
452end;
453
454destructor TMicroResponse.Destroy;
455begin
456  if (FResponse<>Nil) then
457    MHD_destroy_response(FResponse);
458  inherited Destroy;
459end;
460
461{ ---------------------------------------------------------------------
462  TRequestHandler
463  ---------------------------------------------------------------------}
464
465constructor TRequestHandler.Create(aHandler: TMicroHTTPHandler; aConnection: PMHD_Connection);
466begin
467  FWebHandler:=aHandler;
468  FConnection:=aConnection;
469  FRequest:=TMicroRequest.Create;
470  FRequest.FHandler:=Self;
471  FResponse:=TMicroResponse.Create(FRequest);
472  FResponse.FHandler:=Self;
473end;
474
475destructor TRequestHandler.Destroy;
476begin
477  FreeAndNil(FRequest);
478  FreeAndNil(FResponse);
479  inherited Destroy;
480end;
481
482procedure TRequestHandler.Initialize(const aUrl, aMethod, aVersion: String);
483begin
484  FRequest.Initialize(aURL,aMethod,aVersion);
485end;
486
487function TRequestHandler.ContinueRequest(Data: PAnsiChar; var DataSize: Size_t): Cint;
488
489Var
490  CanHandleRequest : Boolean;
491
492begin
493  CanHandleRequest:=Datasize=0;
494  if Datasize>0 then
495    DataSize:=DataSize-FRequest.AddData(Data,Datasize);
496  If Not CanHandleRequest then
497    Result:=BoolToYesNo[DataSize=0]
498  else
499    begin
500    try
501      WebHandler.HandleRequest(FRequest,FResponse);
502      If Not FResponse.ContentSent then
503        try
504          FResponse.SendContent;
505        except
506          On E : Exception do
507            WebHandler.HandleRequestError(WebHandler,E);
508        end;
509      Result:=MHD_YES;
510    except
511      On E : Exception do
512        begin
513        Result:=MHD_NO;
514        WebHandler.HandleRequestError(WebHandler,E);
515        end;
516    end;
517    end;
518end;
519
520
521{ ---------------------------------------------------------------------
522  TMicroHTTPHandler
523  ---------------------------------------------------------------------}
524
525procedure TMicroHTTPHandler.RequestCompleted(aRequest: TRequestHandler);
526
527begin
528  try
529    EndRequest(aRequest.FRequest,aRequest.FResponse);
530    aRequest.FRequest:=Nil;
531    aRequest.FResponse:=Nil;
532    aRequest.Free;
533  except
534    On E: Exception do
535      HandleRequestError(Self,E);
536  end;
537end;
538
539function TMicroHTTPHandler.DoRequest(connection: PMHD_Connection; Const aUrl,aMethod,aVersion: String; Data: PAnsiChar; var DataSize: Size_t) : TRequestHandler;
540
541begin
542  Result:=TRequestHandler.Create(Self,Connection);
543  Result.Initialize(aURl,aMethod,AVersion);
544  if (DataSize>0) then
545    if Result.ContinueRequest(Data,Datasize)<>MHD_YES then
546      FreeAndNil(Result);
547end;
548
549procedure TMicroHTTPHandler.SetExtraHeaders(AValue: TStrings);
550begin
551  if FExtraHeaders=AValue then Exit;
552  FExtraHeaders.Assign(AValue);
553end;
554
555procedure TMicroHTTPHandler.HandleRequestError(Sender: TObject; E: Exception);
556begin
557  Try
558    If Assigned(FOnRequestError) then
559      FOnRequestError(Sender,E)
560    else
561      Log(etError,Format('Error (%s) handling request : %s',[E.ClassName,E.Message]));
562  except
563    // Do not let errors escape
564  end;
565end;
566
567procedure TMicroHTTPHandler.CheckInactive;
568
569begin
570  if Assigned(FServer) then
571    Raise EHTTP.Create(SErrServerActive);
572end;
573
574procedure TMicroHTTPHandler.SetHostName(AValue: String);
575begin
576  CheckInactive;
577  FHostName:=aValue;
578end;
579
580
581procedure TMicroHTTPHandler.SetOptions(AValue: TMicroServerOptions);
582begin
583  if FOptions=AValue then Exit;
584  CheckInactive;
585  FOptions:=AValue;
586end;
587
588procedure TMicroHTTPHandler.SetPort(const AValue: Word);
589begin
590  CheckInactive;
591  FPort:=Avalue
592end;
593
594procedure TMicroHTTPHandler.InitRequest(ARequest: TRequest);
595begin
596  inherited InitRequest(ARequest);
597end;
598
599procedure TMicroHTTPHandler.InitResponse(AResponse: TResponse);
600begin
601  inherited InitResponse(AResponse);
602end;
603
604function TMicroHTTPHandler.WaitForRequest(out ARequest: TRequest;
605  out AResponse: TResponse): boolean;
606begin
607  Result:=False;
608  ARequest:=Nil;
609  AResponse:=Nil;
610end;
611
612Function TMicroHTTPHandler.DoAcceptConnection(Addr : PSockAddr; addrLen : socklen_t) : Boolean;
613
614begin
615  Result:=True;
616  if Assigned(FAcceptHandler) then
617    FAcceptHandler(Self,Addr,addrLen,Result);
618end;
619
620
621function TMicroHTTPHandler.OptionsToFlags : Integer;
622
623Var
624  O : TMicroServerOption;
625
626begin
627  Result:=0;
628  For O in TMicroServerOption do
629    if O in Options then
630      Result:=Result or OptionFlags[O];
631end;
632
633
634function TMicroHTTPHandler.CreateServer: PMHD_Daemon;
635
636Var
637  F,P : Integer;
638
639begin
640  F:=OptionsToFlags;
641  P:=Port;
642  Result:= MHD_start_daemon(F,P,
643    @AcceptCallBack, Self,
644    @DoMHDRequest, Self,
645    MHD_OPTION_NOTIFY_COMPLETED, @HandleRequestCompleted,
646    Nil,MHD_OPTION_END);
647  if Result=Nil then
648    Writeln('a')
649  else
650    Writeln('b');
651end;
652
653procedure TMicroHTTPHandler.Run;
654begin
655  FServer:=CreateServer;
656  if (FServer=Nil) then
657    Raise EHTTP.Create(SErrFailedToStartServer);
658  Repeat
659    Sleep(50);
660  Until Terminated;
661end;
662
663procedure TMicroHTTPHandler.MaybeStopServer;
664
665begin
666  if Assigned(FServer) then
667    begin
668    MHD_stop_daemon(FServer);
669    FServer:=Nil;
670    end;
671end;
672
673constructor TMicroHTTPHandler.Create(AOwner: TComponent);
674begin
675  inherited Create(AOwner);
676  FExtraHeaders:=TStringList.Create;
677  Options:=[mcoSelectInternally];
678  Port:=80;
679end;
680
681destructor TMicroHTTPHandler.Destroy;
682begin
683  MaybeStopServer;
684  FreeAndNil(FExtraHeaders);
685  inherited Destroy;
686end;
687
688
689{ ---------------------------------------------------------------------
690  TCustomMicroHTTPApplication
691  ---------------------------------------------------------------------}
692
693
694procedure TCustomMicroHTTPApplication.SetHostName(AValue: String);
695begin
696  HTTPHandler.HostName:=aValue;
697end;
698
699procedure TCustomMicroHTTPApplication.SetOptions(AValue: TMicroServerOptions);
700begin
701  HTTPHandler.Options:=aValue;
702end;
703
704procedure TCustomMicroHTTPApplication.SetPort(AValue: Word);
705begin
706  HTTPHandler.Port:=aValue;
707end;
708
709procedure TCustomMicroHTTPApplication.SetUSeSSL(AValue: Boolean);
710begin
711  if AValue then
712    Options:=Options+[mcoSSL]
713  else
714    Options:=Options-[mcoSSL]
715end;
716
717function TCustomMicroHTTPApplication.GetPort: Word;
718begin
719  Result:=HTTPHandler.Port;
720end;
721
722function TCustomMicroHTTPApplication.GetUseSSL: Boolean;
723begin
724  Result:=mcoSSL in Options;
725end;
726
727procedure TCustomMicroHTTPApplication.SetExtraHeaders(AValue: TStrings);
728begin
729  HTTPHandler.ExtraHeaders.Assign(AValue);
730end;
731
732function TCustomMicroHTTPApplication.InitializeWebHandler: TWebHandler;
733begin
734  Result:=TMicroHTTPHandler.Create(Self);
735end;
736
737function TCustomMicroHTTPApplication.HTTPHandler: TMicroHTTPHandler;
738begin
739  Result:=Webhandler as TMicroHTTPHandler;
740end;
741
742constructor TCustomMicroHTTPApplication.Create(aOwner: TComponent);
743begin
744  inherited Create(aOwner);
745  MHD_set_panic_func(@DoPanic,Self);
746end;
747
748destructor TCustomMicroHTTPApplication.Destroy;
749begin
750  MHD_set_panic_func(@DoPanic,Nil);
751  inherited Destroy;
752end;
753
754function TCustomMicroHTTPApplication.GetExtraHeaders: TStrings;
755begin
756  Result:=HTTPHandler.ExtraHeaders;
757end;
758
759function TCustomMicroHTTPApplication.GetHostName: String;
760begin
761  Result:=HTTPHandler.HostName;
762end;
763
764function TCustomMicroHTTPApplication.GetOptions: TMicroServerOptions;
765begin
766  Result:=HTTPHandler.Options;
767end;
768
769
770end.
771