From 6168573e867296415a5e1adad84b2c5469d8fc1c Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 16:07:21 +0000 Subject: [PATCH] Feature: Render error messages via Report XML and XSLT --- admin/error-html.xsl | 17 +++++++ admin/error-plaintext.xsl | 10 ++++ src/client.c | 100 ++++++++++++++++++-------------------- src/client.h | 2 + src/errors.c | 5 +- src/errors.h | 3 +- src/xslt.c | 19 ++++++-- 7 files changed, 96 insertions(+), 60 deletions(-) create mode 100644 admin/error-html.xsl create mode 100644 admin/error-plaintext.xsl diff --git a/admin/error-html.xsl b/admin/error-html.xsl new file mode 100644 index 00000000..98c19417 --- /dev/null +++ b/admin/error-html.xsl @@ -0,0 +1,17 @@ + + + + Error + + +
+ +
+

Response

+

Message

+

+
+
+
+
+
diff --git a/admin/error-plaintext.xsl b/admin/error-plaintext.xsl new file mode 100644 index 00000000..cece3b4a --- /dev/null +++ b/admin/error-plaintext.xsl @@ -0,0 +1,10 @@ + + + + + Report: + + + + + diff --git a/src/client.c b/src/client.c index 9f694518..ffc80d39 100644 --- a/src/client.c +++ b/src/client.c @@ -239,79 +239,73 @@ int client_read_bytes(client_t *client, void *buf, unsigned len) return bytes; } -static inline void _client_send_error(client_t *client, int plain, const icecast_error_t *error) +static inline void _client_send_error(client_t *client, const icecast_error_t *error) { - ssize_t ret; - refbuf_t *data; + ice_config_t *config; + reportxml_t *report; + admin_format_t admin_format; + const char *xslt = NULL; - if (error->http_status == 500) { - client_send_500(client, error->message); - return; + admin_format = client_get_admin_format_by_content_negotiation(client); + + switch (admin_format) { + case ADMIN_FORMAT_RAW: + xslt = NULL; + break; + case ADMIN_FORMAT_TRANSFORMED: + xslt = CLIENT_DEFAULT_ERROR_XSL_TRANSFORMED; + break; + case ADMIN_FORMAT_PLAINTEXT: + xslt = CLIENT_DEFAULT_ERROR_XSL_PLAINTEXT; + break; + default: + client_send_500(client, "Invalid Admin Type"); + break; } - data = refbuf_new(PER_CLIENT_REFBUF_SIZE); - if (!data) { - client_send_500(client, error->message); - return; + + config = config_get_config(); + report = reportxml_database_build_report(config->reportxml_db, error->uuid, -1); + config_release_config(); + + if (!report) { + reportxml_node_t *root, *incident, *state, *text; + + report = reportxml_new(); + root = reportxml_get_root_node(report); + incident = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL); + state = reportxml_node_new(REPORTXML_NODE_TYPE_STATE, NULL, error->uuid, NULL); + text = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL); + reportxml_node_set_content(text, error->message); + reportxml_node_add_child(state, text); + reportxml_node_add_child(incident, state); + reportxml_node_add_child(root, incident); + refobject_unref(text); + refobject_unref(state); + refobject_unref(incident); + refobject_unref(root); } - client->reuse = ICECAST_REUSE_KEEPALIVE; + client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status); - ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, - 0, error->http_status, NULL, - plain ? "text/plain" : "text/html", "utf-8", - NULL, NULL, client); - - if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) { - ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); - client_send_500(client, "Header generation failed."); - return; - } - - if (plain) { - snprintf(data->data, data->len, "Error %i\r\n---------\r\n\r\nMessage: %s\r\n\r\nError code: %s\r\n", - error->http_status, error->message, error->uuid - ); - } else { - snprintf(data->data, data->len, - "Error %i

Error %i


%s

Error code: %s

\r\n", - error->http_status, error->http_status, error->message, error->uuid); - } - data->len = strlen(data->data); - - snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, - "Content-Length: %llu\r\n\r\n", - (long long unsigned int)data->len); - - client->respcode = error->http_status; - client->refbuf->len = strlen (client->refbuf->data); - client->refbuf->next = data; - - fserve_add_client (client, NULL); + refobject_unref(report); } void client_send_error_by_id(client_t *client, icecast_error_id_t id) { const icecast_error_t *error = error_get_by_id(id); - const char *pref; - int plain; if (!error) { client_send_500(client, "Unknown error ID"); return; } - pref = util_http_select_best(httpp_getvar(client->parser, "accept"), "text/plain", "text/html", (const char*)NULL); - - if (strcmp(pref, "text/plain") == 0) { - plain = 1; - } else if (strcmp(pref, "text/html") == 0) { - plain = 0; - } else { - plain = 1; + if (error->http_status == 500) { + client_send_500(client, error->message); + return; } - _client_send_error(client, plain, error); + _client_send_error(client, error); } void client_send_101(client_t *client, reuse_t reuse) diff --git a/src/client.h b/src/client.h index 08757304..f0aab70b 100644 --- a/src/client.h +++ b/src/client.h @@ -29,6 +29,8 @@ #define CLIENT_DEFAULT_REPORT_XSL_TRANSFORMED "report-html.xsl" #define CLIENT_DEFAULT_REPORT_XSL_PLAINTEXT "report-plaintext.xsl" +#define CLIENT_DEFAULT_ERROR_XSL_TRANSFORMED "error-html.xsl" +#define CLIENT_DEFAULT_ERROR_XSL_PLAINTEXT "error-plaintext.xsl" #define CLIENT_DEFAULT_ADMIN_FORMAT ADMIN_FORMAT_TRANSFORMED typedef enum _document_domain_tag { diff --git a/src/errors.c b/src/errors.c index d570cb37..d553a523 100644 --- a/src/errors.c +++ b/src/errors.c @@ -132,7 +132,10 @@ static const icecast_error_t __errors[] = { .message = "Could not parse XSLT file"}, {.id = ICECAST_ERROR_XSLT_problem, .http_status = 500, .uuid = "d3c6e4b3-7d6e-4191-a81b-970273067ae3", - .message = "XSLT problem"} + .message = "XSLT problem"}, + {.id = ICECAST_ERROR_RECURSIVE_ERROR, .http_status = 500, + .uuid = "13489d5c-eae6-4bf3-889e-ec1fa9a9b9ac", + .message = "Recursive error"} }; const icecast_error_t * error_get_by_id(icecast_error_id_t id) { diff --git a/src/errors.h b/src/errors.h index 0b14c028..babfed91 100644 --- a/src/errors.h +++ b/src/errors.h @@ -48,7 +48,8 @@ typedef enum { ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE, ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR, ICECAST_ERROR_XSLT_PARSE, - ICECAST_ERROR_XSLT_problem + ICECAST_ERROR_XSLT_problem, + ICECAST_ERROR_RECURSIVE_ERROR } icecast_error_id_t; struct icecast_error_tag { diff --git a/src/xslt.c b/src/xslt.c index 12de9376..366ed4c2 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -285,6 +285,15 @@ static xmlDocPtr custom_loader(const xmlChar *URI, return ret; } +static inline void _send_error(client_t *client, icecast_error_id_t id, int old_status) { + if (old_status >= 400) { + client_send_error_by_id(client, ICECAST_ERROR_RECURSIVE_ERROR); + return; + } + + client_send_error_by_id(client, id); +} + void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status) { xmlDocPtr res; @@ -305,7 +314,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in { thread_mutex_unlock(&xsltlock); ICECAST_LOG_ERROR("problem reading stylesheet \"%s\"", xslfilename); - client_send_error_by_id(client, ICECAST_ERROR_XSLT_PARSE); + _send_error(client, ICECAST_ERROR_XSLT_PARSE, status); return; } @@ -349,7 +358,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in ret = util_http_build_header(refbuf->data, full_len, 0, 0, status, NULL, mediatype, charset, NULL, NULL, client); if (ret == -1) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); - client_send_error_by_id(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); + _send_error(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED, status); } else { if ( full_len < (ret + (ssize_t)len + (ssize_t)64) ) { void *new_data; @@ -362,12 +371,12 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in ret = util_http_build_header(refbuf->data, full_len, 0, 0, status, NULL, mediatype, charset, NULL, NULL, client); if (ret == -1) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); - client_send_error_by_id(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); + _send_error(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED, status); failed = 1; } } else { ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client."); - client_send_error_by_id(client, ICECAST_ERROR_GEN_BUFFER_REALLOC); + _send_error(client, ICECAST_ERROR_GEN_BUFFER_REALLOC, status); failed = 1; } } @@ -387,7 +396,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in else { ICECAST_LOG_WARN("problem applying stylesheet \"%s\"", xslfilename); - client_send_error_by_id(client, ICECAST_ERROR_XSLT_problem); + _send_error(client, ICECAST_ERROR_XSLT_problem, status); } thread_mutex_unlock (&xsltlock); xmlFreeDoc(res);