{ This file is part of the Web Service Toolkit Copyright (c) 2006 by Inoussa OUEDRAOGO This file is provide under modified LGPL licence ( the files COPYING.modifiedLGPL and COPYING.LGPL). 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. } {$INCLUDE wst_global.inc} unit fpc_http_server; interface uses Classes, SysUtils, httpdefs, fphttpserver, server_listener, wst_types; type IObjectRef = interface ['{B62EC733-999D-4DEC-A69F-B7546A16F661}'] function GetObject() : TObject; end; { TFPWorkerObject } TFPWorkerObject = class(TInterfacedObject,IObjectRef) private FHTTPServerObject: TFPHTTPServer; FRootAddress : string; FServerSoftware : String; FOnNotifyMessage : TListnerNotifyMessage; private function GetHandleRequestInThread : Boolean; function GetListeningPort : Integer; procedure SetHandleRequestInThread(const AValue : Boolean); procedure SetListeningPort(const AValue : Integer); procedure ProcessWSDLRequest( ARequest : TRequest; AResponse : TResponse; var APath : string ); procedure ProcessServiceRequest( ARequest : TRequest; AResponse : TResponse; var APath : string ); private procedure RequestHandler( Sender : TObject; Var ARequest : TFPHTTPConnectionRequest; Var AResponse : TFPHTTPConnectionResponse ); protected function GetObject() : TObject; public constructor Create(); destructor Destroy(); override; procedure Start(); procedure Stop(); function IsActive : Boolean; property RootAddress : string read FRootAddress write FRootAddress; property ServerSoftware : string read FServerSoftware write FServerSoftware; property ListeningPort : Integer read GetListeningPort write SetListeningPort; property OnNotifyMessage : TListnerNotifyMessage read FOnNotifyMessage write FOnNotifyMessage; property HandleRequestInThread : Boolean read GetHandleRequestInThread write SetHandleRequestInThread; end; { TServerListnerThread } TServerListnerThread = class(TThread) private FWorkerObject : IObjectRef; public constructor Create(AWorkerObject : TFPWorkerObject); procedure Execute(); override; end; TListenerOption = (loExecuteInThread, loHandleRequestInThread); TListenerOptions = set of TListenerOption; { TwstFPHttpListener } TwstFPHttpListener = class(TwstListener) private FOptions : TListenerOptions; FWorkerObjectRef : IObjectRef; FWorkerObject : TFPWorkerObject; protected procedure SetOnNotifyMessage(const AValue : TListnerNotifyMessage);override; public constructor Create( const AServerIpAddress : string = '127.0.0.1'; const AListningPort : Integer = 8000; const ADefaultClientPort : Integer = 25000; const AServerSoftware : string = 'Web Service Toolkit Application' ); destructor Destroy(); override; class function GetDescription() : string;override; procedure Start();override; procedure Stop();override; function IsActive : Boolean; override; property Options : TListenerOptions read FOptions write FOptions; end; implementation uses wst_consts, base_service_intf, server_service_intf, server_service_imputils, metadata_wsdl; {$IFDEF WST_DBG} procedure Display(const AMsg : string); begin if IsConsole then WriteLn(AMsg); end; {$ENDIF} function ExtractNextPathElement(var AFullPath : string):string; var i : SizeInt; begin Result := ''; if ( Length(AFullPath) > 0 ) then begin while ( Length(AFullPath) > 0 ) and ( AFullPath[1] = sSEPARATOR ) do begin Delete(AFullPath,1,1); end; i := Pos(sSEPARATOR,AFullPath); if ( i < 1 ) then begin Result := AFullPath; AFullPath := ''; end else begin Result := Copy(AFullPath,1,Pred(i)); Delete(AFullPath,1,i); end; end; end; { TServerListnerThread } constructor TServerListnerThread.Create(AWorkerObject : TFPWorkerObject); begin FreeOnTerminate := True; FWorkerObject := AWorkerObject; inherited Create(False); end; procedure TServerListnerThread.Execute(); var locObject : TFPWorkerObject; begin try locObject := TFPWorkerObject(FWorkerObject.GetObject()); locObject.Start(); except end; end; { TFPWorkerObject } procedure TFPWorkerObject.ProcessWSDLRequest( ARequest : TRequest; AResponse : TResponse; var APath : string ); var locRepName, strBuff : string; i : Integer; begin locRepName := ExtractNextPathElement(APath); if AnsiSameText(sWSDL,locRepName) then locRepName := ExtractNextPathElement(APath); strBuff := GenerateWSDL(locRepName,FRootAddress); i:=Length(strBuff); if (StrBuff<>'') then begin AResponse.ContentType := 'text/xml'; AResponse.Content:=strBuff; end else begin AResponse.ContentType := 'text/html'; AResponse.Content := GenerateWSDLHtmlTable(); end; if AResponse.ContentLength=0 then AResponse.ContentLength:=Length(AResponse.Content); end; procedure TFPWorkerObject.ProcessServiceRequest( ARequest : TRequest; AResponse : TResponse; var APath : string ); var trgt,ctntyp, frmt : string; rqst : IRequestBuffer; inStream : TStringStream; begin trgt := ExtractNextPathElement(APath); if AnsiSameText(sWSDL,trgt) then begin ProcessWSDLRequest(ARequest,AResponse,APath); Exit; end; inStream := nil; try inStream := TStringStream.Create(ARequest.Content); try AResponse.ContentStream := TMemoryStream.Create(); ctntyp := ARequest.ContentType; AResponse.ContentType := ctntyp; frmt := Trim(ARequest.QueryFields.Values['format']); rqst := TRequestBuffer.Create(trgt,ctntyp,inStream,AResponse.ContentStream,frmt); rqst.GetPropertyManager().SetProperty(sREMOTE_IP,ARequest.RemoteAddress); HandleServiceRequest(rqst); AResponse.ContentLength:=AResponse.ContentStream.Size; finally inStream.Free(); end; except on e : Exception do begin if Assigned(FOnNotifyMessage) then FOnNotifyMessage(Self,'ProcessData()>> Exception = '+e.Message); raise; end; end; end; function TFPWorkerObject.GetHandleRequestInThread : Boolean; begin Result := FHTTPServerObject.Threaded; end; function TFPWorkerObject.GetListeningPort : Integer; begin Result := FHTTPServerObject.Port; end; procedure TFPWorkerObject.RequestHandler( Sender : TObject; var ARequest : TFPHTTPConnectionRequest; var AResponse : TFPHTTPConnectionResponse ); var {$IFDEF WST_DBG} s : string; j : SizeInt; {$ENDIF} locPath, locPathPart : string; begin AResponse.Server:=FServerSoftware; locPath := ARequest.URL; locPathPart := ExtractNextPathElement(locPath); if AnsiSameText(sSERVICES_PREFIXE,locPathPart) then ProcessServiceRequest(ARequest,AResponse,locPath) else ProcessWSDLRequest(ARequest,AResponse,locPath); try AResponse.SendContent; finally if Assigned(AResponse.ContentStream) then begin AResponse.ContentStream.Free(); AResponse.ContentStream := nil; end; end; end; function TFPWorkerObject.GetObject : TObject; begin Result := Self; end; procedure TFPWorkerObject.SetHandleRequestInThread(const AValue : Boolean); begin if FHTTPServerObject.Active then raise Exception.CreateFmt(SERR_ObjectStateDoesNotAllowOperation,['SetHandleRequestInThread']); FHTTPServerObject.Threaded := AValue; end; procedure TFPWorkerObject.SetListeningPort(const AValue : Integer); begin if FHTTPServerObject.Active then raise Exception.CreateFmt(SERR_ObjectStateDoesNotAllowOperation,['SetListeningPort']); FHTTPServerObject.Port := AValue; end; constructor TFPWorkerObject.Create(); begin inherited Create(); FHTTPServerObject := TFPHTTPServer.Create(nil); FHTTPServerObject.OnRequest := @RequestHandler; end; destructor TFPWorkerObject.Destroy(); begin if (FHTTPServerObject <> nil) then FHTTPServerObject.Active := False; FreeAndNil(FHTTPServerObject); inherited Destroy(); end; procedure TFPWorkerObject.Start(); begin if not FHTTPServerObject.Active then FHTTPServerObject.Active := True; end; procedure TFPWorkerObject.Stop(); begin if FHTTPServerObject.Active then FHTTPServerObject.Active := False; end; function TFPWorkerObject.IsActive : Boolean; begin Result := FHTTPServerObject.Active; end; { TwstFPHttpListener } procedure TwstFPHttpListener.SetOnNotifyMessage(const AValue : TListnerNotifyMessage); begin inherited SetOnNotifyMessage(AValue); if (FWorkerObject <> nil) then FWorkerObject.OnNotifyMessage := AValue; end; constructor TwstFPHttpListener.Create( const AServerIpAddress : string; const AListningPort : Integer; const ADefaultClientPort : Integer; const AServerSoftware : string ); begin inherited Create(); FWorkerObjectRef := TFPWorkerObject.Create(); FWorkerObject := TFPWorkerObject(FWorkerObjectRef.GetObject()); FWorkerObject.RootAddress := AServerIpAddress; FWorkerObject.ServerSoftware := AServerSoftware; FWorkerObject.ListeningPort := AListningPort; end; destructor TwstFPHttpListener.Destroy(); begin if (FWorkerObject <> nil) then Stop(); FWorkerObjectRef := nil; inherited Destroy(); end; procedure TwstFPHttpListener.Start(); begin if not FWorkerObject.IsActive() then begin FWorkerObject.HandleRequestInThread := (loHandleRequestInThread in Options); if (loExecuteInThread in Options) then begin // The thread is create with "FreeOnTerminate := True" TServerListnerThread.Create(FWorkerObject); end else begin FWorkerObject.Start(); end; end; end; procedure TwstFPHttpListener.Stop(); begin if FWorkerObject.IsActive() then begin //In case of the thread(loExecuteInThread in Options), //this will make the thread exit and free itself as "FreeOnTerminate := True" FWorkerObject.Stop(); end; end; class function TwstFPHttpListener.GetDescription() : string; begin Result := 'WST FP HTTP Listener'; end; function TwstFPHttpListener.IsActive: Boolean; begin Result := FWorkerObject.IsActive(); end; initialization end.