Genivia Home Documentation
The CURL plugin

updated Mon Dec 3 2018 by Robert van Engelen
 
The CURL plugin

Table of Contents

Overview

The CURL plugin for gSOAP provides a bridge for the gSOAP engine to use libcurl for internet communications. While gSOAP provides a full HTTP stack, libcurl can be used to support additional protocols and features by replacing gSOAP's HTTP stack.

CURL plugin setup

To use the CURL plugin:

  1. Add #include "plugin/curlapi.h" to your client-side code and compile your code together with plugin/curlapi.c. Link your code with libcurl.
  2. Add curl_global_init(CURL_GLOBAL_ALL) at the start of your program to initialize CURL. Add curl_global_cleanup() at the end of your program.
  3. In your source code where you create a soap context, register the plugin with this soap context, Or use the soap member of a soapcpp2-generated C++ proxy class. Use soap_register_plugin(soap, soap_curl) to register.
  4. Alternatively, if you have a CURL *curl handle already set up, then register the plugin with soap_register_plugin_arg(soap, soap_curl, curl). The benefit of this is that you can set CURL options of the handle. Do not delete this handle until the soap context is deleted.
  5. If you register multiple other plugins with the context, you should register the CURL plugin always first.

The plugin is not limited to SOAP calls, you can use it with XML REST and JSON in gSOAP. The plugin registry steps are the same for any client-side API service calls.

The CURL plugin supports SOAP with MTOM attachments, including streaming MTOM. Other plugins can be combined with this plugin, such as WSSE for WS-Security.

Note
The CURL plugin increases the overhead of HTTP calls compared to the gSOAP HTTP stack. The overhead is due to buffering of the entire outbound message before sending and buffering of the entire message received. By contrast, gSOAP uses a streaming approach and only buffers the socket communications to (de)serialize XML directly into C/C++ data.

Configuration and settings

To use the CURL plugin, register the plugin with the current soap context using soap_register_plugin(soap, soap_curl). This also creates a new CURL handle that is internally used by the plugin until the soap context is deleted. For C++ proxy classes generated with soapcpp2, register the plugin with the soap member of the proxy class.

The gSOAP HTTP chunked transfer mode SOAP_IO_CHUNK and timeout settings are also used by the CURL plugin, when set, as follows:

#include "plugin/curlapi.h"
...
struct soap *soap;
curl_global_init(CURL_GLOBAL_ALL);
soap = soap_new1(SOAP_IO_CHUNK | SOAP_XML_INDENT);
soap_register_plugin(soap, soap_curl);
soap->connect_timeout = 60; // 1 minute
soap->send_timeout = 10; // 10 seconds
soap->recv_timeout = 10; // 10 seconds
soap->transfer_timeout = 20; // 20 seconds
...
// client program runs
...
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
curl_global_cleanup();

It is strongly recommended to set timeouts. The timeout values specified here are just examples. Actual values depend on the application's performance characteristics.

Also compression is used by the CURL plugin when enabled with SOAP_ENC_ZLIB:

#include "plugin/curlapi.h"
...
struct soap *soap;
curl_global_init(CURL_GLOBAL_ALL);
soap = soap_new1(SOAP_IO_CHUNK | SOAP_ENC_ZLIB | SOAP_XML_INDENT);
soap_register_plugin(soap, soap_curl);
...
// client program runs
...
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
curl_global_cleanup();

When an transmission error occurs, use soap_curl_reset(soap) to reset the plugin. This ensures that the gSOAP IO operations are reset and will behave again normally.

Alternatively, you can create your own CURL *curl handle, configure it, and pass it to the plugin as follows:

#include "plugin/curlapi.h"
...
struct soap *soap;
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_easy_setopt(data->curl, CURLOPT_CONNECTTIMEOUT, 60L);
curl_easy_setopt(data->curl, CURLOPT_TIMEOUT, 10L);
soap = soap_new1(SOAP_XML_INDENT);
soap_register_plugin_arg(soap, soap_curl, curl);
...
// client program runs
...
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
curl_easy_cleanup(curl);
...
curl_global_cleanup();

Note that C++ proxy classes generated by soapcpp2 with option -j have a soap member that should be used to register the plugin with:

#include "plugin/curlapi.h"
...
Proxy proxy(SOAP_XML_INDENT);
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_easy_setopt(data->curl, CURLOPT_CONNECTTIMEOUT, 60L);
curl_easy_setopt(data->curl, CURLOPT_TIMEOUT, 10L);
soap_register_plugin_arg(proxy.soap, soap_curl, curl);
...
// make calls with the proxy object
...
proxy.destroy();
curl_easy_cleanup(curl);
...
curl_global_cleanup();

SOAP client example

This example shows a calculator client application with CURL and gSOAP.

The soapcpp2 command is applied to calc.h with soapcpp2 -c -CL calc.h, where calc.h is:

//gsoap ns service name: calc Simple calculator service described at https://www.genivia.com/dev.html
//gsoap ns service protocol: SOAP
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service namespace: http://websrv.cs.fsu.edu/~engelen/calc.wsdl
//gsoap ns service location: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi
//gsoap ns schema namespace: urn:calc
//gsoap ns service method: add Sums two values
int ns__add(double a, double b, double *result);
//gsoap ns service method: sub Subtracts two values
int ns__sub(double a, double b, double *result);
//gsoap ns service method: mul Multiplies two values
int ns__mul(double a, double b, double *result);
//gsoap ns service method: div Divides two values
int ns__div(double a, double b, double *result);
//gsoap ns service method: pow Raises a to b
int ns__pow(double a, double b, double *result);

This generates soapStub.h, soapH.h, soapC.c, soapClient.c, and calc.nsmap.

To keep this example small, the main program uses the calculator service to add two values:

#include "soapH.h"
#include "calc.nsmap"
#include "plugin/curlapi.h"
const char server[] = "http://websrv.cs.fsu.edu/~engelen/calcserver.cgi";
int main(int argc, char **argv)
{
struct soap *soap = soap_new1(SOAP_XML_INDENT);
double result;
curl_global_init(CURL_GLOBAL_ALL);
soap_register_plugin(soap, soap_curl);
if (soap_call_ns__add(soap, server, "", 2.0, 3.0, &result))
{
soap_print_fault(soap, stderr);
}
else
printf("2 +3 = %g\n", result);
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
curl_global_cleanup();
return 0;
}

We compile this example program together with stdsoap2.c, soapC.c, soapClient.c, plugin/curlapi.c and we link it with libcurl.

As stated previously, to use a current CURL *curl handle that you have created, use soap_register_plugin_arg(soap, soap_curl, curl) to register the plugin.

JSON REST example

See the gSOAP JSON documentation for details about using JSON with gSOAP in C and in C++.

A JSON client in C with CURL has the following outline:

#include "plugin/curlapi.h"
#include "json.h"
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}};
...
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
struct value *request = new_value(ctx);
struct value response;
curl_global_init(CURL_GLOBAL_ALL);
soap_register_plugin(ctx, soap_curl);
... // here we populate the request data to send
if (json_call(ctx, "endpoint URL", request, &response))
{
soap_print_fault(ctx, stderr);
}
else
{
... // use the response data here
}
soap_destroy(ctx); // delete objects
soap_end(ctx); // delete data
... // here we can make other calls etc.
soap_free(ctx); // delete the context
curl_global_cleanup();

As stated previously, to use a current CURL *curl handle that you have created, use soap_register_plugin_arg(soap, soap_curl, curl) to register the plugin.

JSON in C++ is similar to the C example shown with the benefit of the easy-to-use JSON C++ API.