Pārlūkot izejas kodu

* Add (cust)microhttpapp, high-concurrency standalone server

git-svn-id: trunk@43063 -
michael 5 gadi atpakaļ
vecāks
revīzija
80663ce324

+ 2 - 0
.gitattributes

@@ -4505,6 +4505,7 @@ packages/fcl-web/src/base/custcgi.pp svneol=native#text/plain
 packages/fcl-web/src/base/custfcgi.pp svneol=native#text/plain
 packages/fcl-web/src/base/custhttpapp.pp svneol=native#text/plain
 packages/fcl-web/src/base/custhttpsys.pp svneol=native#text/pascal
+packages/fcl-web/src/base/custmicrohttpapp.pp svneol=native#text/plain
 packages/fcl-web/src/base/custweb.pp svneol=native#text/plain
 packages/fcl-web/src/base/ezcgi.pp svneol=native#text/plain
 packages/fcl-web/src/base/fcgigate.pp svneol=native#text/plain
@@ -4533,6 +4534,7 @@ packages/fcl-web/src/base/httpdefs.pp svneol=native#text/plain
 packages/fcl-web/src/base/httpprotocol.pp svneol=native#text/plain
 packages/fcl-web/src/base/httproute.pp svneol=native#text/plain
 packages/fcl-web/src/base/iniwebsession.pp svneol=native#text/plain
+packages/fcl-web/src/base/microhttpapp.pp svneol=native#text/plain
 packages/fcl-web/src/base/restbase.pp svneol=native#text/plain
 packages/fcl-web/src/base/restcodegen.pp svneol=native#text/plain
 packages/fcl-web/src/base/tcwebmodule.pp svneol=native#text/plain

+ 87 - 8
packages/fcl-web/examples/simpleserver/simpleserver.pas

@@ -1,25 +1,45 @@
 {$mode objfpc}
 {$h+}
-{ $define USEGNUTLS}
+
+{ $DEFINE USEGNUTLS}
+{$DEFINE USEMICROHTTP}
+
 program simpleserver;
 
+{$IFDEF USEMICROHTTP}
+{$UNDEF USEGNUTLS}
+{$ENDIF}
+
 uses
-  sysutils,Classes,
+
+
+
+{$IFNDEF USEMICROHTTP}
 {$ifdef USEGNUTLS}
   gnutlssockets,
 {$else}
   opensslsockets,
 {$endif}
+  custhttpapp,
+{$ELSE}
+  cthreads,
+  custmicrohttpapp,
+{$ENDIF}
   {$ifdef unix}
   baseunix,
   {$endif}
-  inifiles, sslbase,httpdefs, custhttpapp, fpmimetypes, fpwebfile, fpwebproxy;
+  sysutils,Classes, inifiles, sslbase, httproute, httpdefs, fpmimetypes, fpwebfile, fpwebproxy, webutil;
 
 Type
 
   { THTTPApplication }
+{$IFDEF USEMICROHTTP}
+  TParentApp = TCustomMicroHTTPApplication;
+{$ELSE}
+  TParentApp = TCustomHTTPApplication;
+{$ENDIF}
 
-  THTTPApplication = Class(TCustomHTTPApplication)
+  THTTPApplication = Class(TParentApp)
   private
     FBaseDir: string;
     FIndexPageName: String;
@@ -27,8 +47,12 @@ Type
     FNoIndexPage: Boolean;
     FQuiet: Boolean;
     FBackground : Boolean;
+    FPassword : string;
+    FEcho : Boolean;
     procedure AddProxy(const aProxyDef: String);
+    procedure DoEcho(ARequest: TRequest; AResponse: TResponse);
     procedure DoProxyLog(Sender: TObject; const aMethod, aLocation, aFromURL, aToURL: String);
+    procedure Doquit(ARequest: TRequest; AResponse: TResponse);
     procedure LoadMimeTypes;
     procedure ProcessOptions;
     procedure ReadConfigFile(const ConfigFile: string);
@@ -49,6 +73,52 @@ Var
 
 { THTTPApplication }
 
+procedure THTTPApplication.DoEcho(ARequest: TRequest; AResponse: TResponse);
+
+Var
+  L : TStrings;
+
+begin
+  L:=TStringList.Create;
+  try
+    L.AddStrings(['<!doctype html>',
+      '<html>',
+      '<head>',
+      '<title>Echo request</title>',
+      '</head>',
+      '<body>'
+    ]);
+    DumpRequest(aRequest,L);
+    L.AddStrings(['</body>','</html>']);
+    AResponse.Content:=L.Text;
+    AResponse.SendResponse;
+  finally
+    L.Free;
+  end;
+end;
+procedure THTTPApplication.Doquit(ARequest: TRequest; AResponse: TResponse);
+
+Var
+  PWD : String;
+
+begin
+  PWD:=ARequest.QueryFields.Values['password'];
+  if PWD='' then
+    ARequest.ContentFields.Values['password'];
+  if PWD=FPassword then
+    begin
+    AResponse.Content:='OK';
+    AResponse.SendContent;
+    Terminate;
+    end
+  else
+    begin
+    AResponse.Code:=403;
+    AResponse.CodeText:='Forbidden';
+    AResponse.SendContent;
+    end;
+end;
+
 procedure THTTPApplication.DoLog(EventType: TEventType; const Msg: String);
 begin
   if Quiet then
@@ -93,6 +163,8 @@ begin
   Writeln('-p --port=NNNN      TCP/IP port to listen on (default is 3000)');
   Writeln('-m --mimetypes=file path of mime.types. Loaded in addition to OS known types');
   Writeln('-q --quiet          Do not write diagnostic messages');
+  Writeln('-Q --quit=PWD       register /quit url. Send request with password variable equal to PWD to stop');
+  Writeln('-e --echo       register /quit url. Send request with password variable equal to PWD to stop');
   Writeln('-s --ssl            Use SSL');
   Writeln('-H --hostname=NAME  set hostname for self-signed SSL certificate');
   Writeln('-x --proxy=proxydef Add proxy definition. Definition is of form:');
@@ -148,6 +220,8 @@ Const
   keyMimetypes = 'mimetypes';
   KeySSL = 'SSL';
   KeyQuiet = 'quiet';
+  KeyQuit = 'quit';
+  KeyEcho = 'echo';
   KeyNoIndexPage = 'noindexpage';
   KeyBackground = 'background';
 
@@ -170,7 +244,8 @@ begin
       HostName:=ReadString(SConfig,KeyHostName,HostName);
       UseSSL:=ReadBool(SConfig,KeySSL,UseSSL);
       FBackground:=ReadBool(SConfig,Keybackground,FBackGround);
-
+      FPassword:=ReadString(SConfig,KeyQuit,FPassword);
+      FEcho:=ReadBool(SConfig,KeyEcho,FEcho);
       L:=TstringList.Create;
       ReadSectionValues(SProxy,L,[]);
       For I:=0 to L.Count-1 do
@@ -193,7 +268,9 @@ Var
 begin
   for S in GetOptionValues('x','proxy') do
     AddProxy(S);
+  FEcho:=HasOption('e','echo');
   Quiet:=HasOption('q','quiet');
+  FPassword:=GetOptionValue('Q','quit');
   Port:=StrToIntDef(GetOptionValue('p','port'),Port);
   LoadMimeTypes;
   if HasOption('d','directory') then
@@ -228,7 +305,7 @@ Var
   S,ConfigFile : String;
 
 begin
-  S:=Checkoptions('hqd:ni:p:sH:m:x:c:b',['help','quiet','noindexpage','directory:','port:','indexpage:','ssl','hostname:','mimetypes:','proxy:','config:','background']);
+  S:=Checkoptions('hqd:ni:p:sH:m:x:c:beQ:',['help','quiet','noindexpage','directory:','port:','indexpage:','ssl','hostname:','mimetypes:','proxy:','config:','background','echo','quit:']);
   if (S<>'') or HasOption('h','help') then
     usage(S);
   if HasOption('c','config') then
@@ -245,7 +322,10 @@ begin
     Log(erError,'Background option not supported');
 {$endif}
     end;
-
+  if FPassword<>'' then
+    HTTPRouter.RegisterRoute('/quit',rmAll,@Doquit,False);
+  if FEcho  then
+    HTTPRouter.RegisterRoute('/echo',rmAll,@DoEcho,False);
   if ProxyManager.LocationCount>0 then
     begin
     TProxyWebModule.RegisterModule('Proxy',True);
@@ -266,7 +346,6 @@ begin
     end;
   if not Quiet then
     WriteInfo;
-
   inherited;
 end;
 

+ 728 - 0
packages/fcl-web/src/base/custmicrohttpapp.pp

@@ -0,0 +1,728 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 1999-2009 by the Free Pascal development team
+
+    THTTPApplication class.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{ $define CGIDEBUG}
+{$mode objfpc}
+{$H+}
+
+unit custmicrohttpapp;
+
+Interface
+
+uses
+  Classes, SysUtils, httpprotocol, httpdefs, custweb, libmicrohttpd;
+
+Type
+  TCustomMicroHTTPApplication = Class;
+  TMicroServerOption = (
+     mcoDebug,
+     mcoSSL,
+     mcoThreadPerConnection,
+     mcoSelectInternally,
+     mcoIPV6,
+     mcoPedanticChecks,
+     mcoPoll,
+     mcoSuppressDateNoClock,
+     mcoNoListenSocket,
+     mcoEPollLinuxOnly,
+     mcoPipeForShutdown,
+     mcoDualStack,
+     mcoEPollTurbo,
+     mcoSuspendResume,
+     mcoTCPFastOpen
+  );
+  TMicroServerOptions = Set of TMicroServerOption;
+
+  TMicroHTTPHandler = Class;
+  TRequestHandler = Class;
+
+  { TMicroRequest }
+
+  TMicroRequest = Class(TRequest)
+  Private
+    FHandler : TRequestHandler;
+    // Return amount of data handled
+    Function AddData(Data: PAnsiChar; DataSize: Size_t) : Size_t;
+    Procedure Initialize(const aUrl, aMethod, aVersion: String);
+    procedure InitRequestVars; override;
+  Protected
+    Property Handler : TRequestHandler Read FHandler;
+  end;
+
+  { TMicroResponse }
+
+  TMicroResponse = Class(TResponse)
+  Private
+    FHandler : TRequestHandler;
+    FResponse : PMHD_Response;
+  Protected
+    Procedure MaybeAllocateResponse; virtual;
+    Procedure DoSendHeaders(Headers: TStrings); override;
+    Procedure DoSendContent; override;
+    Property Handler : TRequestHandler Read FHandler;
+    Property Response : PMHD_Response Read FResponse;
+  Public
+    Destructor Destroy; override;
+  end;
+
+  { TRequestHandler }
+
+  TRequestHandler = Class
+    FConnection : PMHD_Connection;
+    FWebHandler : TMicroHTTPHandler;
+    FRequest : TMicroRequest;
+    FResponse : TMicroResponse;
+  Public
+    Constructor Create(aHandler : TMicroHTTPHandler; aConnection :PMHD_Connection);
+    Destructor Destroy; override;
+    Procedure Initialize(const aUrl, aMethod, aVersion: String);
+    Function ContinueRequest(Data: PAnsiChar; var DataSize: Size_t) : Cint;
+    Property Connection : PMHD_Connection Read FConnection;
+    Property WebHandler : TMicroHTTPHandler Read FWebHandler;
+    Property Request : TMicroRequest Read FRequest;
+    Property Response : TMicroResponse Read FResponse;
+  end;
+
+  { TMicroHTTPHandler }
+
+  TAcceptHandler = Procedure (Sender : TObject; Addr : PSockAddr; addrLen : socklen_t; var Allow : Boolean) of object;
+  TRequestErrorHandler = Procedure (Sender : TObject; E : Exception) of object;
+
+  TMicroHTTPHandler = class(TWebHandler)
+  Private
+    FAcceptHandler: TAcceptHandler;
+    FExtraHeaders: TStrings;
+    FOnRequestError: TRequestErrorHandler;
+    FPort : Word;
+    FOptions: TMicroServerOptions;
+    FServer: PMHD_Daemon;
+    FHostName : string;
+    procedure MaybeStopServer;
+    procedure RequestCompleted(aRequest: TRequestHandler);
+    function DoRequest(connection: PMHD_Connection; const aUrl, aMethod, aVersion: String; Data: PAnsiChar; var DataSize: Size_t): TRequestHandler;
+    procedure SetExtraHeaders(AValue: TStrings);
+    procedure SetHostName(AValue: String);
+    procedure SetOptions(AValue: TMicroServerOptions);
+    procedure SetPort(const AValue: Word);
+  protected
+    function OptionsToFlags : Integer;
+    Function DoAcceptConnection(Addr : PSockAddr; addrLen : socklen_t) : Boolean;
+    procedure CheckInactive;
+    function CreateServer: PMHD_Daemon; virtual;
+    procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
+    Procedure InitRequest(ARequest : TRequest); override;
+    Procedure InitResponse(AResponse : TResponse); override;
+    function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
+    Property Daemon : PMHD_Daemon Read FServer;
+  Public
+    Procedure Run; override;
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+    // Port to listen on.
+    Property Port : Word Read FPort Write SetPort Default 80;
+    // HostName to use when using SSL
+    Property HostName : String Read FHostName Write SetHostName;
+    // ServerOPtions
+    Property Options : TMicroServerOptions Read FOptions Write SetOptions;
+    // On Accept handler
+    Property OnAccept : TAcceptHandler Read FAcceptHandler Write FAcceptHandler;
+    // Handle On Request error. If not set, error is logged.
+    Property OnRequestError : TRequestErrorHandler Read FOnRequestError Write FOnRequestError;
+    // Extra non-standard headers which can be accepted as part of requests
+    Property ExtraHeaders : TStrings Read FExtraHeaders Write SetExtraHeaders;
+  end;
+
+  { TCustomMicroHTTPApplication }
+
+  TCustomMicroHTTPApplication = Class(TCustomWebApplication)
+  private
+    function GetExtraHeaders: TStrings;
+    function GetHostName: String;
+    function GetOptions: TMicroServerOptions;
+    function GetPort: Word;
+    function GetUseSSL: Boolean;
+    procedure SetExtraHeaders(AValue: TStrings);
+    procedure SetHostName(AValue: String);
+    procedure SetOptions(AValue: TMicroServerOptions);
+    procedure SetPort(AValue: Word);
+    procedure SetUseSSL(AValue: Boolean);
+  protected
+    function InitializeWebHandler: TWebHandler; override;
+    Function HTTPHandler : TMicroHTTPHandler;
+  Public
+    constructor Create(aOwner : TComponent); override;
+    destructor Destroy; override;
+    Property Port : Word Read GetPort Write SetPort Default 80;
+    // Hostname to use when using SSL
+    Property HostName : String Read GetHostName Write SetHostName;
+    // ServerOptions
+    Property Options : TMicroServerOptions Read GetOptions Write SetOptions;
+    // For compatibility
+    Property UseSSL : Boolean Read GetUseSSL Write SetUSeSSL;
+    // Extra non-standard headers which can be accepted as part of requests
+    Property ExtraHeaders : TStrings Read GetExtraHeaders Write SetExtraHeaders;
+  end;
+
+
+Implementation
+
+Resourcestring
+  SErrServerActive = 'Operation cannot be performed while server is active';
+  SErrFailedToStartServer = 'Failed to start server';
+
+
+Const
+  BoolToYesNo : Array[Boolean] of Integer = (MHD_NO,MHD_YES);
+
+Const
+  OptionFlags :   Array[TMicroServerOption] of Integer = (
+  MHD_USE_DEBUG,
+  MHD_USE_SSL,
+  MHD_USE_THREAD_PER_CONNECTION,
+  MHD_USE_SELECT_INTERNALLY,
+  MHD_USE_IPv6,
+  MHD_USE_PEDANTIC_CHECKS,
+  MHD_USE_POLL,
+  MHD_SUPPRESS_DATE_NO_CLOCK,
+  MHD_USE_NO_LISTEN_SOCKET,
+  MHD_USE_EPOLL_LINUX_ONLY,
+  MHD_USE_PIPE_FOR_SHUTDOWN,
+  MHD_USE_DUAL_STACK,
+  MHD_USE_EPOLL_TURBO,
+  MHD_USE_SUSPEND_RESUME,
+  MHD_USE_TCP_FASTOPEN);
+
+{ ---------------------------------------------------------------------
+  libmicrohttp Callbacks
+  ---------------------------------------------------------------------}
+
+procedure DoPanic(cls: Pointer; &file: Pcchar; line: cuint; reason: Pcchar); cdecl;
+
+  Function MaybeS(p : pchar) : String;
+  begin
+    if Assigned(P) then Result:=P else Result:='';
+  end;
+
+begin
+  if Assigned(cls) then
+    TCustomMicroHTTPApplication(Cls).Log(etError,Format('Panic at %s(%d): %s ',[MaybeS(&File),line,MaybeS(reason)]))
+  else if IsConsole then
+    writeln('Panic: File ',MaybeS(&File),'(',line,')',MaybeS(Reason));
+end;
+
+
+function DoReadResponse(cls: pointer; pos: cuint64; buf: Pcchar; max: size_t): ssize_t; cdecl;
+
+Var
+  Resp : TMicroResponse;
+
+begin
+  Resp:=TMicroResponse(cls);
+  if Pos<>Resp.ContentStream.Position then
+    Resp.ContentStream.Position:=Pos;
+  Result:=Resp.ContentStream.Read(buf^,max);
+end;
+
+
+function AcceptCallBack(cls: Pointer; addr: psockaddr; addrlen: socklen_t): cint; cdecl;
+begin
+  Result:=BoolToYesNo[TMicroHTTPHandler(Cls).DoAcceptConnection(addr,addrlen)];
+end;
+
+
+function DoMHDRequest(cls: Pointer; connection: PMHD_Connection; url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
+  upload_data_size: pSize_t; con_cls: PPointer): cint; cdecl;
+
+Var
+  aURL : String;
+  aMethod : String;
+  aVersion : String;
+  H : TMicroHTTPHandler;
+
+begin
+  aURL:=URl;
+  aMethod:=Method;
+  aVersion:=Version;
+  if (Con_cls^=Nil) then
+    begin
+    H:=TMicroHTTPHandler(Cls);
+    Con_cls^:=H.DoRequest(connection,aURL,aMethod,aVersion,Upload_Data,Upload_data_size^);
+    Result:=BoolToYesNo[con_cls^<>Nil];
+    end
+  else
+    Result:=TRequestHandler(Con_cls^).ContinueRequest(Upload_Data,Upload_data_size^);
+end;
+
+procedure HandleRequestCompleted(ACls: Pointer; AConnection: PMHD_Connection; AConCls: PPointer; AToe: MHD_RequestTerminationCode); cdecl;
+var
+  Req: TRequestHandler;
+  H :  TMicroHTTPHandler;
+begin
+  Req:=TRequestHandler(AConCls^);
+  if not Assigned(Req) then
+    Exit;
+  H:=TMicroHTTPHandler(aCls);
+  if not Assigned(H) then
+    H:=Req.WebHandler;
+  if Assigned(H) then
+    H.RequestCompleted(Req)
+  else
+    Req.Free;
+  AConCls^ := nil;
+end;
+
+
+{ ---------------------------------------------------------------------
+  TMicroRequest
+  ---------------------------------------------------------------------}
+
+
+function TMicroRequest.AddData(Data: PAnsiChar; DataSize: Size_t): Size_t;
+
+Var
+  C : String;
+  L : Integer;
+
+begin
+  C:=Content;
+  L:=Length(C);
+  SetLength(C,L+Datasize);
+  Move(Data^,C[L+1],DataSize);
+  InitContent(C);
+  Result:=Datasize;
+end;
+
+procedure TMicroRequest.Initialize(const aUrl, aMethod, aVersion: String);
+
+begin
+  SetHTTPVariable(hvURL,aURL);
+  SetHTTPVariable(hvMethod,aMethod);
+  SetHTTPVariable(hvHTTPVersion,aVersion);
+  InitRequestVars;
+end;
+
+procedure TMicroRequest.InitRequestVars;
+
+Var
+  H : THeader;
+  P : Pchar;
+  N,S  : String;
+  I : integer;
+
+begin
+  S:=URL;
+  I:=Pos('?',S);
+  if (I<>0) then
+    SetHTTPVariable(hvQuery,Copy(S,I+1,Length(S)-I));
+  for H in THeader do
+    if hdRequest in HTTPHeaderDirections[h] then
+      begin
+      P:=MHD_lookup_connection_value(FHandler.FConnection, MHD_HEADER_KIND,Pchar(HeaderName(H)));
+      If P<>Nil then
+        SetHeader(H,P);
+      end;
+  for N in FHandler.WebHandler.ExtraHeaders do
+    begin
+    P:=MHD_lookup_connection_value(FHandler.FConnection, MHD_HEADER_KIND,Pchar(N));
+    If P<>Nil then
+      SetCustomHeader(N,P);
+    end;
+  if (I>0) then
+    begin
+    S:=Copy(S,1,I-1);
+    end;
+  If (Length(S)>1) and (S[1]<>'/') then
+    S:='/'+S
+  else if S='/' then
+    S:='';
+  PathInfo:=S;
+
+  Inherited;
+end;
+
+{ ---------------------------------------------------------------------
+  TMicroResponse
+  ---------------------------------------------------------------------}
+
+procedure TMicroResponse.MaybeAllocateResponse;
+
+Var
+  L : Integer;
+  P : PChar;
+  B : TBytes;
+
+begin
+  if FResponse<>Nil then exit;
+  if Assigned(ContentStream) then
+    begin
+    ContentStream.Position:=0;
+    L:=ContentStream.Size;
+    if FreeContentStream then
+      FResponse:=MHD_create_response_from_callback(L,4096,@DoReadResponse,Self,Nil)
+    else
+      // We must copy the bytes, because we don't know when the stream is freed.
+      begin
+      SetLength(B,L);
+      ContentStream.ReadBuffer(B[0],L);
+      P:=Pchar(B);
+      FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
+      end;
+    end
+  else
+    begin
+    L:=Length(Content);
+    P:=PChar(Content);
+    FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
+    end;
+end;
+
+procedure TMicroResponse.DoSendHeaders(Headers: TStrings);
+
+Var
+  I : Integer;
+  N,V : String;
+
+begin
+  // Note that if the response is allocated, then you cannot set the content stream any more...
+  MaybeAllocateResponse;
+  For I:=0 to Headers.Count-1 do
+    begin
+    Headers.GetNameValue(I,N,V);
+    MHD_add_response_header(FResponse,PAnsiChar(N),PAnsiChar(V));
+    end;
+end;
+
+procedure TMicroResponse.DoSendContent;
+begin
+  MaybeAllocateResponse;
+  MHD_queue_response(FHandler.FConnection,Self.Code,FResponse);
+end;
+
+destructor TMicroResponse.Destroy;
+begin
+  if (FResponse<>Nil) then
+    MHD_destroy_response(FResponse);
+  inherited Destroy;
+end;
+
+{ ---------------------------------------------------------------------
+  TRequestHandler
+  ---------------------------------------------------------------------}
+
+constructor TRequestHandler.Create(aHandler: TMicroHTTPHandler; aConnection: PMHD_Connection);
+begin
+  FWebHandler:=aHandler;
+  FConnection:=aConnection;
+  FRequest:=TMicroRequest.Create;
+  FRequest.FHandler:=Self;
+  FResponse:=TMicroResponse.Create(FRequest);
+  FResponse.FHandler:=Self;
+end;
+
+destructor TRequestHandler.Destroy;
+begin
+  FreeAndNil(FRequest);
+  FreeAndNil(FResponse);
+  inherited Destroy;
+end;
+
+procedure TRequestHandler.Initialize(const aUrl, aMethod, aVersion: String);
+begin
+  FRequest.Initialize(aURL,aMethod,aVersion);
+end;
+
+function TRequestHandler.ContinueRequest(Data: PAnsiChar; var DataSize: Size_t): Cint;
+
+Var
+  CanHandleRequest : Boolean;
+
+begin
+  CanHandleRequest:=Datasize=0;
+  if Datasize>0 then
+    DataSize:=DataSize-FRequest.AddData(Data,Datasize);
+  If Not CanHandleRequest then
+    Result:=BoolToYesNo[DataSize=0]
+  else
+    begin
+    try
+      WebHandler.HandleRequest(FRequest,FResponse);
+      If Not FResponse.ContentSent then
+        try
+          FResponse.SendContent;
+        except
+          On E : Exception do
+            WebHandler.HandleRequestError(WebHandler,E);
+        end;
+      Result:=MHD_YES;
+    except
+      On E : Exception do
+        begin
+        Result:=MHD_NO;
+        WebHandler.HandleRequestError(WebHandler,E);
+        end;
+    end;
+    end;
+end;
+
+
+{ ---------------------------------------------------------------------
+  TMicroHTTPHandler
+  ---------------------------------------------------------------------}
+
+procedure TMicroHTTPHandler.RequestCompleted(aRequest: TRequestHandler);
+
+begin
+  try
+    EndRequest(aRequest.FRequest,aRequest.FResponse);
+    aRequest.FRequest:=Nil;
+    aRequest.FResponse:=Nil;
+    aRequest.Free;
+  except
+    On E: Exception do
+      HandleRequestError(Self,E);
+  end;
+end;
+
+function TMicroHTTPHandler.DoRequest(connection: PMHD_Connection; Const aUrl,aMethod,aVersion: String; Data: PAnsiChar; var DataSize: Size_t) : TRequestHandler;
+
+begin
+  Result:=TRequestHandler.Create(Self,Connection);
+  Result.Initialize(aURl,aMethod,AVersion);
+  if (DataSize>0) then
+    if Result.ContinueRequest(Data,Datasize)<>MHD_YES then
+      FreeAndNil(Result);
+end;
+
+procedure TMicroHTTPHandler.SetExtraHeaders(AValue: TStrings);
+begin
+  if FExtraHeaders=AValue then Exit;
+  FExtraHeaders.Assign(AValue);
+end;
+
+procedure TMicroHTTPHandler.HandleRequestError(Sender: TObject; E: Exception);
+begin
+  Try
+    If Assigned(FOnRequestError) then
+      FOnRequestError(Sender,E)
+    else
+      Log(etError,Format('Error (%s) handling request : %s',[E.ClassName,E.Message]));
+  except
+    // Do not let errors escape
+  end;
+end;
+
+procedure TMicroHTTPHandler.CheckInactive;
+
+begin
+  if Assigned(FServer) then
+    Raise EHTTP.Create(SErrServerActive);
+end;
+
+procedure TMicroHTTPHandler.SetHostName(AValue: String);
+begin
+  CheckInactive;
+  FHostName:=aValue;
+end;
+
+
+procedure TMicroHTTPHandler.SetOptions(AValue: TMicroServerOptions);
+begin
+  if FOptions=AValue then Exit;
+  CheckInactive;
+  FOptions:=AValue;
+end;
+
+procedure TMicroHTTPHandler.SetPort(const AValue: Word);
+begin
+  CheckInactive;
+  FPort:=Avalue
+end;
+
+procedure TMicroHTTPHandler.InitRequest(ARequest: TRequest);
+begin
+  inherited InitRequest(ARequest);
+end;
+
+procedure TMicroHTTPHandler.InitResponse(AResponse: TResponse);
+begin
+  inherited InitResponse(AResponse);
+end;
+
+function TMicroHTTPHandler.WaitForRequest(out ARequest: TRequest;
+  out AResponse: TResponse): boolean;
+begin
+  Result:=False;
+  ARequest:=Nil;
+  AResponse:=Nil;
+end;
+
+Function TMicroHTTPHandler.DoAcceptConnection(Addr : PSockAddr; addrLen : socklen_t) : Boolean;
+
+begin
+  Result:=True;
+  if Assigned(FAcceptHandler) then
+    FAcceptHandler(Self,Addr,addrLen,Result);
+end;
+
+
+function TMicroHTTPHandler.OptionsToFlags : Integer;
+
+Var
+  O : TMicroServerOption;
+
+begin
+  Result:=0;
+  For O in TMicroServerOption do
+    if O in Options then
+      Result:=Result or OptionFlags[O];
+end;
+
+
+function TMicroHTTPHandler.CreateServer: PMHD_Daemon;
+
+Var
+  F,P : Integer;
+
+begin
+  F:=OptionsToFlags or MHD_USE_DEBUG;
+  P:=Port;
+  Result:= MHD_start_daemon(F,P,
+    @AcceptCallBack, Self,
+    @DoMHDRequest, Self,
+    MHD_OPTION_NOTIFY_COMPLETED, @HandleRequestCompleted,
+    Nil,MHD_OPTION_END);
+  if Result=Nil then
+    Writeln('a')
+  else
+    Writeln('b');
+end;
+
+procedure TMicroHTTPHandler.Run;
+begin
+  FServer:=CreateServer;
+  if (FServer=Nil) then
+    Raise EHTTP.Create(SErrFailedToStartServer);
+  Repeat
+    Sleep(50);
+  Until Terminated;
+end;
+
+procedure TMicroHTTPHandler.MaybeStopServer;
+
+begin
+  if Assigned(FServer) then
+    begin
+    MHD_stop_daemon(FServer);
+    FServer:=Nil;
+    end;
+end;
+
+constructor TMicroHTTPHandler.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FExtraHeaders:=TStringList.Create;
+  Options:=[mcoSelectInternally];
+  Port:=80;
+end;
+
+destructor TMicroHTTPHandler.Destroy;
+begin
+  MaybeStopServer;
+  FreeAndNil(FExtraHeaders);
+  inherited Destroy;
+end;
+
+
+{ ---------------------------------------------------------------------
+  TCustomMicroHTTPApplication
+  ---------------------------------------------------------------------}
+
+
+procedure TCustomMicroHTTPApplication.SetHostName(AValue: String);
+begin
+  HTTPHandler.HostName:=aValue;
+end;
+
+procedure TCustomMicroHTTPApplication.SetOptions(AValue: TMicroServerOptions);
+begin
+  HTTPHandler.Options:=aValue;
+end;
+
+procedure TCustomMicroHTTPApplication.SetPort(AValue: Word);
+begin
+  HTTPHandler.Port:=aValue;
+end;
+
+procedure TCustomMicroHTTPApplication.SetUSeSSL(AValue: Boolean);
+begin
+  if AValue then
+    Options:=Options+[mcoSSL]
+  else
+    Options:=Options-[mcoSSL]
+end;
+
+function TCustomMicroHTTPApplication.GetPort: Word;
+begin
+  Result:=HTTPHandler.Port;
+end;
+
+function TCustomMicroHTTPApplication.GetUseSSL: Boolean;
+begin
+  Result:=mcoSSL in Options;
+end;
+
+procedure TCustomMicroHTTPApplication.SetExtraHeaders(AValue: TStrings);
+begin
+  HTTPHandler.ExtraHeaders.Assign(AValue);
+end;
+
+function TCustomMicroHTTPApplication.InitializeWebHandler: TWebHandler;
+begin
+  Result:=TMicroHTTPHandler.Create(Self);
+end;
+
+function TCustomMicroHTTPApplication.HTTPHandler: TMicroHTTPHandler;
+begin
+  Result:=Webhandler as TMicroHTTPHandler;
+end;
+
+constructor TCustomMicroHTTPApplication.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  MHD_set_panic_func(@DoPanic,Self);
+end;
+
+destructor TCustomMicroHTTPApplication.Destroy;
+begin
+  MHD_set_panic_func(@DoPanic,Nil);
+  inherited Destroy;
+end;
+
+function TCustomMicroHTTPApplication.GetExtraHeaders: TStrings;
+begin
+  Result:=HTTPHandler.ExtraHeaders;
+end;
+
+function TCustomMicroHTTPApplication.GetHostName: String;
+begin
+  Result:=HTTPHandler.HostName;
+end;
+
+function TCustomMicroHTTPApplication.GetOptions: TMicroServerOptions;
+begin
+  Result:=HTTPHandler.Options;
+end;
+
+
+end.

+ 65 - 0
packages/fcl-web/src/base/microhttpapp.pp

@@ -0,0 +1,65 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2019 by the Free Pascal development team
+
+    TMicroHTTPApplication class.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$mode objfpc}
+{$H+}
+unit microhttpapp;
+
+interface
+
+uses SysUtils,Classes, custmicrohttpapp;
+
+Type
+
+  { TCGIApplication }
+
+  TMicroHTTPApplication = Class(TCustomMicroHTTPApplication)
+  end;
+
+Var
+  Application : TMicroHTTPApplication;
+  ShowCleanUpErrors : Boolean = False;
+  
+Implementation
+
+uses CustApp;
+
+Procedure InitHTTP;
+
+begin
+  Application:=TMicroHTTPApplication.Create(Nil);
+  if not assigned(CustomApplication) then
+    CustomApplication := Application;
+end;
+
+Procedure DoneHTTP;
+
+begin
+  if CustomApplication=Application then
+    CustomApplication := nil;
+  try  
+    FreeAndNil(Application);
+  except
+    if ShowCleanUpErrors then
+      Raise;
+  end;
+end;
+
+Initialization
+  InitHTTP;
+  
+Finalization
+  DoneHTTP;
+  
+end.