Commit 4f4e7be9 authored by Karl Heyes's avatar Karl Heyes

push HTTP header writing for file download into file serving thread to prevent

stalls in connection thread.  perform most file checking in fserve but allow
for m3u file override and using the Host header if available.

svn path=/icecast/trunk/icecast/; revision=9462
parent 7446328d
......@@ -765,7 +765,6 @@ static void _handle_get_request (client_t *client, char *passed_uri)
struct stat statbuf;
source_t *source;
int fileserve;
char *host = NULL;
int port;
int i;
char *serverhost = NULL;
......@@ -777,8 +776,6 @@ static void _handle_get_request (client_t *client, char *passed_uri)
config = config_get_config();
fileserve = config->fileserve;
if (config->hostname)
host = strdup (config->hostname);
port = config->port;
for(i = 0; i < global.server_sockets; i++) {
if(global.serversock[i] == client->con->serversock) {
......@@ -817,7 +814,6 @@ static void _handle_get_request (client_t *client, char *passed_uri)
(strncmp(uri, "/admin/", 7) == 0)) {
admin_handle_request(client, uri);
if (uri != passed_uri) free (uri);
free (host);
return;
}
......@@ -842,46 +838,17 @@ static void _handle_get_request (client_t *client, char *passed_uri)
}
free(fullpath);
if (uri != passed_uri) free (uri);
free (host);
return;
}
else if(fileserve && stat(fullpath, &statbuf) == 0 &&
#ifdef _WIN32
((statbuf.st_mode) & _S_IFREG))
#else
S_ISREG(statbuf.st_mode))
#endif
if (fserve_client_create (client, uri))
{
fserve_client_create(client, fullpath);
free(fullpath);
if (uri != passed_uri) free (uri);
free (host);
return;
}
free(fullpath);
if(strcmp(util_get_extension(uri), "m3u") == 0) {
char *sourceuri = strdup(uri);
char *dot = strrchr(sourceuri, '.');
*dot = 0;
client->respcode = 200;
bytes = sock_write(client->con->sock,
"HTTP/1.0 200 OK\r\n"
"Content-Type: audio/x-mpegurl\r\n\r\n"
"http://%s:%d%s\r\n",
host,
port,
sourceuri
);
if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client);
free(sourceuri);
if (uri != passed_uri) free (uri);
free (host);
return;
}
free (host);
avl_tree_rlock(global.source_tree);
source = source_find_mount(uri);
if (source) {
......
......@@ -32,6 +32,7 @@
#include <winsock2.h>
#include <windows.h>
#define snprintf _snprintf
#define S_ISREG(mode) ((mode) & _S_IFREG)
#endif
#include "thread/thread.h"
......@@ -93,14 +94,6 @@ static void create_mime_mappings(const char *fn);
void fserve_initialize(void)
{
ice_config_t *config = config_get_config();
int serve = config->fileserve;
config_release_config();
if(!serve)
return;
create_mime_mappings(MIMETYPESFILE);
thread_mutex_create (&pending_lock);
......@@ -230,7 +223,7 @@ static void wait_for_fds() {
pending_list = NULL;
thread_mutex_unlock (&pending_lock);
}
/* drop out of here is someone is ready */
/* drop out of here if someone is ready */
if (fserve_client_waiting())
break;
}
......@@ -259,7 +252,10 @@ static void *fserv_thread_function(void *arg)
if (client->pos == refbuf->len)
{
/* Grab a new chunk */
bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
if (fclient->file)
bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
else
bytes = 0;
if (bytes == 0)
{
fserve_t *to_go = fclient;
......@@ -341,6 +337,8 @@ char *fserve_content_type (const char *path)
return "image/jpeg";
else if(!strcmp(ext, "png"))
return "image/png";
else if(!strcmp(ext, "m3u"))
return "audio/x-mpegurl";
else
return "application/octet-stream";
}
......@@ -362,42 +360,106 @@ static void fserve_client_destroy(fserve_t *fclient)
int fserve_client_create(client_t *httpclient, const char *path)
{
fserve_t *client;
int bytes;
struct stat file_buf;
char *range = NULL;
int64_t new_content_len = 0;
int64_t rangenumber = 0;
int64_t rangenumber = 0, content_length;
int rangeproblem = 0;
int ret = 0;
char *fullpath;
int m3u_requested = 0, m3u_file_available = 1;
ice_config_t *config;
FILE *file;
fullpath = util_get_path_from_normalised_uri (path);
INFO2 ("checking for file %s (%s)", path, fullpath);
if (strcmp (util_get_extension (fullpath), "m3u") == 0)
m3u_requested = 1;
/* check for the actual file */
if (stat (fullpath, &file_buf) != 0)
{
/* the m3u can be generated, but send an m3u file if available */
if (m3u_requested == 0)
{
free (fullpath);
return 0;
}
m3u_file_available = 0;
}
client_set_queue (httpclient, NULL);
httpclient->refbuf = refbuf_new (BUFSIZE);
if (m3u_requested && m3u_file_available == 0)
{
char *host = httpp_getvar (httpclient->parser, "host");
char *sourceuri = strdup (path);
char *dot = strrchr(sourceuri, '.');
*dot = 0;
httpclient->respcode = 200;
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"
"http://%s:%d%s\r\n",
config->hostname, config->port,
sourceuri
);
config_release_config();
}
else
{
snprintf (httpclient->refbuf->data, BUFSIZE,
"HTTP/1.0 200 OK\r\n"
"Content-Type: audio/x-mpegurl\r\n\r\n"
"http://%s%s\r\n",
host,
sourceuri
);
}
httpclient->refbuf->len = strlen (httpclient->refbuf->data);
fserve_add_client (httpclient, NULL);
free (sourceuri);
free (fullpath);
return 1;
}
if (stat (path, &file_buf) != 0)
/* on demand file serving check */
config = config_get_config();
if (config->fileserve == 0)
{
DEBUG1 ("on demand file \"%s\" refused", fullpath);
client_send_404 (httpclient, "The file you requested could not be found");
config_release_config();
free (fullpath);
return 0;
}
config_release_config();
client = calloc (1, sizeof(fserve_t));
if (client == NULL)
if (S_ISREG (file_buf.st_mode) == 0)
{
client_send_404 (httpclient, "memory exhausted");
return 0;
client_send_404 (httpclient, "The file you requested could not be found");
WARN1 ("found requested file but there is no handler for it: %s", fullpath);
free (fullpath);
return 1;
}
client->file = fopen (path, "rb");
if (client->file == NULL)
file = fopen (fullpath, "rb");
free (fullpath);
if (file == NULL)
{
WARN1 ("Problem accessing file \"%s\"", fullpath);
client_send_404 (httpclient, "File not readable");
fserve_client_destroy (client);
return 0;
return 1;
}
client->client = httpclient;
client->ready = 0;
client_set_queue (httpclient, NULL);
httpclient->refbuf = refbuf_new (BUFSIZE);
client->content_length = (int64_t)file_buf.st_size;
range = httpp_getvar (client->client->parser, "range");
content_length = (int64_t)file_buf.st_size;
range = httpp_getvar (httpclient->parser, "range");
if (range != NULL) {
ret = sscanf(range, "bytes=" FORMAT_INT64 "-", &rangenumber);
......@@ -410,9 +472,9 @@ int fserve_client_create(client_t *httpclient, const char *path)
rangeproblem = 1;
}
if (!rangeproblem) {
ret = fseek(client->file, rangenumber, SEEK_SET);
ret = fseek (file, rangenumber, SEEK_SET);
if (ret != -1) {
new_content_len = client->content_length - rangenumber;
new_content_len = content_length - rangenumber;
if (new_content_len < 0) {
rangeproblem = 1;
}
......@@ -445,25 +507,25 @@ int fserve_client_create(client_t *httpclient, const char *path)
new_content_len,
rangenumber,
endpos,
client->content_length,
content_length,
fserve_content_type(path));
}
else {
httpclient->respcode = 416;
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
sock_write (httpclient->con->sock,
"HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
fserve_client_destroy(client);
return -1;
client_destroy (httpclient);
return 1;
}
}
else {
/* If we run into any issues with the ranges
we fallback to a normal/non-range request */
httpclient->respcode = 416;
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
sock_write (httpclient->con->sock,
"HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
fserve_client_destroy(client);
return -1;
client_destroy (httpclient);
return 1;
}
}
else {
......@@ -473,17 +535,42 @@ int fserve_client_create(client_t *httpclient, const char *path)
"HTTP/1.0 200 OK\r\n"
"Content-Length: " FORMAT_INT64 "\r\n"
"Content-Type: %s\r\n\r\n",
client->content_length,
content_length,
fserve_content_type(path));
}
httpclient->refbuf->len = bytes;
httpclient->pos = 0;
stats_event_inc (NULL, "file_connections");
sock_set_blocking(client->client->con->sock, SOCK_NONBLOCK);
sock_set_nodelay(client->client->con->sock);
fserve_add_client (httpclient, file);
return 1;
}
/* Add client to fserve thread, client needs to have refbuf set and filled
* but may provide a NULL file if no data needs to be read
*/
int fserve_add_client (client_t *client, FILE *file)
{
fserve_t *fclient = calloc (1, sizeof(fserve_t));
DEBUG0 ("Adding client to file serving engine");
if (fclient == NULL)
{
client_send_404 (client, "memory exhausted");
return -1;
}
fclient->file = file;
fclient->client = client;
fclient->ready = 0;
sock_set_blocking (client->con->sock, SOCK_NONBLOCK);
sock_set_nodelay (client->con->sock);
thread_mutex_lock (&pending_lock);
client->next = (fserve_t *)pending_list;
pending_list = client;
fclient->next = (fserve_t *)pending_list;
pending_list = fclient;
thread_mutex_unlock (&pending_lock);
return 0;
......
......@@ -14,14 +14,12 @@
#define __FSERVE_H__
#include <stdio.h>
#include "compat.h"
typedef struct _fserve_t
{
client_t *client;
FILE *file;
int64_t content_length;
int ready;
struct _fserve_t *next;
} fserve_t;
......@@ -29,6 +27,7 @@ typedef struct _fserve_t
void fserve_initialize(void);
void fserve_shutdown(void);
int fserve_client_create(client_t *httpclient, const char *path);
int fserve_add_client (client_t *client, FILE *file);
char *fserve_content_type (const char *path);
......
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