diff options
| author | Per Larsson <[email protected]> | 2021-12-09 17:01:57 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2021-12-09 17:01:57 +0100 |
| commit | 20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6 (patch) | |
| tree | fbb0f274840a12c32d93c7e342c0427f2617a651 /zenhttp/httpasio.cpp | |
| parent | Disabled cache tracker. (diff) | |
| parent | Return status_code as ErrorCode from jupiter api if not successful (diff) | |
| download | zen-20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6.tar.xz zen-20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6.zip | |
Merged main.
Diffstat (limited to 'zenhttp/httpasio.cpp')
| -rw-r--r-- | zenhttp/httpasio.cpp | 99 |
1 files changed, 84 insertions, 15 deletions
diff --git a/zenhttp/httpasio.cpp b/zenhttp/httpasio.cpp index d5fe9adbb..7ee7193d1 100644 --- a/zenhttp/httpasio.cpp +++ b/zenhttp/httpasio.cpp @@ -15,6 +15,14 @@ ZEN_THIRD_PARTY_INCLUDES_START #include <asio.hpp> ZEN_THIRD_PARTY_INCLUDES_END +#define ASIO_VERBOSE_TRACE 0 + +#if ASIO_VERBOSE_TRACE +#define ZEN_TRACE_VERBOSE ZEN_TRACE +#else +#define ZEN_TRACE_VERBOSE(fmtstr, ...) +#endif + namespace zen::asio_http { using namespace std::literals; @@ -114,7 +122,7 @@ struct HttpRequest HttpVerb RequestVerb() const { return m_RequestVerb; } bool IsKeepAlive() const { return m_KeepAlive; } - std::string_view Url() const { return std::string_view(m_Url, m_UrlLength); } + std::string_view Url() const { return m_NormalizedUrl.empty() ? std::string_view(m_Url, m_UrlLength) : m_NormalizedUrl; } std::string_view QueryString() const { return std::string_view(m_QueryString, m_QueryLength); } IoBuffer Body() { return m_BodyBuffer; } @@ -171,6 +179,7 @@ private: uint64_t m_BodyPosition = 0; http_parser m_Parser; char m_HeaderBuffer[1024]; + std::string m_NormalizedUrl; void AppendInputBytes(const char* Data, size_t Bytes); void AppendCurrentHeader(); @@ -318,6 +327,7 @@ private: std::unique_ptr<asio::ip::tcp::socket> m_Socket; std::atomic<uint32_t> m_RequestCounter{0}; uint32_t m_ConnectionId = 0; + Ref<IHttpPackageHandler> m_PackageHandler; RwLock m_ResponsesLock; std::deque<std::unique_ptr<HttpResponse>> m_Responses; @@ -330,12 +340,12 @@ HttpServerConnection::HttpServerConnection(HttpAsioServerImpl& Server, std::uniq , m_Socket(std::move(Socket)) , m_ConnectionId(g_ConnectionIdCounter.fetch_add(1)) { - ZEN_TRACE("new connection #{}", m_ConnectionId); + ZEN_TRACE_VERBOSE("new connection #{}", m_ConnectionId); } HttpServerConnection::~HttpServerConnection() { - ZEN_TRACE("destroying connection #{}", m_ConnectionId); + ZEN_TRACE_VERBOSE("destroying connection #{}", m_ConnectionId); } void @@ -371,18 +381,18 @@ HttpServerConnection::EnqueueRead() asio::async_read(*m_Socket.get(), m_RequestBuffer, - asio::transfer_at_least(16), + asio::transfer_at_least(1), [Conn = AsSharedPtr()](const asio::error_code& Ec, std::size_t ByteCount) { Conn->OnDataReceived(Ec, ByteCount); }); } void -HttpServerConnection::OnDataReceived(const asio::error_code& Ec, std::size_t ByteCount) +HttpServerConnection::OnDataReceived(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount) { if (Ec) { if (m_RequestState == RequestState::kDone || m_RequestState == RequestState::kInitialRead) { - ZEN_TRACE("on data received ERROR (EXPECTED), connection '{}' reason '{}'", m_ConnectionId, Ec.message()); + ZEN_TRACE_VERBOSE("on data received ERROR (EXPECTED), connection '{}' reason '{}'", m_ConnectionId, Ec.message()); return; } else @@ -392,7 +402,7 @@ HttpServerConnection::OnDataReceived(const asio::error_code& Ec, std::size_t Byt } } - ZEN_TRACE("on data received, connection '{}', request '{}', thread '{}', bytes '{}'", + ZEN_TRACE_VERBOSE("on data received, connection '{}', request '{}', thread '{}', bytes '{}'", m_ConnectionId, m_RequestCounter.load(std::memory_order_relaxed), GetCurrentThreadId(), @@ -421,7 +431,7 @@ HttpServerConnection::OnDataReceived(const asio::error_code& Ec, std::size_t Byt } void -HttpServerConnection::OnResponseDataSent(const asio::error_code& Ec, std::size_t ByteCount, bool Pop) +HttpServerConnection::OnResponseDataSent(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount, bool Pop) { if (Ec) { @@ -430,7 +440,7 @@ HttpServerConnection::OnResponseDataSent(const asio::error_code& Ec, std::size_t } else { - ZEN_TRACE("on data sent, connection '{}', request '{}', thread '{}', bytes '{}'", + ZEN_TRACE_VERBOSE("on data sent, connection '{}', request '{}', thread '{}', bytes '{}'", m_ConnectionId, m_RequestCounter.load(std::memory_order_relaxed), GetCurrentThreadId(), @@ -485,9 +495,23 @@ HttpServerConnection::HandleRequest() { HttpAsioServerRequest Request(m_RequestData, *Service, m_RequestData.Body()); - ZEN_TRACE("handle request, connection '{}' request '{}'", m_ConnectionId, m_RequestCounter.load(std::memory_order_relaxed)); - Service->HandleRequest(Request); + ZEN_TRACE_VERBOSE("handle request, connection '{}' request '{}'", m_ConnectionId, m_RequestCounter.load(std::memory_order_relaxed)); + + if (!HandlePackageOffers(*Service, Request, m_PackageHandler)) + { + try + { + Service->HandleRequest(Request); + } + catch (std::exception& ex) + { + ZEN_ERROR("Caught exception while handling request: '{}'", ex.what()); + + Request.WriteResponse(HttpResponseCode::InternalServerError, HttpContentType::kText, ex.what()); + } + + } if (std::unique_ptr<HttpResponse> Response = std::move(Request.m_Response)) { @@ -737,6 +761,37 @@ HttpRequest::TerminateConnection() m_Connection.TerminateConnection(); } +static void +NormalizeUrlPath(const char* Url, size_t UrlLength, std::string& NormalizedUrl) +{ + bool LastCharWasSeparator = false; + for (std::string_view::size_type UrlIndex = 0; UrlIndex < UrlLength; ++UrlIndex) + { + const char UrlChar = Url[UrlIndex]; + const bool IsSeparator = (UrlChar == '/'); + + if (IsSeparator && LastCharWasSeparator) + { + if (NormalizedUrl.empty()) + { + NormalizedUrl.reserve(UrlLength); + NormalizedUrl.append(Url, UrlIndex); + } + + if (!LastCharWasSeparator) + { + NormalizedUrl.push_back('/'); + } + } + else if (!NormalizedUrl.empty()) + { + NormalizedUrl.push_back(UrlChar); + } + + LastCharWasSeparator = IsSeparator; + } +} + int HttpRequest::OnHeadersComplete() { @@ -803,6 +858,9 @@ HttpRequest::OnHeadersComplete() m_QueryLength = Url.size() - QuerySplit - 1; } + NormalizeUrlPath(m_Url, m_UrlLength, m_NormalizedUrl); + + return 0; } @@ -843,6 +901,7 @@ HttpRequest::ResetState() m_BodyBuffer = {}; m_BodyPosition = 0; m_Headers.clear(); + m_NormalizedUrl.clear(); } int @@ -935,7 +994,7 @@ HttpAsioServerRequest::HttpAsioServerRequest(asio_http::HttpRequest& Request, Ht const int PrefixLength = Service.UriPrefixLength(); std::string_view Uri = Request.Url(); - Uri.remove_prefix(PrefixLength); + Uri.remove_prefix(std::min(PrefixLength, static_cast<int>(Uri.size()))); m_Uri = Uri; m_QueryString = Request.QueryString(); @@ -1099,6 +1158,10 @@ HttpAsioServerImpl::RegisterService(const char* InUrlPath, HttpService& Service) { std::string_view UrlPath(InUrlPath); Service.SetUriPrefixLength(UrlPath.size()); + if (!UrlPath.empty() && UrlPath.back() == '/') + { + UrlPath.remove_suffix(1); + } RwLock::ExclusiveLockScope _(m_Lock); m_UriHandlers.push_back({std::string(UrlPath), &Service}); @@ -1109,16 +1172,22 @@ HttpAsioServerImpl::RouteRequest(std::string_view Url) { RwLock::SharedLockScope _(m_Lock); + HttpService* CandidateService = nullptr; + std::string::size_type CandidateMatchSize = 0; for (const ServiceEntry& SvcEntry : m_UriHandlers) { const std::string& SvcUrl = SvcEntry.ServiceUrlPath; - if (Url.compare(0, SvcUrl.size(), SvcUrl) == 0) + const std::string::size_type SvcUrlSize = SvcUrl.size(); + if ((SvcUrlSize >= CandidateMatchSize) && + Url.compare(0, SvcUrlSize, SvcUrl) == 0 && + ((SvcUrlSize == Url.size()) || (Url[SvcUrlSize] == '/'))) { - return SvcEntry.Service; + CandidateMatchSize = SvcUrl.size(); + CandidateService = SvcEntry.Service; } } - return nullptr; + return CandidateService; } } // namespace zen::asio_http |