Contiki 3.x
rest-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  * An abstraction layer for RESTful Web services (Erbium).
35  * Inspired by RESTful Contiki by Dogan Yazar.
36  * \author
37  * Matthias Kovatsch <kovatsch@inf.ethz.ch>
38  */
39 
40 #include <string.h>
41 #include <stdio.h>
42 #include "contiki.h"
43 #include "rest-engine.h"
44 
45 #define DEBUG 0
46 #if DEBUG
47 #include <stdio.h>
48 #define PRINTF(...) printf(__VA_ARGS__)
49 #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
50 #define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
51 #else
52 #define PRINTF(...)
53 #define PRINT6ADDR(addr)
54 #define PRINTLLADDR(addr)
55 #endif
56 
57 PROCESS(rest_engine_process, "REST Engine");
58 /*---------------------------------------------------------------------------*/
59 LIST(restful_services);
60 LIST(restful_periodic_services);
61 /*---------------------------------------------------------------------------*/
62 /*- REST Engine API ---------------------------------------------------------*/
63 /*---------------------------------------------------------------------------*/
64 /**
65  * \brief Initializes and starts the REST Engine process
66  *
67  * This function must be called by server processes before any resources are
68  * registered through rest_activate_resource().
69  */
70 void
72 {
73  /* avoid initializing twice */
74  static uint8_t initialized = 0;
75 
76  if(initialized) {
77  PRINTF("REST engine process already running - double initialization?\n");
78  return;
79  }
80  initialized = 1;
81 
82  list_init(restful_services);
83 
84  REST.set_service_callback(rest_invoke_restful_service);
85 
86  /* Start the RESTful server implementation. */
87  REST.init();
88 
89  /*Start REST engine process */
90  process_start(&rest_engine_process, NULL);
91 }
92 /*---------------------------------------------------------------------------*/
93 /**
94  * \brief Makes a resource available under the given URI path
95  * \param resource A pointer to a resource implementation
96  * \param path The URI path string for this resource
97  *
98  * The resource implementation must be imported first using the
99  * extern keyword. The build system takes care of compiling every
100  * *.c file in the ./resources/ sub-directory (see example Makefile).
101  */
102 void
103 rest_activate_resource(resource_t *resource, char *path)
104 {
105  resource->url = path;
106  list_add(restful_services, resource);
107 
108  PRINTF("Activating: %s\n", resource->url);
109 
110  /* Only add periodic resources with a periodic_handler and a period > 0. */
111  if(resource->flags & IS_PERIODIC && resource->periodic->periodic_handler
112  && resource->periodic->period) {
113  PRINTF("Periodic resource: %p (%s)\n", resource->periodic,
114  resource->periodic->resource->url);
115  list_add(restful_periodic_services, resource->periodic);
116  }
117 }
118 /*---------------------------------------------------------------------------*/
119 /*- Internal API ------------------------------------------------------------*/
120 /*---------------------------------------------------------------------------*/
121 list_t
123 {
124  return restful_services;
125 }
126 /*---------------------------------------------------------------------------*/
127 int
128 rest_invoke_restful_service(void *request, void *response, uint8_t *buffer,
129  uint16_t buffer_size, int32_t *offset)
130 {
131  uint8_t found = 0;
132  uint8_t allowed = 1;
133 
134  resource_t *resource = NULL;
135  const char *url = NULL;
136  int url_len, res_url_len;
137 
138  url_len = REST.get_url(request, &url);
139  for(resource = (resource_t *)list_head(restful_services);
140  resource; resource = resource->next) {
141 
142  /* if the web service handles that kind of requests and urls matches */
143  res_url_len = strlen(resource->url);
144  if((url_len == res_url_len
145  || (url_len > res_url_len
146  && (resource->flags & HAS_SUB_RESOURCES)
147  && url[res_url_len] == '/'))
148  && strncmp(resource->url, url, res_url_len) == 0) {
149  found = 1;
150  rest_resource_flags_t method = REST.get_method_type(request);
151 
152  PRINTF("/%s, method %u, resource->flags %u\n", resource->url,
153  (uint16_t)method, resource->flags);
154 
155  if((method & METHOD_GET) && resource->get_handler != NULL) {
156  /* call handler function */
157  resource->get_handler(request, response, buffer, buffer_size, offset);
158  } else if((method & METHOD_POST) && resource->post_handler != NULL) {
159  /* call handler function */
160  resource->post_handler(request, response, buffer, buffer_size,
161  offset);
162  } else if((method & METHOD_PUT) && resource->put_handler != NULL) {
163  /* call handler function */
164  resource->put_handler(request, response, buffer, buffer_size, offset);
165  } else if((method & METHOD_DELETE) && resource->delete_handler != NULL) {
166  /* call handler function */
167  resource->delete_handler(request, response, buffer, buffer_size,
168  offset);
169  } else {
170  allowed = 0;
171  REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED);
172  }
173  break;
174  }
175  }
176  if(!found) {
177  REST.set_response_status(response, REST.status.NOT_FOUND);
178  } else if(allowed) {
179  /* final handler for special flags */
180  if(resource->flags & IS_OBSERVABLE) {
181  REST.subscription_handler(resource, request, response);
182  }
183  }
184  return found & allowed;
185 }
186 /*-----------------------------------------------------------------------------------*/
187 PROCESS_THREAD(rest_engine_process, ev, data)
188 {
189  PROCESS_BEGIN();
190 
191  /* pause to let REST server finish adding resources. */
192  PROCESS_PAUSE();
193 
194  /* initialize the PERIODIC_RESOURCE timers, which will be handled by this process. */
195  periodic_resource_t *periodic_resource = NULL;
196 
197  for(periodic_resource =
198  (periodic_resource_t *)list_head(restful_periodic_services);
199  periodic_resource; periodic_resource = periodic_resource->next) {
200  if(periodic_resource->periodic_handler && periodic_resource->period) {
201  PRINTF("Periodic: Set timer for /%s to %lu\n",
202  periodic_resource->resource->url, periodic_resource->period);
203  etimer_set(&periodic_resource->periodic_timer,
204  periodic_resource->period);
205  }
206  }
207  while(1) {
209 
210  if(ev == PROCESS_EVENT_TIMER) {
211  for(periodic_resource =
212  (periodic_resource_t *)list_head(restful_periodic_services);
213  periodic_resource; periodic_resource = periodic_resource->next) {
214  if(periodic_resource->period
215  && etimer_expired(&periodic_resource->periodic_timer)) {
216 
217  PRINTF("Periodic: etimer expired for /%s (period: %lu)\n",
218  periodic_resource->resource->url, periodic_resource->period);
219 
220  /* Call the periodic_handler function, which was checked during adding to list. */
221  (periodic_resource->periodic_handler)();
222 
223  etimer_reset(&periodic_resource->periodic_timer);
224  }
225  }
226  }
227  }
228 
229  PROCESS_END();
230 }
231 /*---------------------------------------------------------------------------*/
#define LIST(name)
Declare a linked list.
Definition: list.h:86
rest_resource_flags_t
Resource flags for allowed methods and special functionalities.
list_t rest_get_resources(void)
Returns the list of registered RESTful resources.
Definition: rest-engine.c:122
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
An abstraction layer for RESTful Web services (Erbium).
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
void ** list_t
The linked list type.
Definition: list.h:133
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
void rest_activate_resource(resource_t *resource, char *path)
Makes a resource available under the given URI path.
Definition: rest-engine.c:103
#define NULL
The null pointer.
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:213
void rest_init_engine(void)
Initializes and starts the REST Engine process.
Definition: rest-engine.c:71
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
#define PROCESS_PAUSE()
Yield the process for a short while.
Definition: process.h:221
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
void etimer_reset(struct etimer *et)
Reset an event timer with the same interval as was previously set.
Definition: etimer.c:192
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120