1

I am working with the libcoap-dev-1 library under Raspbian with a Raspberry Pi using the library with a simple client and simple server to implement the CoAP protocol. I have a working client and a working server and am now trying to modify the server so that it can accept a URI with a query string and I can then fetch the query string of the URI.

The URI I am currently testing is coap://127.0.0.1/hello?item=1 which has a query parameter, the item=1 after the ?.

The request handler in the server is the following function which is a stub to test out sending a canned response back to the client as JSON text. This works fine and the client receives the JSON text and is able to use the Fast JSON library to parse it.

// header for the libcoap library. it should be in /usr/include/coap
#include <coap/coap.h>

/*
 * The resource handler which the libcoap library will invoke when the registered
 * URI is specified.
 */
static void
hello_handler(coap_context_t *ctx, struct coap_resource_t *resource,
              const coap_endpoint_t *local_interface, coap_address_t *peer,
              coap_pdu_t *request, str *token, coap_pdu_t *response)
{
        static  int iCount = 0;
        unsigned char buf[3];
        const char* response_data     = "{\"device\": \"DEV-01-123\", \"item\" : %d }";
        char response_buf[256] = {0};

        response->hdr->code           = COAP_RESPONSE_CODE(205);
        coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);

        sprintf (response_buf, response_data, iCount);
        coap_add_data  (response, strlen(response_buf), (unsigned char *)response_buf);
        iCount++;

        printf (" request %s\n", resource->uri.s);
}

The printf() at the end of the function prints the URI without the query portion. So what is printed is hello and not hello?item=1.

My question is how can I access the query portion of the URI the client is sending?

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • What version of libcoap are you using? – jwdonahue Dec 26 '17 at 22:55
  • @jwdonahue I believe it is 1.0.x, I installed it with `apt-get` using `sudo apt-get install libcoap-1-0-dev`. I do not have access to the Pi at the moment. What difference does that make? – Richard Chambers Dec 27 '17 at 00:08
  • I found this, https://github.com/obgm/libcoap/blob/develop/examples/contiki/server.c at line 131 using `coap_check_option()` in a decision `if (request != NULL && (option = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter)) && memcmp(COAP_OPT_VALUE(option), "ticks", min(5, COAP_OPT_LENGTH(option))) == 0) {` – Richard Chambers Dec 27 '17 at 00:20
  • Well that's the develop branch, and you are think you have 1.0.x, so it may or may not be relevant. Anyway, what point are you trying to make with that? – jwdonahue Dec 27 '17 at 00:59
  • Of course! Now I see it. Well good luck. I only poked my nose in to see if I could learn something and IETF-RFC-7252 looks like it was worth the effort. Thanks! – jwdonahue Dec 27 '17 at 04:02
  • @jwdonahue just a note to say I have a solution with sample programs in my github repository if you are interested. – Richard Chambers Dec 30 '17 at 16:48
  • where would that be? – jwdonahue Dec 30 '17 at 18:36
  • @jwdonahue in the answer I posted the link. https://github.com/RichardChambers/raspberrypi/tree/master/coap which should be the CoAP sample program that is part of my Raspberry Pi experiments, I plan to take the test program I did for MQTT and do something similar with CoAP in which a sensing device sends data to a server where it is stored in an SQLite database and available for retrieval. – Richard Chambers Dec 30 '17 at 23:13

1 Answers1

2

The libcoap functions for creating a server and a client require a few more steps as part of formatting and retrieving the URI and the URI query information into/from a CoAP request.

The complete sample applications, client.c and server.c, are in my GitHub repository at https://github.com/RichardChambers/raspberrypi/tree/master/coap .

On the server side the message handler will use an option iterator, a coap_opt_iterator_t struct object, to iterate over the options of the coap_pdu_t struct request object. While iterating, the server will be looking for options of type COAP_OPTION_URI_QUERY which will contain a query argument, usually a keyword equal value pair.

So the function hello_handler() of the posted question could be rewritten with a helper function to obtain the list of URI query items as follows:

/*
 * The following helper function, printQueryOption(), is used to
 * iterate over the list of options looking for query type options
 * to the base URI. This is analogous to a web URL that contains
 * a question mark followed by options such as:
 *    http://www.server.com/hello?type=1,id=2345
 *
 * We will return a value of 1 (true) if we found something or a value
 * of 0 (false) if we do not.
*/
static int
printQueryOption (coap_pdu_t *request, coap_opt_iterator_t * popt_iter )
{
        int  iRet = 0;
        coap_opt_t *option;

        // iterate over the options looking for queries. If we find one
        // then print it and return a value of 1, true, to indicate we
        // found one. If we don't find any then return the default value
        // of 0, false.
        while (option = coap_option_next(popt_iter)) {
                // found an option, is it a query option or not.
                if (popt_iter->type != COAP_OPTION_URI_QUERY) continue;
                // it is a query option so print out the query text.
                char xBuff[128] = {0};
                strncpy (xBuff, COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option));
                printf ("    option  len %d\n         %s \n", COAP_OPT_LENGTH(option), xBuff);

                // indicate that we found a query option.
                iRet = 1;
                break;
        }

        return iRet;
}

/*
 * The resource handler which the libcoap library will invoke when the registered
 * URI is specified. This is a simple request handler which will just display some
 * of the information from the request.
 */
static void
hello_handler(coap_context_t *ctx, struct coap_resource_t *resource,
              const coap_endpoint_t *local_interface, coap_address_t *peer,
              coap_pdu_t *request, str *token, coap_pdu_t *response)
{
        static  int iCount = 0;   // a simple count to provide some kind of response data.
        unsigned char buf[3];
        const char* response_data     = "{\"device\": \"DEV-01-123\", \"item\" : %d }";
        char response_buf[256] = {0};

        // generate a response to this request. we have a hard coded JSON text that
        // we are using as a stub for testing.
        response->hdr->code           = COAP_RESPONSE_CODE(205);
        coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);

        sprintf (response_buf, response_data, iCount);
        coap_add_data  (response, strlen(response_buf), (unsigned char *)response_buf);
        iCount++;    // this count is some type of varying data so that we can see things are working.

        if (request != NULL) {
                // there is a request URI so lets print out the base URI and then
                // iterate over the options looking for the Query type options.
                coap_opt_iterator_t  opt_iter;

                printf (" request %s\n", resource->uri.s);
                coap_option_iterator_init (request, &opt_iter, COAP_OPT_ALL);

                // iterate over the options of the request printing out any
                // query text that may exist.
                while (printQueryOption (request, &opt_iter));
        } else {
                printf (" request - NONE\n");
        }
}

On the client side we would build our request adding the queries we want to include in the request with a series of calls of function coap_add_option() as in:

coap_split_uri(server_uri, strlen(server_uri), &uri);
request            = coap_new_pdu();
request->hdr->type = COAP_MESSAGE_CON;
request->hdr->id   = coap_new_message_id(ctx);
request->hdr->code = get_method;

printf (" Request URI: path %d %s\n", uri.path.length, uri.path.s);

coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s);
sprintf (server_query, "item=%d", iItem);
coap_add_option(request, COAP_OPTION_URI_QUERY, strlen(server_query), server_query);
printf ("         Query: len %d %s\n", strlen(server_query), server_query);
sprintf (server_query, "device=%s", aszDevName);
coap_add_option(request, COAP_OPTION_URI_QUERY, strlen(server_query), server_query);
printf ("         Query: len %d %s\n", strlen(server_query), server_query);
sprintf (server_query, "tempo=%s", aszTempoName);
coap_add_option(request, COAP_OPTION_URI_QUERY, strlen(server_query), server_query);
printf ("         Query: len %d %s\n", strlen(server_query), server_query);

These statements create a CoAP request to a particular URI with a set of arguments or query options so that the server's handler of the URI can provide a specific response back to the request. For this example the variables used are hard coded with values like the following:

char *aszDevName = "DEV-01-203";
char *aszTempoName = "TEMPO-12345";
int  iItem = 5;

const char*       server_uri = "coap://127.0.0.1/hello";

The two sample programs can be run in two separate terminal windows, starting the server first. Trying the client three times, the first with a URI of "hello", the second with a URI of "goodbye", and the third time again with a URI of "hello" we see the following output.

From the server:

pi@raspberrypi:~/Documents/raspberrypi/coap $ ./server
 request hello
    option  len 6
         item=5 
    option  len 17
         device=DEV-01-203 
    option  len 17
         tempo=TEMPO-12345 
 request hello
    option  len 6
         item=5 
    option  len 17
         device=DEV-01-203 
    option  len 17
         tempo=TEMPO-12345 

From the terminal window of the client:

pi@raspberrypi:~/Documents/raspberrypi/coap $ ./client
 Request URI: path 5 hello
         Query: len 6 item=5
         Query: len 17 device=DEV-01-203
         Query: len 17 tempo=TEMPO-12345
Received: {"device": "DEV-01-123", "item" : 0 } -> item = 0
pi@raspberrypi:~/Documents/raspberrypi/coap $ ./client goodbye
 Request URI: path 7 goodbye
         Query: len 6 item=5
         Query: len 17 device=DEV-01-203
         Query: len 17 tempo=TEMPO-12345

COAP_RESPONSE_CLASS() unknown.
pi@raspberrypi:~/Documents/raspberrypi/coap $ ./client
 Request URI: path 5 hello
         Query: len 6 item=5
         Query: len 17 device=DEV-01-203
         Query: len 17 tempo=TEMPO-12345
Received: {"device": "DEV-01-123", "item" : 1 } -> item = 1
pi@raspberrypi:~/Documents/raspberrypi/coap $ 
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106