Only read body from authenticated connections

This means we are not allowing unauthenticated requests anymore
This commit is contained in:
Jean Flach 2018-02-01 15:10:28 +01:00
parent e1c46cedd1
commit a46dc64e6a
3 changed files with 162 additions and 138 deletions

View File

@ -29,19 +29,22 @@
using namespace icinga; using namespace icinga;
HttpRequest::HttpRequest(const Stream::Ptr& stream) HttpRequest::HttpRequest(const Stream::Ptr& stream)
: Complete(false), : CompleteHeaders(false),
CompleteBody(false),
ProtocolVersion(HttpVersion11), ProtocolVersion(HttpVersion11),
Headers(new Dictionary()), Headers(new Dictionary()),
m_Stream(stream), m_Stream(stream),
m_State(HttpRequestStart) m_State(HttpRequestStart)
{ } { }
bool HttpRequest::Parse(StreamReadContext& src, bool may_wait) bool HttpRequest::ParseHeader(StreamReadContext& src, bool may_wait)
{ {
if (!m_Stream) if (!m_Stream)
return false; return false;
if (m_State != HttpRequestBody) { if (m_State != HttpRequestStart && m_State != HttpRequestHeaders)
return false;
String line; String line;
StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);
@ -60,8 +63,7 @@ bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
if (line == "") if (line == "")
return true; return true;
std::vector<String> tokens; std::vector<String> tokens = line.Split(" ");
boost::algorithm::split(tokens, line, boost::is_any_of(" "));
Log(LogDebug, "HttpRequest") Log(LogDebug, "HttpRequest")
<< "line: " << line << ", tokens: " << tokens.size(); << "line: " << line << ", tokens: " << tokens.size();
if (tokens.size() != 3) if (tokens.size() != 3)
@ -78,16 +80,11 @@ bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version")); BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));
m_State = HttpRequestHeaders; m_State = HttpRequestHeaders;
} else if (m_State == HttpRequestHeaders) { return true;
} else { // m_State = HttpRequestHeaders
if (line == "") { if (line == "") {
m_State = HttpRequestBody; m_State = HttpRequestBody;
CompleteHeaders = true;
/* 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; return true;
} else { } else {
@ -104,11 +101,27 @@ bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
if (key == "x-http-method-override") if (key == "x-http-method-override")
RequestMethod = value; RequestMethod = value;
return true;
} }
} else {
VERIFY(!"Invalid HTTP request state.");
} }
} else if (m_State == HttpRequestBody) { }
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 (Headers->Get("transfer-encoding") == "chunked") {
if (!m_ChunkContext) if (!m_ChunkContext)
m_ChunkContext = boost::make_shared<ChunkReadContext>(boost::ref(src)); m_ChunkContext = boost::make_shared<ChunkReadContext>(boost::ref(src));
@ -125,7 +138,7 @@ bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
delete [] data; delete [] data;
if (size == 0) { if (size == 0) {
Complete = true; CompleteBody = true;
return true; return true;
} }
} else { } else {
@ -155,10 +168,9 @@ bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
m_Body->Write(src.Buffer, length_indicator); m_Body->Write(src.Buffer, length_indicator);
src.DropData(length_indicator); src.DropData(length_indicator);
Complete = true; CompleteBody = true;
return true; return true;
} }
}
return true; return true;
} }

View File

@ -52,7 +52,8 @@ enum HttpRequestState
struct I2_REMOTE_API HttpRequest struct I2_REMOTE_API HttpRequest
{ {
public: public:
bool Complete; bool CompleteHeaders;
bool CompleteBody;
String RequestMethod; String RequestMethod;
Url::Ptr RequestUrl; Url::Ptr RequestUrl;
@ -62,7 +63,8 @@ public:
HttpRequest(const Stream::Ptr& stream); HttpRequest(const 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); size_t ReadBody(char *data, size_t count);
void AddHeader(const String& key, const String& value); void AddHeader(const String& key, const String& value);

View File

@ -92,7 +92,7 @@ bool HttpServerConnection::ProcessMessage(void)
bool res; bool res;
try { try {
res = m_CurrentRequest.Parse(m_Context, false); res = m_CurrentRequest.ParseHeader(m_Context, false);
} catch (const std::invalid_argument& ex) { } catch (const std::invalid_argument& ex) {
HttpResponse response(m_Stream, m_CurrentRequest); HttpResponse response(m_Stream, m_CurrentRequest);
response.SetStatus(400, "Bad request"); response.SetStatus(400, "Bad request");
@ -113,7 +113,7 @@ bool HttpServerConnection::ProcessMessage(void)
return false; return false;
} }
if (m_CurrentRequest.Complete) { if (m_CurrentRequest.CompleteHeaders) {
m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync, m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync,
HttpServerConnection::Ptr(this), m_CurrentRequest)); HttpServerConnection::Ptr(this), m_CurrentRequest));
@ -240,6 +240,16 @@ void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
String msg = "<h1>Unauthorized. Please check your user credentials.</h1>"; String msg = "<h1>Unauthorized. Please check your user credentials.</h1>";
response.WriteBody(msg.CStr(), msg.GetLength()); response.WriteBody(msg.CStr(), msg.GetLength());
} }
} else {
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;
result->Set("error", 404);
result->Set("status", "Bad Request: Malformed body.");
HttpUtility::SendJsonBody(response, result);
} else { } else {
try { try {
HttpHandler::ProcessRequest(user, request, response); HttpHandler::ProcessRequest(user, request, response);
@ -252,7 +262,6 @@ void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
if (request.Headers->Get("accept") == "application/json") { if (request.Headers->Get("accept") == "application/json") {
Dictionary::Ptr result = new Dictionary(); Dictionary::Ptr result = new Dictionary();
result->Set("error", 503); result->Set("error", 503);
result->Set("status", errorInfo); result->Set("status", errorInfo);
@ -263,6 +272,7 @@ void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
} }
} }
} }
}
response.Finish(); response.Finish();