Contiki 3.x
res_sample.c
Go to the documentation of this file.
1 /**
2  * @file res_sample.c
3  * Sample resource.
4  * Serves stored samples, and can delete them.
5  * Arthur Fabre 2015
6  */
7 
8 #include "er-server.h"
9 #include "rest-engine.h"
10 #include "pb_decode.h"
11 #include "pb_encode.h"
12 #include "readings.pb.h"
13 #include "store.h"
14 #include <inttypes.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 
19 #define DEBUG_ON
20 #include "debug.h"
21 
22 /**
23  * Maximum length of a URI
24  */
25 #define MAX_URI_LENGTH 20
26 
27 /**
28  * Indicates no trailing sample id was found in the URI
29  */
30 #define NO_SAMPLE_ID -1
31 
32 /**
33  * Indicates a sample id was found in the URI, but that it could not be sucesfully parsed
34  */
35 #define INVALID_SAMPLE_ID -2
36 
37 /**
38  * The separator used in URIs, either as a character, or as a string literal.
39  */
40 #define SEPARATOR_CHAR '/'
41 #define SEPARATOR_STR "/"
42 
43 /**
44  * Get handler for Samples.
45  * Supports the optional param id. Serves latest if id isn't specified.
46  * Format is GET /sample/23 to get sample #23. GET /sample to get the latest sample.
47  */
48 static void res_get_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
49 
50 /**
51  * Delete handler for Samples.
52  * Supports deleting arbitrary Samples.
53  * Format is DELETE /sample/23 to delete sample #23
54  */
55 static void res_delete_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
56 
57 /**
58  * Parse a trailing sample id from a URI.
59  * Deals with validating the ID, trailing slashes.
60  * @return NO_SAMPLE_ID if no trailing sample id was found in the uri.
61  * INVALID_SAMPLE_ID If a trailing sample if was found, but it could not be parsed.
62  * The sample id on success.
63  */
64 static int16_t parse_sample_id(void *request);
65 
66 /**
67  * Sample ressource.
68  * Parent ressource as we use URL based parametes (like GET /sample/32)
69  */
71 
72 void res_get_handler(void* request, void* response, uint8_t *payload_buffer, uint16_t preferred_size, int32_t *offset) {
73  static uint8_t sample_buffer[Sample_size];
74  static uint8_t sample_len;
75  int16_t sample_id;
76  uint8_t payload_len;
77 
78  int16_t current_offset = *offset;
79 
80  DEBUG("Serving request! Offset %d, PrefSize %d\n", current_offset, preferred_size);
81 
82  // Only get data if this is the first request of a blockwise transfer
83  if (current_offset == 0) {
84 
85  sample_id = parse_sample_id(request);
86 
87  // Error out if a sample specified was invalid
88  if (sample_id == INVALID_SAMPLE_ID) {
89  DEBUG("Get request with invalid sample id!\n");
90  REST.set_response_status(response, REST.status.BAD_REQUEST);
91  return;
92  }
93 
94  // Get the latest sample if no sample id was specified
95  if (sample_id == NO_SAMPLE_ID) {
96  sample_len = store_get_latest_raw_sample(sample_buffer);
97  } else {
98  sample_len = store_get_raw_sample(sample_id, sample_buffer);
99  }
100 
101  if (!sample_len) {
102  DEBUG("Unable to get sample!\n");
103  REST.set_response_status(response, REST.status.NOT_FOUND);
104  return;
105  }
106  }
107 
108  DEBUG("Got a Sample, size: %u\n", sample_len);
109 
110  // If whatever we have to send fits in one block, just send that
111  if (sample_len - current_offset <= preferred_size) {
112  DEBUG("Request fits in a single block\n");
113 
114  payload_len = sample_len - current_offset;
115 
116  // Indicates this is the last chunk
117  *offset = -1;
118 
119  // Otherwise copy data in chunks to the buffer
120  } else {
121  DEBUG("Request will be split into chunks\n");
122 
123  payload_len = preferred_size;
124 
125  *offset += preferred_size;
126  }
127 
128  memcpy(payload_buffer, sample_buffer + current_offset, payload_len);
129 
130  REST.set_header_content_type(response, REST.type.APPLICATION_OCTET_STREAM);
131  REST.set_response_payload(response, payload_buffer, payload_len);
132  DEBUG("Done!\n");
133 }
134 
135 void res_delete_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) {
136  int16_t sample_id;
137 
138  sample_id = parse_sample_id(request);
139 
140  if (sample_id == NO_SAMPLE_ID || sample_id == INVALID_SAMPLE_ID) {
141  DEBUG("Delete request with invalid / missing sample id!\n");
142  REST.set_response_status(response, REST.status.BAD_REQUEST);
143  return;
144  }
145 
146  DEBUG("Delete request for: %d\n", sample_id);
147 
148  if (!store_delete_sample(sample_id)) {
149  DEBUG("Failed to delete sample\n");
150  REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR);
151  return;
152  }
153 
154  REST.set_response_status(response, REST.status.DELETED);
155 }
156 
157 int16_t parse_sample_id(void *request) {
158  const char *uri_path;
159  int uri_length;
160  // Need extra byte for null terminator
161  char terminated_uri_path[MAX_URI_LENGTH + 1];
162  char *token_ptr;
163  char *end_ptr;
164  int16_t sample_id;
165 
166  uri_length = REST.get_url(request, &uri_path);
167 
168  if (uri_length > MAX_URI_LENGTH) {
169  uri_length = MAX_URI_LENGTH;
170  }
171 
172  // The returned uir isn't NULL terminated - need to do it ourselves
173  strncpy(terminated_uri_path, uri_path, uri_length);
174  terminated_uri_path[uri_length] = '\0';
175  token_ptr = &terminated_uri_path[0];
176 
177  // Parse the uri
178  strsep(&token_ptr, SEPARATOR_STR);
179  if (token_ptr == NULL) {
180  DEBUG("Request with no sample id!\n");
181  return NO_SAMPLE_ID;
182  }
183 
184  // Convert the string to an int
185  errno = 0;
186  sample_id = strtoul(token_ptr, &end_ptr, 0);
187  // Errno being set indicates an error occured.
188  // endptr != \0 indicates that the entire string was not parsed succesfully - we want to ensure the entire string is valid.
189  // endptr == SEPARATOR is valid to accept trailing SEPARATOR
190  if (errno != 0 || (*end_ptr != '\0' && *end_ptr != SEPARATOR_CHAR)) {
191  DEBUG("Request with invalid sample id!\n");
192  return INVALID_SAMPLE_ID;
193  }
194 
195  return sample_id;
196 }
#define NO_SAMPLE_ID
Indicates no trailing sample id was found in the URI.
Definition: res_sample.c:30
PARENT_RESOURCE(res_sample,"Sample", res_get_handler, NULL, NULL, res_delete_handler)
Sample ressource.
#define MAX_URI_LENGTH
Maximum length of a URI.
Definition: res_sample.c:25
An abstraction layer for RESTful Web services (Erbium).
uint8_t store_get_raw_sample(uint16_t id, uint8_t buffer[Sample_size])
Get a given sample from the flash, in the form of an encoded protocol buffer.
Definition: store.c:155
#define INVALID_SAMPLE_ID
Indicates a sample id was found in the URI, but that it could not be sucesfully parsed.
Definition: res_sample.c:35
static void res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
Delete handler for Samples.
Definition: res_sample.c:135
#define NULL
The null pointer.
Convenience layer for storing readings and the config.
bool store_delete_sample(uint16_t id)
Delete a given sample from the flash.
Definition: store.c:174
#define SEPARATOR_CHAR
The separator used in URIs, either as a character, or as a string literal.
Definition: res_sample.c:40
static int16_t parse_sample_id(void *request)
Parse a trailing sample id from a URI.
Definition: res_sample.c:157
static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
Get handler for Samples.
Definition: res_sample.c:72
uint8_t store_get_latest_raw_sample(uint8_t buffer[Sample_size])
Get the most recent sample from the flash, in the form of an encoded protocol buffer.
Definition: store.c:135