MXS-1220: Add error messages to bad request
Requests now contain a JSON API-conforming, human-readable error message.
This commit is contained in:
@ -268,13 +268,14 @@ complete the request.
|
|||||||
### 4xx Client Error
|
### 4xx Client Error
|
||||||
|
|
||||||
The 4xx class of status code is when the client seems to have erred. Except when
|
The 4xx class of status code is when the client seems to have erred. Except when
|
||||||
responding to a HEAD request, the body of the response contains a JSON
|
responding to a HEAD request, the body of the response *MAY* contains a JSON
|
||||||
representation of the error in the following format.
|
representation of the error.
|
||||||
|
|
||||||
```
|
```javascript
|
||||||
{
|
{
|
||||||
"error": "Method not supported",
|
"error": {
|
||||||
"description": "The `/service` resource does not support POST."
|
"detail" : "The new `/server/` resource is missing the `port` parameter"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <maxscale/atomic.h>
|
#include <maxscale/atomic.h>
|
||||||
#include <maxscale/paths.h>
|
#include <maxscale/paths.h>
|
||||||
|
#include <maxscale/platform.h>
|
||||||
#include <maxscale/spinlock.h>
|
#include <maxscale/spinlock.h>
|
||||||
#include <maxscale/jansson.hh>
|
#include <maxscale/jansson.hh>
|
||||||
#include <maxscale/json_api.h>
|
#include <maxscale/json_api.h>
|
||||||
@ -40,6 +41,8 @@ using mxs::Closer;
|
|||||||
|
|
||||||
static SPINLOCK crt_lock = SPINLOCK_INIT;
|
static SPINLOCK crt_lock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
thread_local stringstream runtime_errmsg;
|
||||||
|
|
||||||
bool runtime_link_server(SERVER *server, const char *target)
|
bool runtime_link_server(SERVER *server, const char *target)
|
||||||
{
|
{
|
||||||
spinlock_acquire(&crt_lock);
|
spinlock_acquire(&crt_lock);
|
||||||
@ -55,6 +58,11 @@ bool runtime_link_server(SERVER *server, const char *target)
|
|||||||
service_serialize(service);
|
service_serialize(service);
|
||||||
rval = true;
|
rval = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Service '" << service->name << "' already uses server '"
|
||||||
|
<< server->unique_name << "'";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (monitor)
|
else if (monitor)
|
||||||
{
|
{
|
||||||
@ -63,6 +71,10 @@ bool runtime_link_server(SERVER *server, const char *target)
|
|||||||
monitor_serialize(monitor);
|
monitor_serialize(monitor);
|
||||||
rval = true;
|
rval = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Server '" << server->unique_name << "' is already monitored";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rval)
|
if (rval)
|
||||||
@ -165,6 +177,10 @@ bool runtime_create_server(const char *name, const char *address, const char *po
|
|||||||
rval = true;
|
rval = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Server '" << name << "' already exists";
|
||||||
|
}
|
||||||
|
|
||||||
spinlock_release(&crt_lock);
|
spinlock_release(&crt_lock);
|
||||||
return rval;
|
return rval;
|
||||||
@ -177,6 +193,8 @@ bool runtime_destroy_server(SERVER *server)
|
|||||||
|
|
||||||
if (service_server_in_use(server) || monitor_server_in_use(server))
|
if (service_server_in_use(server) || monitor_server_in_use(server))
|
||||||
{
|
{
|
||||||
|
runtime_errmsg << "Cannot destroy server '" << server->unique_name <<
|
||||||
|
"' as it is used by at least one service or monitor";
|
||||||
MXS_ERROR("Cannot destroy server '%s' as it is used by at least one "
|
MXS_ERROR("Cannot destroy server '%s' as it is used by at least one "
|
||||||
"service or monitor", server->unique_name);
|
"service or monitor", server->unique_name);
|
||||||
}
|
}
|
||||||
@ -319,9 +337,16 @@ bool runtime_alter_server(SERVER *server, const char *key, const char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid && server_serialize(server))
|
if (valid)
|
||||||
{
|
{
|
||||||
MXS_NOTICE("Updated server '%s': %s=%s", server->unique_name, key, value);
|
if (server_serialize(server))
|
||||||
|
{
|
||||||
|
MXS_NOTICE("Updated server '%s': %s=%s", server->unique_name, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Invalid server parameter: " << key;
|
||||||
}
|
}
|
||||||
|
|
||||||
spinlock_release(&crt_lock);
|
spinlock_release(&crt_lock);
|
||||||
@ -468,6 +493,10 @@ bool runtime_alter_monitor(MXS_MONITOR *monitor, const char *key, const char *va
|
|||||||
|
|
||||||
MXS_NOTICE("Updated monitor '%s': %s=%s", monitor->name, key, value);
|
MXS_NOTICE("Updated monitor '%s': %s=%s", monitor->name, key, value);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Invalid monitor parameter: " << key;
|
||||||
|
}
|
||||||
|
|
||||||
spinlock_release(&crt_lock);
|
spinlock_release(&crt_lock);
|
||||||
return valid;
|
return valid;
|
||||||
@ -537,6 +566,7 @@ bool runtime_alter_service(SERVICE *service, const char* zKey, const char* zValu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
runtime_errmsg << "Invalid service parameter: " << key;
|
||||||
MXS_ERROR("Unknown parameter for service '%s': %s=%s",
|
MXS_ERROR("Unknown parameter for service '%s': %s=%s",
|
||||||
service->name, key.c_str(), value.c_str());
|
service->name, key.c_str(), value.c_str());
|
||||||
valid = false;
|
valid = false;
|
||||||
@ -703,7 +733,7 @@ bool runtime_create_monitor(const char *name, const char *module)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_WARNING("Can't create monitor, it already exists");
|
runtime_errmsg << "Can't create monitor '" << name << "', it already exists";
|
||||||
}
|
}
|
||||||
|
|
||||||
spinlock_release(&crt_lock);
|
spinlock_release(&crt_lock);
|
||||||
@ -908,10 +938,14 @@ SERVER* runtime_create_server_from_json(json_t* json)
|
|||||||
rval = NULL;
|
rval = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Invalid relationships in request JSON";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_WARNING("Invalid request JSON: %s", mxs::json_dump(json).c_str());
|
runtime_errmsg << "Missing or bad parameters in request JSON";
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
@ -1082,7 +1116,7 @@ MXS_MONITOR* runtime_create_monitor_from_json(json_t* json)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_WARNING("Invalid request JSON: %s", mxs::json_dump(json).c_str());
|
runtime_errmsg << "Missing or bad parameters in request JSON";
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
@ -1386,6 +1420,31 @@ bool runtime_create_listener_from_json(SERVICE* service, json_t* json)
|
|||||||
ssl_key, ssl_cert, ssl_ca_cert, ssl_version,
|
ssl_key, ssl_cert, ssl_ca_cert, ssl_version,
|
||||||
ssl_cert_verify_depth);
|
ssl_cert_verify_depth);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runtime_errmsg << "Missing or bad parameters in request JSON";
|
||||||
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json_t* runtime_get_json_error()
|
||||||
|
{
|
||||||
|
json_t* obj = NULL;
|
||||||
|
string errmsg = runtime_errmsg.str();
|
||||||
|
|
||||||
|
if (errmsg.length())
|
||||||
|
{
|
||||||
|
runtime_errmsg.str("");
|
||||||
|
json_t* err = json_object();
|
||||||
|
json_object_set_new(err, "detail", json_string(errmsg.c_str()));
|
||||||
|
|
||||||
|
json_t* arr = json_array();
|
||||||
|
json_array_append_new(arr, err);
|
||||||
|
|
||||||
|
obj = json_object();
|
||||||
|
json_object_set_new(obj, "errors", arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|||||||
@ -257,4 +257,11 @@ bool runtime_create_listener_from_json(SERVICE* service, json_t* json);
|
|||||||
*/
|
*/
|
||||||
bool runtime_alter_logs_from_json(json_t* json);
|
bool runtime_alter_logs_from_json(json_t* json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current runtime error in JSON format
|
||||||
|
*
|
||||||
|
* @return The latest runtime error in JSON format or NULL if no error has occurred
|
||||||
|
*/
|
||||||
|
json_t* runtime_get_json_error();
|
||||||
|
|
||||||
MXS_END_DECLS
|
MXS_END_DECLS
|
||||||
|
|||||||
@ -223,7 +223,7 @@ HttpResponse cb_create_server(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_alter_server(const HttpRequest& request)
|
HttpResponse cb_alter_server(const HttpRequest& request)
|
||||||
@ -240,7 +240,7 @@ HttpResponse cb_alter_server(const HttpRequest& request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_create_monitor(const HttpRequest& request)
|
HttpResponse cb_create_monitor(const HttpRequest& request)
|
||||||
@ -252,7 +252,7 @@ HttpResponse cb_create_monitor(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_create_service_listener(const HttpRequest& request)
|
HttpResponse cb_create_service_listener(const HttpRequest& request)
|
||||||
@ -265,7 +265,7 @@ HttpResponse cb_create_service_listener(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_alter_monitor(const HttpRequest& request)
|
HttpResponse cb_alter_monitor(const HttpRequest& request)
|
||||||
@ -282,7 +282,7 @@ HttpResponse cb_alter_monitor(const HttpRequest& request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_alter_service(const HttpRequest& request)
|
HttpResponse cb_alter_service(const HttpRequest& request)
|
||||||
@ -299,7 +299,7 @@ HttpResponse cb_alter_service(const HttpRequest& request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_alter_logs(const HttpRequest& request)
|
HttpResponse cb_alter_logs(const HttpRequest& request)
|
||||||
@ -311,7 +311,7 @@ HttpResponse cb_alter_logs(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_delete_server(const HttpRequest& request)
|
HttpResponse cb_delete_server(const HttpRequest& request)
|
||||||
@ -323,7 +323,7 @@ HttpResponse cb_delete_server(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_delete_monitor(const HttpRequest& request)
|
HttpResponse cb_delete_monitor(const HttpRequest& request)
|
||||||
@ -335,7 +335,7 @@ HttpResponse cb_delete_monitor(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
return HttpResponse(MHD_HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_FORBIDDEN);
|
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_all_servers(const HttpRequest& request)
|
HttpResponse cb_all_servers(const HttpRequest& request)
|
||||||
@ -352,7 +352,7 @@ HttpResponse cb_get_server(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host()));
|
return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR);
|
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_all_services(const HttpRequest& request)
|
HttpResponse cb_all_services(const HttpRequest& request)
|
||||||
@ -369,7 +369,7 @@ HttpResponse cb_get_service(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_OK, service_to_json(service, request.host()));
|
return HttpResponse(MHD_HTTP_OK, service_to_json(service, request.host()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR);
|
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_get_service_listeners(const HttpRequest& request)
|
HttpResponse cb_get_service_listeners(const HttpRequest& request)
|
||||||
@ -392,7 +392,7 @@ HttpResponse cb_get_filter(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_OK, filter_to_json(filter, request.host()));
|
return HttpResponse(MHD_HTTP_OK, filter_to_json(filter, request.host()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR);
|
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_all_monitors(const HttpRequest& request)
|
HttpResponse cb_all_monitors(const HttpRequest& request)
|
||||||
@ -409,7 +409,7 @@ HttpResponse cb_get_monitor(const HttpRequest& request)
|
|||||||
return HttpResponse(MHD_HTTP_OK, monitor_to_json(monitor, request.host()));
|
return HttpResponse(MHD_HTTP_OK, monitor_to_json(monitor, request.host()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR);
|
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse cb_all_sessions(const HttpRequest& request)
|
HttpResponse cb_all_sessions(const HttpRequest& request)
|
||||||
|
|||||||
Reference in New Issue
Block a user