Browse Source

* Adapt fcl-web (and examples) to unicode rtl: Need widestringmanager, use echo as path, make runnable.

Michael VAN CANNEYT 2 years ago
parent
commit
5cfaac64d5
30 changed files with 610 additions and 254 deletions
  1. 16 24
      packages/fcl-web/examples/echo/apache/echo.lpi
  2. 10 3
      packages/fcl-web/examples/echo/apache/echo.lpr
  3. 28 17
      packages/fcl-web/examples/echo/cgi/echo.lpi
  4. 4 0
      packages/fcl-web/examples/echo/cgi/echo.lpr
  5. 16 25
      packages/fcl-web/examples/echo/fcgi/echo.lpi
  6. 8 1
      packages/fcl-web/examples/echo/fcgi/echo.lpr
  7. BIN
      packages/fcl-web/examples/echo/fcgi/echo.res
  8. 1 1
      packages/fcl-web/examples/echo/webmodule/wmecho.pas
  9. 7 0
      packages/fcl-web/examples/fptemplate/simpletemplate/cgi/simpletemplate.lpr
  10. 6 0
      packages/fcl-web/examples/fptemplate/simpletemplate/webmodule/webmodule.lfm
  11. 10 4
      packages/fcl-web/examples/fptemplate/simpletemplate/webmodule/webmodule.pas
  12. 6 0
      packages/fcl-web/examples/httpclient/httpget.pas
  13. 6 1
      packages/fcl-web/examples/httpclient/httppost.pp
  14. 5 1
      packages/fcl-web/examples/httpclient/httppostfile.pp
  15. 5 6
      packages/fcl-web/examples/simpleserver/simpleserver.pas
  16. 88 12
      packages/fcl-web/src/base/custapache.pp
  17. 131 69
      packages/fcl-web/src/base/custapache24.pp
  18. 108 37
      packages/fcl-web/src/base/custfcgi.pp
  19. 81 20
      packages/fcl-web/src/base/custmicrohttpapp.pp
  20. 7 7
      packages/fcl-web/src/base/fcgigate.pp
  21. 11 3
      packages/fcl-web/src/base/fpapache.pp
  22. 12 4
      packages/fcl-web/src/base/fpapache24.pp
  23. 4 5
      packages/fcl-web/src/base/fphttpserver.pp
  24. 4 0
      packages/fcl-web/src/base/fpweb.pp
  25. 20 7
      packages/fcl-web/src/base/httpdefs.pp
  26. 10 2
      packages/fcl-web/src/jwt/fpjwaes256.pp
  27. 2 2
      packages/fcl-web/src/jwt/fpjwt.pp
  28. 1 1
      packages/fcl-web/src/restbridge/sqldbrestbridge.pp
  29. 1 1
      packages/fcl-web/src/restbridge/sqldbrestschema.pp
  30. 2 1
      packages/libmicrohttpd/src/libmicrohttpd.pp

+ 16 - 24
packages/fcl-web/examples/echo/apache/echo.lpi

@@ -1,76 +1,68 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="12"/>
     <PathDelim Value="\"/>
     <General>
       <Flags>
         <SaveOnlyProjectUnits Value="True"/>
         <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
         <Runnable Value="False"/>
+        <CompatibilityMode Value="True"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>
       <ResourceType Value="res"/>
       <UseXPManifest Value="True"/>
     </General>
     <i18n>
       <EnableI18N LFM="False"/>
     </i18n>
-    <VersionInfo>
-      <StringTable ProductVersion=""/>
-    </VersionInfo>
     <BuildModes Count="1">
       <Item1 Name="Default" Default="True"/>
     </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
-      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
-        <LaunchingApplication PathPlusParams="\usr\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
+        <LaunchingApplication PathPlusParams="\usr\bin\xterm -T &apos;Lazarus Run Output&apos; -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <LaunchingApplication PathPlusParams="\usr\bin\xterm -T &apos;Lazarus Run Output&apos; -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
-    <RequiredPackages Count="1">
-      <Item1>
-        <PackageName Value="WebLaz"/>
-      </Item1>
-    </RequiredPackages>
     <Units Count="2">
       <Unit0>
         <Filename Value="echo.lpr"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="echo"/>
       </Unit0>
       <Unit1>
         <Filename Value="..\webmodule\wmecho.pas"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="EchoModule"/>
         <ResourceBaseClass Value="DataModule"/>
-        <UnitName Value="wmecho"/>
       </Unit1>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="..\webmodule;..\..\..\src\base"/>
     </SearchPaths>
     <Linking>
       <Options>
         <ExecutableType Value="Library"/>
       </Options>
     </Linking>
-    <Other>
-      <CompilerMessages>
-        <UseMsgFile Value="True"/>
-      </CompilerMessages>
-      <CompilerPath Value="$(CompPath)"/>
-    </Other>
   </CompilerOptions>
   <Debugging>
     <Exceptions Count="3">

+ 10 - 3
packages/fcl-web/examples/echo/apache/echo.lpr

@@ -2,18 +2,25 @@ Library echo;
 
 {$mode objfpc}{$H+}
 
+{ $define use_apache22}
+
 Uses
 {$ifdef unix}
-  cthreads,
+  cthreads, cwstring,
+{$endif}
+{$ifdef use_apache22}
+  httpd,fpApache,
+{$else}
+  httpd24,fpApache24,
 {$endif}
-  httpd,fpApache, wmecho;
+  wmecho;
 
 Const
 
 { The following constant is used to export the module record. It must 
   always match the name in the LoadModule statement in the apache
   configuration file(s). It is case sensitive !}
-  ModuleName='mod_echo';
+  ModuleName='mod_fpcecho';
 
 { The following constant is used to determine whether the module will
   handle a request. It should match the name in the SetHandler statement

+ 28 - 17
packages/fcl-web/examples/echo/cgi/echo.lpi

@@ -1,43 +1,50 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="12"/>
     <PathDelim Value="\"/>
     <General>
       <Flags>
         <SaveOnlyProjectUnits Value="True"/>
         <MainUnitHasCreateFormStatements Value="False"/>
-        <Runnable Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <CompatibilityMode Value="True"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>
       <ResourceType Value="res"/>
       <UseXPManifest Value="True"/>
     </General>
     <i18n>
       <EnableI18N LFM="False"/>
     </i18n>
-    <VersionInfo>
-      <StringTable ProductVersion=""/>
-    </VersionInfo>
     <BuildModes Count="1">
       <Item1 Name="Default" Default="True"/>
     </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
-      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
     </PublishOptions>
     <RunParams>
-      <local>
-        <FormatVersion Value="1"/>
-      </local>
+      <environment>
+        <UserOverrides Count="3">
+          <Variable0 Name="REQUEST_METHOD" Value="GET"/>
+          <Variable1 Name="QUERY_STRING" Value="me=13&amp;you=12&amp;them=SOSO"/>
+          <Variable2 Name="PATH_INFO" Value="echo"/>
+        </UserOverrides>
+      </environment>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <environment>
+            <UserOverrides Count="3">
+              <Variable0 Name="REQUEST_METHOD" Value="GET"/>
+              <Variable1 Name="QUERY_STRING" Value="me=13&amp;you=12&amp;them=SOSO"/>
+              <Variable2 Name="PATH_INFO" Value="echo"/>
+            </UserOverrides>
+          </environment>
+        </Mode0>
+      </Modes>
     </RunParams>
-    <RequiredPackages Count="1">
-      <Item1>
-        <PackageName Value="WebLaz"/>
-      </Item1>
-    </RequiredPackages>
     <Units Count="2">
       <Unit0>
         <Filename Value="echo.lpr"/>
@@ -47,6 +54,7 @@
         <Filename Value="..\webmodule\wmecho.pas"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="EchoModule"/>
+        <HasResources Value="True"/>
         <ResourceBaseClass Value="DataModule"/>
       </Unit1>
     </Units>
@@ -54,9 +62,12 @@
   <CompilerOptions>
     <Version Value="11"/>
     <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="echo.cgi" ApplyConventions="False"/>
+    </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="..\webmodule"/>
+      <OtherUnitFiles Value="..\webmodule;..\..\..\src\base"/>
     </SearchPaths>
   </CompilerOptions>
   <Debugging>

+ 4 - 0
packages/fcl-web/examples/echo/cgi/echo.lpr

@@ -3,12 +3,16 @@ program echo;
 {$mode objfpc}{$H+}
 
 uses
+{$ifdef unix}
+  cwstring,
+{$endif}
   fpCGI, wmecho;
 
 {$R *.res}
 
 begin
   Application.Initialize;
+  Application.Title:='Echo demo';
   Application.Run;
 end.
 

+ 16 - 25
packages/fcl-web/examples/echo/fcgi/echo.lpi

@@ -1,71 +1,62 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="12"/>
     <PathDelim Value="\"/>
     <General>
       <Flags>
         <SaveOnlyProjectUnits Value="True"/>
         <MainUnitHasCreateFormStatements Value="False"/>
-        <Runnable Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <CompatibilityMode Value="True"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>
       <ResourceType Value="res"/>
       <UseXPManifest Value="True"/>
     </General>
     <i18n>
       <EnableI18N LFM="False"/>
     </i18n>
-    <VersionInfo>
-      <StringTable ProductVersion=""/>
-    </VersionInfo>
     <BuildModes Count="1">
       <Item1 Name="Default" Default="True"/>
     </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
-      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
-        <LaunchingApplication PathPlusParams="\usr\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
+        <LaunchingApplication PathPlusParams="\usr\bin\xterm -T &apos;Lazarus Run Output&apos; -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <LaunchingApplication PathPlusParams="\usr\bin\xterm -T &apos;Lazarus Run Output&apos; -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
-    <RequiredPackages Count="1">
-      <Item1>
-        <PackageName Value="WebLaz"/>
-      </Item1>
-    </RequiredPackages>
     <Units Count="2">
       <Unit0>
         <Filename Value="echo.lpr"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="echo"/>
       </Unit0>
       <Unit1>
         <Filename Value="..\webmodule\wmecho.pas"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="EchoModule"/>
         <ResourceBaseClass Value="DataModule"/>
-        <UnitName Value="wmecho"/>
       </Unit1>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="..\webmodule;..\..\..\src\base"/>
     </SearchPaths>
-    <Other>
-      <CompilerMessages>
-        <UseMsgFile Value="True"/>
-      </CompilerMessages>
-      <CompilerPath Value="$(CompPath)"/>
-    </Other>
   </CompilerOptions>
   <Debugging>
     <Exceptions Count="3">

+ 8 - 1
packages/fcl-web/examples/echo/fcgi/echo.lpr

@@ -3,12 +3,19 @@ program echo;
 {$mode objfpc}{$H+}
 
 uses
-  fpFCGI, wmecho;
+  {$ifdef unix}
+  fpwidestring, unicodeducet,
+  {$endif}
+  fpFCGI, custfcgi, wmecho;
 
 {$R *.res}
 
 begin
+  {$IFDEF UNIX}
+  SetActiveCollation('DUCET');
+  {$ENDIF}
   Application.Port:=2015;//Port the FCGI application is listening on
+  Application.PathInfoHandling:=pihLastScriptComponent; // Assume url is of form /scriptname/echo
   Application.Initialize;
   Application.Run;
 end.

BIN
packages/fcl-web/examples/echo/fcgi/echo.res


+ 1 - 1
packages/fcl-web/examples/echo/webmodule/wmecho.pas

@@ -48,6 +48,6 @@ begin
 end;
 
 initialization
-  RegisterHTTPModule('TEchoModule', TEchoModule);
+  RegisterHTTPModule('Echo', TEchoModule);
 end.
 

+ 7 - 0
packages/fcl-web/examples/fptemplate/simpletemplate/cgi/simpletemplate.lpr

@@ -3,11 +3,18 @@ program simpletemplate;
 {$mode objfpc}{$H+}
 
 uses
+  {$ifdef unix}
+  fpwidestring, unicodeducet,
+  {$endif}
+
   fpCGI, webmodule;
 
 {$R *.res}
 
 begin
+  {$ifdef unix}
+  SetActiveCollation('DUCET');
+  {$endif}
   Application.Initialize;
   Application.Run;
 end.

+ 6 - 0
packages/fcl-web/examples/fptemplate/simpletemplate/webmodule/webmodule.lfm

@@ -8,6 +8,12 @@ object FPWebModule1: TFPWebModule1
       Template.AllowTagParams = False
     end>
   CreateSession = False
+  CORS.Enabled = False
+  CORS.Options = [coAllowCredentials, coEmptyDomainToOrigin]
+  CORS.AllowedMethods = 'GET, PUT, POST, OPTIONS, HEAD'
+  CORS.AllowedOrigins = '*'
+  CORS.AllowedHeaders = 'x-requested-with, content-type, authorization'
+  CORS.MaxAge = 0
   Height = 300
   HorizontalOffset = 290
   VerticalOffset = 149

+ 10 - 4
packages/fcl-web/examples/fptemplate/simpletemplate/webmodule/webmodule.pas

@@ -33,16 +33,22 @@ implementation
 
 procedure TFPWebModule1.func1callRequest(Sender: TObject; ARequest: TRequest;
   AResponse: TResponse; var Handled: Boolean);
+var
+  S : String;
 begin
   //ModuleTemplate is a web module global property
   //To use the Template propery of the current web action (which is visible in
   //the object inspector for every Action), use
   //(Sender as TFPWebAction).Template.FileName := 'mytemplate1.html'; and so on.
-  ModuleTemplate.FileName := 'mytemplate1.html';//best to use full path here
+  ModuleTemplate.FileName := ExtractFilePath(paramstr(0))+'../templates/mytemplate1.html';//best to use full path here
   ModuleTemplate.AllowTagParams := true;
   ModuleTemplate.OnReplaceTag := @func1callReplaceTag;
-
-  AResponse.Content := ModuleTemplate.GetContent;
+  S:=ModuleTemplate.GetContent;
+  {$IF SIZEOF(CHAR)=2}
+  AResponse.Content := UTF8Encode(S);
+  {$ELSE}
+  AResponse.Content := S;
+  {$ENDIF}
 
   Handled := true;
 end;
@@ -61,5 +67,5 @@ begin
 end;
 
 initialization
-  RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
+  RegisterHTTPModule('template', TFPWebModule1);
 end.

+ 6 - 0
packages/fcl-web/examples/httpclient/httpget.pas

@@ -4,6 +4,9 @@ program httpget;
 {$DEFINE USEGNUTLS}
 
 uses
+  {$IFDEF UNIX}
+  fpwidestring, unicodeducet,
+  {$ENDIF}
   SysUtils, Classes, fphttpclient, ssockets,
 {$IFNDEF USEGNUTLS}
   fpopenssl, opensslsockets,
@@ -141,6 +144,9 @@ begin
 end;
 
 begin
+  {$IFDEF UNIX}
+  SetActiveCollation('DUCET');
+  {$ENDIF}
   With TTestApp.Create do
     try
       Run;

+ 6 - 1
packages/fcl-web/examples/httpclient/httppost.pp

@@ -9,6 +9,8 @@ Var
   F : TFileStream;
   Vars : TStrings;
   i : integer;
+  Fmt : UNicodeString;
+
 begin
   With TFPHTTPClient.Create(Nil) do
     begin
@@ -17,7 +19,10 @@ begin
       Vars:=TstringList.Create;
       try
         For i:=1 to 10 do
-          Vars.Add(Format('Var%d=Value %d',[i,i]));
+          begin
+          Fmt:='Var%d=Value %d';
+          Vars.Add(Format(Fmt,[i,i]));
+          end;
         FormPost(ParamStr(1),vars,f);
       finally
         Vars.Free;

+ 5 - 1
packages/fcl-web/examples/httpclient/httppostfile.pp

@@ -1,14 +1,18 @@
 program httppostfile;
 
+
 {$mode objfpc}{$H+}
 
 uses
+  {$ifdef unix}
+  cwstring,
+  {$endif}
   SysUtils, Classes, fphttpclient, opensslsockets;
 
 Var
   F : TFileStream;
   Vars : TStrings;
-  i : integer;
+
 begin
   With TFPHTTPClient.Create(Nil) do
     begin

+ 5 - 6
packages/fcl-web/examples/simpleserver/simpleserver.pas

@@ -25,9 +25,10 @@ program simpleserver;
 {$ENDIF}
 
 uses
-
-
-
+{$IFDEF UNIX}
+  cwstring,
+  cthreads,
+{$ENDIF}
 {$IFNDEF USEMICROHTTP}
 {$ifdef USEGNUTLS}
   gnutlssockets,
@@ -36,9 +37,6 @@ uses
 {$endif}
   custhttpapp,
 {$ELSE}
-{$ifdef unix}
-  cthreads,
-{$endif}  
   custmicrohttpapp,
 {$ENDIF}
   {$ifdef unix}
@@ -140,6 +138,7 @@ begin
     DumpRequest(aRequest,L);
     L.AddStrings(['</body>','</html>']);
     AResponse.Content:=L.Text;
+    AResponse.ContentLength:=Length(AResponse.Content);
     AResponse.SendResponse;
   finally
     L.Free;

+ 88 - 12
packages/fcl-web/src/base/custapache.pp

@@ -32,6 +32,9 @@ Type
     FApache : TApacheHandler;
     FRequest : PRequest_rec;
   Protected
+    function GetApacheHeaderValue(H: THeader): String;
+    function GetApacheVariableValue(V: THTTPVariableType): String;
+    procedure initrequestvars; override;
     Procedure InitFromRequest;
     procedure ReadContent; override;
   Public
@@ -165,7 +168,7 @@ const
   HPRIO : Array[THandlerPriority] of Integer
         = (APR_HOOK_FIRST,APR_HOOK_MIDDLE,APR_HOOK_LAST);
 
-Function MaybeP(P : Pchar) : String;
+Function MaybeP(P : PAnsiChar) : String;
 
 begin
   If (P<>Nil) then
@@ -188,7 +191,7 @@ Procedure RegisterApacheHooks(P: PApr_pool_t);cdecl;
 
 Var
   H : ap_hook_handler_t;
-  PP1,PP2 : PPChar;
+  PP1,PP2 : PPAnsiChar;
 
 begin
   H:=AlternateHandler;
@@ -313,13 +316,13 @@ begin
     Raise EFPApacheError.Create(SErrNoModuleName);
   STANDARD20_MODULE_STUFF(FModuleRecord^);
   If (StrPas(FModuleRecord^.name)<>FModuleName) then
-    FModuleRecord^.Name:=PChar(FModuleName);
+    FModuleRecord^.Name:=PAnsiChar(FModuleName);
   FModuleRecord^.register_hooks:=@RegisterApacheHooks;
 end;
 
 procedure TApacheHandler.LogErrorMessage(const Msg: String; LogLevel: integer);
 begin
-  ap_log_error(pchar(FModuleName),0,LogLevel,0,Nil,'module: %s',[pchar(Msg)]);
+  ap_log_error(PAnsiChar(FModuleName),0,LogLevel,0,Nil,'module: %s',[PAnsiChar(Msg)]);
 end;
 
 function TApacheHandler.GetIdleModuleCount : Integer;
@@ -432,7 +435,7 @@ procedure TApacheRequest.ReadContent;
 
 Var
   Left,Len,Count,Bytes : Integer;
-  P : Pchar;
+  P : PAnsiChar;
   S : String;
 
 begin
@@ -443,7 +446,7 @@ begin
     If (Len>0) then
       begin
       SetLength(S,Len);
-      P:=PChar(S);
+      P:=PAnsiChar(S);
       Left:=Len;
       Count:=0;
       Repeat
@@ -458,6 +461,79 @@ begin
   InitContent(S);
 end;
 
+function TApacheRequest.GetApacheHeaderValue(H: THeader): String;
+
+var
+  FN : AnsiString;
+  I : Integer;
+  S : String;
+
+begin
+  Result:='';
+  Str(H,S);
+  If Not Assigned(FRequest) then
+    exit;
+  Case h of
+    hhContentEncoding:
+      Result:=MaybeP(FRequest^.content_encoding);
+    hhHost:
+      Result:=MaybeP(FRequest^.HostName);
+  else
+    FN:=HeaderName(H);
+    Result:=MaybeP(apr_table_get(FRequest^.headers_in,PAnsiChar(FN)));
+  end;
+end;
+
+function TApacheRequest.GetApacheVariableValue(V: THTTPVariableType): String;
+var
+  i : integer;
+
+begin
+  Result:='';
+  if not Assigned(FRequest) then
+    exit;
+  case V of
+    hvHTTPVersion:
+      Result:=MaybeP(FRequest^.protocol); // ProtocolVersion
+    hvPathInfo:
+      Result:=MaybeP(FRequest^.path_info); // PathInfo
+    hvPathTranslated:
+      Result:=MaybeP(FRequest^.filename); // PathTranslated
+    hvRemoteAddress :
+      If (FRequest^.Connection<>Nil) then
+        Result:=MaybeP(FRequest^.Connection^.remote_ip);
+    hvRemoteHost:
+      If (FRequest^.Connection<>Nil) then
+        begin
+        Result:=MaybeP(ap_get_remote_host(FRequest^.Connection,
+                       FRequest^.per_dir_config,
+//                     nil,
+                       REMOTE_NAME,@i));
+        end;
+    hvScriptName:
+      begin // ScriptName
+      Result:=MaybeP(FRequest^.unparsed_uri);
+      I:=Pos('?',Result)-1;
+      If (I=-1) then
+       I:=Length(Result);
+      Result:=Copy(Result,1,I-Length(PathInfo));
+      end;
+    hvServerPort:
+      Result:=IntToStr(ap_get_server_port(FRequest)); // ServerPort
+    hvMethod:
+      Result:=MaybeP(FRequest^.method); // Method
+    hvURL:
+      Result:=MaybeP(FRequest^.unparsed_uri); // URL
+    hvQuery:
+      Result:=MaybeP(FRequest^.args); // Query
+    end;
+end;
+
+procedure TApacheRequest.initrequestvars;
+begin
+  inherited initrequestvars;
+end;
+
 procedure TApacheRequest.InitFromRequest;
 
 
@@ -513,7 +589,7 @@ function TApacheRequest.GetCustomHeader(const Name: String): String;
 begin
   Result:=inherited GetCustomHeader(Name);
   if Result='' then
-    Result:=MaybeP(apr_table_get(FRequest^.headers_in,pchar(Name)));
+    Result:=MaybeP(apr_table_get(FRequest^.headers_in,PAnsiChar(Name)));
 end;
 
 { TApacheResponse }
@@ -534,7 +610,7 @@ begin
       N:=Copy(V,1,P-1);
       System.Delete(V,1,P);
       V := Trim(V);//no need space before the value, apache puts it there
-      apr_table_set(FRequest^.headers_out,Pchar(N),Pchar(V));
+      apr_table_set(FRequest^.headers_out,PAnsiChar(N),PAnsiChar(V));
       end;
     end;
 end;
@@ -548,10 +624,10 @@ Var
 begin
   S:=ContentType;
   If (S<>'') then
-    FRequest^.content_type:=apr_pstrdup(FRequest^.pool,Pchar(S));
+    FRequest^.content_type:=apr_pstrdup(FRequest^.pool,PAnsiChar(S));
   S:=ContentEncoding;
   If (S<>'') then
-    FRequest^.content_encoding:=apr_pstrdup(FRequest^.pool,Pchar(S));
+    FRequest^.content_encoding:=apr_pstrdup(FRequest^.pool,PAnsiChar(S));
   If Code <> 200 then
     FRequest^.status := Code;
   If assigned(ContentStream) then
@@ -561,7 +637,7 @@ begin
       begin
       S:=Contents[i]+LineEnding;
       // If there is a null, it's written also with ap_rwrite
-      ap_rwrite(PChar(S),Length(S),FRequest);
+      ap_rwrite(PAnsiChar(S),Length(S),FRequest);
       end;
 end;
 
@@ -699,7 +775,7 @@ end;
 
 procedure TCustomApacheApplication.ShowException(E: Exception);
 begin
-  ap_log_error(pchar(TApacheHandler(WebHandler).ModuleName),0,APLOG_ERR,0,Nil,'module: %s',[Pchar(E.Message)]);
+  ap_log_error(PAnsiChar(TApacheHandler(WebHandler).ModuleName),0,APLOG_ERR,0,Nil,'module: %s',[PAnsiChar(E.Message)]);
 end;
 
 function TCustomApacheApplication.ProcessRequest(P: PRequest_Rec): Integer;

+ 131 - 69
packages/fcl-web/src/base/custapache24.pp

@@ -20,7 +20,7 @@ unit custapache24;
 interface
 
 uses
-  SysUtils, Classes, CustWeb, httpDefs, fpHTTP, httpd24, apr24, SyncObjs;
+  SysUtils, Classes, CustWeb, httpDefs, fpHTTP, httpd24, apr24, SyncObjs, httpprotocol;
 
 Type
 
@@ -33,8 +33,10 @@ Type
     FApache : TApacheHandler;
     FRequest : PRequest_rec;
   Protected
-    Function GetFieldValue(Index : Integer) : String; override;
+    function GetApacheHeaderValue(H: THeader): String;
+    function GetApacheVariableValue(V: THTTPVariableType): String;
     Procedure InitFromRequest;
+    procedure initrequestvars; override;
     procedure ReadContent; override;
   Public
     Constructor CreateReq(App : TApacheHandler; ARequest : PRequest_rec);
@@ -153,6 +155,7 @@ Var
 
 
 implementation
+
 uses CustApp;
 
 resourcestring
@@ -162,6 +165,18 @@ resourcestring
   SErrNoModuleName = 'No module name set';
   SErrTooManyRequests = 'Too many simultaneous requests.';
 
+
+Function MaybeAnsi(S : String) : AnsiString; inline;
+
+begin
+{$IF SIZEOF(CHAR)=1}
+  Result:=S;
+{$ELSE}
+  Result:=UTF8Encode(S);
+{$ENDIF}
+end;
+
+
 const
   HPRIO : Array[THandlerPriority] of Integer
         = (APR_HOOK_FIRST,APR_HOOK_MIDDLE,APR_HOOK_LAST);
@@ -184,7 +199,7 @@ Procedure RegisterApacheHooks(P: PApr_pool_t);cdecl;
 
 Var
   H : ap_hook_handler_t;
-  PP1,PP2 : PPChar;
+  PP1,PP2 : PPAnsiChar;
 
 begin
   H:=AlternateHandler;
@@ -309,21 +324,21 @@ begin
     Raise EFPApacheError.Create(SErrNoModuleName);
   STANDARD20_MODULE_STUFF(FModuleRecord^);
   If (StrPas(FModuleRecord^.name)<>FModuleName) then
-    FModuleRecord^.Name:=PChar(FModuleName);
+    FModuleRecord^.Name:=PAnsiChar(FModuleName);
   FModuleRecord^.register_hooks:=@RegisterApacheHooks;
 end;
 
 procedure TApacheHandler.LogErrorMessage(const Msg: String; LogLevel: integer);
 var a: ap_version_t;
 begin
-  ap_log_error(pchar(FModuleName),  //The file in which this function is called
+  ap_log_error(PAnsiChar(FModuleName),  //The file in which this function is called
                0,                   //The line number on which this function is called
                0,                   //The module_index of the module generating this message
                LogLevel,            //The level of this error message
                0,                   //The status code from the previous command
                Nil,                 //The server on which we are logging
                'module: %s',        //The format string
-               [pchar(Msg)])        //The arguments to use to fill out fmt.
+               [PAnsiChar(Msg)])        //The arguments to use to fill out fmt.
 end;
 
 function TApacheHandler.GetIdleModuleCount : Integer;
@@ -423,60 +438,83 @@ end;
 
 { TApacheRequest }
 
-function TApacheRequest.GetFieldValue(Index: Integer): String;
+Function MaybeP(P : PAnsiChar) : String;
 
-  Function MaybeP(P : Pchar) : String;
+begin
+  If (P<>Nil) then
+    Result:=StrPas(P);
+end;
 
-  begin
-    If (P<>Nil) then
-      Result:=StrPas(P);
-  end;
+function TApacheRequest.GetApacheVariableValue(V: THTTPVariableType): String;
 
 var
-  FN : String;
-  I : Integer;
+  i : integer;
 
 begin
   Result:='';
-  If (Index in [1..NoHTTPFields]) then
-    begin
-    FN:=HTTPFieldNames[Index];
-    Result:=MaybeP(apr_table_get(FRequest^.headers_in,pchar(FN)));
-    end;
-  if (Result='') and Assigned(FRequest) then
-    case Index of
-      0  : Result:=MaybeP(FRequest^.protocol); // ProtocolVersion
-      7  : Result:=MaybeP(FRequest^.content_encoding); //ContentEncoding
-      25 : Result:=MaybeP(FRequest^.path_info); // PathInfo
-      26 : Result:=MaybeP(FRequest^.filename); // PathTranslated
-      27 : // RemoteAddr
-           If (FRequest^.Connection<>Nil) then
-             Result:=MaybeP(FRequest^.Connection^.remote_ip);
-      28 : // RemoteHost
-           If (FRequest^.Connection<>Nil) then
-             begin
-             Result:=MaybeP(ap_get_remote_host(FRequest^.Connection,
-                            FRequest^.per_dir_config,
-//                            nil,
-                            REMOTE_NAME,@i));
-             end;
-      29 : begin // ScriptName
-           Result:=MaybeP(FRequest^.unparsed_uri);
-           I:=Pos('?',Result)-1;
-           If (I=-1) then
-             I:=Length(Result);
-           Result:=Copy(Result,1,I-Length(PathInfo));
-           end;
-      30 : Result:=IntToStr(ap_get_server_port(FRequest)); // ServerPort
-      31 : Result:=MaybeP(FRequest^.method); // Method
-      32 : Result:=MaybeP(FRequest^.unparsed_uri); // URL
-      33 : Result:=MaybeP(FRequest^.args); // Query
-      34 : Result:=MaybeP(FRequest^.HostName); // Host
-    else
-      Result:=inherited GetFieldValue(Index);
+  if not Assigned(FRequest) then
+    exit;
+  case V of
+    hvHTTPVersion:
+      Result:=MaybeP(FRequest^.protocol); // ProtocolVersion
+    hvPathInfo:
+      Result:=MaybeP(FRequest^.path_info); // PathInfo
+    hvPathTranslated:
+      Result:=MaybeP(FRequest^.filename); // PathTranslated
+    hvRemoteAddress :
+      If (FRequest^.Connection<>Nil) then
+        Result:=MaybeP(FRequest^.Connection^.remote_ip);
+    hvRemoteHost:
+      If (FRequest^.Connection<>Nil) then
+        begin
+        Result:=MaybeP(ap_get_remote_host(FRequest^.Connection,
+                       FRequest^.per_dir_config,
+//                     nil,
+                       REMOTE_NAME,@i));
+        end;
+    hvScriptName:
+      begin // ScriptName
+      Result:=MaybeP(FRequest^.unparsed_uri);
+      I:=Pos('?',Result)-1;
+      If (I=-1) then
+       I:=Length(Result);
+      Result:=Copy(Result,1,I-Length(PathInfo));
+      end;
+    hvServerPort:
+      Result:=IntToStr(ap_get_server_port(FRequest)); // ServerPort
+    hvMethod:
+      Result:=MaybeP(FRequest^.method); // Method
+    hvURL:
+      Result:=MaybeP(FRequest^.unparsed_uri); // URL
+    hvQuery:
+      Result:=MaybeP(FRequest^.args); // Query
     end;
 end;
 
+function TApacheRequest.GetApacheHeaderValue(H: THeader): String;
+
+var
+  FN : AnsiString;
+  I : Integer;
+  S : String;
+
+begin
+  Result:='';
+  Str(H,S);
+  If Not Assigned(FRequest) then
+    exit;
+  Case h of
+    hhContentEncoding:
+      Result:=MaybeP(FRequest^.content_encoding);
+    hhHost:
+      Result:=MaybeP(FRequest^.HostName);
+  else
+    FN:=MaybeAnsi(HeaderName(H));
+    Result:=MaybeP(apr_table_get(FRequest^.headers_in,PAnsiChar(FN)));
+  end;
+end;
+
+
 procedure TApacheRequest.ReadContent;
 
   Function MinS(A,B : Integer) : Integer;
@@ -490,7 +528,7 @@ procedure TApacheRequest.ReadContent;
 
 Var
   Left,Len,Count,Bytes : Integer;
-  P : Pchar;
+  P : PAnsiChar;
   S : String;
 
 begin
@@ -501,7 +539,7 @@ begin
     If (Len>0) then
       begin
       SetLength(S,Len);
-      P:=PChar(S);
+      P:=PAnsiChar(S);
       Left:=Len;
       Count:=0;
       Repeat
@@ -516,13 +554,39 @@ begin
   InitContent(S);
 end;
 
+
 procedure TApacheRequest.InitFromRequest;
 begin
   ParseCookies;
   ReadContent;
 end;
 
-Constructor TApacheRequest.CreateReq(App : TApacheHandler; ARequest : PRequest_rec);
+procedure TApacheRequest.initrequestvars;
+
+Var
+  H : THeader;
+  V : THTTPVariableType;
+  S : String;
+
+begin
+  For H in Theader do
+    if hdRequest in HTTPHeaderDirections[H] then
+      begin
+      S:=GetApacheHeaderValue(H);
+      if S<>'' then
+        SetHeader(H,S);
+      end;
+  For V in THTTPVariableType do
+    begin
+    S:=GetApacheVariableValue(V);
+    if S<>'' then
+      SetHTTPVariable(V,S);
+    end;
+  inherited initrequestvars;
+end;
+
+constructor TApacheRequest.CreateReq(App: TApacheHandler; ARequest: PRequest_rec
+  );
 
 begin
   FApache:=App;
@@ -538,7 +602,7 @@ procedure TApacheResponse.DoSendHeaders(Headers: TStrings);
 
 Var
   I,P : Integer;
-  N,V : String;
+  N,V : AnsiString;
 
 begin
   For I:=0 to Headers.Count-1 do
@@ -550,35 +614,33 @@ begin
       N:=Copy(V,1,P-1);
       System.Delete(V,1,P);
       V := Trim(V);//no need space before the value, apache puts it there
-      apr_table_set(FRequest^.headers_out,Pchar(N),Pchar(V));
+      apr_table_set(FRequest^.headers_out,PAnsiChar(N),PAnsiChar(V));
       end;
     end;
 end;
 
+
 procedure TApacheResponse.DoSendContent;
 
 Var
-  S : String;
-  I : Integer;
+  S : AnsiString;
 
 begin
-  S:=ContentType;
+  S:=MaybeAnsi(ContentType);
   If (S<>'') then
-    FRequest^.content_type:=apr_pstrdup(FRequest^.pool,Pchar(S));
-  S:=ContentEncoding;
+    FRequest^.content_type:=apr_pstrdup(FRequest^.pool,PAnsiChar(S));
+  S:=MaybeAnsi(ContentEncoding);
   If (S<>'') then
-    FRequest^.content_encoding:=apr_pstrdup(FRequest^.pool,Pchar(S));
+    FRequest^.content_encoding:=apr_pstrdup(FRequest^.pool,PAnsiChar(S));
   If Code <> 200 then
     FRequest^.status := Code;
   If assigned(ContentStream) then
     SendStream(Contentstream)
   else
-    for I:=0 to Contents.Count-1 do
-      begin
-      S:=Contents[i]+LineEnding;
-      // If there is a null, it's written also with ap_rwrite
-      ap_rwrite(PChar(S),Length(S),FRequest);
-      end;
+    begin
+    S:=Content;
+    ap_rwrite(PAnsiChar(S),Length(S),FRequest);
+    end;
 end;
 
 Procedure TApacheResponse.SendStream(S : TStream);
@@ -715,14 +777,14 @@ end;
 
 procedure TCustomApacheApplication.ShowException(E: Exception);
 begin
-  ap_log_error(PChar(TApacheHandler(WebHandler).ModuleName),  //The file in which this function is called
+  ap_log_error(PAnsiChar(TApacheHandler(WebHandler).ModuleName),  //The file in which this function is called
                0,                                             //The line number on which this function is called
                0,                                             //The module_index of the module generating this message
                APLOG_ERR,                                     //The level of this error message
                0,                                             //The status code from the previous command
                Nil,                                           //The server on which we are logging
                'module: %s',                                  //The format string
-               [Pchar(E.Message)]);                           //The arguments to use to fill out fmt.
+               [PAnsiChar(E.Message)]);                           //The arguments to use to fill out fmt.
 end;
 
 function TCustomApacheApplication.ProcessRequest(P: PRequest_Rec): Integer;

+ 108 - 37
packages/fcl-web/src/base/custfcgi.pp

@@ -47,6 +47,7 @@ Type
   TProtocolOption = (poNoPadding,poStripContentLength, poFailonUnknownRecord,
                      poReuseAddress, poUseSelect );
   TProtocolOptions = Set of TProtocolOption;
+  TPathInfoHandling = (pihNone,pohAll,pihLastScriptComponent,pihFirstScriptComponent,pihSkipFirstScriptComponent);
 
   TUnknownRecordEvent = Procedure (ARequest : TFCGIRequest; AFCGIRecord: PFCGI_Header) Of Object;
   TFastCGIReadEvent = Function (AHandle : THandle; Var ABuf; ACount : Integer) : Integer of Object;
@@ -56,14 +57,12 @@ Type
   Private
     FHandle: THandle;
     FKeepConnectionAfterRequest: boolean;
+    FPathInfoHandling: TPathInfoHandling;
     FPO: TProtoColOptions;
     FRequestID : Word;
     FCGIParams : TSTrings;
     FUR: TUnknownRecordEvent;
     FLog : TLogEvent;
-    FSTDin : String;
-    FSTDinRead: Integer;
-
     FRequestHeadersInitialized: Boolean;
     FStreamingContentReceived: Boolean;
   Protected
@@ -77,6 +76,7 @@ Type
     property Handle : THandle read FHandle write FHandle;
     property KeepConnectionAfterRequest : boolean read FKeepConnectionAfterRequest;
     Property ProtocolOptions : TProtoColOptions read FPO Write FPO;
+    Property PathInfoHandling : TPathInfoHandling Read FPathInfoHandling Write FPathInfoHandling;
     Property OnUnknownRecord : TUnknownRecordEvent Read FUR Write FUR;
   end;
   TFCGIRequestClass = Class of TFCGIRequest;
@@ -106,6 +106,7 @@ Type
   Private
     FLingerTimeOut: integer;
     FOnUnknownRecord: TUnknownRecordEvent;
+    FPathInfoHandling: TPathInfoHandling;
     FPO: TProtoColOptions;
     FRequestsArray : Array of TReqResp;
     FRequestsAvail : integer;
@@ -146,6 +147,7 @@ Type
     Property ProtocolOptions : TProtoColOptions Read FPO Write FPO;
     Property OnUnknownRecord : TUnknownRecordEvent Read FOnUnknownRecord Write FOnUnknownRecord;
     Property TimeOut : Integer Read FTimeOut Write FTimeOut;
+    Property PathInfoHandling : TPathInfoHandling Read FPathInfoHandling Write FPathInfoHandling;
   end;
   TFCgiHandlerClass = Class of TFCgiHandler;
 
@@ -154,22 +156,27 @@ Type
   TCustomFCgiApplication = Class(TCustomWebApplication)
   private
     function GetAddress: string;
+    function GetCH: TFCgiHandler;
     function GetFPO: TProtoColOptions;
     function GetLingerTimeOut: integer;
     function GetOnUnknownRecord: TUnknownRecordEvent;
+    function GetPIH: TPathInfoHandling;
     function GetPort: integer;
     procedure SetAddress(const AValue: string);
     procedure SetLingerTimeOut(const AValue: integer);
     procedure SetOnUnknownRecord(const AValue: TUnknownRecordEvent);
+    procedure SetPIH(AValue: TPathInfoHandling);
     procedure SetPort(const AValue: integer);
     procedure SetPO(const AValue: TProtoColOptions);
   protected
     function InitializeWebHandler: TWebHandler; override;
+    Property FCGIHandler : TFCgiHandler Read GetCH;
   Public
     property Port: integer read GetPort write SetPort;
     property LingerTimeOut : integer read GetLingerTimeOut write SetLingerTimeOut;
     property Address: string read GetAddress write SetAddress;
     Property ProtocolOptions : TProtoColOptions Read GetFPO Write SetPO;
+    Property PathInfoHandling : TPathInfoHandling Read GetPIH Write SetPIH;
     Property OnUnknownRecord : TUnknownRecordEvent Read GetOnUnknownRecord Write SetOnUnknownRecord;
   end;
 
@@ -190,10 +197,12 @@ ResourceString
   
 Implementation
 
-{$ifdef CGIDEBUG}
 uses
-  dbugintf;
+{$ifdef CGIDEBUG}
+  dbugintf,
 {$endif}
+  strutils;
+
 {$undef nosignal}
 
 {$if defined(FreeBSD) or defined(Linux)}
@@ -336,20 +345,32 @@ var
     inc(i);
   end;
 
-  function GetString(ALength : integer) : string;
+  function GetBytes(ALength : integer) : TBytes;
   begin
     if (ALength<0) then
       ALength:=0;
     SetLength(Result,ALength);
     if (ALength>0) then
-      move(ARecord^.ContentData[i],Result[1],ALength);
+      move(ARecord^.ContentData[i],Result[0],ALength);
     inc(i,ALength);
   end;
 
+  function MakeString(B : TBytes) : string;
+
+
+  begin
+    {$IF SIZEOF(CHAR)=2}
+      Result:=TEncoding.UTF8.GetString(B);
+    {$else}
+      Result:=TEncoding.UTF8.GetAnsiString(B);
+    {$ENDIF}
+  end;
+
 var
-  VarNo,NameLength, ValueLength : Integer;
+  NameLength, ValueLength : Integer;
   RecordLength : Integer;
-  Name,Value : String;
+  Name,Tmp : String;
+  Value : TBytes;
   h : THeader;
   v : THTTPVariableType;
 
@@ -360,23 +381,44 @@ begin
     begin
     NameLength:=GetVarLength;
     ValueLength:=GetVarLength;
-    Name:=GetString(NameLength);
-    Value:=GetString(ValueLength);
-    VarNo:=IndexOfCGIVar(Name);
+    Name:=MakeString(GetBytes(NameLength));
+    Value:=GetBytes(ValueLength);
     if Not DoMapCgiToHTTP(Name,H,V) then
-      NameValueList.Add(Name+'='+Value)
+      NameValueList.Add(Name+'='+MakeString(Value))
     else if (H<>hhUnknown) then
-      SetHeader(H,Value)
+      SetHeader(H,MakeString(Value))
+    else if (v=hvContent) then
+      ContentBytes:=Value
     else if (v<>hvUnknown) then
       begin
-      if (V=hvPathInfo) and (Copy(Value,1,2)='//') then //mod_proxy_fcgi gives double slashes at the beginning for some reason
-          Delete(Value,1,3);
+      Tmp:=MakeString(Value);
+      if (V=hvPathInfo) and (Copy(Tmp,1,2)='//') then //mod_proxy_fcgi gives double slashes at the beginning for some reason
+          Delete(Tmp,1,3);
       if (V<>hvQuery) then
-        Value:=HTTPDecode(Value);
-      SetHTTPVariable(v,Value);
+        Tmp:=HTTPDecode(Tmp);
+      SetHTTPVariable(v,Tmp);
       end
     else
-      NameValueList.Add(Name+'='+Value)
+      NameValueList.Add(Name+'='+MakeString(Value));
+    end;
+  if (PathInfo='') then
+    // Apache does not send PathInfo if configured via proxy
+    begin
+    Tmp:=ScriptName;
+    ValueLength:=Length(Tmp);
+    Case PathInfoHandling of
+      pihNone : ;
+      pohAll : PathInfo:=Tmp;
+      pihLastScriptComponent :
+         PathInfo:=Copy(Tmp,RPos('/',Tmp)+1,ValueLength);
+      pihFirstScriptComponent :
+         PathInfo:=Copy(Tmp,RPos('/',Tmp)-1,ValueLength);
+      pihSkipFirstScriptComponent:
+        begin
+        Delete(Value,1,RPos('/',ScriptName));
+        PathInfo:=Tmp;
+        end;
+    end;
     end;
   // Microsoft-IIS hack. IIS includes the script name in the PATH_INFO
   if Pos('IIS', ServerSoftware) > 0 then
@@ -429,7 +471,7 @@ procedure TFCGIResponse.DoSendHeaders(Headers : TStrings);
 var
   cl : word;
   pl : byte;
-  str : String;
+  str : AnsiString;
   ARespRecord : PFCGI_ContentRecord;
   I : Integer;
 
@@ -442,7 +484,11 @@ begin
     For I:=Headers.Count-1 downto 0 do
       If (Pos('Content-Length',Headers[i])<>0)  then
         Headers.Delete(i);
+  {$if SIZEOF(CHAR)=2}
+  str := UTF8Encode(Headers.Text+sLineBreak);
+  {$ELSE}
   str := Headers.Text+sLineBreak;
+  {$ENDIF}
   cl := length(str);
   if ((cl mod 8)=0) or (poNoPadding in ProtocolOptions) then
     pl:=0
@@ -473,19 +519,26 @@ var
   bs,l : Integer;
   cl : word;
   pl : byte;
-  str : String;
+  str : TBytes;
   ARespRecord : PFCGI_ContentRecord;
   EndRequest : FCGI_EndRequestRecord;
 
 begin
+  Str:=[];
   If Assigned(ContentStream) then
     begin
     setlength(str,ContentStream.Size);
     ContentStream.Position:=0;
-    ContentStream.Read(str[1],ContentStream.Size);
+    ContentStream.Read(str[0],ContentStream.Size);
     end
   else
-    str := Contents.Text;
+    begin
+    {$IF SIZEOF(CHAR)=2}
+    str := TENcoding.UTF8.GetBytes(Contents.Text);
+    {$ELSE}
+    str := TENcoding.UTF8.GetAnsiBytes(Contents.Text);
+    {$ENDIF}
+    end;
   L:=Length(Str);
   BS:=0;
   Repeat
@@ -505,7 +558,7 @@ begin
       ARespRecord^.header.paddingLength:=pl;
       ARespRecord^.header.contentLength:=NtoBE(cl);
       ARespRecord^.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
-      move(Str[BS+1],ARespRecord^.ContentData,cl);
+      move(Str[BS],ARespRecord^.ContentData,cl);
       Write_FCGIRecord(PFCGI_Header(ARespRecord));
     finally
       Freemem(ARespRecord);
@@ -643,7 +696,7 @@ function TFCgiHandler.Read_FCGIRecord : PFCGI_Header;
         if s2[1] = ' ' then s2[1] := '0';
         s1 := s1 + s2;
         If PByte(ResRecord)[i]>32 then
-          S:=S+char(PByte(ResRecord)[i])
+          S:=S+AnsiChar(PByte(ResRecord)[i])
         else
           S:=S+' ';
         if (I>0) and (((I+1) mod 16) = 0) then
@@ -728,7 +781,8 @@ begin
   end;
 end;
 
-procedure TFCgiHandler.SetupSocket(var IAddress : TInetSockAddr; Var AddressLength : tsocklen);
+procedure TFCgiHandler.SetupSocket(var IAddress: TInetSockAddr;
+  var AddressLength: tsocklen);
 
 Var
   L : Linger;
@@ -824,6 +878,8 @@ begin
   if (C=Nil) then
     C:=TFCGIRequest;
   Result:=C.Create;
+  if Result is TFCGIRequest then
+    TFCGIRequest(Result).PathInfoHandling:=PathInfoHandling;
 end;
 
 function TFCgiHandler.CreateResponse(ARequest: TFCGIRequest): TFCGIResponse;
@@ -848,7 +904,7 @@ begin
 end;
 
 function TFCgiHandler.DoFastCGIWrite(AHandle: THandle; const ABuf;
-  ACount: Integer; Out ExtendedErrorCode : Integer): Integer;
+  ACount: Integer; out ExtendedErrorCode: Integer): Integer;
 begin
   {$ifdef windowspipe}
   if FIsWinPipe then
@@ -1005,52 +1061,67 @@ end;
 
 function TCustomFCgiApplication.GetAddress: string;
 begin
-  result := TFCgiHandler(WebHandler).Address;
+  result := FCGIHandler.Address;
+end;
+
+function TCustomFCgiApplication.GetCH: TFCgiHandler;
+begin
+  Result:=WebHandler as TFCgiHandler;
 end;
 
 function TCustomFCgiApplication.GetFPO: TProtoColOptions;
 begin
-  result := TFCgiHandler(WebHandler).ProtocolOptions;
+  result := FCGIHandler.ProtocolOptions;
 end;
 
 function TCustomFCgiApplication.GetLingerTimeOut: integer;
 begin
-  Result:=TFCgiHandler(WebHandler).LingerTimeOut;
+  Result:=FCGIHandler.LingerTimeOut;
 end;
 
 function TCustomFCgiApplication.GetOnUnknownRecord: TUnknownRecordEvent;
 begin
-  result := TFCgiHandler(WebHandler).OnUnknownRecord;
+  result := FCGIHandler.OnUnknownRecord;
+end;
+
+function TCustomFCgiApplication.GetPIH: TPathInfoHandling;
+begin
+  Result:=FCGIHandler.PathInfoHandling;
 end;
 
 function TCustomFCgiApplication.GetPort: integer;
 begin
-  result := TFCgiHandler(WebHandler).Port;
+  result := FCGIHandler.Port;
 end;
 
 procedure TCustomFCgiApplication.SetAddress(const AValue: string);
 begin
-  TFCgiHandler(WebHandler).Address := AValue;
+  FCGIHandler.Address := AValue;
 end;
 
 procedure TCustomFCgiApplication.SetLingerTimeOut(const AValue: integer);
 begin
-  TFCgiHandler(WebHandler).LingerTimeOut:=AValue;
+  FCGIHandler.LingerTimeOut:=AValue;
 end;
 
 procedure TCustomFCgiApplication.SetOnUnknownRecord(const AValue: TUnknownRecordEvent);
 begin
-  TFCgiHandler(WebHandler).OnUnknownRecord := AValue;
+  FCGIHandler.OnUnknownRecord := AValue;
+end;
+
+procedure TCustomFCgiApplication.SetPIH(AValue: TPathInfoHandling);
+begin
+  FCGIHandler.PathInfoHandling:=aValue;
 end;
 
 procedure TCustomFCgiApplication.SetPort(const AValue: integer);
 begin
-  TFCgiHandler(WebHandler).Port := AValue;
+  FCGIHandler.Port := AValue;
 end;
 
 procedure TCustomFCgiApplication.SetPO(const AValue: TProtoColOptions);
 begin
-  TFCgiHandler(WebHandler).ProtocolOptions := AValue;
+  FCGIHandler.ProtocolOptions := AValue;
 end;
 
 function TCustomFCgiApplication.InitializeWebHandler: TWebHandler;

+ 81 - 20
packages/fcl-web/src/base/custmicrohttpapp.pp

@@ -105,6 +105,7 @@ Type
   TMicroHTTPHandler = class(TWebHandler)
   Private
     FAcceptHandler: TAcceptHandler;
+    FAddress: String;
     FExtraHeaders: TStrings;
     FOnRequestError: TRequestErrorHandler;
     FPort : Word;
@@ -144,17 +145,21 @@ Type
     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;
+    // Interface Address to listen on
+    Property Address : String Read FAddress Write FAddress;
   end;
 
   { TCustomMicroHTTPApplication }
 
   TCustomMicroHTTPApplication = Class(TCustomWebApplication)
   private
+    function GetAddress: String;
     function GetExtraHeaders: TStrings;
     function GetHostName: String;
     function GetOptions: TMicroServerOptions;
     function GetPort: Word;
     function GetUseSSL: Boolean;
+    procedure SetAddress(AValue: String);
     procedure SetExtraHeaders(AValue: TStrings);
     procedure SetHostName(const AValue: String);
     procedure SetOptions(AValue: TMicroServerOptions);
@@ -175,6 +180,8 @@ Type
     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;
+    // Interface Address to listen on
+    Property Address : String Read GetAddress Write SetAddress;
   end;
 
 
@@ -193,7 +200,7 @@ Const
   MHD_USE_DEBUG,
   MHD_USE_SSL,
   MHD_USE_THREAD_PER_CONNECTION,
-  MHD_USE_SELECT_INTERNALLY,
+  MHD_USE_INTERNAL_POLLING_THREAD,
   MHD_USE_IPv6,
   MHD_USE_PEDANTIC_CHECKS,
   MHD_USE_POLL,
@@ -210,9 +217,18 @@ Const
   libmicrohttp Callbacks
   ---------------------------------------------------------------------}
 
-Function MaybeS(p : pchar) : String;
+Function MaybeS(p : PAnsiChar) : String;
+
+Var
+  S : AnsiString;
+
 begin
-  if Assigned(P) then Result:=P else Result:='';
+  if Assigned(P) then S:=P else S:='';
+  {$IF SIZEOF(CHAR)=2}
+  Result:=UTF8Decode(S);
+  {$ELSE}
+  Result:=S;
+  {$ENDIF}
 end;
 
 function GetRequestData(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; value: Pcchar): cint; cdecl;
@@ -221,6 +237,7 @@ var
   K,V : String;
 
 
+
 begin
   K:=MaybeS(key);
   V:=MaybeS(Value);
@@ -340,15 +357,23 @@ end;
 function TMicroRequest.AddData(Data: PAnsiChar; DataSize: Size_t): Size_t;
 
 Var
-  C : String;
+  C : RawByteString;
   L : Integer;
 
 begin
+  {$IF SIZEOF(CHAR)=2}
+  C:=UTF8Encode(Content);
+  {$ELSE}
   C:=Content;
+  {$ENDIF}
   L:=Length(C);
   SetLength(C,L+Datasize);
   Move(Data^,C[L+1],DataSize);
+  {$IF SIZEOF(CHAR)=2}
+  InitContent(UTF8Decode(C));
+  {$ELSE}
   InitContent(C);
+  {$ENDIF}
   Result:=Datasize;
 end;
 
@@ -364,18 +389,33 @@ end;
 procedure TMicroRequest.InitRequestVars;
 
 Var
-  P : Pchar;
-  N,S  : String;
+  P : PAnsiChar;
+  N  : AnsiString;
+  HN,S,V : String;
   I : integer;
 
 begin
   MHD_get_connection_values(FHandler.FConnection, MHD_GET_ARGUMENT_KIND,@GetRequestData,Self);
   MHD_get_connection_values(FHandler.FConnection, MHD_HEADER_KIND,@GetRequestData,Self);
-  for N in FHandler.WebHandler.ExtraHeaders do
+  for S in FHandler.WebHandler.ExtraHeaders do
     begin
-    P:=MHD_lookup_connection_value(FHandler.FConnection, MHD_HEADER_KIND,Pchar(N));
+    {$IF SIZEOF(Char)=2}
+    N:=UTF8Encode(S);
+    {$ELSE}
+    N:=S;
+    {$ENDIF}
+    P:=MHD_lookup_connection_value(FHandler.FConnection, MHD_HEADER_KIND,PAnsiChar(N));
     If P<>Nil then
-      SetCustomHeader(N,P);
+      begin
+      {$IF SIZEOF(Char)=2}
+      HN:=UTF8Decode(N);
+      V:=UTF8Decode(P);
+      {$ELSE}
+      HN:=N;
+      V:=P;
+      {$ENDIF}
+      SetCustomHeader(HN,V);
+      end;
     end;
   S:=URL;
   I:=Pos('?',S);
@@ -400,7 +440,7 @@ procedure TMicroResponse.MaybeAllocateResponse;
 
 Var
   L : Integer;
-  P : PChar;
+  P : PAnsiChar;
   B : TBytes;
 
 begin
@@ -416,14 +456,15 @@ begin
       begin
       SetLength(B,L);
       ContentStream.ReadBuffer(B[0],L);
-      P:=Pchar(B);
+      P:=PAnsiChar(B);
       FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
       end;
     end
   else
     begin
-    L:=Length(Content);
-    P:=PChar(Content);
+    B:=TEncoding.UTF8.GetAnsiBytes(Content);
+    L:=Length(B);
+    P:=PAnsiChar(B);
     FResponse:=MHD_create_response_from_buffer(L,P,MHD_RESPMEM_MUST_COPY);
     end;
 end;
@@ -433,6 +474,8 @@ procedure TMicroResponse.DoSendHeaders(Headers: TStrings);
 Var
   I : Integer;
   N,V : String;
+  NA : RawByteString {$IF SIZEOF(CHAR)=1}absolute N{$ENDIF};
+  VA : RawByteString {$IF SIZEOF(CHAR)=1}absolute V{$ENDIF};
 
 begin
   // Note that if the response is allocated, then you cannot set the content stream any more...
@@ -441,7 +484,11 @@ begin
   For I:=0 to Headers.Count-1 do
     begin
     Headers.GetNameValue(I,N,V);
-    MHD_add_response_header(FResponse,PAnsiChar(N),PAnsiChar(V));
+    {$IF SIZEOF(CHAR)=2}
+    NA:=UTF8Encode(N);
+    VA:=UTF8Encode(V);
+    {$ENDIF}
+    MHD_add_response_header(FResponse,PAnsiChar(NA),PAnsiChar(VA));
     end;
 end;
 
@@ -536,7 +583,9 @@ begin
   end;
 end;
 
-function TMicroHTTPHandler.DoRequest(connection: PMHD_Connection; Const aUrl,aMethod,aVersion: String; Data: PAnsiChar; var DataSize: Size_t) : TRequestHandler;
+function TMicroHTTPHandler.DoRequest(connection: PMHD_Connection; const aUrl,
+  aMethod, aVersion: String; Data: PAnsiChar; var DataSize: Size_t
+  ): TRequestHandler;
 
 begin
   Result:=TRequestHandler.Create(Self,Connection);
@@ -609,7 +658,8 @@ begin
   AResponse:=Nil;
 end;
 
-Function TMicroHTTPHandler.DoAcceptConnection(Addr : PSockAddr; addrLen : socklen_t) : Boolean;
+function TMicroHTTPHandler.DoAcceptConnection(Addr: PSockAddr;
+  addrLen: socklen_t): Boolean;
 
 begin
   Result:=True;
@@ -634,7 +684,8 @@ end;
 function TMicroHTTPHandler.CreateServer: PMHD_Daemon;
 
 Var
-  F,P : Integer;
+  F : Integer;
+  P : Word;
 
 begin
   F:=OptionsToFlags;
@@ -642,8 +693,8 @@ begin
   Result:= MHD_start_daemon(F,P,
     @AcceptCallBack, Self,
     @DoMHDRequest, Self,
-    MHD_OPTION_NOTIFY_COMPLETED, @HandleRequestCompleted,
-    Nil,MHD_OPTION_END);
+    MHD_OPTION_NOTIFY_COMPLETED, @HandleRequestCompleted, Nil,
+    MHD_OPTION_END,Nil);
 end;
 
 procedure TMicroHTTPHandler.Run;
@@ -702,7 +753,7 @@ begin
   HTTPHandler.Port:=aValue;
 end;
 
-procedure TCustomMicroHTTPApplication.SetUSeSSL(AValue: Boolean);
+procedure TCustomMicroHTTPApplication.SetUseSSL(AValue: Boolean);
 begin
   if AValue then
     Options:=Options+[mcoSSL]
@@ -720,6 +771,11 @@ begin
   Result:=mcoSSL in Options;
 end;
 
+procedure TCustomMicroHTTPApplication.SetAddress(AValue: String);
+begin
+  HTTPHandler.Address:=aValue;
+end;
+
 procedure TCustomMicroHTTPApplication.SetExtraHeaders(AValue: TStrings);
 begin
   HTTPHandler.ExtraHeaders.Assign(AValue);
@@ -747,6 +803,11 @@ begin
   inherited Destroy;
 end;
 
+function TCustomMicroHTTPApplication.GetAddress: String;
+begin
+  Result:=HTTPHandler.Address;
+end;
+
 function TCustomMicroHTTPApplication.GetExtraHeaders: TStrings;
 begin
   Result:=HTTPHandler.ExtraHeaders;

+ 7 - 7
packages/fcl-web/src/base/fcgigate.pp

@@ -106,7 +106,7 @@ Type
     // Transform CGI environment variables.
     Function TransformRequestVars: String;virtual;
     // Encode name=value pair for PARAMS fastcgi record.
-    Function EncodeFastCGIParam(N, V: AnsiString): String;
+    Function EncodeFastCGIParam(N, V: AnsiString): ansiString;
     // High-level Communication
     // Send data from request
     procedure SendRequestData(const ARequest : TRequest); virtual;
@@ -246,7 +246,7 @@ begin
       Result:=Result+Format('#%.3d',[Ord(S[i])]);
 end;
 
-Function TFastCGIGatewayHandler.EncodeFastCGIParam(N,V : AnsiString) : String;
+Function TFastCGIGatewayHandler.EncodeFastCGIParam(N,V : AnsiString) : AnsiString;
 
   Function CalcJump(ALen : Integer) : Integer;
   begin
@@ -256,7 +256,7 @@ Function TFastCGIGatewayHandler.EncodeFastCGIParam(N,V : AnsiString) : String;
       Result:=4;
   end;
 
-  Procedure AddLengthEncoding(Var S : String; ALen : Integer; Var Offset : Integer);
+  Procedure AddLengthEncoding(Var S : AnsiString; ALen : Integer; Var Offset : Integer);
 
   Var
     J,L : integer;
@@ -445,7 +445,7 @@ var
   BytesRead : integer;
   ContentLength : word;
   PaddingLength : byte;
-  ReadBuf : Pchar;
+  ReadBuf : PAnsiChar;
 
   function ReadBytes(ByteAmount : Word) : boolean;
 
@@ -470,7 +470,7 @@ begin
   PaddingLength:=Header.paddingLength;
   Result:=Getmem(BytesRead+ContentLength+PaddingLength);
   Result^:=Header;
-  ReadBuf:=Pchar(Result)+SizeOf(Header);
+  ReadBuf:=PAnsiChar(Result)+SizeOf(Header);
   ReadBytes(ContentLength);
   ReadBuf:=ReadBuf+BytesRead;
   ReadBytes(PaddingLength);
@@ -492,7 +492,7 @@ Procedure TFastCGIGatewayHandler.ReadResponse(AResponse : TResponse);
 Var
   Rec : PFCGI_Header;
   CL : Integer;
-  WBuf : PChar;
+  WBuf : PAnsiChar;
   EOR : Boolean;
 
 begin
@@ -508,7 +508,7 @@ begin
          begin
          AResponse.ContentStream:=TMemoryStream.Create;
          end;
-      WBuf:=Pchar(Rec)+SizeOf(FCGI_Header);
+      WBuf:=PAnsiChar(Rec)+SizeOf(FCGI_Header);
       AResponse.ContentStream.WriteBuffer(WBuf^,CL);
       end
     else If (Rec^.ReqType=FCGI_END_REQUEST) and (CL>0) then

+ 11 - 3
packages/fcl-web/src/base/fpapache.pp

@@ -43,12 +43,20 @@ Type
     Property WorkingWebModuleCount;
   end;
 
+Function Application : TCustomApacheApplication;
+
 Implementation
 
+Function Application : TCustomApacheApplication;
+
+begin
+  Result:=CustApache.Application;
+end;
+
 Procedure InitApache;
 
 begin
-  Application:=TApacheApplication.Create(Nil);
+  custapache.Application:=TApacheApplication.Create(Nil);
   if not assigned(CustomApplication) then
     CustomApplication := Application;
 end;
@@ -57,9 +65,9 @@ Procedure DoneApache;
 
 begin
   Try
-    if CustomApplication=Application then
+    if CustomApplication=CustApache.Application then
       CustomApplication := nil;
-    FreeAndNil(Application);
+    FreeAndNil(CustApache.Application);
   except
     if ShowCleanUpErrors then
       Raise;

+ 12 - 4
packages/fcl-web/src/base/fpapache24.pp

@@ -44,23 +44,31 @@ Type
   end;
 
 
+Function Application : TCustomApacheApplication;
+
 Implementation
 
+Function Application : TCustomApacheApplication;
+
+begin
+  Result:=CustApache24.Application;
+end;
+
 Procedure InitApache;
 
 begin
-  Application:=TApacheApplication.Create(Nil);
+  CustApache24.Application:=TApacheApplication.Create(Nil);
   if not assigned(CustomApplication) then
-    CustomApplication := Application;
+    CustomApplication := CustApache24.Application;
 end;
 
 Procedure DoneApache;
 
 begin
   Try
-    if CustomApplication=Application then
+    if CustomApplication=CustApache24.Application then
       CustomApplication := nil;
-    FreeAndNil(Application);
+    FreeAndNil(CustApache24.Application);
   except
     if ShowCleanUpErrors then
       Raise;

+ 4 - 5
packages/fcl-web/src/base/fphttpserver.pp

@@ -1078,10 +1078,10 @@ procedure TFPHTTPConnection.ReadRequestContent(
 
 Var
   P,L,R : integer;
-  S : String;
+  S : TBytes;
 
 begin
-  S:='';
+  S:=[];
   L:=ARequest.ContentLength;
   If (L>0) then
     begin
@@ -1091,11 +1091,10 @@ begin
       begin
       if P>L then
         P:=L;
-      Move(FBuffer[1],S[1],P);
+      Move(FBuffer[1],S[0],P);
       FBuffer:='';
       L:=L-P;
       end;
-    P:=P+1;
     R:=1;
     While (L>0) and (R>0) do
       begin
@@ -1109,7 +1108,7 @@ begin
         end;
       end;  
     end;
-  ARequest.InitContent(S);
+  ARequest.ContentBytes:=S;
 end;
 
 function TFPHTTPConnection.ReadRequestHeaders: TFPHTTPConnectionRequest;

+ 4 - 0
packages/fcl-web/src/base/fpweb.pp

@@ -415,7 +415,11 @@ procedure TCustomFPWebModule.GetTemplateContent(ARequest: TRequest;
   
 begin
   TFPWebTemplate(FTemplate).Request:=ARequest;
+  {$IF SIZEOF(CHAR)=2}
+  AResponse.Content:=UTF8Encode(FTemplate.GetContent);
+  {$ELSE}
   AResponse.Content:=FTemplate.GetContent;
+  {$ENDIF}
 end;
 
 function TCustomFPWebModule.GetContent: String;

+ 20 - 7
packages/fcl-web/src/base/httpdefs.pp

@@ -310,13 +310,13 @@ type
 
   TStreamingMimeItems = class(TMimeItems)
   private
-    FBuffer: string;
+    FBuffer: Ansistring;
     FBufferCount: SizeInt;
     FCurrentItem: TMimeItem;
     FMimeEndFound: Boolean;
     FAtStart: Boolean;
   protected
-    procedure SetBoundary(const AValue: string); override;
+    procedure SetBoundary(const AValue: String); override;
     procedure ProcessStreamingMultiPart(const State: TContentStreamingState; const Buf; const Size: Integer); override;
     class function SupportsStreamingProcessing: Boolean; override;
   end;
@@ -841,13 +841,19 @@ end;
 { TStreamingMimeItems }
 
 procedure TStreamingMimeItems.ProcessStreamingMultiPart(const State: TContentStreamingState; const Buf; const Size: Integer);
+
+Const
+   DashDash : AnsiString = '--';
+   CRLFDashDash : AnsiString = #13#10'--';
+
 var
   bl: SizeInt;
   p: SizeInt;
   BufEnd: SizeInt;
   LeadingLineEndMissing: Boolean;
   Bound,EndBound : RawByteString;
-  
+  Sep : AnsiString;
+
 begin
   // The length of the boundary, including the leading CR/LF, '--' and trailing '--' or
   // CR/LF.
@@ -878,14 +884,14 @@ begin
 
   FBufferCount := 1;
   repeat
-  if FAtStart and CompareMem(@FBuffer[1], PChar('--'+FBoundary), Length(FBoundary)+2) then
+  if FAtStart and CompareMem(@FBuffer[1], PAnsiChar(EndBound), Length(Bound)+2) then
     begin
     // Sometimes a mime-message (mistakenly) does not start with CR/LF.
     p := 1;
     LeadingLineEndMissing := True;
     end
   else
-    p := Pos(#13#10'--'+FBoundary, FBuffer, FBufferCount);
+    p := Pos(CRLFDashDash+Bound, FBuffer, FBufferCount);
   if (P > 0) and (P < Size) then
     begin
     if Assigned(FCurrentItem) then
@@ -896,8 +902,14 @@ begin
     else
       begin
       if FAtStart and (P > 1) then
+        begin
         // Add the preamble to the content
+        {$IF SIZEOF(CHAR)=2}
+        FPreamble := UTF8Decode(Copy(FBuffer, FBufferCount, P-1));
+        {$ELSE}
         FPreamble := Copy(FBuffer, FBufferCount, P-1);
+        {$ENDIF}
+        end;
       end;
     FAtStart := False;
     Inc(P, bl);
@@ -907,7 +919,8 @@ begin
       LeadingLineEndMissing := False;
       end;
     FBufferCount := P;
-    if (Copy(FBuffer,p-2,2)='--') then
+    Sep:=Copy(FBuffer,p-2,2);
+    if (Sep=DashDash) then
       FMimeEndFound := True;
     end;
   if not Assigned(FCurrentItem) and not FMimeEndFound then
@@ -2227,7 +2240,7 @@ begin
   P:=PathInfo;
 {$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s"',[P,FReturnedPathInfo]));{$ENDIF}
   if (P <> '') and (P[length(P)] = '/') then
-    Delete(P, length(P), 1);//last char is '/'
+    Delete(P, length(P), 1); // last char is '/'
   If (P<>'') and (P[1]='/') then
     Delete(P,1,1);
   Delete(P,1,Length(IncludeHTTPPathDelimiter(FReturnedPathInfo)));

+ 10 - 2
packages/fcl-web/src/jwt/fpjwaes256.pp

@@ -60,7 +60,7 @@ end;
 Class function TJWTSignerES256.Verify(const aJWT: String; aPrivateKey: TECCPrivateKey): Boolean;
 
 Var
-  J,C,S : AnsiString;
+  J,C,S : String;
   aSignature : TEccSignature;
   B : TBytes;
 
@@ -68,7 +68,11 @@ begin
   Result:=GetParts(aJWT,J,C,S);
   if Not Result then
     exit;
+{$IF SIZEOF(CHAR)=2}    
+  B:=TEncoding.UTF8.GetBytes(J+'.'+C);
+{$ELSE}  
   B:=TEncoding.UTF8.GetAnsiBytes(J+'.'+C);
+{$ENDIF}  
   BytesToVar(Base64url.Decode(S),aSignature,Sizeof(aSignature));
   Result:=TECDSA.verifySHA256(B,aPrivateKey,aSignature);
 end;
@@ -76,7 +80,7 @@ end;
 class function TJWTSignerES256.Verify(const aJWT: String; aPublicKey: TECCPublicKey): Boolean;
 
 Var
-  J,C,S : AnsiString;
+  J,C,S : String;
   aSignature : TEccSignature;
   B : TBytes;
 
@@ -84,7 +88,11 @@ begin
   Result:=GetParts(aJWT,J,C,S);
   if Not Result then
     exit;
+{$IF SIZEOF(CHAR)=2}    
+  B:=TEncoding.UTF8.GetBytes(J+'.'+C);
+{$ELSE}     
   B:=TEncoding.UTF8.GetAnsiBytes(J+'.'+C);
+{$ENDIF}  
   Base64url.Decode(S,@aSignature);
   Result:=TECDSA.verifySHA256(B,aPublicKey,aSignature);
 end;

+ 2 - 2
packages/fcl-web/src/jwt/fpjwt.pp

@@ -609,7 +609,7 @@ Var
   PL : PPropList;
   I,VI,Count : Integer;
   VF : Double;
-  C : Char;
+  C : AnsiChar;
   CW : WideChar;
   I64 : Int64;
   W : UnicodeString;
@@ -631,7 +631,7 @@ begin
             end;
           tkChar :
             begin
-            C:=Char(GetOrdProp(Self,P));
+            C:=AnsiChar(GetOrdProp(Self,P));
             if All or (C<>#0) then
               if C=#0 then
                 JSON.Add(p^.Name,'')

+ 1 - 1
packages/fcl-web/src/restbridge/sqldbrestbridge.pp

@@ -2352,7 +2352,7 @@ begin
   // Check & discard basepath parts of the URL
   Path:=aRequest.GetNextPathInfo;
   Full:=BasePath;
-  BasePaths:=Full.Split('/',TStringSplitOptions.ExcludeEmpty);
+  BasePaths:=Full.Split(RTLString('/'),TStringSplitOptions.ExcludeEmpty);
   I:=0;
   While (I<Length(BasePaths)) and SameText(Path,BasePaths[i]) do
     begin

+ 1 - 1
packages/fcl-web/src/restbridge/sqldbrestschema.pp

@@ -1533,7 +1533,7 @@ begin
     O:=aMinFieldOpts;
     if FD.Required then
        Include(O,foRequired);
-    If AnsiIndexStr(FN,aIndexFields)<>-1 then
+    If IndexStr(FN,aIndexFields)<>-1 then
       begin
       Include(O,foInKey);
       Exclude(O,foFilter);

+ 2 - 1
packages/libmicrohttpd/src/libmicrohttpd.pp

@@ -209,7 +209,8 @@ const
   MHD_USE_DEBUG = 1;
   MHD_USE_SSL = 2;
   MHD_USE_THREAD_PER_CONNECTION = 4;
-  MHD_USE_SELECT_INTERNALLY = 8;
+  MHD_USE_INTERNAL_POLLING_THREAD = 8;
+  MHD_USE_SELECT_INTERNALLY = 8 deprecated 'use MHD_USE_INTERNAL_POLLING_THREAD';
   MHD_USE_IPv6 = 16;
   MHD_USE_PEDANTIC_CHECKS = 32;
   MHD_USE_POLL = 64;