simpleserver.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by the Free Pascal development team
  4. Sample HTTP server application with 2 interceptors
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. program simpleserver;
  14. {$IFDEF USEMICROHTTP}
  15. {$UNDEF USEGNUTLS}
  16. {$ENDIF}
  17. uses
  18. {$ifdef unix}
  19. cthreads,
  20. {$endif}
  21. sysutils, strutils, custapp, custhttpapp, Classes, httproute, httpdefs, fpmimetypes, fpwebfile, fpwebproxy, webutil, base64;
  22. Type
  23. { THTTPApplication }
  24. THTTPApplication = Class(TCustomHTTPApplication)
  25. private
  26. FBaseDir: string;
  27. FIndexPageName: String;
  28. FMimeFile: String;
  29. FNoIndexPage: Boolean;
  30. FQuiet: Boolean;
  31. FPassword : string;
  32. FAuth : String;
  33. procedure DoAuthorization(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  34. procedure DoRequestEnd(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  35. procedure DoRequestStart(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  36. procedure ProcessOptions;
  37. procedure Usage(Msg: String);
  38. procedure Writeinfo;
  39. published
  40. procedure DoLog(EventType: TEventType; const Msg: String); override;
  41. Procedure DoRun; override;
  42. property Quiet : Boolean read FQuiet Write FQuiet;
  43. Property MimeFile : String Read FMimeFile Write FMimeFile;
  44. Property BaseDir : string Read FBaseDir Write FBaseDir;
  45. Property NoIndexPage : Boolean Read FNoIndexPage Write FNoIndexPage;
  46. Property IndexPageName : String Read FIndexPageName Write FIndexPageName;
  47. end;
  48. Var
  49. Application : THTTPApplication;
  50. { THTTPApplication }
  51. procedure THTTPApplication.DoLog(EventType: TEventType; const Msg: String);
  52. begin
  53. if IsConsole then
  54. Writeln(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',Now),' [',EventType,'] ',Msg)
  55. else
  56. inherited DoLog(EventType, Msg);
  57. end;
  58. procedure THTTPApplication.Usage(Msg : String);
  59. begin
  60. if (Msg<>'') then
  61. Writeln('Error: ',Msg);
  62. Writeln('Usage ',ExtractFileName(ParamStr(0)),' [options] ');
  63. Writeln('Where options is one or more of : ');
  64. Writeln('-d --directory=dir Base directory from which to serve files.');
  65. Writeln(' Default is current working directory: ',GetCurrentDir);
  66. Writeln('-h --help This help text');
  67. Writeln('-i --indexpage=name Directory index page to use (default: index.html)');
  68. Writeln('-n --noindexpage Do not allow index page.');
  69. Writeln('-p --port=NNNN TCP/IP port to listen on (default is 3000)');
  70. Writeln('-q --quiet Do not register log intercepts');
  71. Writeln('-a --authenticate=PWD Register authentication intercept - authenticate with PWD');
  72. Halt(Ord(Msg<>''));
  73. end;
  74. procedure THTTPApplication.ProcessOptions;
  75. Var
  76. S : String;
  77. begin
  78. Quiet:=HasOption('q','quiet');
  79. FAuth:=GetoptionValue('a','authenticate');
  80. Port:=StrToIntDef(GetOptionValue('p','port'),Port);
  81. if HasOption('d','directory') then
  82. BaseDir:=GetOptionValue('d','directory');
  83. if HasOption('H','hostname') then
  84. HostName:=GetOptionValue('H','hostname');
  85. if HasOption('n','noindexpage') then
  86. NoIndexPage:=True
  87. else
  88. IndexPageName:=GetOptionValue('i','indexpage');
  89. end;
  90. procedure THTTPApplication.DoRequestStart(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  91. begin
  92. DoLog(etInfo,Format('Request %s: %s',[aRequest.RequestID,aRequest.URL]));
  93. end;
  94. procedure THTTPApplication.DoRequestEnd(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  95. begin
  96. DoLog(etInfo,Format('Request %s: %s : %d (%d bytes)',[aRequest.RequestID,aRequest.URL,aResponse.Code, aResponse.ContentLength]));
  97. end;
  98. procedure THTTPApplication.DoAuthorization(ARequest: TRequest; AResponse: TResponse; var aContinue: Boolean);
  99. Var
  100. S : String;
  101. begin
  102. S:=Trim(aRequest.Authorization);
  103. aContinue:=SameText(ExtractWord(1,S,[' ']),'Basic');
  104. if aContinue then
  105. begin
  106. S:=ExtractWord(2,S,[' ']); // Username:Password in base64
  107. S:=DecodeStringBase64(S); // Decode
  108. S:=ExtractWord(2,S,[':']); // extract password
  109. aContinue:=SameText(S,Fauth); // Check
  110. if not aContinue then
  111. DoLog(etInfo,'Invalid password provided: '+S);
  112. end
  113. else
  114. if S='' then
  115. DoLog(etInfo,'Missing authorization header')
  116. else
  117. DoLog(etInfo,'Invalid authorization header: '+S);
  118. if not aContinue then
  119. begin
  120. aResponse.Code:=401;
  121. aResponse.CodeText:='Unauthorized';
  122. aResponse.WWWAuthenticate:='Basic Realm="This site needs a password"';
  123. aResponse.SendContent;
  124. end;
  125. end;
  126. procedure THTTPApplication.Writeinfo;
  127. Var
  128. I : Integer;
  129. begin
  130. Log(etInfo,'Listening on port %d, serving files from directory: %s (using SSL: %s)',[Port,BaseDir,BoolToStr(UseSSL,'true','false')]);
  131. if not NoIndexPage then
  132. Log(etInfo,'Using index page %s',[IndexPageName]);
  133. end;
  134. procedure THTTPApplication.DoRun;
  135. Var
  136. S : String;
  137. begin
  138. S:=Checkoptions('hqnd:p:i:a:',['help','quiet','noindexpage','directory:','port:','indexpage:','authenticate:']);
  139. if (S<>'') or HasOption('h','help') then
  140. usage(S);
  141. ProcessOptions;
  142. if BaseDir='' then
  143. BaseDir:=GetCurrentDir;
  144. if (BaseDir<>'') then
  145. BaseDir:=IncludeTrailingPathDelimiter(BaseDir);
  146. MimeTypes.LoadKnownTypes;
  147. if Fauth<>'' then
  148. HTTPRouter.RegisterInterceptor('auth',@DoAuthorization);
  149. if not FQuiet then
  150. begin
  151. HTTPRouter.RegisterInterceptor('logstart',@DoRequestStart);
  152. HTTPRouter.RegisterInterceptor('logend',@DoRequestEnd,iaAfter);
  153. end;
  154. TSimpleFileModule.RegisterDefaultRoute;
  155. TSimpleFileModule.BaseDir:=BaseDir;
  156. TSimpleFileModule.OnLog:=@Log;
  157. If not NoIndexPage then
  158. begin
  159. if (IndexPageName='') then
  160. IndexPageName:='index.html';
  161. TSimpleFileModule.IndexPageName:=IndexPageName;
  162. end;
  163. inherited;
  164. end;
  165. begin
  166. Application:=THTTPApplication.Create(Nil);
  167. Application.Initialize;
  168. Application.Run;
  169. Application.Free;
  170. end.