net: lib: coap_client: check poll() condition before retrying CoAP msg

Refactor the CoAP retry handling into the handle_poll() function,
so that we only try to send retries if the socket reports POLLOUT.

Also move the receiving into same loop, so when poll() reports POLLIN
we recv() the message and handle it before proceeding to other sockets.

Also fix tests to handle POLLOUT flag and add support for testing
multiple clients.

Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
This commit is contained in:
Seppo Takalo 2024-10-24 13:25:37 +03:00 committed by Mahesh Mahadevan
commit 4c6dd4c7b7
6 changed files with 194 additions and 147 deletions

View file

@ -31,3 +31,4 @@ add_compile_definitions(CONFIG_COAP_CLIENT_MAX_REQUESTS=2)
add_compile_definitions(CONFIG_COAP_CLIENT_MAX_INSTANCES=2)
add_compile_definitions(CONFIG_COAP_MAX_RETRANSMIT=4)
add_compile_definitions(CONFIG_COAP_BACKOFF_PERCENT=200)
add_compile_definitions(CONFIG_COAP_LOG_LEVEL=4)

View file

@ -77,7 +77,7 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake(int sock, void *buf, size_t max
memcpy(buf, ack_data, sizeof(ack_data));
clear_socket_events();
clear_socket_events(ZSOCK_POLLIN);
return sizeof(ack_data);
}
@ -205,7 +205,7 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_response(int sock, void *buf, s
memcpy(buf, ack_data, sizeof(ack_data));
clear_socket_events();
clear_socket_events(ZSOCK_POLLIN);
return sizeof(ack_data);
}
@ -247,7 +247,7 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_unmatching(int sock, void *buf,
memcpy(buf, ack_data, sizeof(ack_data));
clear_socket_events();
clear_socket_events(ZSOCK_POLLIN);
return sizeof(ack_data);
}
@ -273,7 +273,7 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_echo(int sock, void *buf, size_
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;
clear_socket_events();
clear_socket_events(ZSOCK_POLLIN);
return sizeof(ack_data);
}
@ -299,7 +299,7 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_echo_next_req(int sock, void *b
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;
clear_socket_events();
clear_socket_events(ZSOCK_POLLIN);
return sizeof(ack_data);
}
@ -335,6 +335,9 @@ void coap_callback(int16_t code, size_t offset, const uint8_t *payload, size_t l
{
LOG_INF("CoAP response callback, %d", code);
last_response_code = code;
if (user_data) {
k_sem_give((struct k_sem *) user_data);
}
}
ZTEST_SUITE(coap_client, NULL, suite_setup, test_setup, NULL, NULL);
@ -392,7 +395,7 @@ ZTEST(coap_client, test_resend_request)
ret = coap_client_req(&client, 0, &address, &client_request, NULL);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
k_sleep(K_MSEC(MORE_THAN_ACK_TIMEOUT_MS));
set_socket_events(ZSOCK_POLLIN);
set_socket_events(ZSOCK_POLLIN | ZSOCK_POLLOUT);
k_sleep(K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
@ -547,6 +550,7 @@ ZTEST(coap_client, test_no_response)
client_request.len = strlen(short_payload);
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_no_reply;
set_socket_events(ZSOCK_POLLOUT);
k_sleep(K_MSEC(1));
@ -592,30 +596,35 @@ ZTEST(coap_client, test_multiple_requests)
{
int ret = 0;
int retry = MORE_THAN_EXCHANGE_LIFETIME_MS;
struct k_sem sem1, sem2;
struct sockaddr address = {0};
struct coap_client_request client_request = {
struct coap_client_request req1 = {
.method = COAP_METHOD_GET,
.confirmable = true,
.path = test_path,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.cb = coap_callback,
.payload = NULL,
.len = 0
.payload = short_payload,
.len = strlen(short_payload),
.user_data = &sem1
};
struct coap_client_request req2 = req1;
client_request.payload = short_payload;
client_request.len = strlen(short_payload);
req2.user_data = &sem2;
k_sem_init(&sem1, 0, 1);
k_sem_init(&sem2, 0, 1);
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_no_reply;
k_sleep(K_MSEC(1));
LOG_INF("Send request");
ret = coap_client_req(&client, 0, &address, &client_request, NULL);
ret = coap_client_req(&client, 0, &address, &req1, NULL);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
ret = coap_client_req(&client, 0, &address, &client_request, NULL);
ret = coap_client_req(&client, 0, &address, &req2, NULL);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
set_socket_events(ZSOCK_POLLIN);
@ -625,8 +634,10 @@ ZTEST(coap_client, test_multiple_requests)
}
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
last_response_code = 0;
set_socket_events(ZSOCK_POLLIN);
k_sleep(K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS));
zassert_ok(k_sem_take(&sem1, K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS)));
zassert_ok(k_sem_take(&sem2, K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS)));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
}
@ -653,6 +664,7 @@ ZTEST(coap_client, test_unmatching_tokens)
client_request.len = strlen(short_payload);
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_unmatching;
set_socket_events(ZSOCK_POLLIN | ZSOCK_POLLOUT);
k_sleep(K_MSEC(1));
@ -663,3 +675,57 @@ ZTEST(coap_client, test_unmatching_tokens)
k_sleep(K_MSEC(MORE_THAN_LONG_EXCHANGE_LIFETIME_MS));
zassert_equal(last_response_code, -ETIMEDOUT, "Unexpected response");
}
ZTEST(coap_client, test_multiple_clients)
{
int ret;
int retry = MORE_THAN_EXCHANGE_LIFETIME_MS;
static struct coap_client client2 = {
.fd = 2,
};
struct k_sem sem1, sem2;
struct sockaddr address = {0};
struct coap_client_request req1 = {
.method = COAP_METHOD_GET,
.confirmable = true,
.path = test_path,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.cb = coap_callback,
.payload = short_payload,
.len = strlen(short_payload),
.user_data = &sem1
};
struct coap_client_request req2 = req1;
req2.user_data = &sem2;
req2.payload = long_payload;
req2.len = strlen(long_payload);
zassert_ok(k_sem_init(&sem1, 0, 1));
zassert_ok(k_sem_init(&sem2, 0, 1));
zassert_ok(coap_client_init(&client2, NULL));
k_sleep(K_MSEC(1));
LOG_INF("Sending requests");
ret = coap_client_req(&client, 1, &address, &req1, NULL);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
ret = coap_client_req(&client2, 2, &address, &req2, NULL);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
while (last_response_code == 0 && retry > 0) {
retry--;
k_sleep(K_MSEC(1));
}
set_socket_events(ZSOCK_POLLIN);
k_sleep(K_SECONDS(1));
/* ensure we got both responses */
zassert_ok(k_sem_take(&sem1, K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS)));
zassert_ok(k_sem_take(&sem2, K_MSEC(MORE_THAN_EXCHANGE_LIFETIME_MS)));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
}

View file

@ -29,9 +29,9 @@ void set_socket_events(short events)
my_events |= events;
}
void clear_socket_events(void)
void clear_socket_events(short events)
{
my_events = 0;
my_events &= ~events;
}
int z_impl_zsock_socket(int family, int type, int proto)
@ -41,13 +41,14 @@ int z_impl_zsock_socket(int family, int type, int proto)
int z_impl_zvfs_poll(struct zvfs_pollfd *fds, int nfds, int poll_timeout)
{
LOG_INF("Polling, events %d", my_events);
int events = 0;
k_sleep(K_MSEC(1));
fds->revents = my_events;
if (my_events) {
return 1;
for (int i = 0; i < nfds; i++) {
fds[i].revents = my_events & (fds[i].events | ZSOCK_POLLERR | ZSOCK_POLLHUP);
if (fds[i].revents) {
events++;
}
}
return 0;
return events;
}

View file

@ -15,11 +15,30 @@
#include <zephyr/net/coap_client.h>
#define ZSOCK_POLLIN 1
/* Copy from zephyr/include/zephyr/net/socket.h */
/**
* @name Options for poll()
* @{
*/
/* ZSOCK_POLL* values are compatible with Linux */
/** zsock_poll: Poll for readability */
#define ZSOCK_POLLIN 1
/** zsock_poll: Poll for exceptional condition */
#define ZSOCK_POLLPRI 2
/** zsock_poll: Poll for writability */
#define ZSOCK_POLLOUT 4
/** zsock_poll: Poll results in error condition (output value only) */
#define ZSOCK_POLLERR 8
/** zsock_poll: Poll detected closed connection (output value only) */
#define ZSOCK_POLLHUP 0x10
/** zsock_poll: Invalid socket (output value only) */
#define ZSOCK_POLLNVAL 0x20
/** @} */
void set_socket_events(short events);
void clear_socket_events(void);
void clear_socket_events(short events);
DECLARE_FAKE_VALUE_FUNC(uint32_t, z_impl_sys_rand32_get);
DECLARE_FAKE_VOID_FUNC(z_impl_sys_rand_get, void *, size_t);