Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: Coap/coap_client.c
- Revision:
- 38:f1bc6e867fcf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Coap/coap_client.c Wed May 24 07:51:33 2017 +0000
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (c) 2015 Keith Cullen.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file coap_client.c
+ *
+ * @brief Source file for the FreeCoAP client library
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "coap_client.h"
+#include "main.h"
+
+#define COAP_CLIENT_ACK_TIMEOUT_SEC 2 /**< Minimum delay to wait before retransmitting a confirmable message */
+#define COAP_CLIENT_MAX_RETRANSMIT 4 /**< Maximum number of times a confirmable message can be retransmitted */
+#define COAP_CLIENT_RESP_TIMEOUT_SEC 30 /**< Maximum amount of time to wait for a response */
+
+/****************************************************************************************************
+ * coap_client *
+ ****************************************************************************************************/
+void coap_client_destroy(coap_client_t *client)
+{
+ memset(client, 0, sizeof(coap_client_t));
+}
+
+/**
+ * @brief Send a message to the server
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Number of bytes sent or error code
+ * @retval >0 Number of bytes sent
+ * @retval <0 Error
+ */
+static int coap_client_send(coap_client_t *client, coap_msg_t *msg)
+{
+ int num = 0;
+ char buf[COAP_MSG_MAX_BUF_LEN] = {0};
+
+ num = coap_msg_format(msg, buf, sizeof(buf));
+ if (num < 0)
+ {
+ return num;
+ }
+ num = send(client->sd, buf, num);
+ if (num < 0)
+ {
+ printf("send error\n");
+ return -errno;
+ }
+
+ return num;
+}
+
+/**
+ * @brief Handle a format error in a received message
+ *
+ * Special handling for the case where a received
+ * message could not be parsed due to a format error.
+ * Extract enough information from the received message
+ * to form a reset message.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] buf Buffer containing the message
+ * @param[in] len length of the buffer
+ */
+static void coap_client_handle_format_error(coap_client_t *client, char *buf, size_t len)
+{
+ coap_msg_t msg = {0};
+ unsigned msg_id = 0;
+ unsigned type = 0;
+ int ret = 0;
+
+ ret = coap_msg_parse_type_msg_id(buf, len, &type, &msg_id);
+ if ((ret == 0) && (type == COAP_MSG_CON))
+ {
+ coap_msg_create(&msg);
+ ret = coap_msg_set_type(&msg, COAP_MSG_RST);
+ if (ret < 0)
+ {
+ coap_msg_destroy(&msg);
+ return;
+ }
+ ret = coap_msg_set_msg_id(&msg, msg_id);
+ if (ret < 0)
+ {
+ coap_msg_destroy(&msg);
+ return;
+ }
+ coap_client_send(client, &msg);
+ coap_msg_destroy(&msg);
+ }
+}
+
+/**
+ * @brief Receive a message from the server
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Number of bytes received or error code
+ * @retval >0 Number of bytes received
+ * @retval <0 Error
+ */
+static int coap_client_recv(coap_client_t *client, coap_msg_t *msg)
+{
+ int num = 0;
+ int ret = 0;
+ char buf[COAP_MSG_MAX_BUF_LEN] = {0};
+
+ num = recv(client->sd, buf, sizeof(buf));
+ if (num < 0)
+ {
+ return -errno;
+ }
+
+ ret = coap_msg_parse(msg, buf, num);
+ if (ret < 0)
+ {
+ if (ret == -EBADMSG)
+ {
+ coap_client_handle_format_error(client, buf, num);
+ }
+ return ret;
+ }
+ return num;
+}
+
+/**
+ * @brief Reject a received confirmable message
+ *
+ * Send a reset message to the server.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_reject_con(coap_client_t *client, coap_msg_t *msg)
+{
+ coap_msg_t rej = {0};
+ int num = 0;
+ int ret = 0;
+
+ printf("Rejecting confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+ coap_msg_create(&rej);
+ ret = coap_msg_set_type(&rej, COAP_MSG_RST);
+ if (ret < 0)
+ {
+ coap_msg_destroy(&rej);
+ return ret;
+ }
+ ret = coap_msg_set_msg_id(&rej, coap_msg_get_msg_id(msg));
+ if (ret < 0)
+ {
+ coap_msg_destroy(&rej);
+ return ret;
+ }
+ num = coap_client_send(client, &rej);
+ coap_msg_destroy(&rej);
+ if (num < 0)
+ {
+ return num;
+ }
+ return 0;
+}
+
+/**
+ * @brief Reject a received non-confirmable message
+ *
+ * @param[in] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ */
+static int coap_client_reject_non(coap_client_t *client, coap_msg_t *msg)
+{
+ printf("Rejecting non-confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+ return 0;
+}
+
+/**
+ * @brief Reject a received acknowledgement message
+ *
+ * @param[in] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ */
+static int coap_client_reject_ack(coap_client_t *client, coap_msg_t *msg)
+{
+ printf("Rejecting acknowledgement message from host %s and port %s\n", client->server_host, client->server_port);
+ return 0;
+}
+
+/**
+ * @brief Reject a received reset message
+ *
+ * @param[in] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ */
+static int coap_client_reject_reset(coap_client_t *client, coap_msg_t *msg)
+{
+ printf("Rejecting reset message from host %s and port %s\n", client->server_host, client->server_port);
+ return 0;
+}
+
+/**
+ * @brief Reject a received message
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_reject(coap_client_t *client, coap_msg_t *msg)
+{
+ if (coap_msg_get_type(msg) == COAP_MSG_CON)
+ {
+ return coap_client_reject_con(client, msg);
+ }
+ else if (coap_msg_get_type(msg) == COAP_MSG_NON)
+ {
+ return coap_client_reject_non(client, msg);
+ }
+ else if (coap_msg_get_type(msg) == COAP_MSG_ACK)
+ {
+ return coap_client_reject_ack(client, msg);
+ }
+ else if (coap_msg_get_type(msg) == COAP_MSG_RST)
+ {
+ return coap_client_reject_reset(client, msg);
+ }
+ return 0; /* should never arrive here */
+}
+
+/**
+ * @brief Send an acknowledgement message to the server
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] msg Pointer to a message structure
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_send_ack(coap_client_t *client, coap_msg_t *msg)
+{
+ coap_msg_t ack = {0};
+ int num = 0;
+ int ret = 0;
+
+ printf("Acknowledging confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+ coap_msg_create(&ack);
+ ret = coap_msg_set_type(&ack, COAP_MSG_ACK);
+ if (ret < 0)
+ {
+ coap_msg_destroy(&ack);
+ return ret;
+ }
+ ret = coap_msg_set_msg_id(&ack, coap_msg_get_msg_id(msg));
+ if (ret < 0)
+ {
+ coap_msg_destroy(&ack);
+ return ret;
+ }
+ num = coap_client_send(client, &ack);
+ coap_msg_destroy(&ack);
+ if (num < 0)
+ {
+ return num;
+ }
+ return 0;
+}
+
+/**
+ * @brief Compare the token values in a request message and a response message
+ *
+ * @param[in] req Pointer to the request message
+ * @param[in] resp Pointer to the response message
+ *
+ * @returns Comparison value
+ * @retval 0 the tokens are not equal
+ * @retval 1 the tokens are equal
+ */
+static int coap_client_match_token(coap_msg_t *req, coap_msg_t *resp)
+{
+ return ((coap_msg_get_token_len(resp) == coap_msg_get_token_len(req))
+ && (memcmp(coap_msg_get_token(resp), coap_msg_get_token(req), coap_msg_get_token_len(req)) == 0));
+}
+
+/**
+ * @brief Check that all of the options in a message are acceptable
+ *
+ * For a proxy, options are acceptable if they are safe to forward or recognized or both.
+ * For a server, options are acceptable if they are elective or recognized or both.
+ *
+ * @param[in] msg Pointer to message structure
+ *
+ * @returns Operation status or bad option number
+ * @retval 0 Success
+ * @retval >0 Bad option number
+ */
+static unsigned coap_client_check_options(coap_msg_t *msg)
+{
+#ifdef COAP_PROXY
+ return coap_msg_check_unsafe_ops(msg);
+#else /* !COAP_PROXY */
+ return coap_msg_check_critical_ops(msg);
+#endif /* COAP_PROXY */
+}
+
+/**
+ * @brief Handle a received piggy-backed response message
+ *
+ * An acknowledgement has been received that contains
+ * the same token as the request. Check the response
+ * contained within it.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] resp Pointer to the response message
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_handle_piggybacked_response(coap_client_t *client, coap_msg_t *resp)
+{
+ unsigned op_num = 0;
+
+ op_num = coap_client_check_options(resp);
+ if (op_num != 0)
+ {
+ printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ printf("Received acknowledgement and response from Server\n");
+ return 0;
+}
+
+/**
+ * @brief Handle a received separate response message
+ *
+ * A separate response has been received that contains
+ * the same token as the request. Check the response
+ * and send an acknowledgement if necessary.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] resp Pointer to the response message
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_handle_sep_response(coap_client_t *client, coap_msg_t *resp)
+{
+ unsigned op_num = 0;
+
+ if (coap_msg_get_type(resp) == COAP_MSG_CON)
+ {
+ printf("Received confirmable response from host %s and port %s\n", client->server_host, client->server_port);
+ op_num = coap_client_check_options(resp);
+ if (op_num != 0)
+ {
+ printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ return coap_client_send_ack(client, resp);
+ }
+ else if (coap_msg_get_type(resp) == COAP_MSG_NON)
+ {
+ printf("Received non-confirmable response from host %s and port %s\n", client->server_host, client->server_port);
+ op_num = coap_client_check_options(resp);
+ if (op_num != 0)
+ {
+ printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ return 0;
+ }
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+}
+
+/**
+ * @brief Handle a separate response to a confirmable request
+ *
+ * An acknowledgement has been received. Receive the
+ * response and send an acknowledgement back to the server.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] req Pointer to the request message
+ * @param[out] resp Pointer to the response message
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_exchange_sep(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+ int num = 0;
+ int ret = 0;
+ int starttimeout,endtimeout;
+
+ /* wait for a separate response to a confirmable request */
+ printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port);
+ while (1)
+ {
+ starttimeout = readseconds();
+ while(1)
+ {
+ num = coap_client_recv(client, resp);
+ if (num < 0)
+ {
+ return num;
+ }
+ else if(num > 0)
+ {
+ break;
+ }
+ endtimeout = readseconds();
+ if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC)
+ {
+ printf("Timeout no data received\n");
+ return -1;
+ }
+ }
+
+ if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+ {
+ if (coap_msg_get_type(resp) == COAP_MSG_ACK)
+ {
+ /* message deduplication */
+ printf("Received duplicate acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
+ continue;
+ }
+ else if (coap_msg_get_type(resp) == COAP_MSG_RST)
+ {
+ return -ECONNRESET;
+ }
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ if (coap_client_match_token(req, resp))
+ {
+ return coap_client_handle_sep_response(client, resp);
+ }
+ /* message deduplication */
+ /* we might have received a duplicate message that was already received from the same server */
+ /* reject the message and continue listening */
+ ret = coap_client_reject(client, resp);
+ if (ret < 0 )
+ {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @brief Handle the response to a confirmable request
+ *
+ * A confirmable request has been sent to the server.
+ * Receive the acknowledgement and response. Send an
+ * acknowledgement if necessary.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] req Pointer to the request message
+ * @param[out] resp Pointer to the response message
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ */
+static int coap_client_exchange_con(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+ int num = 0;
+ int ret = 0;
+ int starttimeout,endtimeout;
+ /* wait for piggy-backed response in ack message
+ * or ack message and separate response message
+ */
+ //printf("Expecting acknowledgement from server\r\n");
+ client->num_retrans = 0;
+ while (1)
+ {
+ int doubletimeout = COAP_CLIENT_ACK_TIMEOUT_SEC;
+ starttimeout = readseconds();
+ while(1)
+ {
+ num = coap_client_recv(client, resp);
+ if (num < 0)
+ {
+ //return num;
+ }
+ else if(num > 0)
+ {
+ break;
+ }
+ endtimeout = readseconds();
+ if((endtimeout - starttimeout) > doubletimeout)
+ {
+ printf("Timeout no data received after %d seconds\n",endtimeout - starttimeout);
+
+ if (client->num_retrans >= COAP_CLIENT_MAX_RETRANSMIT)
+ {
+ printf("Maximum retries reached no ack from server!\r\n");
+ return -ETIMEDOUT;
+ }
+
+ doubletimeout = 2 * doubletimeout;
+ starttimeout = readseconds();
+ client->num_retrans++;
+
+ printf("Retransmitting to server...\r\n");
+ num = coap_client_send(client, req);
+ if (num < 0)
+ {
+ printf("Resending Failed to server...\r\n");
+ return num;
+ }
+ }
+ }
+
+ if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+ {
+ if (coap_msg_get_type(resp) == COAP_MSG_ACK)
+ {
+ if (coap_msg_is_empty(resp))
+ {
+ /* received ack message, wait for separate response message */
+ printf("Received acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
+ return coap_client_exchange_sep(client, req, resp);
+ }
+ else if (coap_client_match_token(req, resp))
+ {
+ return coap_client_handle_piggybacked_response(client, resp);
+ }
+ }
+ else if (coap_msg_get_type(resp) == COAP_MSG_RST)
+ {
+ return -ECONNRESET;
+ }
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ else if (coap_client_match_token(req, resp))
+ {
+ /* RFC7252
+ * as the underlying datagram transport may not be sequence-preserving,
+ * the Confirmable message carrying the response may actually arrive
+ * before or after the Acknowledgement message for the request; for
+ * the purposes of terminating the retransmission sequence, this also
+ * serves as an acknowledgement.
+ */
+ return coap_client_handle_sep_response(client, resp);
+ }
+ /* message deduplication */
+ /* we might have received a duplicate message that was already received from the same server */
+ /* reject the message and continue listening */
+ ret = coap_client_reject(client, resp);
+ if (ret < 0 )
+ {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @brief Handle the response to a non-confirmable request
+ *
+ * A non-confirmable request has been sent to the server.
+ * Receive the response.
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] req Pointer to the request message
+ * @param[out] resp Pointer to the response message
+ *
+ * @returns Operation status
+ * @retval 0 Success
+ * @retval <0 Error
+ **/
+static int coap_client_exchange_non(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+ int num = 0;
+ int ret = 0;
+ int starttimeout,endtimeout;
+
+ printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port);
+ while (1)
+ {
+ starttimeout = readseconds();
+ while(1)
+ {
+ num = coap_client_recv(client, resp);
+ if (num < 0)
+ {
+ return num;
+ }
+ else if(num > 0)
+ {
+ break;
+ }
+ endtimeout = readseconds();
+ if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC)
+ {
+ printf("Timeout no data received\n");
+ return -1;
+ }
+ }
+
+ if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+ {
+ if (coap_msg_get_type(resp) == COAP_MSG_RST)
+ {
+ return -ECONNRESET;
+ }
+ coap_client_reject(client, resp);
+ return -EBADMSG;
+ }
+ if (coap_client_match_token(req, resp))
+ {
+ return coap_client_handle_sep_response(client, resp);
+ }
+ /* message deduplication */
+ /* we might have received a duplicate message that was already received from the same server */
+ /* reject the message and continue listening */
+ ret = coap_client_reject(client, resp);
+ if (ret < 0 )
+ {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int coap_client_exchange(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+ unsigned char msg_id_buf[2] = {0};
+ unsigned msg_id = 0;
+ int num = 0;
+ char token[4] = {0};
+ int ret = 0;
+
+ /* check for a valid request */
+ if ((coap_msg_get_type(req) == COAP_MSG_ACK)
+ || (coap_msg_get_type(req) == COAP_MSG_RST)
+ || (coap_msg_get_code_class(req) != COAP_MSG_REQ))
+ {
+ return -EINVAL;
+ }
+
+ /* generate the message ID */
+ coap_msg_gen_rand_str((char *)msg_id_buf, sizeof(msg_id_buf));
+ msg_id = (((unsigned)msg_id_buf[1]) << 8) | (unsigned)msg_id_buf[0];
+ ret = coap_msg_set_msg_id(req, msg_id);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* generate the token */
+ coap_msg_gen_rand_str(token, sizeof(token));
+ ret = coap_msg_set_token(req, token, sizeof(token));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ num = coap_client_send(client, req);
+ if (num < 0)
+ {
+ return num;
+ }
+
+ if (coap_msg_get_type(req) == COAP_MSG_CON)
+ {
+ return coap_client_exchange_con(client, req, resp);
+ }
+ else if (coap_msg_get_type(req) == COAP_MSG_NON)
+ {
+ return coap_client_exchange_non(client, req, resp);
+ }
+ return -EINVAL;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Test an exchange with the server
+ *
+ * @param[in] data Pointer to a client test data structure
+ *
+ * @returns Test result
+ */
+test_result_t test_exchange_func(char* buf,int buf_len)
+{
+ printf("----------------------------------------\n");
+ test_result_t result = PASS;
+ coap_client_t client = {0};
+ coap_msg_t resp = {0};
+ coap_msg_t req = {0};
+ int ret = 0;
+
+ test_coap_client_msg_op_t req_ops;
+ req_ops.num = COAP_MSG_URI_PATH;
+ req_ops.len = 8;
+ req_ops.val = (char*)"resource";
+
+ test_coap_client_msg_op_t resp_ops;
+ resp_ops.num = 0;
+ resp_ops.len = 0;
+ resp_ops.val = 0;
+
+ test_coap_client_msg_t test1_req;
+ test1_req.type = COAP_MSG_CON;
+ test1_req.code_class = COAP_MSG_REQ;
+ test1_req.code_detail = COAP_MSG_GET;
+ test1_req.ops = req_ops;
+ test1_req.num_ops = 1;
+ test1_req.payload = buf;
+ test1_req.payload_len = buf_len;
+
+ test_coap_client_msg_t test1_resp;
+ test1_resp.type = COAP_MSG_ACK;
+ test1_resp.code_class = COAP_MSG_SUCCESS;
+ test1_resp.code_detail = COAP_MSG_CONTENT;
+ test1_resp.ops = resp_ops;
+ test1_resp.num_ops = 0;
+ test1_resp.payload = (char*)"Hello Client!";
+ test1_resp.payload_len = 13;
+
+ test_coap_client_data_t test1_data;
+ test1_data.desc = "Send a confirmable request to server and expect a piggy-backed response";
+ test1_data.host = HOST;
+ test1_data.port = PORT;
+ test1_data.common_name = (char*)"dummy/server";
+ test1_data.test_req = test1_req;
+ test1_data.test_resp = test1_resp;
+ test1_data.num_msg = 1;
+
+ //printf("%s\n", test1_data.desc);
+
+ if (ret < 0)
+ {
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+
+ coap_msg_create(&req);
+ coap_msg_create(&resp);
+
+ ret = exchange(&client, &test1_data.test_req, &req, &resp);
+ //printf("exchange function\n");
+ if (ret != PASS)
+ {
+ printf("exchange function fail\n");
+ coap_msg_destroy(&resp);
+ coap_msg_destroy(&req);
+ coap_client_destroy(&client);
+ return FAIL;
+ }
+
+ ret = compare_ver_token(&req, &resp);
+ if (ret != PASS)
+ {
+ coap_msg_destroy(&resp);
+ coap_msg_destroy(&req);
+ coap_client_destroy(&client);
+ return FAIL;
+ }
+
+ ret = check_resp(&test1_data.test_resp, &resp);
+ if (ret != PASS)
+ {
+ coap_msg_destroy(&resp);
+ coap_msg_destroy(&req);
+ coap_client_destroy(&client);
+ return FAIL;
+ }
+
+ coap_msg_destroy(&resp);
+ coap_msg_destroy(&req);
+ coap_client_destroy(&client);
+
+ return result;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Print a CoAP message
+ *
+ * @param[in] str String to be printed before the message
+ * @param[in] msg Pointer to a message structure
+ */
+void print_coap_msg(const char *str, coap_msg_t *msg)
+{
+ //coap_msg_op_t *op = NULL;
+ //unsigned num = 0;
+ //unsigned len = 0;
+ unsigned i = 0;
+ //unsigned j = 0;
+ char *payload = NULL;
+ char *token = NULL;
+ //char *val = NULL;
+
+ printf("%s\n", str);
+ printf("ver: 0x%02x\n", coap_msg_get_ver(msg));
+ printf("type: 0x%02x\n", coap_msg_get_type(msg));
+ printf("token_len: %d\n", coap_msg_get_token_len(msg));
+ printf("code_class: %d\n", coap_msg_get_code_class(msg));
+ printf("code_detail: %d\n", coap_msg_get_code_detail(msg));
+ printf("msg_id: 0x%04x\n", coap_msg_get_msg_id(msg));
+ printf("token: ");
+ token = coap_msg_get_token(msg);
+ for (i = 0; i < coap_msg_get_token_len(msg); i++)
+ {
+ printf(" 0x%02x", (unsigned char)token[i]);
+ }
+ printf("\n");
+ /*op = coap_msg_get_first_op(msg);
+ while (op != NULL)
+ {
+ num = coap_msg_op_get_num(op);
+ len = coap_msg_op_get_len(op);
+ val = coap_msg_op_get_val(op);
+ printf("op[%u].num: %u\n", j, num);
+ printf("op[%u].len: %u\n", j, len);
+ printf("op[%u].val: ", j);
+ for (i = 0; i < len; i++)
+ {
+ printf(" 0x%02x", (unsigned char)val[i]);
+ }
+ printf("\n");
+ op = coap_msg_op_get_next(op);
+ j++;
+ }*/
+ printf("payload: ");
+ payload = coap_msg_get_payload(msg);
+ for (i = 0; i < coap_msg_get_payload_len(msg); i++)
+ {
+ printf("%c", payload[i]);
+ //ucReturnCode[i] = payload[i];
+ }
+ //ucReturnCode[i] = '\0';
+ printf("\n");
+ printf("payload_len: %zu\n", coap_msg_get_payload_len(msg));
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Populate a request message with details from a test request message structure
+ *
+ * @param[in] test_req Pointer to a test request message structure
+ * @param[out] req Pointer to a request message structure
+ *
+ * @returns Test result
+ */
+test_result_t populate_req(test_coap_client_msg_t *test_req, coap_msg_t *req)
+{
+ int ret = 0;
+
+ ret = coap_msg_set_type(req, test_req->type);
+ if (ret < 0)
+ {
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+ ret = coap_msg_set_code(req, test_req->code_class, test_req->code_detail);
+ if (ret < 0)
+ {
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+ ret = coap_msg_add_op(req, test_req->ops.num, test_req->ops.len, test_req->ops.val);
+ if (ret < 0)
+ {
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+ if (test_req->payload)
+ {
+ ret = coap_msg_set_payload(req, test_req->payload, test_req->payload_len);
+ if (ret < 0)
+ {
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+ }
+ return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Send a request to the server and receive the response
+ *
+ * @param[in,out] client Pointer to a client structure
+ * @param[in] test_req Pointer to a test request message structure
+ * @param[out] req Pointer to a request message structure
+ * @param[out] resp Pointer to a response message structure
+ *
+ * @returns Test result
+ */
+test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp)
+{
+ test_result_t result = PASS;
+ int ret = 0;
+
+ result = populate_req(test_req, req);
+ if (result != PASS)
+ {
+ printf("populate_req FAIL\n");
+ return result;
+ }
+ ret = coap_client_exchange(client, req, resp);
+ if (ret < 0)
+ {
+ printf("coap_client_exchange FAIL\n");
+ printf("Error : %s\n",strerror(-ret));
+ return FAIL;
+ }
+ //printf("coap_client_exchange PASS\n");
+ print_coap_msg("Sent:", req);
+ print_coap_msg("Received:", resp);
+
+ return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Compare the version and token fields in a request message and a response message
+ *
+ * @param[out] req Pointer to a request message structure
+ * @param[out] resp Pointer to a response message structure
+ *
+ * @returns Test result
+ */
+test_result_t compare_ver_token(coap_msg_t *req, coap_msg_t *resp)
+{
+ if (coap_msg_get_ver(req) != coap_msg_get_ver(resp))
+ {
+ return FAIL;
+ }
+ if (coap_msg_get_token_len(req) != coap_msg_get_token_len(resp))
+ {
+ return FAIL;
+ }
+ else if (memcmp(coap_msg_get_token(req), coap_msg_get_token(resp), coap_msg_get_token_len(req)) != 0)
+ {
+ return FAIL;
+ }
+ return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ * @brief Check the fields in a response message against the expected values
+ *
+ * @param[out] test_resp Pointer to a test response message structure
+ * @param[out] resp Pointer to a response message structure
+ *
+ * @returns Test result
+ */
+test_result_t check_resp(test_coap_client_msg_t *test_resp, coap_msg_t *resp)
+{
+ if (test_resp->type != coap_msg_get_type(resp))
+ {
+ return FAIL;
+ }
+ if (test_resp->code_class != coap_msg_get_code_class(resp))
+ {
+ return FAIL;
+ }
+ if (test_resp->code_detail != coap_msg_get_code_detail(resp))
+ {
+ return FAIL;
+ }
+ return PASS;
+}