10 #include "httpdclient.h"
12 #include "vdrclient.h"
14 #define POSTBUFFERSIZE 512
16 std::shared_ptr<spd::logger> HTTPDClient::logger;
17 struct MHD_Daemon* HTTPDClient::httpdserver = NULL;
18 std::string HTTPDClient::docRoot;
20 bool HTTPDClient::StartServer(std::string _docRoot, int port)
24 logger = spd::get("jsonserver_spdlog");
26 httpdserver = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
27 &HTTPDClient::handle_connection, NULL,
28 MHD_OPTION_NOTIFY_CONNECTION, &HTTPDClient::connection_notify, NULL,
29 MHD_OPTION_NOTIFY_COMPLETED, &HTTPDClient::request_completed, NULL,
30 MHD_OPTION_CONNECTION_TIMEOUT, 70,
32 if (httpdserver == NULL) { logger.reset(); return false; }
33 logger->info("HTTPDClient: Started");
37 void HTTPDClient::StopServer()
39 if (httpdserver) MHD_stop_daemon(httpdserver);
44 // Now for the libmicrohttpd callbacks
46 int HTTPDClient::uri_key_value(void *cls, enum MHD_ValueKind kind, const char* key, const char* value)
48 if (kind != MHD_GET_ARGUMENT_KIND) return MHD_NO;
49 HTTPDClient* httpdclient = (HTTPDClient*)cls;
50 httpdclient->addGetVar(key, value);
54 int HTTPDClient::iterate_post(void *clientIdentifier, enum MHD_ValueKind kind, const char *key,
55 const char *filename, const char* content_type,
56 const char *transfer_encoding, const char *data, uint64_t off, size_t size)
58 if (size == 0) return MHD_NO;
60 HTTPDClient* httpdclient = (HTTPDClient*)clientIdentifier;
61 httpdclient->addPostField(key, data, size);
64 //Return MHD_YES to continue iterating, MHD_NO to abort the iteration.
67 void HTTPDClient::request_completed(void *cls, struct MHD_Connection *mhd_connection,
68 void **unused, enum MHD_RequestTerminationCode toe)
70 const MHD_ConnectionInfo* mhdc = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_SOCKET_CONTEXT);
71 HTTPDClient* httpdclient = (HTTPDClient*)mhdc->socket_context;
72 if (!httpdclient) return;
73 httpdclient->requestComplete();
76 void HTTPDClient::connection_notify(void *cls, struct MHD_Connection* mhd_connection,
77 void **socket_context, enum MHD_ConnectionNotificationCode toe)
79 if (toe == MHD_CONNECTION_NOTIFY_STARTED)
81 *socket_context = (void*)new HTTPDClient(mhd_connection);
83 else if (toe == MHD_CONNECTION_NOTIFY_CLOSED)
85 HTTPDClient* httpdclient = (HTTPDClient*)*socket_context;
86 if (!httpdclient) return;
88 *socket_context = NULL;
92 int HTTPDClient::handle_connection(void* cls, struct MHD_Connection* mhd_connection,
93 const char* url, const char* method,
94 const char* version, const char* upload_data,
95 size_t* upload_data_size, void** userData)
98 printf("handle_connection called\n");
99 printf("hc: cls %p\n", cls);
100 printf("hc: mhd_connection %p\n", mhd_connection);
101 printf("hc: url %p\n", url);
102 if (url) printf("hc: url: %s\n", url);
103 printf("hc: method %p\n", method);
104 if (url) printf("hc: method: %s\n", method);
105 printf("hc: version %p\n", version);
106 if (url) printf("hc: version: %s\n", version);
107 printf("hc: upload_data %p\n", upload_data);
108 printf("hc: upload_data_size %lu\n", *upload_data_size);
109 printf("hc: userData %p\n", *userData);
112 const MHD_ConnectionInfo* mhdc = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_SOCKET_CONTEXT);
114 HTTPDClient* httpdclient = (HTTPDClient*)mhdc->socket_context;
116 // Now we are going to use userData as a flag to say first run or not
118 if (!*userData) // new request
120 *userData = (void*)1;
121 httpdclient->setUrl(url);
123 if (!strcmp(method, "POST"))
125 MHD_get_connection_values(mhd_connection, MHD_GET_ARGUMENT_KIND, HTTPDClient::uri_key_value, (void*)httpdclient);
127 // The following returns NULL if there are no POST fields to come
128 httpdclient->postprocessor = MHD_create_post_processor(mhd_connection, POSTBUFFERSIZE,
129 HTTPDClient::iterate_post, (void*)httpdclient);
131 else if (!strcmp(method, "GET"))
133 // OK, nothing else to do here
143 // Not first go at this request
145 if (!strcmp(method, "GET"))
147 return httpdclient->processGET();
149 else if (!strcmp(method, "POST"))
151 // HC will be called at least three times. Once above to create the HTTPDClient object (above).
152 // Here the middle calls will be called with upload_data_size > 0
153 // The last call is with upload_data_size == 0 and signals the end, a response must be queued
155 if (*upload_data_size != 0) // There is more to process, and signal run again
157 MHD_post_process(httpdclient->postprocessor, upload_data, *upload_data_size);
158 *upload_data_size = 0;
163 // printf("hc: zero post provided, end of upload\n");
164 return httpdclient->processPOST();
169 return httpdclient->sendStockResponse(405);
173 // End of static callbacks, now for the client object itself
175 HTTPDClient::HTTPDClient(struct MHD_Connection* _mhd_connection)
176 : postprocessor(NULL), url(NULL), mhd_connection(_mhd_connection)
178 // printf("HTTPDClient created %p\n", this);
181 HTTPDClient::~HTTPDClient()
183 // printf("%p HTTPDClient destructor\n", this);
187 void HTTPDClient::requestComplete()
189 // printf("%p HTTPDClient request complete\n", this);
193 MHD_destroy_post_processor(postprocessor);
194 postprocessor = NULL;
198 void HTTPDClient::setUrl(const char* _url)
200 int size __attribute__((unused)) = asprintf(&url, "%s", _url);
203 void HTTPDClient::addGetVar(const char* key, const char* value)
205 if (strlen(key) > 50) return;
206 if (value && (strlen(value) > 1000)) return;
208 getVars[std::string(key)] = std::string(value);
210 for(auto gv : getVars)
212 printf("%s %s\n", gv.first.c_str(), gv.second.c_str());
217 void HTTPDClient::addPostField(const char* key, const char* value, int valueLength)
219 if (strlen(key) > 50) return;
220 if (strlen(value) > 1000) return;
222 postFields[std::string(key)] = std::string(value, valueLength);
225 for(auto pf : postFields)
227 printf("%s %s\n", pf.first.c_str(), pf.second.c_str());
232 int HTTPDClient::processGET()
234 const char* defaultfilename = "index.html";
236 if (url == NULL) return MHD_NO;
237 if (strstr(url, "..")) return MHD_NO; // MHD deals with these already and limits it. If it occurs here, error.
239 int size __attribute__((unused));
241 int slen = strlen(url);
243 if (url[slen-1] == '/')
244 size = asprintf(&fullpath, "%s%s%s", docRoot.c_str(), url, defaultfilename);
246 size = asprintf(&fullpath, "%s%s", docRoot.c_str(), url);
248 //printf("FILENAME: '%s'\n", fullpath);
251 if ( (stat(fullpath, &sbuf) == -1) // failed to stat
252 || ((sbuf.st_mode & S_IFMT) != S_IFREG) ) // must be regular file
255 return sendStockResponse(404);
258 int fd = open(fullpath, O_RDONLY);
261 if (fd == -1) return MHD_NO;
263 struct MHD_Response* response = MHD_create_response_from_fd(sbuf.st_size, fd);
264 int ret = MHD_queue_response(mhd_connection, MHD_HTTP_OK, response);
265 MHD_destroy_response(response);
270 int HTTPDClient::processPOST()
272 // printf("Process POST:\n");
273 //printf("REQ: %s\n", getVars["req"].c_str());
275 if (strcmp(url, "/jsonserver")) return sendStockResponse(404);
276 if (getVars["req"].empty()) return sendStockResponse(400);
278 std::string returnData;
280 bool success = vdrclient.process(getVars["req"], postFields, returnData);
281 if (!success) return sendStockResponse(500);
283 struct MHD_Response* response = MHD_create_response_from_buffer(strlen(returnData.c_str()), (void *)returnData.c_str(), MHD_RESPMEM_MUST_COPY);
284 MHD_add_response_header(response, "Content-Type", "application/json");
285 int ret = MHD_queue_response(mhd_connection, MHD_HTTP_OK, response);
286 MHD_destroy_response(response);
290 int HTTPDClient::sendStockResponse(int code)
292 const char *page400 = "<html><body>Bad request</body></html>\n";
293 const char *page404 = "<html><body>File not found</body></html>\n";
294 const char *page405 = "<html><body>Method not allowed</body></html>\n";
295 const char *page500 = "<html><body>Internal server error</body></html>\n";
297 if (code == 400) page = page400;
298 else if (code == 404) page = page404;
299 else if (code == 405) page = page405;
300 else if (code == 500) page = page500;
303 struct MHD_Response* response = MHD_create_response_from_buffer(strlen(page), (void *)page, MHD_RESPMEM_PERSISTENT);
304 int ret = MHD_queue_response(mhd_connection, code, response);
305 MHD_destroy_response(response);