diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index a62db3b102640cdce3328a6f160f99d1901e5d32..bf3f498c79a58231aebf77acd56872fafaf6b0fd 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -796,71 +796,77 @@ size_t gcoap_req_send2(const uint8_t *buf, size_t len, gcoap_resp_handler_t resp_handler) { gcoap_request_memo_t *memo = NULL; - assert(remote != NULL); - - /* Find empty slot in list of open requests. */ - mutex_lock(&_coap_state.lock); - for (int i = 0; i < GCOAP_REQ_WAITING_MAX; i++) { - if (_coap_state.open_reqs[i].state == GCOAP_MEMO_UNUSED) { - memo = &_coap_state.open_reqs[i]; - memo->state = GCOAP_MEMO_WAIT; - break; - } - } - if (!memo) { - mutex_unlock(&_coap_state.lock); - DEBUG("gcoap: dropping request; no space for response tracking\n"); - return 0; - } - unsigned msg_type = (*buf & 0x30) >> 4; uint32_t timeout = 0; - memo->resp_handler = resp_handler; - memcpy(&memo->remote_ep, remote, sizeof(sock_udp_ep_t)); - - switch (msg_type) { - case COAP_TYPE_CON: - /* copy buf to resend_bufs record */ - memo->msg.data.pdu_buf = NULL; - for (int i = 0; i < GCOAP_RESEND_BUFS_MAX; i++) { - if (!_coap_state.resend_bufs[i][0]) { - memo->msg.data.pdu_buf = &_coap_state.resend_bufs[i][0]; - memcpy(memo->msg.data.pdu_buf, buf, GCOAP_PDU_BUF_SIZE); - memo->msg.data.pdu_len = len; + + assert(remote != NULL); + + /* Only allocate memory if necessary (i.e. if user is interested in the + * response or request is confirmable) */ + if ((resp_handler != NULL) || (msg_type == COAP_TYPE_CON)) { + mutex_lock(&_coap_state.lock); + /* Find empty slot in list of open requests. */ + for (int i = 0; i < GCOAP_REQ_WAITING_MAX; i++) { + if (_coap_state.open_reqs[i].state == GCOAP_MEMO_UNUSED) { + memo = &_coap_state.open_reqs[i]; + memo->state = GCOAP_MEMO_WAIT; break; } } - if (memo->msg.data.pdu_buf) { - memo->send_limit = COAP_MAX_RETRANSMIT; - timeout = (uint32_t)COAP_ACK_TIMEOUT * US_PER_SEC; - uint32_t variance = (uint32_t)COAP_ACK_VARIANCE * US_PER_SEC; - timeout = random_uint32_range(timeout, timeout + variance); + if (!memo) { + mutex_unlock(&_coap_state.lock); + DEBUG("gcoap: dropping request; no space for response tracking\n"); + return 0; } - else { + + memo->resp_handler = resp_handler; + memcpy(&memo->remote_ep, remote, sizeof(sock_udp_ep_t)); + + switch (msg_type) { + case COAP_TYPE_CON: + /* copy buf to resend_bufs record */ + memo->msg.data.pdu_buf = NULL; + for (int i = 0; i < GCOAP_RESEND_BUFS_MAX; i++) { + if (!_coap_state.resend_bufs[i][0]) { + memo->msg.data.pdu_buf = &_coap_state.resend_bufs[i][0]; + memcpy(memo->msg.data.pdu_buf, buf, GCOAP_PDU_BUF_SIZE); + memo->msg.data.pdu_len = len; + break; + } + } + if (memo->msg.data.pdu_buf) { + memo->send_limit = COAP_MAX_RETRANSMIT; + timeout = (uint32_t)COAP_ACK_TIMEOUT * US_PER_SEC; + uint32_t variance = (uint32_t)COAP_ACK_VARIANCE * US_PER_SEC; + timeout = random_uint32_range(timeout, timeout + variance); + } + else { + memo->state = GCOAP_MEMO_UNUSED; + DEBUG("gcoap: no space for PDU in resend bufs\n"); + } + break; + + case COAP_TYPE_NON: + memo->send_limit = GCOAP_SEND_LIMIT_NON; + memcpy(&memo->msg.hdr_buf[0], buf, GCOAP_HEADER_MAXLEN); + timeout = GCOAP_NON_TIMEOUT; + break; + default: memo->state = GCOAP_MEMO_UNUSED; - DEBUG("gcoap: no space for PDU in resend bufs\n"); + DEBUG("gcoap: illegal msg type %u\n", msg_type); + break; + } + mutex_unlock(&_coap_state.lock); + if (memo->state == GCOAP_MEMO_UNUSED) { + return 0; } - break; - - case COAP_TYPE_NON: - memo->send_limit = GCOAP_SEND_LIMIT_NON; - memcpy(&memo->msg.hdr_buf[0], buf, GCOAP_HEADER_MAXLEN); - timeout = GCOAP_NON_TIMEOUT; - break; - default: - memo->state = GCOAP_MEMO_UNUSED; - DEBUG("gcoap: illegal msg type %u\n", msg_type); - break; - } - mutex_unlock(&_coap_state.lock); - if (memo->state == GCOAP_MEMO_UNUSED) { - return 0; } /* Memos complete; send msg and start timer */ ssize_t res = sock_udp_send(&_sock, buf, len, remote); - if ((res > 0) && (timeout > 0)) { /* timeout may be zero for non-confirmable */ + /* timeout may be zero for non-confirmable */ + if ((memo != NULL) && (res > 0) && (timeout > 0)) { /* We assume gcoap_req_send2() is called on some thread other than * gcoap's. First, put a message in the mbox for the sock udp object, * which will interrupt listening on the gcoap thread. (When there are @@ -884,10 +890,12 @@ size_t gcoap_req_send2(const uint8_t *buf, size_t len, } } if (res <= 0) { - if (msg_type == COAP_TYPE_CON) { - *memo->msg.data.pdu_buf = 0; /* clear resend buffer */ + if (memo != NULL) { + if (msg_type == COAP_TYPE_CON) { + *memo->msg.data.pdu_buf = 0; /* clear resend buffer */ + } + memo->state = GCOAP_MEMO_UNUSED; } - memo->state = GCOAP_MEMO_UNUSED; DEBUG("gcoap: sock send failed: %d\n", (int)res); } return (size_t)((res > 0) ? res : 0);