12 #include "httpdclient.h"
14 #include "vdrclient.h"
16 #define POSTBUFFERSIZE 512
18 std::shared_ptr<spd::logger> HTTPDClient::logger;
19 struct MHD_Daemon* HTTPDClient::httpdserver = NULL;
20 std::string HTTPDClient::docRoot;
21 std::string HTTPDClient::configDir;
23 bool HTTPDClient::StartServer(std::string _docRoot, int port, const char* _configDir)
26 configDir = std::string(_configDir);
28 logger = spd::get("jsonserver_spdlog");
30 httpdserver = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv6, port, NULL, NULL,
31 &HTTPDClient::handle_connection, NULL,
32 MHD_OPTION_NOTIFY_CONNECTION, &HTTPDClient::connection_notify, NULL,
33 MHD_OPTION_NOTIFY_COMPLETED, &HTTPDClient::request_completed, NULL,
34 MHD_OPTION_CONNECTION_TIMEOUT, 70,
36 if (httpdserver == NULL) { logger.reset(); return false; }
37 logger->info("HTTPDClient: Started");
41 void HTTPDClient::StopServer()
43 if (httpdserver) MHD_stop_daemon(httpdserver);
48 // Now for the libmicrohttpd callbacks
50 MHD_Result HTTPDClient::uri_key_value(void *cls, enum MHD_ValueKind kind, const char* key, const char* value)
52 if (kind != MHD_GET_ARGUMENT_KIND) return MHD_NO;
53 HTTPDClient* httpdclient = (HTTPDClient*)cls;
54 httpdclient->addGetVar(key, value);
58 MHD_Result HTTPDClient::iterate_post(void *clientIdentifier, enum MHD_ValueKind kind, const char *key,
59 const char *filename, const char* content_type,
60 const char *transfer_encoding, const char *data, uint64_t off, size_t size)
62 // logger->info("HTTPDClient: add post field: {} {} {}", key, data, size);
63 HTTPDClient* httpdclient = (HTTPDClient*)clientIdentifier;
64 httpdclient->addPostField(key, data, size);
65 return MHD_YES; //Return MHD_YES to continue iterating, MHD_NO to abort the iteration.
68 void HTTPDClient::request_completed(void *cls, struct MHD_Connection *mhd_connection,
69 void **unused, enum MHD_RequestTerminationCode toe)
71 const MHD_ConnectionInfo* mhdc = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_SOCKET_CONTEXT);
72 HTTPDClient* httpdclient = (HTTPDClient*)mhdc->socket_context;
73 if (!httpdclient) return;
74 httpdclient->requestComplete();
77 void HTTPDClient::connection_notify(void *cls, struct MHD_Connection* mhd_connection,
78 void **socket_context, enum MHD_ConnectionNotificationCode toe)
80 if (toe == MHD_CONNECTION_NOTIFY_STARTED)
82 *socket_context = (void*)new HTTPDClient(mhd_connection, configDir);
84 else if (toe == MHD_CONNECTION_NOTIFY_CLOSED)
86 HTTPDClient* httpdclient = (HTTPDClient*)*socket_context;
87 if (!httpdclient) return;
89 *socket_context = NULL;
93 MHD_Result HTTPDClient::handle_connection(void* cls, struct MHD_Connection* mhd_connection,
94 const char* url, const char* method,
95 const char* version, const char* upload_data,
96 long unsigned int* upload_data_size, void** userData)
98 const MHD_ConnectionInfo* mhdc = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_SOCKET_CONTEXT);
99 HTTPDClient* httpdclient = (HTTPDClient*)mhdc->socket_context;
100 return httpdclient->handleConnection(url, method, version, upload_data, upload_data_size, userData);
103 // End of static callbacks, now for the client object itself
105 HTTPDClient::HTTPDClient(struct MHD_Connection* _mhd_connection, const std::string& _configDir)
106 : postprocessor(NULL), url(NULL), mhd_connection(_mhd_connection), vdrclient(_configDir)
108 // printf("HTTPDClient created %p\n", this);
111 HTTPDClient::~HTTPDClient()
113 // printf("%p HTTPDClient destructor\n", this);
117 MHD_Result HTTPDClient::handleConnection(const char* turl, const char* method,
118 const char* version, const char* upload_data,
119 size_t* upload_data_size, void** userData)
122 logger->debug("handle_connection called");
123 logger->debug("hc: cls {}", (void*)cls);
124 logger->debug("hc: mhd_connection {}", (void*)mhd_connection);
125 logger->debug("hc: url {}", (void*)url);
126 if (url) logger->debug("hc: url: {}", url);
127 logger->debug("hc: method {}", (void*)method);
128 if (url) logger->debug("hc: method: {}", method);
129 logger->debug("hc: version {}", (void*)version);
130 if (url) logger->debug("hc: version: {}", version);
131 logger->debug("hc: upload_data {}", (void*)upload_data);
132 logger->debug("hc: upload_data_size {}", *upload_data_size);
133 logger->debug("hc: userData {}", (void*)*userData);
136 // Now we are going to use userData as a flag to say first run or not
138 if (!*userData) // new request
140 *userData = (void*)1;
141 int size __attribute__((unused)) = asprintf(&url, "%s", turl);
143 if (!strcmp(method, "POST"))
145 MHD_get_connection_values(mhd_connection, MHD_GET_ARGUMENT_KIND, HTTPDClient::uri_key_value, (void*)this);
147 // The following returns NULL if there are no POST fields to come
148 postprocessor = MHD_create_post_processor(mhd_connection, POSTBUFFERSIZE,
149 HTTPDClient::iterate_post, (void*)this);
151 else if (!strcmp(method, "GET"))
153 // OK, nothing else to do here
163 // Not first go at this request
165 if (!strcmp(method, "GET"))
169 else if (!strcmp(method, "POST"))
171 // HC will be called at least three times. Once above to create the HTTPDClient object (above).
172 // Here the middle calls will be called with upload_data_size > 0
173 // The last call is with upload_data_size == 0 and signals the end, a response must be queued
175 if (*upload_data_size != 0) // There is more to process, and signal run again
177 MHD_post_process(postprocessor, upload_data, *upload_data_size);
178 *upload_data_size = 0;
183 // printf("hc: zero post provided, end of upload\n");
185 MHD_destroy_post_processor(postprocessor);
186 postprocessor = NULL;
188 return processPOST();
193 return sendStockResponse(405);
197 void HTTPDClient::requestComplete()
199 // printf("%p HTTPDClient request complete\n", this);
203 MHD_destroy_post_processor(postprocessor);
204 postprocessor = NULL;
208 void HTTPDClient::addGetVar(const char* key, const char* value)
210 if (strlen(key) > 50) return;
211 if (value && (strlen(value) > 1000)) return;
213 getVars[std::string(key)] = std::string(value);
215 for(auto gv : getVars)
217 printf("%s %s\n", gv.first.c_str(), gv.second.c_str());
222 void HTTPDClient::addPostField(const char* key, const char* value, int valueLength)
224 if (strlen(key) > 50) return;
225 if (strlen(value) > 1000) return;
227 postFields[std::string(key)] = std::string(value, valueLength);
230 for(auto pf : postFields)
232 printf("%s %s\n", pf.first.c_str(), pf.second.c_str());
237 MHD_Result HTTPDClient::processGET()
239 const char* defaultfilename = "index.html";
241 if (url == NULL) return MHD_NO;
242 if (strstr(url, "..")) return MHD_NO; // MHD deals with these already and limits it. If it occurs here, error.
244 int size __attribute__((unused));
246 int slen = strlen(url);
248 if (url[slen-1] == '/')
249 size = asprintf(&fullpath, "%s%s%s", docRoot.c_str(), url, defaultfilename);
251 size = asprintf(&fullpath, "%s%s", docRoot.c_str(), url);
253 //printf("FILENAME: '%s'\n", fullpath);
256 if ( (stat(fullpath, &sbuf) == -1) // failed to stat
257 || ((sbuf.st_mode & S_IFMT) != S_IFREG) ) // must be regular file
260 return sendStockResponse(404);
263 int fd = open(fullpath, O_RDONLY);
266 if (fd == -1) return MHD_NO;
268 struct MHD_Response* response = MHD_create_response_from_fd(sbuf.st_size, fd);
269 MHD_Result ret = MHD_queue_response(mhd_connection, MHD_HTTP_OK, response);
273 MHD_destroy_response(response);
278 MHD_Result HTTPDClient::processPOST()
280 // printf("Process POST:\n");
281 //printf("REQ: %s\n", getVars["req"].c_str());
283 if (strcmp(url, "/jsonserver")) return sendStockResponse(404);
284 if (getVars["req"].empty()) return sendStockResponse(400);
286 std::stringstream returnData;
288 bool success = vdrclient.process(getVars["req"], postFields, &returnData);
289 if (!success) return sendStockResponse(500);
291 std::string returnDataStr = returnData.str();
293 struct MHD_Response* response = MHD_create_response_from_buffer(strlen(returnDataStr.c_str()), (void *)returnDataStr.c_str(), MHD_RESPMEM_MUST_COPY);
294 MHD_add_response_header(response, "Content-Type", "application/json");
295 MHD_Result ret = MHD_queue_response(mhd_connection, MHD_HTTP_OK, response);
299 MHD_destroy_response(response);
303 MHD_Result HTTPDClient::sendStockResponse(int code)
305 const char *page400 = "<html><body>Bad request</body></html>\n";
306 const char *page404 = "<html><body>File not found</body></html>\n";
307 const char *page405 = "<html><body>Method not allowed</body></html>\n";
308 const char *page500 = "<html><body>Internal server error</body></html>\n";
310 if (code == 400) page = page400;
311 else if (code == 404) page = page404;
312 else if (code == 405) page = page405;
313 else if (code == 500) page = page500;
316 struct MHD_Response* response = MHD_create_response_from_buffer(strlen(page), (void *)page, MHD_RESPMEM_PERSISTENT);
317 MHD_Result ret = MHD_queue_response(mhd_connection, code, response);
321 MHD_destroy_response(response);