net: coap: add support for Echo option (RFC 9175)
Resend the request with Echo option and the received Echo option value when receiving a 4.01 (Unauthorized) response with the Echo option. Add missing header file kernel.h to coap_client.h. Signed-off-by: Juha Ylinen <juha.ylinen@nordicsemi.no>
This commit is contained in:
parent
1d9098f3f0
commit
1aef034126
@ -61,6 +61,7 @@ enum coap_option_num {
|
||||
COAP_OPTION_PROXY_URI = 35, /**< Proxy-Uri */
|
||||
COAP_OPTION_PROXY_SCHEME = 39, /**< Proxy-Scheme */
|
||||
COAP_OPTION_SIZE1 = 60, /**< Size1 */
|
||||
COAP_OPTION_ECHO = 252, /**< Echo (RFC 9175) */
|
||||
COAP_OPTION_REQUEST_TAG = 292 /**< Request-Tag (RFC 9175) */
|
||||
};
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <zephyr/net/coap.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
|
||||
#define MAX_COAP_MSG_LEN (CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE + \
|
||||
@ -101,6 +102,8 @@ struct coap_client {
|
||||
uint8_t send_buf[MAX_COAP_MSG_LEN];
|
||||
uint8_t recv_buf[MAX_COAP_MSG_LEN];
|
||||
struct coap_client_internal_request requests[CONFIG_COAP_CLIENT_MAX_REQUESTS];
|
||||
struct coap_option echo_option;
|
||||
bool send_echo;
|
||||
};
|
||||
/** @endcond */
|
||||
|
||||
|
||||
@ -332,6 +332,17 @@ int coap_client_req(struct coap_client *client, int sock, const struct sockaddr
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (client->send_echo) {
|
||||
ret = coap_packet_append_option(&internal_req->request, COAP_OPTION_ECHO,
|
||||
client->echo_option.value, client->echo_option.len);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to append echo option");
|
||||
k_mutex_unlock(&client->send_mutex);
|
||||
goto out;
|
||||
}
|
||||
client->send_echo = false;
|
||||
}
|
||||
|
||||
ret = coap_client_schedule_poll(client, sock, req, internal_req);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to schedule polling");
|
||||
@ -604,6 +615,11 @@ struct coap_client_internal_request *get_request_with_token(struct coap_client *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool find_echo_option(const struct coap_packet *response, struct coap_option *option)
|
||||
{
|
||||
return coap_find_options(response, COAP_OPTION_ECHO, option, 1);
|
||||
}
|
||||
|
||||
static int handle_response(struct coap_client *client, const struct coap_packet *response)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -651,6 +667,60 @@ static int handle_response(struct coap_client *client, const struct coap_packet
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Received echo option */
|
||||
if (find_echo_option(response, &client->echo_option)) {
|
||||
/* Resend request with echo option */
|
||||
if (response_code == COAP_RESPONSE_CODE_UNAUTHORIZED) {
|
||||
k_mutex_lock(&client->send_mutex, K_FOREVER);
|
||||
|
||||
ret = coap_client_init_request(client, &internal_req->coap_request,
|
||||
internal_req, false);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error creating a CoAP request");
|
||||
k_mutex_unlock(&client->send_mutex);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = coap_packet_append_option(&internal_req->request, COAP_OPTION_ECHO,
|
||||
client->echo_option.value,
|
||||
client->echo_option.len);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to append echo option");
|
||||
k_mutex_unlock(&client->send_mutex);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (coap_header_get_type(&internal_req->request) == COAP_TYPE_CON) {
|
||||
ret = coap_pending_init(&internal_req->pending,
|
||||
&internal_req->request, &client->address,
|
||||
internal_req->retry_count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error creating pending");
|
||||
k_mutex_unlock(&client->send_mutex);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
coap_pending_cycle(&internal_req->pending);
|
||||
}
|
||||
|
||||
ret = send_request(client->fd, internal_req->request.data,
|
||||
internal_req->request.offset, 0, &client->address,
|
||||
client->socklen);
|
||||
k_mutex_unlock(&client->send_mutex);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error sending a CoAP request");
|
||||
goto fail;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* Send echo in next request */
|
||||
client->send_echo = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send ack for CON */
|
||||
if (response_type == COAP_TYPE_CON) {
|
||||
/* CON response is always a separate response, respond with empty ACK. */
|
||||
|
||||
@ -75,6 +75,89 @@ static ssize_t z_impl_zsock_sendto_custom_fake(int sock, void *buf, size_t len,
|
||||
last_response_code = ((uint8_t *) buf)[1];
|
||||
|
||||
LOG_INF("Latest message ID: %d", last_message_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t z_impl_zsock_sendto_custom_fake_echo(int sock, void *buf, size_t len,
|
||||
int flags, const struct sockaddr *dest_addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
uint16_t last_message_id = 0;
|
||||
struct coap_packet response = {0};
|
||||
struct coap_option option = {0};
|
||||
|
||||
last_message_id |= ((uint8_t *) buf)[2] << 8;
|
||||
last_message_id |= ((uint8_t *) buf)[3];
|
||||
|
||||
if (messages_needing_response[0] == 0) {
|
||||
messages_needing_response[0] = last_message_id;
|
||||
} else {
|
||||
messages_needing_response[1] = last_message_id;
|
||||
}
|
||||
|
||||
last_response_code = ((uint8_t *) buf)[1];
|
||||
|
||||
LOG_INF("Latest message ID: %d", last_message_id);
|
||||
|
||||
int ret = coap_packet_parse(&response, buf, len, NULL, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Invalid data received");
|
||||
}
|
||||
|
||||
ret = coap_find_options(&response, COAP_OPTION_ECHO, &option, 1);
|
||||
|
||||
zassert_equal(ret, 1, "Coap echo option not found, %d", ret);
|
||||
zassert_mem_equal(option.value, "echo_value", option.len, "Incorrect echo data");
|
||||
|
||||
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t z_impl_zsock_sendto_custom_fake_echo_next_req(int sock, void *buf, size_t len,
|
||||
int flags, const struct sockaddr *dest_addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
uint16_t last_message_id = 0;
|
||||
struct coap_packet response = {0};
|
||||
struct coap_option option = {0};
|
||||
|
||||
last_message_id |= ((uint8_t *) buf)[2] << 8;
|
||||
last_message_id |= ((uint8_t *) buf)[3];
|
||||
|
||||
if (messages_needing_response[0] == 0) {
|
||||
messages_needing_response[0] = last_message_id;
|
||||
} else {
|
||||
messages_needing_response[1] = last_message_id;
|
||||
}
|
||||
|
||||
last_response_code = ((uint8_t *) buf)[1];
|
||||
|
||||
LOG_INF("Latest message ID: %d", last_message_id);
|
||||
|
||||
int ret = coap_packet_parse(&response, buf, len, NULL, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Invalid data received");
|
||||
}
|
||||
|
||||
ret = coap_header_get_code(&response);
|
||||
zassert_equal(ret, COAP_METHOD_POST, "Incorrect method, %d", ret);
|
||||
|
||||
uint16_t payload_len;
|
||||
|
||||
const uint8_t *payload = coap_packet_get_payload(&response, &payload_len);
|
||||
|
||||
zassert_mem_equal(payload, "echo testing", payload_len, "Incorrect payload");
|
||||
|
||||
ret = coap_find_options(&response, COAP_OPTION_ECHO, &option, 1);
|
||||
zassert_equal(ret, 1, "Coap echo option not found, %d", ret);
|
||||
zassert_mem_equal(option.value, "echo_value", option.len, "Incorrect echo data");
|
||||
|
||||
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -158,6 +241,66 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_unmatching(int sock, void *buf,
|
||||
return sizeof(ack_data);
|
||||
}
|
||||
|
||||
static ssize_t z_impl_zsock_recvfrom_custom_fake_echo(int sock, void *buf, size_t max_len,
|
||||
int flags, struct sockaddr *src_addr,
|
||||
socklen_t *addrlen)
|
||||
{
|
||||
uint16_t last_message_id = 0;
|
||||
|
||||
LOG_INF("Recvfrom");
|
||||
uint8_t ack_data[] = {0x68, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xda, 0xef, 'e', 'c',
|
||||
'h', 'o', '_', 'v', 'a', 'l', 'u', 'e'};
|
||||
|
||||
if (messages_needing_response[0] != 0) {
|
||||
last_message_id = messages_needing_response[0];
|
||||
messages_needing_response[0] = 0;
|
||||
} else {
|
||||
last_message_id = messages_needing_response[1];
|
||||
messages_needing_response[1] = 0;
|
||||
}
|
||||
|
||||
ack_data[2] = (uint8_t) (last_message_id >> 8);
|
||||
ack_data[3] = (uint8_t) last_message_id;
|
||||
|
||||
memcpy(buf, ack_data, sizeof(ack_data));
|
||||
|
||||
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_response;
|
||||
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_echo;
|
||||
|
||||
return sizeof(ack_data);
|
||||
}
|
||||
|
||||
static ssize_t z_impl_zsock_recvfrom_custom_fake_echo_next_req(int sock, void *buf, size_t max_len,
|
||||
int flags, struct sockaddr *src_addr,
|
||||
socklen_t *addrlen)
|
||||
{
|
||||
uint16_t last_message_id = 0;
|
||||
|
||||
LOG_INF("Recvfrom");
|
||||
uint8_t ack_data[] = {0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xda, 0xef, 'e', 'c',
|
||||
'h', 'o', '_', 'v', 'a', 'l', 'u', 'e'};
|
||||
|
||||
if (messages_needing_response[0] != 0) {
|
||||
last_message_id = messages_needing_response[0];
|
||||
messages_needing_response[0] = 0;
|
||||
} else {
|
||||
last_message_id = messages_needing_response[1];
|
||||
messages_needing_response[1] = 0;
|
||||
}
|
||||
|
||||
ack_data[2] = (uint8_t) (last_message_id >> 8);
|
||||
ack_data[3] = (uint8_t) last_message_id;
|
||||
|
||||
memcpy(buf, ack_data, sizeof(ack_data));
|
||||
|
||||
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_response;
|
||||
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_echo_next_req;
|
||||
|
||||
return sizeof(ack_data);
|
||||
}
|
||||
|
||||
static void *suite_setup(void)
|
||||
{
|
||||
coap_client_init(&client, NULL);
|
||||
@ -214,6 +357,83 @@ ZTEST(coap_client, test_get_request)
|
||||
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
|
||||
}
|
||||
|
||||
ZTEST(coap_client, test_echo_option)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr address = {0};
|
||||
struct coap_client_request client_request = {
|
||||
.method = COAP_METHOD_GET,
|
||||
.confirmable = true,
|
||||
.path = test_path,
|
||||
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
|
||||
.cb = coap_callback,
|
||||
.payload = NULL,
|
||||
.len = 0
|
||||
};
|
||||
|
||||
client_request.payload = short_payload;
|
||||
client_request.len = strlen(short_payload);
|
||||
|
||||
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_echo;
|
||||
|
||||
k_sleep(K_MSEC(1));
|
||||
|
||||
LOG_INF("Send request");
|
||||
ret = coap_client_req(&client, 0, &address, &client_request, -1);
|
||||
zassert_true(ret >= 0, "Sending request failed, %d", ret);
|
||||
set_socket_events(ZSOCK_POLLIN);
|
||||
|
||||
k_sleep(K_MSEC(5));
|
||||
k_sleep(K_MSEC(1000));
|
||||
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
|
||||
}
|
||||
|
||||
ZTEST(coap_client, test_echo_option_next_req)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr address = {0};
|
||||
struct coap_client_request client_request = {
|
||||
.method = COAP_METHOD_GET,
|
||||
.confirmable = true,
|
||||
.path = test_path,
|
||||
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
|
||||
.cb = coap_callback,
|
||||
.payload = NULL,
|
||||
.len = 0
|
||||
};
|
||||
|
||||
client_request.payload = short_payload;
|
||||
client_request.len = strlen(short_payload);
|
||||
|
||||
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_echo_next_req;
|
||||
|
||||
k_sleep(K_MSEC(1));
|
||||
|
||||
LOG_INF("Send request");
|
||||
ret = coap_client_req(&client, 0, &address, &client_request, -1);
|
||||
zassert_true(ret >= 0, "Sending request failed, %d", ret);
|
||||
set_socket_events(ZSOCK_POLLIN);
|
||||
|
||||
k_sleep(K_MSEC(5));
|
||||
k_sleep(K_MSEC(1000));
|
||||
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
|
||||
|
||||
char *payload = "echo testing";
|
||||
|
||||
client_request.method = COAP_METHOD_POST;
|
||||
client_request.payload = payload;
|
||||
client_request.len = strlen(payload);
|
||||
|
||||
LOG_INF("Send next request");
|
||||
ret = coap_client_req(&client, 0, &address, &client_request, -1);
|
||||
zassert_true(ret >= 0, "Sending request failed, %d", ret);
|
||||
set_socket_events(ZSOCK_POLLIN);
|
||||
|
||||
k_sleep(K_MSEC(5));
|
||||
k_sleep(K_MSEC(1000));
|
||||
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
|
||||
}
|
||||
|
||||
ZTEST(coap_client, test_get_no_path)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user