From f933aafd299065fec012278acfaf91735c3c7068 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 23 May 2019 15:25:08 +0200 Subject: [PATCH] Quality: Purge old HTTP code in lib/remote --- lib/cli/consolecommand.cpp | 3 +- lib/perfdata/elasticsearchwriter.cpp | 2 - lib/perfdata/influxdbwriter.cpp | 2 - lib/remote/CMakeLists.txt | 4 - lib/remote/apiclient.cpp | 164 ----------------- lib/remote/apiclient.hpp | 43 ----- lib/remote/httpclientconnection.cpp | 159 ---------------- lib/remote/httpclientconnection.hpp | 61 ------- lib/remote/httphandler.hpp | 1 - lib/remote/httprequest.cpp | 248 ------------------------- lib/remote/httprequest.hpp | 69 ------- lib/remote/httpresponse.cpp | 259 --------------------------- lib/remote/httpresponse.hpp | 66 ------- lib/remote/httputility.cpp | 76 +------- lib/remote/httputility.hpp | 14 +- 15 files changed, 9 insertions(+), 1162 deletions(-) delete mode 100644 lib/remote/apiclient.cpp delete mode 100644 lib/remote/apiclient.hpp delete mode 100644 lib/remote/httpclientconnection.cpp delete mode 100644 lib/remote/httpclientconnection.hpp delete mode 100644 lib/remote/httprequest.cpp delete mode 100644 lib/remote/httprequest.hpp delete mode 100644 lib/remote/httpresponse.cpp delete mode 100644 lib/remote/httpresponse.hpp diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp index 868c66cd4..848b8b07c 100644 --- a/lib/cli/consolecommand.cpp +++ b/lib/cli/consolecommand.cpp @@ -2,7 +2,6 @@ #include "cli/consolecommand.hpp" #include "config/configcompiler.hpp" -#include "remote/apiclient.hpp" #include "remote/consolehandler.hpp" #include "remote/url.hpp" #include "base/configwriter.hpp" @@ -721,4 +720,4 @@ Array::Ptr ConsoleCommand::AutoCompleteScript(const String& session, const Strin } return suggestions; -} \ No newline at end of file +} diff --git a/lib/perfdata/elasticsearchwriter.cpp b/lib/perfdata/elasticsearchwriter.cpp index 8ea4fdef1..f308d3dff 100644 --- a/lib/perfdata/elasticsearchwriter.cpp +++ b/lib/perfdata/elasticsearchwriter.cpp @@ -3,8 +3,6 @@ #include "perfdata/elasticsearchwriter.hpp" #include "perfdata/elasticsearchwriter-ti.cpp" #include "remote/url.hpp" -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" #include "icinga/compatutility.hpp" #include "icinga/service.hpp" #include "icinga/checkcommand.hpp" diff --git a/lib/perfdata/influxdbwriter.cpp b/lib/perfdata/influxdbwriter.cpp index c99164e4a..c5e3edb2f 100644 --- a/lib/perfdata/influxdbwriter.cpp +++ b/lib/perfdata/influxdbwriter.cpp @@ -3,8 +3,6 @@ #include "perfdata/influxdbwriter.hpp" #include "perfdata/influxdbwriter-ti.cpp" #include "remote/url.hpp" -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" #include "icinga/service.hpp" #include "icinga/macroprocessor.hpp" #include "icinga/icingaapplication.hpp" diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index 29c45a29b..bd0884876 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -9,7 +9,6 @@ set(remote_SOURCES i2-remote.hpp actionshandler.cpp actionshandler.hpp apiaction.cpp apiaction.hpp - apiclient.cpp apiclient.hpp apifunction.cpp apifunction.hpp apilistener.cpp apilistener.hpp apilistener-ti.hpp apilistener-configsync.cpp apilistener-filesync.cpp apilistener-authority.cpp @@ -27,10 +26,7 @@ set(remote_SOURCES eventshandler.cpp eventshandler.hpp filterutility.cpp filterutility.hpp httpchunkedencoding.cpp httpchunkedencoding.hpp - httpclientconnection.cpp httpclientconnection.hpp httphandler.cpp httphandler.hpp - httprequest.cpp httprequest.hpp - httpresponse.cpp httpresponse.hpp httpserverconnection.cpp httpserverconnection.hpp httputility.cpp httputility.hpp infohandler.cpp infohandler.hpp diff --git a/lib/remote/apiclient.cpp b/lib/remote/apiclient.cpp deleted file mode 100644 index 59d53d04c..000000000 --- a/lib/remote/apiclient.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#include "remote/apiclient.hpp" -#include "base/base64.hpp" -#include "base/json.hpp" -#include "base/logger.hpp" -#include "base/exception.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -ApiClient::ApiClient(const String& host, const String& port, - String user, String password) - : m_Connection(new HttpClientConnection(host, port, true)), m_User(std::move(user)), m_Password(std::move(password)) -{ - m_Connection->Start(); -} - -void ApiClient::ExecuteScript(const String& session, const String& command, bool sandboxed, - const ExecuteScriptCompletionCallback& callback) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "console", "execute-script" }); - - url->SetQuery({ - {"session", session}, - {"command", command}, - {"sandboxed", sandboxed ? "1" : "0"} - }); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "POST"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(ExecuteScriptHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), Empty); - } -} - -void ApiClient::ExecuteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ExecuteScriptCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - Value result; - String errorMessage = "Unexpected result from API."; - - if (results && results->GetLength() > 0) { - Dictionary::Ptr resultInfo = results->Get(0); - errorMessage = resultInfo->Get("status"); - - if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) { - result = resultInfo->Get("result"); - } else { - DebugInfo di; - Dictionary::Ptr debugInfo = resultInfo->Get("debug_info"); - if (debugInfo) { - di.Path = debugInfo->Get("path"); - di.FirstLine = debugInfo->Get("first_line"); - di.FirstColumn = debugInfo->Get("first_column"); - di.LastLine = debugInfo->Get("last_line"); - di.LastColumn = debugInfo->Get("last_column"); - } - bool incompleteExpression = resultInfo->Get("incomplete_expression"); - BOOST_THROW_EXCEPTION(ScriptError(errorMessage, di, incompleteExpression)); - } - } - - callback(boost::exception_ptr(), result); - } catch (const std::exception&) { - callback(boost::current_exception(), Empty); - } -} - -void ApiClient::AutocompleteScript(const String& session, const String& command, bool sandboxed, - const AutocompleteScriptCompletionCallback& callback) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "console", "auto-complete-script" }); - - url->SetQuery({ - {"session", session}, - {"command", command}, - {"sandboxed", sandboxed ? "1" : "0"} - }); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "POST"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(AutocompleteScriptHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), nullptr); - } -} - -void ApiClient::AutocompleteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const AutocompleteScriptCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - Array::Ptr suggestions; - String errorMessage = "Unexpected result from API."; - - if (results && results->GetLength() > 0) { - Dictionary::Ptr resultInfo = results->Get(0); - errorMessage = resultInfo->Get("status"); - - if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) - suggestions = resultInfo->Get("suggestions"); - else - BOOST_THROW_EXCEPTION(ScriptError(errorMessage)); - } - - callback(boost::exception_ptr(), suggestions); - } catch (const std::exception&) { - callback(boost::current_exception(), nullptr); - } -} diff --git a/lib/remote/apiclient.hpp b/lib/remote/apiclient.hpp deleted file mode 100644 index 001f4947b..000000000 --- a/lib/remote/apiclient.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#ifndef APICLIENT_H -#define APICLIENT_H - -#include "remote/httpclientconnection.hpp" -#include "base/value.hpp" -#include "base/exception.hpp" -#include - -namespace icinga -{ - -class ApiClient : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(ApiClient); - - ApiClient(const String& host, const String& port, - String user, String password); - - typedef std::function ExecuteScriptCompletionCallback; - void ExecuteScript(const String& session, const String& command, bool sandboxed, - const ExecuteScriptCompletionCallback& callback) const; - - typedef std::function AutocompleteScriptCompletionCallback; - void AutocompleteScript(const String& session, const String& command, bool sandboxed, - const AutocompleteScriptCompletionCallback& callback) const; - -private: - HttpClientConnection::Ptr m_Connection; - String m_User; - String m_Password; - - static void ExecuteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ExecuteScriptCompletionCallback& callback); - static void AutocompleteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const AutocompleteScriptCompletionCallback& callback); -}; - -} - -#endif /* APICLIENT_H */ diff --git a/lib/remote/httpclientconnection.cpp b/lib/remote/httpclientconnection.cpp deleted file mode 100644 index 2124b19ee..000000000 --- a/lib/remote/httpclientconnection.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#include "remote/httpclientconnection.hpp" -#include "base/configtype.hpp" -#include "base/objectlock.hpp" -#include "base/base64.hpp" -#include "base/utility.hpp" -#include "base/logger.hpp" -#include "base/exception.hpp" -#include "base/convert.hpp" -#include "base/tcpsocket.hpp" -#include "base/tlsstream.hpp" -#include "base/networkstream.hpp" - -using namespace icinga; - -HttpClientConnection::HttpClientConnection(String host, String port, bool tls) - : m_Host(std::move(host)), m_Port(std::move(port)), m_Tls(tls) -{ } - -void HttpClientConnection::Start() -{ - /* Nothing to do here atm. */ -} - -void HttpClientConnection::Reconnect() -{ - if (m_Stream) - m_Stream->Close(); - - m_Context.~StreamReadContext(); - new (&m_Context) StreamReadContext(); - - m_Requests.clear(); - m_CurrentResponse.reset(); - - TcpSocket::Ptr socket = new TcpSocket(); - socket->Connect(m_Host, m_Port); - - if (m_Tls) - m_Stream = new TlsStream(socket, m_Host, RoleClient); - else - ASSERT(!"Non-TLS HTTP connections not supported."); - /* m_Stream = new NetworkStream(socket); - * -- does not currently work because the NetworkStream class doesn't support async I/O - */ - - /* the stream holds an owning reference to this object through the callback we're registering here */ - m_Stream->RegisterDataHandler(std::bind(&HttpClientConnection::DataAvailableHandler, HttpClientConnection::Ptr(this), _1)); - if (m_Stream->IsDataAvailable()) - DataAvailableHandler(m_Stream); -} - -Stream::Ptr HttpClientConnection::GetStream() const -{ - return m_Stream; -} - -String HttpClientConnection::GetHost() const -{ - return m_Host; -} - -String HttpClientConnection::GetPort() const -{ - return m_Port; -} - -bool HttpClientConnection::GetTls() const -{ - return m_Tls; -} - -void HttpClientConnection::Disconnect() -{ - Log(LogDebug, "HttpClientConnection", "Http client disconnected"); - - m_Stream->Shutdown(); -} - -bool HttpClientConnection::ProcessMessage() -{ - bool res; - - if (m_Requests.empty()) { - m_Stream->Close(); - return false; - } - - const std::pair, HttpCompletionCallback>& currentRequest = *m_Requests.begin(); - HttpRequest& request = *currentRequest.first.get(); - const HttpCompletionCallback& callback = currentRequest.second; - - if (!m_CurrentResponse) - m_CurrentResponse = std::make_shared(m_Stream, request); - - std::shared_ptr currentResponse = m_CurrentResponse; - HttpResponse& response = *currentResponse.get(); - - try { - res = response.Parse(m_Context, false); - } catch (const std::exception&) { - callback(request, response); - - m_Stream->Shutdown(); - return false; - } - - if (response.Complete) { - callback(request, response); - - m_Requests.pop_front(); - m_CurrentResponse.reset(); - - return true; - } - - return res; -} - -void HttpClientConnection::DataAvailableHandler(const Stream::Ptr& stream) -{ - ASSERT(stream == m_Stream); - - bool close = false; - - if (!m_Stream->IsEof()) { - boost::mutex::scoped_lock lock(m_DataHandlerMutex); - - try { - while (ProcessMessage()) - ; /* empty loop body */ - } catch (const std::exception& ex) { - Log(LogWarning, "HttpClientConnection") - << "Error while reading Http response: " << DiagnosticInformation(ex); - - close = true; - Disconnect(); - } - } else - close = true; - - if (close) - m_Stream->Close(); -} - -std::shared_ptr HttpClientConnection::NewRequest() -{ - Reconnect(); - return std::make_shared(m_Stream); -} - -void HttpClientConnection::SubmitRequest(const std::shared_ptr& request, - const HttpCompletionCallback& callback) -{ - m_Requests.emplace_back(request, callback); - request->Finish(); -} - diff --git a/lib/remote/httpclientconnection.hpp b/lib/remote/httpclientconnection.hpp deleted file mode 100644 index bff0f51d6..000000000 --- a/lib/remote/httpclientconnection.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#ifndef HTTPCLIENTCONNECTION_H -#define HTTPCLIENTCONNECTION_H - -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" -#include "base/stream.hpp" -#include "base/timer.hpp" -#include - -namespace icinga -{ - -/** - * An HTTP client connection. - * - * @ingroup remote - */ -class HttpClientConnection final : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(HttpClientConnection); - - HttpClientConnection(String host, String port, bool tls = true); - - void Start(); - - Stream::Ptr GetStream() const; - String GetHost() const; - String GetPort() const; - bool GetTls() const; - - void Disconnect(); - - std::shared_ptr NewRequest(); - - typedef std::function HttpCompletionCallback; - void SubmitRequest(const std::shared_ptr& request, const HttpCompletionCallback& callback); - -private: - String m_Host; - String m_Port; - bool m_Tls; - Stream::Ptr m_Stream; - std::deque, HttpCompletionCallback> > m_Requests; - std::shared_ptr m_CurrentResponse; - boost::mutex m_DataHandlerMutex; - - StreamReadContext m_Context; - - void Reconnect(); - bool ProcessMessage(); - void DataAvailableHandler(const Stream::Ptr& stream); - - void ProcessMessageAsync(HttpRequest& request); -}; - -} - -#endif /* HTTPCLIENTCONNECTION_H */ diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index 6c10d0aff..a6a730255 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -5,7 +5,6 @@ #include "remote/i2-remote.hpp" #include "remote/url.hpp" -#include "remote/httpresponse.hpp" #include "remote/httpserverconnection.hpp" #include "remote/apiuser.hpp" #include "base/registry.hpp" diff --git a/lib/remote/httprequest.cpp b/lib/remote/httprequest.cpp deleted file mode 100644 index 6c64dd609..000000000 --- a/lib/remote/httprequest.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#include "remote/httprequest.hpp" -#include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -HttpRequest::HttpRequest(Stream::Ptr stream) - : CompleteHeaders(false), - CompleteHeaderCheck(false), - CompleteBody(false), - ProtocolVersion(HttpVersion11), - Headers(new Dictionary()), - m_Stream(std::move(stream)), - m_State(HttpRequestStart) -{ } - -bool HttpRequest::ParseHeaders(StreamReadContext& src, bool may_wait) -{ - if (!m_Stream) - return false; - - if (m_State != HttpRequestStart && m_State != HttpRequestHeaders) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid HTTP state")); - - String line; - StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); - - if (srs != StatusNewItem) { - if (src.Size > 8 * 1024) - BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded")); - - return false; - } - - if (line.GetLength() > 8 * 1024) { -#ifdef I2_DEBUG /* I2_DEBUG */ - Log(LogDebug, "HttpRequest") - << "Header size: " << line.GetLength() << " content: '" << line << "'."; -#endif /* I2_DEBUG */ - - 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 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) - return false; - - if (m_State != HttpRequestBody) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid HTTP state")); - - /* 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 (Headers->Get("transfer-encoding") == "chunked") { - if (!m_ChunkContext) - m_ChunkContext = std::make_shared(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; - } - - 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); - CompleteBody = true; - return true; -} - -size_t HttpRequest::ReadBody(char *data, size_t count) -{ - if (!m_Body) - return 0; - else - return m_Body->Read(data, count, true); -} - -void HttpRequest::AddHeader(const String& key, const String& value) -{ - ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders); - Headers->Set(key.ToLower(), value); -} - -void HttpRequest::FinishHeaders() -{ - if (m_State == HttpRequestStart) { - String rqline = RequestMethod + " " + RequestUrl->Format(true) + " HTTP/1." + (ProtocolVersion == HttpVersion10 ? "0" : "1") + "\r\n"; - m_Stream->Write(rqline.CStr(), rqline.GetLength()); - m_State = HttpRequestHeaders; - } - - if (m_State == HttpRequestHeaders) { - AddHeader("User-Agent", "Icinga/" + Application::GetAppVersion()); - - if (ProtocolVersion == HttpVersion11) { - AddHeader("Transfer-Encoding", "chunked"); - if (!Headers->Contains("Host")) - AddHeader("Host", RequestUrl->GetHost() + ":" + RequestUrl->GetPort()); - } - - ObjectLock olock(Headers); - for (const Dictionary::Pair& kv : Headers) - { - String header = kv.first + ": " + kv.second + "\r\n"; - m_Stream->Write(header.CStr(), header.GetLength()); - } - - m_Stream->Write("\r\n", 2); - - m_State = HttpRequestBody; - } -} - -void HttpRequest::WriteBody(const char *data, size_t count) -{ - ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders || m_State == HttpRequestBody); - - if (ProtocolVersion == HttpVersion10) { - if (!m_Body) - m_Body = new FIFO(); - - m_Body->Write(data, count); - } else { - FinishHeaders(); - - HttpChunkedEncoding::WriteChunkToStream(m_Stream, data, count); - } -} - -void HttpRequest::Finish() -{ - ASSERT(m_State != HttpRequestEnd); - - if (ProtocolVersion == HttpVersion10) { - if (m_Body) - AddHeader("Content-Length", Convert::ToString(m_Body->GetAvailableBytes())); - - FinishHeaders(); - - while (m_Body && m_Body->IsDataAvailable()) { - char buffer[1024]; - size_t rc = m_Body->Read(buffer, sizeof(buffer), true); - m_Stream->Write(buffer, rc); - } - } else { - if (m_State == HttpRequestStart || m_State == HttpRequestHeaders) - FinishHeaders(); - - WriteBody(nullptr, 0); - m_Stream->Write("\r\n", 2); - } - - m_State = HttpRequestEnd; -} - diff --git a/lib/remote/httprequest.hpp b/lib/remote/httprequest.hpp deleted file mode 100644 index 1f1a9734b..000000000 --- a/lib/remote/httprequest.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#ifndef HTTPREQUEST_H -#define HTTPREQUEST_H - -#include "remote/i2-remote.hpp" -#include "remote/httpchunkedencoding.hpp" -#include "remote/url.hpp" -#include "base/stream.hpp" -#include "base/fifo.hpp" -#include "base/dictionary.hpp" - -namespace icinga -{ - -enum HttpVersion -{ - HttpVersion10, - HttpVersion11 -}; - -enum HttpRequestState -{ - HttpRequestStart, - HttpRequestHeaders, - HttpRequestBody, - HttpRequestEnd -}; - -/** - * An HTTP request. - * - * @ingroup remote - */ -struct HttpRequest -{ -public: - bool CompleteHeaders; - bool CompleteHeaderCheck; - bool CompleteBody; - - String RequestMethod; - Url::Ptr RequestUrl; - HttpVersion ProtocolVersion; - - Dictionary::Ptr Headers; - - HttpRequest(Stream::Ptr stream); - - bool ParseHeaders(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); - void WriteBody(const char *data, size_t count); - void Finish(); - -private: - Stream::Ptr m_Stream; - std::shared_ptr m_ChunkContext; - HttpRequestState m_State; - FIFO::Ptr m_Body; - - void FinishHeaders(); -}; - -} - -#endif /* HTTPREQUEST_H */ diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp deleted file mode 100644 index d6e5d3f66..000000000 --- a/lib/remote/httpresponse.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#include "remote/httpresponse.hpp" -#include "remote/httpchunkedencoding.hpp" -#include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -HttpResponse::HttpResponse(Stream::Ptr stream, const HttpRequest& request) - : Complete(false), m_State(HttpResponseStart), m_Request(&request), m_Stream(std::move(stream)) -{ } - -void HttpResponse::SetStatus(int code, const String& message) -{ - ASSERT(code >= 100 && code <= 599); - ASSERT(!message.IsEmpty()); - - if (m_State != HttpResponseStart) { - Log(LogWarning, "HttpResponse", "Tried to set Http response status after headers had already been sent."); - return; - } - - String status = "HTTP/"; - - if (m_Request->ProtocolVersion == HttpVersion10) - status += "1.0"; - else - status += "1.1"; - - status += " " + Convert::ToString(code) + " " + message + "\r\n"; - - m_Stream->Write(status.CStr(), status.GetLength()); - - m_State = HttpResponseHeaders; -} - -void HttpResponse::AddHeader(const String& key, const String& value) -{ - m_Headers.emplace_back(key + ": " + value + "\r\n"); -} - -void HttpResponse::FinishHeaders() -{ - if (m_State == HttpResponseHeaders) { - if (m_Request->ProtocolVersion == HttpVersion11) - AddHeader("Transfer-Encoding", "chunked"); - - AddHeader("Server", "Icinga/" + Application::GetAppVersion()); - - for (const String& header : m_Headers) - m_Stream->Write(header.CStr(), header.GetLength()); - - m_Stream->Write("\r\n", 2); - m_State = HttpResponseBody; - } -} - -void HttpResponse::WriteBody(const char *data, size_t count) -{ - ASSERT(m_State == HttpResponseHeaders || m_State == HttpResponseBody); - - if (m_Request->ProtocolVersion == HttpVersion10) { - if (!m_Body) - m_Body = new FIFO(); - - m_Body->Write(data, count); - } else { - FinishHeaders(); - - HttpChunkedEncoding::WriteChunkToStream(m_Stream, data, count); - } -} - -void HttpResponse::Finish() -{ - ASSERT(m_State != HttpResponseEnd); - - if (m_Request->ProtocolVersion == HttpVersion10) { - if (m_Body) - AddHeader("Content-Length", Convert::ToString(m_Body->GetAvailableBytes())); - - FinishHeaders(); - - while (m_Body && m_Body->IsDataAvailable()) { - char buffer[1024]; - size_t rc = m_Body->Read(buffer, sizeof(buffer), true); - m_Stream->Write(buffer, rc); - } - } else { - WriteBody(nullptr, 0); - m_Stream->Write("\r\n", 2); - } - - m_State = HttpResponseEnd; - - /* Close the connection on - * a) HTTP/1.0 - * b) Connection: close in the sent header. - * - * Do this here and not in DataAvailableHandler - there might still be incoming data in there. - */ - if (m_Request->ProtocolVersion == HttpVersion10 || m_Request->Headers->Get("connection") == "close") - m_Stream->Shutdown(); -} - -bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) -{ - if (m_State != HttpResponseBody) { - String line; - StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); - - if (srs != StatusNewItem) - return false; - - if (m_State == HttpResponseStart) { - /* ignore trailing new-lines */ - if (line == "") - return true; - - std::vector tokens = line.Split(" "); - Log(LogDebug, "HttpRequest") - << "line: " << line << ", tokens: " << tokens.size(); - if (tokens.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP response (Status line)")); - - if (tokens[0] == "HTTP/1.0") - ProtocolVersion = HttpVersion10; - else if (tokens[0] == "HTTP/1.1") { - ProtocolVersion = HttpVersion11; - } else - BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version")); - - StatusCode = Convert::ToLong(tokens[1]); - - if (tokens.size() >= 3) - StatusMessage = tokens[2]; // TODO: Join tokens[2..end] - - m_State = HttpResponseHeaders; - } else if (m_State == HttpResponseHeaders) { - if (!Headers) - Headers = new Dictionary(); - - if (line == "") { - m_State = HttpResponseBody; - m_Body = new FIFO(); - - return true; - - } else { - 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); - } - } else { - VERIFY(!"Invalid HTTP request state."); - } - } else if (m_State == HttpResponseBody) { - if (Headers->Get("transfer-encoding") == "chunked") { - if (!m_ChunkContext) - m_ChunkContext = std::make_shared(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; - - Log(LogNotice, "HttpResponse") - << "Read " << size << " bytes"; - - m_Body->Write(data, size); - - delete[] data; - - if (size == 0) { - Complete = true; - return true; - } - } else { - bool hasLengthIndicator = false; - size_t lengthIndicator = 0; - Value contentLengthHeader; - - if (Headers->Get("content-length", &contentLengthHeader)) { - hasLengthIndicator = true; - lengthIndicator = Convert::ToLong(contentLengthHeader); - } - - if (!hasLengthIndicator && ProtocolVersion != HttpVersion10 && !Headers->Contains("transfer-encoding")) { - Complete = true; - return true; - } - - if (hasLengthIndicator && src.Eof) - BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - - if (src.MustRead) { - if (!src.FillFromStream(m_Stream, may_wait)) - src.Eof = true; - - src.MustRead = false; - } - - if (!hasLengthIndicator) - lengthIndicator = src.Size; - - if (src.Size < lengthIndicator) { - src.MustRead = true; - return may_wait; - } - - m_Body->Write(src.Buffer, lengthIndicator); - src.DropData(lengthIndicator); - - if (!hasLengthIndicator && !src.Eof) { - src.MustRead = true; - return may_wait; - } - - Complete = true; - return true; - } - } - - return true; -} - -size_t HttpResponse::ReadBody(char *data, size_t count) -{ - if (!m_Body) - return 0; - else - return m_Body->Read(data, count, true); -} - -size_t HttpResponse::GetBodySize() const -{ - if (!m_Body) - return 0; - else - return m_Body->GetAvailableBytes(); -} - -bool HttpResponse::IsPeerConnected() const -{ - return !m_Stream->IsEof(); -} - -void HttpResponse::RebindRequest(const HttpRequest& request) -{ - m_Request = &request; -} diff --git a/lib/remote/httpresponse.hpp b/lib/remote/httpresponse.hpp deleted file mode 100644 index 90b25764f..000000000 --- a/lib/remote/httpresponse.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#ifndef HTTPRESPONSE_H -#define HTTPRESPONSE_H - -#include "remote/httprequest.hpp" -#include "base/stream.hpp" -#include "base/fifo.hpp" -#include - -namespace icinga -{ - -enum HttpResponseState -{ - HttpResponseStart, - HttpResponseHeaders, - HttpResponseBody, - HttpResponseEnd -}; - -/** - * An HTTP response. - * - * @ingroup remote - */ -struct HttpResponse -{ -public: - bool Complete; - - HttpVersion ProtocolVersion; - int StatusCode; - String StatusMessage; - - Dictionary::Ptr Headers; - - HttpResponse(Stream::Ptr stream, const HttpRequest& request); - - bool Parse(StreamReadContext& src, bool may_wait); - size_t ReadBody(char *data, size_t count); - size_t GetBodySize() const; - - void SetStatus(int code, const String& message); - void AddHeader(const String& key, const String& value); - void WriteBody(const char *data, size_t count); - void Finish(); - - bool IsPeerConnected() const; - - void RebindRequest(const HttpRequest& request); - -private: - HttpResponseState m_State; - std::shared_ptr m_ChunkContext; - const HttpRequest *m_Request; - Stream::Ptr m_Stream; - FIFO::Ptr m_Body; - std::vector m_Headers; - - void FinishHeaders(); -}; - -} - -#endif /* HTTPRESPONSE_H */ diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index c97297cd0..91902ba50 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -37,29 +37,6 @@ Dictionary::Ptr HttpUtility::FetchRequestParameters(const Url::Ptr& url, const s return result; } -void HttpUtility::SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val) -{ - response.AddHeader("Content-Type", "application/json"); - - bool prettyPrint = false; - - if (params) - prettyPrint = GetLastParameter(params, "pretty"); - - String body = JsonEncode(val, prettyPrint); - - response.WriteBody(body.CStr(), body.GetLength()); -} - -void HttpUtility::SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val) -{ - namespace http = boost::beast::http; - - response.set(http::field::content_type, "application/json"); - response.body() = JsonEncode(val, params && GetLastParameter(params, "pretty")); - response.set(http::field::content_length, response.body().size()); -} - Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& key) { Value varr = params->Get(key); @@ -75,27 +52,13 @@ Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& return arr->Get(arr->GetLength() - 1); } -void HttpUtility::SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, - int code, const String& info, const String& diagnosticInformation) +void HttpUtility::SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val) { - Dictionary::Ptr result = new Dictionary(); - response.SetStatus(code, HttpUtility::GetErrorNameByCode(code)); - result->Set("error", code); - - bool verbose = false; - - if (params) - verbose = HttpUtility::GetLastParameter(params, "verbose"); - - if (!info.IsEmpty()) - result->Set("status", info); - - if (verbose) { - if (!diagnosticInformation.IsEmpty()) - result->Set("diagnostic_information", diagnosticInformation); - } + namespace http = boost::beast::http; - HttpUtility::SendJsonBody(response, params, result); + response.set(http::field::content_type, "application/json"); + response.body() = JsonEncode(val, params && GetLastParameter(params, "pretty")); + response.set(http::field::content_length, response.body().size()); } void HttpUtility::SendJsonError(boost::beast::http::response& response, @@ -115,32 +78,3 @@ void HttpUtility::SendJsonError(boost::beast::http::response #include +#include namespace icinga { @@ -23,17 +21,11 @@ class HttpUtility public: static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body); - static void SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val); - static void SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val); static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); - static void SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, const int code, - const String& verbose = String(), const String& diagnosticInformation = String()); + + static void SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val); static void SendJsonError(boost::beast::http::response& response, const Dictionary::Ptr& params, const int code, const String& verbose = String(), const String& diagnosticInformation = String()); - -private: - static String GetErrorNameByCode(int code); - }; } -- 2.40.0