simpleserver.pas 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. {$mode objfpc}
  2. {$h+}
  3. { $DEFINE USEGNUTLS}
  4. {$DEFINE USEMICROHTTP}
  5. program simpleserver;
  6. {$IFDEF USEMICROHTTP}
  7. {$UNDEF USEGNUTLS}
  8. {$ENDIF}
  9. uses
  10. {$IFNDEF USEMICROHTTP}
  11. {$ifdef USEGNUTLS}
  12. gnutlssockets,
  13. {$else}
  14. opensslsockets,
  15. {$endif}
  16. custhttpapp,
  17. {$ELSE}
  18. cthreads,
  19. custmicrohttpapp,
  20. {$ENDIF}
  21. {$ifdef unix}
  22. baseunix,
  23. {$endif}
  24. sysutils,Classes, inifiles, sslbase, httproute, httpdefs, fpmimetypes, fpwebfile, fpwebproxy, webutil;
  25. Type
  26. { THTTPApplication }
  27. {$IFDEF USEMICROHTTP}
  28. TParentApp = TCustomMicroHTTPApplication;
  29. {$ELSE}
  30. TParentApp = TCustomHTTPApplication;
  31. {$ENDIF}
  32. THTTPApplication = Class(TParentApp)
  33. private
  34. FBaseDir: string;
  35. FIndexPageName: String;
  36. FMimeFile: String;
  37. FNoIndexPage: Boolean;
  38. FQuiet: Boolean;
  39. FBackground : Boolean;
  40. FPassword : string;
  41. FEcho : Boolean;
  42. procedure AddProxy(const aProxyDef: String);
  43. procedure DoEcho(ARequest: TRequest; AResponse: TResponse);
  44. procedure DoProxyLog(Sender: TObject; const aMethod, aLocation, aFromURL, aToURL: String);
  45. procedure Doquit(ARequest: TRequest; AResponse: TResponse);
  46. procedure LoadMimeTypes;
  47. procedure ProcessOptions;
  48. procedure ReadConfigFile(const ConfigFile: string);
  49. procedure Usage(Msg: String);
  50. procedure Writeinfo;
  51. published
  52. procedure DoLog(EventType: TEventType; const Msg: String); override;
  53. Procedure DoRun; override;
  54. property Quiet : Boolean read FQuiet Write FQuiet;
  55. Property MimeFile : String Read FMimeFile Write FMimeFile;
  56. Property BaseDir : string Read FBaseDir Write FBaseDir;
  57. Property NoIndexPage : Boolean Read FNoIndexPage Write FNoIndexPage;
  58. Property IndexPageName : String Read FIndexPageName Write FIndexPageName;
  59. end;
  60. Var
  61. Application : THTTPApplication;
  62. { THTTPApplication }
  63. procedure THTTPApplication.DoEcho(ARequest: TRequest; AResponse: TResponse);
  64. Var
  65. L : TStrings;
  66. begin
  67. L:=TStringList.Create;
  68. try
  69. L.AddStrings(['<!doctype html>',
  70. '<html>',
  71. '<head>',
  72. '<title>Echo request</title>',
  73. '</head>',
  74. '<body>'
  75. ]);
  76. DumpRequest(aRequest,L);
  77. L.AddStrings(['</body>','</html>']);
  78. AResponse.Content:=L.Text;
  79. AResponse.SendResponse;
  80. finally
  81. L.Free;
  82. end;
  83. end;
  84. procedure THTTPApplication.Doquit(ARequest: TRequest; AResponse: TResponse);
  85. Var
  86. PWD : String;
  87. begin
  88. PWD:=ARequest.QueryFields.Values['password'];
  89. if PWD='' then
  90. ARequest.ContentFields.Values['password'];
  91. if PWD=FPassword then
  92. begin
  93. AResponse.Content:='OK';
  94. AResponse.SendContent;
  95. Terminate;
  96. end
  97. else
  98. begin
  99. AResponse.Code:=403;
  100. AResponse.CodeText:='Forbidden';
  101. AResponse.SendContent;
  102. end;
  103. end;
  104. procedure THTTPApplication.DoLog(EventType: TEventType; const Msg: String);
  105. begin
  106. if Quiet then
  107. exit;
  108. if IsConsole then
  109. Writeln(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',Now),' [',EventType,'] ',Msg)
  110. else
  111. inherited DoLog(EventType, Msg);
  112. end;
  113. procedure THTTPApplication.DoProxyLog(Sender: TObject; const aMethod, aLocation, aFromURL, aToURL: String);
  114. Var
  115. Msg : String;
  116. begin
  117. if Quiet then
  118. exit;
  119. Msg:=Format('(Proxy redirect) location: %s, Method: %s, From: %s, to: %s',[aLocation,aMethod,aFromURl,atoURL]);
  120. if IsConsole then
  121. Writeln(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',Now),' [',etInfo,'] ',Msg)
  122. else
  123. inherited DoLog(etInfo, Msg);
  124. end;
  125. procedure THTTPApplication.Usage(Msg : String);
  126. begin
  127. if (Msg<>'') then
  128. Writeln('Error: ',Msg);
  129. Writeln('Usage ',ExtractFileName(ParamStr(0)),' [options] ');
  130. Writeln('Where options is one or more of : ');
  131. Writeln('-c --config=file Ini configuration file (default: simpleserver.ini)');
  132. {$ifdef unix}
  133. Writeln('-b --background fork to background');
  134. {$endif}
  135. Writeln('-d --directory=dir Base directory from which to serve files.');
  136. Writeln(' Default is current working directory: ',GetCurrentDir);
  137. Writeln('-h --help This help text');
  138. Writeln('-i --indexpage=name Directory index page to use (default: index.html)');
  139. Writeln('-n --noindexpage Do not allow index page.');
  140. Writeln('-p --port=NNNN TCP/IP port to listen on (default is 3000)');
  141. Writeln('-m --mimetypes=file path of mime.types. Loaded in addition to OS known types');
  142. Writeln('-q --quiet Do not write diagnostic messages');
  143. Writeln('-Q --quit=PWD register /quit url. Send request with password variable equal to PWD to stop');
  144. Writeln('-e --echo register /quit url. Send request with password variable equal to PWD to stop');
  145. Writeln('-s --ssl Use SSL');
  146. Writeln('-H --hostname=NAME set hostname for self-signed SSL certificate');
  147. Writeln('-x --proxy=proxydef Add proxy definition. Definition is of form:');
  148. Writeln(' name:BaseURL');
  149. Writeln('');
  150. Writeln('Config file is ini file, section [Server]. Key names are long option names');
  151. Writeln('Proxies are defined in section [Proxy], Key is name, value is URL');
  152. Halt(Ord(Msg<>''));
  153. end;
  154. procedure THTTPApplication.LoadMimeTypes;
  155. begin
  156. MimeTypes.LoadKnownTypes;
  157. if MimeFile<>'' then
  158. begin
  159. MimeTypesFile:=MimeFile;
  160. if (MimeTypesFile<>'') and not FileExists(MimeTypesFile) then
  161. begin
  162. Log(etWarning,'mimetypes file not found: '+MimeTypesFile);
  163. MimeTypesFile:='';
  164. end;
  165. end;
  166. If MimeTypesFile<>'' then
  167. MimeTypes.LoadFromFile(MimeTypesFile);
  168. end;
  169. procedure THTTPApplication.AddProxy(Const aProxyDef : String);
  170. Var
  171. P : Integer;
  172. N,URL : String;
  173. begin
  174. P:=Pos(':',aProxyDef);
  175. If P=0 then Raise
  176. EHTTP.CreateFmt('Invalid proxy definition: %s',[aProxyDef]);
  177. N:=Copy(aProxyDef,1,P-1);
  178. URL:=Copy(aProxyDef,P+1,Length(aProxyDef));
  179. ProxyManager.RegisterLocation(N,URL).AppendPathInfo:=True;
  180. end;
  181. procedure THTTPApplication.ReadConfigFile(Const ConfigFile : string);
  182. Const
  183. SConfig = 'Server';
  184. SProxy = 'Proxy';
  185. KeyPort = 'Port';
  186. KeyDir = 'Directory';
  187. KeyIndexPage = 'IndexPage';
  188. KeyHostName = 'hostname';
  189. keyMimetypes = 'mimetypes';
  190. KeySSL = 'SSL';
  191. KeyQuiet = 'quiet';
  192. KeyQuit = 'quit';
  193. KeyEcho = 'echo';
  194. KeyNoIndexPage = 'noindexpage';
  195. KeyBackground = 'background';
  196. Var
  197. L : TStringList;
  198. P,U : String;
  199. I : Integer;
  200. begin
  201. if (ConfigFile='') or Not FileExists(ConfigFile) then exit;
  202. L:=Nil;
  203. With TMemIniFile.Create(ConfigFile) do
  204. try
  205. BaseDir:=ReadString(SConfig,KeyDir,BaseDir);
  206. Port:=ReadInteger(SConfig,KeyPort,Port);
  207. Quiet:=ReadBool(SConfig,KeyQuiet,Quiet);
  208. MimeFile:=ReadString(SConfig,keyMimetypes,MimeFile);
  209. NoIndexPage:=ReadBool(SConfig,KeyNoIndexPage,NoIndexPage);
  210. IndexPageName:=ReadString(SConfig,KeyIndexPage,IndexPageName);
  211. HostName:=ReadString(SConfig,KeyHostName,HostName);
  212. UseSSL:=ReadBool(SConfig,KeySSL,UseSSL);
  213. FBackground:=ReadBool(SConfig,Keybackground,FBackGround);
  214. FPassword:=ReadString(SConfig,KeyQuit,FPassword);
  215. FEcho:=ReadBool(SConfig,KeyEcho,FEcho);
  216. L:=TstringList.Create;
  217. ReadSectionValues(SProxy,L,[]);
  218. For I:=0 to L.Count-1 do
  219. begin
  220. L.GetNameValue(I,P,U);
  221. if (P<>'') and (U<>'') then
  222. ProxyManager.RegisterLocation(P,U).AppendPathInfo:=true;
  223. end;
  224. finally
  225. L.Free;
  226. Free;
  227. end;
  228. end;
  229. procedure THTTPApplication.ProcessOptions;
  230. Var
  231. S : String;
  232. begin
  233. for S in GetOptionValues('x','proxy') do
  234. AddProxy(S);
  235. FEcho:=HasOption('e','echo');
  236. Quiet:=HasOption('q','quiet');
  237. FPassword:=GetOptionValue('Q','quit');
  238. Port:=StrToIntDef(GetOptionValue('p','port'),Port);
  239. LoadMimeTypes;
  240. if HasOption('d','directory') then
  241. BaseDir:=GetOptionValue('d','directory');
  242. UseSSL:=HasOption('s','ssl');
  243. if HasOption('H','hostname') then
  244. HostName:=GetOptionValue('H','hostname');
  245. if HasOption('n','noindexpage') then
  246. NoIndexPage:=True
  247. else
  248. IndexPageName:=GetOptionValue('i','indexpage');
  249. FBackground:=HasOption('b','background');
  250. end;
  251. procedure THTTPApplication.Writeinfo;
  252. Var
  253. I : Integer;
  254. begin
  255. Log(etInfo,'Listening on port %d, serving files from directory: %s (using SSL: %s)',[Port,BaseDir,BoolToStr(UseSSL,'true','false')]);
  256. For I:=0 to ProxyManager.LocationCount-1 do
  257. with ProxyManager.Locations[i] do
  258. Log(etInfo,'Proxy location /proxy/%s redirects to %s',[Path,URL]);
  259. if not NoIndexPage then
  260. Log(etInfo,'Using index page %s',[IndexPageName]);
  261. end;
  262. procedure THTTPApplication.DoRun;
  263. Var
  264. S,ConfigFile : String;
  265. begin
  266. S:=Checkoptions('hqd:ni:p:sH:m:x:c:beQ:',['help','quiet','noindexpage','directory:','port:','indexpage:','ssl','hostname:','mimetypes:','proxy:','config:','background','echo','quit:']);
  267. if (S<>'') or HasOption('h','help') then
  268. usage(S);
  269. if HasOption('c','config') then
  270. ConfigFile:=GetOptionValue('c','config')
  271. else
  272. ConfigFile:='simpleserver.ini';
  273. ReadConfigFile(ConfigFile);
  274. ProcessOptions;
  275. if FBackground then
  276. begin
  277. {$ifdef unix}
  278. if FPFork>0 then Halt(0);
  279. {$else}
  280. Log(erError,'Background option not supported');
  281. {$endif}
  282. end;
  283. if FPassword<>'' then
  284. HTTPRouter.RegisterRoute('/quit',rmAll,@Doquit,False);
  285. if FEcho then
  286. HTTPRouter.RegisterRoute('/echo',rmAll,@DoEcho,False);
  287. if ProxyManager.LocationCount>0 then
  288. begin
  289. TProxyWebModule.RegisterModule('Proxy',True);
  290. ProxyManager.OnLog:=@DoProxyLog;
  291. end;
  292. if BaseDir='' then
  293. BaseDir:=GetCurrentDir;
  294. if (BaseDir<>'') then
  295. BaseDir:=IncludeTrailingPathDelimiter(BaseDir);
  296. TSimpleFileModule.RegisterDefaultRoute;
  297. TSimpleFileModule.BaseDir:=BaseDir;
  298. TSimpleFileModule.OnLog:=@Log;
  299. If not NoIndexPage then
  300. begin
  301. if (IndexPageName='') then
  302. IndexPageName:='index.html';
  303. TSimpleFileModule.IndexPageName:=IndexPageName;
  304. end;
  305. if not Quiet then
  306. WriteInfo;
  307. inherited;
  308. end;
  309. begin
  310. Application:=THTTPApplication.Create(Nil);
  311. Application.Initialize;
  312. Application.Run;
  313. Application.Free;
  314. end.