mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
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
This commit is contained in:
parent
7446328d1d
commit
4f4e7be98f
@ -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) {
|
||||
|
181
src/fserve.c
181
src/fserve.c
@ -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;
|
||||
|
||||
if (stat (path, &file_buf) != 0)
|
||||
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)
|
||||
{
|
||||
client_send_404 (httpclient, "The file you requested could not be found");
|
||||
return 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 = calloc (1, sizeof(fserve_t));
|
||||
if (client == NULL)
|
||||
{
|
||||
client_send_404 (httpclient, "memory exhausted");
|
||||
return 0;
|
||||
}
|
||||
client->file = fopen (path, "rb");
|
||||
if (client->file == NULL)
|
||||
{
|
||||
client_send_404 (httpclient, "File not readable");
|
||||
fserve_client_destroy (client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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();
|
||||
|
||||
if (S_ISREG (file_buf.st_mode) == 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;
|
||||
}
|
||||
|
||||
file = fopen (fullpath, "rb");
|
||||
free (fullpath);
|
||||
if (file == NULL)
|
||||
{
|
||||
WARN1 ("Problem accessing file \"%s\"", fullpath);
|
||||
client_send_404 (httpclient, "File not readable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user