mirror of
https://github.com/Icinga/icinga2.git
synced 2025-04-08 17:05:25 +02:00
Only read body from authenticated connections
This means we are not allowing unauthenticated requests anymore
This commit is contained in:
parent
81c4004894
commit
73b85bcccb
@ -25,134 +25,147 @@
|
||||
using namespace icinga;
|
||||
|
||||
HttpRequest::HttpRequest(Stream::Ptr stream)
|
||||
: Complete(false),
|
||||
: CompleteHeaders(false),
|
||||
CompleteBody(false),
|
||||
ProtocolVersion(HttpVersion11),
|
||||
Headers(new Dictionary()),
|
||||
m_Stream(std::move(stream)),
|
||||
m_State(HttpRequestStart)
|
||||
{ }
|
||||
|
||||
bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
|
||||
bool HttpRequest::ParseHeader(StreamReadContext& src, bool may_wait)
|
||||
{
|
||||
if (!m_Stream)
|
||||
return false;
|
||||
|
||||
if (m_State != HttpRequestBody) {
|
||||
String line;
|
||||
StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);
|
||||
if (m_State != HttpRequestStart && m_State != HttpRequestHeaders)
|
||||
return false;
|
||||
|
||||
if (srs != StatusNewItem) {
|
||||
if (src.Size > 512)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded"));
|
||||
String line;
|
||||
StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);
|
||||
|
||||
if (srs != StatusNewItem) {
|
||||
if (src.Size > 512)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (line.GetLength() > 512)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded"));
|
||||
|
||||
if (m_State == HttpRequestStart) {
|
||||
/* ignore trailing new-lines */
|
||||
if (line == "")
|
||||
return true;
|
||||
|
||||
std::vector<String> tokens = line.Split(" ");
|
||||
Log(LogDebug, "HttpRequest")
|
||||
<< "line: " << line << ", tokens: " << tokens.size();
|
||||
if (tokens.size() != 3)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
|
||||
|
||||
RequestMethod = tokens[0];
|
||||
RequestUrl = new class Url(tokens[1]);
|
||||
|
||||
if (tokens[2] == "HTTP/1.0")
|
||||
ProtocolVersion = HttpVersion10;
|
||||
else if (tokens[2] == "HTTP/1.1") {
|
||||
ProtocolVersion = HttpVersion11;
|
||||
} else
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));
|
||||
|
||||
m_State = HttpRequestHeaders;
|
||||
return true;
|
||||
} else { // m_State = HttpRequestHeaders
|
||||
if (line == "") {
|
||||
m_State = HttpRequestBody;
|
||||
CompleteHeaders = true;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if (Headers->GetLength() > 128)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Maximum number of HTTP request headers exceeded"));
|
||||
|
||||
String::SizeType pos = line.FindFirstOf(":");
|
||||
if (pos == String::NPos)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
|
||||
|
||||
String key = line.SubStr(0, pos).ToLower().Trim();
|
||||
String value = line.SubStr(pos + 1).Trim();
|
||||
Headers->Set(key, value);
|
||||
|
||||
if (key == "x-http-method-override")
|
||||
RequestMethod = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpRequest::ParseBody(StreamReadContext& src, bool may_wait)
|
||||
{
|
||||
if (!m_Stream || m_State != HttpRequestBody)
|
||||
return false;
|
||||
|
||||
/* we're done if the request doesn't contain a message body */
|
||||
if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding")) {
|
||||
CompleteBody = true;
|
||||
return true;
|
||||
} else if (!m_Body)
|
||||
m_Body = new FIFO();
|
||||
|
||||
if (CompleteBody)
|
||||
return true;
|
||||
|
||||
if (Headers->Get("transfer-encoding") == "chunked") {
|
||||
if (!m_ChunkContext)
|
||||
m_ChunkContext = std::make_shared<ChunkReadContext>(std::ref(src));
|
||||
|
||||
char *data;
|
||||
size_t size;
|
||||
StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);
|
||||
|
||||
if (srs != StatusNewItem)
|
||||
return false;
|
||||
|
||||
m_Body->Write(data, size);
|
||||
|
||||
delete [] data;
|
||||
|
||||
if (size == 0) {
|
||||
CompleteBody = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (src.Eof)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
|
||||
|
||||
if (src.MustRead) {
|
||||
if (!src.FillFromStream(m_Stream, false)) {
|
||||
src.Eof = true;
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
|
||||
}
|
||||
|
||||
src.MustRead = false;
|
||||
}
|
||||
|
||||
long length_indicator_signed = Convert::ToLong(Headers->Get("content-length"));
|
||||
|
||||
if (length_indicator_signed < 0)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative."));
|
||||
|
||||
size_t length_indicator = length_indicator_signed;
|
||||
|
||||
if (src.Size < length_indicator) {
|
||||
src.MustRead = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (line.GetLength() > 512)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded"));
|
||||
|
||||
if (m_State == HttpRequestStart) {
|
||||
/* ignore trailing new-lines */
|
||||
if (line == "")
|
||||
return true;
|
||||
|
||||
std::vector<String> tokens = line.Split(" ");
|
||||
Log(LogDebug, "HttpRequest")
|
||||
<< "line: " << line << ", tokens: " << tokens.size();
|
||||
if (tokens.size() != 3)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
|
||||
|
||||
RequestMethod = tokens[0];
|
||||
RequestUrl = new class Url(tokens[1]);
|
||||
|
||||
if (tokens[2] == "HTTP/1.0")
|
||||
ProtocolVersion = HttpVersion10;
|
||||
else if (tokens[2] == "HTTP/1.1") {
|
||||
ProtocolVersion = HttpVersion11;
|
||||
} else
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));
|
||||
|
||||
m_State = HttpRequestHeaders;
|
||||
} else if (m_State == HttpRequestHeaders) {
|
||||
if (line == "") {
|
||||
m_State = HttpRequestBody;
|
||||
|
||||
/* we're done if the request doesn't contain a message body */
|
||||
if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding"))
|
||||
Complete = true;
|
||||
else
|
||||
m_Body = new FIFO();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if (Headers->GetLength() > 128)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Maximum number of HTTP request headers exceeded"));
|
||||
|
||||
String::SizeType pos = line.FindFirstOf(":");
|
||||
if (pos == String::NPos)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
|
||||
|
||||
String key = line.SubStr(0, pos).ToLower().Trim();
|
||||
String value = line.SubStr(pos + 1).Trim();
|
||||
Headers->Set(key, value);
|
||||
|
||||
if (key == "x-http-method-override")
|
||||
RequestMethod = value;
|
||||
}
|
||||
} else {
|
||||
VERIFY(!"Invalid HTTP request state.");
|
||||
}
|
||||
} else if (m_State == HttpRequestBody) {
|
||||
if (Headers->Get("transfer-encoding") == "chunked") {
|
||||
if (!m_ChunkContext)
|
||||
m_ChunkContext = std::make_shared<ChunkReadContext>(std::ref(src));
|
||||
|
||||
char *data;
|
||||
size_t size;
|
||||
StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);
|
||||
|
||||
if (srs != StatusNewItem)
|
||||
return false;
|
||||
|
||||
m_Body->Write(data, size);
|
||||
|
||||
delete [] data;
|
||||
|
||||
if (size == 0) {
|
||||
Complete = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (src.Eof)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
|
||||
|
||||
if (src.MustRead) {
|
||||
if (!src.FillFromStream(m_Stream, false)) {
|
||||
src.Eof = true;
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
|
||||
}
|
||||
|
||||
src.MustRead = false;
|
||||
}
|
||||
|
||||
long length_indicator_signed = Convert::ToLong(Headers->Get("content-length"));
|
||||
|
||||
if (length_indicator_signed < 0)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative."));
|
||||
|
||||
size_t length_indicator = length_indicator_signed;
|
||||
|
||||
if (src.Size < length_indicator) {
|
||||
src.MustRead = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Body->Write(src.Buffer, length_indicator);
|
||||
src.DropData(length_indicator);
|
||||
Complete = true;
|
||||
return true;
|
||||
}
|
||||
m_Body->Write(src.Buffer, length_indicator);
|
||||
src.DropData(length_indicator);
|
||||
CompleteBody = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -52,7 +52,8 @@ enum HttpRequestState
|
||||
struct HttpRequest
|
||||
{
|
||||
public:
|
||||
bool Complete;
|
||||
bool CompleteHeaders;
|
||||
bool CompleteBody;
|
||||
|
||||
String RequestMethod;
|
||||
Url::Ptr RequestUrl;
|
||||
@ -62,7 +63,8 @@ public:
|
||||
|
||||
HttpRequest(Stream::Ptr stream);
|
||||
|
||||
bool Parse(StreamReadContext& src, bool may_wait);
|
||||
bool ParseHeader(StreamReadContext& src, bool may_wait);
|
||||
bool ParseBody(StreamReadContext& src, bool may_wait);
|
||||
size_t ReadBody(char *data, size_t count);
|
||||
|
||||
void AddHeader(const String& key, const String& value);
|
||||
|
@ -92,7 +92,7 @@ bool HttpServerConnection::ProcessMessage()
|
||||
bool res;
|
||||
|
||||
try {
|
||||
res = m_CurrentRequest.Parse(m_Context, false);
|
||||
res = m_CurrentRequest.ParseHeader(m_Context, false);
|
||||
} catch (const std::invalid_argument& ex) {
|
||||
HttpResponse response(m_Stream, m_CurrentRequest);
|
||||
response.SetStatus(400, "Bad request");
|
||||
@ -113,7 +113,7 @@ bool HttpServerConnection::ProcessMessage()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_CurrentRequest.Complete) {
|
||||
if (m_CurrentRequest.CompleteHeaders) {
|
||||
m_RequestQueue.Enqueue(std::bind(&HttpServerConnection::ProcessMessageAsync,
|
||||
HttpServerConnection::Ptr(this), m_CurrentRequest));
|
||||
|
||||
@ -241,25 +241,37 @@ void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
|
||||
response.WriteBody(msg.CStr(), msg.GetLength());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
HttpHandler::ProcessRequest(user, request, response);
|
||||
} catch (const std::exception& ex) {
|
||||
Log(LogCritical, "HttpServerConnection")
|
||||
<< "Unhandled exception while processing Http request: " << DiagnosticInformation(ex);
|
||||
response.SetStatus(503, "Unhandled exception");
|
||||
bool res = true;
|
||||
while (!request.CompleteBody)
|
||||
res = request.ParseBody(m_Context, false);
|
||||
if (!res) {
|
||||
Log(LogCritical, "HttpServerConnection", "Failed to read body");
|
||||
Dictionary::Ptr result = new Dictionary({
|
||||
{ "error", 400 },
|
||||
{ "status", "Bad Request: Malformed body." }
|
||||
});
|
||||
HttpUtility::SendJsonBody(response, nullptr, result);
|
||||
} else {
|
||||
try {
|
||||
HttpHandler::ProcessRequest(user, request, response);
|
||||
} catch (const std::exception& ex) {
|
||||
Log(LogCritical, "HttpServerConnection")
|
||||
<< "Unhandled exception while processing Http request: " << DiagnosticInformation(ex);
|
||||
response.SetStatus(503, "Unhandled exception");
|
||||
|
||||
String errorInfo = DiagnosticInformation(ex);
|
||||
String errorInfo = DiagnosticInformation(ex);
|
||||
|
||||
if (request.Headers->Get("accept") == "application/json") {
|
||||
Dictionary::Ptr result = new Dictionary({
|
||||
{ "error", 503 },
|
||||
{ "status", errorInfo }
|
||||
});
|
||||
if (request.Headers->Get("accept") == "application/json") {
|
||||
Dictionary::Ptr result = new Dictionary({
|
||||
{ "error", 503 },
|
||||
{ "status", errorInfo }
|
||||
});
|
||||
|
||||
HttpUtility::SendJsonBody(response, nullptr, result);
|
||||
} else {
|
||||
response.AddHeader("Content-Type", "text/plain");
|
||||
response.WriteBody(errorInfo.CStr(), errorInfo.GetLength());
|
||||
HttpUtility::SendJsonBody(response, nullptr, result);
|
||||
} else {
|
||||
response.AddHeader("Content-Type", "text/plain");
|
||||
response.WriteBody(errorInfo.CStr(), errorInfo.GetLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user