mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
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
This commit is contained in:
parent
9c2db2107f
commit
abf6c134d0
47
src/admin.c
47
src/admin.c
@ -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;
|
||||
|
||||
|
44
src/client.c
44
src/client.c
@ -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);
|
||||
|
14
src/format.c
14
src/format.c
@ -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;
|
||||
|
49
src/fserve.c
49
src/fserve.c
@ -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;
|
||||
|
79
src/util.c
79
src/util.c
@ -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));
|
||||
|
27
src/util.h
27
src/util.h
@ -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 {
|
||||
|
10
src/xslt.c
10
src/xslt.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user