using namespace icinga;
HttpRequest::HttpRequest(const Stream::Ptr& stream)
- : Complete(false),
- ProtocolVersion(HttpVersion11),
- Headers(new Dictionary()),
- m_Stream(stream),
- m_State(HttpRequestStart)
+ : CompleteHeaders(false),
+ CompleteBody(false),
+ ProtocolVersion(HttpVersion11),
+ Headers(new Dictionary()),
+ m_Stream(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);
- return false;
- }
-
- if (line.GetLength() > 512)
+ if (srs != StatusNewItem) {
+ if (src.Size > 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;
+ return false;
+ }
- std::vector<String> tokens;
- boost::algorithm::split(tokens, line, boost::is_any_of(" "));
- Log(LogDebug, "HttpRequest")
- << "line: " << line << ", tokens: " << tokens.size();
- if (tokens.size() != 3)
- BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
+ if (line.GetLength() > 512)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded"));
- RequestMethod = tokens[0];
- RequestUrl = new class Url(tokens[1]);
+ if (m_State == HttpRequestStart) {
+ /* ignore trailing new-lines */
+ if (line == "")
+ return true;
- 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"));
+ 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"));
- m_State = HttpRequestHeaders;
- } else if (m_State == HttpRequestHeaders) {
- if (line == "") {
- m_State = HttpRequestBody;
+ RequestMethod = tokens[0];
+ RequestUrl = new class Url(tokens[1]);
- /* 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();
+ 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"));
- return true;
+ 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"));
+ } 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::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);
+ 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.");
+ if (key == "x-http-method-override")
+ RequestMethod = value;
+
+ return true;
}
- } else if (m_State == HttpRequestBody) {
- if (Headers->Get("transfer-encoding") == "chunked") {
- if (!m_ChunkContext)
- m_ChunkContext = boost::make_shared<ChunkReadContext>(boost::ref(src));
+ }
+}
- char *data;
- size_t size;
- StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);
+bool HttpRequest::ParseBody(StreamReadContext& src, bool may_wait)
+{
+ if (!m_Stream || m_State != HttpRequestBody)
+ return false;
- if (srs != StatusNewItem)
- 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();
- m_Body->Write(data, size);
+ if (CompleteBody)
+ return true;
- delete [] data;
+ if (Headers->Get("transfer-encoding") == "chunked") {
+ if (!m_ChunkContext)
+ m_ChunkContext = boost::make_shared<ChunkReadContext>(boost::ref(src));
- if (size == 0) {
- Complete = true;
- return true;
- }
- } else {
- if (src.Eof)
- BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
+ 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 (src.MustRead) {
- if (!src.FillFromStream(m_Stream, false)) {
- src.Eof = true;
- BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
- }
+ if (size == 0) {
+ CompleteBody = true;
+ return true;
+ }
+ } else {
+ if (src.Eof)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
- src.MustRead = false;
+ if (src.MustRead) {
+ if (!src.FillFromStream(m_Stream, false)) {
+ src.Eof = true;
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
}
- long length_indicator_signed = Convert::ToLong(Headers->Get("content-length"));
+ src.MustRead = false;
+ }
- if (length_indicator_signed < 0)
- BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative."));
+ long length_indicator_signed = Convert::ToLong(Headers->Get("content-length"));
- size_t length_indicator = length_indicator_signed;
+ if (length_indicator_signed < 0)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative."));
- if (src.Size < length_indicator) {
- src.MustRead = true;
- return false;
- }
+ size_t length_indicator = length_indicator_signed;
- m_Body->Write(src.Buffer, length_indicator);
- src.DropData(length_indicator);
- Complete = true;
- return true;
+ if (src.Size < length_indicator) {
+ src.MustRead = true;
+ return false;
}
+
+ m_Body->Write(src.Buffer, length_indicator);
+ src.DropData(length_indicator);
+ CompleteBody = true;
+ return true;
}
return true;
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");
return false;
}
- if (m_CurrentRequest.Complete) {
+ if (m_CurrentRequest.CompleteHeaders) {
m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync,
HttpServerConnection::Ptr(this), m_CurrentRequest));
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");
-
- String errorInfo = DiagnosticInformation(ex);
-
- if (request.Headers->Get("accept") == "application/json") {
- Dictionary::Ptr result = new Dictionary();
-
- result->Set("error", 503);
- result->Set("status", errorInfo);
-
- HttpUtility::SendJsonBody(response, result);
- } else {
- response.AddHeader("Content-Type", "text/plain");
- response.WriteBody(errorInfo.CStr(), errorInfo.GetLength());
+ 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 {
+ 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);
+
+ if (request.Headers->Get("accept") == "application/json") {
+ Dictionary::Ptr result = new Dictionary();
+ result->Set("error", 503);
+ result->Set("status", errorInfo);
+
+ HttpUtility::SendJsonBody(response, result);
+ } else {
+ response.AddHeader("Content-Type", "text/plain");
+ response.WriteBody(errorInfo.CStr(), errorInfo.GetLength());
+ }
}
}
}