JSON current time client & server example ========================================= Overview -------- This example implements a simple JSON REST API client and server in C. For more information about the use of JSON in gSOAP, see the [JSON documentation.](../../doc/xml-rpc-json/html/index.html) Implementing the client application ----------------------------------- The client application [`currentTime.c`](currentTime.c) is run at the command line and displays the current time: #include "json.h" int main() { struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT); struct value *request = new_value(ctx); struct value response; ctx->send_timeout = 10; /* 10 sec, stop if server is not accepting msg */ ctx->recv_timeout = 10; /* 10 sec, stop if server does not respond in time */ /* make the JSON REST POST request and get response */ *string_of(request) = "getCurrentTime"; if (json_call(ctx, "http://www.cs.fsu.edu/~engelen/currentTimeJSON.cgi", request, &response)) soap_print_fault(ctx, stderr); else if (is_string(&response)) /* JSON does not support a dateTime value: this is a string */ printf("Time = %s\n", *string_of(&response)); else /* error? */ { printf("Error: "); json_write(ctx, &response); } /* clean up */ soap_destroy(ctx); soap_end(ctx); soap_free(ctx); return 0; } /* Don't need a namespace table. We put an empty one here to avoid link errors */ struct Namespace namespaces[] = { {NULL, NULL} }; The JSON request message is the string `"getCurrentTime"` that is POSTed to the server. The response is a string with the current time in UTC. The context `ctx` is initialized with `SOAP_C_UTFSTRING` to ensure 8-bit strings contain Unicode in UTF-8 format. The `SOAP_XML_INDENT` flag is optional, it displays JSON with an indented layout (just as XML is indented with this flag). The `struct value` data represents a JSON value (and in fact also an XML-RPC value). The JSON API is called with `json_call(ctx, "URL", request, &response)` using an HTTP POST because both input and output parameters are non-NULL. The call `json_call(ctx, "URL", in, out))` with pointers `in` and `out` to JSON values supports HTTP POST (both `in` and `out` are non-NULL), HTTP GET (`in` is NULL, `out` is non-NULL), HTTP PUT (`in` is non-NULL, `out` is NULL), and HTTP DELETE (`in` and `out` are NULL). JSON values can be read with `json_read(ctx, v)` with pointer `v` to an (empty) JSON value that will be assigned. To read from a file descriptor, set the context `ctx->recvfd` to the descriptor. To read from a string, set the context `ctx->is` to a `char*` string which works in C only. In C++ set the context `ctx->is` input stream to a string stream. JSON values can be written with `json_write(ctx, v)` with pointer `v` to a JSON value that will be saved. To write to a file descriptor, set the context `ctx->sendfd` to the descriptor. To write to a string, set the context `ctx->os` to point to a `char*` to be assigned with the string saved, which works in C only. In C++ set the context `ctx->os` output stream to a string stream. An empty request value is created with `new_value(ctx)`. The response JSON value is a string that is verified with `is_string(&response)` and the string value is obtained with `string_of(&response)`. A JSON value `v` can be assigned a choice of value types: *int_of(v) = 12345LL; /* 64 bit int */ *double_of(v) = 12.34; /* double float */ *string_of(v) = "abc"; /* string */ *string_of(v) = soap_wchar2s(ctx, L"xyz"); /* wide string (converted to UTF-8) */ *bool_of(v) = 0; /* Boolean false (0) or true (1) */ *dateTime_of(v) = soap_dateTime2s(ctx, time(0)); /* time_t value serialized as ISO 8601 date time */ /* create an array [24, 99.99, "abc"] */ *int_of(nth_value(v, 0)) = 24; *double_of(nth_value(v, 1)) = 99.99; *string_of(nth_value(v, 2)) = "abc"; /* create object {"name": "gsoap", "major": 2.8, "©": 2015} */ *string_of(value_at(v, "name")) = "gsoap"; *double_of(value_at(v, "major")) = 2.8; *int_of(value_atw(v, L"©")) = 2015; /* wide string properties are OK */ These functions are also used to retrieve values. For example int n = *int_of(v); which takes or converts the JSON value `v` to an integer. Before taking or converting a JSON value, you may want to check its type: is_null(v) /* true if value is not set (JSON null) */ is_bool(v) /* true if value is a Boolean "true" or "false" value */ is_true(v) /* true if value is Boolean "true" */ is_false(v) /* true if value is Boolean "false" */ is_number(v) /* true if value is a number (int or float) */ is_int(v) /* true if value is a 32 or a 64 bit int */ is_double(v) /* true if value is a 64 bit double floating point (not integer) */ is_string(v) /* true if value is a string */ is_array(v) /* true if array of values */ is_struct(v) /* true if object structure */ The following functions can be used with JSON arrays and objects: void set_struct(v) /* reset/create an empty object structure */ void set_size(v, int) /* reset/change array size or pre-allocate space */ int has_size(v) /* returns array or object structure size or 0 */ struct value *nth_value(v, int) /* returns nth value in array or object structure */ struct value *value_at(v, const char*) /* returns property value of object structure */ struct value *value_atw(v, const wchar_t*) /* returns property value of object structure */ int nth_at(v, const char*) /* returns nth index of property of object or -1 */ int nth_atw(v, const wchar_t*) /* returns nth index of property of object or -1 */ int nth_nth(v, int) /* returns nth index if nth index exists in array or -1 */ See the [JSON documentation](../../doc/xml-rpc-json/html/index.html) for more details on the JSON functions to create, inspect, and retrieve values. For more information about authentication and HTTPS, see the [tutorials.](../../tutorials.html) Also see the [tutorials](../../tutorials.html) on how to set timeouts and handle signals. [![To top](../../images/go-up.png) To top](#) Build steps for the client application -------------------------------------- The use of JSON requires a one-time step to generate a data model that supports both JSON and the older XML-RPC protocols: [command] soapcpp2 -c -CSL xml-rpc.h The `xml-rpc.h` file and all other source code files that we need can be found in the `samples/xml-rpc-json` directory of the gSOAP package. To build the example client application we need `stdsoap2.h`, `stdsoap2.c`, `json.h`, `json.c`, `xml-rpc.c` and the generated `soapStub.h`, `soapH.h`, and `soapC.c` files: [command] cc -DWITH_OPENSSL -DWITH_GZIP -o currentTime currentTime.c \ xml-rpc.c json.c stdsoap2.c soapC.c -lcrypto -lssl -lz This may look odd, to include "soap-related" files in the build steps, but this is with a reason. We can combine JSON with all of our XML/SOAP Web services by simply adding an `#import "xml-rpc.h"` to the header file for soapcpp2 to process. The `xml-rpc.h` file specifies the data model to support JSON and XML-RPC. When combining XML/SOAP with JSON, we compile the `json.c` and `xml-rpc.c` files also, as shown. OpenSSL and Zlib are required for our client application to work propertly with HTTPS. [![To top](../../images/go-up.png) To top](#) Implementing a CGI server application ------------------------------------- The server application `currentTimeServer.c` implements the JSON API with CGI as follows: #include "json.h" int main() { struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); struct value request; /* receive JSON request value */ if (soap_begin_recv(ctx) || json_recv(ctx, &request) || soap_end_recv(ctx)) { soap_send_fault(ctx); /* or better use json_send_fault(ctx) */ } else { struct value *response = new_value(ctx); /* if the name matches: set response to time, else error */ if (is_string(&request) && !strcmp(*string_of(&request), "getCurrentTime")) { *dateTime_of(response) = soap_dateTime2s(ctx, time(0)); } else { *string_of(value_at(response, "fault")) = "Wrong method"; *value_at(response, "detail") = request; } /* set the http content type */ ctx->http_content = "application/json; charset=utf-8"; /* send the response */ if (soap_response(ctx, SOAP_FILE) || json_send(ctx, response) || soap_end_send(ctx)) soap_send_fault(ctx); /* or better use json_send_fault(ctx) */ } soap_destroy(ctx); soap_end(ctx); soap_free(ctx); return 0; } /* Don't need a namespace table. We put an empty one here to avoid link errors */ struct Namespace namespaces[] = { {NULL, NULL} }; Install the CGI application in the cgi-bin folder of your Web server. [![To top](../../images/go-up.png) To top](#) Implementing a stand-alone multi-threaded server application ------------------------------------------------------------ This example shows a stand-alone iterative server that accepts incoming requests on a host port. The program is the same as the CGI service except for the service request dispatching over sockets in a loop: #include "json.h" #include "plugin/threads.h" int serve_request(struct soap*); int main(int argc, char **argv) { struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); int port = 8080; if (!soap_valid_socket(soap_bind(ctx, NULL, port, 100))) { soap_print_fault(ctx, stderr); exit(1); } soap_set_mode(ctx, SOAP_IO_KEEPALIVE); /* enable HTTP keep-alive, which is optional */ ctx->send_timeout = 10; ctx->recv_timeout = 10; ctx->transfer_timeout = 30; while (1) { if (!soap_valid_socket(soap_accept(ctx))) { soap_print_fault(ctx, stderr); } else { THREAD_TYPE tid; void *arg = (void*)soap_copy(ctx); /* use updated THREAD_CREATE from plugin/threads.h https://www.genivia.com/files/threads.zip */ if (!arg) soap_closesock(ctx); else while (THREAD_CREATE(&tid, (void*(*)(void*))serve_request, arg)) sleep(1); } } soap_destroy(ctx); soap_end(ctx); soap_free(ctx); return 0; } int serve_request(struct soap* ctx) { /* HTTP keep-alive max number of iterations */ unsigned int k = ctx->max_keep_alive; struct value *request = new_value(ctx); int err; THREAD_DETACH(THREAD_ID); do { if (ctx->max_keep_alive > 0 && !--k) ctx->keep_alive = 0; /* receive JSON request */ if (soap_begin_recv(ctx) || json_recv(ctx, request) || soap_end_recv(ctx)) { soap_send_fault(ctx); /* or better use json_send_fault(ctx) */ } else { struct value *response = new_value(ctx); if (is_string(request) && !strcmp(*string_of(request), "getCurrentTime")) { *dateTime_of(response) = soap_dateTime2s(ctx, time(0)); } else { *string_of(value_at(response, "fault")) = "Wrong method"; *value_at(response, "detail") = *request; } ctx->http_content = "application/json; charset=utf-8"; if (soap_response(ctx, SOAP_FILE) || json_send(ctx, response) || soap_end_send(ctx)) soap_send_fault(ctx); /* or better use json_send_fault(ctx) */ } /* close (keep-alive may keep socket open when client supports it) */ soap_closesock(ctx); } while (ctx->keep_alive); err = ctx->error; /* clean up */ soap_destroy(ctx); soap_end(ctx); soap_free(ctx); return err; } /* Don't need a namespace table. We put an empty one here to avoid link errors */ struct Namespace namespaces[] = { {NULL, NULL} }; HTTP Keep Alive is enabled by adding the `SOAP_IO_KEEPALIVE` to `soap_new1()` or with `soap_set_mode(ctx, SOAP_IO_KEEPALIVE)`. This enables the inner loop to handle multiple requests over the current connection. Make the JSON response indented with the `SOAP_XML_INDENT` flag. To combine JSON REST with SOAP/XML services that are based on `soap_serve()` to serve SOAP/XML requests, see the [XML-RPC & JSON/JSONPath](https://www.genivia.com/doc/xml-rpc-json/html/index.html#soap) documentation. See the [tutorials](https://www.genivia.com/tutorials.html) on how to use HTTPS and how to harden stand-alone services. See the [documentation](https://www.genivia.com/docs.html) on how to use ISAPI and Apache modules to develop services. [![To top](../../images/go-up.png) To top](#) Build steps for the server application -------------------------------------- Similar to the client example above, the use of JSON requires a one-time step to generate a data model that supports both JSON and the older XML-RPC protocols: [command] soapcpp2 -c -CSL xml-rpc.h The `xml-rpc.h` file and all other source code files that we need can be found in the `samples/xml-rpc-json` directory of the gSOAP package. To build the example server application we need `stdsoap2.h`, `stdsoap2.c`, `json.h`, `json.c`, `xml-rpc.c` and the generated `soapStub.h`, `soapH.h`, and `soapC.c` files: [command] cc -DWITH_OPENSSL -DWITH_GZIP -o currentTimeServer currentTimeServer.c \ xml-rpc.c json.c stdsoap2.c soapC.c -lcrypto -lssl -lz OpenSSL and Zlib are required for our server application to work propertly with HTTPS. [![To top](../../images/go-up.png) To top](#) Running the example ------------------- [command] ./currentTime Time = 2017-06-05T17:26:12Z To run the example against a stand-alone server on port 8080, change the endpoint URL to `"http://localhost:8080"`. [![To top](../../images/go-up.png) To top](#)