response = coap_pdu_init(COAP_MESSAGE_ACK, 0, request->hdr->id,
sizeof(coap_pdu_t));
if (response) {
- result = coap_send(context, dst, response);
+ result = coap_send(context, dst, response, SEND_NOW);
coap_delete_pdu(response);
}
}
#if defined(WITH_POSIX) || defined(WITH_ARDUINO)
/* releases space allocated by PDU if free_pdu is set */
-coap_tid_t
+int
coap_send_impl(coap_context_t *context,
const coap_address_t *dst,
coap_pdu_t *pdu) {
-#ifdef WITH_POSIX
- ssize_t bytes_written;
-#else /* if it is Arduino */
- int bytes_written;
-#endif
- coap_tid_t id = COAP_INVALID_TID;
+
+ int bytes_written = -1;
if ( !context || !dst || !pdu )
- return id;
+ return bytes_written;
bytes_written = OCSendTo( context->sockfd, (uint8_t*)(pdu->hdr), pdu->length, 0,
(OCDevAddr*)dst);
debug("bytes_written %d\n", (int)bytes_written);
- if (bytes_written >= 0) {
- coap_transaction_id(dst, pdu, &id);
- } else {
- coap_log(LOG_CRIT, "coap_send: sendto\n");
- }
-
- return id;
+ return bytes_written;
}
#endif /* WITH_POSIX || WITH_ARDUINO */
#ifdef WITH_CONTIKI
}
#endif /* WITH_LWIP */
-coap_tid_t coap_send(coap_context_t *context, const coap_address_t *dst,
- coap_pdu_t *pdu) {
- return coap_send_impl(context, dst, pdu);
-}
-
coap_tid_t coap_send_error(coap_context_t *context, coap_pdu_t *request,
const coap_address_t *dst, unsigned char code, coap_opt_filter_t opts) {
coap_pdu_t *response;
response = coap_new_error_response(request, code, opts);
if (response) {
- result = coap_send(context, dst, response);
+ result = coap_send(context, dst, response, SEND_NOW);
coap_delete_pdu(response);
}
if (request) {
response = coap_pdu_init(type, 0, request->hdr->id, sizeof(coap_pdu_t));
if (response) {
- result = coap_send(context, dst, response);
+ result = coap_send(context, dst, response, SEND_NOW);
coap_delete_pdu(response);
}
}
return result;
}
-coap_tid_t coap_send_confirmed(coap_context_t *context,
- const coap_address_t *dst, coap_pdu_t *pdu) {
- coap_queue_t *node;
+coap_tid_t coap_send(coap_context_t *context,
+ const coap_address_t *dst, coap_pdu_t *pdu, const uint8_t flag)
+{
+ coap_queue_t *node = NULL;
coap_tick_t now;
+ coap_tid_t tid;
+ int bytesWritten;
int r;
- node = coap_new_node();
- if (!node) {
- debug("coap_send_confirmed: insufficient memory\n");
+ if (!context)
return COAP_INVALID_TID;
+ if(flag != SEND_RETX){
+ coap_transaction_id(dst, pdu, &tid);
+ }
+ if(flag == SEND_NOW || flag == SEND_RETX)
+ {
+ goto sending;
}
- node->id = coap_send_impl(context, dst, pdu);
- if (COAP_INVALID_TID == node->id) {
- debug("coap_send_confirmed: error sending pdu\n");
- coap_free_node(node);
+ node = coap_new_node();
+ if (!node) {
+ debug("coap_send: insufficient memory\n");
return COAP_INVALID_TID;
}
prng((unsigned char * )&r, sizeof(r));
-
/* add randomized RESPONSE_TIMEOUT to determine retransmission timeout */
- node->timeout = COAP_DEFAULT_RESPONSE_TIMEOUT * COAP_TICKS_PER_SECOND
- + (COAP_DEFAULT_RESPONSE_TIMEOUT >> 1)
- * ((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
+ if(flag == SEND_NOW_CON)
+ {
+ node->timeout = COAP_DEFAULT_RESPONSE_TIMEOUT * COAP_TICKS_PER_SECOND
+ + (COAP_DEFAULT_RESPONSE_TIMEOUT >> 1)
+ * ((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
+ }
+ else
+ {
+ node->timeout = MAX_MULTICAST_DELAY_SEC * ((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
+ node->delayedResponse = 1;
+ }
memcpy(&node->remote, dst, sizeof(coap_address_t));
node->pdu = pdu;
+ node->id = tid;
/* Set timer for pdu retransmission. If this is the first element in
* the retransmission queue, the base time is set to the current
* to be retransmitted earlier. Therefore, node->timeout is first
* normalized to the base time and then inserted into the queue with
* an adjusted relative time.
- */coap_ticks(&now);
- if (context->sendqueue == NULL) {
+ */
+
+ coap_ticks(&now);
+ if (context->sendqueue == NULL)
+ {
node->t = node->timeout;
context->sendqueue_basetime = now;
- } else {
+ }
+ else
+ {
/* make node->t relative to context->sendqueue_basetime */
node->t = (now - context->sendqueue_basetime) + node->timeout;
}
-
coap_insert_node(&context->sendqueue, node);
-#ifdef WITH_LWIP
- if (node == context->sendqueue) /* don't bother with timer stuff if there are earlier retransmits */
- coap_retransmittimer_restart(context);
-#endif
+ #ifdef WITH_LWIP
+ if (node == context->sendqueue)
+ /* don't bother with timer stuff if there are earlier retransmits */
+ coap_retransmittimer_restart(context);
+ #endif
-#ifdef WITH_CONTIKI
+ #ifdef WITH_CONTIKI
{ /* (re-)initialize retransmission timer */
coap_queue_t *nextpdu;
etimer_set(&context->retransmit_timer, nextpdu->t);
PROCESS_CONTEXT_END(&coap_retransmit_process);
}
-#endif /* WITH_CONTIKI */
+ #endif /* WITH_CONTIKI */
- return node->id;
+ if(flag == SEND_NOW_CON)
+ {
+ goto sending;
+ }
+ return tid;
+
+ sending:
+ bytesWritten = coap_send_impl(context, dst, pdu);
+ if(bytesWritten > 0)
+ {
+ return tid;
+ }
+ debug("coap_send_impl: error sending pdu\n");
+ coap_free_node(node);
+ return COAP_INVALID_TID;
}
coap_tid_t coap_retransmit(coap_context_t *context, coap_queue_t *node) {
+ coap_tid_t tid = COAP_INVALID_TID;
+
if (!context || !node)
return COAP_INVALID_TID;
debug("** retransmission #%d of transaction %d\n", node->retransmit_cnt,
ntohs(node->pdu->hdr->id));
-
- node->id = coap_send_impl(context, &node->remote, node->pdu);
- return node->id;
+ tid = coap_send(context, (coap_address_t *)&(node->remote),node->pdu, SEND_RETX);
+ return (tid == COAP_INVALID_TID)? COAP_INVALID_TID : node->id;
}
/* no more retransmissions, remove node from system */
}
#endif /* WITHOUT_OBSERVE */
- /* And finally delete the node */
- coap_delete_node(node);
+ // deletion of node will happen in ocoap since we still need the info node has
return COAP_INVALID_TID;
}
char *buf;
#endif
coap_hdr_t *pdu;
-#ifndef WITH_ARDUINO
- ssize_t bytes_read = -1;
-#else /* if it is Arduino */
int bytes_read = -1;
-#endif
+
coap_address_t src, dst;
coap_queue_t *node;
+ unsigned char delayRes = 0;
#ifdef WITH_CONTIKI
buf = uip_appdata;
bytes_read = OCRecvFrom( sockfd, (uint8_t*)buf, sizeof(buf), 0,
(OCDevAddr*)&src);
+ // Set the delayed response flag for responding to multicast requests
+ if (sockfd == ctx->sockfd_wellknown && bytes_read > 0) {
+ delayRes = 1;
+ }
+
#endif /* WITH_POSIX || WITH_ARDUINO */
#ifdef WITH_CONTIKI
if(uip_newdata()) {
goto error;
}
+ //set the delayed response flag
+ node->delayedResponse = delayRes;
+
/* and add new node to receive queue */
coap_transaction_id(&node->remote, node->pdu, &node->id);
coap_insert_node(&ctx->recvqueue, node);
}
#endif
static void handle_request(coap_context_t *context, coap_queue_t *rcvd) {
-
/* Call application-specific reponse handler when available. If
* not, we must acknowledge confirmable messages. */
if (context->request_handler) {
}
}
-static inline void handle_response(coap_context_t *context, coap_queue_t *rcvd) {
-
+static void handle_response(coap_context_t *context, coap_queue_t *rcvd) {
/* Call application-specific reponse handler when available. If
* not, we must acknowledge confirmable messages. */
if (context->response_handler) {
}
}
+static void handle_ack_rst(coap_context_t *context, uint8_t msgType, coap_queue_t *sent) {
+ /* Call application-specific reponse handler when available. If
+ * not, we must acknowledge confirmable messages. */
+ if (context->ack_rst_handler) {
+ context->ack_rst_handler(context, msgType, sent);
+ }
+}
+
static inline int
#ifdef __GNUC__
handle_locally(coap_context_t *context __attribute__ ((unused)),
}
switch (rcvd->pdu->hdr->type) {
+ case COAP_MESSAGE_ACK:
+ /* find transaction in sendqueue to stop retransmission */
+ if(coap_remove_from_queue(&context->sendqueue, rcvd->id, &sent)){
+ handle_ack_rst(context, COAP_MESSAGE_ACK, sent);
+ }
+
+ //delete empty messages, this is ACK only message no piggybacked response
+ if (rcvd->pdu->hdr->code == 0)
+ goto cleanup;
+ break;
+
case COAP_MESSAGE_NON: /* check for unknown critical options */
if (coap_option_check_critical(context, rcvd->pdu, opt_filter)
== 0)
goto cleanup;
break;
- case COAP_MESSAGE_RST:
- /* We have sent something the receiver disliked, so we remove
- * not only the transaction but also the subscriptions we might
- * have. */
-
- coap_log(LOG_ALERT, "got RST for message %u\n",
- ntohs(rcvd->pdu->hdr->id));
- // Handing this up, hoping there's enough info to remove an observe if at all possible.
- handle_response(context, rcvd);
+ case COAP_MESSAGE_CON: /* check for unknown critical options */
+ if (coap_option_check_critical(context, rcvd->pdu, opt_filter)
+ == 0) {
+ /* FIXME: send response only if we have received a request. Otherwise,
+ * send RST. */
+ response = coap_new_error_response(rcvd->pdu,
+ COAP_RESPONSE_CODE(402), opt_filter);
+ if (!response)
+ warn("coap_dispatch: cannot create error reponse\n");
+ else {
+ if (coap_send(context, &rcvd->remote,
+ response, SEND_NOW) == COAP_INVALID_TID) {
+ warn("coap_dispatch: error sending reponse\n");
+ }
+ coap_delete_pdu(response);
+ }
+ goto cleanup;
+ }
+ break;
- /* find transaction in sendqueue to stop retransmission */
- coap_remove_from_queue(&context->sendqueue, rcvd->id, &sent);
+ case COAP_MESSAGE_RST:
+ /* find transaction in sendqueue to stop retransmission */
+ if(coap_remove_from_queue(&context->sendqueue, rcvd->id, &sent)){
+ handle_ack_rst(context, COAP_MESSAGE_RST, sent);
+ }
+ goto cleanup;
+ break;
- if (sent)
- coap_handle_rst(context, sent);
- goto cleanup;
default:
debug(
"TODO: Need to handle other message types in coap_dispatch");
/* Pass message to upper layer if a specific handler was
* registered for a request that should be handled locally. */
if (handle_locally(context, rcvd)) {
- if (COAP_MESSAGE_IS_REQUEST(rcvd->pdu->hdr))
+ if (COAP_MESSAGE_IS_REQUEST(rcvd->pdu->hdr)){
handle_request(context, rcvd);
- else if (COAP_MESSAGE_IS_RESPONSE(rcvd->pdu->hdr))
+ }
+ else if (COAP_MESSAGE_IS_RESPONSE(rcvd->pdu->hdr)){
handle_response(context, rcvd);
+ }
else {
debug("dropped message with invalid code\n");
coap_send_message_type(context, &rcvd->remote, rcvd->pdu,
}
}
- // we should not retrying responses.....
- cleanup: coap_delete_node(sent);
- coap_delete_node(rcvd);
+ // we should not retry responses.....
+ cleanup:
+ coap_delete_node(sent);
+ coap_delete_node(rcvd);
}
}
#include "pdu.h"
#include "coap_time.h"
+#define SEND_NOW (1) /*Flag used when sending non-confirmable, ACK and RESET coap pdus*/
+#define SEND_NOW_CON (2) /*Flag used when sending confirmable coap pdu*/
+#define SEND_DELAYED (3) /*Flag used to delay the transmission of coap pdu*/
+#define SEND_RETX (4) /*Flag used to retransmit a confirmable pdu*/
+
struct coap_queue_t;
typedef struct coap_queue_t {
coap_tid_t id; /**< unique transaction id */
coap_pdu_t *pdu; /**< the CoAP PDU to send */
+
+ unsigned char delayedResponse; /**< delayed response flag */
} coap_queue_t;
/** Adds node to given queue, ordered by node->t. */
typedef void (*coap_response_handler_t)(struct coap_context_t *,
const coap_queue_t * rcvd);
+/** Message handler for ack and rst that is used as call-back in coap_context_t */
+typedef void (*coap_ack_rst_handler_t)(struct coap_context_t *, uint8_t msgType,
+ const coap_queue_t * sent);
+
#define COAP_MID_CACHE_SIZE 3
typedef struct {
unsigned char flags[COAP_MID_CACHE_SIZE];
coap_request_handler_t request_handler;
coap_response_handler_t response_handler;
+ coap_ack_rst_handler_t ack_rst_handler;
} coap_context_t;
/**
context->response_handler = handler;
}
+/**
+ * Registers a new message handler that is called whenever ack or rst
+ * was received that matches an ongoing transaction.
+ *
+ * @param context The context to register the handler for.
+ * @param handler The handler to register.
+ */
+static inline void
+coap_register_ack_rst_handler(coap_context_t *context,
+ coap_ack_rst_handler_t handler) {
+ context->ack_rst_handler = handler;
+}
+
/**
* Registers the option type @p type with the given context object @p
* ctx.
/* CoAP stack context must be released with coap_free_context() */
void coap_free_context( coap_context_t *context );
-
/**
* Sends a confirmed CoAP message to given destination. The memory
* that is allocated by pdu will not be released by
* @param pdu The CoAP PDU to send.
* @return The message id of the sent message or @c COAP_INVALID_TID on error.
*/
-coap_tid_t coap_send_confirmed(coap_context_t *context,
+coap_tid_t coap_send_confirmed(coap_context_t *context,
const coap_address_t *dst,
coap_pdu_t *pdu);
unsigned char code,
coap_opt_filter_t opts);
/**
- * Sends a non-confirmed CoAP message to given destination. The memory
- * that is allocated by pdu will not be released by coap_send().
- * The caller must release the memory.
+ * Sends a CoAP message to given destination. The memory
+ * that is allocated by pdu will be released by coap_send().
*
* @param context The CoAP context to use.
* @param dst The address to send to.
* @param pdu The CoAP PDU to send.
+ * @param flag The flag indicating if the message will be sent with delay
* @return The message id of the sent message or @c COAP_INVALID_TID on error.
*/
-coap_tid_t coap_send(coap_context_t *context,
- const coap_address_t *dst,
- coap_pdu_t *pdu);
+
+coap_tid_t coap_send(coap_context_t *context, const coap_address_t *dst, coap_pdu_t *pdu, const uint8_t flag);
/**
* Sends an error response with code @p code for request @p request to
}
/** Handles retransmissions of confirmable messages */
-coap_tid_t coap_retransmit( coap_context_t *context, coap_queue_t *node );
+coap_tid_t coap_retransmit( coap_context_t *context, coap_queue_t *node);
/**
* Reads data from the network and tries to parse as CoAP PDU. On success, 0 is returned
#endif
/* pre-defined constants that reflect defaults for CoAP */
+// This value is based on the DEFAULT_LEISURE (5 seconds) defined in RFC 7252
+#define MAX_MULTICAST_DELAY_SEC (5)
-#define COAP_DEFAULT_RESPONSE_TIMEOUT 2 /* response timeout in seconds */
-#define COAP_DEFAULT_MAX_RETRANSMIT 4 /* max number of retransmissions */
+#define COAP_DEFAULT_RESPONSE_TIMEOUT 3 /* response timeout in seconds */
+#define COAP_DEFAULT_MAX_RETRANSMIT 3 /* max number of retransmissions */
#define COAP_DEFAULT_PORT 5683 /* CoAP default UDP port */
#define COAP_DEFAULT_MAX_AGE 60 /* default maximum object lifetime in seconds */
#ifndef COAP_MAX_PDU_SIZE
* Adds Char to Buf if Offset is zero. Otherwise, Char is not written
* and Offset is decremented.
*/
-#define PRINT_WITH_OFFSET(Buf,Offset,Char) \
- if ((Offset) == 0) { \
- (*(Buf)++) = (Char); \
- } else { \
- (Offset)--; \
- } \
+#define PRINT_WITH_OFFSET(Buf,Offset,Char) \
+ if ((Offset) == 0) { \
+ (*(Buf)++) = (Char); \
+ } else { \
+ (Offset)--; \
+ } \
/**
* Adds Char to Buf if Offset is zero and Buf is less than Bufend.
*/
-#define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) { \
- if ((Buf) < (Bufend)) { \
- PRINT_WITH_OFFSET(Buf,Offset,Char); \
- } \
- (Result)++; \
+#define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) { \
+ if ((Buf) < (Bufend)) { \
+ PRINT_WITH_OFFSET(Buf,Offset,Char); \
+ } \
+ (Result)++; \
}
/**
* characters are skipped. Output may be truncated to Bufend - Buf
* characters.
*/
-#define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) { \
- size_t i; \
- for (i = 0; i < (Length); i++) { \
+#define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) { \
+ size_t i; \
+ for (i = 0; i < (Length); i++) { \
PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
- } \
+ } \
}
int
#if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
coap_print_status_t
print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
- size_t offset,
- coap_opt_t *query_filter __attribute__ ((unused))) {
+ size_t offset,
+ coap_opt_t *query_filter __attribute__ ((unused))) {
#else /* not a GCC */
coap_print_status_t
print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
- size_t offset, coap_opt_t *query_filter) {
+ size_t offset, coap_opt_t *query_filter) {
#endif /* GCC */
coap_resource_t *r;
unsigned char *p = buf;
if (query_filter) {
resource_param.s = COAP_OPT_VALUE(query_filter);
while (resource_param.length < COAP_OPT_LENGTH(query_filter)
- && resource_param.s[resource_param.length] != '=')
+ && resource_param.s[resource_param.length] != '=')
resource_param.length++;
if (resource_param.length < COAP_OPT_LENGTH(query_filter)) {
const str *rt_attributes;
if (resource_param.length == 4 &&
- memcmp(resource_param.s, "href", 4) == 0)
- flags |= MATCH_URI;
+ memcmp(resource_param.s, "href", 4) == 0)
+ flags |= MATCH_URI;
for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
if (resource_param.length == rt_attributes->length &&
/* rest is query-pattern */
query_pattern.s =
- COAP_OPT_VALUE(query_filter) + resource_param.length + 1;
+ COAP_OPT_VALUE(query_filter) + resource_param.length + 1;
assert((resource_param.length + 1) <= COAP_OPT_LENGTH(query_filter));
query_pattern.length =
- COAP_OPT_LENGTH(query_filter) - (resource_param.length + 1);
+ COAP_OPT_LENGTH(query_filter) - (resource_param.length + 1);
if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
query_pattern.s++;
}
if (query_pattern.length &&
- query_pattern.s[query_pattern.length-1] == '*') {
- query_pattern.length--;
- flags |= MATCH_PREFIX;
+ query_pattern.s[query_pattern.length-1] == '*') {
+ query_pattern.length--;
+ flags |= MATCH_PREFIX;
}
}
}
#ifndef WITHOUT_QUERY_FILTER
if (resource_param.length) { /* there is a query filter */
- if (flags & MATCH_URI) { /* match resource URI */
- if (!match(&r->uri, &query_pattern, (flags & MATCH_PREFIX) != 0, (flags & MATCH_SUBSTRING) != 0))
- continue;
- } else { /* match attribute */
- coap_attr_t *attr;
+ if (flags & MATCH_URI) { /* match resource URI */
+ if (!match(&r->uri, &query_pattern, (flags & MATCH_PREFIX) != 0, (flags & MATCH_SUBSTRING) != 0))
+ continue;
+ } else { /* match attribute */
+ coap_attr_t *attr;
str unquoted_val;
- attr = coap_find_attr(r, resource_param.s, resource_param.length);
+ attr = coap_find_attr(r, resource_param.s, resource_param.length);
if (!attr) continue;
if (attr->value.s[0] == '"') { /* if attribute has a quoted value, remove double quotes */
unquoted_val.length = attr->value.length - 2;
} else {
unquoted_val = attr->value;
}
- if (!(match(&unquoted_val, &query_pattern,
+ if (!(match(&unquoted_val, &query_pattern,
(flags & MATCH_PREFIX) != 0,
(flags & MATCH_SUBSTRING) != 0)))
- continue;
+ continue;
}
}
#endif /* WITHOUT_QUERY_FILTER */
- if (!subsequent_resource) { /* this is the first resource */
+ if (!subsequent_resource) { /* this is the first resource */
subsequent_resource = 1;
} else {
PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
coap_attr_t *
coap_add_attr(coap_resource_t *resource,
- const unsigned char *name, size_t nlen,
- const unsigned char *val, size_t vlen,
+ const unsigned char *name, size_t nlen,
+ const unsigned char *val, size_t vlen,
int flags) {
coap_attr_t *attr;
coap_attr_t *
coap_find_attr(coap_resource_t *resource,
- const unsigned char *name, size_t nlen) {
+ const unsigned char *name, size_t nlen) {
coap_attr_t *attr;
if (!resource || !name)
attr = list_item_next(attr)) {
#endif /* WITH_CONTIKI */
if (attr->name.length == nlen &&
- memcmp(attr->name.s, name, nlen) == 0)
+ memcmp(attr->name.s, name, nlen) == 0)
return attr;
}
/* if you think you can outspart the compiler and speed things up by (eg by
* casting to uint32* and comparing alues), increment this counter: 1 */
if (memcmp(key, resource->key, sizeof(coap_key_t)) == 0)
- return resource;
+ return resource;
}
return NULL;
#else
ptr2 = (coap_resource_t *)resource_storage.mem;
for (i = 0; i < resource_storage.num; ++i) {
if (resource_storage.count[i] &&
- (memcmp(ptr2->key, key, sizeof(coap_key_t)) == 0))
+ (memcmp(ptr2->key, key, sizeof(coap_key_t)) == 0))
return (coap_resource_t *)ptr2;
++ptr2;
}
coap_print_status_t
coap_print_link(const coap_resource_t *resource,
- unsigned char *buf, size_t *len, size_t *offset) {
+ unsigned char *buf, size_t *len, size_t *offset) {
unsigned char *p = buf;
const unsigned char *bufend = buf + *len;
coap_attr_t *attr;
PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
COPY_COND_WITH_OFFSET(p, bufend, *offset,
- resource->uri.s, resource->uri.length, *len);
+ resource->uri.s, resource->uri.length, *len);
PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
COPY_COND_WITH_OFFSET(p, bufend, *offset,
- attr->name.s, attr->name.length, *len);
+ attr->name.s, attr->name.length, *len);
if (attr->value.s) {
PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
COPY_COND_WITH_OFFSET(p, bufend, *offset,
- attr->value.s, attr->value.length, *len);
+ attr->value.s, attr->value.length, *len);
}
}
#ifndef WITHOUT_OBSERVE
coap_subscription_t *
coap_find_observer(coap_resource_t *resource, const coap_address_t *peer,
- const str *token) {
+ const str *token) {
coap_subscription_t *s;
assert(resource);
for (s = (coap_subscription_t*)list_head(resource->subscribers); s;
s = (coap_subscription_t*)list_item_next(s)) {
if (coap_address_equals(&s->subscriber, peer)
- && (!token || (token->length == s->token_length
- && memcmp(token->s, s->token, token->length) == 0)))
+ && (!token || (token->length == s->token_length
+ && memcmp(token->s, s->token, token->length) == 0)))
return s;
}
coap_subscription_t *
coap_add_observer(coap_resource_t *resource,
- const coap_address_t *observer,
- const str *token) {
+ const coap_address_t *observer,
+ const str *token) {
coap_subscription_t *s;
assert(observer);
void
coap_touch_observer(coap_context_t *context, const coap_address_t *observer,
- const str *token) {
+ const str *token) {
coap_resource_t *r;
coap_subscription_t *s;
if (resource_storage.count[i]) {
s = coap_find_observer(r, observer, token);
if (s) {
- s->fail_cnt = 0;
+ s->fail_cnt = 0;
}
}
}
void
coap_delete_observer(coap_resource_t *resource, const coap_address_t *observer,
- const str *token) {
+ const str *token) {
coap_subscription_t *s;
s = coap_find_observer(resource, observer, token);
/* retrieve GET handler, prepare response */
h = r->handler[COAP_REQUEST_GET - 1];
- assert(h); /* we do not allow subscriptions if no
- * GET handler is defined */
+ assert(h); /* we do not allow subscriptions if no
+ * GET handler is defined */
for (obs = (coap_subscription_t*)list_head(r->subscribers); obs;
obs = (coap_subscription_t*)list_item_next(obs)) {
if (!response) {
obs->dirty = 1;
r->partiallydirty = 1;
- debug("coap_check_notify: pdu init failed, resource stays partially dirty\n");
- continue;
+ debug("coap_check_notify: pdu init failed, resource stays partially dirty\n");
+ continue;
}
if (!coap_add_token(response, obs->token_length, obs->token)) {
obs->dirty = 1;
r->partiallydirty = 1;
- debug("coap_check_notify: cannot add token, resource stays partially dirty\n");
- coap_delete_pdu(response);
- continue;
+ debug("coap_check_notify: cannot add token, resource stays partially dirty\n");
+ coap_delete_pdu(response);
+ continue;
}
token.length = obs->token_length;
response->hdr->id = coap_new_message_id(context);
if (obs->non && obs->non_cnt < COAP_OBS_MAX_NON) {
- response->hdr->type = COAP_MESSAGE_NON;
+ response->hdr->type = COAP_MESSAGE_NON;
} else {
- response->hdr->type = COAP_MESSAGE_CON;
+ response->hdr->type = COAP_MESSAGE_CON;
}
/* fill with observer-specific data */
h(context, r, &obs->subscriber, NULL, &token, response);
if (response->hdr->type == COAP_MESSAGE_CON) {
- tid = coap_send_confirmed(context, &obs->subscriber, response);
- obs->non_cnt = 0;
+ tid = coap_send(context, &obs->subscriber, response, SEND_NOW_CON);
+ obs->non_cnt = 0;
} else {
- tid = coap_send(context, &obs->subscriber, response);
- obs->non_cnt++;
+ tid = coap_send(context, &obs->subscriber, response, SEND_NOW);
+ obs->non_cnt++;
}
if (COAP_INVALID_TID == tid || response->hdr->type != COAP_MESSAGE_CON)
- coap_delete_pdu(response);
+ coap_delete_pdu(response);
if (COAP_INVALID_TID == tid)
{
- debug("coap_check_notify: sending failed, resource stays partially dirty\n");
+ debug("coap_check_notify: sending failed, resource stays partially dirty\n");
obs->dirty = 1;
r->partiallydirty = 1;
}
*/
static void
coap_remove_failed_observers(coap_context_t *context,
- coap_resource_t *resource,
- const coap_address_t *peer,
- const str *token) {
+ coap_resource_t *resource,
+ const coap_address_t *peer,
+ const str *token) {
coap_subscription_t *obs;
for (obs = (coap_subscription_t*)list_head(resource->subscribers); obs;
obs = (coap_subscription_t*)list_item_next(obs)) {
if (coap_address_equals(peer, &obs->subscriber) &&
- token->length == obs->token_length &&
- memcmp(token->s, obs->token, token->length) == 0) {
+ token->length == obs->token_length &&
+ memcmp(token->s, obs->token, token->length) == 0) {
/* count failed notifies and remove when
* COAP_MAX_FAILED_NOTIFY is reached */
if (obs->fail_cnt < COAP_OBS_MAX_FAIL)
- obs->fail_cnt++;
+ obs->fail_cnt++;
else {
- list_remove(resource->subscribers, obs);
- obs->fail_cnt = 0;
+ list_remove(resource->subscribers, obs);
+ obs->fail_cnt = 0;
#ifndef NDEBUG
- if (LOG_DEBUG <= coap_get_log_level()) {
+ if (LOG_DEBUG <= coap_get_log_level()) {
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 40
#endif
- unsigned char addr[INET6_ADDRSTRLEN+8];
+ unsigned char addr[INET6_ADDRSTRLEN+8];
- if (coap_print_addr(&obs->subscriber, addr, INET6_ADDRSTRLEN+8))
- debug("** removed observer %s\n", addr);
- }
+ if (coap_print_addr(&obs->subscriber, addr, INET6_ADDRSTRLEN+8))
+ debug("** removed observer %s\n", addr);
+ }
#endif
- coap_cancel_all_messages(context, &obs->subscriber,
- obs->token, obs->token_length);
+ coap_cancel_all_messages(context, &obs->subscriber,
+ obs->token, obs->token_length);
- COAP_FREE_TYPE(subscription, obs);
+ COAP_FREE_TYPE(subscription, obs);
}
}
- break; /* break loop if observer was found */
+ break; /* break loop if observer was found */
}
}
void
coap_handle_failed_notify(coap_context_t *context,
- const coap_address_t *peer,
- const str *token) {
+ const coap_address_t *peer,
+ const str *token) {
coap_resource_t *r;
#ifndef WITH_CONTIKI
coap_resource_t *tmp;
HASH_ITER(hh, context->resources, r, tmp) {
#endif
- coap_remove_failed_observers(context, r, peer, token);
+ coap_remove_failed_observers(context, r, peer, token);
}
#else /* WITH_CONTIKI */
int i;
// Typedefs
//-----------------------------------------------------------------------------
-typedef enum {
- OC_COAP_OK = 0,
- OC_COAP_ERR
-} OCCoAPResult;
-
//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
* 0 - success
* TBD - TBD error
*/
-int OCInitCoAP(const char *address, uint16_t port, OCMode mode);
+OCStackResult OCInitCoAP(const char *address, uint16_t port, OCMode mode);
/**
* Discover OC resources
* 0 - success
* TBD - TBD error
*/
-int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
+OCStackResult OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
const char *Uri, const char *payload);
*
* @return 0 - success, else - TBD error
*/
-int OCStopCoAP();
+OCStackResult OCStopCoAP();
/**
* Called in main loop of CoAP client or server. Allows low-level CoAP processing of
*
* @return 0 - success, else - TBD error
*/
-int OCProcessCoAP();
+OCStackResult OCProcessCoAP();
OCCoAPToken * OCGenerateCoAPToken();
/**
* Initiate sending of CoAP messages. Example: server uses it to send observe messages
*
- * @return OC_COAP_OK - success, OC_COAP_ERR
+ * @return 0 - success, else - TBD error
*/
-int OCCoAPSendMessage (OCDevAddr *dstAddr, OCStackResult msgCode,
- OCQualityOfService qos, OCCoAPToken * token,
- const char *payload, uint32_t seqNum);
+OCStackResult OCSendCoAPNotification (OCDevAddr *dstAddr, OCStackResult result,
+ OCQualityOfService qos, OCCoAPToken * token,
+ unsigned char *payload, uint32_t seqNum);
#endif /* OCCOAP_H_ */
// Internal function to generate a coap pdu based on passed parameters
coap_pdu_t *
GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
- size_t tokenLength, uint8_t * token, unsigned char * payloadJSON,
+ OCCoAPToken * token, unsigned char * payloadJSON,
coap_list_t *options);
+// Internal function to send a coap pdu, it also handles NON and CON
+OCStackResult
+SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
+ uint8_t delayFlag);
+
// Call back function used by libcoap to order option in coap pdu
int OrderOptions(void *a, void *b);
unsigned char *data);
// Internal function to create OCRequest struct at the server from a received coap pdu
-OCStackResult FormOCRequest(const coap_queue_t * rcvdRequest,
- OCRequest * * requestLoc, unsigned char * uriBuf,
- unsigned char * queryBuf);
+OCStackResult FormOCRequest(OCRequest * * requestLoc, OCQualityOfService qos,
+ unsigned char * uriBuf, OCObserveReq * observeReq,
+ OCEntityHandlerRequest * entityHandlerRequest);
// Internal function to create OCEntityHandlerRequest at the server from a received coap pdu
-OCStackResult FormOCEntityHandlerRequest(const coap_queue_t * rcvdRequest,
- OCEntityHandlerRequest * * entityHandlerRequestLoc,
- unsigned char * bufRes, unsigned char * query);
+OCStackResult FormOCEntityHandlerRequest(OCEntityHandlerRequest * * entityHandlerRequestLoc,
+ OCMethod method, unsigned char * resBuf, unsigned char * reqBuf,
+ unsigned char * queryBuf);
+
+// Internal function to retrieve Uri and Query from received coap pdu
+OCStackResult ParseCoAPPdu(coap_pdu_t * pdu, unsigned char * uriBuf,
+ unsigned char * queryBuf, uint8_t * * obsOptionLoc, unsigned char * * payloadLoc);
// Internal function to retrieve a Token from received coap pdu
-OCStackResult RetrieveOCCoAPToken(const coap_queue_t * rcvdRequest,
+OCStackResult RetrieveOCCoAPToken(const coap_pdu_t * pdu,
OCCoAPToken * * rcvdTokenLoc);
+// Internal function to create OCObserveReq at the server
+OCStackResult FormOCObserveReq(OCObserveReq ** observeReqLoc, uint8_t obsOption,
+ OCDevAddr * remote, OCCoAPToken * rcvdToken);
+
// Internal function to create OCResponse struct at the client from a received coap pdu
-OCStackResult FormOCResponse(const coap_queue_t * rcvdResponse,
- OCResponse * * responseLoc);
+OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode,
+ OCClientResponse * clientResponse);
// Internal function to create OCClientResponse struct at the client from a received coap pdu
-OCStackResult FormOCClientResponse(const coap_queue_t * rcvdResponse,
- OCClientResponse * * clientResponseLoc);
+OCStackResult FormOCClientResponse(OCClientResponse * * clientResponseLoc,
+ OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
+ const unsigned char * resJSONPayload);
+
+// Internal function to handle the queued pdus in the send queue
+void HandleSendQueue(coap_context_t * gCoAPCtx);
+
+// Internal function to form the standard response option list
+OCStackResult FormResponseOptList(coap_list_t * * optList, uint8_t * addMediaType,
+ uint32_t * addMaxAge, uint8_t observeOptionLength, uint8_t * observeOptionPtr);
+
+// Internal function to retransmit a queue
+OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue);
+// Internal function called when sending/retransmission fails
+OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue);
#endif /* OCCOAPHELPER_H_ */
#define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG_V(FATAL, TAG, "%s is NULL", #arg); goto exit;} }
#define BUF_SIZE (64)
-#define BUF_SIZE_ENCODE_OPTION (3)
-#define BUF_SIZE_PORT (2)
//=============================================================================
// Private Variables
return token;
}
+//This function is called back by libcoap when ack or rst are received
+static void HandleCoAPAckRst(struct coap_context_t * ctx, uint8_t msgType,
+ const coap_queue_t * sentQueue){
+
+ // silence warnings
+ (void) ctx;
+
+ OCStackResult result = OC_STACK_ERROR;
+ OCCoAPToken * sentToken = NULL;
+ uint8_t * observeOption = NULL;
+ coap_pdu_t * sentPdu = sentQueue->pdu;
+
+ // fill the buffers of Uri and Query
+ result = ParseCoAPPdu(sentPdu, NULL, NULL, &observeOption, NULL);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+
+ // fill OCCoAPToken structure
+ result = RetrieveOCCoAPToken(sentPdu, &sentToken);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+
+ if(msgType == COAP_MESSAGE_RST){
+ // now the observer should be deleted
+ result = OCObserverStatus(sentToken, OC_OBSERVER_NOT_INTERESTED);
+ if(result == OC_STACK_OBSERVER_REMOVED){
+ OC_LOG_V(DEBUG, TAG, "Received RST, removing all queues associated with Token %d bytes",sentToken->tokenLength);
+ OC_LOG_BUFFER(INFO, TAG, sentToken->token, sentToken->tokenLength);
+ coap_cancel_all_messages(ctx, &sentQueue->remote, sentToken->token,
+ sentToken->tokenLength);
+ }
+ }else if(observeOption && msgType == COAP_MESSAGE_ACK){
+ OC_LOG_V(DEBUG, TAG, "Received ACK, for Token %d bytes",sentToken->tokenLength);
+ OC_LOG_BUFFER(INFO, TAG, sentToken->token, sentToken->tokenLength);
+ // now the observer is still interested
+ OCObserverStatus(sentToken, OC_OBSERVER_STILL_INTERESTED);
+ }
+
+ exit:
+ OCFree(sentToken);
+ OCFree(observeOption);
+}
+
//This function is called back by libcoap when a request is received
static void HandleCoAPRequests(struct coap_context_t *ctx,
const coap_queue_t * rcvdRequest)
// silence warnings
(void) ctx;
- OCStackResult result;
+ OCStackResult result = OC_STACK_ERROR;
+ OCStackResult responseResult = OC_STACK_ERROR;
OCRequest * request = NULL;
OCEntityHandlerRequest * entityHandlerRequest = NULL;
OCCoAPToken * rcvdToken = NULL;
+ OCObserveReq * rcvdObsReq = NULL;
+ coap_pdu_t * sendPdu = NULL;
+ coap_list_t *optList = NULL;
+ uint8_t mediaType = COAP_MEDIATYPE_APPLICATION_JSON;
+ uint32_t maxAge = 0x2ffff;
unsigned char rcvdUri[MAX_URI_LENGTH] = { 0 };
unsigned char rcvdQuery[MAX_QUERY_LENGTH] = { 0 };
-
unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
+ uint8_t * rcvObserveOption = NULL;
+ unsigned char * bufReqPayload = NULL;
+ uint8_t observeOption = OC_RESOURCE_NO_OBSERVE;
- coap_list_t *optList = NULL;
- unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
- coap_pdu_t *pdu;
- coap_tid_t tid = COAP_INVALID_TID;
+ coap_pdu_t * recvPdu = rcvdRequest->pdu;
- // fill OCRequest structure
- result = FormOCRequest(rcvdRequest, &request, rcvdUri, rcvdQuery);
+ // fill the buffers of Uri and Query
+ result = ParseCoAPPdu(recvPdu, rcvdUri, rcvdQuery, &rcvObserveOption, &bufReqPayload);
VERIFY_SUCCESS(result, OC_STACK_OK);
+ if(rcvObserveOption){
+ observeOption = (uint8_t)(*rcvObserveOption);
+ }
- // fill OCEntityHandlerRequest structure
- result = FormOCEntityHandlerRequest(rcvdRequest, &entityHandlerRequest,
- bufRes, rcvdQuery);
+ // fill OCCoAPToken structure
+ result = RetrieveOCCoAPToken(recvPdu, &rcvdToken);
VERIFY_SUCCESS(result, OC_STACK_OK);
- // fill OCCoAPToken structure
- result = RetrieveOCCoAPToken(rcvdRequest, &rcvdToken);
+ // fill OCEntityHandlerRequest structure
+ result = FormOCEntityHandlerRequest(&entityHandlerRequest,
+ (recvPdu->hdr->code == COAP_REQUEST_GET) ?
+ OC_REST_GET : OC_REST_PUT, bufRes, bufReqPayload, rcvdQuery);
VERIFY_SUCCESS(result, OC_STACK_OK);
- request->entityHandlerRequest = entityHandlerRequest;
+ // fill OCObserveReq
+ result = FormOCObserveReq(&rcvdObsReq, observeOption,
+ (OCDevAddr *)&(rcvdRequest->remote), rcvdToken);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+
+ // fill OCRequest structure
+ result = FormOCRequest(&request, (recvPdu->hdr->type == COAP_MESSAGE_CON) ?
+ OC_CONFIRMABLE : OC_NON_CONFIRMABLE, rcvdUri, rcvdObsReq, entityHandlerRequest);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
OC_LOG_V(INFO, TAG, " Receveid uri: %s", request->resourceUrl);
OC_LOG_V(INFO, TAG, " Receveid query: %s", entityHandlerRequest->query);
OC_LOG_BUFFER(INFO, TAG, rcvdToken->token, rcvdToken->tokenLength);
// process the request
- result = HandleStackRequests(request);
+ responseResult = HandleStackRequests(request);
- if (result == OC_STACK_OK)
- {
- OC_LOG_V(INFO, TAG, "Response from ocstack: %s", request->entityHandlerRequest->resJSONPayload);
- // need to build the response PDU
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
- coap_encode_var_bytes(tempBuf, COAP_MEDIATYPE_APPLICATION_JSON),
- tempBuf), OrderOptions);
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_MAXAGE,
- coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf),
- OrderOptions);
- }
-
- // generate the pdu, if the request was CON, then the response is ACK, otherwire NON
- pdu = GenerateCoAPPdu(
- rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON ?
- COAP_MESSAGE_ACK : COAP_MESSAGE_NON,
- OCToCoAPResponseCode(result), rcvdRequest->pdu->hdr->id,
- rcvdToken->tokenLength, rcvdToken->token,
- request->entityHandlerRequest->resJSONPayload, optList);
- VERIFY_NON_NULL(pdu);
- coap_show_pdu(pdu);
+ TODO("we should return the same registration option; however, this will confuse the receiver \
+ whether it is a sequence number or registration option!------------");
+ OC_LOG_V(INFO, TAG, "Response from ocstack: %s", request->entityHandlerRequest->resJSONPayload);
- if (pdu->hdr->type != COAP_MESSAGE_NON
- || (pdu->hdr->code >= 64 && !coap_is_mcast(&rcvdRequest->local)))
+ switch(responseResult)
{
- tid = coap_send(gCoAPCtx, &rcvdRequest->remote, pdu);
+ case OC_STACK_OBSERVER_ADDED:
+ observeOption = OC_RESOURCE_OBSERVE_REGISTER;
+ result = FormResponseOptList(&optList, &mediaType, &maxAge, 0, NULL);
+ break;
+ case OC_STACK_OBSERVER_REMOVED:
+ observeOption = OC_RESOURCE_OBSERVE_DEREGISTER;
+ result = FormResponseOptList(&optList, &mediaType, &maxAge, 0, NULL);
+ break;
+ case OC_STACK_OK:
+ default:
+ result = FormResponseOptList(&optList, &mediaType, &maxAge, 0, NULL);
+ break;
}
+ VERIFY_SUCCESS(result, OC_STACK_OK);
- OC_LOG_V(INFO, TAG, "TID %d", tid);
- // unlike stock libcoap (deletion in handle_request in net.c), we are deleting the response here
- // in the future, the response might be queued for SLOW resources
- if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
- {
- OC_LOG(INFO, TAG, PCF("Deleting PDU"));
- coap_delete_pdu(pdu);
+ // generate the pdu, if the request was CON, then the response is ACK, otherwire NON
+ sendPdu = GenerateCoAPPdu(
+ (rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON) ?
+ COAP_MESSAGE_ACK : COAP_MESSAGE_NON,
+ OCToCoAPResponseCode(responseResult), rcvdRequest->pdu->hdr->id,
+ rcvdToken,
+ request->entityHandlerRequest->resJSONPayload, optList);
+ VERIFY_NON_NULL(sendPdu);
+ coap_show_pdu(sendPdu);
+
+ if(SendCoAPPdu(gCoAPCtx, (coap_address_t*) &(rcvdRequest->remote), sendPdu, rcvdRequest->delayedResponse)
+ != OC_STACK_OK){
+ OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
}
exit:
- coap_delete_list(optList);
+ OCFree(rcvObserveOption);
OCFree(rcvdToken);
OCFree(entityHandlerRequest);
+ OCFree(rcvdObsReq);
OCFree(request);
}
static void HandleCoAPResponses(struct coap_context_t *ctx,
const coap_queue_t * rcvdResponse) {
OCResponse * response = NULL;
- OCCoAPToken * token = NULL;
+ OCCoAPToken * rcvdToken = NULL;
OCClientResponse * clientResponse = NULL;
ClientCB * cbNode = NULL;
- OCStackResult result;
+ unsigned char * bufRes = NULL;
+ uint8_t * rcvObserveOption;
+ uint32_t sequenceNumber = 0;
+ OCStackResult result = OC_STACK_ERROR;
+ coap_pdu_t *sendPdu = NULL;
+ //coap_list_t *optList = NULL;
+ //uint8_t deregisterObserveOption = OC_RESOURCE_OBSERVE_DEREGISTER;
VERIFY_NON_NULL(ctx);
VERIFY_NON_NULL(rcvdResponse);
+ coap_pdu_t * recvPdu = rcvdResponse->pdu;
- // TODO: we should check if we are interested in the token
- // Now, just accept NON packets
+ result = ParseCoAPPdu(recvPdu, NULL, NULL, &rcvObserveOption, &bufRes);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
- if (rcvdResponse->pdu->hdr->type == COAP_MESSAGE_NON)
- {
- // fill OCResponse structure
- result = FormOCResponse(rcvdResponse, &response);
- VERIFY_SUCCESS(result, OC_STACK_OK);
+ if(rcvObserveOption){
+ sequenceNumber = *((uint32_t *) rcvObserveOption);
+ }
- // fill OCCoAPToken structure
- result = RetrieveOCCoAPToken(rcvdResponse, &token);
- VERIFY_SUCCESS(result, OC_STACK_OK);
+ OC_LOG_V(DEBUG, TAG, "The sequence number of this response %d", sequenceNumber);
+ OC_LOG_V(DEBUG, TAG, "The response received is %s", bufRes);
- // fill OCClientResponse structure
- result = FormOCClientResponse(rcvdResponse, &clientResponse);
- VERIFY_SUCCESS(result, OC_STACK_OK);
+ // fill OCCoAPToken structure
+ result = RetrieveOCCoAPToken(recvPdu, &rcvdToken);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+ OC_LOG_V(INFO, TAG,"Received a pdu with Token", rcvdToken->tokenLength);
+ OC_LOG_BUFFER(INFO, TAG, rcvdToken->token, rcvdToken->tokenLength);
- // put everything together
- response->clientResponse = clientResponse;
+ // fill OCClientResponse structure
+ result = FormOCClientResponse(&clientResponse, CoAPToOCResponseCode(recvPdu->hdr->code),
+ (OCDevAddr *) &(rcvdResponse->remote), sequenceNumber, bufRes);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
- cbNode = GetClientCB(token, NULL);
+ cbNode = GetClientCB(rcvdToken, NULL);
- OC_LOG_V(INFO, TAG, " Received a response HandleCoAPResponses in occoap: %s",
- response->clientResponse->resJSONPayload);
- OC_LOG_V(INFO, TAG,"Token received %d bytes", token->tokenLength);
- OC_LOG_BUFFER(INFO, TAG, token->token,
- token->tokenLength);
+ // fill OCResponse structure
+ result = FormOCResponse(&response, cbNode, clientResponse);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
- if(cbNode && (cbNode->method == OC_REST_OBSERVE || cbNode->method == OC_REST_OBSERVE_ALL))
- {
- if(clientResponse->sequenceNumber != 0)
- {
- if(cbNode->method == OC_REST_OBSERVE && (clientResponse->sequenceNumber <= cbNode->sequenceNumber))
- {
- OC_LOG_V(DEBUG, TAG, "Observe notification came out of order. \
- Ignoring Incoming:%d Against Current:%d.",
- clientResponse->sequenceNumber, cbNode->sequenceNumber);
- return;
- }
- else
- {
- cbNode->sequenceNumber = clientResponse->sequenceNumber;
- }
+ if(cbNode)
+ {
+ if(clientResponse->sequenceNumber != 0 &&
+ (cbNode->method == OC_REST_OBSERVE ||
+ cbNode->method == OC_REST_OBSERVE_ALL)){
+ OC_LOG(INFO, TAG, PCF("Received an observe notification"));
+ if(recvPdu->hdr->type == COAP_MESSAGE_CON){
+ sendPdu = GenerateCoAPPdu(COAP_MESSAGE_ACK, 0,
+ recvPdu->hdr->id, NULL, NULL, NULL);
+ VERIFY_NON_NULL(sendPdu);
+ result = SendCoAPPdu(gCoAPCtx, (coap_address_t*) &rcvdResponse->remote,
+ sendPdu, 0);
}
- }
- else if(!cbNode && clientResponse && clientResponse->sequenceNumber != 0) // Ensure that this is an observe notification.
- {
-
- coap_pdu_t *pdu;
- coap_list_t *optList = NULL;
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
- strlen(OC_RESOURCE_OBSERVE_DEREGISTER), (unsigned char *)OC_RESOURCE_OBSERVE_DEREGISTER), OrderOptions);
-
- pdu = GenerateCoAPPdu(COAP_MESSAGE_NON, COAP_REQUEST_GET,
- coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
- (unsigned char*)"", optList);
- VERIFY_NON_NULL(pdu);
- coap_tid_t tid;
- tid = coap_send(gCoAPCtx, (coap_address_t*) &rcvdResponse->remote, pdu);
-
- OC_LOG_V(INFO, TAG, "TID %d", tid);
- if (tid != COAP_INVALID_TID)
- {
- OC_LOG(INFO, TAG, PCF("Deleting PDU"));
- coap_delete_pdu(pdu);
+ if(cbNode->method == OC_REST_OBSERVE &&
+ clientResponse->sequenceNumber <= cbNode->sequenceNumber){
+ OC_LOG_V(DEBUG, TAG, "Observe notification came out of order. \
+ Ignoring Incoming:%d Against Current:%d.",
+ clientResponse->sequenceNumber, cbNode->sequenceNumber);
+ goto exit;
}
- else
- {
- OC_LOG(INFO, TAG, PCF("Keeping PDU, we should handle the retry of this pdu"));
+ if(clientResponse->sequenceNumber > cbNode->sequenceNumber){
+ cbNode->sequenceNumber = clientResponse->sequenceNumber;
}
- goto exit;
}
- response->cbNode = cbNode;
- response->clientResponse->result = CoAPToOCResponseCode(rcvdResponse->pdu->hdr->code);
HandleStackResponses(response);
+ }else if(!cbNode && clientResponse->sequenceNumber != 0){
+ OC_LOG(INFO, TAG, PCF("Received an observe notification, but I do not have callback \
+ ------------ sending RESET"));
+ sendPdu = GenerateCoAPPdu(COAP_MESSAGE_RST, 0,
+ recvPdu->hdr->id, NULL, NULL, NULL);
+ VERIFY_NON_NULL(sendPdu);
+ result = SendCoAPPdu(gCoAPCtx, (coap_address_t*) &rcvdResponse->remote, sendPdu, 0);
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+ }else{
+ // TODO: we should send a RST here and..
}
- else
- {
- OC_LOG(DEBUG, TAG, PCF("Do not accept other than NON in HandleCoAPResponses"));
- }
-
-exit:
- OCFree(response);
- OCFree(token);
- OCFree(clientResponse);
+ exit:
+ OCFree(rcvObserveOption);
+ OCFree(rcvdToken);
+ OCFree(clientResponse);
+ OCFree(response);
}
//=============================================================================
* 0 - success
* TBD - TBD error
*/
-int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
+OCStackResult OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
- int ret = OC_COAP_ERR;
+ int ret = OC_STACK_ERROR;
TODO ("Below should go away and be replaced by OC_LOG");
coap_log_t log_level = (coap_log_t)(LOG_DEBUG + 1);
{
if (!ParseIPv4Address((unsigned char *) address, ipAddr))
{
- return OC_COAP_ERR;
+ ret = OC_STACK_ERROR;
+ goto exit;
}
OC_LOG_V(INFO, TAG, "Parsed IP Address %d.%d.%d.%d",ipAddr[0],ipAddr[1],ipAddr[2],ipAddr[3]);
+
+ OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
+ &devAddr);
}
OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
coap_join_wellknown_group(gCoAPCtx,
(coap_address_t* )&mcastAddr), 0);
}
+
coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
+ coap_register_ack_rst_handler(gCoAPCtx, HandleCoAPAckRst);
- ret = OC_COAP_OK;
+ ret = OC_STACK_OK;
exit:
- if (ret != OC_COAP_OK)
+ if (ret != OC_STACK_OK)
{
OCStopCoAP();
}
* 0 - success
* TBD - TBD error
*/
-int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
+OCStackResult OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
const char *Uri, const char *payload)
{
- int ret = OC_COAP_ERR;
+ int ret = OC_STACK_ERROR;
coap_pdu_t *pdu = NULL;
coap_uri_t uri;
OCDevAddr dst;
uint8_t ipAddr[4] = { 0 };
coap_list_t *optList = NULL;
- unsigned char portBuf[BUF_SIZE_PORT];
size_t buflen;
unsigned char _buf[BUF_SIZE];
unsigned char *buf = _buf;
int res;
uint8_t coapMsgType;
uint8_t coapMethod;
+ uint8_t observeOption;
OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
//create appropriate coap options
if (uri.port != COAP_DEFAULT_PORT) {
- coap_insert(&optList,
- CreateNewOptionNode(COAP_OPTION_URI_PORT,
- coap_encode_var_bytes(portBuf, uri.port), portBuf),
- OrderOptions);
+ coap_insert(&optList, CreateNewOptionNode( COAP_OPTION_URI_PORT,
+ sizeof(uri.port), (uint8_t *)&(uri.port)), OrderOptions);
}
if (uri.path.length) {
OC_LOG_V(DEBUG, TAG, "uri.port %d", uri.port);
OC_LOG_V(DEBUG, TAG, "uri.query.s %s", uri.query.s);
}
- coapMsgType = COAP_MESSAGE_NON;
+ coapMsgType = COAP_MESSAGE_NON;
// Decide message type
if (qos == OC_CONFIRMABLE) {
coapMsgType = COAP_MESSAGE_CON;
- OC_LOG(FATAL, TAG, PCF("qos == OC_CONFIRMABLE is not supported in OCDoCoAPResource"));
}
+
// Decide method type
switch (method) {
case OC_REST_GET:
case OC_REST_OBSERVE_ALL:
case OC_REST_OBSERVE:
coapMethod = COAP_REQUEST_GET;
+ observeOption = OC_RESOURCE_OBSERVE_REGISTER;
coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
- strlen(OC_RESOURCE_OBSERVE_REGISTER), (unsigned char *)OC_RESOURCE_OBSERVE_REGISTER), OrderOptions);
+ sizeof(observeOption), &observeOption), OrderOptions);
break;
default:
- coapMethod = 0;
+ coapMethod = OC_REST_NOMETHOD;
OC_LOG(FATAL, TAG, PCF("OCDoCoAPResource only supports GET, PUT, & OBSERVE methods"));
break;
}
VERIFY_NON_NULL(gCoAPCtx);
pdu = GenerateCoAPPdu(coapMsgType, coapMethod,
- coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
+ coap_new_message_id(gCoAPCtx), token,
(unsigned char*) payload, optList);
VERIFY_NON_NULL(pdu);
- coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
-
- //OC_LOG_V(INFO, TAG, "TID %d", tid);
- TODO ("Once CON implementation is available, pdu should be saved until ACK is received");
- //if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
- {
- OC_LOG(INFO, TAG, PCF("Deleting PDU"));
- coap_delete_pdu(pdu);
- pdu = NULL;
- }
-
- ret = OC_COAP_OK;
+ ret = SendCoAPPdu(gCoAPCtx, (coap_address_t*) &dst, pdu, 0);
exit:
- coap_delete_list(optList);
- if (ret != OC_COAP_OK)
+ if (ret!= OC_STACK_OK)
{
- coap_delete_pdu(pdu);
+ OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
}
return ret;
}
-int OCCoAPSendMessage (OCDevAddr *dstAddr, OCStackResult msgCode,
+OCStackResult OCSendCoAPNotification (OCDevAddr *dstAddr, OCStackResult result,
OCQualityOfService qos, OCCoAPToken * token,
- const char *payload, uint32_t seqNum)
+ unsigned char *payload, uint32_t seqNum)
{
coap_list_t *optList = NULL;
- coap_pdu_t *pdu;
- unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
uint8_t coapMsgType = COAP_MESSAGE_NON;
- coap_tid_t tid = COAP_INVALID_TID;
-
- OC_LOG(INFO, TAG, PCF("Entering OCCoAPSendMessage"));
-
- OC_LOG_V(INFO, TAG, "OCStack payload: %s", payload);
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
- coap_encode_var_bytes(tempBuf, COAP_MEDIATYPE_APPLICATION_JSON),
- tempBuf), OrderOptions);
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_MAXAGE,
- coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf), OrderOptions);
- coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
- coap_encode_var_bytes(tempBuf, seqNum), tempBuf), OrderOptions);
-
- pdu = GenerateCoAPPdu (coapMsgType, OCToCoAPResponseCode(msgCode),
- coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
- (unsigned char*) payload, optList);
- VERIFY_NON_NULL(pdu);
- coap_show_pdu(pdu);
+ uint8_t mediaType = COAP_MEDIATYPE_APPLICATION_JSON;
+ uint32_t maxAge = 0x2ffff;
+ coap_pdu_t *sendPdu;
- tid = coap_send(gCoAPCtx, (coap_address_t*)dstAddr, pdu);
- OC_LOG_V(INFO, TAG, "TID %d", tid);
- if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
- {
- OC_LOG(INFO, TAG, PCF("Deleting PDU"));
- coap_delete_pdu(pdu);
- pdu = NULL;
+ OC_LOG(INFO, TAG, PCF("Entering OCSendCoAPNotification"));
+
+ coapMsgType = COAP_MESSAGE_NON;
+ // Decide message type
+ if (qos == OC_CONFIRMABLE) {
+ coapMsgType = COAP_MESSAGE_CON;
}
- return OC_COAP_OK;
+ result = FormResponseOptList(&optList, &mediaType, &maxAge, 4, (uint8_t *)(&seqNum));
+ VERIFY_SUCCESS(result, OC_STACK_OK);
+ sendPdu = GenerateCoAPPdu(
+ coapMsgType == COAP_MESSAGE_CON ? COAP_MESSAGE_CON : COAP_MESSAGE_NON,
+ OCToCoAPResponseCode(result), coap_new_message_id(gCoAPCtx),
+ token, payload, optList);
+ VERIFY_NON_NULL(sendPdu);
+ coap_show_pdu(sendPdu);
+
+ if(SendCoAPPdu(gCoAPCtx, (coap_address_t*) dstAddr, sendPdu, 0)
+ != OC_STACK_OK){
+ OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
+ }
+ return OC_STACK_OK;
exit:
- coap_delete_list(optList);
- return OC_COAP_ERR;
+ return OC_STACK_ERROR;
}
/**
*
* @return 0 - success, else - TBD error
*/
-int OCStopCoAP() {
+OCStackResult OCStopCoAP() {
OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
coap_free_context(gCoAPCtx);
gCoAPCtx = NULL;
- return 0;
+ return OC_STACK_OK;
}
/**
*
* @return 0 - success, else - TBD error
*/
-int OCProcessCoAP() {
+OCStackResult OCProcessCoAP() {
+
OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
int read = 0;
read = coap_read(gCoAPCtx, gCoAPCtx->sockfd);
}
}
coap_dispatch(gCoAPCtx);
- return 0;
+
+ HandleSendQueue(gCoAPCtx);
+
+ return OC_STACK_OK;
}
//-----------------------------------------------------------------------------
#include "occoaphelper.h"
#include "logger.h"
+#include "ocobserve.h"
+#include "coap_time.h"
//-----------------------------------------------------------------------------
// Macros
uint8_t ret;
switch(result)
{
+ case OC_STACK_OBSERVER_ADDED :
+ case OC_STACK_OBSERVER_REMOVED :
case OC_STACK_OK :
ret = COAP_RESPONSE_200;
break;
return ret;
}
-
-// Form the OCRequest struct
-OCStackResult FormOCRequest(const coap_queue_t * rcvdRequest,
- OCRequest * * requestLoc, unsigned char * uriBuf,
- unsigned char * queryBuf) {
-
- OCRequest * request = NULL;
- OCObserveReq *obsReq = NULL;
- size_t bufLen;
- size_t optLen;
+// Retrieve Uri and Query from received coap pdu
+OCStackResult ParseCoAPPdu(coap_pdu_t * pdu, unsigned char * uriBuf,
+ unsigned char * queryBuf, uint8_t * * observeOptionLoc, unsigned char * * payloadLoc)
+{
coap_opt_filter_t filter;
coap_opt_iterator_t opt_iter;
- coap_opt_t *option;
-
- // allocate it
- request = (OCRequest *) OCMalloc(sizeof(OCRequest));
- if (!request) {
- return OC_STACK_NO_MEMORY;
- }
-
- // fill in qos
- request->qos = OC_NON_CONFIRMABLE;
- if (rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON) {
- request->qos = OC_CONFIRMABLE;
- }
-
- // fill in uri
- request->resourceUrl = NULL;
- bufLen = 0;
- coap_option_filter_clear(filter);
- coap_option_setb(filter, COAP_OPTION_URI_PATH);
- coap_option_iterator_init(rcvdRequest->pdu, &opt_iter, filter);
- while ((option = coap_option_next(&opt_iter))) {
- optLen = COAP_OPT_LENGTH(option);
- if (bufLen + 1 + optLen < MAX_URI_LENGTH) {
- //we still have room in the buffer
- uriBuf[bufLen++] = '/';
- memcpy(uriBuf + bufLen, COAP_OPT_VALUE(option), optLen);
- bufLen += optLen;
- } else {
- // TODO: should it be OC_STACK_NO_MEMORY
- // TODO: we should check that resources do not have long uri at the registration
- return OC_STACK_INVALID_URI;
- }
- }
- uriBuf[bufLen] = '\0';
- request->resourceUrl = uriBuf;
+ coap_opt_t *option = NULL;
+ size_t bufLen = 0;
+ size_t optLen = 0;
+ uint8_t * observeOption = NULL;
+ uint8_t observeOptionFound = 0;
- // fill in query
- bufLen = 0;
- coap_option_filter_clear(filter);
- coap_option_setb(filter, COAP_OPTION_URI_QUERY);
- coap_option_iterator_init(rcvdRequest->pdu, &opt_iter, filter);
- while ((option = coap_option_next(&opt_iter))) {
- optLen = COAP_OPT_LENGTH(option);
- if (bufLen + 1 + optLen < MAX_QUERY_LENGTH) {
- //we still have room in the buffer
- memcpy(queryBuf + bufLen, COAP_OPT_VALUE(option), optLen);
- bufLen += optLen;
- queryBuf[bufLen++] = '&';
- } else {
- // TODO: should it be OC_STACK_NO_MEMORY
- return OC_STACK_INVALID_QUERY;
- }
- }
- // delete last '&'
- queryBuf[bufLen ? (bufLen - 1) : (bufLen)] = '\0';
-
- // fill in observe, if present
- request->observe = NULL;
- coap_option_filter_clear(filter);
- coap_option_setb(filter, COAP_OPTION_OBSERVE);
- coap_option_iterator_init(rcvdRequest->pdu, &opt_iter, filter);
- while ((option = coap_option_next(&opt_iter))) {
- request->observe = (OCObserveReq *)OCMalloc(sizeof(OCObserveReq));
- if (request->observe)
+ if(uriBuf)
+ {
+ // parse the Uri
+ coap_option_filter_clear(filter);
+ coap_option_setb(filter, COAP_OPTION_URI_PATH);
+ coap_option_iterator_init(pdu, &opt_iter, filter);
+ while ((option = coap_option_next(&opt_iter)))
{
- obsReq = request->observe;
- obsReq->option = NULL;
- obsReq->option = (unsigned char *)OCMalloc(COAP_OPT_LENGTH(option)+1);
- if (obsReq->option)
+ optLen = COAP_OPT_LENGTH(option);
+ if (bufLen + 1 + optLen < MAX_URI_LENGTH)
{
- memcpy(obsReq->option, COAP_OPT_VALUE(option),COAP_OPT_LENGTH(option));
- (obsReq->option)[COAP_OPT_LENGTH(option)] = '\0';
+ //we still have room in the buffer
+ uriBuf[bufLen++] = '/';
+ memcpy(uriBuf + bufLen, COAP_OPT_VALUE(option), optLen);
+ bufLen += optLen;
}
else
{
- OCFree (request->observe);
- OCFree (request);
+ // TODO: we should check that resources do not have long uri at the resource creation
return OC_STACK_NO_MEMORY;
}
- obsReq->token = (OCCoAPToken *)OCMalloc(sizeof(MAX_TOKEN_LENGTH));
- if(obsReq->token)
+ }
+ uriBuf[bufLen] = '\0';
+ }
+
+ if(queryBuf)
+ {
+ // parse the Query
+ bufLen = 0;
+ coap_option_filter_clear(filter);
+ coap_option_setb(filter, COAP_OPTION_URI_QUERY);
+ coap_option_iterator_init(pdu, &opt_iter, filter);
+ while ((option = coap_option_next(&opt_iter)))
+ {
+ optLen = COAP_OPT_LENGTH(option);
+ if (bufLen + 1 + optLen < MAX_QUERY_LENGTH)
{
- memcpy (&obsReq->token->token, rcvdRequest->pdu->hdr->token,
- rcvdRequest->pdu->hdr->token_length);
- obsReq->token->tokenLength = rcvdRequest->pdu->hdr->token_length;
+ //we still have room in the buffer
+ memcpy(queryBuf + bufLen, COAP_OPT_VALUE(option), optLen);
+ bufLen += optLen;
+ queryBuf[bufLen++] = '&';
}
else
{
- OCFree (request->observe);
- OCFree (request);
+ // TODO: should it be OC_STACK_NO_MEMORY
+ return OC_STACK_NO_MEMORY;
+ }
+ }
+ // delete last '&'
+ queryBuf[bufLen ? (bufLen - 1) : (bufLen)] = '\0';
+ }
+
+ if(observeOptionLoc)
+ {
+ // parse the observe option
+ coap_option_filter_clear(filter);
+ coap_option_setb(filter, COAP_OPTION_OBSERVE);
+ coap_option_iterator_init(pdu, &opt_iter, filter);
+ while ((option = coap_option_next(&opt_iter)))
+ {
+ observeOption = (uint8_t *) OCMalloc(COAP_OPT_LENGTH(option));
+ if(!observeOption)
+ {
return OC_STACK_NO_MEMORY;
}
- obsReq->subAddr = (OCDevAddr *)&(rcvdRequest->remote);
- } else {
- OCFree (request);
- return OC_STACK_NO_MEMORY;
+ memcpy(observeOption, COAP_OPT_VALUE(option),COAP_OPT_LENGTH(option));
+ observeOptionFound = 1;
+ break;
}
+ if(observeOptionFound)
+ {
+ *observeOptionLoc = observeOption;
+ }
+ else
+ {
+ OCFree(observeOption);
+ *observeOptionLoc = NULL;
+ }
+ }
+
+ // get the payload
+ if(payloadLoc)
+ {
+ coap_get_data(pdu, &bufLen, payloadLoc);
}
- OC_LOG_V(INFO, TAG, "Observe option %d", request->observe);
+
+ return OC_STACK_OK;
+}
+
+// Form the OCRequest struct
+OCStackResult FormOCRequest(OCRequest * * requestLoc, OCQualityOfService qos,
+ unsigned char * uriBuf, OCObserveReq * observeReq,
+ OCEntityHandlerRequest * entityHandlerRequest)
+{
+ OCRequest * request = NULL;
+
+ // allocate it
+ request = (OCRequest *) OCMalloc(sizeof(OCRequest));
+ if (!request)
+ {
+ return OC_STACK_NO_MEMORY;
+ }
+
+ // fill in qos
+ request->qos = qos;
+
+ // fill in uri
+ request->resourceUrl = uriBuf;
+
+ // fill in observe
+ request->observe = observeReq;
+
+ // add entityHandlerRequest
+ request->entityHandlerRequest = entityHandlerRequest;
+
+ //TODO: this needs to be filled in the future
+ request->sequenceNum = 0;
*requestLoc = request;
return OC_STACK_OK;
}
+// Form the OCObserveReq struct
+OCStackResult FormOCObserveReq(OCObserveReq ** observeReqLoc, uint8_t observeOption,
+ OCDevAddr * remote, OCCoAPToken * rcvdToken)
+{
+ OCObserveReq * observeReq;
+
+ if(observeOption == OC_RESOURCE_NO_OBSERVE)
+ {
+ return OC_STACK_OK;
+ }
+
+ observeReq = (OCObserveReq *)OCMalloc(sizeof(OCObserveReq));
+ if(!observeReq)
+ {
+ *observeReqLoc = NULL;
+ return OC_STACK_NO_MEMORY;
+ }
+
+ observeReq->option = observeOption;
+ observeReq->subAddr = remote;
+ observeReq->token = rcvdToken;
+
+ *observeReqLoc = observeReq;
+ return OC_STACK_OK;
+}
+
// Form the OCEntityHandlerRequest struct
-OCStackResult FormOCEntityHandlerRequest(const coap_queue_t * rcvdRequest,
- OCEntityHandlerRequest * * entityHandlerRequestLoc,
- unsigned char * resBuf, unsigned char * query)
+OCStackResult FormOCEntityHandlerRequest(OCEntityHandlerRequest * * entityHandlerRequestLoc,
+ OCMethod method, unsigned char * resBuf, unsigned char * bufReqPayload,
+ unsigned char * queryBuf)
{
OCEntityHandlerRequest * entityHandlerRequest = NULL;
- unsigned char * pReq = NULL;
- size_t bufLen = 0;
entityHandlerRequest = (OCEntityHandlerRequest *) OCMalloc(
sizeof(OCEntityHandlerRequest));
{
return OC_STACK_NO_MEMORY;
}
+ //set it to NULL for now, it will be modified in ocstack
+ entityHandlerRequest->resource = NULL;
+
+ entityHandlerRequest->method = method;
- entityHandlerRequest->method = (rcvdRequest->pdu->hdr->code == COAP_REQUEST_GET) ?
- OC_REST_GET : OC_REST_PUT;
+ // fill in query
+ entityHandlerRequest->query = queryBuf;
- entityHandlerRequest->query = query;
- coap_get_data(rcvdRequest->pdu, &bufLen, &pReq);
- entityHandlerRequest->reqJSONPayload = pReq;
+ // fill payload
+ entityHandlerRequest->reqJSONPayload = bufReqPayload;
entityHandlerRequest->resJSONPayload = resBuf;
entityHandlerRequest->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
}
// Retrieve the token from the PDU
-OCStackResult RetrieveOCCoAPToken(const coap_queue_t * rcvdRequest,
- OCCoAPToken * * rcvdTokenLoc) {
+OCStackResult RetrieveOCCoAPToken(const coap_pdu_t * pdu,
+ OCCoAPToken * * rcvdTokenLoc)
+{
OCCoAPToken * rcvdToken = NULL;
rcvdToken = (OCCoAPToken *) OCMalloc(sizeof(OCCoAPToken));
- if (!rcvdToken) {
+ if (!rcvdToken)
+ {
return OC_STACK_NO_MEMORY;
}
- rcvdToken->tokenLength = rcvdRequest->pdu->hdr->token_length;
- memcpy(rcvdToken->token, rcvdRequest->pdu->hdr->token,
+ rcvdToken->tokenLength = pdu->hdr->token_length;
+ memcpy(rcvdToken->token, pdu->hdr->token,
rcvdToken->tokenLength);
*rcvdTokenLoc = rcvdToken;
return OC_STACK_OK;
}
-OCStackResult FormOCResponse(const coap_queue_t * rcvdResponse,
- OCResponse * * responseLoc) {
+OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode,
+ OCClientResponse * clientResponse)
+{
OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse));
- if (!response) {
+ if (!response)
+ {
return OC_STACK_NO_MEMORY;
}
+ response->cbNode = cbNode;
+ response->clientResponse = clientResponse;
+
*responseLoc = response;
return OC_STACK_OK;
}
-OCStackResult FormOCClientResponse(const coap_queue_t * rcvdResponse,
- OCClientResponse * * clientResponseLoc) {
-
- coap_opt_filter_t filter;
- coap_opt_iterator_t opt_iter;
- coap_opt_t *option;
- unsigned char * pRes = NULL;
- size_t bufLen = 0;
+OCStackResult FormOCClientResponse(OCClientResponse * * clientResponseLoc,
+ OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
+ const unsigned char * resJSONPayload)
+{
OCClientResponse * clientResponse = (OCClientResponse *) OCMalloc(
sizeof(OCClientResponse));
- if (!clientResponse) {
+ if (!clientResponse)
+ {
return OC_STACK_NO_MEMORY;
}
- clientResponse->sequenceNumber = 0;
- clientResponse->result = OC_STACK_ERROR;
- clientResponse->addr = (OCDevAddr *) &(rcvdResponse->remote);
- // fill in observe, if present
- coap_option_filter_clear(filter);
- coap_option_setb(filter, COAP_OPTION_OBSERVE);
- coap_option_iterator_init(rcvdResponse->pdu, &opt_iter, filter);
- while ((option = coap_option_next(&opt_iter))) {
- if (option)
+ clientResponse->sequenceNumber = seqNum;
+ clientResponse->result = result;
+ clientResponse->addr = remote;
+ clientResponse->resJSONPayload = resJSONPayload;
+
+ *clientResponseLoc = clientResponse;
+ return OC_STACK_OK;
+}
+
+OCStackResult FormResponseOptList(coap_list_t * * optListLoc, uint8_t * addMediaType,
+ uint32_t * addMaxAge, uint8_t observeOptionLength, uint8_t * observeOptionPtr)
+{
+ coap_list_t * optNode = NULL;
+
+ if(addMediaType)
+ {
+ optNode = CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
+ sizeof(*addMediaType), addMediaType);
+ VERIFY_NON_NULL(optNode);
+ coap_insert(optListLoc, optNode, OrderOptions);
+ }
+ if(addMaxAge)
+ {
+ optNode = CreateNewOptionNode(COAP_OPTION_MAXAGE,
+ sizeof(*addMaxAge), (uint8_t *)addMaxAge);
+ VERIFY_NON_NULL(optNode);
+ coap_insert(optListLoc, optNode, OrderOptions);
+ }
+ if(observeOptionPtr)
+ {
+ optNode = CreateNewOptionNode(COAP_OPTION_OBSERVE,
+ observeOptionLength, (uint8_t *)observeOptionPtr);
+ VERIFY_NON_NULL(optNode);
+ coap_insert(optListLoc, optNode, OrderOptions);
+ }
+
+ return OC_STACK_OK;
+ exit:
+ coap_delete_list(*optListLoc);
+ return OC_STACK_NO_MEMORY;
+}
+
+//Send a coap pdu
+OCStackResult
+SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
+ uint8_t delayFlag)
+{
+ coap_tid_t tid = COAP_INVALID_TID;
+ OCStackResult res = OC_STACK_COMM_ERROR;
+ uint8_t sendFlag = SEND_NOW;
+
+ if(delayFlag)
+ {
+ sendFlag = SEND_DELAYED;
+ }
+ else
+ {
+ if(pdu->hdr->type != COAP_MESSAGE_CON)
{
- memcpy(&clientResponse->sequenceNumber, COAP_OPT_VALUE(option),COAP_OPT_LENGTH(option));
+ sendFlag = SEND_NOW;
}
else
{
- return OC_STACK_NO_MEMORY;
+ sendFlag = SEND_NOW_CON;
}
}
- coap_get_data(rcvdResponse->pdu, &bufLen, &pRes);
- clientResponse->resJSONPayload = pRes;
+ tid = coap_send(gCoAPCtx, dst, pdu, sendFlag);
+ OC_LOG_V(INFO, TAG, "TID %d", tid);
+ if ((pdu->hdr->type != COAP_MESSAGE_CON && !delayFlag) || tid == COAP_INVALID_TID)
+ {
+ OC_LOG(INFO, TAG, PCF("Deleting PDU"));
+ coap_delete_pdu(pdu);
+ }
+ else
+ {
+ OC_LOG(INFO, TAG, PCF("Keeping PDU, we will handle the retry/delay of this pdu"));
+ }
- *clientResponseLoc = clientResponse;
- return OC_STACK_OK;
+ if(tid != COAP_INVALID_TID)
+ {
+ OC_LOG(INFO, TAG, PCF("Sending a pdu with Token:"));
+ OC_LOG_BUFFER(INFO,TAG, pdu->hdr->token, pdu->hdr->token_length);
+ res = OC_STACK_OK;
+ }
+
+ return res;
}
//generate a coap message
coap_pdu_t *
GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
- size_t tokenLength, uint8_t * token, unsigned char * payloadJSON,
- coap_list_t *options) {
+ OCCoAPToken * token, unsigned char * payloadJSON,
+ coap_list_t *options)
+{
coap_pdu_t *pdu;
coap_list_t *opt;
- pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
- VERIFY_NON_NULL(pdu);
-
- pdu->hdr->token_length = tokenLength;
- if (!coap_add_token(pdu, tokenLength, token)) {
- OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
+ if(token)
+ {
+ pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
+ VERIFY_NON_NULL(pdu);
+ pdu->hdr->token_length = token->tokenLength;
+ if (!coap_add_token(pdu, token->tokenLength, token->token))
+ {
+ OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
+ }
+ }
+ else
+ {
+ pdu = coap_pdu_init(msgType, code, id, sizeof(coap_pdu_t));
+ VERIFY_NON_NULL(pdu);
}
- for (opt = options; opt; opt = opt->next) {
+ for (opt = options; opt; opt = opt->next)
+ {
coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *) opt->data),
COAP_OPTION_LENGTH(*(coap_option *) opt->data),
COAP_OPTION_DATA(*(coap_option *) opt->data));
}
- if (payloadJSON) {
+ if (payloadJSON)
+ {
coap_add_data(pdu, strlen((const char *) payloadJSON) + 1,
(unsigned char*) payloadJSON);
}
// display the pdu for debugging purposes
coap_show_pdu(pdu);
+ // clean up
+ coap_delete_list(options);
return pdu;
- exit: return NULL;
+ exit:
+ coap_delete_list(options);
+ return NULL;
}
//a function to help in ordering coap options
-int OrderOptions(void *a, void *b) {
- if (!a || !b) {
+int OrderOptions(void *a, void *b)
+{
+ if (!a || !b)
+ {
return a < b ? -1 : 1;
}
if (COAP_OPTION_KEY(*(coap_option *)a)
- < COAP_OPTION_KEY(*(coap_option *)b) ) {
+ < COAP_OPTION_KEY(*(coap_option *)b) )
+ {
return -1;
}
coap_free(option);
return NULL;
}
+
+OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue)
+{
+ coap_tid_t tid = COAP_INVALID_TID;
+ OCStackResult result = OC_STACK_ERROR;
+ tid = coap_retransmit( ctx, queue);
+ if(tid == COAP_INVALID_TID)
+ {
+ OC_LOG_V(DEBUG, TAG, "Retransmission Failed TID %d",
+ queue->id);
+ result = OC_STACK_COMM_ERROR;
+ }
+ else
+ {
+ OC_LOG_V(DEBUG, TAG, "Retransmission TID %d, this is attempt %d",
+ queue->id, queue->retransmit_cnt);
+ result = OC_STACK_OK;
+ }
+ return result;
+}
+
+OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue)
+{
+ OCResponse * response = NULL;
+ ClientCB * cbNode = NULL;
+ ResourceObserver * observer = NULL;
+ OCClientResponse * clientResponse = NULL;
+ OCCoAPToken * token = NULL;
+ OCStackResult result = OC_STACK_OK;
+
+ result = RetrieveOCCoAPToken(queue->pdu, &token);
+ if(result != OC_STACK_OK)
+ {
+ goto exit;
+ }
+
+ cbNode = GetClientCB(token, NULL);
+ if(!cbNode)
+ {
+ goto observation;
+ }
+ result = FormOCClientResponse(&clientResponse, OC_STACK_COMM_ERROR,
+ (OCDevAddr *) &(queue->remote), 0, NULL);
+ if(result != OC_STACK_OK)
+ {
+ goto observation;
+ }
+ result = FormOCResponse(&response, cbNode, clientResponse);
+ if(result != OC_STACK_OK)
+ {
+ goto observation;
+ }
+ HandleStackResponses(response);
+
+observation:
+ observer = GetObserver(token);
+ if(!observer)
+ {
+ goto exit;
+ }
+
+ result = OCObserverStatus(token, OC_OBSERVER_FAILED_COMM);
+ if(result == OC_STACK_OBSERVER_REMOVED)
+ {
+ coap_cancel_all_messages(ctx, &queue->remote, token->token, token->tokenLength);
+ }
+
+ exit:
+ OCFree(token);
+ OCFree(clientResponse);
+ OCFree(response);
+ return result;
+}
+
+// a function to handle the send queue in the passed context
+void HandleSendQueue(coap_context_t * ctx)
+{
+ coap_tick_t now;
+ coap_queue_t *nextQueue = NULL;
+
+ coap_ticks(&now);
+ nextQueue = coap_peek_next( ctx );
+ while (nextQueue && nextQueue->t <= now - ctx->sendqueue_basetime)
+ {
+ nextQueue = coap_pop_next( ctx );
+ if((uint8_t)nextQueue->delayedResponse)
+ {
+ OC_LOG_V(DEBUG, TAG, "Sending Delayed response TID %d",
+ nextQueue->id);
+ if(SendCoAPPdu(ctx, &nextQueue->remote, nextQueue->pdu, 0)
+ == OC_STACK_COMM_ERROR)
+ {
+ OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
+ HandleFailedCommunication(ctx, nextQueue);
+ }
+ nextQueue->pdu = NULL;
+ coap_delete_node(nextQueue);
+ }
+ else
+ {
+ OC_LOG_V(DEBUG, TAG, "Retrying a CON pdu TID %d",nextQueue->id);
+ if(ReTXCoAPQueue(ctx, nextQueue) == OC_STACK_COMM_ERROR)
+ {
+ OC_LOG(DEBUG, TAG, PCF("A problem occurred in retransmitting a pdu"));
+ HandleFailedCommunication(ctx, nextQueue);
+ coap_delete_node(nextQueue);
+ }
+ }
+ nextQueue = coap_peek_next( ctx );
+ }
+}
TEST(OCCoapTest, General) {
EXPECT_EQ(0, 0);
}
-
-/*TEST(OCCoapTest, ServiceDiscovery) {
- OC_LOG(INFO, TAG, "Running ServiceDiscovery");
-
- EXPECT_EQ(0, OCInitCoAP("127.0.0.1", 0, OC_CLIENT));
-
- OCCoAPClientCallbackFunction asyncReturnFunc = discoverServicesAsync;
- EXPECT_EQ(0, OCDoCoAPResource(OC_REST_GET, "oc/core/d", asyncReturnFunc));
-
- EXPECT_EQ(0, OCProcessCoAP());
-
- EXPECT_EQ(0, OCStopCoAP());
-}*/
#define OC_OBSERVE_H
/* In CoAP sequence number is a 24 bit field */
-#define MAX_SEQUENCE_NUMBER 0xFFFFFF
-#define OC_RESOURCE_OBSERVE_REGISTER "0"
-#define OC_RESOURCE_OBSERVE_DEREGISTER "1"
+#define MAX_SEQUENCE_NUMBER (0xFFFFFF)
+#define OC_RESOURCE_OBSERVE_REGISTER (0)
+#define OC_RESOURCE_OBSERVE_DEREGISTER (1)
+#define OC_RESOURCE_NO_OBSERVE (2)
+
+#define MAX_OBSERVER_FAILED_COMM (2)
+#define MAX_OBSERVER_NON_COUNT (3)
+
+#define OC_OBSERVER_NOT_INTERESTED (0)
+#define OC_OBSERVER_STILL_INTERESTED (1)
+#define OC_OBSERVER_FAILED_COMM (2)
/* This information is stored for each registerd observer */
-typedef struct ObserveResourceServer {
+typedef struct ResourceObserver {
// URI of observed resource
unsigned char *resUri;
+ //Quality of service of the request
+ OCQualityOfService qos;
// Query
unsigned char *query;
// CoAP token for the observe request
OCResource *resource;
// IP address & port of client registered for observe
OCDevAddr *addr;
+ // number of times the server failed to reach the observer
+ uint8_t failedCommCount;
+ // number of times the server sent NON notifications
+ uint8_t NONCount;
+ // force the qos value to CON
+ uint8_t forceCON;
// next node in this list
- struct ObserveResourceServer *next;
-} ObserveResourceServer;
+ struct ResourceObserver *next;
+} ResourceObserver;
+
+OCStackResult OCObserverStatus(OCCoAPToken * token, uint8_t status);
OCStackResult ProcessObserveRequest (OCResource *resource, OCRequest *request);
-OCStackResult SendObserverNotification (OCResourceHandle handle, OCResource *resPtr);
+OCStackResult SendObserverNotification (OCResource *resPtr);
void DeleteObserverList();
+OCStackResult AddObserver ( const char *resUri,
+ const char *query,
+ OCCoAPToken * token,
+ OCDevAddr *addr,
+ OCResource *resHandle,
+ OCQualityOfService qos);
+OCStackResult DeleteObserver (OCCoAPToken * token);
+
+ResourceObserver* GetObserver (const OCCoAPToken * token);
+
#endif //OC_OBSERVE_H
typedef struct {
// Observe option field
- unsigned char *option;
+ uint8_t option;
// IP address & port of client registered for observe
OCDevAddr *subAddr;
// CoAP token for the observe request
OC_STACK_RESOURCE_ERROR, /*ex: not supported method or interface*/
OC_STACK_SLOW_RESOURCE,
OC_STACK_NO_OBSERVERS, /* resource has no registered observers */
+ OC_STACK_OBSERVER_NOT_FOUND,
+ OC_STACK_OBSERVER_ADDED,
+ OC_STACK_OBSERVER_REMOVED,
OC_STACK_ERROR
} OCStackResult;
const char *getResult(OCStackResult result);
std::string getIPAddrTBServer(OCClientResponse * clientResponse);
std::string getPortTBServer(OCClientResponse * clientResponse);
-std::string getQueryStrForGetPut(unsigned const char * responsePayload);
+std::string getQueryStrForGetPut(OCClientResponse * clientResponse);
#define TAG PCF("occlient")
#define CTX_VAL 0x99
typedef enum {
TEST_DISCOVER_REQ = 1,
- TEST_GET_REQ,
- TEST_PUT_REQ,
- TEST_OBS_REQ,
- TEST_GET_UNAVAILABLE_RES_REQ,
+ TEST_GET_REQ_NON,
+ TEST_PUT_REQ_NON,
+ TEST_OBS_REQ_NON,
+ TEST_GET_UNAVAILABLE_RES_REQ_NON,
+ TEST_GET_REQ_CON,
+ TEST_OBS_REQ_CON,
MAX_TESTS
} CLIENT_TEST;
static int TEST_CASE = 0;
static const char * TEST_APP_UNICAST_DISCOVERY_QUERY = "coap://0.0.0.0:5683/oc/core";
static std::string putPayload = "{\"state\":\"off\",\"power\":\"0\"}";
+static std::string coapServerIP = "255.255.255.255";
+static std::string coapServerPort = "5683";
+static std::string coapServerResource = "/a/led";
// The handle for the observe registration
OCDoHandle gObserveDoHandle;
}
// Forward Declaration
-OCStackApplicationResult getReqCB(void* ctx, OCDoHandle handle, OCClientResponse * clientResponse);
-int InitGetRequestToUnavailableResource(OCClientResponse * clientResponse);
-int InitObserveRequest(OCClientResponse * clientResponse);
-int InitPutRequest(OCClientResponse * clientResponse);
-int InitGetRequest(OCClientResponse * clientResponse);
+int InitGetRequestToUnavailableResource();
+int InitObserveRequest(OCQualityOfService qos);
+int InitPutRequest();
+int InitGetRequest(OCQualityOfService qos);
int InitDiscovery();
+void parseClientResponse(OCClientResponse * clientResponse);
static void PrintUsage()
{
- OC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1|2|3|4|5>");
+ OC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1|2|3|4|5|6|7>");
OC_LOG(INFO, TAG, "-u <0|1> : Perform multicast/unicast discovery of resources");
OC_LOG(INFO, TAG, "-t 1 : Discover Resources");
- OC_LOG(INFO, TAG, "-t 2 : Discover Resources and Initiate Get Request");
- OC_LOG(INFO, TAG, "-t 3 : Discover Resources and Initiate Put Requests");
- OC_LOG(INFO, TAG, "-t 4 : Discover Resources and Initiate Observe Requests");
- OC_LOG(INFO, TAG, "-t 5 : Discover Resources and Initiate Get Request for a resource which is unavailable");
+ OC_LOG(INFO, TAG, "-t 2 : Discover Resources and Initiate Nonconfirmable Get Request");
+ OC_LOG(INFO, TAG, "-t 3 : Discover Resources and Initiate Nonconfirmable Put Requests");
+ OC_LOG(INFO, TAG, "-t 4 : Discover Resources and Initiate Nonconfirmable Observe Requests");
+ OC_LOG(INFO, TAG, "-t 5 : Discover Resources and Initiate Nonconfirmable Get Request for a resource which is unavailable");
+ OC_LOG(INFO, TAG, "-t 6 : Discover Resources and Initiate Confirmable Get Request");
+ OC_LOG(INFO, TAG, "-t 7 : Discover Resources and Initiate Confirmable Observe Requests");
}
-
OCStackResult InvokeOCDoResource(std::ostringstream &query,
OCMethod method, OCQualityOfService qos,
OCClientResponseHandler cb )
clientResponse->resJSONPayload, remoteIpAddr[0], remoteIpAddr[1],
remoteIpAddr[2], remoteIpAddr[3], remotePortNu);
-
- if (TEST_CASE == TEST_GET_REQ)
- {
- InitGetRequest(clientResponse);
- }
- else if (TEST_CASE == TEST_PUT_REQ)
- {
- InitPutRequest(clientResponse);
- }
- else if (TEST_CASE == TEST_OBS_REQ)
- {
- InitObserveRequest(clientResponse);
- }
- else if (TEST_CASE == TEST_GET_UNAVAILABLE_RES_REQ)
- {
- InitGetRequestToUnavailableResource(clientResponse);
+ parseClientResponse(clientResponse);
+
+ switch(TEST_CASE){
+ case TEST_GET_REQ_NON:
+ InitGetRequest(OC_NON_CONFIRMABLE);
+ break;
+ case TEST_PUT_REQ_NON:
+ InitPutRequest();
+ break;
+ case TEST_OBS_REQ_NON:
+ InitObserveRequest(OC_NON_CONFIRMABLE);
+ break;
+ case TEST_GET_UNAVAILABLE_RES_REQ_NON:
+ InitGetRequestToUnavailableResource();
+ break;
+ case TEST_GET_REQ_CON:
+ InitGetRequest(OC_CONFIRMABLE);
+ break;
+ case TEST_OBS_REQ_CON:
+ InitObserveRequest(OC_CONFIRMABLE);
+ break;
+ default:
+ PrintUsage();
+ break;
}
}
}
-
-int InitGetRequestToUnavailableResource(OCClientResponse * clientResponse)
+int InitGetRequestToUnavailableResource()
{
OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
std::ostringstream query;
- query << "coap://" << getIPAddrTBServer(clientResponse) << ":" << getPortTBServer(clientResponse) << "/SomeUnknownResource";
+ query << "coap://" << coapServerIP << ":" << coapServerPort << "/SomeUnknownResource";
return (InvokeOCDoResource(query, OC_REST_GET, OC_NON_CONFIRMABLE, getReqCB));
}
-int InitObserveRequest(OCClientResponse * clientResponse)
+int InitObserveRequest(OCQualityOfService qos)
{
OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
std::ostringstream query;
- query << "coap://" << getIPAddrTBServer(clientResponse) << ":" << getPortTBServer(clientResponse) << getQueryStrForGetPut(clientResponse->resJSONPayload);
- return (InvokeOCDoResource(query, OC_REST_OBSERVE, OC_NON_CONFIRMABLE, obsReqCB));
+ query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
+ return (InvokeOCDoResource(query, OC_REST_OBSERVE, (qos == OC_CONFIRMABLE)? OC_CONFIRMABLE:OC_NON_CONFIRMABLE, obsReqCB));
}
-int InitPutRequest(OCClientResponse * clientResponse)
+int InitPutRequest()
{
OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
std::ostringstream query;
- query << "coap://" << getIPAddrTBServer(clientResponse) << ":" << getPortTBServer(clientResponse) << getQueryStrForGetPut(clientResponse->resJSONPayload);
+ query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
return (InvokeOCDoResource(query, OC_REST_PUT, OC_NON_CONFIRMABLE, putReqCB));
}
-int InitGetRequest(OCClientResponse * clientResponse)
+int InitGetRequest(OCQualityOfService qos)
{
OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
std::ostringstream query;
- query << "coap://" << getIPAddrTBServer(clientResponse) << ":" << getPortTBServer(clientResponse) << getQueryStrForGetPut(clientResponse->resJSONPayload);
- return (InvokeOCDoResource(query, OC_REST_GET, OC_NON_CONFIRMABLE, getReqCB));
+ query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
+ return (InvokeOCDoResource(query, OC_REST_GET, (qos == OC_CONFIRMABLE)? OC_CONFIRMABLE:OC_NON_CONFIRMABLE, getReqCB));
}
int InitDiscovery()
return ss.str();
}
-std::string getQueryStrForGetPut(unsigned const char * responsePayload){
- // JSON = {"oc":{"payload":[{"href":"/a/led","rt":["core.led"],"if":["core.rw"],"obs":1}]}}
-
- //std::string jsonPayload(responsePayload, responsePayload + sizeof responsePayload / sizeof responsePayload[0]);
- std::string jsonPayload(reinterpret_cast<char*>(const_cast<unsigned char*>(responsePayload)));
- //std::cout << jsonPayload << std::endl;
+std::string getQueryStrForGetPut(OCClientResponse * clientResponse){
return "/a/led";
}
+
+void parseClientResponse(OCClientResponse * clientResponse){
+ coapServerIP = getIPAddrTBServer(clientResponse);
+ coapServerPort = getPortTBServer(clientResponse);
+ coapServerResource = getQueryStrForGetPut(clientResponse);
+}
}
} else if (entityHandlerRequest && flag == OC_OBSERVE_FLAG) {
+ //TODO : Should the OC_OBSERVE_FLAG behave just like a GET according to the standard?
gLEDUnderObservation = 1;
}
OC_DISCOVERABLE|OC_OBSERVABLE);
OC_LOG_V(INFO, TAG, "Created LED resource with result: %s", getResult(res));
}
-
#include "debug.h"
#include <string.h>
-#define TAG PCF("ocbserve")
+// Module Name
+#define MOD_NAME PCF("ocobserve")
+
+#define TAG PCF("OCStackObserve")
#define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
-static struct ObserveResourceServer *serverObsList = NULL;
+static struct ResourceObserver * serverObsList = NULL;
-OCStackResult AddObserver (const char *resUri,
- const char *query,
- uint8_t *token,
- size_t tokenLength,
- OCDevAddr *addr,
- OCResource *resHandle);
-OCStackResult DeleteObserver (uint8_t *token, size_t tokenLength);
+OCStackResult OCObserverStatus(OCCoAPToken * token, uint8_t status)
+{
+ OCStackResult result = OC_STACK_ERROR;
+ ResourceObserver * observer = NULL;
+
+ switch(status)
+ {
+ case OC_OBSERVER_NOT_INTERESTED:
+ OC_LOG(DEBUG, TAG, PCF("observer is not interested in our notifications anymore"));
+ //observer is dead, or it is not observing anymore
+ result = DeleteObserver (token);
+ if(result == OC_STACK_OK)
+ {
+ OC_LOG(DEBUG, TAG, PCF("removing an observer"));
+ result = OC_STACK_OBSERVER_REMOVED;
+ }
+ break;
+ case OC_OBSERVER_STILL_INTERESTED:
+ //observer is still interested
+ OC_LOG(DEBUG, TAG, PCF("observer is interested in our \
+ notifications, reset the failedCount"));
+ observer = GetObserver(token);
+ if(observer)
+ {
+ observer->forceCON = 0;
+ observer->failedCommCount = 0;
+ result = OC_STACK_OK;
+ }
+ else
+ {
+ result = OC_STACK_OBSERVER_NOT_FOUND;
+ }
+ break;
+ case OC_OBSERVER_FAILED_COMM:
+ //observer is not reachable
+ OC_LOG(DEBUG, TAG, PCF("observer is not reachable"));
+ observer = GetObserver(token);
+ if(observer)
+ {
+ if(observer->failedCommCount >= MAX_OBSERVER_FAILED_COMM)
+ {
+ result = DeleteObserver (token);
+ OC_LOG(DEBUG, TAG, PCF("removing an observer"));
+ result = OC_STACK_OBSERVER_REMOVED;
+ }
+ else
+ {
+ observer->failedCommCount++;
+ result = OC_STACK_OK;
+ }
+ observer->forceCON = 1;
+ OC_LOG_V(DEBUG, TAG, "Failed count for this observer is %d",observer->failedCommCount);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
OCStackResult ProcessObserveRequest (OCResource *resource, OCRequest *request)
{
- OCStackResult result;
+ OCStackResult result = OC_STACK_ERROR;
OCEntityHandlerRequest *ehReq = request->entityHandlerRequest;
OCObserveReq *obs = request->observe;
OC_LOG(INFO, TAG, PCF("Entering ProcessObserveRequest"));
- if (strcmp ((char *)obs->option, OC_RESOURCE_OBSERVE_REGISTER) == 0) {
- if (NULL == resource)
+
+ // Register new observation
+ request->entityHandlerRequest->resource = (OCResourceHandle)resource;
+ result = resource->entityHandler(OC_OBSERVE_FLAG, request->entityHandlerRequest);
+
+ if (obs->option == OC_RESOURCE_OBSERVE_REGISTER)
+ {
+ // Add subscriber to the server observation list
+ // TODO: we need to check if the obsrever is already there using its OCDevAdd....
+ result = AddObserver ((const char*)(request->resourceUrl), (const char *)(ehReq->query),
+ obs->token, obs->subAddr, resource, request->qos);
+ if(result == OC_STACK_OK)
{
- return OC_STACK_ERROR;
+ result = OC_STACK_OBSERVER_ADDED;
}
- // Register new observation
- request->entityHandlerRequest->resource = (OCResourceHandle)resource;
- result = resource->entityHandler(OC_OBSERVE_FLAG, request->entityHandlerRequest);
- if (OC_STACK_OK == result)
+ OC_LOG(DEBUG, TAG, PCF("adding an observer"));
+ }
+ else if (obs->option == OC_RESOURCE_OBSERVE_DEREGISTER)
+ {
+ // Deregister observation
+ result = DeleteObserver (obs->token);
+ if(result == OC_STACK_OK)
{
- // Add subscriber to the server observation list
- result = AddObserver ((const char*)(request->resourceUrl), (const char *)(ehReq->query),
- obs->token->token, obs->token->tokenLength, obs->subAddr, resource);
+ OC_LOG(DEBUG, TAG, PCF("removing an observer"));
+ result = OC_STACK_OBSERVER_REMOVED;
}
- return result;
- } else if (strcmp ((char *)obs->option, OC_RESOURCE_OBSERVE_DEREGISTER) == 0) {
- // Deregister observation
- result = DeleteObserver (obs->token->token, obs->token->tokenLength);
- return result;
- } else {
+ }
+ else
+ {
// Invalid option
OC_LOG(ERROR, TAG, PCF("Invalid CoAP observe option"));
- return OC_STACK_INVALID_OBSERVE_PARAM;
+ result = OC_STACK_INVALID_OBSERVE_PARAM;
}
+ return result;
}
-OCStackResult SendObserverNotification (OCResourceHandle handle, OCResource *resPtr)
+OCStackResult SendObserverNotification (OCResource *resPtr)
{
uint8_t numObs = 0;
- OCStackResult result;
- ObserveResourceServer *obsRes = serverObsList;
- OCEntityHandlerRequest entityHandlerReq;
- unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 } ;
- /*
- * TODO: In the current releast all observes are set as non-confirmable since the
- * entity handler does not have a way to specify the message QoS - add a parameter.
- * Sending all observes NON does not confirm with the observe draft (ver14).
- */
+ OCStackResult result = OC_STACK_ERROR;
+ ResourceObserver *resourceObserver = serverObsList;
+ OCEntityHandlerRequest * entityHandlerReq = NULL;
+ unsigned char bufRes[MAX_RESPONSE_LENGTH] = {0};
+ // TODO: we should allow the server application to define qos for each notification
OCQualityOfService qos = OC_NON_CONFIRMABLE;
// Increment the sequence number
resPtr->sequenceNum += 1;
if (resPtr->sequenceNum == MAX_SEQUENCE_NUMBER)
+ {
resPtr->sequenceNum = 1;
+ }
// Find clients that are observing this resource
- while (obsRes) {
- if (obsRes->resource == handle) {
- // Invoke the entity handler for the client to process the query according to
- // the new representation
+ while (resourceObserver)
+ {
+ if (resourceObserver->resource == resPtr)
+ {
+ // Invoke the entity handler for the client to process
+ // the query according to the new representation
numObs++;
- entityHandlerReq.resource = handle;
- entityHandlerReq.query = obsRes->query;
- entityHandlerReq.method = OC_REST_GET;
- entityHandlerReq.reqJSONPayload = NULL;
- entityHandlerReq.resJSONPayload = bufRes;
- entityHandlerReq.resJSONPayloadLen = MAX_RESPONSE_LENGTH;
- // Even if entity handler for a resource is not successful we continue calling
- // entity handler for other resources
- if ( BuildObsJSONResponse((OCResource *)handle, &entityHandlerReq)
- == OC_EH_OK)
+ FormOCEntityHandlerRequest(&entityHandlerReq, OC_REST_GET, bufRes,
+ NULL, resourceObserver->query);
+ entityHandlerReq->resource = (OCResourceHandle)resPtr;
+
+ // Even if entity handler for a resource is not successful
+ // we continue calling entity handler for other resources
+ result = resPtr->entityHandler (OC_REQUEST_FLAG, entityHandlerReq);
+ if (OC_STACK_OK == result)
{
- result = OC_STACK_OK;
- OCCoAPSendMessage (obsRes->addr, result, qos, obsRes->token,
- (const char *)entityHandlerReq.resJSONPayload,
- resPtr->sequenceNum);
+ OC_LOG_V(INFO, TAG, "OCStack payload: %s",
+ entityHandlerReq->resJSONPayload);
+
+ // send notifications based on the qos of the request
+ qos = resourceObserver->qos;
+ if(qos == OC_NON_CONFIRMABLE)
+ {
+ OC_LOG_V(INFO, TAG, "Current NON count for this observer is %d",
+ resourceObserver->NONCount);
+ if(resourceObserver->forceCON ||
+ resourceObserver->NONCount >= MAX_OBSERVER_NON_COUNT)
+ {
+ resourceObserver->NONCount = 0;
+ // at some point we have to to send CON to check on the
+ // availability of observer
+ OC_LOG(INFO, TAG, PCF("This time we are sending the \
+ notification as CON"));
+ qos = OC_CONFIRMABLE;
+ }
+ else
+ {
+ resourceObserver->NONCount++;
+ }
+ }
+
+ OCSendCoAPNotification(resourceObserver->addr, result, qos,
+ resourceObserver->token,
+ (unsigned char *)entityHandlerReq->resJSONPayload,
+ resPtr->sequenceNum);
}
}
- obsRes = obsRes->next;
+ resourceObserver = resourceObserver->next;
}
if (numObs == 0)
{
OCStackResult AddObserver (const char *resUri,
const char *query,
- uint8_t *token,
- size_t tokenLength,
+ OCCoAPToken * token,
OCDevAddr *addr,
- OCResource *resHandle)
+ OCResource *resHandle,
+ OCQualityOfService qos)
{
- ObserveResourceServer *obsNode;
- OCCoAPToken *tokPtr;
+ ResourceObserver *obsNode = NULL;
+ OCCoAPToken *tokPtr = NULL;
- obsNode = (ObserveResourceServer *) OCMalloc(sizeof(ObserveResourceServer));
- if (obsNode) {
+ obsNode = (ResourceObserver *) OCMalloc(sizeof(ResourceObserver));
+ if (obsNode)
+ {
obsNode->resUri = (unsigned char *)OCMalloc(sizeof(strlen(resUri)+1));
VERIFY_NON_NULL (obsNode->resUri);
+ obsNode->qos = qos;
memcpy (obsNode->resUri, resUri, sizeof(strlen(resUri)+1));
obsNode->query = (unsigned char *)OCMalloc(sizeof(strlen(query)+1));
VERIFY_NON_NULL (obsNode->query);
obsNode->token = (OCCoAPToken *)OCMalloc(sizeof(OCCoAPToken));
VERIFY_NON_NULL (obsNode->token);
tokPtr = obsNode->token;
- memcpy (tokPtr->token, token, sizeof(OCCoAPToken));
- tokPtr->tokenLength = tokenLength;
+ tokPtr->tokenLength = token->tokenLength;
+ memcpy (tokPtr->token, token->token, tokPtr->tokenLength);
obsNode->addr = (OCDevAddr *)OCMalloc(sizeof(OCDevAddr));
VERIFY_NON_NULL (obsNode->addr);
memcpy (obsNode->addr, addr, sizeof(OCDevAddr));
obsNode->resource = resHandle;
+ obsNode->failedCommCount = 0;
+ obsNode->NONCount = 0;
+ obsNode->forceCON = 0;
LL_APPEND (serverObsList, obsNode);
return OC_STACK_OK;
return OC_STACK_NO_MEMORY;
}
-ObserveResourceServer* GetObserver (const uint8_t *token, const size_t tokenLength)
+ResourceObserver* GetObserver (const OCCoAPToken * token)
{
- ObserveResourceServer *out = NULL;
+ ResourceObserver *out = NULL;
- if(token)
+ if(token)
{
- LL_FOREACH (serverObsList, out)
+ LL_FOREACH (serverObsList, out)
{
- if((out->token->tokenLength == tokenLength) &&
- (memcmp(out->token->token, token, tokenLength) == 0)) {
+ if((out->token->tokenLength == token->tokenLength) &&
+ (memcmp(out->token->token, token->token, token->tokenLength) == 0))
+ {
return out;
}
}
return NULL;
}
-OCStackResult DeleteObserver (uint8_t *token, size_t tokenLength)
+OCStackResult DeleteObserver (OCCoAPToken * token)
{
- ObserveResourceServer *obsNode = NULL;
+ ResourceObserver *obsNode = NULL;
- obsNode = GetObserver (token, tokenLength);
- if (obsNode) {
+ obsNode = GetObserver (token);
+ if (obsNode)
+ {
LL_DELETE (serverObsList, obsNode);
OCFree(obsNode->resUri);
OCFree(obsNode->query);
OCFree(obsNode->token);
OCFree(obsNode->addr);
OCFree(obsNode);
- return OC_STACK_OK;
}
- return OC_STACK_ERROR;
+ // it is ok if we did not find the observer...
+ return OC_STACK_OK;
}
void DeleteObserverList()
{
- ObserveResourceServer *out;
- ObserveResourceServer *tmp;
- LL_FOREACH_SAFE (serverObsList, out, tmp)
+ ResourceObserver *out = NULL;
+ ResourceObserver *tmp = NULL;
+ LL_FOREACH_SAFE (serverObsList, out, tmp)
{
- DeleteObserver (out->token->token, out->token->tokenLength);
+ DeleteObserver (out->token);
}
serverObsList = NULL;
}
}
case OC_RESOURCE_NOT_SPECIFIED:
+ // This case is not needed as the logic changed so OCCancel results in RESET
+ // rather than a GET. RESET is handled at lower layers.
+
// TODO: This is a special case. In M1 this occurs only for observation
// delete since OCCancel (on the client) only takes OCDoHandle param.
// TODO: Remove comments below before release - only for code review
//-----------------------------------------------------------------------------
//This function will be called back by occoap layer when a request is received
-OCStackResult HandleStackRequests(OCRequest * request) {
+OCStackResult HandleStackRequests(OCRequest * request)
+{
OC_LOG(INFO, TAG, PCF("Entering OCStackHandleReceiveRequest (OCStack Layer)"));
OCStackResult result = OC_STACK_ERROR;
}
//This function will be called back by occoap layer when a response is received
-void HandleStackResponses(OCResponse * response) {
+void HandleStackResponses(OCResponse * response)
+{
OCStackApplicationResult result = OC_STACK_DELETE_TRANSACTION;
OC_LOG(INFO, TAG, PCF("Entering HandleStackResponses (OCStack Layer)"));
- if (response->cbNode) {
+ if (response->cbNode)
+ {
OC_LOG(INFO, TAG, PCF("Calling into application address space"));
- result = response->cbNode->callBack(response->cbNode->context, response->cbNode->handle, response->clientResponse);
- if (result == OC_STACK_DELETE_TRANSACTION) {
+ result = response->cbNode->callBack(response->cbNode->context,
+ response->cbNode->handle, response->clientResponse);
+ if (result == OC_STACK_DELETE_TRANSACTION ||
+ response->clientResponse->result == OC_STACK_COMM_ERROR)
+ {
FindAndDeleteClientCB(response->cbNode);
}
}
}
-int ParseIPv4Address(unsigned char * ipAddrStr, uint8_t * ipAddr) {
+int ParseIPv4Address(unsigned char * ipAddrStr, uint8_t * ipAddr)
+{
size_t index = 0;
unsigned char *itr, *coap;
uint8_t dotCount = 0;
/* search for scheme */
itr = ipAddrStr;
- if (!isdigit((unsigned char) *ipAddrStr)) {
+ if (!isdigit((unsigned char) *ipAddrStr))
+ {
coap = (unsigned char *) OC_COAP_SCHEME;
- while (*coap && tolower(*itr) == *coap) {
+ while (*coap && tolower(*itr) == *coap)
+ {
coap++;
itr++;
}
ipAddrStr = itr;
while (*ipAddrStr) {
- if (isdigit((unsigned char) *ipAddrStr)) {
+ if (isdigit((unsigned char) *ipAddrStr))
+ {
ipAddr[index] *= 10;
ipAddr[index] += *ipAddrStr - '0';
- } else if ((unsigned char) *ipAddrStr == '.') {
+ }
+ else if ((unsigned char) *ipAddrStr == '.')
+ {
index++;
dotCount++;
- } else {
+ }
+ else
+ {
break;
}
ipAddrStr++;
}
if (ipAddr[0] < 255 && ipAddr[1] < 255 && ipAddr[2] < 255 && ipAddr[3] < 255
- && dotCount == 3) {
+ && dotCount == 3)
+ {
return 1;
- } else {
+ }
+ else
+ {
return 0;
}
}
* OC_STACK_OK - no errors
* OC_STACK_ERROR - stack init error
*/
-OCStackResult OCInit(const char *ipAddr, uint16_t port, OCMode mode) {
+OCStackResult OCInit(const char *ipAddr, uint16_t port, OCMode mode)
+{
OC_LOG(INFO, TAG, PCF("Entering OCInit"));
- if (ipAddr) {
+ if (ipAddr)
+ {
OC_LOG_V(INFO, TAG, "IP Address = %s", ipAddr);
}
- switch (mode) {
+ switch (mode)
+ {
case OC_CLIENT:
OC_LOG(INFO, TAG, PCF("Client mode"));
break;
initResources();
// Make call to OCCoAP layer
- if (OCInitCoAP(ipAddr, (uint16_t) port, mode) == 0) {
+ if (OCInitCoAP(ipAddr, (uint16_t) port, mode) == OC_STACK_OK)
+ {
stackState = OC_STACK_INITIALIZED;
return OC_STACK_OK;
}
* OC_STACK_OK - no errors
* OC_STACK_ERROR - stack not initialized
*/
-OCStackResult OCStop() {
+OCStackResult OCStop()
+{
OCStackResult result = OC_STACK_ERROR;
OC_LOG(INFO, TAG, PCF("Entering OCStop"));
- if (stackState != OC_STACK_INITIALIZED) {
+ if (stackState != OC_STACK_INITIALIZED)
+ {
OC_LOG(ERROR, TAG, PCF("Stack not initialized"));
return OC_STACK_ERROR;
}
// Make call to OCCoAP layer
- if (OCStopCoAP() == 0) {
+ if (OCStopCoAP() == OC_STACK_OK)
+ {
// Remove all observers
DeleteObserverList();
// Remove all the client callbacks
case OC_REST_OBSERVE_ALL:
break;
default:
- return OC_STACK_INVALID_METHOD;
+ result = OC_STACK_INVALID_METHOD;
+ goto exit;
}
*handle = GenerateInvocationHandle();
- VERIFY_NON_NULL(*handle, FATAL, OC_STACK_ERROR);
+ if(!*handle)
+ {
+ result = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+
token = OCGenerateCoAPToken();
if (!token)
{
+ result = OC_STACK_NO_MEMORY;
goto exit;
}
if((result = AddClientCB(&clientCB, cbData, token, *handle, method)) != OC_STACK_OK)
{
+ result = OC_STACK_NO_MEMORY;
goto exit;
}
result = (OCStackResult)OCDoCoAPResource(method, qos, token, requiredUri, request);
exit:
-
if (result != OC_STACK_OK)
{
OC_LOG(ERROR, TAG, PCF("OCDoResource error"));
- if (clientCB)
- {
- DeleteClientCB(clientCB);
- }
- else
- {
- OCFree(token);
- OCFree(*handle);
- }
+ FindAndDeleteClientCB(clientCB);
+ OCFree(token);
+ OCFree(*handle);
}
return result;
}
* This ftn can be implemented one of two ways:
*
* 1. When observe is unobserved..Remove the callback associated on client side.
- * When the next notification comes in from server, reply with RST message to server.
+ * When the next notification comes in from server, reply with RESET message to server.
*
* 2. When OCCancel is called, and it is associated with an observe request
* (i.e. ClientCB->method == OC_REST_OBSERVE || OC_REST_OBSERVE_ALL),
{
case OC_REST_OBSERVE:
case OC_REST_OBSERVE_ALL:
- // Make call to OCCoAP layer
- DeleteClientCB(clientCB);
+ FindAndDeleteClientCB(clientCB);
break;
default:
return OC_STACK_INVALID_METHOD;
{
return OC_STACK_NO_RESOURCE;
} else {
- result = SendObserverNotification (handle, resPtr);
+ result = SendObserverNotification (resPtr);
return result;
}
}
void EXPECT_STREQ(const char *a, const char *b) {
if (strcmp(a, b) == 0) {
- OC_LOG(INFO, TAG, "PASS");
+ OC_LOG(INFO, TAG, PCF("PASS"));
} else {
- OC_LOG(ERROR, TAG, "FAIL");
+ OC_LOG(ERROR, TAG, PCF("FAIL"));
}
}
//-----------------------------------------------------------------------------