Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

SOAPing without a Web Server

0.00/5 (No votes)
17 May 2001 1  
Demonstrates how to use ATL Server to build SOAP servers/clients over various transport channels.

ATL Server is a new set of classes in ATL for creating Web Applications and Web Services. These Web Services are completely compatible with clients written in C++, C#, VB and other tools that can call a Web Service through SOAP. The Web Service support in ATL Server only supports invocation over an HTTP tranport for this version, however like the rest of ATL, ATL Server is designed to be very flexible. You may wish to create Web Services which can be called over other transport mechanisms, such as TCP/IP, and this article will help explain how you would go about changing the default transport ATL Server supports for Web Services. Two examples are included, one implementing a Web Service which can be called over TCP/IP and another sample which uses a Floppy Disk for the transport

This article shows how to separate the SOAP support offered by ATL Server from the ISAPI framework, allowing use of ATL Server to build SOAP servers and clients over virtually any communication channel.

Summary:

Server Side

The ATL Server Web Service Wizard will generate the code for a Web Service exposed through HTTP. The Web Service is implemented as a class with the following declaration:

[
request_handler(name="Default", sdl="GenSimpleSoapAppServiceSDL"),
soap_handler(
		name="SimpleSoapAppService", 
		namespace="urn:SimpleSoapAppService",
		protocol="soap"
	)
]
class CSimpleSoapAppService :
	public ISimpleSoapAppService

In this code sequence, the attributes before the class declaration do most of the exposing. The soap_handler attribute will inject the following methods : DispatchSoapCall, ParseSoapHeaders and __AtlGetSDL. These methods are required for required for handling SOAP messages and mapping them to internal method calls and also for wrapping the result of internal methods invocations to SOAP responses. (The MSDN library coming with Visual Studio.NET 7.0 Beta1 contains details on the code injected by this attribute). The request_handler attribute will make it able to handle HTTP requests. If the transport layer is not going to be HTTP, the request_handler attribute is no longer needed, so it can be commented out.

The CSoapHandler<> class in atlsoap.h is designed to handle SOAP requests through HTTP. The entry point for handling an HTTP SOAP request is:

HTTP_CODE HandleRequest(AtlServerRequest *, IServiceProvider *)

The request body and the SOAP related HTTP header ("SOAPAction") are retrieved from the ISAPI extension through the pServerRequest(an ECB wrapper)member of the AtlServerRequest* parameter. The pServerRequest object provides methods from reading the request body, reading the SOAPAction HTTP header and writing the SOAP response back to the client. As the purpose is to replace the HTTP transport, we need to find another way to launch the SOAP request processing and to provide it with a way of performing the same operations.

This is solved by creating a CSoapHandler derivative, CSoapTransportHandler, that implements the following entry point (file SoapTransportSrv.h of the downloadable code):

HTTP_CODE InvokeSoapMethod(stSoapTransportDescription	*pTransInfo) 

And the stSoapTransportDescription structure is defined as:

struct  stSoapTransportDescription
{
// the stream to write the SOAP response to

	IWriteStream     *pWriteStream;	
// the stream to read the SOAP request from

	IStream	         *pReadStream;
// the SOAP Action	

	CStringA          strSOAPAction;	 
// the service provider

	IServiceProvider *pServiceProvider; 
};

Perhaps the only member that needs a few details is pServiceProvider. It allows the SOAP servers to share services exactly as those in an ISAPI dll. It can be NULL in an implementation, as long as the SOAP servers will not attempt to use it.

Now, the SOAP server class generated by the wizard has to be modified to use the new CSoapTransportHandler functionality. The new code would look like:

[
	//request_handler(name="Default", sdl= "GenSimpleSoapAppWSDL"),

	soap_handler(
		name="SimpleSoapAppService", 
		namespace="urn:SimpleSoapAppService",
		protocol="soap"
	)
]
class CSimpleSoapAppService :
	public CSoapTransportHandler<CSimpleSoapAppService>,
	public ISimpleSoapAppService
	...

There is no other modification that has to be done in the wizard-generated server code.

Right now, the SOAP server can be instantiated and the SOAP methods can be invoked through CSoapTransportHandler::InvokeSoapMethod, therefore with custom streams to read from and write to, and it does not depend on the ATL Server HTTP support anymore.

The instantiation of the server(s) is handled by another class, CSoapDispatcher, implemented in the soapDispatch.h file. The CSoapDispatcher class fills a stSoapTransportDescription structure, then instantiates a SOAP server and invokes a SOAP call like below

	stSoapTransportDescription transInfo;
	// set the transInfo stream pointers

	...
	CComObjectNoLock<CSimpleSoapAppService> srv;
	HRESULT	hRet = S_OK;
	try
	{
		hRet = srv.InvokeSoapMethod( &transInfo );
	}
	catch(...)
...

At this point, a server application only has to:

  • get the SOAP request and the SOAP Action header through any transport channel
  • wrap the request in an IStream interface
  • provide an IWriteStream to store the response
  • invoke CSoapDispatcher::DispatchCall
  • send the content of the write stream (the SOAP response) over any transport channel to the client

Client Side

The SOAP proxy class generated by sproxy.exe has the following prototype:

class CSimpleSoapAppService : public CSoapSocketClient
...

By inspecting the code generated by sproxy.exe, one can notice that only a few methods/members of the CSoapSocketClient are actually used by the proxy class. These would be

  • the constructor
  • HRESULT SendRequest(LPCTSTR tszAction) throw()
  • void Cleanup() throw()
  • m_Stream, used as an IStream interface to store the request before sending
  • m_ReadStream, used as an IStream interface to store the response before parsing

The method that does most of the work here is SendRequest. This method is supposed to send the request from m_Stream to the SOAP server and to fill m_ReadStream with the server response. By replacing (in the sproxy.exe generated file) CSoapSocketClient with a custom class that contains all these methods and attributes, the actual channel used to send the request to the SOAP server only depends on the implementation of the SendRequest method.

It is exactly what happens in the attached sample code. To reuse the sproxy.exe generated code for such different transport classes, the following modification would be useful:

template<TTransportClass>
class CSimpleSoapAppService : public TTransportClass
...

Transport Channels

The sample implements the following transport channels:

  • Communication through files saved on a storage media(in the FloppyTransport folder)
  • TCP/IP communication (in the TCPIPTransport folder)

Each transport channel implementation contains a client application and a server application. The server uses the SOAP server included in the Include folder (which is built as described above). The code in the server application only takes care of the transport. The client application only implements the transport specific class to act as a TTransportClass parameter for the modified class generated by sproxy.exe

Please note that the modifications specified above only allow sending/receiving SOAP messages over the custom channel. They do not allow retrieving the description (SDL) for the service. Therefore, a limitation of the TCPIPTransport and FloppyTransport samples is that the SDL used in generating the proxy has to be generated outside of the sample. The one used in the applications specified above is saved as simpleSoapSrv.wsdl in the Include folder. To get the server description, one would have to create the SOAP server through the wizard, complete the interface, then save the SDL through the default HTTP way provided by ATL Server.

An Extension : HTTPListenerTransport

This folder contains an enhanced version of the TCPIPTransport server. This server listens on a specified TCPIP port, but it accepts HTTP requests. It provides a way of responding to .disco requests, to generate the SDL for the implemented SOAP servers and also to accept SOAP invocations. This server can provide a very light-weight, complete, SOAP implementation, without requiring an HTTP server to be installed on the machine. To create a client for this light server, just use the Add Web Reference wizard from the Visual Studio.NET 7.0 Beta1 IDE, and point the wizard to the following URL:

http://<your_machine>:<ListenPort>/disco

If you build the HTTPListenerServer sample, the URL would become:

http://localhost:333/disco

The C# client provided for this server (HttpListenerCSharpClient) is generated this way

Final Considerations

To build the sample code, just unzip the source archive, then load and build the solution file (ATLSSoapTransport.sln)

An updated version of the sample code will be included as an ATL Server sample starting with Visual Studio.NET Beta2. It will add the following :

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here