MediaProcessors
procs_api_http.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Rafael Antoniello
3  *
4  * This file is part of MediaProcessors.
5  *
6  * MediaProcessors is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * MediaProcessors is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with MediaProcessors. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
25 #include "procs_api_http.h"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <pthread.h>
32 
33 #include <libcjson/cJSON.h>
34 #include <libmediaprocsutils/uri_parser.h>
35 #include <libmediaprocsutils/log.h>
36 #include <libmediaprocsutils/stat_codes.h>
37 #include <libmediaprocsutils/check_utils.h>
38 
39 #include "procs.h"
40 
41 /* **** Definitions **** */
42 
52 #define RESPONSE_FMT "{\"code\":%d,\"status\":%s,\"message\":%s,\"data\":%s}"
53 
57 #define URL_HAS(NEEDLE) \
58  (url!= NULL && strstr(url, NEEDLE)!= NULL)
59 
63 #define URL_METHOD_IS(TAG) \
64  (request_method!= NULL && \
65  strncmp(request_method, TAG, strlen(TAG))== 0)
66 
70 #define HTTP_OK "\"OK\""
71 #define HTTP_CREATED "\"Created\""
72 #define HTTP_NOCONTENT "\"No Content\""
73 #define HTTP_NOTFOUND "\"Not Found\""
74 #define HTTP_NOTMODIF "\"Not Modified\""
75 #define HTTP_CREATED "\"Created\""
76 #define HTTP_CONFLICT "\"Conflict\""
77 
78 /* **** Prototypes **** */
79 
80 /* **** Implementations **** */
81 
82 int procs_api_http_req_handler(procs_ctx_t *procs_ctx, const char *url,
83  const char *query_string, const char *request_method, char *content,
84  size_t content_len, char **ref_str_response)
85 {
86  int http_code;
87  const char *http_status_str, *msg_str;
88  size_t response_size;
89  int proc_id, ret_code, end_code= STAT_ERROR;
90  char *proc_name_str= NULL, *response_str= NULL, *data_obj_str= NULL;
91  int64_t aux_id= -1;
92  LOG_CTX_INIT(NULL);
93 
94  /* Check arguments.
95  * NOTE: Arguments 'query_string' and 'content' are allowed to be NULL.
96  */
97  CHECK_DO(url!= NULL, return STAT_ERROR);
98  CHECK_DO(request_method!= NULL, return STAT_ERROR);
99  CHECK_DO(ref_str_response!= NULL, return STAT_ERROR);
100 
101  //LOGV("\n//----------------------------------------------------------//\n"
102  // "HTTP REQUEST: url: '%s?%s'; request_method: %s\n"
103  // "message body (%d): '%s'\n", url, query_string? query_string: "",
104  // request_method, (int)content_len, content); //comment-me
105 
106  *ref_str_response= NULL;
107 
108  /* Check 'PROCS' URL tree root existence */
109  if(!URL_HAS("/procs")) {
110  end_code= STAT_ENOTFOUND;
111  goto end;
112  }
113 
114  if(URL_HAS("/procs.json")) {
115 
116  /* **** Handle PROCS list related requests **** */
117 
118  if(0/*URL_METHOD_IS("POST")*/) {
119 
120  /* Among query-string parameters it is mandatory to find the
121  * processor type name (key "proc_name") to be instantiated.
122  * Note also that parameters that do not correspond to the
123  * settings of the instantiated processor will be just ignored,
124  * thus we can pass the full query-string for the settings.
125  */
126  if(query_string== NULL ||
127  (proc_name_str= uri_parser_query_str_get_value("proc_name",
128  query_string))== NULL) {
129  end_code= STAT_EINVAL;
130  goto end;
131  }
132  end_code= procs_opt(procs_ctx, "PROCS_POST", proc_name_str,
133  query_string, &data_obj_str);
134 
135  } else if(URL_METHOD_IS("GET")) {
136  end_code= procs_opt(procs_ctx, "PROCS_GET", &data_obj_str, NULL);
137  } else {
138  end_code= STAT_ENOTFOUND;
139  }
140  } else if(URL_HAS("/procs/")) {
141 
142  /* **** Handle PROC instance related requests **** */
143 
144  /* We need processor identifier for these requests */
145  ret_code= uri_parser_get_id_from_rest_url(url, "/procs/",
146  (long long*)&aux_id);
147  proc_id= (int)aux_id;
148  if(proc_id< -1 || ret_code!= STAT_SUCCESS) {
149  end_code= STAT_ENOTFOUND;
150  goto end;
151  }
152 
153  if(URL_METHOD_IS("PUT"))
154  end_code= procs_opt(procs_ctx, "PROCS_ID_PUT", proc_id,
155  query_string);
156  else if (URL_METHOD_IS("GET"))
157  end_code= procs_opt(procs_ctx, "PROCS_ID_GET", proc_id,
158  &data_obj_str);
159  //else if(URL_METHOD_IS("DELETE"))
160  // end_code= procs_opt(procs_ctx, "PROCS_ID_DELETE", proc_id);
161  else
162  end_code= STAT_ENOTFOUND;
163  } else {
164  end_code= STAT_ENOTFOUND;
165  }
166 
167 end:
168  /* Translate end-code to HTTP status code and get related description.
169  * The translation table is as follows:
170  *
171  * HTTP method | HTTP status code
172  * -------------------------------------------------------------
173  * GET | 200 (OK), 404 (Not Found), 304 (Not Modified)
174  * POST | 201 (Created), 404 (Not Found), 409 (Conflict)
175  * PUT | 200 (OK), 204 (No Content), 404 (Not Found)
176  * DELETE | 200 (OK), 404 (Not Found)
177  *
178  * Any other combination is assumed to be 404- "Not Found".
179  */
180  switch(end_code) {
181  case STAT_SUCCESS:
182  if(URL_METHOD_IS("POST")) {
183  http_code= 201; http_status_str= HTTP_CREATED;
184  } else {
185  http_code= 200; http_status_str= HTTP_OK;
186  }
187  break;
188  case STAT_ENOTFOUND:
189  if(URL_METHOD_IS("PUT")) {
190  http_code= 204; http_status_str= HTTP_NOCONTENT;
191  } else {
192  http_code= 404; http_status_str= HTTP_NOTFOUND;
193  }
194  break;
195  case STAT_NOTMODIFIED:
196  case STAT_EAGAIN:
197  if(URL_METHOD_IS("GET")) {
198  http_code= 304; http_status_str= HTTP_NOTMODIF;
199  } else if(URL_METHOD_IS("PUT")) {
200  http_code= 204; http_status_str= HTTP_NOCONTENT;
201  } else if (URL_METHOD_IS("POST")) {
202  http_code= 409; http_status_str= HTTP_CONFLICT;
203  } else {
204  http_code= 404; http_status_str= HTTP_NOTFOUND;
205  }
206  break;
207  case STAT_ERROR:
208  default:
209  http_code= 404; http_status_str= HTTP_NOTFOUND;
210  break;
211  }
212 
213  /* Compose response */
214  msg_str= stat_codes_get_description(end_code); // Do not release (static)
215  response_size= strlen(RESPONSE_FMT)+ sizeof(http_code);
216  response_size+= http_status_str!= NULL? strlen(http_status_str): 4/*null*/;
217  response_size+= msg_str!= NULL? strlen(msg_str): 4/*null*/;
218  response_size+= data_obj_str!= NULL? strlen(data_obj_str): 4/*null*/;
219  ASSERT((response_str= (char*)malloc(response_size))!= NULL);
220  if(response_str!= NULL) {
221  snprintf(response_str, response_size, RESPONSE_FMT, http_code,
222  http_status_str!= NULL? http_status_str: "null",
223  msg_str!= NULL && strlen(msg_str)> 0? msg_str: "null",
224  data_obj_str!= NULL? data_obj_str: "null");
225  *ref_str_response= response_str;
226  response_str= NULL; // Avoid double referencing
227  }
228 
229  if(response_str!= NULL)
230  free(response_str);
231  if(proc_name_str!= NULL)
232  free(proc_name_str);
233  if(data_obj_str!= NULL)
234  free(data_obj_str);
235 
236  //LOGV("'%s' returns: \n'%s'\n", __func__, *ref_str_response!= NULL?
237  // *ref_str_response: "NULL"); //comment-me
238  return end_code;
239 }
Generic processors (PROC) module.
int procs_opt(procs_ctx_t *procs_ctx, const char *tag,...)
Definition: procs.c:474
#define HTTP_OK
#define URL_METHOD_IS(TAG)
Processors (PROCS) module HTTP API adaptation layer.
#define RESPONSE_FMT
#define URL_HAS(NEEDLE)
#define CHECK_DO(COND, ACTION)
Definition: check_utils.h:57
int procs_api_http_req_handler(procs_ctx_t *procs_ctx, const char *url, const char *query_string, const char *request_method, char *content, size_t content_len, char **ref_str_response)
#define ASSERT(COND)
Definition: check_utils.h:51