//{$UNDEF WST_DBG} //{$DEFINE WST_DBG} (* Without the broker (WST_BROKER undefined): Apache must be configured to route requests to wst services SetHandler wst-handler Services can then be invoked through the following addressing schema http://127.0.0.1:8080/wst/services/UserService UserService : the target service wst/services : constant. ============================================================================ WST_BROKER(still experimental !!!) enable the service brokering : if enabled, this module just forwards the request to the implementation libraries contained in the WstRootPath path. WST load these libraries in the local file system folder configured by the value of WstRootPath. WstRootPath is a configuration directive which must be in the wst "Location" scope. Example : SetHandler wst-handler WstRootPath "C:/Programmes/lazarus/wst/trunk/samples/library_server/" Services can then be invoked through the following addressing schema http://127.0.0.1:8080/wst/services/lib_server/UserService lib_server : the library name ( without extension ) UserService : the target service wst/services : constant. *) //{$DEFINE WST_BROKER} unit wst_apache_binding; {$mode objfpc}{$H+} interface uses Classes, SysUtils, httpd, apr, apriconv, aprutil; const sWST_ROOT = 'wst'; sSEPARATOR = '/'; sSERVICES_PREFIXE = 'services'; sWSDL = 'WSDL'; sHTTP_BINARY_CONTENT_TYPE = 'application/octet-stream'; sCONTENT_TYPE = 'Content-Type'; {$IFDEF WST_BROKER} sWstRootPath = 'WstRootPath'; // The WST local file system path configure in apache sWST_LIBRARY_EXTENSION = '.dll'; type PWstConfigData = ^TWstConfigData; TWstConfigData = record Dir : PChar; BasePath : PChar; end; {$ENDIF WST_BROKER} var wst_module_ptr : Pmodule = nil; {$IFDEF WST_BROKER} WstConfigData : PWstConfigData = nil; WstCommandStructArray : array[0..1] of command_rec = ( ( name : sWstRootPath; func : ( func_take1 : @ap_set_string_slot ); cmd_data : ( nil {@WstConfigData^.BasePath} ); req_override : OR_ALL; args_how : TAKE1; errmsg : 'usage : WstRootPath ' + LineEnding + ' path is the path to the WST root path.'; ), () ); {$ENDIF WST_BROKER} function wst_RequestHandler(r: Prequest_rec): Integer; {$IFDEF WST_BROKER} function wst_create_dir_config(p: Papr_pool_t; dir: PChar) : Pointer;cdecl; {$ENDIF WST_BROKER} implementation uses base_service_intf, server_service_intf, server_service_imputils, server_service_soap, server_binary_formatter, server_service_xmlrpc, metadata_repository, metadata_wsdl, imp_utils, binary_streamer, library_base_intf, library_imp_utils, DOM, XMLWrite, metadata_service, metadata_service_binder, metadata_service_imp; procedure wst_initialize(); begin RegisterStdTypes(); Server_service_RegisterBinaryFormat(); Server_service_RegisterSoapFormat(); //Server_service_RegisterXmlRpcFormat(); RegisterWSTMetadataServiceImplementationFactory(); Server_service_RegisterWSTMetadataServiceService(); end; {$IFDEF WST_BROKER} function wst_create_dir_config(p: Papr_pool_t; dir: PChar) : Pointer; cdecl; begin WstConfigData := PWstConfigData(apr_palloc(p,SizeOf(TWstConfigData))); FillChar(WstConfigData^,SizeOf(TWstConfigData),#0); WstConfigData^.Dir := apr_pstrdup(p,dir); Result := WstConfigData; end; function GetWstPath(): PChar;inline; begin Result := WstConfigData^.BasePath; end; {$ENDIF WST_BROKER} type PRequestArgument = ^TRequestArgument; TRequestArgument = record Name : shortstring; Value : shortstring; Next : PRequestArgument; end; TRequestInfo = record InnerRequest : Pointer; Root : string; URI : string; ContentType : string; Buffer : string; Arguments : string; ArgList : PRequestArgument; end; TResponseInfo = record ContentText : string; ContentType : string; end; function ParseArgs( const APool : Papr_pool_t; const AArgs : string; const ASeparator : Char = '&' ) : PRequestArgument; var locBuffer, locArg : string; locPrev, locTmpArg : PRequestArgument; begin Result := nil; locBuffer := Trim(AArgs); if not IsStrEmpty(locBuffer) then begin locTmpArg := nil; locPrev := nil; while True do begin locArg := GetToken(locBuffer,ASeparator); if IsStrEmpty(locArg) then Break; locPrev := locTmpArg; locTmpArg := PRequestArgument(apr_palloc(APool,SizeOf(TRequestArgument))); FillChar(locTmpArg^,SizeOf(TRequestArgument),#0); if ( Result = nil ) then begin Result := locTmpArg; end else begin locPrev^.Next := locTmpArg; end; locTmpArg^.Name := GetToken(locArg,'='); locTmpArg^.Value := locArg; end; end; end; function FindArg(const AArgs : PRequestArgument; const AName : string) : PRequestArgument; var p : PRequestArgument; begin Result := nil; p := AArgs; while Assigned(p) do begin if AnsiSameText(AName,AArgs^.Name) then begin Result := p; Break; end; p := p^.Next; end; end; {$IFDEF WST_DBG} procedure SaveStringToFile(const AStr,AFile:string;const AKeepExisting : Boolean); begin with TMemoryStream.Create() do try if AKeepExisting and FileExists(AFile) then begin LoadFromFile(AFile); Position := Size; end; if ( Length(AStr) > 0 ) then Write(AStr[1],Length(AStr)); SaveToFile(AFile); finally Free(); end; end; {$ENDIF WST_DBG} function ReadBuffer(r : Prequest_rec; out rbuf : string ) : Integer; var argsbuffer : string; rsize, len_read, rpos : Integer; loc_length : Integer; begin rbuf := ''; Result := ap_setup_client_block(r, REQUEST_CHUNKED_ERROR); if ( Result <> OK ) then Exit; if ( ap_should_client_block(r) <> 0 ) then begin SetLength(argsbuffer,HUGE_STRING_LEN); FillChar(argsbuffer[1],Length(argsbuffer),0); rsize := 0; len_read := 0; rpos := 0; loc_length := r^.remaining; SetLength(rbuf, loc_length ); while True do begin len_read := ap_get_client_block(r, @(argsbuffer[1]), Length(argsbuffer)); if ( len_read <= 0 ) then Exit; if ( ( rpos + len_read ) > loc_length ) then rsize := loc_length - rpos else rsize := len_read; Move(argsbuffer[1],rbuf[ ( 1 + rpos ) ], rsize); Inc(rpos,rsize); end; end; end; 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; function GetWSDL(const ARepName, ARootAddress: shortstring):string; var strm : TMemoryStream; rep : PServiceRepository; doc :TXMLDocument; i : SizeInt; s : string; begin Result := ''; rep := nil; doc := Nil; i := GetModuleMetadataMngr().IndexOfName(ARepName); if ( i < 0 ) then Exit; strm := TMemoryStream.Create(); try s := GetModuleMetadataMngr().GetRepositoryName(i); GetModuleMetadataMngr().LoadRepositoryName(s,ARootAddress,rep); strm.Clear(); doc := TXMLDocument.Create(); GenerateWSDL(rep,doc); WriteXMLFile(doc,strm); i := strm.Size; if ( i > 0 ) then begin SetLength(Result,i); Move(strm.memory^,Result[1],i); end; finally doc.Free(); strm.Free(); GetModuleMetadataMngr().ClearRepository(rep); end; end; function GenerateWSDLTable(): string; var r : IModuleMetadataMngr; i : Integer; begin r := GetModuleMetadataMngr(); Result := '' + ''+ ''+ 'The Web Service Toolkit generated Metadata table XXXXX'+ ''+ '' + '

The following repositories has available. Click on the link to view the corresponding WSDL.

'+ ''; for i := 0 to Pred(r.GetCount()) do Result := Result + '' + '' + ''; Result := Result + ''+ '
' + Format('',[sSEPARATOR+sWST_ROOT+sSEPARATOR+sSERVICES_PREFIXE+sSEPARATOR+sWSDL+sSEPARATOR+r.GetRepositoryName(i)])+ r.GetRepositoryName(i) + ''+ '
'+ ''+ ''+ ''; end; procedure ProcessWSDLRequest( const ARequestInfo : TRequestInfo; out AResponseInfo : TResponseInfo ); var locRepName, strBuff, locPath : string; i : Integer; begin FillChar(AResponseInfo,SizeOf(TResponseInfo),#0); locPath := ARequestInfo.URI; locRepName := ExtractNextPathElement(locPath); if AnsiSameText(sWSDL,locRepName) then locRepName := ExtractNextPathElement(locPath); strBuff := GetWSDL(locRepName,ARequestInfo.Root); i := Length(strBuff); if ( i > 0 ) then begin AResponseInfo.ContentType := 'text/xml'; AResponseInfo.ContentText := strBuff; Exit; end; AResponseInfo.ContentText := GenerateWSDLTable(); AResponseInfo.ContentType := 'text/html'; end; function ProcessServiceRequest( const ARequestInfo : TRequestInfo; out AResponseInfo : TResponseInfo ):Boolean; var trgt,ctntyp, loc_path : string; rqst : IRequestBuffer; inStream, outStream: TMemoryStream; i : Integer; begin FillChar(AResponseInfo,SizeOf(TResponseInfo),#0); loc_path := ARequestInfo.URI; trgt := ExtractNextPathElement(loc_path); Result := False; if AnsiSameText(sWSDL,trgt) then Exit; Result := True; inStream := nil; outStream := nil; try inStream := TMemoryStream.Create(); outStream := TMemoryStream.Create(); ctntyp := ARequestInfo.ContentType; i := Length(ARequestInfo.Buffer); if ( i > 0 ) then inStream.Write(ARequestInfo.Buffer[1],i); inStream.Position := 0; if AnsiSameText(sBINARY_CONTENT_TYPE,ctntyp) then AResponseInfo.ContentType := sHTTP_BINARY_CONTENT_TYPE else AResponseInfo.ContentType := ctntyp; rqst := TRequestBuffer.Create(trgt,ctntyp,inStream,outStream,ARequestInfo.ContentType); HandleServiceRequest(rqst); i := outStream.Size; if ( i > 0 ) then begin SetLength(AResponseInfo.ContentText,i); Move(outStream.Memory^,AResponseInfo.ContentText[1],i); end; finally outStream.Free(); inStream.Free(); {$IFDEF WST_DBG} SaveStringToFile('RequestInfo.Arguments=' + ARequestInfo.Arguments + LineEnding,'c:\log.log',False); SaveStringToFile('ResponseInfo.ContentText=' + AResponseInfo.ContentText + LineEnding,'c:\log.log',False); {$ENDIF} end; end; {$IFDEF WST_BROKER} const MAX_ERR_LEN = 500; function ProcessServiceRequestLibrary( const ARequestInfo : TRequestInfo; out AResponseInfo : TResponseInfo ) : Boolean; var loc_path, ctntyp : string; targetModuleName, targetFormat, targetService : string; wrtr : IDataStore; buffStream : TMemoryStream; strBuff : string; intfBuffer : IwstStream; bl : LongInt; targetModule : IwstModule; handlerProc : TwstLibraryHandlerFunction; pArg : PRequestArgument; i : Integer; begin try FillChar(AResponseInfo,SizeOf(TResponseInfo),#0); loc_path := ARequestInfo.URI; targetModuleName := ExtractNextPathElement(loc_path); Result := False; targetModule := LibraryManager.Get(GetWstPath() + targetModuleName + sWST_LIBRARY_EXTENSION); handlerProc := TwstLibraryHandlerFunction(targetModule.GetProc(WST_LIB_HANDLER)); if not Assigned(handlerProc) then Exit; targetService := ExtractNextPathElement(loc_path); if AnsiSameText(sWSDL,targetService) then Exit; pArg := FindArg(ARequestInfo.ArgList,'format'); if Assigned(pArg) then targetFormat := pArg^.Value; if IsStrEmpty(targetFormat) then targetFormat := ARequestInfo.ContentType; buffStream := TMemoryStream.Create(); try wrtr := CreateBinaryWriter(buffStream); wrtr.WriteInt32S(0); wrtr.WriteAnsiStr(targetService); wrtr.WriteAnsiStr(ARequestInfo.ContentType); wrtr.WriteAnsiStr(targetFormat); wrtr.WriteAnsiStr(ARequestInfo.Buffer); buffStream.Position := 0; wrtr.WriteInt32S(buffStream.Size-4); buffStream.Position := 0; intfBuffer := TwstStream.Create(buffStream); bl := MAX_ERR_LEN; strBuff := StringOfChar(#0,bl); i := handlerProc(intfBuffer,Pointer(strBuff),bl); if ( i <> RET_OK ) then raise Exception.CreateFmt('Library server error :'#13'Code : %d'#13'Message : %s',[i,strBuff]); if AnsiSameText(sBINARY_CONTENT_TYPE,ARequestInfo.ContentType) then AResponseInfo.ContentType := sHTTP_BINARY_CONTENT_TYPE else AResponseInfo.ContentType := ARequestInfo.ContentType; buffStream.Position := 0; if ( buffStream.Size > 0 ) then begin SetLength(AResponseInfo.ContentText,buffStream.Size); buffStream.Read(AResponseInfo.ContentText[1],Length(AResponseInfo.ContentText)); end else begin AResponseInfo.ContentText := ''; end; finally buffStream.Free(); end; Result := True; except on e : Exception do begin Result := False; ap_log_rerror(PCHAR('wst_apache_binding'),392,APLOG_ERR,0,Prequest_rec(ARequestInfo.InnerRequest),PCHAR(e.Message),[]); end; end; end; {$ENDIF WST_BROKER} function wst_RequestHandler(r: Prequest_rec): Integer; function FillRequestInfo(var ARequestInfo : TRequestInfo):Integer; begin ARequestInfo.InnerRequest := r; ARequestInfo.ContentType := apr_table_get(r^.headers_in,sCONTENT_TYPE); ARequestInfo.Root := ap_get_server_name(r) + sSEPARATOR + sWST_ROOT + sSEPARATOR; ARequestInfo.URI := r^.uri; ARequestInfo.Arguments := r^.args; ARequestInfo.ArgList := ParseArgs(r^.pool,ARequestInfo.Arguments); Result := ReadBuffer(r,ARequestInfo.Buffer); end; var loc_RequestInfo : TRequestInfo; loc_ResponseInfo : TResponseInfo; begin Result := FillRequestInfo(loc_RequestInfo); if not AnsiSameText(sWST_ROOT,ExtractNextPathElement(loc_RequestInfo.URI)) then Result := DECLINED; if ( Result <> OK ) then Exit; try if AnsiSameText(sSERVICES_PREFIXE,ExtractNextPathElement(loc_RequestInfo.URI)) then begin {$IFDEF WST_BROKER} if not ProcessServiceRequestLibrary(loc_RequestInfo,loc_ResponseInfo) then ProcessWSDLRequest(loc_RequestInfo,loc_ResponseInfo); {$ELSE} if not ProcessServiceRequest(loc_RequestInfo,loc_ResponseInfo) then ProcessWSDLRequest(loc_RequestInfo,loc_ResponseInfo); {$ENDIF} end else begin ProcessWSDLRequest(loc_RequestInfo,loc_ResponseInfo); end; ap_set_content_type(r, PCHAR(loc_ResponseInfo.ContentType)); // if AnsiSameText(sHTTP_BINARY_CONTENT_TYPE,loc_ResponseInfo.ContentType) then begin ap_set_content_length(r,Length(loc_ResponseInfo.ContentText)); ap_rwrite(@(loc_ResponseInfo.ContentText[1]),Length(loc_ResponseInfo.ContentText),r); ap_rflush(r); { end else begin ap_rputs(PCHAR(loc_ResponseInfo.ContentText), r); ap_rflush(r); end;} Result := OK; except on e : Exception do begin ap_set_content_type(r, 'text/html'); ap_rputs(' Error' + LineEnding, r); ap_rputs('',r); ap_rprintf(r, '

"%s"

' + LineEnding, [PCHAR(e.Message)]); ap_rflush(r); Exit; end; end; end; initialization wst_initialize(); end.