1 /* Copyright (c) 2013-2020 the Civetweb developers
2 * Copyright (c) 2013 No Face Press, LLC
4 * License http://opensource.org/licenses/mit-license.php MIT License
7 #include "CivetServer.h"
13 #ifndef UNUSED_PARAMETER
14 #define UNUSED_PARAMETER(x) (void)(x)
17 #ifndef MAX_PARAM_BODY_LENGTH
18 // Set a default limit for parameters in a form body: 2 MB
19 #define MAX_PARAM_BODY_LENGTH (1024 * 1024 * 2)
23 CivetHandler::handleGet(CivetServer
*server
, struct mg_connection
*conn
)
25 UNUSED_PARAMETER(server
);
26 UNUSED_PARAMETER(conn
);
31 CivetHandler::handleGet(CivetServer
*server
,
32 struct mg_connection
*conn
,
35 UNUSED_PARAMETER(server
);
36 UNUSED_PARAMETER(conn
);
44 CivetHandler::handlePost(CivetServer
*server
, struct mg_connection
*conn
)
46 UNUSED_PARAMETER(server
);
47 UNUSED_PARAMETER(conn
);
52 CivetHandler::handlePost(CivetServer
*server
,
53 struct mg_connection
*conn
,
56 UNUSED_PARAMETER(server
);
57 UNUSED_PARAMETER(conn
);
65 CivetHandler::handleHead(CivetServer
*server
, struct mg_connection
*conn
)
67 UNUSED_PARAMETER(server
);
68 UNUSED_PARAMETER(conn
);
73 CivetHandler::handleHead(CivetServer
*server
,
74 struct mg_connection
*conn
,
77 UNUSED_PARAMETER(server
);
78 UNUSED_PARAMETER(conn
);
86 CivetHandler::handlePut(CivetServer
*server
, struct mg_connection
*conn
)
88 UNUSED_PARAMETER(server
);
89 UNUSED_PARAMETER(conn
);
94 CivetHandler::handlePut(CivetServer
*server
,
95 struct mg_connection
*conn
,
98 UNUSED_PARAMETER(server
);
99 UNUSED_PARAMETER(conn
);
107 CivetHandler::handlePatch(CivetServer
*server
, struct mg_connection
*conn
)
109 UNUSED_PARAMETER(server
);
110 UNUSED_PARAMETER(conn
);
115 CivetHandler::handlePatch(CivetServer
*server
,
116 struct mg_connection
*conn
,
119 UNUSED_PARAMETER(server
);
120 UNUSED_PARAMETER(conn
);
128 CivetHandler::handleDelete(CivetServer
*server
, struct mg_connection
*conn
)
130 UNUSED_PARAMETER(server
);
131 UNUSED_PARAMETER(conn
);
136 CivetHandler::handleDelete(CivetServer
*server
,
137 struct mg_connection
*conn
,
140 UNUSED_PARAMETER(server
);
141 UNUSED_PARAMETER(conn
);
149 CivetHandler::handleOptions(CivetServer
*server
, struct mg_connection
*conn
)
151 UNUSED_PARAMETER(server
);
152 UNUSED_PARAMETER(conn
);
157 CivetHandler::handleOptions(CivetServer
*server
,
158 struct mg_connection
*conn
,
161 UNUSED_PARAMETER(server
);
162 UNUSED_PARAMETER(conn
);
170 CivetWebSocketHandler::handleConnection(CivetServer
*server
,
171 const struct mg_connection
*conn
)
173 UNUSED_PARAMETER(server
);
174 UNUSED_PARAMETER(conn
);
179 CivetWebSocketHandler::handleReadyState(CivetServer
*server
,
180 struct mg_connection
*conn
)
182 UNUSED_PARAMETER(server
);
183 UNUSED_PARAMETER(conn
);
188 CivetWebSocketHandler::handleData(CivetServer
*server
,
189 struct mg_connection
*conn
,
194 UNUSED_PARAMETER(server
);
195 UNUSED_PARAMETER(conn
);
196 UNUSED_PARAMETER(bits
);
197 UNUSED_PARAMETER(data
);
198 UNUSED_PARAMETER(data_len
);
203 CivetWebSocketHandler::handleClose(CivetServer
*server
,
204 const struct mg_connection
*conn
)
206 UNUSED_PARAMETER(server
);
207 UNUSED_PARAMETER(conn
);
212 CivetServer::requestHandler(struct mg_connection
*conn
, void *cbdata
)
214 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
215 assert(request_info
!= NULL
);
216 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
218 int http_status_code
= -1;
219 bool status_ok
= false;
221 // Happens when a request hits the server before the context is saved
222 if (me
->context
== NULL
)
225 mg_lock_context(me
->context
);
226 me
->connections
[conn
] = CivetConnection();
227 mg_unlock_context(me
->context
);
229 CivetHandler
*handler
= (CivetHandler
*)cbdata
;
232 if (strcmp(request_info
->request_method
, "GET") == 0) {
233 status_ok
= handler
->handleGet(me
, conn
, &http_status_code
);
234 if (http_status_code
< 0) {
235 status_ok
= handler
->handleGet(me
, conn
);
237 } else if (strcmp(request_info
->request_method
, "POST") == 0) {
238 status_ok
= handler
->handlePost(me
, conn
, &http_status_code
);
239 if (http_status_code
< 0) {
240 status_ok
= handler
->handlePost(me
, conn
);
242 } else if (strcmp(request_info
->request_method
, "HEAD") == 0) {
243 status_ok
= handler
->handleHead(me
, conn
, &http_status_code
);
244 if (http_status_code
< 0) {
245 status_ok
= handler
->handleHead(me
, conn
);
247 } else if (strcmp(request_info
->request_method
, "PUT") == 0) {
248 status_ok
= handler
->handlePut(me
, conn
, &http_status_code
);
249 if (http_status_code
< 0) {
250 status_ok
= handler
->handlePut(me
, conn
);
252 } else if (strcmp(request_info
->request_method
, "DELETE") == 0) {
253 status_ok
= handler
->handleDelete(me
, conn
, &http_status_code
);
254 if (http_status_code
< 0) {
255 status_ok
= handler
->handleDelete(me
, conn
);
257 } else if (strcmp(request_info
->request_method
, "OPTIONS") == 0) {
258 status_ok
= handler
->handleOptions(me
, conn
, &http_status_code
);
259 if (http_status_code
< 0) {
260 status_ok
= handler
->handleOptions(me
, conn
);
262 } else if (strcmp(request_info
->request_method
, "PATCH") == 0) {
263 status_ok
= handler
->handlePatch(me
, conn
, &http_status_code
);
264 if (http_status_code
< 0) {
265 status_ok
= handler
->handlePatch(me
, conn
);
270 if (http_status_code
< 0) {
271 http_status_code
= status_ok
? 1 : 0;
274 return http_status_code
;
278 CivetServer::authHandler(struct mg_connection
*conn
, void *cbdata
)
280 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
281 assert(request_info
!= NULL
);
282 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
285 // Happens when a request hits the server before the context is saved
286 if (me
->context
== NULL
)
289 mg_lock_context(me
->context
);
290 me
->connections
[conn
] = CivetConnection();
291 mg_unlock_context(me
->context
);
293 CivetAuthHandler
*handler
= (CivetAuthHandler
*)cbdata
;
296 return handler
->authorize(me
, conn
) ? 1 : 0;
299 return 0; // No handler found
303 CivetServer::webSocketConnectionHandler(const struct mg_connection
*conn
,
306 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
307 assert(request_info
!= NULL
);
308 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
311 // Happens when a request hits the server before the context is saved
312 if (me
->context
== NULL
)
315 CivetWebSocketHandler
*handler
= (CivetWebSocketHandler
*)cbdata
;
318 return handler
->handleConnection(me
, conn
) ? 0 : 1;
321 return 1; // No handler found, close connection
325 CivetServer::webSocketReadyHandler(struct mg_connection
*conn
, void *cbdata
)
327 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
328 assert(request_info
!= NULL
);
329 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
332 // Happens when a request hits the server before the context is saved
333 if (me
->context
== NULL
)
336 CivetWebSocketHandler
*handler
= (CivetWebSocketHandler
*)cbdata
;
339 handler
->handleReadyState(me
, conn
);
344 CivetServer::webSocketDataHandler(struct mg_connection
*conn
,
350 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
351 assert(request_info
!= NULL
);
352 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
355 // Happens when a request hits the server before the context is saved
356 if (me
->context
== NULL
)
359 CivetWebSocketHandler
*handler
= (CivetWebSocketHandler
*)cbdata
;
362 return handler
->handleData(me
, conn
, bits
, data
, data_len
) ? 1 : 0;
365 return 1; // No handler found
369 CivetServer::webSocketCloseHandler(const struct mg_connection
*conn
,
372 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
373 assert(request_info
!= NULL
);
374 CivetServer
*me
= (CivetServer
*)(request_info
->user_data
);
377 // Happens when a request hits the server before the context is saved
378 if (me
->context
== NULL
)
381 CivetWebSocketHandler
*handler
= (CivetWebSocketHandler
*)cbdata
;
384 handler
->handleClose(me
, conn
);
388 CivetCallbacks::CivetCallbacks()
390 memset(this, 0, sizeof(*this));
393 CivetServer::CivetServer(const char **options
,
394 const struct CivetCallbacks
*_callbacks
,
395 const void *UserContextIn
)
398 struct CivetCallbacks callbacks
;
399 memset(&callbacks
, 0, sizeof(callbacks
));
401 UserContext
= UserContextIn
;
404 callbacks
= *_callbacks
;
405 userCloseHandler
= _callbacks
->connection_close
;
407 userCloseHandler
= NULL
;
409 callbacks
.connection_close
= closeHandler
;
410 context
= mg_start(&callbacks
, this, options
);
411 if (context
== NULL
) {
412 throw CivetException("null context when constructing CivetServer. "
413 "Possible problem binding to port.");
417 CivetServer::CivetServer(const std::vector
<std::string
> &options
,
418 const struct CivetCallbacks
*_callbacks
,
419 const void *UserContextIn
)
422 struct CivetCallbacks callbacks
;
423 memset(&callbacks
, 0, sizeof(callbacks
));
425 UserContext
= UserContextIn
;
428 callbacks
= *_callbacks
;
429 userCloseHandler
= _callbacks
->connection_close
;
431 userCloseHandler
= NULL
;
433 callbacks
.connection_close
= closeHandler
;
435 std::vector
<const char *> pointers(options
.size() + 1);
436 for (size_t i
= 0; i
< options
.size(); i
++) {
437 pointers
[i
] = (options
[i
].c_str());
439 pointers
.back() = NULL
;
441 context
= mg_start(&callbacks
, this, &pointers
[0]);
443 throw CivetException("null context when constructing CivetServer. "
444 "Possible problem binding to port.");
447 CivetServer::~CivetServer()
453 CivetServer::closeHandler(const struct mg_connection
*conn
)
455 CivetServer
*me
= (CivetServer
*)mg_get_user_data(mg_get_context(conn
));
458 // Happens when a request hits the server before the context is saved
459 if (me
->context
== NULL
)
462 if (me
->userCloseHandler
) {
463 me
->userCloseHandler(conn
);
465 mg_lock_context(me
->context
);
466 me
->connections
.erase(conn
);
467 mg_unlock_context(me
->context
);
471 CivetServer::addHandler(const std::string
&uri
, CivetHandler
*handler
)
473 mg_set_request_handler(context
, uri
.c_str(), requestHandler
, handler
);
477 CivetServer::addWebSocketHandler(const std::string
&uri
,
478 CivetWebSocketHandler
*handler
)
480 mg_set_websocket_handler(context
,
482 webSocketConnectionHandler
,
483 webSocketReadyHandler
,
484 webSocketDataHandler
,
485 webSocketCloseHandler
,
490 CivetServer::addAuthHandler(const std::string
&uri
, CivetAuthHandler
*handler
)
492 mg_set_auth_handler(context
, uri
.c_str(), authHandler
, handler
);
496 CivetServer::removeHandler(const std::string
&uri
)
498 mg_set_request_handler(context
, uri
.c_str(), NULL
, NULL
);
502 CivetServer::removeWebSocketHandler(const std::string
&uri
)
504 mg_set_websocket_handler(
505 context
, uri
.c_str(), NULL
, NULL
, NULL
, NULL
, NULL
);
509 CivetServer::removeAuthHandler(const std::string
&uri
)
511 mg_set_auth_handler(context
, uri
.c_str(), NULL
, NULL
);
524 CivetServer::getCookie(struct mg_connection
*conn
,
525 const std::string
&cookieName
,
526 std::string
&cookieValue
)
528 // Maximum cookie length as per microsoft is 4096.
529 // http://msdn.microsoft.com/en-us/library/ms178194.aspx
530 char _cookieValue
[4096];
531 const char *cookie
= mg_get_header(conn
, "Cookie");
532 int lRead
= mg_get_cookie(cookie
,
535 sizeof(_cookieValue
));
537 cookieValue
.append(_cookieValue
);
542 CivetServer::getHeader(struct mg_connection
*conn
,
543 const std::string
&headerName
)
545 return mg_get_header(conn
, headerName
.c_str());
549 CivetServer::getMethod(struct mg_connection
*conn
)
551 const struct mg_request_info
*request_info
= mg_get_request_info(conn
);
552 assert(request_info
!= NULL
);
553 return request_info
->request_method
;
557 CivetServer::urlDecode(const char *src
,
559 bool is_form_url_encoded
)
561 urlDecode(src
, strlen(src
), dst
, is_form_url_encoded
);
565 CivetServer::urlDecode(const char *src
,
568 bool is_form_url_encoded
)
570 // assign enough buffer
571 std::vector
<char> buf(src_len
+ 1);
572 int r
= mg_url_decode(src
,
573 static_cast<int>(src_len
),
575 static_cast<int>(buf
.size()),
576 is_form_url_encoded
);
579 throw std::out_of_range("");
581 // dst can contain NUL characters
582 dst
.assign(buf
.begin(), buf
.begin() + r
);
586 CivetServer::getParam(struct mg_connection
*conn
,
591 const char *formParams
= NULL
;
592 const char *queryString
= NULL
;
593 const struct mg_request_info
*ri
= mg_get_request_info(conn
);
595 CivetServer
*me
= (CivetServer
*)(ri
->user_data
);
597 mg_lock_context(me
->context
);
598 CivetConnection
&conobj
= me
->connections
[conn
];
599 mg_unlock_context(me
->context
);
601 mg_lock_connection(conn
);
602 if (conobj
.postData
.empty()) {
603 // check if there is a request body
606 int r
= mg_read(conn
, buf
, sizeof(buf
));
609 conobj
.postData
.push_back('\0');
612 || ((conobj
.postData
.size() + r
)
613 > MAX_PARAM_BODY_LENGTH
)) {
614 conobj
.postData
.assign(1, '\0');
617 conobj
.postData
.insert(conobj
.postData
.end(), buf
, buf
+ r
);
619 conobj
.postData
.clear();
624 if (!conobj
.postData
.empty()) {
625 // check if form parameter are already stored
626 formParams
= &conobj
.postData
[0];
629 if (ri
->query_string
!= NULL
) {
630 // get requests do store html <form> field values in the http
632 queryString
= ri
->query_string
;
635 mg_unlock_connection(conn
);
637 bool get_param_success
= false;
638 if (formParams
!= NULL
) {
640 getParam(formParams
, strlen(formParams
), name
, dst
, occurrence
);
642 if (!get_param_success
&& queryString
!= NULL
) {
644 getParam(queryString
, strlen(queryString
), name
, dst
, occurrence
);
647 return get_param_success
;
651 CivetServer::getParam(const char *data
,
658 int r
= mg_get_var2(data
, data_len
, name
, buf
, sizeof(buf
), occurrence
);
660 // dst can contain NUL characters
663 } else if (r
== -2) {
665 std::vector
<char> vbuf(sizeof(buf
) * 2);
668 data
, data_len
, name
, &vbuf
[0], vbuf
.size(), occurrence
);
670 dst
.assign(vbuf
.begin(), vbuf
.begin() + r
);
672 } else if (r
!= -2) {
676 vbuf
.resize(vbuf
.size() * 2);
684 CivetServer::getPostData(struct mg_connection
*conn
)
686 mg_lock_connection(conn
);
687 std::string postdata
;
689 int r
= mg_read(conn
, buf
, sizeof(buf
));
691 postdata
.append(buf
, r
);
692 r
= mg_read(conn
, buf
, sizeof(buf
));
694 mg_unlock_connection(conn
);
699 CivetServer::urlEncode(const char *src
, std::string
&dst
, bool append
)
701 urlEncode(src
, strlen(src
), dst
, append
);
705 CivetServer::urlEncode(const char *src
,
713 for (; src_len
> 0; src
++, src_len
--) {
715 // src and dst can contain NUL characters without encoding
718 char buf
[2] = {*src
, '\0'};
720 if (mg_url_encode(buf
, dst_buf
, sizeof(dst_buf
)) < 0) {
722 throw std::out_of_range("");
730 CivetServer::getListeningPorts()
732 std::vector
<struct mg_server_port
> server_ports
= getListeningPortsFull();
734 std::vector
<int> ports(server_ports
.size());
735 for (size_t i
= 0; i
< server_ports
.size(); i
++) {
736 ports
[i
] = server_ports
[i
].port
;
742 std::vector
<struct mg_server_port
>
743 CivetServer::getListeningPortsFull()
745 std::vector
<struct mg_server_port
> server_ports(8);
747 int size
= mg_get_server_ports(context
,
748 static_cast<int>(server_ports
.size()),
750 if (size
< static_cast<int>(server_ports
.size())) {
751 server_ports
.resize(size
< 0 ? 0 : size
);
754 server_ports
.resize(server_ports
.size() * 2);