bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
- if (request.RequestUrl->GetPath().size() < 3)
- return false;
-
if (request.RequestMethod != "POST") {
- response.SetStatus(400, "Bad request");
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be POST.");
+ return true;
+ }
+
+ if (request.RequestUrl->GetPath().size() < 3) {
+ HttpUtility::SendJsonError(response, 400, "Action is missing.");
return true;
}
ApiAction::Ptr action = ApiAction::GetByName(actionName);
- if (!action)
- return false;
+ if (!action) {
+ HttpUtility::SendJsonError(response, 404, "Action '" + actionName + "' could not be found.");
+ return true;
+ }
QueryDescription qd;
if (!types.empty()) {
qd.Types = std::set<String>(types.begin(), types.end());
- objs = FilterUtility::GetFilterTargets(qd, params);
+ try {
+ objs = FilterUtility::GetFilterTargets(qd, params);
+ } catch (const std::exception& ex) {
+ HttpUtility::SendJsonError(response, 400,
+ "Type/Filter was required but not provided or was invalid.",
+ request.GetVerboseErrors() ? DiagnosticInformation(ex) : "");
+ return true;
+ }
} else
objs.push_back(ConfigObject::Ptr());
results->Add(action->Invoke(obj, params));
} catch (const std::exception& ex) {
Dictionary::Ptr fail = new Dictionary();
- fail->Set("code", 501);
- fail->Set("status", "Error: " + DiagnosticInformation(ex));
+ fail->Set("code", 500);
+ fail->Set("status", "Action execution failed.");
+ if (request.GetVerboseErrors())
+ fail->Set("diagnostic information", DiagnosticInformation(ex));
results->Add(fail);
}
}
Array::Ptr errors = new Array();
bool cascade = true; //TODO pass that through the cluster
if (!ConfigObjectUtility::DeleteObject(object, cascade, errors)) {
- Log(LogCritical, "ApiListener", "Could not delete object:");
+ Log(LogCritical, "ApiListener", "Could not delete object:");
- ObjectLock olock(errors);
- BOOST_FOREACH(const String& error, errors) {
- Log(LogCritical, "ApiListener", error);
- }
+ ObjectLock olock(errors);
+ BOOST_FOREACH(const String& error, errors) {
+ Log(LogCritical, "ApiListener", error);
+ }
}
} else {
Log(LogNotice, "ApiListener")
try {
cert = GetX509Certificate(GetCertPath());
} catch (const std::exception&) {
- BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate from cert path: '" + GetCertPath() + "'.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate from cert path: '"
+ + GetCertPath() + "'.", GetDebugInfo()));
}
try {
SetIdentity(GetCertificateCN(cert));
} catch (const std::exception&) {
- BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate common name from cert path: '" + GetCertPath() + "'.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate common name from cert path: '"
+ + GetCertPath() + "'.", GetDebugInfo()));
}
Log(LogInformation, "ApiListener")
try {
m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
} catch (const std::exception&) {
- BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '" + GetCertPath() + "' key path: '" + GetKeyPath() + "' ca path: '" + GetCaPath() + "'.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '"
+ + GetCertPath() + "' key path: '" + GetKeyPath() + "' ca path: '" + GetCaPath() + "'.", GetDebugInfo()));
}
if (!GetCrlPath().IsEmpty()) {
try {
AddCRLToSSLContext(m_SSLContext, GetCrlPath());
} catch (const std::exception&) {
- BOOST_THROW_EXCEPTION(ScriptError("Cannot add certificate revocation list to SSL context for crl path: '" + GetCrlPath() + "'.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot add certificate revocation list to SSL context for crl path: '"
+ + GetCrlPath() + "'.", GetDebugInfo()));
}
}
}
{
SyncZoneDirs();
- if (std::distance(ConfigType::GetObjectsByType<ApiListener>().first, ConfigType::GetObjectsByType<ApiListener>().second) > 1) {
+ if (std::distance(ConfigType::GetObjectsByType<ApiListener>().first,
+ ConfigType::GetObjectsByType<ApiListener>().second) > 1) {
Log(LogCritical, "ApiListener", "Only one ApiListener object is allowed.");
return;
}
/* only connect to endpoints in a) the same zone b) our parent zone c) immediate child zones */
if (my_zone != zone && my_zone != zone->GetParent() && zone != my_zone->GetParent()) {
Log(LogDebug, "ApiListener")
- << "Not connecting to Zone '" << zone->GetName() << "' because it's not in the same zone, a parent or a child zone.";
+ << "Not connecting to Zone '" << zone->GetName()
+ << "' because it's not in the same zone, a parent or a child zone.";
continue;
}
/* don't try to connect to endpoints which don't have a host and port */
if (endpoint->GetHost().IsEmpty() || endpoint->GetPort().IsEmpty()) {
Log(LogDebug, "ApiListener")
- << "Not connecting to Endpoint '" << endpoint->GetName() << "' because the host/port attributes are missing.";
+ << "Not connecting to Endpoint '" << endpoint->GetName()
+ << "' because the host/port attributes are missing.";
continue;
}
/* don't try to connect if there's already a connection attempt */
if (endpoint->GetConnecting()) {
Log(LogDebug, "ApiListener")
- << "Not connecting to Endpoint '" << endpoint->GetName() << "' because we're already trying to connect to it.";
+ << "Not connecting to Endpoint '" << endpoint->GetName()
+ << "' because we're already trying to connect to it.";
continue;
}
/* don't try to connect if we're already connected */
if (endpoint->IsConnected()) {
Log(LogDebug, "ApiListener")
- << "Not connecting to Endpoint '" << endpoint->GetName() << "' because we're already connected to it.";
+ << "Not connecting to Endpoint '" << endpoint->GetName()
+ << "' because we're already connected to it.";
continue;
}
<< "Connected endpoints: " << Utility::NaturalJoin(names);
}
-void ApiListener::RelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
+void ApiListener::RelayMessage(const MessageOrigin::Ptr& origin,
+ const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
{
m_RelayQueue.Enqueue(boost::bind(&ApiListener::SyncRelayMessage, this, origin, secobj, message, log), true);
}
pmessage->Set("timestamp", ts);
pmessage->Set("message", JsonEncode(message));
-
Dictionary::Ptr secname = new Dictionary();
secname->Set("type", secobj->GetType()->GetName());
secname->Set("name", secobj->GetName());
}
-void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
+void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin,
+ const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
{
double ts = Utility::GetTime();
message->Set("ts", ts);
double peer_ts = endpoint->GetLocalLogPosition();
double logpos_ts = peer_ts;
bool last_sync = false;
-
+
Endpoint::Ptr target_endpoint = client->GetEndpoint();
ASSERT(target_endpoint);
-
+
Zone::Ptr target_zone = target_endpoint->GetZone();
-
+
if (!target_zone)
return;
continue;
Dictionary::Ptr secname = pmessage->Get("secobj");
-
+
if (secname) {
ConfigType::Ptr dtype = ConfigType::GetByName(secname->Get("type"));
-
+
if (!dtype)
continue;
-
+
ConfigObject::Ptr secobj = dtype->GetObject(secname->Get("name"));
-
+
if (!secobj)
continue;
-
+
if (!target_zone->CanAccessObject(secobj))
continue;
}
if (request.RequestMethod == "GET")
HandleGet(user, request, response);
else
- response.SetStatus(400, "Bad request");
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be GET.");
return true;
}
String packageName = HttpUtility::GetLastParameter(params, "package");
String stageName = HttpUtility::GetLastParameter(params, "stage");
- if (!ConfigPackageUtility::ValidateName(packageName) || !ConfigPackageUtility::ValidateName(stageName)) {
- response.SetStatus(403, "Forbidden");
- return;
- }
+ if (!ConfigPackageUtility::ValidateName(packageName))
+ return HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
+
+ if (!ConfigPackageUtility::ValidateName(stageName))
+ return HttpUtility::SendJsonError(response, 404, "Stage is not valid or does not exist.");
String relativePath = HttpUtility::GetLastParameter(params, "path");
- if (ConfigPackageUtility::ContainsDotDot(relativePath)) {
- response.SetStatus(403, "Forbidden");
- return;
- }
+ if (ConfigPackageUtility::ContainsDotDot(relativePath))
+ return HttpUtility::SendJsonError(response, 403, "Path contains '..' (not allowed).");
String path = ConfigPackageUtility::GetPackageDir() + "/" + packageName + "/" + stageName + "/" + relativePath;
- if (!Utility::PathExists(path)) {
- response.SetStatus(404, "File not found");
- return;
- }
+ if (!Utility::PathExists(path))
+ return HttpUtility::SendJsonError(response, 404, "Path not found.");
try {
std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary);
response.AddHeader("Content-Type", "application/octet-stream");
response.WriteBody(content.CStr(), content.GetLength());
} catch (const std::exception& ex) {
- response.SetStatus(503, "Could not read file");
+ return HttpUtility::SendJsonError(response, 500, "Could not read file.",
+ request.GetVerboseErrors() ? DiagnosticInformation(ex) : "");
}
}
ConfigWriter::EmitConfigItem(config, type->GetName(), name, false, templates, allAttrs);
ConfigWriter::EmitRaw(config, "\n");
- return config.str();
+ return config.str();
}
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
if (!parents.empty() && !cascade) {
if (errors)
- errors->Add("Object cannot be deleted because other objects depend on it. Use cascading delete to delete it anyway.");
+ errors->Add("Object cannot be deleted because other objects depend on it. "
+ "Use cascading delete to delete it anyway.");
return false;
}
#include "remote/configpackageutility.hpp"
#include "remote/httputility.hpp"
#include "base/exception.hpp"
+#include <boost/algorithm/string/join.hpp>
using namespace icinga;
bool ConfigPackagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
- if (request.RequestUrl->GetPath().size() > 4)
- return false;
+ if (request.RequestUrl->GetPath().size() > 4) {
+ String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/");
+ HttpUtility::SendJsonError(response, 404, "The requested path is too long to match any config package requests");
+ return true;
+ }
if (request.RequestMethod == "GET")
HandleGet(user, request, response);
else if (request.RequestMethod == "DELETE")
HandleDelete(user, request, response);
else
- response.SetStatus(400, "Bad request");
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be GET, POST or DELETE.");
return true;
}
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidateName(packageName)) {
- response.SetStatus(403, "Forbidden");
+ HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
return;
}
- int code = 200;
- String status = "Created package.";
+ Dictionary::Ptr result1 = new Dictionary();
try {
ConfigPackageUtility::CreatePackage(packageName);
} catch (const std::exception& ex) {
- code = 501;
- status = "Error: " + DiagnosticInformation(ex);
+ HttpUtility::SendJsonError(response, 500, "Could not create package.",
+ request.GetVerboseErrors() ? DiagnosticInformation(ex) : "");
}
- Dictionary::Ptr result1 = new Dictionary();
-
- result1->Set("package", packageName);
- result1->Set("code", code);
- result1->Set("status", status);
+ result1->Set("code", 200);
+ result1->Set("status", "Created package.");
Array::Ptr results = new Array();
results->Add(result1);
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
- response.SetStatus(code, (code == 200) ? "OK" : "Error");
+ response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, result);
}
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidateName(packageName)) {
- response.SetStatus(403, "Forbidden");
+ HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
return;
}
int code = 200;
String status = "Deleted package.";
+ Dictionary::Ptr result1 = new Dictionary();
try {
ConfigPackageUtility::DeletePackage(packageName);
} catch (const std::exception& ex) {
- code = 501;
- status = "Error: " + DiagnosticInformation(ex);
+ code = 500;
+ status = "Failed to delete package.";
+ if (request.GetVerboseErrors())
+ result1->Set("diagnostic information", DiagnosticInformation(ex));
}
- Dictionary::Ptr result1 = new Dictionary();
result1->Set("package", packageName);
result1->Set("code", code);
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
- response.SetStatus(code, (code == 200) ? "OK" : "Error");
+ response.SetStatus(code, (code == 200) ? "OK" : "Internal Server Error");
HttpUtility::SendJsonBody(response, result);
}
std::vector<String> ConfigPackageUtility::GetPackages(void)
{
std::vector<String> packages;
- Utility::Glob(GetPackageDir() + "/*", boost::bind(&ConfigPackageUtility::CollectDirNames, _1, boost::ref(packages)), GlobDirectory);
+ Utility::Glob(GetPackageDir() + "/*", boost::bind(&ConfigPackageUtility::CollectDirNames,
+ _1, boost::ref(packages)), GlobDirectory);
return packages;
}
WriteStageConfig(packageName, stageName);
bool foundDotDot = false;
-
+
if (files) {
ObjectLock olock(files);
BOOST_FOREACH(const Dictionary::Pair& kv, files) {
foundDotDot = true;
break;
}
-
+
String filePath = path + "/" + kv.first;
-
+
Log(LogInformation, "ConfigPackageUtility")
<< "Updating configuration file: " << filePath;
-
+
//pass the directory and generate a dir tree, if not existing already
Utility::MkDirP(Utility::DirName(filePath), 0750);
std::ofstream fp(filePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
return (!boost::regex_search(name.GetData(), what, expr));
}
-
#include "base/application.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
+#include <boost/algorithm/string/join.hpp>
using namespace icinga;
bool ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
- if (request.RequestUrl->GetPath().size() > 5)
- return false;
+ if (request.RequestUrl->GetPath().size() > 5) {
+ String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/");
+ HttpUtility::SendJsonError(response, 404, "The requested path is too long to match any config tag requests.");
+ return true;
+ }
if (request.RequestMethod == "GET")
HandleGet(user, request, response);
else if (request.RequestMethod == "DELETE")
HandleDelete(user, request, response);
else
- response.SetStatus(400, "Bad request");
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be GET, POST or DELETE.");
return true;
}
String packageName = HttpUtility::GetLastParameter(params, "package");
String stageName = HttpUtility::GetLastParameter(params, "stage");
- if (!ConfigPackageUtility::ValidateName(packageName) || !ConfigPackageUtility::ValidateName(stageName)) {
- response.SetStatus(403, "Forbidden");
- return;
- }
+ if (!ConfigPackageUtility::ValidateName(packageName))
+ return HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
+
+ if (!ConfigPackageUtility::ValidateName(stageName))
+ return HttpUtility::SendJsonError(response, 404, "Stage is not valid or does not exist.");
Array::Ptr results = new Array();
String packageName = HttpUtility::GetLastParameter(params, "package");
- if (!ConfigPackageUtility::ValidateName(packageName)) {
- response.SetStatus(403, "Forbidden");
- return;
- }
+ if (!ConfigPackageUtility::ValidateName(packageName))
+ return HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
Dictionary::Ptr files = params->Get("files");
- int code = 200;
- String status = "Created stage.";
String stageName;
try {
/* validate the config. on success, activate stage and reload */
ConfigPackageUtility::AsyncTryActivateStage(packageName, stageName);
} catch (const std::exception& ex) {
- code = 501;
- status = "Error: " + DiagnosticInformation(ex);
+ return HttpUtility::SendJsonError(response, 500,
+ "Stage creation failed.",
+ request.GetVerboseErrors() ? DiagnosticInformation(ex) : "");
}
Dictionary::Ptr result1 = new Dictionary();
- result1->Set("package", packageName);
- result1->Set("stage", stageName);
- result1->Set("code", code);
- result1->Set("status", status);
+ result1->Set("code", 200);
+ result1->Set("status", "Created stage.");
Array::Ptr results = new Array();
results->Add(result1);
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
- response.SetStatus(code, (code == 200) ? "OK" : "Error");
+ response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, result);
}
String packageName = HttpUtility::GetLastParameter(params, "package");
String stageName = HttpUtility::GetLastParameter(params, "stage");
- if (!ConfigPackageUtility::ValidateName(packageName) || !ConfigPackageUtility::ValidateName(stageName)) {
- response.SetStatus(403, "Forbidden");
- return;
- }
+ if (!ConfigPackageUtility::ValidateName(packageName))
+ return HttpUtility::SendJsonError(response, 404, "Package is not valid or does not exist.");
- int code = 200;
- String status = "Deleted stage.";
+ if (!ConfigPackageUtility::ValidateName(stageName))
+ return HttpUtility::SendJsonError(response, 404, "Stage is not valid or does not exist.");
try {
ConfigPackageUtility::DeleteStage(packageName, stageName);
} catch (const std::exception& ex) {
- code = 501;
- status = "Error: " + DiagnosticInformation(ex);
+ return HttpUtility::SendJsonError(response, 500,
+ "Failed to delete stage.",
+ request.GetVerboseErrors() ? DiagnosticInformation(ex) : "");
}
Dictionary::Ptr result1 = new Dictionary();
- result1->Set("package", packageName);
- result1->Set("stage", stageName);
- result1->Set("code", code);
- result1->Set("status", status);
+ result1->Set("code", 200);
+ result1->Set("status", "Stage deleted");
Array::Ptr results = new Array();
results->Add(result1);
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
- response.SetStatus(code, (code == 200) ? "OK" : "Error");
+ response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, result);
}
bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
- if (request.RequestMethod != "PUT")
- return false;
+ if (request.RequestMethod != "PUT") {
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be PUT.");
+ return true;
+ }
- if (request.RequestUrl->GetPath().size() < 3)
- return false;
+ if (request.RequestUrl->GetPath().size() < 3) {
+ HttpUtility::SendJsonError(response, 400, "Object name is missing.");
+ return true;
+ }
Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[1]);
- if (!type)
- return false;
+ if (!type) {
+ HttpUtility::SendJsonError(response, 403, "Erroneous type was supplied.");
+ return true;
+ }
String name = request.RequestUrl->GetPath()[2];
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
int code;
String status;
Array::Ptr errors = new Array();
-
+
String config = ConfigObjectUtility::CreateObjectConfig(type, name, templates, attrs);
-
+
if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) {
result1->Set("errors", errors);
- code = 500;
- status = "Object could not be created.";
- } else {
- code = 200;
- status = "Object was created.";
+ HttpUtility::SendJsonError(response, 500, "Object could not be created.");
+ return true;
}
- result1->Set("code", code);
- result1->Set("status", status);
+ result1->Set("code", 200);
+ result1->Set("status", "Object was created");
Array::Ptr results = new Array();
results->Add(result1);
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
- response.SetStatus(code, status);
+ response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, result);
return true;
bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
- if (request.RequestMethod != "DELETE")
- return false;
+ if (request.RequestMethod != "DELETE") {
+ HttpUtility::SendJsonError(response, 400, "Invalid request type. Must be DELETE.");
+ return true;
+ }
- if (request.RequestUrl->GetPath().size() < 2)
- return false;
+ if (request.RequestUrl->GetPath().size() < 2) {
+ String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/");
+ HttpUtility::SendJsonError(response, 404, "The requested path is too long to match any config tag requests.");
+ return true;
+ }
Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[1]);
- if (!type)
- return false;
+ if (!type) {
+ HttpUtility::SendJsonError(response, 400, "Erroneous type was supplied.");
+ return true;
+ }
QueryDescription qd;
qd.Types.insert(type->GetName());
results->Add(result1);
Array::Ptr errors = new Array();
-
+
if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors)) {
- result1->Set("code", 500);
+ result1->Set("code", 500);
result1->Set("status", "Object could not be deleted.");
result1->Set("errors", errors);
} else {
if (members.find(this) != members.end()) {
if (m_Zone)
- BOOST_THROW_EXCEPTION(ScriptError("Endpoint '" + GetName() + "' is in more than one zone.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Endpoint '" + GetName()
+ + "' is in more than one zone.", GetDebugInfo()));
m_Zone = zone;
}
}
if (!m_Zone)
- BOOST_THROW_EXCEPTION(ScriptError("Endpoint '" + GetName() + "' does not belong to a zone.", GetDebugInfo()));
+ BOOST_THROW_EXCEPTION(ScriptError("Endpoint '" + GetName() +
+ "' does not belong to a zone.", GetDebugInfo()));
}
void Endpoint::AddClient(const JsonRpcConnection::Ptr& client)
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
+ /* m_Stream = new NetworkStream(socket);
+ -- does not currently work because the NetworkStream class doesn't support async I/O */
m_Stream->RegisterDataHandler(boost::bind(&HttpClientConnection::DataAvailableHandler, this));
if (m_Stream->IsDataAvailable())
return boost::make_shared<HttpRequest>(m_Stream);
}
-void HttpClientConnection::SubmitRequest(const boost::shared_ptr<HttpRequest>& request, const HttpCompletionCallback& callback)
+void HttpClientConnection::SubmitRequest(const boost::shared_ptr<HttpRequest>& request,
+ const HttpCompletionCallback& callback)
{
m_Requests.push_back(std::make_pair(request, callback));
request->Finish();
}
+
******************************************************************************/
#include "remote/httphandler.hpp"
+#include "remote/httputility.hpp"
#include "base/singleton.hpp"
+#include <boost/algorithm/string/join.hpp>
using namespace icinga;
}
}
if (!processed) {
- response.SetStatus(404, "Not found");
- response.AddHeader("Content-Type", "text/html");
- String msg = "<h1>Not found</h1>";
- response.WriteBody(msg.CStr(), msg.GetLength());
+ String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/");
+ HttpUtility::SendJsonError(response, 404, "The requested API '" + path +
+ "' could not be found. Please check it for common errors like spelling and consult the docs.");
return;
}
}
+
ProtocolVersion(HttpVersion11),
Headers(new Dictionary()),
m_Stream(stream),
- m_State(HttpRequestStart)
+ m_State(HttpRequestStart),
+ verboseErrors(false)
{ }
bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
<< "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]);
+ verboseErrors = (RequestUrl->GetQueryElement("verboseErrors") == "true");
if (tokens[2] == "HTTP/1.0")
ProtocolVersion = HttpVersion10;
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 key = line.SubStr(0, pos).ToLower().Trim();
String value = line.SubStr(pos + 1).Trim();
Headers->Set(key, value);
m_State = HttpRequestEnd;
}
+
void WriteBody(const char *data, size_t count);
void Finish(void);
+ inline bool GetVerboseErrors(void)
+ { return verboseErrors; }
+
private:
Stream::Ptr m_Stream;
boost::shared_ptr<ChunkReadContext> m_ChunkContext;
HttpRequestState m_State;
FIFO::Ptr m_Body;
+ bool verboseErrors;
void FinishHeaders(void);
};
if (m_State == HttpResponseHeaders) {
if (m_Request.ProtocolVersion == HttpVersion11)
AddHeader("Transfer-Encoding", "chunked");
-
+
AddHeader("Server", "Icinga/" + Application::GetAppVersion());
m_Stream->Write("\r\n", 2);
m_State = HttpResponseBody;
else
return m_Body->Read(data, count, true);
}
+
}
if (m_CurrentRequest.Complete) {
- m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync, HttpServerConnection::Ptr(this), m_CurrentRequest));
+ m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync,
+ HttpServerConnection::Ptr(this), m_CurrentRequest));
m_Seen = Utility::GetTime();
m_PendingRequests++;
client->CheckLiveness();
}
}
+
else
return arr->Get(arr->GetLength() - 1);
}
+
+void HttpUtility::SendJsonError(HttpResponse& response, const int code,
+ const String& info, const String& diagnosticInformation)
+{
+ Dictionary::Ptr result = new Dictionary();
+ response.SetStatus(code, HttpUtility::GetErrorNameByCode(code));
+ result->Set("error", code);
+ if (!info.IsEmpty())
+ result->Set("status", info);
+ if (!diagnosticInformation.IsEmpty())
+ result->Set("diagnostic information", diagnosticInformation);
+ HttpUtility::SendJsonBody(response, result);
+}
+
+String HttpUtility::GetErrorNameByCode(const int code)
+{
+ switch(code) {
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 204:
+ return "No Content";
+ case 304:
+ return "Not Modified";
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 409:
+ return "Conflict";
+ case 500:
+ return "Internal Server Error";
+ default:
+ return "Unknown Error Code";
+ }
+}
+
static Dictionary::Ptr FetchRequestParameters(HttpRequest& request);
static void SendJsonBody(HttpResponse& response, const Value& val);
static Value GetLastParameter(const Dictionary::Ptr& params, const String& key);
+ static void SendJsonError(HttpResponse& response, const int code,
+ const String& verbose="", const String& diagnosticInformation="");
+
+private:
+ static String GetErrorNameByCode(int code);
+
};
}
#include "remote/jsonrpc.hpp"
#include "base/netstring.hpp"
#include "base/json.hpp"
-//#include <iostream>
using namespace icinga;
void JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message)
{
String json = JsonEncode(message);
- //std::cerr << ">> " << json << std::endl;
NetString::WriteStringToStream(stream, json);
}
if (srs != StatusNewItem)
return srs;
- //std::cerr << "<< " << jsonString << std::endl;
Value value = JsonDecode(jsonString);
if (!value.IsObjectType<Dictionary>()) {
return StatusNewItem;
}
+
static boost::once_flag l_JsonRpcConnectionOnceFlag = BOOST_ONCE_INIT;
static Timer::Ptr l_JsonRpcConnectionTimeoutTimer;
-JsonRpcConnection::JsonRpcConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream, ConnectionRole role)
- : m_Identity(identity), m_Authenticated(authenticated), m_Stream(stream), m_Role(role), m_Seen(Utility::GetTime()),
+JsonRpcConnection::JsonRpcConnection(const String& identity, bool authenticated,
+ const TlsStream::Ptr& stream, ConnectionRole role)
+ : m_Identity(identity), m_Authenticated(authenticated), m_Stream(stream),
+ m_Role(role), m_Seen(Utility::GetTime()),
m_NextHeartbeat(0), m_HeartbeatTimeout(0)
{
boost::call_once(l_JsonRpcConnectionOnceFlag, &JsonRpcConnection::StaticInitialize);
resultMessage->Set("result", afunc->Invoke(origin, message->Get("params")));
} catch (const std::exception& ex) {
- //TODO: Add a user readable error message for the remote caller
+ /* TODO: Add a user readable error message for the remote caller */
resultMessage->Set("error", DiagnosticInformation(ex));
std::ostringstream info;
info << "Error while processing message for identity '" << m_Identity << "'";
; /* empty loop body */
} catch (const std::exception& ex) {
Log(LogWarning, "JsonRpcConnection")
- << "Error while reading JSON-RPC message for identity '" << m_Identity << "': " << DiagnosticInformation(ex);
+ << "Error while reading JSON-RPC message for identity '" << m_Identity
+ << "': " << DiagnosticInformation(ex);
Disconnect();
}
}
}
}
+
public:
DECLARE_PTR_TYPEDEFS(TypeTargetProvider);
- virtual void FindTargets(const String& type, const boost::function<void (const Value&)>& addTarget) const override
+ virtual void FindTargets(const String& type,
+ const boost::function<void (const Value&)>& addTarget) const override
{
std::vector<Type::Ptr> targets;
param = "?";
else
param += "&";
-
+
String temp;
BOOST_FOREACH (const String s, kv.second) {
if (!temp.IsEmpty())
bool Url::ParseQuery(const String& query)
{
- //Tokenizer does not like String AT ALL
+ /* Tokenizer does not like String AT ALL */
std::string queryStr = query;
boost::char_separator<char> sep("&");
boost::tokenizer<boost::char_separator<char> > tokens(queryStr, sep);
if (pHelper == 0)
// /?foo=bar&=bar == invalid
return false;
-
+
String key = token.SubStr(0, pHelper);
String value = Empty;
if (!ValidateToken(value, ACQUERY))
return false;
-
+
value = Utility::UnescapeString(value);
pHelper = key.Find("[]");
return false;
key = key.SubStr(0, pHelper);
-
+
if (!ValidateToken(key, ACQUERY))
return false;
m_Query[key].push_back(value);
} else
m_Query[key].push_back(value);
-
+
}
return true;
return true;
}
+
return local->GetZone();
}
+