Commit abf6c134 authored by Philipp Schafft's avatar Philipp Schafft 🦁

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.

svn path=/icecast/trunk/icecast/; revision=18464
parent 9c2db210
......@@ -269,14 +269,19 @@ void admin_send_response (xmlDocPtr doc, client_t *client,
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 admin_handle_mount_request(client_t *client, source_t *source,
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 @@ static void command_buildm3u(client_t *client, const char *mount)
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 @@ static void command_buildm3u(client_t *client, const char *mount)
);
config_release_config();
client->respcode = 200;
client->refbuf->len = strlen (client->refbuf->data);
fserve_add_client (client, NULL);
}
......@@ -1014,8 +1029,10 @@ static void command_list_mounts(client_t *client, int response)
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;
......
......@@ -182,21 +182,32 @@ int client_read_bytes (client_t *client, void *buf, unsigned len)
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;
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);
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);
client->respcode = 404;
client->refbuf->len = strlen (client->refbuf->data);
fserve_add_client (client, NULL);
......@@ -204,11 +215,10 @@ void client_send_404(client_t *client, char *message) {
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_401(client_t *client) {
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);
......
......@@ -298,8 +298,7 @@ static int format_prepare_headers (source_t *source, client_t *client)
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 @@ static int format_prepare_headers (source_t *source, client_t *client)
}
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;
......
......@@ -454,12 +454,13 @@ int fserve_client_create (client_t *httpclient, const char *path)
*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 @@ int fserve_client_create (client_t *httpclient, const char *path)
}
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 @@ int fserve_client_create (client_t *httpclient, const char *path)
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 @@ int fserve_client_create (client_t *httpclient, const char *path)
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;
......
......@@ -485,6 +485,85 @@ char *util_base64_decode(const char *data)
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));
......
......@@ -35,6 +35,33 @@ char *util_bin_to_hex(unsigned char *data, int len);
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 {
......
......@@ -227,14 +227,16 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
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);
......
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