From 4f4e7be98fdd60fbe2c34ba50fa881eec21c67b8 Mon Sep 17 00:00:00 2001 From: Karl Heyes Date: Fri, 17 Jun 2005 22:55:59 +0000 Subject: [PATCH] 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 --- src/connection.c | 37 +--------- src/fserve.c | 181 +++++++++++++++++++++++++++++++++++------------ src/fserve.h | 3 +- 3 files changed, 137 insertions(+), 84 deletions(-) diff --git a/src/connection.c b/src/connection.c index ee3a8653..6238ccbc 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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) { diff --git a/src/fserve.c b/src/fserve.c index aace3d7f..73516574 100644 --- a/src/fserve.c +++ b/src/fserve.c @@ -32,6 +32,7 @@ #include #include #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; diff --git a/src/fserve.h b/src/fserve.h index c69e31fd..48408cb6 100644 --- a/src/fserve.h +++ b/src/fserve.h @@ -14,14 +14,12 @@ #define __FSERVE_H__ #include -#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);