33 #include <curl/curl.h>
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')))
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')))
95 if (strcmp(haystack,
"*/*") == 0)
102 (*encoded) = curl_easy_escape(rest->
curl, decoded, 0);
109 (*decoded) = curl_easy_unescape(rest->
curl, encoded, 0, NULL);
117 time_t now = time(NULL);
118 struct tm *tm = gmtime(&now);
120 strftime(str, 20,
"%FT%TZ", tm);
133 const char *adp_id, *dev_id, *srv_id;
149 char *aid = NULL, *did = NULL, *sid = NULL;
154 (*url) = malloc((strlen(aid)+strlen(did)+strlen(sid)+3+1)*
sizeof(
char));
188 const char *aid_p, *did_p, *sid_p;
189 size_t aid_l, did_l, sid_l;
197 if (aid_p[0] ==
'\0' || aid_p[0] ==
'/')
199 for (aid_l = 0; aid_p[aid_l] !=
'/'; aid_l ++)
200 if (aid_p[aid_l] ==
'\0')
203 did_p = &aid_p[aid_l+1];
204 if (did_p[0] ==
'\0' || did_p[0] ==
'/')
206 for (did_l = 0; did_p[did_l] !=
'/'; did_l ++)
207 if (did_p[did_l] ==
'\0')
210 sid_p = &did_p[did_l+1];
211 if (sid_p[0] ==
'\0' || sid_p[0] ==
'/')
213 for (sid_l = 0; sid_p[sid_l] !=
'/' && sid_p[sid_l] !=
'\0'; sid_l++);
215 if (!sid_p[sid_l] ==
'\0')
244 #define XX(NUM, STR) case HPD_S_##NUM: break;
248 if (context)
HPD_LOG_WARN(context,
"Unknown HTTP Status code.");
256 #define XX(NUM, STR) case HPD_S_##NUM: rc = hpd_httpd_response_sendf(res, #STR); break;
266 if (rest_req) rest_req->
http_res = res;
272 return rest_reply(req, HPD_S_500, rest_req, context);
277 return rest_reply(req, HPD_S_404, rest_req, context);
282 return rest_reply(req, HPD_S_415, rest_req, context);
287 return rest_reply(req, HPD_S_400, rest_req, context);
292 return rest_reply(req, HPD_S_405, rest_req, context);
314 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
332 HPD_LOG_ERROR(context,
"Failed to send unsupported media type response (code: %d).", rc);
348 HPD_LOG_ERROR(context,
"Failed to destroy response (code: %d).", rc2);
355 #ifdef HPD_REST_ORIGIN
367 if (strcmp(rest_req->
url,
"/devices") == 0) {
368 strcat(methods,
"GET");
375 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
381 if (strlen(methods) > 0) strcat(methods,
", ");
382 strcat(methods,
"GET");
385 if (strlen(methods) > 0) strcat(methods,
", ");
386 strcat(methods,
"PUT");
392 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
402 HPD_LOG_ERROR(context,
"Failed to send not found response (code: %d).", rc2);
407 HPD_LOG_ERROR(context,
"Failed to loop over actions (code: %d).", rc);
409 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
425 HPD_LOG_ERROR(context,
"Failed to destroy response (code: %d).", rc2);
429 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
484 if (!http_req)
return;
496 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
498 HPD_LOG_ERROR(context,
"Failed to get data (code: %d).", rc);
504 if ((rc2 =
rest_reply(http_req, status, rest_req, context))) {
505 HPD_LOG_ERROR(context,
"Failed to send status response (code: %d).", rc2);
522 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
535 HPD_LOG_ERROR(context,
"Failed to send unsupport1ed media type response (code: %d).", rc2);
542 body = malloc((len+1) *
sizeof(
char));
545 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
550 strncpy(body, val, len);
556 goto error_free_body;
563 switch (accept_type) {
578 #ifdef HPD_REST_ORIGIN
588 if (rc)
HPD_LOG_ERROR(context,
"Failed to destroy httpd response [code: %i].", rc);
596 HPD_LOG_ERROR(context,
"Failed to destroy response (code: %d).", rc2);
601 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
605 HPD_LOG_ERROR(context,
"on_response failed (code: %i).", rc);
612 struct hpd_rest *rest = httpd_ctx;
617 rest_req->
rest = rest;
619 *req_data = rest_req;
625 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc);
634 struct hpd_rest *rest = httpd_ctx;
641 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
645 if (!rest_req->
url) {
647 HPD_LOG_ERROR(context,
"Failed to send not found response (code: %d).", rc2);
656 char *aid_encoded, *did_encoded, *sid_encoded;
657 switch ((rc =
rest_url_extract(rest, rest_req->
url, &aid_encoded, &did_encoded, &sid_encoded))) {
661 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
666 HPD_LOG_ERROR(context,
"Failed to send not found response (code: %d).", rc2);
674 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
680 char *aid_decoded, *did_decoded, *sid_decoded;
682 HPD_LOG_ERROR(context,
"Failed to decode id (code: %d).", rc);
684 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
689 HPD_LOG_ERROR(context,
"Failed to decode id (code: %d).", rc);
691 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
696 HPD_LOG_ERROR(context,
"Failed to decode id (code: %d).", rc);
698 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
708 HPD_LOG_ERROR(context,
"Failed to allocate id (code: %d).", rc);
710 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
740 struct hpd_rest *rest = httpd_ctx;
745 HPD_LOG_ERROR(context,
"Failed to get headers (code: %d).", rc);
747 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
754 HPD_LOG_ERROR(context,
"Failed to get method (code: %d).", rc);
756 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
766 if (strcmp(rest_req->
url,
"/devices") == 0) {
768 HPD_LOG_ERROR(context,
"Failed to reply with devices list (code: %d).", rc);
770 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
782 HPD_LOG_ERROR(context,
"Failed to send not found response (code: %d).", rc2);
787 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
793 HPD_LOG_ERROR(context,
"Failed to send method not allowed response (code: %d).", rc2);
799 #ifdef HPD_REST_ORIGIN
801 if ((rc = rest_reply_options(rest_req))) {
802 HPD_LOG_ERROR(context,
"Failed to reply with devices list (code: %d).", rc);
804 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
813 HPD_LOG_ERROR(context,
"Failed to send method not allowed response (code: %d).", rc2);
826 strncpy(&rest_req->
body[rest_req->
len], chunk, len);
827 rest_req->
len += len;
833 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
847 if (rest_req->
body) {
849 const char *content_type;
857 HPD_LOG_ERROR(context,
"Failed to get content-type (code: %d).", rc);
859 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
873 HPD_LOG_ERROR(context,
"Failed to send bad request response (code: %d).", rc2);
877 HPD_LOG_ERROR(context,
"Failed to parse value (code: %d).", rc);
879 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
890 HPD_LOG_ERROR(context,
"Failed to send bad request response (code: %d).", rc2);
894 HPD_LOG_ERROR(context,
"Failed to parse value (code: %d).", rc);
896 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
905 HPD_LOG_ERROR(context,
"Failed to send unsupported media type response (code: %d).", rc2);
913 HPD_LOG_ERROR(context,
"Unable to allocate value (code: %d).", rc);
915 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
924 HPD_LOG_ERROR(context,
"Unable to allocate request (code: %d).", rc);
926 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
931 HPD_LOG_ERROR(context,
"Unable to set value (code: %d).", rc);
933 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
936 HPD_LOG_ERROR(context,
"Failed to free request (code: %d).", rc2);
941 HPD_LOG_ERROR(context,
"Unable to set data (code: %d).", rc);
943 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
946 HPD_LOG_ERROR(context,
"Failed to free request (code: %d).", rc2);
951 HPD_LOG_ERROR(context,
"Unable to send request (code: %d).", rc);
953 HPD_LOG_ERROR(context,
"Failed to send internal server error response (code: %d).", rc2);
956 HPD_LOG_ERROR(context,
"Failed to free request (code: %d).", rc2);
962 HPD_LOG_WARN(context,
"Failed to keep the connection open, hoping for the best... (code: %d).", rc);
971 if ((rc =
hpd_module_add_option(context,
"port",
"port", 0,
"Listener port for rest server.")))
return rc;
987 rest->
curl = curl_easy_init();
1004 if (rest->
curl) curl_easy_cleanup(rest->
curl);
1026 HPD_LOG_ERROR(context,
"Failed to destroy httpd (code: %d).", rc2);
1044 if (rc)
HPD_LOG_ERROR(context,
"Failed to destroy httpd (code: %d).", rc2);
1058 if (strcmp(name,
"port") == 0) {
1060 if (port <= HPD_TCPD_P_SYSTEM_PORTS_START || port > HPD_TCPD_P_DYNAMIC_PORTS_END)
return HPD_E_ARGUMENT;
hpd_error_t hpd_rest_json_get_configuration(hpd_t *hpd, hpd_rest_t *rest, const hpd_module_t *context, char **out)
enum hpd_status hpd_status_t
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)
enum hpd_tcpd_port hpd_tcpd_port_t
hpd_error_t hpd_request(hpd_request_t *request)
hpd_httpd_method_t http_method
hpd_error_t hpd_rest_json_get_value(char *value, const hpd_module_t *context, char **out)
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)
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)
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)
static hpd_error_t rest_url_encode(hpd_rest_t *rest, const char *decoded, char **encoded)
static hpd_error_t rest_on_destroy(void *data)
hpd_httpd_request_t * http_req
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)
hpd_error_t hpd_service_id_alloc(hpd_service_id_t **id, hpd_t *hpd, const char *aid, const char *did, const char *sid)
hpd_error_t hpd_httpd_destroy(hpd_httpd_t *httpd)
Destroy a httpd instance.
hpd_error_t hpd_value_alloc(hpd_value_t **value, const char *body, int len)
[Browsing foreach loops]
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]
static hpd_error_t rest_url_extract(hpd_rest_t *rest, const char *url, char **aid, char **did, char **sid)
struct hpd_rest_req hpd_rest_req_t
static hpd_httpd_return_t rest_on_req_cmpl(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
hpd_error_t hpd_value_get_body(const hpd_value_t *value, const char **body, size_t *len)
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)
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)
hpd_error_t hpd_request_free(hpd_request_t *request)
static hpd_error_t rest_reply_devices(hpd_rest_req_t *rest_req)
#define HPD_STR_N_CPY(DST, SRC, LEN)
static hpd_httpd_return_t rest_on_req_destroy(hpd_httpd_t *ins, hpd_httpd_request_t *req, void *ws_ctx, void **req_data)
#define HPD_LOG_INFO(CONTEXT, FMT,...)
const hpd_module_t * context
hpd_error_t hpd_rest_get_timestamp(const hpd_module_t *context, char *str)
#define HPD_CALLOC(PTR, NUM, CAST)
Allocates and zeros a structure.
#define HPD_HTTP_STATUS_CODE_MAP(XX)
[hpd_method_t]
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.
hpd_error_t hpd_request_set_value(hpd_request_t *request, hpd_value_t *value)
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)
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)
hpd_httpd_nodata_f on_req_url_cmpl
hpd_httpd_response_t * http_res
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]
#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_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
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
#define HPD_LOG_RETURN_E_ALLOC(CONTEXT)
hpd_httpd_nodata_f on_req_begin
static void rest_on_response(void *data, const hpd_response_t *res)
[Application API Callbacks]
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)
hpd_error_t hpd_response_get_status(const hpd_response_t *response, hpd_status_t *status)
[hpd_request_t functions]
hpd_error_t hpd_device_id_free(hpd_device_id_t *id)
hpd_httpd_nodata_f on_req_hdr_cmpl
static void rest_on_free(void *data)
static hpd_error_t rest_reply_bad_request(hpd_httpd_request_t *req, hpd_rest_req_t *rest_req, const hpd_module_t *context)
hpd_error_t hpd_map_get(hpd_map_t *map, const char *k, const char **v)
hpd_error_t hpd_request_set_data(hpd_request_t *request, void *data, hpd_free_f on_free)
static hpd_error_t rest_on_stop(void *data, hpd_t *hpd)
Settings struct for webserver.
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
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.
hpd_error_t hpd_httpd_stop(hpd_httpd_t *httpd)
Stop a httpd instance.
hpd_httpd_settings_t ws_set
hpd_error_t hpd_httpd_start(hpd_httpd_t *httpd)
Start a httpd instance.
hpd_httpd_nodata_f on_req_destroy
hpd_httpd_nodata_f on_req_cmpl
state
The possible states of a request.
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)
hpd_error_t hpd_rest_url_create(hpd_rest_t *rest, hpd_service_id_t *service, char **url)
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)
static hpd_httpd_return_t rest_on_req_begin(hpd_httpd_t *httpd, hpd_httpd_request_t *req, void *httpd_ctx, void **req_data)
hpd_error_t hpd_rest_xml_get_configuration(hpd_t *hpd, hpd_rest_t *rest, const hpd_module_t *context, char **out)
static hpd_error_t rest_on_start(void *data, hpd_t *hpd)
#define HPD_HTTPD_SETTINGS_DEFAULT
Default settings for httpd.
hpd_httpd_data_f on_req_body
static hpd_error_t rest_url_decode(hpd_rest_t *rest, const char *encoded, char **decoded)
#define HPD_LOG_WARN(CONTEXT, FMT,...)
hpd_error_t hpd_rest_xml_get_value(char *value, const hpd_module_t *context, char **out)
enum hpd_httpd_method hpd_httpd_method_t
hpd_error_t hpd_service_id_free(hpd_service_id_t *id)
enum rest_content_type rest_content_type_t
struct ev_loop hpd_ev_loop_t
#define HPD_REALLOC(PTR, NUM, CAST)
CAST is for c++ compatibility (tests).
enum hpd_method hpd_method_t
[hpd_log_level_t]
hpd_error_t hpd_rest_json_parse_value(const char *in, const hpd_module_t *context, char **out)
hpd_error_t hpd_rest_xml_parse_value(const char *in, const hpd_module_t *context, char **out)
hpd_error_t hpd_response_get_value(const hpd_response_t *response, const hpd_value_t **value)
#define HPD_LOG_ERROR(CONTEXT, FMT,...)
hpd_error_t hpd_get_loop(hpd_t *hpd, hpd_ev_loop_t **loop)
static hpd_error_t rest_on_parse_opt(void *data, const char *name, const char *arg)