Commit 6168573e authored by Philipp Schafft's avatar Philipp Schafft 🦁

Feature: Render error messages via Report XML and XSLT

parent 7fadb6ce
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
<xsl:include href="includes/web-page.xsl"/>
<xsl:variable name="title">Error</xsl:variable>
<xsl:template name="content">
<div class="roundbox">
<xsl:for-each select="/report/incident">
<div class="article">
<h3>Response</h3>
<h4>Message</h4>
<p><xsl:value-of select="state/text" /></p>
</div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0">
<xsl:output omit-xml-declaration="yes" media-type="text/plain" method="text" indent="no" encoding="UTF-8" />
<xsl:template name="content" match="/report">
<xsl:for-each select="/report/incident">
<xsl:text>Report:&#xa;</xsl:text>
<xsl:value-of select="state/text" />
<xsl:text>&#xa;</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
......@@ -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);
data = refbuf_new(PER_CLIENT_REFBUF_SIZE);
if (!data) {
client_send_500(client, error->message);
return;
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;
}
client->reuse = ICECAST_REUSE_KEEPALIVE;
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;
}
config = config_get_config();
report = reportxml_database_build_report(config->reportxml_db, error->uuid, -1);
config_release_config();
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,
"<html><head><title>Error %i</title></head><body><h1>Error %i</h1><hr><p><b>%s</b></p><p>Error code: %s</p></body></html>\r\n",
error->http_status, error->http_status, error->message, error->uuid);
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);
}
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;
client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status);
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)
......
......@@ -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 {
......
......@@ -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) {
......
......@@ -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 {
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment