void HttpServerLog::Append(rs::httpserver::socket_ptr socket, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response, const std::time_t& start, long duration) { int year, month, day, hour, min, sec; GetTimestamp(start, year, month, day, hour, min, sec); const auto queryString = request->getHeaders()->getQueryString(); const auto localAddr = socket->getLocalEndpoint().address().to_string(); const auto remoteAddr = socket->getRemoteEndpoint().address().to_string(); auto index = writeRowIndex++; index %= maxLogRows; // TODO: optimize the string formatting to reduce ad-hoc allocations and re-use already known string values (dates, etc.) logRows[index].status = response->getStatusCode(); std::snprintf(logRows[index].data, sizeof(logRows[0].data), "%04d-%02d-%02d %02d:%02d:%02dZ %s %s %s %s %u %s %s %d %u", year, month, day, hour, min, sec, localAddr.c_str(), request->getMethod().c_str(), request->getUri().c_str(), queryString.size() > 0 ? queryString.c_str() : "-", socket->getLocalEndpoint().port(), remoteAddr.c_str(), boost::replace_all_copy(request->getHeaders()->getUserAgent(), " ", "+").c_str(), response->getStatusCode(), duration); }
void HttpServer::HandleUtilsRequest(rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) { auto uri = request->getUri(); if (uri == "/_utils" || uri == "/_utils/") { response->Redirect("/_utils/index.html"); } else { auto contentType = rs::httpserver::MimeTypes::GetType(uri); if (contentType) { uri = "www" + uri; rs::httpserver::FileStream stream(uri); if (stream) { auto lastModifiedTime = stream.getLastModifiedTime(); if (lastModifiedTime) { auto etag = boost::lexical_cast<std::string>(lastModifiedTime.get()); if (etag == request->getIfNoneMatch()) { response->setStatusCode(304).setStatusDescription("Not Modified").Send(); } else { response->setETag(etag); } } if (!response->HasResponded()) { response->setContentType(contentType.get()).Send(stream); } } } } }
void RestServer::RouteRequest(rs::httpserver::socket_ptr, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) { router_.Match(request, response); if (!response->HasResponded()) { response->setContentType(ContentTypes::textPlain).setStatusCode(404).setStatusDescription("Not Found").Send(R"({"error":"not_found","reason":"no_db_file"})"); } } bool RestServer::GetActiveTasks(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) { response->setContentType(ContentTypes::applicationJson).Send("[]"); return true; } bool RestServer::GetSession(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) { response->setContentType(ContentTypes::applicationJson).Send(R"({"ok":true,"userCtx":{"name":null,"roles":["_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["oauth","cookie","default"],"authenticated":"default"}})"); return true; } bool RestServer::GetAllDbs(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) { auto dbs = databases_.GetDatabases(); JsonStream stream{JsonStream::ContextType::Array}; for (int i = 0; i < dbs.size(); ++i) { stream.Append(dbs[i]); } response->setContentType(ContentTypes::applicationJson).Send(stream.Flush()); return true; }
void HttpServer::RequestCallback(rs::httpserver::socket_ptr socket, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) { auto start = boost::chrono::system_clock::now(); try { if (request->getUri().find("/_utils") == 0) { HandleUtilsRequest(request, response); if (!response->HasResponded()) { response->setStatusCode(404).setStatusDescription("Not Found").Send(); } } else { rest_.RouteRequest(socket, request, response); } } catch (const HttpServerException& ex) { if (!response->HasResponded()) { try { response->setContentType(ex.ContentType()).setStatusCode(ex.StatusCode()).setStatusDescription(ex.Description()).Send(ex.Body()); } catch (...) {} } } catch (const boost::exception& ex) { if (!response->HasResponded()) { InternalErrorResponse(socket, request, response); } } catch (const std::exception& ex) { if (!response->HasResponded()) { InternalErrorResponse(socket, request, response); } } if (response->HasResponded()) { auto duration = boost::chrono::system_clock::now() - start; auto durationMS = boost::chrono::duration_cast<boost::chrono::milliseconds>(duration); HttpServerLog::Append(socket, request, response, boost::chrono::system_clock::to_time_t(start), durationMS.count()); } }
void HttpServer::InternalErrorResponse(rs::httpserver::socket_ptr socket, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) noexcept { try { response->setContentType("text/plain").setStatusCode(500).setStatusDescription("Internal Server Error").Send(); } catch (...) {} }