]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/src/CivetServer.cpp
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / src / CivetServer.cpp
CommitLineData
11fdf7f2 1/* Copyright (c) 2013-2017 the Civetweb developers
7c673cae
FG
2 * Copyright (c) 2013 No Face Press, LLC
3 *
4 * License http://opensource.org/licenses/mit-license.php MIT License
5 */
6
7#include "CivetServer.h"
8
9#include <stdlib.h>
10#include <string.h>
11#include <assert.h>
12#include <stdexcept>
13
14#ifndef UNUSED_PARAMETER
15#define UNUSED_PARAMETER(x) (void)(x)
16#endif
17
18bool
19CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
20{
21 UNUSED_PARAMETER(server);
22 UNUSED_PARAMETER(conn);
23 return false;
24}
25
26bool
27CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
28{
29 UNUSED_PARAMETER(server);
30 UNUSED_PARAMETER(conn);
31 return false;
32}
33
34bool
35CivetHandler::handleHead(CivetServer *server, struct mg_connection *conn)
36{
37 UNUSED_PARAMETER(server);
38 UNUSED_PARAMETER(conn);
39 return false;
40}
41
42bool
43CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
44{
45 UNUSED_PARAMETER(server);
46 UNUSED_PARAMETER(conn);
47 return false;
48}
49
50bool
51CivetHandler::handlePatch(CivetServer *server, struct mg_connection *conn)
52{
53 UNUSED_PARAMETER(server);
54 UNUSED_PARAMETER(conn);
55 return false;
56}
57
58bool
59CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
60{
61 UNUSED_PARAMETER(server);
62 UNUSED_PARAMETER(conn);
63 return false;
64}
65
66bool
67CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
68{
69 UNUSED_PARAMETER(server);
70 UNUSED_PARAMETER(conn);
71 return false;
72}
73
74bool
75CivetWebSocketHandler::handleConnection(CivetServer *server,
76 const struct mg_connection *conn)
77{
78 UNUSED_PARAMETER(server);
79 UNUSED_PARAMETER(conn);
80 return true;
81}
82
83void
84CivetWebSocketHandler::handleReadyState(CivetServer *server,
85 struct mg_connection *conn)
86{
87 UNUSED_PARAMETER(server);
88 UNUSED_PARAMETER(conn);
89 return;
90}
91
92bool
93CivetWebSocketHandler::handleData(CivetServer *server,
94 struct mg_connection *conn,
95 int bits,
96 char *data,
97 size_t data_len)
98{
99 UNUSED_PARAMETER(server);
100 UNUSED_PARAMETER(conn);
101 UNUSED_PARAMETER(bits);
102 UNUSED_PARAMETER(data);
103 UNUSED_PARAMETER(data_len);
104 return true;
105}
106
107void
108CivetWebSocketHandler::handleClose(CivetServer *server,
109 const struct mg_connection *conn)
110{
111 UNUSED_PARAMETER(server);
112 UNUSED_PARAMETER(conn);
113 return;
114}
115
116int
117CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
118{
119 const struct mg_request_info *request_info = mg_get_request_info(conn);
120 assert(request_info != NULL);
121 CivetServer *me = (CivetServer *)(request_info->user_data);
122 assert(me != NULL);
123
124 // Happens when a request hits the server before the context is saved
125 if (me->context == NULL)
126 return 0;
127
128 mg_lock_context(me->context);
129 me->connections[conn] = CivetConnection();
130 mg_unlock_context(me->context);
131
132 CivetHandler *handler = (CivetHandler *)cbdata;
133
134 if (handler) {
135 if (strcmp(request_info->request_method, "GET") == 0) {
136 return handler->handleGet(me, conn) ? 1 : 0;
137 } else if (strcmp(request_info->request_method, "POST") == 0) {
138 return handler->handlePost(me, conn) ? 1 : 0;
139 } else if (strcmp(request_info->request_method, "HEAD") == 0) {
140 return handler->handleHead(me, conn) ? 1 : 0;
141 } else if (strcmp(request_info->request_method, "PUT") == 0) {
142 return handler->handlePut(me, conn) ? 1 : 0;
143 } else if (strcmp(request_info->request_method, "DELETE") == 0) {
144 return handler->handleDelete(me, conn) ? 1 : 0;
145 } else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
146 return handler->handleOptions(me, conn) ? 1 : 0;
147 } else if (strcmp(request_info->request_method, "PATCH") == 0) {
148 return handler->handlePatch(me, conn) ? 1 : 0;
149 }
150 }
151
152 return 0; // No handler found
153}
154
155int
156CivetServer::authHandler(struct mg_connection *conn, void *cbdata)
157{
158 const struct mg_request_info *request_info = mg_get_request_info(conn);
159 assert(request_info != NULL);
160 CivetServer *me = (CivetServer *)(request_info->user_data);
161 assert(me != NULL);
162
163 // Happens when a request hits the server before the context is saved
164 if (me->context == NULL)
165 return 0;
166
167 mg_lock_context(me->context);
168 me->connections[conn] = CivetConnection();
169 mg_unlock_context(me->context);
170
171 CivetAuthHandler *handler = (CivetAuthHandler *)cbdata;
172
173 if (handler) {
174 return handler->authorize(me, conn) ? 1 : 0;
175 }
176
177 return 0; // No handler found
178}
179
180int
181CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,
182 void *cbdata)
183{
184 const struct mg_request_info *request_info = mg_get_request_info(conn);
185 assert(request_info != NULL);
186 CivetServer *me = (CivetServer *)(request_info->user_data);
187 assert(me != NULL);
188
189 // Happens when a request hits the server before the context is saved
190 if (me->context == NULL)
191 return 0;
192
193 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
194
195 if (handler) {
196 return handler->handleConnection(me, conn) ? 0 : 1;
197 }
198
199 return 1; // No handler found, close connection
200}
201
202void
203CivetServer::webSocketReadyHandler(struct mg_connection *conn, void *cbdata)
204{
205 const struct mg_request_info *request_info = mg_get_request_info(conn);
206 assert(request_info != NULL);
207 CivetServer *me = (CivetServer *)(request_info->user_data);
208 assert(me != NULL);
209
210 // Happens when a request hits the server before the context is saved
211 if (me->context == NULL)
212 return;
213
214 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
215
216 if (handler) {
217 handler->handleReadyState(me, conn);
218 }
219}
220
221int
222CivetServer::webSocketDataHandler(struct mg_connection *conn,
223 int bits,
224 char *data,
225 size_t data_len,
226 void *cbdata)
227{
228 const struct mg_request_info *request_info = mg_get_request_info(conn);
229 assert(request_info != NULL);
230 CivetServer *me = (CivetServer *)(request_info->user_data);
231 assert(me != NULL);
232
233 // Happens when a request hits the server before the context is saved
234 if (me->context == NULL)
235 return 0;
236
237 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
238
239 if (handler) {
240 return handler->handleData(me, conn, bits, data, data_len) ? 1 : 0;
241 }
242
243 return 1; // No handler found
244}
245
246void
247CivetServer::webSocketCloseHandler(const struct mg_connection *conn,
248 void *cbdata)
249{
250 const struct mg_request_info *request_info = mg_get_request_info(conn);
251 assert(request_info != NULL);
252 CivetServer *me = (CivetServer *)(request_info->user_data);
253 assert(me != NULL);
254
255 // Happens when a request hits the server before the context is saved
256 if (me->context == NULL)
257 return;
258
259 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
260
261 if (handler) {
262 handler->handleClose(me, conn);
263 }
264}
265
266CivetCallbacks::CivetCallbacks()
267{
268 memset(this, 0, sizeof(*this));
269}
270
271CivetServer::CivetServer(const char **options,
11fdf7f2
TL
272 const struct CivetCallbacks *_callbacks,
273 const void *UserContextIn)
7c673cae
FG
274 : context(0)
275{
276 struct CivetCallbacks callbacks;
277
11fdf7f2
TL
278 UserContext = UserContextIn;
279
7c673cae
FG
280 if (_callbacks) {
281 callbacks = *_callbacks;
282 userCloseHandler = _callbacks->connection_close;
283 } else {
284 userCloseHandler = NULL;
285 }
286 callbacks.connection_close = closeHandler;
287 context = mg_start(&callbacks, this, options);
288 if (context == NULL)
289 throw CivetException("null context when constructing CivetServer. "
290 "Possible problem binding to port.");
291}
292
293CivetServer::CivetServer(std::vector<std::string> options,
11fdf7f2
TL
294 const struct CivetCallbacks *_callbacks,
295 const void *UserContextIn)
7c673cae
FG
296 : context(0)
297{
298 struct CivetCallbacks callbacks;
299
11fdf7f2
TL
300 UserContext = UserContextIn;
301
7c673cae
FG
302 if (_callbacks) {
303 callbacks = *_callbacks;
304 userCloseHandler = _callbacks->connection_close;
305 } else {
306 userCloseHandler = NULL;
307 }
308 callbacks.connection_close = closeHandler;
309
310 std::vector<const char *> pointers(options.size());
311 for (size_t i = 0; i < options.size(); i++) {
312 pointers[i] = (options[i].c_str());
313 }
314 pointers.push_back(0);
315
316 context = mg_start(&callbacks, this, &pointers[0]);
317 if (context == NULL)
318 throw CivetException("null context when constructing CivetServer. "
319 "Possible problem binding to port.");
320}
321
322CivetServer::~CivetServer()
323{
324 close();
325}
326
327void
328CivetServer::closeHandler(const struct mg_connection *conn)
329{
11fdf7f2 330 CivetServer *me = (CivetServer *)mg_get_user_data(mg_get_context(conn));
7c673cae
FG
331 assert(me != NULL);
332
333 // Happens when a request hits the server before the context is saved
334 if (me->context == NULL)
335 return;
336
11fdf7f2 337 if (me->userCloseHandler) {
7c673cae 338 me->userCloseHandler(conn);
11fdf7f2 339 }
7c673cae
FG
340 mg_lock_context(me->context);
341 me->connections.erase(const_cast<struct mg_connection *>(conn));
342 mg_unlock_context(me->context);
343}
344
345void
346CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
347{
348 mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
349}
350
351void
352CivetServer::addWebSocketHandler(const std::string &uri,
353 CivetWebSocketHandler *handler)
354{
355 mg_set_websocket_handler(context,
356 uri.c_str(),
357 webSocketConnectionHandler,
358 webSocketReadyHandler,
359 webSocketDataHandler,
360 webSocketCloseHandler,
361 handler);
362}
363
364void
365CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler)
366{
367 mg_set_auth_handler(context, uri.c_str(), authHandler, handler);
368}
369
370void
371CivetServer::removeHandler(const std::string &uri)
372{
373 mg_set_request_handler(context, uri.c_str(), NULL, NULL);
374}
375
376void
377CivetServer::removeWebSocketHandler(const std::string &uri)
378{
379 mg_set_websocket_handler(
380 context, uri.c_str(), NULL, NULL, NULL, NULL, NULL);
381}
382
383void
384CivetServer::removeAuthHandler(const std::string &uri)
385{
386 mg_set_auth_handler(context, uri.c_str(), NULL, NULL);
387}
388
389void
390CivetServer::close()
391{
392 if (context) {
393 mg_stop(context);
394 context = 0;
395 }
396}
397
398int
399CivetServer::getCookie(struct mg_connection *conn,
400 const std::string &cookieName,
401 std::string &cookieValue)
402{
403 // Maximum cookie length as per microsoft is 4096.
404 // http://msdn.microsoft.com/en-us/library/ms178194.aspx
405 char _cookieValue[4096];
406 const char *cookie = mg_get_header(conn, "Cookie");
407 int lRead = mg_get_cookie(cookie,
408 cookieName.c_str(),
409 _cookieValue,
410 sizeof(_cookieValue));
411 cookieValue.clear();
412 cookieValue.append(_cookieValue);
413 return lRead;
414}
415
416const char *
417CivetServer::getHeader(struct mg_connection *conn,
418 const std::string &headerName)
419{
420 return mg_get_header(conn, headerName.c_str());
421}
422
423void
424CivetServer::urlDecode(const char *src,
425 std::string &dst,
426 bool is_form_url_encoded)
427{
428 urlDecode(src, strlen(src), dst, is_form_url_encoded);
429}
430
431void
432CivetServer::urlDecode(const char *src,
433 size_t src_len,
434 std::string &dst,
435 bool is_form_url_encoded)
436{
437 int i, j, a, b;
438#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
439
440 dst.clear();
441 for (i = j = 0; i < (int)src_len; i++, j++) {
442 if (i < (int)src_len - 2 && src[i] == '%'
443 && isxdigit(*(const unsigned char *)(src + i + 1))
444 && isxdigit(*(const unsigned char *)(src + i + 2))) {
445 a = tolower(*(const unsigned char *)(src + i + 1));
446 b = tolower(*(const unsigned char *)(src + i + 2));
447 dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));
448 i += 2;
449 } else if (is_form_url_encoded && src[i] == '+') {
450 dst.push_back(' ');
451 } else {
452 dst.push_back(src[i]);
453 }
454 }
455}
456
457bool
458CivetServer::getParam(struct mg_connection *conn,
459 const char *name,
460 std::string &dst,
461 size_t occurrence)
462{
463 const char *formParams = NULL;
464 const struct mg_request_info *ri = mg_get_request_info(conn);
465 assert(ri != NULL);
466 CivetServer *me = (CivetServer *)(ri->user_data);
467 assert(me != NULL);
468 mg_lock_context(me->context);
469 CivetConnection &conobj = me->connections[conn];
470 mg_lock_connection(conn);
471 mg_unlock_context(me->context);
472
473 if (conobj.postData != NULL) {
474 formParams = conobj.postData;
475 } else {
476 const char *con_len_str = mg_get_header(conn, "Content-Length");
477 if (con_len_str) {
478 unsigned long con_len = atoi(con_len_str);
479 if (con_len > 0) {
480 // Add one extra character: in case the post-data is a text, it
481 // is required as 0-termination.
482 // Do not increment con_len, since the 0 terminating is not part
483 // of the content (text or binary).
484 conobj.postData = (char *)malloc(con_len + 1);
485 if (conobj.postData != NULL) {
486 // malloc may fail for huge requests
487 mg_read(conn, conobj.postData, con_len);
488 conobj.postData[con_len] = 0;
489 formParams = conobj.postData;
490 conobj.postDataLen = con_len;
491 }
492 }
493 }
494 }
495 if (formParams == NULL) {
496 // get requests do store html <form> field values in the http
497 // query_string
498 formParams = ri->query_string;
499 }
500 mg_unlock_connection(conn);
501
502 if (formParams != NULL) {
503 return getParam(formParams, strlen(formParams), name, dst, occurrence);
504 }
505
506 return false;
507}
508
509bool
510CivetServer::getParam(const char *data,
511 size_t data_len,
512 const char *name,
513 std::string &dst,
514 size_t occurrence)
515{
516 const char *p, *e, *s;
517 size_t name_len;
518
519 dst.clear();
520 if (data == NULL || name == NULL || data_len == 0) {
521 return false;
522 }
523 name_len = strlen(name);
524 e = data + data_len;
525
526 // data is "var1=val1&var2=val2...". Find variable first
527 for (p = data; p + name_len < e; p++) {
528 if ((p == data || p[-1] == '&') && p[name_len] == '='
529 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
530
531 // Point p to variable value
532 p += name_len + 1;
533
534 // Point s to the end of the value
535 s = (const char *)memchr(p, '&', (size_t)(e - p));
536 if (s == NULL) {
537 s = e;
538 }
539 assert(s >= p);
540
541 // Decode variable into destination buffer
542 urlDecode(p, (int)(s - p), dst, true);
543 return true;
544 }
545 }
546 return false;
547}
548
549void
550CivetServer::urlEncode(const char *src, std::string &dst, bool append)
551{
552 urlEncode(src, strlen(src), dst, append);
553}
554
555void
556CivetServer::urlEncode(const char *src,
557 size_t src_len,
558 std::string &dst,
559 bool append)
560{
561 static const char *dont_escape = "._-$,;~()";
562 static const char *hex = "0123456789abcdef";
563
564 if (!append)
565 dst.clear();
566
567 for (; src_len > 0; src++, src_len--) {
568 if (isalnum(*(const unsigned char *)src)
569 || strchr(dont_escape, *(const unsigned char *)src) != NULL) {
570 dst.push_back(*src);
571 } else {
572 dst.push_back('%');
573 dst.push_back(hex[(*(const unsigned char *)src) >> 4]);
574 dst.push_back(hex[(*(const unsigned char *)src) & 0xf]);
575 }
576 }
577}
578
579std::vector<int>
580CivetServer::getListeningPorts()
581{
11fdf7f2
TL
582 std::vector<int> ports(50);
583 std::vector<struct mg_server_ports> server_ports(50);
584 int size = mg_get_server_ports(context,
585 (int)server_ports.size(),
586 &server_ports[0]);
587 if (size <= 0) {
588 ports.resize(0);
589 return ports;
590 }
7c673cae 591 ports.resize(size);
11fdf7f2
TL
592 server_ports.resize(size);
593 for (int i = 0; i < size; i++) {
594 ports[i] = server_ports[i].port;
595 }
596
7c673cae
FG
597 return ports;
598}
599
600CivetServer::CivetConnection::CivetConnection()
601{
602 postData = NULL;
603 postDataLen = 0;
604}
605
606CivetServer::CivetConnection::~CivetConnection()
607{
608 free(postData);
609}