diff --git a/src/client.c b/src/client.c index 380fece5..98cff179 100644 --- a/src/client.c +++ b/src/client.c @@ -92,12 +92,35 @@ int client_create(client_t **c_ptr, connection_t *con, http_parser_t *parser) return ret; } +static inline void client_reuseconnection(client_t *client) { + connection_t *con; + reuse_t reuse; + + if (!client) + return; + + con = client->con; + con = connection_create(con->sock, con->serversock, strdup(con->ip)); + reuse = client->reuse; + client->con->sock = -1; /* TODO: do not use magic */ + client->reuse = ICECAST_REUSE_CLOSE; + + client_destroy(client); + + connection_queue(con); +} + void client_destroy(client_t *client) { ICECAST_LOG_DEBUG("Called to destory client %p", client); if (client == NULL) return; + if (client->reuse != ICECAST_REUSE_CLOSE) { + client_reuseconnection(client); + return; + } + /* release the buffer now, as the buffer could be on the source queue * and may of disappeared after auth completes */ if (client->refbuf) { @@ -163,16 +186,23 @@ int client_read_bytes(client_t *client, void *buf, unsigned len) void client_send_error(client_t *client, int status, int plain, const char *message) { ssize_t ret; + refbuf_t *data; if (status == 500) { client_send_500(client, message); return; } + data = refbuf_new(PER_CLIENT_REFBUF_SIZE); + if (!data) { + client_send_500(client, message); + return; + } + ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status, NULL, plain ? "text/plain" : "text/html", "utf-8", - plain ? message : "", NULL, client); + NULL, NULL, client); if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); @@ -180,13 +210,26 @@ void client_send_error(client_t *client, int status, int plain, const char *mess return; } - if (!plain) - snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, + if (plain) { + strncpy(data->data, message, data->len); + data->data[data->len-1] = 0; + } else { + snprintf(data->data, data->len, "Error %i%i - %s\r\n", status, status, message); + } + data->len = strlen(data->data); + + snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, + "Content-Length: %llu\r\nConnection: Keep-Alive\r\n\r\n", + (long long unsigned int)data->len); client->respcode = status; client->refbuf->len = strlen (client->refbuf->data); + client->refbuf->next = data; + + client->reuse = ICECAST_REUSE_KEEPALIVE; + fserve_add_client (client, NULL); } diff --git a/src/client.h b/src/client.h index 5fdcd47e..6644129e 100644 --- a/src/client.h +++ b/src/client.h @@ -30,6 +30,13 @@ typedef enum _protocol_tag { ICECAST_PROTOCOL_SHOUTCAST } protocol_t; +typedef enum _reuse_tag { + /* do not reuse */ + ICECAST_REUSE_CLOSE = 0, + /* reuse */ + ICECAST_REUSE_KEEPALIVE +} reuse_t; + typedef struct _client_tag { /* mode of operation for this client */ @@ -37,6 +44,10 @@ typedef struct _client_tag /* the client's connection */ connection_t *con; + + /* Reuse this connection ... */ + reuse_t reuse; + /* the client's http headers */ http_parser_t *parser; diff --git a/src/connection.c b/src/connection.c index fdcce8e7..4ca9443c 100644 --- a/src/connection.c +++ b/src/connection.c @@ -685,6 +685,41 @@ static client_queue_t *create_client_node(client_t *client) return node; } +void connection_queue(connection_t *con) +{ + client_queue_t *node; + client_t *client = NULL; + + global_lock(); + if (client_create(&client, con, NULL) < 0) { + global_unlock(); + client_send_error(client, 403, 1, "Icecast connection limit reached"); + /* don't be too eager as this is an imposed hard limit */ + thread_sleep(400000); + return; + } + + /* setup client for reading incoming http */ + client->refbuf->data[PER_CLIENT_REFBUF_SIZE-1] = '\000'; + + if (sock_set_blocking(client->con->sock, 0) || sock_set_nodelay(client->con->sock)) { + global_unlock(); + ICECAST_LOG_WARN("failed to set tcp options on client connection, dropping"); + client_destroy(client); + return; + } + node = create_client_node(client); + global_unlock(); + + if (node == NULL) { + client_destroy(client); + return; + } + + _add_request_queue(node); + stats_event_inc(NULL, "connections"); +} + void connection_accept_loop(void) { connection_t *con; @@ -699,38 +734,7 @@ void connection_accept_loop(void) con = _accept_connection (duration); if (con) { - client_queue_t *node; - client_t *client = NULL; - - global_lock(); - if (client_create (&client, con, NULL) < 0) { - global_unlock(); - client_send_error(client, 403, 1, "Icecast connection limit reached"); - /* don't be too eager as this is an imposed hard limit */ - thread_sleep (400000); - continue; - } - - /* setup client for reading incoming http */ - client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; - - if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay (client->con->sock)) { - global_unlock(); - ICECAST_LOG_WARN("failed to set tcp options on client connection, dropping"); - client_destroy(client); - continue; - } - - node = create_client_node(client); - global_unlock(); - - if (node == NULL) { - client_destroy(client); - continue; - } - - _add_request_queue(node); - stats_event_inc(NULL, "connections"); + connection_queue(con); duration = 5; } else { if (_req_queue == NULL) @@ -1569,6 +1573,9 @@ int connection_setup_sockets (ice_config_t *config) void connection_close(connection_t *con) { + if (!con) + return; + sock_close(con->sock); if (con->ip) free(con->ip); diff --git a/src/connection.h b/src/connection.h index 7790fca8..7dfcd91d 100644 --- a/src/connection.h +++ b/src/connection.h @@ -60,6 +60,7 @@ int connection_setup_sockets(struct ice_config_tag *config); void connection_close(connection_t *con); connection_t *connection_create(sock_t sock, sock_t serversock, char *ip); int connection_complete_source(struct source_tag *source, int response); +void connection_queue(connection_t *con); extern rwlock_t _source_shutdown_rwlock; diff --git a/src/util.c b/src/util.c index 9062dfbe..6bb207ca 100644 --- a/src/util.c +++ b/src/util.c @@ -696,7 +696,7 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset, config = config_get_config(); extra_headers = _build_headers(status, config, source); - ret = snprintf (out, len, "%sServer: %s\r\nAccept-Encoding: identity\r\nConnection: close\r\nAllow: %s\r\n%s%s%s%s%s%s%s", + ret = snprintf (out, len, "%sServer: %s\r\nAccept-Encoding: identity\r\nAllow: %s\r\n%s%s%s%s%s%s%s", status_buffer, config->server_id, (client->admin_command == ADMIN_COMMAND_ERROR ?