Commit 235a2639 authored by Jack Moffitt's avatar Jack Moffitt
Browse files

Ouch. Serious bug found by Ricardo Galli.

When the cond var is signalled it will wake up a thread.  If all the
connection handler threads are handling connections, the signal will be
ignored and clients will 'pend' until another client causes the to signal
again.

We have to check to see if there are more pending connections before waiting
on the signal again.

svn path=/trunk/icecast/; revision=3178
parent 785cfefe
...@@ -310,161 +310,140 @@ static void *_handle_connection(void *arg) ...@@ -310,161 +310,140 @@ static void *_handle_connection(void *arg)
if (global.running != ICE_RUNNING) break; if (global.running != ICE_RUNNING) break;
/* grab a connection and set the socket to blocking */ /* grab a connection and set the socket to blocking */
con = _get_connection(); while (con = _get_connection()) {
stats_event_inc(NULL, "connections");
stats_event_inc(NULL, "connections"); sock_set_blocking(con->sock, SOCK_BLOCK);
sock_set_blocking(con->sock, SOCK_BLOCK); /* fill header with the http header */
if (util_read_header(con->sock, header, 4096) == 0) {
/* fill header with the http header */ /* either we didn't get a complete header, or we timed out */
if (util_read_header(con->sock, header, 4096) == 0) {
/* either we didn't get a complete header, or we timed out */
connection_close(con);
continue;
}
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, header, strlen(header))) {
/* handle the connection or something */
if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0 && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0) {
printf("DEBUG: bad protocol\n");
connection_close(con); connection_close(con);
httpp_destroy(parser);
continue; continue;
} }
if (parser->req_type == httpp_req_source) { parser = httpp_create_parser();
char *contenttype; httpp_initialize(parser, NULL);
if (httpp_parse(parser, header, strlen(header))) {
printf("DEBUG: source logging in\n"); /* handle the connection or something */
stats_event_inc(NULL, "source_connections");
if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) { if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0 && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0) {
printf("DEBUG: bad password\n"); printf("DEBUG: bad protocol\n");
INFO1("Source (%s) attempted to login with bad password", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con); connection_close(con);
httpp_destroy(parser); httpp_destroy(parser);
continue; continue;
} }
/* check to make sure this source has if (parser->req_type == httpp_req_source) {
** a unique mountpoint char *contenttype;
*/
avl_tree_rlock(global.source_tree); printf("DEBUG: source logging in\n");
if (source_find_mount(httpp_getvar(parser, HTTPP_VAR_URI)) != NULL) { stats_event_inc(NULL, "source_connections");
printf("Source attempted to connect with an already used mountpoint.\n");
INFO1("Source tried to log in as %s, but is already used", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
avl_tree_unlock(global.source_tree);
continue;
}
avl_tree_unlock(global.source_tree);
/* check to make sure this source wouldn't
** be over the limit
*/
global_lock();
if (global.sources >= config_get_config()->source_limit) {
printf("TOO MANY SOURCE, KICKING THIS ONE\n");
INFO1("Source (%s) logged in, but there are too many sources", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
global_unlock();
continue;
}
global.sources++;
global_unlock();
stats_event_inc(NULL, "sources");
contenttype = httpp_getvar(parser, "content-type");
if (contenttype != NULL) {
format_type_t format = format_get_type(contenttype);
if(format < 0) {
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
continue;
}
else
source = source_create(con, parser, httpp_getvar(parser, HTTPP_VAR_URI), format);
}
else {
WARN0("No content-type header, cannot handle source");
continue;
}
source->shutdown_rwlock = &_source_shutdown_rwlock;
sock_set_blocking(con->sock, SOCK_NONBLOCK);
thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED); if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) {
printf("DEBUG: bad password\n");
continue; INFO1("Source (%s) attempted to login with bad password", httpp_getvar(parser, HTTPP_VAR_URI));
} else if (parser->req_type == httpp_req_stats) { connection_close(con);
printf("DEBUG: stats connection...\n"); httpp_destroy(parser);
stats_event_inc(NULL, "stats_connections"); continue;
}
if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) {
printf("DEBUG: bad password\n");
connection_close(con);
httpp_destroy(parser);
continue;
}
stats_event_inc(NULL, "stats"); /* check to make sure this source has
** a unique mountpoint
*/
avl_tree_rlock(global.source_tree);
if (source_find_mount(httpp_getvar(parser, HTTPP_VAR_URI)) != NULL) {
printf("Source attempted to connect with an already used mountpoint.\n");
INFO1("Source tried to log in as %s, but is already used", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
avl_tree_unlock(global.source_tree);
continue;
}
avl_tree_unlock(global.source_tree);
/* create stats connection and create stats handler thread */ /* check to make sure this source wouldn't
stats = (stats_connection_t *)malloc(sizeof(stats_connection_t)); ** be over the limit
stats->parser = parser; */
stats->con = con; global_lock();
if (global.sources >= config_get_config()->source_limit) {
printf("TOO MANY SOURCE, KICKING THIS ONE\n");
INFO1("Source (%s) logged in, but there are too many sources", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
global_unlock();
continue;
}
global.sources++;
global_unlock();
stats_event_inc(NULL, "sources");
contenttype = httpp_getvar(parser, "content-type");
if (contenttype != NULL) {
format_type_t format = format_get_type(contenttype);
if (format < 0) {
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
continue;
} else {
source = source_create(con, parser, httpp_getvar(parser, HTTPP_VAR_URI), format);
}
} else {
WARN0("No content-type header, cannot handle source");
continue;
}
thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED); source->shutdown_rwlock = &_source_shutdown_rwlock;
continue; sock_set_blocking(con->sock, SOCK_NONBLOCK);
} else if (parser->req_type == httpp_req_play || parser->req_type == httpp_req_get) {
printf("DEBUG: client coming in...\n"); thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
/* make a client */
client = client_create(con, parser);
stats_event_inc(NULL, "client_connections");
/* there are several types of HTTP GET clients
** media clients, which are looking for a source (eg, URI = /stream.ogg)
** stats clients, which are looking for /stats.xml
** and director server authorizers, which are looking for /GUID-xxxxxxxx (where xxxxxx is the GUID in question
** we need to handle the latter two before the former, as the latter two
** aren't subject to the limits.
*/
// TODO: add GUID-xxxxxx
if (strcmp(httpp_getvar(parser, HTTPP_VAR_URI), "/stats.xml") == 0) {
printf("sending stats.xml\n");
stats_sendxml(client);
continue; continue;
} } else if (parser->req_type == httpp_req_stats) {
printf("DEBUG: stats connection...\n");
global_lock(); stats_event_inc(NULL, "stats_connections");
if (global.clients >= config_get_config()->client_limit) {
if (parser->req_type == httpp_req_get) { if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) {
client->respcode = 504; printf("DEBUG: bad password\n");
bytes = sock_write(client->con->sock, "HTTP/1.0 504 Server Full\r\nContent-Type: text/html\r\n\r\n"\ connection_close(con);
"<b>The server is already full. Try again later.</b>\r\n"); httpp_destroy(parser);
if (bytes > 0) client->con->sent_bytes = bytes; continue;
} }
client_destroy(client);
global_unlock(); stats_event_inc(NULL, "stats");
/* create stats connection and create stats handler thread */
stats = (stats_connection_t *)malloc(sizeof(stats_connection_t));
stats->parser = parser;
stats->con = con;
thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED);
continue; continue;
} } else if (parser->req_type == httpp_req_play || parser->req_type == httpp_req_get) {
global_unlock(); printf("DEBUG: client coming in...\n");
avl_tree_rlock(global.source_tree); /* make a client */
source = source_find_mount(httpp_getvar(parser, HTTPP_VAR_URI)); client = client_create(con, parser);
if (source) { stats_event_inc(NULL, "client_connections");
printf("DEBUG: source found for client\n");
/* there are several types of HTTP GET clients
** media clients, which are looking for a source (eg, URI = /stream.ogg)
** stats clients, which are looking for /stats.xml
** and director server authorizers, which are looking for /GUID-xxxxxxxx (where xxxxxx is the GUID in question
** we need to handle the latter two before the former, as the latter two
** aren't subject to the limits.
*/
// TODO: add GUID-xxxxxx
if (strcmp(httpp_getvar(parser, HTTPP_VAR_URI), "/stats.xml") == 0) {
printf("sending stats.xml\n");
stats_sendxml(client);
continue;
}
global_lock(); global_lock();
if (global.clients >= config_get_config()->client_limit) { if (global.clients >= config_get_config()->client_limit) {
if (parser->req_type == httpp_req_get) { if (parser->req_type == httpp_req_get) {
...@@ -477,60 +456,80 @@ static void *_handle_connection(void *arg) ...@@ -477,60 +456,80 @@ static void *_handle_connection(void *arg)
global_unlock(); global_unlock();
continue; continue;
} }
global.clients++;
global_unlock(); global_unlock();
if (parser->req_type == httpp_req_get) { avl_tree_rlock(global.source_tree);
client->respcode = 200; source = source_find_mount(httpp_getvar(parser, HTTPP_VAR_URI));
sock_write(client->con->sock, "HTTP/1.0 200 OK\r\nContent-Type: application/x-ogg\r\n"); if (source) {
/* iterate through source http headers and send to client */ printf("DEBUG: source found for client\n");
avl_tree_rlock(source->parser->vars);
node = avl_get_first(source->parser->vars); global_lock();
while (node) { if (global.clients >= config_get_config()->client_limit) {
var = (http_var_t *)node->key; if (parser->req_type == httpp_req_get) {
if (strcasecmp(var->name, "ice-password") && !strncasecmp("ice-", var->name, 4)) { client->respcode = 504;
printf("DEBUG: sending %s: %s\n", var->name, var->value); bytes = sock_write(client->con->sock, "HTTP/1.0 504 Server Full\r\nContent-Type: text/html\r\n\r\n"\
sock_write(client->con->sock, "%s: %s\r\n", var->name, var->value); "<b>The server is already full. Try again later.</b>\r\n");
if (bytes > 0) client->con->sent_bytes = bytes;
} }
node = avl_get_next(node); client_destroy(client);
global_unlock();
continue;
} }
avl_tree_unlock(source->parser->vars); global.clients++;
global_unlock();
sock_write(client->con->sock, "\r\n");
sock_set_blocking(client->con->sock, SOCK_NONBLOCK); if (parser->req_type == httpp_req_get) {
client->respcode = 200;
sock_write(client->con->sock, "HTTP/1.0 200 OK\r\nContent-Type: application/x-ogg\r\n");
/* iterate through source http headers and send to client */
avl_tree_rlock(source->parser->vars);
node = avl_get_first(source->parser->vars);
while (node) {
var = (http_var_t *)node->key;
if (strcasecmp(var->name, "ice-password") && !strncasecmp("ice-", var->name, 4)) {
printf("DEBUG: sending %s: %s\n", var->name, var->value);
sock_write(client->con->sock, "%s: %s\r\n", var->name, var->value);
}
node = avl_get_next(node);
}
avl_tree_unlock(source->parser->vars);
sock_write(client->con->sock, "\r\n");
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
}
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree);
} }
avl_tree_wlock(source->pending_tree); avl_tree_unlock(global.source_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree); if (!source) {
} printf("DEBUG: source not found for client\n");
if (parser->req_type == httpp_req_get) {
avl_tree_unlock(global.source_tree); client->respcode = 404;
bytes = sock_write(client->con->sock, "HTTP/1.0 404 Source Not Found\r\nContent-Type: text/html\r\n\r\n"\
if (!source) { "<b>The source you requested could not be found.</b>\r\n");
printf("DEBUG: source not found for client\n"); if (bytes > 0) client->con->sent_bytes = bytes;
if (parser->req_type == httpp_req_get) { }
client->respcode = 404; client_destroy(client);
bytes = sock_write(client->con->sock, "HTTP/1.0 404 Source Not Found\r\nContent-Type: text/html\r\n\r\n"\
"<b>The source you requested could not be found.</b>\r\n");
if (bytes > 0) client->con->sent_bytes = bytes;
} }
client_destroy(client);
continue;
} else {
printf("DEBUG: wrong request type\n");
connection_close(con);
httpp_destroy(parser);
continue;
} }
continue;
} else { } else {
printf("DEBUG: wrong request type\n"); printf("DEBUG: parsing failed\n");
connection_close(con); connection_close(con);
httpp_destroy(parser); httpp_destroy(parser);
continue; continue;
} }
} else {
printf("DEBUG: parsing failed\n");
connection_close(con);
httpp_destroy(parser);
continue;
} }
} }
......
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