BellHTTPServer.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include "BellHTTPServer.h"
  2. #include <string.h> // for memcpy
  3. #include <cassert> // for assert
  4. #include <exception> // for exception
  5. #include <mutex> // for scoped_lock
  6. #include <regex> // for sregex_token_iterator, regex
  7. #include "BellLogger.h" // for AbstractLogger, BELL_LOG, bell
  8. #include "CivetServer.h" // for CivetServer, CivetWebSocketHandler
  9. #include "civetweb.h" // for mg_get_request_info, mg_printf, mg_set_user...
  10. using namespace bell;
  11. class WebSocketHandler : public CivetWebSocketHandler {
  12. public:
  13. BellHTTPServer::WSDataHandler dataHandler;
  14. BellHTTPServer::WSStateHandler stateHandler;
  15. WebSocketHandler(BellHTTPServer::WSDataHandler dataHandler,
  16. BellHTTPServer::WSStateHandler stateHandler) {
  17. this->dataHandler = dataHandler;
  18. this->stateHandler = stateHandler;
  19. }
  20. virtual bool handleConnection(CivetServer* server,
  21. struct mg_connection* conn) {
  22. this->stateHandler(conn, BellHTTPServer::WSState::CONNECTED);
  23. return true;
  24. }
  25. virtual void handleReadyState(CivetServer* server,
  26. struct mg_connection* conn) {
  27. this->stateHandler(conn, BellHTTPServer::WSState::READY);
  28. }
  29. virtual bool handleData(CivetServer* server, struct mg_connection* conn,
  30. int bits, char* data, size_t data_len) {
  31. this->dataHandler(conn, data, data_len);
  32. return true;
  33. }
  34. virtual void handleClose(CivetServer* server, struct mg_connection* conn) {
  35. stateHandler(conn, BellHTTPServer::WSState::CLOSED);
  36. }
  37. };
  38. std::vector<std::string> BellHTTPServer::Router::split(
  39. const std::string str, const std::string regex_str) {
  40. std::regex regexz(regex_str);
  41. return {std::sregex_token_iterator(str.begin(), str.end(), regexz, -1),
  42. std::sregex_token_iterator()};
  43. }
  44. void BellHTTPServer::Router::insert(const std::string& route,
  45. HTTPHandler& value) {
  46. auto parts = split(route, "/");
  47. auto currentNode = &root;
  48. for (int index = 0; index < parts.size(); index++) {
  49. auto part = parts[index];
  50. if (part[0] == ':') {
  51. currentNode->isParam = true;
  52. currentNode->paramName = part.substr(1);
  53. part = "";
  54. } else if (part[0] == '*') {
  55. currentNode->isCatchAll = true;
  56. currentNode->value = value;
  57. return;
  58. }
  59. if (!currentNode->children.count(part)) {
  60. currentNode->children[part] = std::make_unique<RouterNode>();
  61. }
  62. currentNode = currentNode->children[part].get();
  63. }
  64. currentNode->value = value;
  65. }
  66. BellHTTPServer::Router::HandlerAndParams BellHTTPServer::Router::find(
  67. const std::string& route) {
  68. auto parts = split(route, "/");
  69. auto currentNode = &root;
  70. std::unordered_map<std::string, std::string> params;
  71. for (int index = 0; index < parts.size(); index++) {
  72. auto part = parts[index];
  73. if (currentNode->children.count(part)) {
  74. currentNode = currentNode->children[part].get();
  75. } else if (currentNode->isParam) {
  76. params[currentNode->paramName] = part;
  77. if (currentNode->children.count("")) {
  78. currentNode = currentNode->children[""].get();
  79. } else {
  80. return {nullptr, Params()};
  81. }
  82. } else if (currentNode->isCatchAll) {
  83. params["**"] = '*';
  84. return {currentNode->value, params};
  85. } else {
  86. return {nullptr, Params()};
  87. }
  88. }
  89. if (currentNode->value != nullptr) {
  90. return {currentNode->value, params};
  91. }
  92. return {nullptr, Params()};
  93. }
  94. bool BellHTTPServer::handleGet(CivetServer* server,
  95. struct mg_connection* conn) {
  96. std::scoped_lock lock(this->responseMutex);
  97. auto requestInfo = mg_get_request_info(conn);
  98. auto handler = getRequestsRouter.find(requestInfo->local_uri);
  99. if (handler.first == nullptr) {
  100. if (this->notFoundHandler != nullptr) {
  101. this->notFoundHandler(conn);
  102. return true;
  103. }
  104. return false;
  105. }
  106. mg_set_user_connection_data(conn, &handler.second);
  107. try {
  108. auto reply = handler.first(conn);
  109. if (reply->body == nullptr) {
  110. return true;
  111. }
  112. mg_printf(
  113. conn,
  114. "HTTP/1.1 %d OK\r\nContent-Type: "
  115. "%s\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n",
  116. reply->status, reply->headers["Content-Type"].c_str());
  117. mg_write(conn, reply->body, reply->bodySize);
  118. return true;
  119. } catch (std::exception& e) {
  120. BELL_LOG(error, "HttpServer", "Exception occured in handler: %s", e.what());
  121. return false;
  122. }
  123. }
  124. bool BellHTTPServer::handlePost(CivetServer* server,
  125. struct mg_connection* conn) {
  126. std::scoped_lock lock(this->responseMutex);
  127. auto requestInfo = mg_get_request_info(conn);
  128. auto handler = postRequestsRouter.find(requestInfo->local_uri);
  129. if (handler.first == nullptr) {
  130. return false;
  131. }
  132. mg_set_user_connection_data(conn, &handler.second);
  133. try {
  134. auto reply = handler.first(conn);
  135. if (reply->body == nullptr) {
  136. return true;
  137. }
  138. mg_printf(
  139. conn,
  140. "HTTP/1.1 %d OK\r\nContent-Type: "
  141. "%s\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n",
  142. reply->status, reply->headers["Content-Type"].c_str());
  143. mg_write(conn, reply->body, reply->bodySize);
  144. return true;
  145. } catch (std::exception& e) {
  146. BELL_LOG(error, "HttpServer", "Exception occured in handler: %s", e.what());
  147. return false;
  148. }
  149. }
  150. BellHTTPServer::BellHTTPServer(int serverPort) {
  151. mg_init_library(0);
  152. BELL_LOG(info, "HttpServer", "Server listening on port %d", serverPort);
  153. this->serverPort = serverPort;
  154. auto port = std::to_string(this->serverPort);
  155. const char* options[] = {"listening_ports", port.c_str(), 0};
  156. server = std::make_unique<CivetServer>(options);
  157. }
  158. std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeJsonResponse(
  159. const std::string& json, int status) {
  160. auto response = std::make_unique<BellHTTPServer::HTTPResponse>();
  161. response->body = (uint8_t*)malloc(json.size());
  162. response->bodySize = json.size();
  163. response->headers["Content-Type"] = "application/json";
  164. response->status = status;
  165. memcpy(response->body, json.c_str(), json.size());
  166. return response;
  167. }
  168. std::unique_ptr<BellHTTPServer::HTTPResponse>
  169. BellHTTPServer::makeEmptyResponse() {
  170. auto response = std::make_unique<BellHTTPServer::HTTPResponse>();
  171. return response;
  172. }
  173. void BellHTTPServer::registerGet(const std::string& url,
  174. BellHTTPServer::HTTPHandler handler) {
  175. server->addHandler(url, this);
  176. getRequestsRouter.insert(url, handler);
  177. }
  178. void BellHTTPServer::registerPost(const std::string& url,
  179. BellHTTPServer::HTTPHandler handler) {
  180. server->addHandler(url, this);
  181. postRequestsRouter.insert(url, handler);
  182. }
  183. void BellHTTPServer::registerWS(const std::string& url,
  184. BellHTTPServer::WSDataHandler dataHandler,
  185. BellHTTPServer::WSStateHandler stateHandler) {
  186. server->addWebSocketHandler(url,
  187. new WebSocketHandler(dataHandler, stateHandler));
  188. }
  189. void BellHTTPServer::registerNotFound(HTTPHandler handler) {
  190. this->notFoundHandler = handler;
  191. }
  192. std::unordered_map<std::string, std::string> BellHTTPServer::extractParams(
  193. struct mg_connection* conn) {
  194. void* data = mg_get_user_connection_data(conn);
  195. assert(data != nullptr);
  196. std::unordered_map<std::string, std::string>& params =
  197. *(std::unordered_map<std::string, std::string>*)data;
  198. return params;
  199. }