Contiki-NG
coap-engine.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 */
31
32/**
33 * \file
34 * CoAP implementation Engine.
35 * \author
36 * Matthias Kovatsch <kovatsch@inf.ethz.ch>
37 */
38
39/**
40 * \addtogroup coap
41 * @{
42 */
43
44#include "coap-engine.h"
45#include "sys/cc.h"
46#include "lib/list.h"
47#include <stdio.h>
48#include <stdlib.h>
49#include <inttypes.h>
50#include <string.h>
51
52/* Log configuration */
53#include "coap-log.h"
54#define LOG_MODULE "coap-eng"
55#define LOG_LEVEL LOG_LEVEL_COAP
56
57static void process_callback(coap_timer_t *t);
58
59/*
60 * To be called by HTTP/COAP server as a callback function when a new service
61 * request appears. This function dispatches the corresponding CoAP service.
62 */
63static int invoke_coap_resource_service(coap_message_t *request,
64 coap_message_t *response,
65 uint8_t *buffer, uint16_t buffer_size,
66 int32_t *offset);
67
68/*---------------------------------------------------------------------------*/
69/*- Variables ---------------------------------------------------------------*/
70/*---------------------------------------------------------------------------*/
71LIST(coap_handlers);
72LIST(coap_resource_services);
73static uint8_t is_initialized = 0;
74
75/*---------------------------------------------------------------------------*/
76/*- CoAP service handlers---------------------------------------------------*/
77/*---------------------------------------------------------------------------*/
78void
79coap_add_handler(coap_handler_t *handler)
80{
81 list_add(coap_handlers, handler);
82}
83/*---------------------------------------------------------------------------*/
84void
85coap_remove_handler(coap_handler_t *handler)
86{
87 list_remove(coap_handlers, handler);
88}
89/*---------------------------------------------------------------------------*/
90coap_handler_status_t
91coap_call_handlers(coap_message_t *request, coap_message_t *response,
92 uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
93{
94 coap_handler_status_t status;
95 coap_handler_t *r;
96 for(r = list_head(coap_handlers); r != NULL; r = r->next) {
97 if(r->handler) {
98 status = r->handler(request, response, buffer, buffer_size, offset);
99 if(status != COAP_HANDLER_STATUS_CONTINUE) {
100 /* Request handled. */
101
102 /* Check response code before doing observe! */
103 if(request->code == COAP_GET) {
104 coap_observe_handler(NULL, request, response);
105 }
106
107 return status;
108 }
109 }
110 }
111 return COAP_HANDLER_STATUS_CONTINUE;
112}
113/*---------------------------------------------------------------------------*/
114static CC_INLINE coap_handler_status_t
115call_service(coap_message_t *request, coap_message_t *response,
116 uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
117{
118 coap_handler_status_t status;
119 status = coap_call_handlers(request, response, buffer, buffer_size, offset);
120 if(status != COAP_HANDLER_STATUS_CONTINUE) {
121 return status;
122 }
123 status = invoke_coap_resource_service(request, response, buffer, buffer_size, offset);
124 if(status != COAP_HANDLER_STATUS_CONTINUE) {
125 return status;
126 }
127
128 coap_set_status_code(response, NOT_FOUND_4_04);
129
130 return COAP_HANDLER_STATUS_CONTINUE;
131}
132
133/*---------------------------------------------------------------------------*/
134/*- Server Part -------------------------------------------------------------*/
135/*---------------------------------------------------------------------------*/
136
137/* the discover resource is automatically included for CoAP */
138extern coap_resource_t res_well_known_core;
139
140/*---------------------------------------------------------------------------*/
141/*- Internal API ------------------------------------------------------------*/
142/*---------------------------------------------------------------------------*/
143int
144coap_receive(const coap_endpoint_t *src,
145 uint8_t *payload, uint16_t payload_length)
146{
147 /* static declaration reduces stack peaks and program code size */
148 static coap_message_t message[1]; /* this way the message can be treated as pointer as usual */
149 static coap_message_t response[1];
150 coap_transaction_t *transaction = NULL;
151 coap_handler_status_t status;
152
153 coap_status_code = coap_parse_message(message, payload, payload_length);
154 coap_set_src_endpoint(message, src);
155
156 if(coap_status_code == NO_ERROR) {
157
158 /*TODO duplicates suppression, if required by application */
159
160 LOG_DBG(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
161 message->type, message->token_len, message->code, message->mid);
162 LOG_DBG(" URL:");
163 LOG_DBG_COAP_STRING(message->uri_path, message->uri_path_len);
164 LOG_DBG_("\n");
165 LOG_DBG(" Payload: ");
166 LOG_DBG_COAP_STRING((const char *)message->payload, message->payload_len);
167 LOG_DBG_("\n");
168
169 /* handle requests */
170 if(message->code >= COAP_GET && message->code <= COAP_DELETE) {
171
172 /* use transaction buffer for response to confirmable request */
173 if((transaction = coap_new_transaction(message->mid, src))) {
174 uint32_t block_num = 0;
175 uint16_t block_size = COAP_MAX_BLOCK_SIZE;
176 uint32_t block_offset = 0;
177 int32_t new_offset = 0;
178
179 /* prepare response */
180 if(message->type == COAP_TYPE_CON) {
181 /* reliable CON requests are answered with an ACK */
182 coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
183 message->mid);
184 } else {
185 /* unreliable NON requests are answered with a NON as well */
186 coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
187 coap_get_mid());
188 /* mirror token */
189 }
190 if(message->token_len) {
191 coap_set_token(response, message->token, message->token_len);
192 /* get offset for blockwise transfers */
193 }
194 if(coap_get_header_block2
195 (message, &block_num, NULL, &block_size, &block_offset)) {
196 LOG_DBG("Blockwise: block request %"PRIu32" (%u/%u) @ %"PRIu32" bytes\n",
197 block_num, block_size, COAP_MAX_BLOCK_SIZE, block_offset);
198 block_size = MIN(block_size, COAP_MAX_BLOCK_SIZE);
199 new_offset = block_offset;
200 }
201
202 if(new_offset < 0) {
203 LOG_DBG("Blockwise: block request offset overflow\n");
204 coap_status_code = BAD_OPTION_4_02;
205 coap_error_message = "BlockOutOfScope";
206 status = COAP_HANDLER_STATUS_CONTINUE;
207 } else {
208 /* call CoAP framework and check if found and allowed */
209 status = call_service(message, response,
210 transaction->message + COAP_MAX_HEADER_SIZE,
211 block_size, &new_offset);
212 }
213
214 if(status != COAP_HANDLER_STATUS_CONTINUE) {
215
216 if(coap_status_code == NO_ERROR) {
217
218 /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */
219
220 /* resource is unaware of Block1 */
221 if(coap_is_option(message, COAP_OPTION_BLOCK1)
222 && response->code < BAD_REQUEST_4_00
223 && !coap_is_option(response, COAP_OPTION_BLOCK1)) {
224 LOG_DBG("Block1 NOT IMPLEMENTED\n");
225
226 coap_status_code = NOT_IMPLEMENTED_5_01;
227 coap_error_message = "NoBlock1Support";
228
229 /* client requested Block2 transfer */
230 } else if(coap_is_option(message, COAP_OPTION_BLOCK2)) {
231
232 /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
233 if(new_offset == block_offset) {
234 LOG_DBG("Blockwise: unaware resource with payload length %u/%u\n",
235 response->payload_len, block_size);
236 if(block_offset >= response->payload_len) {
237 LOG_DBG("handle_incoming_data(): block_offset >= response->payload_len\n");
238
239 response->code = BAD_OPTION_4_02;
240 coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
241 } else {
242 coap_set_header_block2(response, block_num,
243 response->payload_len -
244 block_offset > block_size,
245 block_size);
246 coap_set_payload(response,
247 response->payload + block_offset,
248 MIN(response->payload_len -
249 block_offset, block_size));
250 } /* if(valid offset) */
251
252 /* resource provides chunk-wise data */
253 } else {
254 LOG_DBG("Blockwise: blockwise resource, new offset %"PRId32"\n",
255 new_offset);
256 coap_set_header_block2(response, block_num,
257 new_offset != -1
258 || response->payload_len >
259 block_size, block_size);
260
261 if(response->payload_len > block_size) {
262 coap_set_payload(response, response->payload,
263 block_size);
264 }
265 } /* if(resource aware of blockwise) */
266
267 /* Resource requested Block2 transfer */
268 } else if(new_offset != 0) {
269 LOG_DBG("Blockwise: no block option for blockwise resource, using block size %u\n",
270 COAP_MAX_BLOCK_SIZE);
271
272 coap_set_header_block2(response, 0, new_offset != -1,
273 COAP_MAX_BLOCK_SIZE);
274 coap_set_payload(response, response->payload,
275 MIN(response->payload_len,
276 COAP_MAX_BLOCK_SIZE));
277 } /* blockwise transfer handling */
278 } /* no errors/hooks */
279 /* successful service callback */
280 /* serialize response */
281 }
282 if(coap_status_code == NO_ERROR) {
283 if((transaction->message_len = coap_serialize_message(response,
284 transaction->
285 message)) ==
286 0) {
287 coap_status_code = PACKET_SERIALIZATION_ERROR;
288 }
289 }
290 } else {
291 coap_status_code = SERVICE_UNAVAILABLE_5_03;
292 coap_error_message = "NoFreeTraBuffer";
293 } /* if(transaction buffer) */
294
295 /* handle responses */
296 } else {
297
298 if(message->type == COAP_TYPE_CON && message->code == 0) {
299 LOG_INFO("Received Ping\n");
300 coap_status_code = PING_RESPONSE;
301 } else if(message->type == COAP_TYPE_ACK) {
302 /* transactions are closed through lookup below */
303 LOG_DBG("Received ACK\n");
304 } else if(message->type == COAP_TYPE_RST) {
305 LOG_INFO("Received RST\n");
306 /* cancel possible subscriptions */
307 coap_remove_observer_by_mid(src, message->mid);
308 }
309
310 if((transaction = coap_get_transaction_by_mid(message->mid))) {
311 /* free transaction memory before callback, as it may create a new transaction */
312 coap_resource_response_handler_t callback = transaction->callback;
313 void *callback_data = transaction->callback_data;
314
315 coap_clear_transaction(transaction);
316
317 /* check if someone registered for the response */
318 if(callback) {
319 callback(callback_data, message);
320 }
321 }
322 /* if(ACKed transaction) */
323 transaction = NULL;
324
325#if COAP_OBSERVE_CLIENT
326 /* if observe notification */
327 if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
328 && coap_is_option(message, COAP_OPTION_OBSERVE)) {
329 LOG_DBG("Observe [%"PRId32"]\n", message->observe);
330 coap_handle_notification(src, message);
331 }
332#endif /* COAP_OBSERVE_CLIENT */
333 } /* request or response */
334 } /* parsed correctly */
335
336 /* if(parsed correctly) */
337 if(coap_status_code == NO_ERROR) {
338 if(transaction) {
339 coap_send_transaction(transaction);
340 }
341 } else if(coap_status_code == MANUAL_RESPONSE) {
342 LOG_DBG("Clearing transaction for manual response");
343 coap_clear_transaction(transaction);
344 } else {
345 coap_message_type_t reply_type = COAP_TYPE_ACK;
346
347 LOG_WARN("ERROR %u: %s\n", coap_status_code, coap_error_message);
348 coap_clear_transaction(transaction);
349
350 if(coap_status_code == PING_RESPONSE) {
351 coap_status_code = 0;
352 reply_type = COAP_TYPE_RST;
353 } else if(coap_status_code >= 192) {
354 /* set to sendable error code */
355 coap_status_code = INTERNAL_SERVER_ERROR_5_00;
356 /* reuse input buffer for error message */
357 }
358 coap_init_message(message, reply_type, coap_status_code,
359 message->mid);
360 coap_set_payload(message, coap_error_message,
361 strlen(coap_error_message));
362 coap_sendto(src, payload, coap_serialize_message(message, payload));
363 }
364
365 /* if(new data) */
366 return coap_status_code;
367}
368/*---------------------------------------------------------------------------*/
369void
370coap_engine_init(void)
371{
372 /* avoid initializing twice */
373 if(is_initialized) {
374 return;
375 }
376 is_initialized = 1;
377
378 LOG_INFO("Starting CoAP engine...\n");
379
380 list_init(coap_handlers);
381 list_init(coap_resource_services);
382
383 coap_activate_resource(&res_well_known_core, ".well-known/core");
384
386 coap_init_connection();
387}
388/*---------------------------------------------------------------------------*/
389/**
390 * \brief Makes a resource available under the given URI path
391 *
392 * The resource implementation must be imported first using the
393 * extern keyword. The build system takes care of compiling every
394 * *.c file in the ./resources/ sub-directory (see example Makefile).
395 */
396void
397coap_activate_resource(coap_resource_t *resource, const char *path)
398{
399 coap_periodic_resource_t *periodic;
400 resource->url = path;
401 list_add(coap_resource_services, resource);
402
403 LOG_INFO("Activating: %s\n", resource->url);
404
405 /* Only add periodic resources with a periodic_handler and a period > 0. */
406 if(resource->flags & IS_PERIODIC && resource->periodic
407 && resource->periodic->periodic_handler
408 && resource->periodic->period) {
409 LOG_DBG("Periodic resource: %p (%s)\n", resource->periodic, path);
410 periodic = resource->periodic;
411 coap_timer_set_callback(&periodic->periodic_timer, process_callback);
412 coap_timer_set_user_data(&periodic->periodic_timer, resource);
413 coap_timer_set(&periodic->periodic_timer, periodic->period);
414 }
415}
416/*---------------------------------------------------------------------------*/
417
418/*---------------------------------------------------------------------------*/
419/*- Internal API ------------------------------------------------------------*/
420/*---------------------------------------------------------------------------*/
421coap_resource_t *
423{
424 return list_head(coap_resource_services);
425}
426/*---------------------------------------------------------------------------*/
427coap_resource_t *
428coap_get_next_resource(coap_resource_t *resource)
429{
430 return list_item_next(resource);
431}
432/*---------------------------------------------------------------------------*/
433static int
434invoke_coap_resource_service(coap_message_t *request, coap_message_t *response,
435 uint8_t *buffer, uint16_t buffer_size,
436 int32_t *offset)
437{
438 uint8_t found = 0;
439 uint8_t allowed = 1;
440
441 coap_resource_t *resource = NULL;
442 const char *url = NULL;
443 int url_len, res_url_len;
444
445 url_len = coap_get_header_uri_path(request, &url);
446 for(resource = list_head(coap_resource_services);
447 resource; resource = resource->next) {
448
449 /* if the web service handles that kind of requests and urls matches */
450 res_url_len = strlen(resource->url);
451 if((url_len == res_url_len
452 || (url_len > res_url_len
453 && (resource->flags & HAS_SUB_RESOURCES)
454 && url[res_url_len] == '/'))
455 && strncmp(resource->url, url, res_url_len) == 0) {
456 coap_resource_flags_t method = coap_get_method_type(request);
457 found = 1;
458
459 LOG_INFO("/%s, method %u, resource->flags %u\n", resource->url,
460 (uint16_t)method, resource->flags);
461
462 if((method & METHOD_GET) && resource->get_handler != NULL) {
463 /* call handler function */
464 resource->get_handler(request, response, buffer, buffer_size, offset);
465 } else if((method & METHOD_POST) && resource->post_handler != NULL) {
466 /* call handler function */
467 resource->post_handler(request, response, buffer, buffer_size,
468 offset);
469 } else if((method & METHOD_PUT) && resource->put_handler != NULL) {
470 /* call handler function */
471 resource->put_handler(request, response, buffer, buffer_size, offset);
472 } else if((method & METHOD_DELETE) && resource->delete_handler != NULL) {
473 /* call handler function */
474 resource->delete_handler(request, response, buffer, buffer_size,
475 offset);
476 } else {
477 allowed = 0;
478 coap_set_status_code(response, METHOD_NOT_ALLOWED_4_05);
479 }
480 break;
481 }
482 }
483 if(!found) {
484 coap_set_status_code(response, NOT_FOUND_4_04);
485 } else if(allowed) {
486 /* final handler for special flags */
487 if(resource->flags & IS_OBSERVABLE) {
488 coap_observe_handler(resource, request, response);
489 }
490 }
491 return found & allowed;
492}
493/*---------------------------------------------------------------------------*/
494/* This callback occurs when t is expired */
495static void
496process_callback(coap_timer_t *t)
497{
498 coap_resource_t *resource;
499 resource = coap_timer_get_user_data(t);
500 if(resource != NULL && (resource->flags & IS_PERIODIC)
501 && resource->periodic != NULL && resource->periodic->period) {
502 LOG_DBG("Periodic: timer expired for /%s (period: %"PRIu32")\n",
503 resource->url, resource->periodic->period);
504
505 if(!is_initialized) {
506 /* CoAP has not yet been initialized. */
507 } else if(resource->periodic->periodic_handler) {
508 /* Call the periodic_handler function. */
509 resource->periodic->periodic_handler();
510 }
511
512 coap_timer_set(t, resource->periodic->period);
513 }
514}
515/*---------------------------------------------------------------------------*/
516/** @} */
Default definitions of C compiler quirk work-arounds.
CoAP engine implementation.
Log support for CoAP.
static void * coap_timer_get_user_data(coap_timer_t *timer)
Get user data that has been attached to a CoAP timer.
Definition: coap-timer.h:118
static void coap_timer_set_user_data(coap_timer_t *timer, void *data)
Attach user data to a CoAP timer.
Definition: coap-timer.h:130
void coap_timer_set(coap_timer_t *timer, uint64_t time)
Set a CoAP timer to expire after the specified time.
Definition: coap-timer.c:103
static void coap_timer_set_callback(coap_timer_t *timer, void(*callback)(coap_timer_t *))
Set a callback function to be called when a CoAP timer expires.
Definition: coap-timer.h:105
int coap_sendto(const coap_endpoint_t *ep, const uint8_t *data, uint16_t len)
Send a message to the specified CoAP endpoint.
Definition: coap-uip.c:369
void coap_transport_init(void)
Initialize the CoAP transport.
Definition: coap-uip.c:328
void coap_activate_resource(coap_resource_t *resource, const char *path)
Makes a resource available under the given URI path.
Definition: coap-engine.c:397
coap_resource_t * coap_get_next_resource(coap_resource_t *resource)
Returns the next registered CoAP resource.
Definition: coap-engine.c:428
coap_resource_flags_t
Resource flags for allowed methods and special functionalities.
coap_resource_t * coap_get_first_resource(void)
Returns the first of the registered CoAP resources.
Definition: coap-engine.c:422
void list_init(list_t list)
Initialize a list.
Definition: list.c:57
#define LIST(name)
Declare a linked list.
Definition: list.h:89
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:89
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition: list.c:152
void * list_item_next(const void *item)
Get the next item following this item.
Definition: list.c:203
void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition: list.c:63
Linked list manipulation routines.