mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-04 09:25:55 +02:00
206 lines
6.1 KiB
ObjectPascal
206 lines
6.1 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2019 by the Free Pascal development team
|
|
|
|
Sample HTTP server application with 2 interceptors
|
|
|
|
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+}
|
|
|
|
|
|
program simpleserver;
|
|
|
|
{$IFDEF USEMICROHTTP}
|
|
{$UNDEF USEGNUTLS}
|
|
{$ENDIF}
|
|
|
|
uses
|
|
{$ifdef unix}
|
|
cthreads,
|
|
{$endif}
|
|
sysutils, strutils, custapp, custhttpapp, Classes, httproute, httpdefs, fpmimetypes, fpwebfile, fpwebproxy, webutil, base64;
|
|
|
|
Type
|
|
|
|
{ THTTPApplication }
|
|
|
|
THTTPApplication = Class(TCustomHTTPApplication)
|
|
private
|
|
FBaseDir: string;
|
|
FIndexPageName: String;
|
|
FMimeFile: String;
|
|
FNoIndexPage: Boolean;
|
|
FQuiet: Boolean;
|
|
FPassword : string;
|
|
FAuth : String;
|
|
procedure DoAuthorization(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
procedure DoRequestEnd(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
procedure DoRequestStart(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
procedure ProcessOptions;
|
|
procedure Usage(Msg: String);
|
|
procedure Writeinfo;
|
|
published
|
|
procedure DoLog(EventType: TEventType; const Msg: String); override;
|
|
Procedure DoRun; override;
|
|
property Quiet : Boolean read FQuiet Write FQuiet;
|
|
Property MimeFile : String Read FMimeFile Write FMimeFile;
|
|
Property BaseDir : string Read FBaseDir Write FBaseDir;
|
|
Property NoIndexPage : Boolean Read FNoIndexPage Write FNoIndexPage;
|
|
Property IndexPageName : String Read FIndexPageName Write FIndexPageName;
|
|
end;
|
|
|
|
Var
|
|
Application : THTTPApplication;
|
|
|
|
{ THTTPApplication }
|
|
|
|
procedure THTTPApplication.DoLog(EventType: TEventType; const Msg: String);
|
|
begin
|
|
if IsConsole then
|
|
Writeln(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',Now),' [',EventType,'] ',Msg)
|
|
else
|
|
inherited DoLog(EventType, Msg);
|
|
end;
|
|
|
|
procedure THTTPApplication.Usage(Msg : String);
|
|
|
|
begin
|
|
if (Msg<>'') then
|
|
Writeln('Error: ',Msg);
|
|
Writeln('Usage ',ExtractFileName(ParamStr(0)),' [options] ');
|
|
Writeln('Where options is one or more of : ');
|
|
Writeln('-d --directory=dir Base directory from which to serve files.');
|
|
Writeln(' Default is current working directory: ',GetCurrentDir);
|
|
Writeln('-h --help This help text');
|
|
Writeln('-i --indexpage=name Directory index page to use (default: index.html)');
|
|
Writeln('-n --noindexpage Do not allow index page.');
|
|
Writeln('-p --port=NNNN TCP/IP port to listen on (default is 3000)');
|
|
Writeln('-q --quiet Do not register log intercepts');
|
|
Writeln('-a --authenticate=PWD Register authentication intercept - authenticate with PWD');
|
|
Halt(Ord(Msg<>''));
|
|
end;
|
|
|
|
|
|
procedure THTTPApplication.ProcessOptions;
|
|
|
|
Var
|
|
S : String;
|
|
|
|
begin
|
|
Quiet:=HasOption('q','quiet');
|
|
FAuth:=GetoptionValue('a','authenticate');
|
|
Port:=StrToIntDef(GetOptionValue('p','port'),Port);
|
|
if HasOption('d','directory') then
|
|
BaseDir:=GetOptionValue('d','directory');
|
|
if HasOption('H','hostname') then
|
|
HostName:=GetOptionValue('H','hostname');
|
|
if HasOption('n','noindexpage') then
|
|
NoIndexPage:=True
|
|
else
|
|
IndexPageName:=GetOptionValue('i','indexpage');
|
|
end;
|
|
|
|
procedure THTTPApplication.DoRequestStart(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
|
|
begin
|
|
DoLog(etInfo,Format('Request %s: %s',[aRequest.RequestID,aRequest.URL]));
|
|
end;
|
|
|
|
procedure THTTPApplication.DoRequestEnd(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
|
|
begin
|
|
DoLog(etInfo,Format('Request %s: %s : %d (%d bytes)',[aRequest.RequestID,aRequest.URL,aResponse.Code, aResponse.ContentLength]));
|
|
end;
|
|
|
|
procedure THTTPApplication.DoAuthorization(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
|
|
|
|
Var
|
|
S : String;
|
|
|
|
begin
|
|
S:=Trim(aRequest.Authorization);
|
|
aContinue:=SameText(ExtractWord(1,S,[' ']),'Basic');
|
|
if aContinue then
|
|
begin
|
|
S:=ExtractWord(2,S,[' ']); // Username:Password in base64
|
|
S:=DecodeStringBase64(S); // Decode
|
|
S:=ExtractWord(2,S,[':']); // extract password
|
|
aContinue:=SameText(S,Fauth); // Check
|
|
if not aContinue then
|
|
DoLog(etInfo,'Invalid password provided: '+S);
|
|
end
|
|
else
|
|
if S='' then
|
|
DoLog(etInfo,'Missing authorization header')
|
|
else
|
|
DoLog(etInfo,'Invalid authorization header: '+S);
|
|
if not aContinue then
|
|
begin
|
|
aResponse.Code:=401;
|
|
aResponse.CodeText:='Unauthorized';
|
|
aResponse.WWWAuthenticate:='Basic Realm="This site needs a password"';
|
|
aResponse.SendContent;
|
|
end;
|
|
end;
|
|
|
|
procedure THTTPApplication.Writeinfo;
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
Log(etInfo,'Listening on port %d, serving files from directory: %s (using SSL: %s)',[Port,BaseDir,BoolToStr(UseSSL,'true','false')]);
|
|
if not NoIndexPage then
|
|
Log(etInfo,'Using index page %s',[IndexPageName]);
|
|
end;
|
|
|
|
procedure THTTPApplication.DoRun;
|
|
|
|
Var
|
|
S : String;
|
|
|
|
begin
|
|
S:=Checkoptions('hqnd:p:i:a:',['help','quiet','noindexpage','directory:','port:','indexpage:','authenticate:']);
|
|
if (S<>'') or HasOption('h','help') then
|
|
usage(S);
|
|
ProcessOptions;
|
|
if BaseDir='' then
|
|
BaseDir:=GetCurrentDir;
|
|
if (BaseDir<>'') then
|
|
BaseDir:=IncludeTrailingPathDelimiter(BaseDir);
|
|
MimeTypes.LoadKnownTypes;
|
|
if Fauth<>'' then
|
|
HTTPRouter.RegisterInterceptor('auth',@DoAuthorization);
|
|
if not FQuiet then
|
|
begin
|
|
HTTPRouter.RegisterInterceptor('logstart',@DoRequestStart);
|
|
HTTPRouter.RegisterInterceptor('logend',@DoRequestEnd,iaAfter);
|
|
end;
|
|
TSimpleFileModule.RegisterDefaultRoute;
|
|
TSimpleFileModule.BaseDir:=BaseDir;
|
|
TSimpleFileModule.OnLog:=@Log;
|
|
If not NoIndexPage then
|
|
begin
|
|
if (IndexPageName='') then
|
|
IndexPageName:='index.html';
|
|
TSimpleFileModule.IndexPageName:=IndexPageName;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
begin
|
|
Application:=THTTPApplication.Create(Nil);
|
|
Application.Initialize;
|
|
Application.Run;
|
|
Application.Free;
|
|
end.
|
|
|