aboutsummaryrefslogtreecommitdiff
path: root/zenhttp/httpasio.cpp
diff options
context:
space:
mode:
authorPer Larsson <[email protected]>2021-12-09 17:01:57 +0100
committerPer Larsson <[email protected]>2021-12-09 17:01:57 +0100
commit20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6 (patch)
treefbb0f274840a12c32d93c7e342c0427f2617a651 /zenhttp/httpasio.cpp
parentDisabled cache tracker. (diff)
parentReturn status_code as ErrorCode from jupiter api if not successful (diff)
downloadzen-20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6.tar.xz
zen-20f3c16b0012cfb8ce7bf9b6dd06a2720b6885c6.zip
Merged main.
Diffstat (limited to 'zenhttp/httpasio.cpp')
-rw-r--r--zenhttp/httpasio.cpp99
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