1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-23 06:25:24 +00:00

Fix: allow Keep-Alive connections on normal error pages

This commit is contained in:
Philipp Schafft 2015-02-01 23:04:54 +00:00
parent 78213fe30a
commit c806e47a4a
5 changed files with 98 additions and 36 deletions

View File

@ -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,
"<html><head><title>Error %i</title></head><body><b>%i - %s</b></body></html>\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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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 ?