HomePort
rest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011 Aalborg University. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this list of
8  * conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11  * of conditions and the following disclaimer in the documentation and/or other materials
12  * provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Aalborg University ''AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Aalborg University OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  */
27 
28 #include "rest_intern.h"
29 #include "hpd/modules/hpd_rest.h"
31 #include "hpd/common/hpd_common.h"
32 #include "hpd/common/hpd_httpd.h"
33 #include <curl/curl.h>
34 #include "rest_json.h"
35 #include "rest_xml.h"
36 
37 static hpd_error_t rest_on_create(void **data, const hpd_module_t *context);
38 static hpd_error_t rest_on_destroy(void *data);
39 static hpd_error_t rest_on_start(void *data, hpd_t *hpd);
40 static hpd_error_t rest_on_stop(void *data, hpd_t *hpd);
41 static hpd_error_t rest_on_parse_opt(void *data, const char *name, const char *arg);
42 
44 
45 typedef enum rest_content_type {
52 
53 struct hpd_rest {
58  CURL *curl;
59 };
60 
61 typedef struct hpd_rest_req {
63 
64  // The HTTP request and data from it
67  const char *url;
69  char *body;
70  size_t len;
71 
72  // Request data converted to hpd notation
76 
77  // The HTTP response (if we sent any yet)
80 
81 static rest_content_type_t rest_media_type_to_enum(const char *haystack)
82 {
83  char *str;
84 
85  if (haystack == NULL)
86  return CONTENT_NONE;
87  if ( strcmp(haystack, "application/xml") == 0 ||
88  strncmp(haystack, "application/xml;", 16) == 0 ||
89  ((str = strstr(haystack, ",application/xml")) && (str[16] == ';' || str[16] == '\0')))
90  return CONTENT_XML;
91  if ( strcmp(haystack, "application/json") == 0 ||
92  strncmp(haystack, "application/json;", 17) == 0 ||
93  ((str = strstr(haystack, ",application/json")) && (str[17] == ';' || str[17] == '\0')))
94  return CONTENT_JSON;
95  if (strcmp(haystack, "*/*") == 0)
96  return CONTENT_WILDCARD;
97  return CONTENT_UNKNOWN;
98 }
99 
100 static hpd_error_t rest_url_encode(hpd_rest_t *rest, const char *decoded, char **encoded)
101 {
102  (*encoded) = curl_easy_escape(rest->curl, decoded, 0);
103  if (!(*encoded)) HPD_LOG_RETURN(rest->context, HPD_E_UNKNOWN, "Curl failed encoding url.");
104  return HPD_E_SUCCESS;
105 }
106 
107 static hpd_error_t rest_url_decode(hpd_rest_t *rest, const char *encoded, char **decoded)
108 {
109  (*decoded) = curl_easy_unescape(rest->curl, encoded, 0, NULL);
110  if (!(*decoded)) HPD_LOG_RETURN(rest->context, HPD_E_UNKNOWN, "Curl failed decoding url.");
111  return HPD_E_SUCCESS;
112 }
113 
114 // Conforms to ISO 8601
116 {
117  time_t now = time(NULL);
118  struct tm *tm = gmtime(&now);
119  if (!tm) HPD_LOG_RETURN(context, HPD_E_UNKNOWN, "Time conversion failed.");
120  strftime(str, 20, "%FT%TZ", tm);
121  return HPD_E_SUCCESS;
122 }
123 
125 {
126  hpd_error_t rc;
127 
128  hpd_adapter_id_t *adapter = NULL;
129  hpd_device_id_t *device = NULL;
130  if ((rc = hpd_service_get_adapter(service, &adapter))) goto id_error;
131  if ((rc = hpd_service_get_device(service, &device))) goto id_error;
132 
133  const char *adp_id, *dev_id, *srv_id;
134  if ((rc = hpd_service_get_id(service, &srv_id))) goto id_error;
135  if ((rc = hpd_device_get_id(device, &dev_id))) goto id_error;
136  if ((rc = hpd_adapter_get_id(adapter, &adp_id))) goto id_error;
137 
138  if ((rc = hpd_device_id_free(device))) {
139  device = NULL;
140  goto id_error;
141  }
142  device = NULL;
143  if ((rc = hpd_adapter_id_free(adapter))) {
144  adapter = NULL;
145  goto id_error;
146  }
147  adapter = NULL;
148 
149  char *aid = NULL, *did = NULL, *sid = NULL;
150  if ((rc = rest_url_encode(rest, adp_id, &aid))) goto encode_error;
151  if ((rc = rest_url_encode(rest, dev_id, &did))) goto encode_error;
152  if ((rc = rest_url_encode(rest, srv_id, &sid))) goto encode_error;
153 
154  (*url) = malloc((strlen(aid)+strlen(did)+strlen(sid)+3+1)*sizeof(char));
155  if (!(*url)) {
156  rc = HPD_E_ALLOC;
157  goto encode_error;
158  }
159  (*url)[0] = '\0';
160 
161  strcat((*url), "/");
162  strcat((*url), aid);
163  strcat((*url), "/");
164  strcat((*url), did);
165  strcat((*url), "/");
166  strcat((*url), sid);
167 
168  free(aid);
169  free(did);
170  free(sid);
171 
172  return HPD_E_SUCCESS;
173 
174  id_error:
175  hpd_adapter_id_free(adapter);
176  hpd_device_id_free(device);
177  return rc;
178 
179  encode_error:
180  free(aid);
181  free(did);
182  free(sid);
183  return rc;
184 }
185 
186 static hpd_error_t rest_url_extract(hpd_rest_t *rest, const char *url, char **aid, char **did, char **sid)
187 {
188  const char *aid_p, *did_p, *sid_p;
189  size_t aid_l, did_l, sid_l;
190 
191  if (strlen(url) < 2)
192  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
193  if (url[0] != '/')
194  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
195 
196  aid_p = &url[1];
197  if (aid_p[0] == '\0' || aid_p[0] == '/')
198  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
199  for (aid_l = 0; aid_p[aid_l] != '/'; aid_l ++)
200  if (aid_p[aid_l] == '\0')
201  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
202 
203  did_p = &aid_p[aid_l+1];
204  if (did_p[0] == '\0' || did_p[0] == '/')
205  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
206  for (did_l = 0; did_p[did_l] != '/'; did_l ++)
207  if (did_p[did_l] == '\0')
208  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
209 
210  sid_p = &did_p[did_l+1];
211  if (sid_p[0] == '\0' || sid_p[0] == '/')
212  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
213  for (sid_l = 0; sid_p[sid_l] != '/' && sid_p[sid_l] != '\0'; sid_l++);
214 
215  if (!sid_p[sid_l] == '\0')
216  HPD_LOG_RETURN(rest->context, HPD_E_ARGUMENT, "URL Parse error.");
217 
218  *aid = NULL;
219  *did = NULL;
220  *sid = NULL;
221  HPD_STR_N_CPY(*aid, aid_p, aid_l);
222  HPD_STR_N_CPY(*did, did_p, did_l);
223  HPD_STR_N_CPY(*sid, sid_p, sid_l);
224 
225  return HPD_E_SUCCESS;
226 
227  alloc_error:
228  free(*aid);
229  free(*did);
230  free(*sid);
231  return HPD_E_ALLOC;
232 }
233 
235  const hpd_module_t *context)
236 {
237  hpd_error_t rc;
238 
239  if (rest_req && rest_req->http_res)
240  HPD_LOG_RETURN(rest_req->rest->context, HPD_E_STATE, "Already replied to request.");
241 
242  // Check status code
243  switch (status) {
244 #define XX(NUM, STR) case HPD_S_##NUM: break;
246 #undef XX
247  default:
248  if (context) HPD_LOG_WARN(context, "Unknown HTTP Status code.");
249  status = HPD_S_500;
250  }
251 
253  if ((rc = hpd_httpd_response_create(&res, req, status))) return rc;
254 
255  switch (status) {
256 #define XX(NUM, STR) case HPD_S_##NUM: rc = hpd_httpd_response_sendf(res, #STR); break;
258 #undef XX
259  }
260 
261  if (rc) {
263  return rc;
264  }
265 
266  if (rest_req) rest_req->http_res = res;
267  return hpd_httpd_response_destroy(res);
268 }
269 
271 {
272  return rest_reply(req, HPD_S_500, rest_req, context);
273 }
274 
276 {
277  return rest_reply(req, HPD_S_404, rest_req, context);
278 }
279 
281 {
282  return rest_reply(req, HPD_S_415, rest_req, context);
283 }
284 
286 {
287  return rest_reply(req, HPD_S_400, rest_req, context);
288 }
289 
291 {
292  return rest_reply(req, HPD_S_405, rest_req, context);
293 }
294 
296 {
297  hpd_error_t rc, rc2;
298  hpd_httpd_request_t *http_req = rest_req->http_req;
299  hpd_rest_t *rest = rest_req->rest;
300  const hpd_module_t *context = rest->context;
301 
302  if (rest_req->http_res) HPD_LOG_RETURN(context, HPD_E_STATE, "Response already sent.");
303 
304  // Get Accept header
305  const char *accept;
306  switch ((rc = hpd_map_get(rest_req->headers, "Accept", &accept))) {
307  case HPD_E_SUCCESS:
308  break;
309  case HPD_E_NOT_FOUND:
310  accept = NULL;
311  break;
312  default:
313  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
314  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
315  }
316  return rc;
317  }
318 
319  // Create body
320  char *body;
321  switch (rest_media_type_to_enum(accept)) {
322  case CONTENT_NONE:
323  case CONTENT_XML:
324  case CONTENT_WILDCARD:
325  if ((rc = hpd_rest_xml_get_configuration(rest->hpd, rest, context, &body))) return rc;
326  break;
327  case CONTENT_JSON:
328  if ((rc = hpd_rest_json_get_configuration(rest->hpd, rest, context, &body))) return rc;
329  break;
330  case CONTENT_UNKNOWN:
331  if ((rc = rest_reply_unsupported_media_type(http_req, rest_req, context))) {
332  HPD_LOG_ERROR(context, "Failed to send unsupported media type response (code: %d).", rc);
333  }
334  return HPD_E_SUCCESS;
335  }
336 
337  // Send response
338  if ((rc = hpd_httpd_response_create(&rest_req->http_res, http_req, HPD_S_200))) goto create_error;
339  if ((rc = hpd_httpd_response_sendf(rest_req->http_res, "%s", body))) goto response_error;
340  if ((rc = hpd_httpd_response_destroy(rest_req->http_res))) goto create_error;
341 
342  free(body);
343 
344  return rc;
345 
346  response_error:
347  if ((rc2 = hpd_httpd_response_destroy(rest_req->http_res)))
348  HPD_LOG_ERROR(context, "Failed to destroy response (code: %d).", rc2);
349  rest_req->http_res = NULL;
350  create_error:
351  free(body);
352  return rc;
353 }
354 
355 #ifdef HPD_REST_ORIGIN
356 static hpd_error_t rest_reply_options(hpd_rest_req_t *rest_req)
357 {
358  hpd_error_t rc, rc2;
359  hpd_httpd_request_t *http_req = rest_req->http_req;
360  const hpd_module_t *context = rest_req->rest->context;
361 
362  if (rest_req->http_res) HPD_LOG_RETURN(context, HPD_E_STATE, "Response already sent.");
363 
364  // Construct methods list
365  char methods[23];
366  methods[0] = '\0';
367  if (strcmp(rest_req->url, "/devices") == 0) {
368  strcat(methods, "GET");
369  } else {
370  hpd_action_t *action;
371  hpd_service_foreach_action(rc, action, rest_req->service) {
372  hpd_method_t method;
373  if ((rc = hpd_action_get_method(action, &method))) {
374  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
375  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
376  }
377  return rc;
378  }
379  switch (method) {
380  case HPD_M_GET:
381  if (strlen(methods) > 0) strcat(methods, ", ");
382  strcat(methods, "GET");
383  break;
384  case HPD_M_PUT:
385  if (strlen(methods) > 0) strcat(methods, ", ");
386  strcat(methods, "PUT");
387  break;
388  case HPD_M_NONE:
389  case HPD_M_COUNT:
390  HPD_LOG_ERROR(context, "Unexpected method.");
391  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
392  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
393  }
394  return rc;
395  }
396  }
397  switch (rc) {
398  case HPD_E_SUCCESS:
399  break;
400  case HPD_E_NOT_FOUND:
401  if ((rc2 = rest_reply_not_found(http_req, rest_req, context))) {
402  HPD_LOG_ERROR(context, "Failed to send not found response (code: %d).", rc2);
403  return rc2;
404  }
405  return HPD_E_SUCCESS;
406  default:
407  HPD_LOG_ERROR(context, "Failed to loop over actions (code: %d).", rc);
408  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
409  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
410  }
411  return rc;
412  }
413  }
414 
415  if ((rc = hpd_httpd_response_create(&rest_req->http_res, http_req, HPD_S_200))) goto create_error;
416  if ((rc = hpd_httpd_response_add_header(rest_req->http_res, "Access-Control-Allow-Origin", "*"))) goto response_error;
417  if ((rc = hpd_httpd_response_add_header(rest_req->http_res, "Access-Control-Allow-Methods", methods))) goto response_error;
418  if ((rc = hpd_httpd_response_add_header(rest_req->http_res, "Access-Control-Allow-Headers", "Content-Type, Cache-Control, Accept, X-Requested-With"))) goto response_error;
419  if ((rc = hpd_httpd_response_sendf(rest_req->http_res, "OK"))) goto response_error;
420  if ((rc = hpd_httpd_response_destroy(rest_req->http_res))) goto create_error;
421  return HPD_E_SUCCESS;
422 
423  response_error:
424  if ((rc2 = hpd_httpd_response_destroy(rest_req->http_res)))
425  HPD_LOG_ERROR(context, "Failed to destroy response (code: %d).", rc2);
426  rest_req->http_res = NULL;
427  create_error:
428  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context)))
429  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
430  return rc;
431 }
432 #endif
433 
434 static void rest_on_free(void *data)
435 {
436  hpd_error_t rc;
437  hpd_rest_req_t *rest_req = data;
438 
439  if (rest_req->http_req) {
440  if (!rest_req->http_res) {
441  if ((rc = rest_reply_internal_server_error(rest_req->http_req, rest_req, rest_req->rest->context)))
442  HPD_LOG_ERROR(rest_req->rest->context, "Reply failed [code: %i]", rc);
443  }
444  rest_req->hpd_request = NULL;
445  } else {
446  if ((rc = hpd_service_id_free(rest_req->service)))
447  HPD_LOG_ERROR(rest_req->rest->context, "Free function failed [code: %i]", rc);
448  free(rest_req->body);
449  free(rest_req);
450  }
451 }
452 
454 {
455  hpd_error_t rc;
456  hpd_rest_req_t *rest_req = *req_data;
457 
458  if (!rest_req) return HPD_HTTPD_R_CONTINUE;
459 
460  if (rest_req->hpd_request) {
461  rest_req->http_req = NULL;
462  return HPD_HTTPD_R_CONTINUE;
463  } else {
464  if (rest_req->service && (rc = hpd_service_id_free(rest_req->service)))
465  HPD_LOG_ERROR(rest_req->rest->context, "Failed to free service id.");
466  free(rest_req->body);
467  free(rest_req);
469  }
470 }
471 
472 static void rest_on_response(void *data, const hpd_response_t *res)
473 {
474  hpd_error_t rc, rc2;
475 
476  // Get rest request
477  hpd_rest_req_t *rest_req = data;
478 
479  // Make life easier
480  hpd_httpd_request_t *http_req = rest_req->http_req;
481  const hpd_module_t *context = rest_req->rest->context;
482 
483  // Check if closed from downstairs
484  if (!http_req) return;
485 
486  // Get data from hpd
487  // TODO Need to send value into xml/json to get headers too !!!
488  const hpd_value_t *value;
489  const char *val;
490  size_t len;
491  hpd_status_t status;
492  if ((rc = hpd_response_get_value(res, &value)) ||
493  (rc = hpd_value_get_body(value, &val, &len)) ||
494  (rc = hpd_response_get_status(res, &status))) {
495  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
496  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
497  }
498  HPD_LOG_ERROR(context, "Failed to get data (code: %d).", rc);
499  return;
500  }
501 
502  // Check val from hpd
503  if (!val) {
504  if ((rc2 = rest_reply(http_req, status, rest_req, context))) {
505  HPD_LOG_ERROR(context, "Failed to send status response (code: %d).", rc2);
506  }
507  HPD_LOG_WARN(context, "No value in response.");
508  return ;
509  }
510 
511  // Get data from httpd
512  const char *accept;
513  rest_content_type_t accept_type;
514  switch ((rc = hpd_map_get(rest_req->headers, "Accept", &accept))) {
515  case HPD_E_SUCCESS:
516  break;
517  case HPD_E_NOT_FOUND:
518  accept = NULL;
519  break;
520  default:
521  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
522  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
523  }
524  HPD_LOG_ERROR(context, "Map error (code: %d).", rc);
525  return;
526  }
527  switch ((accept_type = rest_media_type_to_enum(accept))) {
528  case CONTENT_NONE:
529  case CONTENT_XML:
530  case CONTENT_JSON:
531  case CONTENT_WILDCARD:
532  break;
533  case CONTENT_UNKNOWN:
534  if ((rc2 = rest_reply_unsupported_media_type(http_req, rest_req, context))) {
535  HPD_LOG_ERROR(context, "Failed to send unsupport1ed media type response (code: %d).", rc2);
536  }
537  return;
538  }
539 
540  // Null terminate val
541  char *body = NULL;
542  body = malloc((len+1) * sizeof(char));
543  if (!body) {
544  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
545  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
546  }
547  HPD_LOG_ERROR(context, "Failed to allocate memory.");
548  return;
549  }
550  strncpy(body, val, len);
551  body[len] = '\0';
552 
553  if (rest_req->http_res) {
554  HPD_LOG_ERROR(context, "Response already sent.");
555  rc = HPD_E_STATE;
556  goto error_free_body;
557  }
558 
559  // Create response
560  if ((rc = hpd_httpd_response_create(&rest_req->http_res, http_req, status))) goto error_send_res;
561  hpd_httpd_response_t *http_res = rest_req->http_res;
562  char *state = NULL;
563  switch (accept_type) {
564  case CONTENT_NONE:
565  case CONTENT_XML:
566  case CONTENT_WILDCARD:
567  if ((rc = hpd_rest_xml_get_value(body, context, &state))) goto error_free_res;
568  if ((rc = hpd_httpd_response_add_header(http_res, "Content-Type", "application/xml"))) goto error_free_state;
569  break;
570  case CONTENT_JSON:
571  if ((rc = hpd_rest_json_get_value(body, context, &state))) goto error_free_res;
572  if ((rc = hpd_httpd_response_add_header(http_res, "Content-Type", "application/json"))) goto error_free_state;
573  break;
574  case CONTENT_UNKNOWN:
575  HPD_LOG_ERROR(context, "Should definitely not be here.");
576  goto error_free_res;
577  }
578 #ifdef HPD_REST_ORIGIN
579  if ((rc = hpd_httpd_response_add_header(http_res, "Access-Control-Allow-Origin", "*"))) goto error_free_state;
580 #endif
581  if ((rc = hpd_httpd_response_sendf(http_res, "%s", state))) goto error_free_state;
582  rc = hpd_httpd_response_destroy(http_res);
583 
584  // Clean up
585  free(state);
586  free(body);
587 
588  if (rc) HPD_LOG_ERROR(context, "Failed to destroy httpd response [code: %i].", rc);
589 
590  return;
591 
592  error_free_state:
593  free(state);
594  error_free_res:
595  if ((rc2 = hpd_httpd_response_destroy(http_res))) {
596  HPD_LOG_ERROR(context, "Failed to destroy response (code: %d).", rc2);
597  }
598  error_send_res:
599  rest_req->http_res = NULL;
600  if ((rc2 = rest_reply_internal_server_error(http_req, rest_req, context))) {
601  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
602  }
603  error_free_body:
604  free(body);
605  HPD_LOG_ERROR(context, "on_response failed (code: %i).", rc);
606  return;
607 }
608 
609 static hpd_httpd_return_t rest_on_req_begin(hpd_httpd_t *httpd, hpd_httpd_request_t *req, void* httpd_ctx, void** req_data)
610 {
611  hpd_error_t rc;
612  struct hpd_rest *rest = httpd_ctx;
613  const hpd_module_t *context = rest->context;
614 
615  hpd_rest_req_t *rest_req;
616  HPD_CALLOC(rest_req, 1, hpd_rest_req_t);
617  rest_req->rest = rest;
618  rest_req->http_req = req;
619  *req_data = rest_req;
620 
621  return HPD_HTTPD_R_CONTINUE;
622 
623  alloc_error:
624  if ((rc = rest_reply_internal_server_error(req, NULL, rest->context))) {
625  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc);
626  };
627  return HPD_HTTPD_R_STOP;
628 }
629 
631 {
632  hpd_error_t rc, rc2;
633  hpd_rest_req_t *rest_req = *req_data;
634  struct hpd_rest *rest = httpd_ctx;
635  const hpd_module_t *context = rest->context;
636 
637  // Get url
638  if ((rc = hpd_httpd_request_get_url(req, &rest_req->url))) {
639  HPD_LOG_ERROR(context, "Failed to get url (code: %d).", rc);
640  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
641  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
642  }
643  return HPD_HTTPD_R_STOP;
644  }
645  if (!rest_req->url) {
646  if ((rc2 = rest_reply_not_found(req, rest_req, context))) {
647  HPD_LOG_ERROR(context, "Failed to send not found response (code: %d).", rc2);
648  }
649  return HPD_HTTPD_R_STOP;
650  }
651 
652  // If /devices its okay
653  if (strcmp(rest_req->url, "/devices") == 0) return HPD_HTTPD_R_CONTINUE;
654 
655  // Get components
656  char *aid_encoded, *did_encoded, *sid_encoded;
657  switch ((rc = rest_url_extract(rest, rest_req->url, &aid_encoded, &did_encoded, &sid_encoded))) {
658  case HPD_E_ALLOC:
659  HPD_LOG_ERROR(context, "Unable to allocate memory.");
660  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
661  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
662  }
663  return HPD_HTTPD_R_STOP;
664  case HPD_E_ARGUMENT:
665  if ((rc2 = rest_reply_not_found(req, rest_req, context))) {
666  HPD_LOG_ERROR(context, "Failed to send not found response (code: %d).", rc2);
667  }
668  return HPD_HTTPD_R_STOP;
669  case HPD_E_SUCCESS:
670  break;
671  default:
672  HPD_LOG_ERROR(context, "Unexpected error (code: %d).", rc);
673  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
674  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
675  }
676  return HPD_HTTPD_R_STOP;
677  }
678 
679  // Decode IDs
680  char *aid_decoded, *did_decoded, *sid_decoded;
681  if ((rc = rest_url_decode(rest, aid_encoded, &aid_decoded))) {
682  HPD_LOG_ERROR(context, "Failed to decode id (code: %d).", rc);
683  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
684  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
685  }
686  return HPD_HTTPD_R_STOP;
687  }
688  if ((rc = rest_url_decode(rest, did_encoded, &did_decoded))) {
689  HPD_LOG_ERROR(context, "Failed to decode id (code: %d).", rc);
690  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
691  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
692  }
693  return HPD_HTTPD_R_STOP;
694  }
695  if ((rc = rest_url_decode(rest, sid_encoded, &sid_decoded))) {
696  HPD_LOG_ERROR(context, "Failed to decode id (code: %d).", rc);
697  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
698  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
699  }
700  return HPD_HTTPD_R_STOP;
701  }
702  free(aid_encoded);
703  free(did_encoded);
704  free(sid_encoded);
705 
706  // Create service ID
707  if ((rc = hpd_service_id_alloc(&rest_req->service, rest->hpd, aid_decoded, did_decoded, sid_decoded))) {
708  HPD_LOG_ERROR(context, "Failed to allocate id (code: %d).", rc);
709  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
710  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
711  }
712  return HPD_HTTPD_R_STOP;
713  }
714  free(aid_decoded);
715  free(did_decoded);
716  free(sid_decoded);
717 
718  return HPD_HTTPD_R_CONTINUE;
719 }
720 
722 {
723  switch(method)
724  {
725  case HPD_HTTPD_M_GET:
726  return HPD_M_GET;
727  case HPD_HTTPD_M_PUT:
728  return HPD_M_PUT;
729  case HPD_HTTPD_M_OPTIONS:
730  case HPD_HTTPD_M_UNKNOWN:
731  default:
732  return HPD_M_NONE;
733  }
734 }
735 
737 {
738  hpd_error_t rc, rc2;
739  hpd_rest_req_t *rest_req = *req_data;
740  struct hpd_rest *rest = httpd_ctx;
741  const hpd_module_t *context = rest->context;
742 
743  // Get headers for later
744  if ((rc = hpd_httpd_request_get_headers(req, &rest_req->headers))) {
745  HPD_LOG_ERROR(context, "Failed to get headers (code: %d).", rc);
746  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
747  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
748  }
749  return HPD_HTTPD_R_STOP;
750  }
751 
752  // Get method
753  if ((rc = hpd_httpd_request_get_method(req, &rest_req->http_method))) {
754  HPD_LOG_ERROR(context, "Failed to get method (code: %d).", rc);
755  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
756  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
757  }
758  return HPD_HTTPD_R_STOP;
759  }
760  rest_req->hpd_method = rest_method_to_method(rest_req->http_method);
761 
762  // Act on method
763  switch (rest_req->http_method) {
764  case HPD_HTTPD_M_GET:
765  case HPD_HTTPD_M_PUT: {
766  if (strcmp(rest_req->url, "/devices") == 0) {
767  if ((rc = rest_reply_devices(rest_req))) {
768  HPD_LOG_ERROR(context, "Failed to reply with devices list (code: %d).", rc);
769  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
770  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
771  }
772  }
773  return HPD_HTTPD_R_STOP;
774  }
775 
776  hpd_bool_t found;
777  switch (hpd_service_has_action(rest_req->service, rest_req->hpd_method, &found)) {
778  case HPD_E_SUCCESS:
779  break;
780  case HPD_E_NOT_FOUND:
781  if ((rc2 = rest_reply_not_found(req, rest_req, context))) {
782  HPD_LOG_ERROR(context, "Failed to send not found response (code: %d).", rc2);
783  }
784  return HPD_HTTPD_R_STOP;
785  default:
786  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
787  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
788  }
789  return HPD_HTTPD_R_STOP;
790  };
791  if (!found) {
792  if ((rc2 = rest_reply_method_not_allowed(req, rest_req, context))) {
793  HPD_LOG_ERROR(context, "Failed to send method not allowed response (code: %d).", rc2);
794  }
795  return HPD_HTTPD_R_STOP;
796  }
797  return HPD_HTTPD_R_CONTINUE;
798  }
799 #ifdef HPD_REST_ORIGIN
800  case HPD_HTTPD_M_OPTIONS: {
801  if ((rc = rest_reply_options(rest_req))) {
802  HPD_LOG_ERROR(context, "Failed to reply with devices list (code: %d).", rc);
803  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
804  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
805  }
806  }
807  return HPD_HTTPD_R_STOP;
808  }
809 #endif
810  case HPD_HTTPD_M_UNKNOWN:
811  default:
812  if ((rc2 = rest_reply_method_not_allowed(req, rest_req, context))) {
813  HPD_LOG_ERROR(context, "Failed to send method not allowed response (code: %d).", rc2);
814  }
815  return HPD_HTTPD_R_STOP;
816  }
817 }
818 
819 static hpd_httpd_return_t rest_on_req_body(hpd_httpd_t *ins, hpd_httpd_request_t *req, void* httpd_ctx, void** req_data, const char* chunk, size_t len)
820 {
821  hpd_error_t rc2;
822  hpd_rest_req_t *rest_req = *req_data;
823  const hpd_module_t *context = rest_req->rest->context;
824 
825  HPD_REALLOC(rest_req->body, rest_req->len + len, char);
826  strncpy(&rest_req->body[rest_req->len], chunk, len);
827  rest_req->len += len;
828  return HPD_HTTPD_R_CONTINUE;
829 
830  alloc_error:
831  HPD_LOG_ERROR(context, "Unable to allocate memory.");
832  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
833  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
834  }
835  return HPD_HTTPD_R_STOP;
836 }
837 
839 {
840  hpd_error_t rc, rc2;
841  hpd_rest_req_t *rest_req = *req_data;
842  const hpd_module_t *context = rest_req->rest->context;
843  hpd_service_id_t *service = rest_req->service;
844 
845  // Construct value
846  hpd_value_t *value = NULL;
847  if (rest_req->body) {
848  // Get content type
849  const char *content_type;
850  switch ((rc = hpd_map_get(rest_req->headers, "Content-Type", &content_type))) {
851  case HPD_E_SUCCESS:
852  break;
853  case HPD_E_NOT_FOUND:
854  content_type = NULL;
855  break;
856  default:
857  HPD_LOG_ERROR(context, "Failed to get content-type (code: %d).", rc);
858  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
859  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
860  }
861  return HPD_HTTPD_R_STOP;
862  }
863 
864  // Construct actual value
865  char *val = NULL;
866  switch (rest_media_type_to_enum(content_type)) {
867  case CONTENT_XML:
868  switch ((rc = hpd_rest_xml_parse_value(rest_req->body, context, &val))) {
869  case HPD_E_SUCCESS:
870  break;
871  case HPD_E_ARGUMENT:
872  if ((rc2 = rest_reply_bad_request(req, rest_req, context))) {
873  HPD_LOG_ERROR(context, "Failed to send bad request response (code: %d).", rc2);
874  }
875  return HPD_HTTPD_R_STOP;
876  default:
877  HPD_LOG_ERROR(context, "Failed to parse value (code: %d).", rc);
878  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
879  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
880  }
881  return HPD_HTTPD_R_STOP;
882  }
883  break;
884  case CONTENT_JSON:
885  switch ((rc = hpd_rest_json_parse_value(rest_req->body, context, &val))) {
886  case HPD_E_SUCCESS:
887  break;
888  case HPD_E_ARGUMENT:
889  if ((rc2 = rest_reply_bad_request(req, rest_req, context))) {
890  HPD_LOG_ERROR(context, "Failed to send bad request response (code: %d).", rc2);
891  }
892  return HPD_HTTPD_R_STOP;
893  default:
894  HPD_LOG_ERROR(context, "Failed to parse value (code: %d).", rc);
895  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
896  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
897  }
898  return HPD_HTTPD_R_STOP;
899  }
900  break;
901  case CONTENT_UNKNOWN:
902  case CONTENT_NONE:
903  case CONTENT_WILDCARD:
904  if ((rc2 = rest_reply_unsupported_media_type(req, rest_req, context))) {
905  HPD_LOG_ERROR(context, "Failed to send unsupported media type response (code: %d).", rc2);
906  }
907  return HPD_HTTPD_R_STOP;
908  }
909 
910  // Allocate hpd value
911  if ((rc = hpd_value_alloc(&value, val, HPD_NULL_TERMINATED))) {
912  free(val);
913  HPD_LOG_ERROR(context, "Unable to allocate value (code: %d).", rc);
914  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
915  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
916  }
917  return HPD_HTTPD_R_STOP;
918  }
919  free(val);
920  }
921 
922  // Send hpd request
923  if ((rc = hpd_request_alloc(&rest_req->hpd_request, service, rest_req->hpd_method, rest_on_response))) {
924  HPD_LOG_ERROR(context, "Unable to allocate request (code: %d).", rc);
925  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
926  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
927  }
928  return HPD_HTTPD_R_STOP;
929  }
930  if (value && (rc = hpd_request_set_value(rest_req->hpd_request, value))) {
931  HPD_LOG_ERROR(context, "Unable to set value (code: %d).", rc);
932  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
933  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
934  }
935  if ((rc2 = hpd_request_free(rest_req->hpd_request))) {
936  HPD_LOG_ERROR(context, "Failed to free request (code: %d).", rc2);
937  }
938  return HPD_HTTPD_R_STOP;
939  }
940  if ((rc = hpd_request_set_data(rest_req->hpd_request, rest_req, rest_on_free))) {
941  HPD_LOG_ERROR(context, "Unable to set data (code: %d).", rc);
942  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
943  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
944  }
945  if ((rc2 = hpd_request_free(rest_req->hpd_request))) {
946  HPD_LOG_ERROR(context, "Failed to free request (code: %d).", rc2);
947  }
948  return HPD_HTTPD_R_STOP;
949  }
950  if ((rc = hpd_request(rest_req->hpd_request))) {
951  HPD_LOG_ERROR(context, "Unable to send request (code: %d).", rc);
952  if ((rc2 = rest_reply_internal_server_error(req, rest_req, context))) {
953  HPD_LOG_ERROR(context, "Failed to send internal server error response (code: %d).", rc2);
954  }
955  if ((rc2 = hpd_request_free(rest_req->hpd_request))) {
956  HPD_LOG_ERROR(context, "Failed to free request (code: %d).", rc2);
957  }
958  return HPD_HTTPD_R_STOP;
959  }
960 
961  if ((rc = hpd_httpd_request_keep_open(rest_req->http_req))) {
962  HPD_LOG_WARN(context, "Failed to keep the connection open, hoping for the best... (code: %d).", rc);
963  }
964  return HPD_HTTPD_R_CONTINUE;
965 }
966 
967 static hpd_error_t rest_on_create(void **data, const hpd_module_t *context)
968 {
969  hpd_error_t rc;
970 
971  if ((rc = hpd_module_add_option(context, "port", "port", 0, "Listener port for rest server."))) return rc;
972 
977  ws_set.on_req_body = rest_on_req_body;
978  ws_set.on_req_cmpl = rest_on_req_cmpl;
980 
981  hpd_rest_t *rest;
982  HPD_CALLOC(rest, 1, hpd_rest_t);
983  rest->ws_set = ws_set;
984  rest->ws_set.httpd_ctx = rest;
985  rest->context = context;
986 
987  rest->curl = curl_easy_init();
988  if (!rest->curl) {
989  rest_on_destroy(rest);
990  HPD_LOG_RETURN(rest->context, HPD_E_UNKNOWN, "Could not initialise curl.");
991  }
992 
993  (*data) = rest;
994  return HPD_E_SUCCESS;
995 
996  alloc_error:
997  HPD_LOG_RETURN_E_ALLOC(context);
998 }
999 
1001 {
1002  hpd_rest_t *rest = data;
1003  if (rest) {
1004  if (rest->curl) curl_easy_cleanup(rest->curl);
1005  free(rest);
1006  }
1007  return HPD_E_SUCCESS;
1008 }
1009 
1011 {
1012  hpd_error_t rc, rc2;
1013  hpd_rest_t *rest = data;
1014  const hpd_module_t *context = rest->context;
1015 
1016  HPD_LOG_INFO(context, "Starting REST server on port %d.", rest->ws_set.port);
1017 
1018  rest->hpd = hpd;
1019 
1021  if ((rc = hpd_get_loop(hpd, &loop))) return rc;
1022 
1023  if ((rc = hpd_httpd_create(&rest->ws, &rest->ws_set, context, loop))) return rc;
1024  if ((rc = hpd_httpd_start(rest->ws))) {
1025  if ((rc2 = hpd_httpd_destroy(rest->ws))) {
1026  HPD_LOG_ERROR(context, "Failed to destroy httpd (code: %d).", rc2);
1027  }
1028  return rc;
1029  }
1030 
1031  return HPD_E_SUCCESS;
1032 }
1033 
1035 {
1036  hpd_error_t rc, rc2;
1037  hpd_rest_t *rest = data;
1038  const hpd_module_t *context = rest->context;
1039 
1040  HPD_LOG_INFO(rest->context, "Stopping REST server.");
1041 
1042  rc = hpd_httpd_stop(rest->ws);
1043  if ((rc2 = hpd_httpd_destroy(rest->ws))) {
1044  if (rc) HPD_LOG_ERROR(context, "Failed to destroy httpd (code: %d).", rc2);
1045  else rc = rc2;
1046  }
1047 
1048  rest->ws = NULL;
1049  rest->hpd = NULL;
1050 
1051  return rc;
1052 }
1053 
1054 static hpd_error_t rest_on_parse_opt(void *data, const char *name, const char *arg)
1055 {
1056  hpd_rest_t *rest = data;
1057 
1058  if (strcmp(name, "port") == 0) {
1059  hpd_tcpd_port_t port = (hpd_tcpd_port_t) atoi(arg);
1060  if (port <= HPD_TCPD_P_SYSTEM_PORTS_START || port > HPD_TCPD_P_DYNAMIC_PORTS_END) return HPD_E_ARGUMENT;
1061  rest->ws_set.port = port;
1062  return HPD_E_SUCCESS;
1063  } else {
1064  return HPD_E_ARGUMENT;
1065  }
1066 }
hpd_error_t hpd_rest_json_get_configuration(hpd_t *hpd, hpd_rest_t *rest, const hpd_module_t *context, char **out)
Definition: rest_json.c:368
enum hpd_status hpd_status_t
Definition: hpd_types.h:168
static hpd_httpd_return_t rest_on_req_hdr_cmpl(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
Definition: rest.c:736
enum hpd_tcpd_port hpd_tcpd_port_t
hpd_error_t hpd_request(hpd_request_t *request)
Definition: request_api.c:62
hpd_httpd_method_t http_method
Definition: rest.c:66
hpd_error_t hpd_rest_json_get_value(char *value, const hpd_module_t *context, char **out)
Definition: rest_json.c:390
static hpd_httpd_return_t rest_on_req_url_cmpl(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
Definition: rest.c:630
hpd_error_t hpd_httpd_response_create(hpd_httpd_response_t **response, hpd_httpd_request_t *req, hpd_status_t status)
Create the reponse and constructs the status line.
#define HPD_LOG_RETURN(CONTEXT, E, FMT,...)
static hpd_error_t rest_reply_internal_server_error(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:270
static hpd_error_t rest_reply_unsupported_media_type(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:280
static hpd_error_t rest_url_encode(hpd_rest_t *rest, const char *decoded, char **encoded)
Definition: rest.c:100
size_t len
Definition: rest.c:70
static hpd_error_t rest_on_destroy(void *data)
Definition: rest.c:1000
hpd_httpd_request_t * http_req
Definition: rest.c:65
hpd_error_t hpd_service_get_adapter(const hpd_service_id_t *sid, hpd_adapter_id_t **aid)
static hpd_error_t rest_reply_not_found(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:275
hpd_error_t hpd_service_id_alloc(hpd_service_id_t **id, hpd_t *hpd, const char *aid, const char *did, const char *sid)
Definition: discovery_api.c:70
hpd_error_t hpd_httpd_destroy(hpd_httpd_t *httpd)
Destroy a httpd instance.
Definition: httpd.c:178
hpd_error_t hpd_value_alloc(hpd_value_t **value, const char *body, int len)
[Browsing foreach loops]
Definition: value_api.c:33
data value
hpd_error_t hpd_httpd_response_sendf(hpd_httpd_response_t *res, const char *fmt,...)
Send response to client.
hpd_error_t hpd_module_add_option(const hpd_module_t *context, const char *name, const char *arg, int flags, const char *doc)
[hpd_t functions]
Definition: daemon_api.c:65
static hpd_error_t rest_url_extract(hpd_rest_t *rest, const char *url, char **aid, char **did, char **sid)
Definition: rest.c:186
struct hpd_rest_req hpd_rest_req_t
rest_content_type
Definition: rest.c:45
static hpd_httpd_return_t rest_on_req_cmpl(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
Definition: rest.c:838
hpd_error_t hpd_value_get_body(const hpd_value_t *value, const char **body, size_t *len)
Definition: value_api.c:87
hpd_error_t hpd_httpd_request_get_url(hpd_httpd_request_t *req, const char **url)
Get the URL of this request.
static hpd_error_t rest_reply(hpd_httpd_request_t *req, enum hpd_status status, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:234
static hpd_httpd_return_t rest_on_req_body(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data, const char *chunk, size_t len)
Definition: rest.c:819
hpd_error_t hpd_request_free(hpd_request_t *request)
Definition: request_api.c:44
char * url
free(data.url)
hpd_map_t * headers
Definition: rest.c:68
static hpd_error_t rest_reply_devices(hpd_rest_req_t *rest_req)
Definition: rest.c:295
#define HPD_STR_N_CPY(DST, SRC, LEN)
Definition: hpd_common.h:69
hpd_method_t hpd_method
Definition: rest.c:73
static hpd_httpd_return_t rest_on_req_destroy(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *ws_ctx, void **req_data)
Definition: rest.c:453
hpd_tcpd_port_t port
Definition: hpd_httpd.h:101
#define HPD_LOG_INFO(CONTEXT, FMT,...)
const hpd_module_t * context
Definition: rest.c:57
An http request.
hpd_error_t hpd_rest_get_timestamp(const hpd_module_t *context, char *str)
Definition: rest.c:115
#define HPD_CALLOC(PTR, NUM, CAST)
Allocates and zeros a structure.
Definition: hpd_common.h:44
#define HPD_HTTP_STATUS_CODE_MAP(XX)
[hpd_method_t]
Definition: hpd_types.h:105
hpd_error_t hpd_adapter_get_id(const hpd_adapter_id_t *aid, const char **id)
[id_t functions]
#define HPD_NULL_TERMINATED
Value to be used in len parameters on \0 terminated strings.
Definition: hpd_types.h:62
hpd_error_t hpd_request_set_value(hpd_request_t *request, hpd_value_t *value)
Definition: request_api.c:50
char * body
Definition: rest.c:69
hpd_error_t hpd_httpd_request_keep_open(hpd_httpd_request_t *req)
Keep the connection for a request open.
static rest_content_type_t rest_media_type_to_enum(const char *haystack)
Definition: rest.c:81
hpd_error_t hpd_device_get_id(const hpd_device_id_t *did, const char **id)
[hpd_adapter_t functions]
static hpd_method_t rest_method_to_method(hpd_httpd_method_t method)
Definition: rest.c:721
hpd_httpd_nodata_f on_req_url_cmpl
Definition: hpd_httpd.h:106
hpd_httpd_response_t * http_res
Definition: rest.c:78
hpd_error_t hpd_request_alloc(hpd_request_t **request, const hpd_service_id_t *id, hpd_method_t method, hpd_response_f on_response)
[hpd_request_t functions]
Definition: request_api.c:35
Definition: comm.h:70
#define hpd_service_foreach_action(RC, ACTION, ID)
[hpd_parameter_t functions]
hpd_error_t hpd_service_get_id(const hpd_service_id_t *sid, const char **id)
[hpd_device_t functions]
hpd_httpd_t * ws
Definition: rest.c:54
hpd_error_t hpd_service_has_action(const hpd_service_id_t *id, const hpd_method_t method, hpd_bool_t *boolean)
hpd_service_id_t * service
Definition: rest.c:74
#define XX(NUM, STR)
hpd_error_t hpd_httpd_request_get_headers(hpd_httpd_request_t *req, hpd_map_t **headers)
Get a linked map of all headers for a request.
enum hpd_error hpd_error_t
Definition: hpd_types.h:167
#define HPD_LOG_RETURN_E_ALLOC(CONTEXT)
hpd_httpd_nodata_f on_req_begin
Definition: hpd_httpd.h:104
static void rest_on_response(void *data, const hpd_response_t *res)
Definition: rest.c:472
[Application API Callbacks]
Definition: hpd_types.h:200
hpd_error_t hpd_httpd_request_get_method(hpd_httpd_request_t *req, hpd_httpd_method_t *method)
Get the method of the http request.
static hpd_error_t rest_on_create(void **data, const hpd_module_t *context)
Definition: rest.c:967
hpd_error_t hpd_response_get_status(const hpd_response_t *response, hpd_status_t *status)
[hpd_request_t functions]
Definition: request_api.c:157
hpd_error_t hpd_device_id_free(hpd_device_id_t *id)
Definition: discovery_api.c:64
hpd_status
Definition: hpd_types.h:148
hpd_httpd_nodata_f on_req_hdr_cmpl
Definition: hpd_httpd.h:109
static void rest_on_free(void *data)
Definition: rest.c:434
static hpd_error_t rest_reply_bad_request(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:285
hpd_error_t hpd_map_get(hpd_map_t *map, const char *k, const char **v)
Definition: map.c:92
char hpd_bool_t
Definition: hpd_types.h:35
hpd_error_t hpd_request_set_data(hpd_request_t *request, void *data, hpd_free_f on_free)
Definition: request_api.c:56
static hpd_error_t rest_on_stop(void *data, hpd_t *hpd)
Definition: rest.c:1034
data port
struct data data
Settings struct for webserver.
Definition: hpd_httpd.h:100
Definition: daemon.h:50
hpd_error_t hpd_httpd_response_destroy(hpd_httpd_response_t *res)
Destroy a hpd_httpd_response.
enum hpd_httpd_return hpd_httpd_return_t
static struct ev_loop * loop
hpd_error_t hpd_service_get_device(const hpd_service_id_t *sid, hpd_device_id_t **did)
hpd_request_t * hpd_request
Definition: rest.c:75
hpd_error_t hpd_httpd_create(hpd_httpd_t **httpd, hpd_httpd_settings_t *settings, const hpd_module_t *context, hpd_ev_loop_t *loop)
Create a new httpd instance.
Definition: httpd.c:135
hpd_error_t hpd_httpd_stop(hpd_httpd_t *httpd)
Stop a httpd instance.
Definition: httpd.c:211
hpd_httpd_settings_t ws_set
Definition: rest.c:55
Definition: rest.c:53
hpd_error_t hpd_httpd_start(hpd_httpd_t *httpd)
Start a httpd instance.
Definition: httpd.c:196
hpd_httpd_nodata_f on_req_destroy
Definition: hpd_httpd.h:112
hpd_httpd_nodata_f on_req_cmpl
Definition: hpd_httpd.h:111
httpd instance struct
Definition: httpd.c:38
state
The possible states of a request.
Definition: httpd_request.c:37
static hpd_error_t rest_reply_method_not_allowed(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
Definition: rest.c:290
hpd_t * hpd
Definition: rest.c:56
hpd_error_t hpd_rest_url_create(hpd_rest_t *rest, hpd_service_id_t *service, char **url)
Definition: rest.c:124
hpd_error_t hpd_httpd_response_add_header(hpd_httpd_response_t *res, const char *field, const char *value)
Add header to a response.
hpd_error_t hpd_action_get_method(const hpd_action_t *action, hpd_method_t *method)
[model foreach loops]
hpd_error_t hpd_adapter_id_free(hpd_adapter_id_t *id)
Definition: discovery_api.c:46
static hpd_httpd_return_t rest_on_req_begin(hpd_httpd_t *httpd, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
Definition: rest.c:609
CURL * curl
Definition: rest.c:58
hpd_error_t hpd_rest_xml_get_configuration(hpd_t *hpd, hpd_rest_t *rest, const hpd_module_t *context, char **out)
Definition: rest_xml.c:240
static hpd_error_t rest_on_start(void *data, hpd_t *hpd)
Definition: rest.c:1010
#define HPD_HTTPD_SETTINGS_DEFAULT
Default settings for httpd.
Definition: hpd_httpd.h:123
hpd_httpd_data_f on_req_body
Definition: hpd_httpd.h:110
static hpd_error_t rest_url_decode(hpd_rest_t *rest, const char *encoded, char **decoded)
Definition: rest.c:107
#define HPD_LOG_WARN(CONTEXT, FMT,...)
const char * url
Definition: rest.c:67
hpd_error_t hpd_rest_xml_get_value(char *value, const hpd_module_t *context, char **out)
Definition: rest_xml.c:286
enum hpd_httpd_method hpd_httpd_method_t
hpd_rest_t * rest
Definition: rest.c:62
hpd_error_t hpd_service_id_free(hpd_service_id_t *id)
Definition: discovery_api.c:82
enum rest_content_type rest_content_type_t
struct ev_loop hpd_ev_loop_t
Definition: hpd_types.h:51
#define HPD_REALLOC(PTR, NUM, CAST)
CAST is for c++ compatibility (tests).
Definition: hpd_common.h:52
enum hpd_method hpd_method_t
[hpd_log_level_t]
Definition: hpd_types.h:166
hpd_error_t hpd_rest_json_parse_value(const char *in, const hpd_module_t *context, char **out)
Definition: rest_json.c:418
hpd_error_t hpd_rest_xml_parse_value(const char *in, const hpd_module_t *context, char **out)
Definition: rest_xml.c:316
hpd_error_t hpd_response_get_value(const hpd_response_t *response, const hpd_value_t **value)
Definition: request_api.c:163
#define HPD_LOG_ERROR(CONTEXT, FMT,...)
hpd_error_t hpd_get_loop(hpd_t *hpd, hpd_ev_loop_t **loop)
Definition: daemon_api.c:44
struct hpd_map hpd_map_t
Definition: hpd_map.h:40
static char * req_data
static hpd_error_t rest_on_parse_opt(void *data, const char *name, const char *arg)
Definition: rest.c:1054
A http response.