123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- {
- 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.
|