Export host statistics in prometheus text format on /metrics.
[darkstat] / http.c
diff --git a/http.c b/http.c
index fba615b..7ba7e47 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,5 +1,5 @@
 /* darkstat 3
- * copyright (c) 2001-2012 Emil Mikulic.
+ * copyright (c) 2001-2016 Emil Mikulic.
  *
  * http.c: embedded webserver.
  * This borrows a lot of code from darkhttpd.
@@ -42,8 +42,10 @@ static int http_base_len = 0;
 
 static const char mime_type_xml[] = "text/xml";
 static const char mime_type_html[] = "text/html; charset=us-ascii";
+static const char mime_type_text_prometheus[] = "text/plain; version=0.0.4";
 static const char mime_type_css[] = "text/css";
 static const char mime_type_js[] = "text/javascript";
+static const char mime_type_png[] = "image/png";
 static const char encoding_identity[] = "identity";
 static const char encoding_gzip[] = "gzip";
 
@@ -468,8 +470,8 @@ static char *parse_field(const struct connection *conn, const char *field)
 
     /* find end */
     for (bound2 = bound1;
-        conn->request[bound2] != '\r' &&
-        bound2 < conn->request_length; bound2++)
+        bound2 < conn->request_length &&
+        conn->request[bound2] != '\r'; bound2++)
             ;
 
     /* copy to buffer */
@@ -541,7 +543,7 @@ static_style_css(struct connection *conn)
 {
 #include "stylecss.h"
 
-    conn->reply = style_css;
+    conn->reply = (char*)style_css;
     conn->reply_length = style_css_len;
     conn->reply_dont_free = 1;
     conn->mime_type = mime_type_css;
@@ -555,12 +557,26 @@ static_graph_js(struct connection *conn)
 {
 #include "graphjs.h"
 
-    conn->reply = graph_js;
+    conn->reply = (char*)graph_js;
     conn->reply_length = graph_js_len;
     conn->reply_dont_free = 1;
     conn->mime_type = mime_type_js;
 }
 
+/* ---------------------------------------------------------------------------
+ * Web interface: favicon.
+ */
+static void
+static_favicon(struct connection *conn)
+{
+#include "favicon.h"
+
+    conn->reply = (char*)favicon_png;
+    conn->reply_length = sizeof(favicon_png);
+    conn->reply_dont_free = 1;
+    conn->mime_type = mime_type_png;
+}
+
 /* ---------------------------------------------------------------------------
  * gzip a reply, if requested and possible.  Don't bother with a minimum
  * length requirement, I've never seen a page fail to compress.
@@ -673,11 +689,19 @@ static void process_get(struct connection *conn)
         /* hack around Opera caching the XML */
         conn->header_extra = "Pragma: no-cache\r\n";
     }
+    else if (str_starts_with(safe_url, "/metrics")) {
+        struct str *buf = text_metrics();
+        str_extract(buf, &(conn->reply_length), &(conn->reply));
+        conn->mime_type = mime_type_text_prometheus;
+    }
     else if (strcmp(safe_url, "/style.css") == 0)
         static_style_css(conn);
     else if (strcmp(safe_url, "/graph.js") == 0)
         static_graph_js(conn);
-    else {
+    else if (strcmp(safe_url, "/favicon.ico") == 0) {
+        /* serves a PNG instead of an ICO, might cause problems for IE6 */
+        static_favicon(conn);
+    } else {
         default_reply(conn, 404, "Not Found",
             "The page you requested could not be found.");
         free(safe_url);
@@ -1181,6 +1205,7 @@ void http_poll(fd_set *recv_set, fd_set *send_set)
 
 void http_stop(void) {
     struct connection *conn;
+    struct connection *next;
     unsigned int i;
 
     free(http_base_url);
@@ -1192,7 +1217,7 @@ void http_stop(void) {
     insocks = NULL;
 
     /* Close in-flight connections. */
-    LIST_FOREACH(conn, &connlist, entries) {
+    LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
         LIST_REMOVE(conn, entries);
         free_connection(conn);
         free(conn);