 |
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:
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:
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
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
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.
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 $
|