
XML REST clients
================

Every type Type with data binding name Name has the following REST operations:

  int soap_GET_Name(struct soap *soap, const char *URL, Type *p)
  int soap_PUT_Name(struct soap *soap, const char *URL, const Type *p)
  int soap_POST_send_Name(struct soap *soap, const char *URL, const Type *p)
  int soap_POST_recv_Name(struct soap *soap, Type *p)

These are GET and PUT methods to read/write a value Type in XML.  The POST
method has a send phase and a receive phase.  After a POST send, a POST receive
MUST be executed (may be with a different type).

There is also a DELETE:

  int soap_DELETE(struct soap *soap, const char *URL)

These functions return SOAP_OK or error code.

XML REST client examples:

  person.h		Person struct uses custom/struct_tm_date.h
  person.c		GET, PUT, POST, DELETE person (C)
  person.cpp		GET, PUT, POST, DELETE person (C++)

Additional REST Examples based on WSDL
======================================

  calcrest.wsdl		WSDL with REST calc operations
  calcrest.c		REST-based calculator client and CGI server in C
  httpgettest.h		demonstrates HTTP GET hook
  httpgettest.c
  httpposttest.h	demonstrates HTTP POST/PUT/DELETE hooks
  httpposttest.c

REST-Based Calculator
=====================

Build steps:

$ wsdl2h -R -c calcrest.wsdl
$ soapcpp2 -0 -L calcrest.h
$ cc -o calcrest calcrest.c soapC.c soapClient.c soapServer.c stdsoap2.c

Example run:

$ ./calcrest add 1 2

HTTP REST Hooks 
===============

1. HTTP REST via hooks: server-side hooks are provided with the gSOAP engine:

  soap::fget(struct soap*)	HTTP GET
  soap::fput(struct soap*)	HTTP PUT
  soap::fdel(struct soap*)	HTTP DELETE
  soap::fopt(struct soap*)	HTTP OPTIONS
  soap::fhead(struct soap*)	HTTP HEAD

  The function being invoked uses the soap.endpoint string to parse the URL.
  The hook should return SOAP_OK or HTTP error code.

2. HTTP REST via plugins: plugins are available for enhanced support for HTTP
   REST GET and POST/PUT/DELETE. See below.

3. In addition, XML can be serialized and deserialized over sockets, file FD,
   and C++ streams. See also the wsdl2h tool output .h file section "XML Data
   Binding" with readers/writers API calls for the XML root elements of a
   schema.

REST support is provided by the following plugins (plugin directory):

  httpget.h	HTTP GET
  httpget.c
  httppost.h	HTTP POST/PUT/DELETE
  httppost.c
  httpform.h	HTTP POST application/x-www-form-urlencoded
  httpform.c
  httpda.h	HTTP Digest authentication (default is basic authentication)
  httpda.c
  httpmd5.h	HTTP MD5 checksum
  httpmd5.c
  threads.h	portable threads
  threads.c

See also the gSOAP documentation:

  Section "RESTful Interface: The HTTP GET Plug-in"
  Section "RESTful Interface: The HTTP POST Plug-in"

The web server example implements GET, POST, and HTML form operations:

  samples/webserver

XML-RPC and JSON examples with HTTP POST:

  samples/xml-rpc-json

HTTP GET Hooks
==============

The HTTP GET plug-in allows your server to handle RESTful HTTP GET requests and
at the same time still serve SOAP-based POST requests. The plug-in provides
support to client applications to issue HTTP GET operations to a server.

Example Client with the GET Hook
--------------------------------

To get the HTTP body as a string:

  #include "plugin/httpget.h"	// also compile and link httpget.c
  ...
  char *body = NULL;
  struct soap *soap = soap_new();
  soap_register_plugin(soap, http_get);
  ...
  if (soap_get_connect(soap, endpoint, NULL)
   || soap_begin_recv(soap)
   || !(body = soap_get_http_body(soap))
   || soap_end_recv(soap))
    ... // error
  soap_destroy(soap);
  soap_end(soap);
  soap_free(soap);  

To get an XML document and deserialize it using a "reader" generated by wsdl2h
and soapcpp2 from an XML schema, say with <ns:root> as a root element
represented by the struct ns__root (or class ns__root in C++):

  #include "plugin/httpget.h"	// also compile and link httpget.c
  ...
  struct ns__root root;
  struct soap *soap = soap_new();
  soap_register_plugin(soap, http_get);
  ...
  soap_default_ns__root(soap, &root);
  if (soap_get_connect(soap, endpoint, NULL)
   || soap_get_ns__root(soap, &root, "root", NULL))
    ... // error
  soap_destroy(soap);
  soap_end(soap);
  soap_free(soap);  

To get a JSON response and convert to C++ (see samples/xml-rpc-json):

  #include "plugin/httpget.h"	// also compile and link httpget.c
  #include "json.h"             // also compile and link json.cpp
  ...
  struct soap *soap = soap_new();
  soap_register_plugin(soap, http_get);
  value val(soap);
  ...
  if (soap_get_connect(soap, endpoint, NULL)
   || soap_begin_recv(soap)
   || json_recv(soap, val)
   || soap_end_recv(soap))
    ... // error
  soap_destroy(soap);
  soap_end(soap);
  soap_free(soap);  

Note: after the call to soap_begin_recv(soap) the soap->http_content string
contains the HTTP ContentType header, which can be used to determine the HTTP
body content information.

Note: if binary data is to be received in a buffer, rather than a string, use:
  soap_http_body(soap, &buf, &len)
which is available in httppost.h and httppost.c

Example Server with the GET Hook
--------------------------------

To extend a SOAP server with GET capability (see also samples/webserver):

  #include "plugin/httpget.h"	// also compile and link httpget.c
  ...
  soap_serve(soap) // run server, see below
  ...
  int my_http_get_handler(struct soap *soap)
  { 
    // use soap_tag_cmp() for pattern matching:
    if (!soap_tag_cmp(soap->path, "*.html"))
    { soap->http_content = "text/html";
      if (soap_response(soap, SOAP_FILE)
       || soap_send(soap, myhtml)
       || soap_end_send(soap))
	return soap->error;
      return SOAP_OK;
    }
    if (!soap_tag_cmp(soap->path, "*.xml"))
    { struct ns__root root;
      ... // populate root XML element
      soap->http_content = "text/xml";
      if (soap_response(soap, SOAP_FILE)
       || soap_put_ns__root(soap, root, NULL, NULL)
       || soap_end_send(soap))
	return soap->error;
      return SOAP_OK;
    }
    return 404; // HTTP Not Found
  }
  ...
  struct soap *soap = soap_new();
  soap_register_plugin_arg(soap, http_get, my_http_get_handler);

To return JSON content, use:

  #include "json.h"             // also compile and link json.cpp
  ...
  int my_http_get_handler(struct soap *soap)
  { ... 
    if (!soap_tag_cmp(soap->path, "*.json"))
    { value val(soap);
      ... // populate root XML element
      soap->http_content = "application/json";
      if (soap_response(soap, SOAP_FILE)
       || json_send(soap, val)
       || soap_end_send(soap))
	return soap->error;
      return SOAP_OK;
    }

HTTP POST Hooks
===============

The HTTP POST plug-in allows your server to handle RESTful HTTP POST requests
and at the same time still serve SOAP-based POST requests. The plug-in also 
provides support for client applications to issue HTTP POST operations to a 
server.

Example Clients with the POST Hook
----------------------------------

To send and receive data over HTTP POST, say HTML content:

  #include "plugin/httppost.h"  // also compile and link plugin/httppost.c
  ...
  struct soap *soap = soap_new();
  char *buf;
  size_t len;
  if (soap_post_connect(soap, endpoint, NULL, "text/html")
   || soap_send(soap, myhtml)
   || soap_end_send(soap))
    ... // error
  if (soap_begin_recv(soap)
   || soap_http_body(soap, &buf, &len)
   || soap_end_recv(soap))
    ... // error
  ... // use data in buf[0..len-1]
  soap_destroy(soap);
  soap_end(soap);
  soap_free(soap);  

To send and receive JSON over HTTP (see samples/xml-rpc-json):

  #include "plugin/httppost.h"  // also compile and link plugin/httppost.c
  #include "json.h"             // also compile and link json.cpp
  ...
  soap *ctx = soap_new();
  value request(ctx);
  // now populate the 'request' data to send
  ...                           
  if (soap_post_connect(ctx, endpoint, NULL, "application/json")
   || json_send(ctx, request)
   || soap_end_send(ctx))
    ... // error
  value response(ctx);
  if (soap_begin_recv(ctx)
   || json_recv(ctx, response)
   || soap_end_recv(ctx))
    ... // error
  // use the 'response' data response
  ...
  // dealloc objects and temp data
  soap_destroy(ctx);
  soap_end(ctx);
  // make other calls etc.
  ...
  // dealloc context
  soap_free(ctx);

Example Server with the POST Hook
---------------------------------

At the server side you need to register the plugin with handlers for MIME
types:

  #include "plugin/httppost.h"  // also compile and link plugin/httppost.c
  ...
  struct http_post_handlers handlers[] =
  { { "image/jpg", jpg_handler },
    { "image/*",   image_handler },
    { "image/*;*", image_handler },
    { "text/*",    text_handler },
    { "text/*;*",  text_handler },
    { NULL }
  };
  struct soap *soap = soap_new();
  soap_register_plugin_arg(&soap, http_post, handlers);
  ...
  soap_serve(soap); // run server, see below
  ...
  int jpg_handler(struct soap *soap)
  { char *buf;
    size_t len;
    if (soap_http_body(soap, &buf, &len)
     || soap_response(soap, SOAP_OK)
     || soap_end_send(soap))
      return soap->error;
    return SOAP_OK;
  }
  int image_handler(struct soap *soap)
  { char *buf;
    size_t len;
    if (soap_http_body(soap, &buf, &len)
     || soap_response(soap, SOAP_HTML)
     || soap_send(soap, "<html>Image received</html>")
     || soap_end_send(soap))
      return soap->error;
    return SOAP_OK;
  }
  int text_handler(struct soap *soap)
  { char *buf;
    size_t len;
    soap_http_body(soap, &buf, &len);
    // set HTTP content header:
    soap->http_content = "text/plain";
    if (soap_response(soap, SOAP_FILE)
     || soap_send_raw(soap, "Thanks!", 6)
     || soap_end_send(soap))
      return soap->error;
    return SOAP_OK;
  }

If you do not use soapcpp2 to generate soap_serve(), then you will need the
following replacement:

  soap_serve(struct soap *soap)
  {
#ifndef WITH_FASTCGI
    soap->keep_alive = soap->max_keep_alive + 1;
#endif
    do
    {
#ifndef WITH_FASTCGI
      if (soap->keep_alive > 0 && soap->max_keep_alive > 0)
	soap->keep_alive--;
#endif
      if (soap_begin_serve(soap))
      {
	if (soap->error >= SOAP_STOP)
	  continue;
	return soap->error;
      }
      soap_closesock(soap);
#ifdef WITH_FASTCGI
      soap_destroy(soap);
      soap_end(soap);
    } while (1);
#else
    } while (soap->keep_alive);
#endif
    return SOAP_OK;
  }

