GridLab logo
Public
* About
* News
* Download
* Documents
* Collaborations
Internal
* Meetings
* Links
* Mailing List
* Management
* Yellow Pages
* Our Eyes Only
Information Society Technologies  
 
| Home | Products & Technologies | Support & Downloads | Contact us |  
GridLab WP-5

WP5: Guide to installing gSOAP

This guide describes how to use gSOAP C/C++ package for GSI-secured WebServices. It expects you to already understand webservices and GSI (Grid Security Infrastructure).

Contents:

Download gSOAP and GSI-plugin

gSOAP is a toolkit for creating clients and servers for WebServices in C/C++. Support for GSI transport-level security was added by Massimo Cafaro and Daniele Lezzi as GSI Plugin for gSOAP. This page describes version 2.0 of the GSI plugin which needs gSOAP version 2.6 and Globus 3.2 libraries. To use it, download:

Install gSOAP and GSI plugin

The downloaded tarballs look big, but in fact you need just six files from them, the rest is documentation and examples. So we can isolate them into a special directory. The files are
  • soapcpp2 - gsoap parser and WSDL generator
  • wsdl2h - gsoap WSDL-to-.h convertor
  • stdsoap2.c - gsoap library
  • stdsoap2.h - gsoap header
  • gsi.c - GSI plugin library
  • gsi.h - GSI plugin header
So do:
tar xzvf gsoap-linux-2.6.tar.gz
tar xzvf gsoap-gsi-plugin-2.0.tgz
mkdir gsisoap/
cp gsoap-linux-2.6/soapcpp2 gsisoap/
cp gsoap-linux-2.6/wsdl2h gsisoap/
cp gsoap-linux-2.6/stdsoap2.c gsisoap/
cp gsoap-linux-2.6/stdsoap2.h gsisoap/
cp gsoap-gsi-plugin-2.0/src/common/gsi.c gsisoap/
cp gsoap-gsi-plugin-2.0/include/gsi.h gsisoap/
Or, you can download gsisoap.tgz file which already has all these files and untar it.
Additionaly you need Globus 3.2 pre-webservices (or Globus 2.4) libraries and set GLOBUS_LOCATION variable. For threaded servers you will need also threaded flavor of Globus libraries. You probbaly already have them, if not, install them. In Globus 2.2 it was done by (I don't know about 3.2):
$GLOBUS_LOCATION/sbin/globus-build -install-only gcc32dbgpthr \
        globus_io-2.2.tar.gz
$GLOBUS_LOCATION/sbin/globus-build -install-only gcc32dbgpthr \
	globus_gram_protocol-2.1.tar.gz
$GLOBUS_LOCATION/sbin/globus-build -install-only gcc32dbgpthr \
	globus_gram_client-2.2.tar.gz

Make GSI-enabled client

Create a file named version.h and write there:
//gsoap ns service name:       VersionService
//gsoap ns service type:       Version
//gsoap ns service port:       httpg://localhost:8080
//gsoap ns service namespace:  http://localhost:8080/axis/services/Version
//gsoap ns service method-style:       getVersion rpc
//gsoap ns service method-encoding:    getVersion literal
//gsoap ns service method-action:      getVersion ""
int ns__getVersion(
  struct ns__getVersionResponse { char * getVersionReturn; } *
  );

That is how gSOAP specifies webservices' interface. This example is intentionally defined so that it matches the "Version" webservice installed by default in Apache Axis, as described in in webservices in Java guide, so you can test interoperability with that. In fact, if you have the Axis server running, you can create this file with command:

gsisoap/wsdl2h -c -o version.h http://localhost:8080/axis/services/Version?wsdl

You also need the implementation of the client, so create a file named client.c and write there:

#include "verH.h"
#include "gsi.h"
#include "ver.nsmap"

int gsi_authorization_callback (struct soap* soap) {
    struct gsi_plugin_data *data;
    data = (struct gsi_plugin_data *) soap_lookup_plugin (soap, GSI_PLUGIN_ID);
    printf("Connected to: %s\n",data->server_identity);
    return 0;
}

int main(int argc,char** argv) {
    char *url = NULL;
    int numrepeat=1;
    struct soap soap;
    struct gsi_plugin_data *data;
    struct ns__getVersionResponse out; 
    int rc,i;
    for(i=1;i<argc;i++) {
	if(strcmp(argv[i],"-n")==0) {
	    if(i+1<argc) {
		sscanf(argv[i+1],"%d",&numrepeat);
		i++;
		continue;
	    } 
	}
	if(strcmp(argv[i],"-url")==0) {
	    if(i+1<argc) {
		url=argv[i+1];
		i++;
		continue;
	    } 
	}
	printf("Usage: verclient [-n times] [-url url]\n");
	exit(1);
    }

   if(url==NULL||strncmp(url,"httpg:",5)==0) {
    //init GSI stuff
    globus_assert (globus_module_activate (GLOBUS_COMMON_MODULE) == GLOBUS_SUCCESS);
    soap_init2(&soap, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);
    soap_set_namespaces(&soap,ver_namespaces);

    if (soap_register_plugin (&soap, globus_gsi)) {
      soap_print_fault (&soap, stderr);
      exit (1);
    }
    /* setup of authorization callback */
    data = (struct gsi_plugin_data *) soap_lookup_plugin (&soap, GSI_PLUGIN_ID);
    data->gsi_authorization_callback = gsi_authorization_callback;
    /* we begin acquiring our credential */
    rc = gsi_acquire_credential(&soap);
    if(rc < 0) exit(1);
    /* setup of GSI channel */
    gsi_set_delegation(&soap, GLOBUS_TRUE);
    gsi_set_replay(&soap, GLOBUS_TRUE);
    gsi_set_sequence(&soap, GLOBUS_TRUE);
    gsi_set_confidentiality(&soap, GLOBUS_TRUE);
    gsi_set_integrity(&soap, GLOBUS_TRUE);

   } else {

    //plain HTTP
    soap_init(&soap);
    soap_set_namespaces(&soap,ver_namespaces);
   }

   for(i=numrepeat;i>0;i--) {
    if(i==1) {
    /* before issuing the last call, we must tell gSOAP that we are going
     to close the connection after this last call
     so that gSOAP can handle Keep-Alive correctly
    */
    soap_clr_omode(&soap, SOAP_IO_KEEPALIVE);
    }
    //call
    if(soap_call_ns__getVersion(&soap,url,NULL,&out) == SOAP_OK) {
      if(strlen(out.getVersionReturn)>50) {
         printf("result: getVersion() = %.50s\n", out.getVersionReturn);
         printf("%d bytes received\n", strlen(out.getVersionReturn));
      } else {
         printf("result: getVersion() = %s\n", out.getVersionReturn);
      }
    } else {// an error occurred 
      soap_print_fault(&soap, stderr);
      exit(1);
    }
   }

    //clean up
    soap_done (&soap);
   if(url==NULL||strncmp(url,"httpg:",5)==0) {
    globus_assert (globus_module_deactivate (GLOBUS_COMMON_MODULE) == GLOBUS_SUCCESS);
   }
}
We will make a library version of client code. It is not necessary for such small example, but will help when creating clients for more than one service in the future. Create an empty file named env.h
//intentionaly left empty
it is used to generate default SOAP headers.
Finaly create Makefile:
#directory for generating "env*" files
ENVDIR=envdir
#directory for generating "ver*" files
VERDIR=verdir
#directory with gSOAP and GSI plugin files
GSISOAP=gsisoap

#flavor of threads to use
GLOBUS_FLAVOR=gcc32dbg
#Globus libraries
LDFLAGS= -L$(GLOBUS_LOCATION)/lib \
		-lglobus_gram_client_$(GLOBUS_FLAVOR) \
		-lglobus_gram_protocol_$(GLOBUS_FLAVOR) \
		-lglobus_gss_assist_$(GLOBUS_FLAVOR) \
		-lglobus_gssapi_gsi_$(GLOBUS_FLAVOR) \
		-lglobus_common_$(GLOBUS_FLAVOR)

#Globus, gSOAP and GSI-plugin includes
CFLAGS=-I$(GSISOAP) -I$(GLOBUS_LOCATION)/include/$(GLOBUS_FLAVOR) \
		-DDEBUG -O2 -g

all: verclient verserver


#GSI
gsi.o: $(GSISOAP)/gsi.c
	gcc $(CFLAGS) -c $(GSISOAP)/gsi.c

#empty definition for standard namespaces and headers
envC.o: env.h
	mkdir $(ENVDIR)
	$(GSISOAP)/soapcpp2 -c -penv -d $(ENVDIR) env.h
	gcc $(CFLAGS) -I$(ENVDIR) -c $(ENVDIR)/envC.c

#main gSOAP library without namespaces
stdsoap2.o: $(GSISOAP)/stdsoap2.c
	gcc $(CFLAGS) -DWITH_NONAMESPACES -c $(GSISOAP)/stdsoap2.c

#libraries specific to Version service
verClientLib.o: $(VERDIR)/verClientLib.c
	gcc $(CFLAGS) -c $(VERDIR)/verClientLib.c 

verServerLib.o: $(VERDIR)/verServerLib.c
	gcc $(CFLAGS) -c $(VERDIR)/verServerLib.c 
		
#generates files for Version library
$(VERDIR)/verClientLib.c: version.h
	mkdir $(VERDIR)
	$(GSISOAP)/soapcpp2 -c -pver -d $(VERDIR) -n version.h
$(VERDIR)/verServerLib.c: version.h
	mkdir $(VERDIR)
	$(GSISOAP)/soapcpp2 -c -pver -d $(VERDIR) -n version.h

verclient:  client.c envC.o verClientLib.o stdsoap2.o gsi.o
	gcc $(CFLAGS) -I$(VERDIR) \
		-o verclient client.c \
		verClientLib.o envC.o stdsoap2.o gsi.o \
		$(LDFLAGS)

verserver:  server.c envC.o  stdsoap2.o verServerLib.o gsi.o
	gcc $(CFLAGS) -I$(VERDIR) \
		-o verserver server.c \
		verServerLib.o envC.o stdsoap2.o gsi.o \
		$(LDFLAGS)

clean:
	rm -f verClient.c verclient verserver VersionService* *.log ver[VSHC]* core *.xsd *.o env[A-Z]*
	rm -rf $(VERDIR) $(ENVDIR)
and type make verclient. That will build the client. If you have the Apache Axis in TomCat with Java GSI-plugin running, you can test it now by running (don't forget to have valid GSI proxy):
./verclient -url httpg://localhost:8443/axis/services/Version

Make GSI-enabled server

We will create a simple server, which uses no threads and no forks. Create a file named server.c:
#include "verH.h"
#include "gsi.h"
#include "ver.nsmap"


static char* returnString = "gSOAP 2.6 server";
int ns__getVersion(struct soap* soap, struct ns__getVersionResponse *out) {

  out->getVersionReturn = returnString;
  return SOAP_OK;
}

int gsi_authorization_callback (struct soap* soap) {
    struct gsi_plugin_data *data;
    data = (struct gsi_plugin_data *) soap_lookup_plugin (soap, GSI_PLUGIN_ID);
    printf("Connected to: %s\n",data->client_identity);
    return 0;
}

int main(int argc,char **argv) {
   struct soap soap;
   int markLarge = 0;
   int size = 1000;
   int port = 8080;
   int rc;
   int i, m, s; // master and slave sockets
   struct gsi_plugin_data *data;


   //options
   for(i=1;i<argc;i++) {
       if(strcmp(argv[i],"-large")==0) {
	       markLarge = 1; 
	       if(i+1<argc) {
		   sscanf(argv[i+1],"%d",&size);
		   i++;
		   continue;
	       }
	       continue;
       }
       printf("Usage: verserver [-large [bytes]]\n");
       exit(1);
   }
   if(markLarge) {
           returnString = (char*)malloc(size+1);
           memset(returnString,'A',size);
	   returnString[size]=0;
   }


   globus_assert (globus_module_activate (GLOBUS_COMMON_MODULE) == GLOBUS_SUCCESS);



  /* we initialize gSOAP for Keep-Alive connections */
  soap_init2(&soap, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);
  soap_set_namespaces(&soap,ver_namespaces);

  /* we register the GSI plugin */
  if (soap_register_plugin (&soap, globus_gsi)){
        soap_print_fault (&soap, stderr);
        exit (1);
  }

  /* we begin acquiring our credential */
  rc = gsi_acquire_credential(&soap);
  if(rc < 0)
        exit(1);

  /* setup of authorization callback */
  data = (struct gsi_plugin_data *) soap_lookup_plugin (&soap, GSI_PLUGIN_ID);
  data->gsi_authorization_callback = gsi_authorization_callback;

  /* listen for incoming connections */
  gsi_listen(&soap, NULL, port, 100);

  for (i = 1; ; i++) {
     struct soap *tsoap;
     /* accepting incoming connections */
     /* TODO */ gsi_accept (&soap);
     if (data->connected_fd < 0) continue;
     /* retrieving information about the peer */
     tsoap=soap_copy(&soap);
     fprintf(stderr, "Accepted connection from %s %s\n", data->host, data->port);
     rc = gsi_accept_security_context(tsoap);
     if(rc == 0){
         ver_serve(tsoap); // process RPC request
         soap_destroy(tsoap); // clean up class instances
         soap_end(tsoap); // clean up everything and close socket
	 soap_done(tsoap);
     } else {
	 fprintf(stderr, "security context not accepted\n");
	 soap_receiver_fault(tsoap,"problems",NULL);
	 soap_send_fault(tsoap);
         soap_end(tsoap); // clean up everything and close socket
     }
  }
}
and type make verserver. Now you can try to run the client against this server, or Java client against this server.
./verserver &
./verclient
This uses default connetcion to httpg://localhost:8080.

A client for multiple services

When you want to write a client which connects to more than one service, the trick (as described in chapter 15.34.2 of gSOAP user guide) is to use switches -p name -n for soapcpp2 compiler, build separate libraries for each service, compile the stdsoap2.c file with option -DWITH_NONAMESPACES, and use call soap_set_namespaces(&soap,name_namespaces) to set appropriate namespaces for calls to each separate service.

However in this moment, when you try to follow the chapter 15.34.2 example, you will find that including headers files for more than one service like

#include "firstStub.h"
#include "secondStub.h"
#include "first.nsmap"
#include "second.nsmap"

produces name clashes. Right now the only solution is to edit these files by hand and remove identical definitions.

That's it. Have fun with GSI WebServices ;-)

Sent any comments to Martin Kuba.

Last updated: $Date: 2004/05/22 16:45:33 $



GridLab: Grid Application Toolkit and Testbed is co-funded by the European Commission under the Fifth Framework Programme (IST-2001-32133).
Web admin: Petr Holub, web design: Radoslaw Strugalski

Last update on Saturday, 22-May-2004 18:45:49 CEST.