[xiph-commits] r18464 - icecast/trunk/icecast/src

ph3-der-loewe at svn.xiph.org ph3-der-loewe at svn.xiph.org
Tue Jul 17 16:55:09 PDT 2012


Author: ph3-der-loewe
Date: 2012-07-17 16:55:09 -0700 (Tue, 17 Jul 2012)
New Revision: 18464

Modified:
   icecast/trunk/icecast/src/admin.c
   icecast/trunk/icecast/src/client.c
   icecast/trunk/icecast/src/format.c
   icecast/trunk/icecast/src/fserve.c
   icecast/trunk/icecast/src/util.c
   icecast/trunk/icecast/src/util.h
   icecast/trunk/icecast/src/xslt.c
Log:
Send proper HTTP headers in responses to clients.
This is currently not implemented for SOURCE and STATS clients as
I suspect to break them. This needs some more research.
close #1639, see #1870 and #1885.


Modified: icecast/trunk/icecast/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/admin.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -269,14 +269,19 @@
         xmlChar *buff = NULL;
         int len = 0;
         unsigned int buf_len;
-        const char *http = "HTTP/1.0 200 OK\r\n"
-               "Content-Type: text/xml\r\n"
-               "Content-Length: ";
         xmlDocDumpMemory(doc, &buff, &len);
-        buf_len = strlen (http) + len + 20;
+        buf_len = len + 256 /* just a random medium number */;
+
         client_set_queue (client, NULL);
         client->refbuf = refbuf_new (buf_len);
-        len = snprintf (client->refbuf->data, buf_len, "%s%d\r\n\r\n%s", http, len, buff);
+
+        /* FIXME: in this section we hope no function will ever return -1 */
+	len = util_http_build_header(client->refbuf->data, buf_len, 0,
+	                             0, 200, NULL,
+				     "text/xml", NULL,
+				     NULL);
+	len += snprintf (client->refbuf->data + len, buf_len - len, "Content-Length: %d\r\n\r\n%s", len, buff);
+
         client->refbuf->len = len;
         xmlFree(buff);
         client->respcode = 200;
@@ -563,11 +568,17 @@
 
 static void html_success(client_t *client, char *message)
 {
+    ssize_t ret;
+
+    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                                 0, 200, NULL,
+				 "text/html", NULL,
+				 "");
+    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
+             "<html><head><title>Admin request successful</title></head>"
+	     "<body><p>%s</p></body></html>", message);
+
     client->respcode = 200;
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 
-            "<html><head><title>Admin request successful</title></head>"
-            "<body><p>%s</p></body></html>", message);
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);
 }
@@ -693,15 +704,18 @@
     const char *username = NULL;
     const char *password = NULL;
     ice_config_t *config;
+    ssize_t ret;
 
     COMMAND_REQUIRE(client, "username", username);
     COMMAND_REQUIRE(client, "password", password);
 
-    client->respcode = 200;
+    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                                 0, 200, NULL,
+				 "audio/x-mpegurl", NULL,
+				 NULL);
+
     config = config_get_config();
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-Type: audio/x-mpegurl\r\n"
+    snprintf (client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
         "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
         "http://%s:%s@%s:%d%s\r\n",
         username,
@@ -712,6 +726,7 @@
     );
     config_release_config();
 
+    client->respcode = 200;
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);
 }
@@ -1014,8 +1029,10 @@
 
     if (response == PLAINTEXT)
     {
-        snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-                "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
+        util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+	                       0, 200, NULL,
+			       "text/html", NULL,
+			       "");
         client->refbuf->len = strlen (client->refbuf->data);
         client->respcode = 200;
 

Modified: icecast/trunk/icecast/src/client.c
===================================================================
--- icecast/trunk/icecast/src/client.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/client.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -182,21 +182,32 @@
 
 
 void client_send_400(client_t *client, char *message) {
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 400 Bad Request\r\n"
-            "Content-Type: text/html\r\n\r\n"
-            "<b>%s</b>\r\n", message);
+    ssize_t ret;
+
+    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                                 0, 400, NULL,
+                                 "text/html", NULL,
+                                 "");
+
+    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
+             "<b>%s</b>\r\n", message);
+
     client->respcode = 400;
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);
 }
 
 void client_send_404(client_t *client, char *message) {
+    ssize_t ret;
 
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 404 File Not Found\r\n"
-            "Content-Type: text/html\r\n\r\n"
-            "<b>%s</b>\r\n", message);
+    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                                 0, 404, NULL,
+                                 "text/html", NULL,
+                                 "");
+
+    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
+             "<b>%s</b>\r\n", message);
+
     client->respcode = 404;
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);
@@ -204,11 +215,10 @@
 
 
 void client_send_401(client_t *client) {
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 401 Authentication Required\r\n"
-            "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
-            "\r\n"
-            "You need to authenticate\r\n");
+    util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                           0, 401, NULL,
+			   "text/plain", NULL,
+			   "You need to authenticate\r\n");
     client->respcode = 401;
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);
@@ -216,10 +226,10 @@
 
 void client_send_403(client_t *client, const char *reason)
 {
-    if (reason == NULL)
-        reason = "Forbidden";
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 403 %s\r\n\r\n", reason);
+    util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
+                           0, 403, reason,
+			   "text/plain", NULL,
+			   "Forbidden");
     client->respcode = 403;
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client (client, NULL);

Modified: icecast/trunk/icecast/src/format.c
===================================================================
--- icecast/trunk/icecast/src/format.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/format.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -298,8 +298,7 @@
     ptr = client->refbuf->data;
     client->respcode = 200;
 
-    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
-            "Content-Type: %s\r\n", source->format->contenttype);
+    bytes = util_http_build_header (ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL);
 
     remaining -= bytes;
     ptr += bytes;
@@ -362,17 +361,6 @@
     }
     avl_tree_unlock(source->parser->vars);
 
-    config = config_get_config();
-    bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id);
-    config_release_config();
-    remaining -= bytes;
-    ptr += bytes;
-
-    /* prevent proxy servers from caching */
-    bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n");
-    remaining -= bytes;
-    ptr += bytes;
-
     bytes = snprintf (ptr, remaining, "\r\n");
     remaining -= bytes;
     ptr += bytes;

Modified: icecast/trunk/icecast/src/fserve.c
===================================================================
--- icecast/trunk/icecast/src/fserve.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/fserve.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -454,12 +454,13 @@
 
         *dot = 0;
         httpclient->respcode = 200;
+        ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
+	                              0, 200, NULL,
+				      "audio/x-mpegurl", NULL, "");
         if (host == NULL)
         {
-            config = config_get_config();
-            snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.0 200 OK\r\n"
-                    "Content-Type: audio/x-mpegurl\r\n\r\n"
+	    config = config_get_config();
+            snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
                     "http://%s:%d%s\r\n", 
                     config->hostname, config->port,
                     sourceuri
@@ -468,9 +469,7 @@
         }
         else
         {
-            snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.0 200 OK\r\n"
-                    "Content-Type: audio/x-mpegurl\r\n\r\n"
+	    snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
                     "http://%s%s\r\n", 
                     host, 
                     sourceuri
@@ -555,36 +554,27 @@
                 rangeproblem = 1;
             }
             if (!rangeproblem) {
-                /* Date: is required on all HTTP1.1 responses */
-                char currenttime[50];
-                time_t now;
-                int strflen;
-                struct tm result;
                 off_t endpos = rangenumber+new_content_len-1;
                 char *type;
 
                 if (endpos < 0) {
                     endpos = 0;
                 }
-                time(&now);
-                strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT",
-                                   gmtime_r(&now, &result));
                 httpclient->respcode = 206;
                 type = fserve_content_type (path);
-                bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.1 206 Partial Content\r\n"
-                    "Date: %s\r\n"
+		bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
+		                                0, 206, NULL,
+						type, NULL,
+						NULL);
+                bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
                     "Accept-Ranges: bytes\r\n"
                     "Content-Length: %" PRI_OFF_T "\r\n"
                     "Content-Range: bytes %" PRI_OFF_T \
-                    "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n"
-                    "Content-Type: %s\r\n\r\n",
-                    currenttime,
+                    "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n",
                     new_content_len,
                     rangenumber,
                     endpos,
-                    content_length,
-                    type);
+                    content_length);
                 free (type);
             }
             else {
@@ -598,13 +588,14 @@
     else {
         char *type = fserve_content_type(path);
         httpclient->respcode = 200;
-        bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
-            "HTTP/1.0 200 OK\r\n"
+	bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
+	                                0, 200, NULL,
+					type, NULL,
+					NULL);
+        bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
             "Accept-Ranges: bytes\r\n"
-            "Content-Length: %" PRI_OFF_T "\r\n"
-            "Content-Type: %s\r\n\r\n",
-            content_length,
-            type);
+            "Content-Length: %" PRI_OFF_T "\r\n\r\n",
+            content_length);
         free (type);
     }
     httpclient->refbuf->len = bytes;

Modified: icecast/trunk/icecast/src/util.c
===================================================================
--- icecast/trunk/icecast/src/util.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/util.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -485,6 +485,85 @@
     return result;
 }
 
+ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
+        int cache,
+        int status, const char * statusmsg,
+        const char * contenttype, const char * charset,
+        const char * datablock) {
+    const char * http_version = "1.0";
+    ice_config_t *config;
+    time_t now;
+    struct tm result;
+    char currenttime_buffer[50];
+    char status_buffer[80];
+    char contenttype_buffer[80];
+    ssize_t ret;
+
+    if (!out)
+        return -1;
+
+    if (offset == -1)
+        offset = strlen (out);
+
+    out += offset;
+    len -= offset;
+
+    if (status == -1)
+    {
+        status_buffer[0] = '\0';
+    }
+    else
+    {
+        if (!statusmsg)
+	{
+	    switch (status)
+	    {
+	        case 200: statusmsg = "OK"; break;
+		case 206: statusmsg = "Partial Content"; http_version = "1.1"; break;
+		case 400: statusmsg = "Bad Request"; break;
+		case 401: statusmsg = "Authentication Required"; break;
+		case 403: statusmsg = "Forbidden"; break;
+		case 404: statusmsg = "File Not Found"; break;
+		case 416: statusmsg = "Request Range Not Satisfiable"; break;
+		default:  statusmsg = "(unknown status code)"; break;
+	    }
+	}
+	snprintf (status_buffer, sizeof (status_buffer), "HTTP/%s %d %s\r\n", http_version, status, statusmsg);
+    }
+
+    if (contenttype)
+    {
+    	if (charset)
+            snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s; charset=%s\r\n",
+	                                                               contenttype, charset);
+	else
+            snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s\r\n",
+                                                                       contenttype);
+    }
+    else
+    {
+        contenttype_buffer[0] = '\0';
+    }
+
+    time(&now);
+    strftime(currenttime_buffer, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result));
+
+    config = config_get_config();
+    ret = snprintf (out, len, "%sServer: %s\r\nDate: %s\r\n%s%s%s%s%s",
+                              status_buffer,
+			      config->server_id,
+			      currenttime_buffer,
+			      contenttype_buffer,
+			      (status == 401 ? "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" : ""),
+                              (cache     ? "" : "Cache-Control: no-cache\r\n"),
+                              (datablock ? "\r\n" : ""),
+                              (datablock ? datablock : ""));
+    config_release_config();
+
+    return ret;
+}
+
+
 util_dict *util_dict_new(void)
 {
     return (util_dict *)calloc(1, sizeof(util_dict));

Modified: icecast/trunk/icecast/src/util.h
===================================================================
--- icecast/trunk/icecast/src/util.h	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/util.h	2012-07-17 23:55:09 UTC (rev 18464)
@@ -35,6 +35,33 @@
 char *util_url_unescape(const char *src);
 char *util_url_escape(const char *src);
 
+/* Function to build up a HTTP header.
+ * out is the pointer to storage.
+ * len is the total length of out.
+ * offset is the offset in out we should start writing at if offset >= 0.
+ * In this case the 'used' length is len-offset.
+ * If offset is -1 we append at the end of out.
+ * In this case the used length is len-strlen(out).
+ * cache is a bool. If set allow the client to cache the content (static data).
+ * if unset we will send no-caching headers.
+ * status is the HTTP status code. If -1 no HTTP status header is generated.
+ * statusmsg is the status header text. If NULL a default one
+ * is used for the given status code. This is ignored if status is -1.
+ * contenttype is the value for the Content-Type header.
+ * if NULL no such header is generated.
+ * charset is the charset for the object. If NULL no header is generated
+ * or default is used.
+ * datablock is random data added to the request.
+ * If datablock is non NULL the end-of-header is appended as well as this datablock.
+ * If datablock is NULL no end-of-header nor any data is appended.
+ * Returns the number of bytes written or -1 on error.
+ */
+ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
+        int cache,
+        int status, const char * statusmsg,
+        const char * contenttype, const char * charset,
+        const char * datablock);
+
 /* String dictionary type, without support for NULL keys, or multiple
  * instances of the same key */
 typedef struct _util_dict {

Modified: icecast/trunk/icecast/src/xslt.c
===================================================================
--- icecast/trunk/icecast/src/xslt.c	2012-07-17 22:02:49 UTC (rev 18463)
+++ icecast/trunk/icecast/src/xslt.c	2012-07-17 23:55:09 UTC (rev 18464)
@@ -227,14 +227,16 @@
     if (problem == 0)
     {
         /* the 100 is to allow for the hardcoded headers */
-        unsigned int full_len = strlen (mediatype) + len + 100;
+        unsigned int full_len = strlen (mediatype) + len + 256;
         refbuf_t *refbuf = refbuf_new (full_len);
+	ssize_t ret;
 
         if (string == NULL)
             string = xmlCharStrdup ("");
-        snprintf (refbuf->data, full_len,
-                "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
-                mediatype, len, string);
+        ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, NULL, NULL, NULL);
+	snprintf (refbuf->data + ret, full_len - ret,
+                "Content-Length: %d\r\n\r\n%s",
+                len, string);
 
         client->respcode = 200;
         client_set_queue (client, NULL);



More information about the commits mailing list