WASP Server instances

A single WASP plugin can be loaded by multiple end points to provide the same server on multiple ports. A plugin could, for example, be configured on one end point to provide services to the internal network and on another end point to provide services to the internet. Alternatively, in later WASP releases, a single plugin may be used to provide services over an insecure link on one end point and via an SSL protected link on another.

To enable the plugin to distinguish between connections from a specific end point WASP supports the concept of server instances.

To support server instances in your plugin you need to support the Initialise() export function. As we found out in the previous tutorial, Initialise() is used to allow WASP to pass your plugin the WASP callback table and any configuration data that the config file holds for your plugin. The return value from Initialise() must be zero if you fail to initialise for any reason and non zero if you initialise successfully. The return value from Initialise() is a WaspInstanceHandle and, as you will have seen in previous tutorials, this is passed in to each call into your plugin after Initialise() has been called. This means that you can use the value that you return from Initialise() as a way of identifying a specific instance of your plugin. WASP doesn’t do anything with the value that you return except pass it to subsequent calls into your plugin, it’s an opaque handle, so you’re free to return anything you like, this could be an index into a table of instance data or a pointer to a dynamically allocated structure or class that contains your per instance data.

To enable you to clean up any data that you allocate for your instance in Initialise() there’s an optional plugin entry point, Shutdown(), which is called when WASP is shutting your plugin down. A plugin might use these two functions to manage a dynamically allocated server instance like this:

WaspInstanceHandle __cdecl Initialise(
   WaspFunctions *pWaspFunctions,
   const char *pConfigValue)
{
   if (pWaspFunctions->size < sizeof(WaspFunctions))
   {
      // By testing to see that the supplied function table is
      // larger than or equal to our function table we can
      // return an error which indicates that we failed to
      // initialise if we have been built expecting a later
      // (and therefore larger) function table than the version
      // of WASP that is hosting us is supplying us.

      return 0;
   }

   WaspInstanceHandle handle = 0;

   try
   {
      CServerInstance *pServer = new CServerInstance(
         pConfigValue,
         pWaspFunctions);

      handle = reinterpret_cast<WaspInstanceHandle>(pServer);
   }
   catch (...)
   {
      // mustn't let exceptions leak from our entry points...
   }

   return handle;
}

void __cdecl Shutdown(
   const WaspInstanceHandle instanceHandle)
{
   CServerInstance *pInstance = reinterpret_cast<CServerInstance *>(instanceHandle);

   delete pInstance;
}

This means that we can move all of our other connection handling code into this per instance class, and access it like this:

void __cdecl OnReadCompleted(
   const WaspInstanceHandle instanceHandle,
   const WaspConnectionHandle connectionHandle,
   WaspConnectionUserData connectionUserData,
   const BYTE * const pDataIn,
   const WaspDataLength dataLength)
{
   CServerInstance *pInstance = reinterpret_cast<CServerInstance *>(instanceHandle);

   pInstance->OnReadCompleted(
      connectionHandle,
      connectionUserData,
      pDataIn,
      dataLength);
}

You can download an example project which uses server instances to differentiate between connections on multiple end points from here.