[Icecast-dev] [PATCH 15/31] Connection: simplify in-connection handeling (kill 1 function, and break shoutcast)

Niv Sardi
Fri, 30 Jul 2010 14:54:37 -0000

src/connection.c |  395 ++++++++++++++++++------------------------------------
 1 files changed, 131 insertions(+), 264 deletions(-)

diff --git a/src/connection.c b/src/connection.c
index ed8bea9..5c9e96e 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -108,7 +108,6 @@ static spin_t _connection_lock;
 static volatile unsigned long _current_id = 0;
 static int _initialized = 0;

-static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail =
&_req_queue;
 static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail =
&_con_queue;
 static int ssl_ok;
 #ifdef HAVE_OPENSSL
@@ -120,7 +119,8 @@ cache_file_contents banned_ip, allowed_ip;

 rwlock_t _source_shutdown_rwlock;

-static void _handle_connection(void);
+static void _handle_shoutcast_compatible (int shoutcast, char
*shoutcast_mount);
+static int _handle_client (client_t *client);

 static int compare_ip (void *arg, void *a, void *b)
 {
@@ -146,8 +146,6 @@ void connection_initialize(void)
     thread_mutex_create(&move_clients_mutex);
     thread_rwlock_create(&_source_shutdown_rwlock);
     thread_cond_init(&global.shutdown_cond);
-    _req_queue = NULL;
-    _req_queue_tail = &_req_queue;
     _con_queue = NULL;
     _con_queue_tail = &_con_queue;

@@ -553,100 +551,6 @@ static connection_t *_accept_connection(int duration)
     return NULL;
 }

-
-/* add client to connection queue. At this point some header information
- * has been collected, so we now pass it onto the connection thread for
- * further processing
- */
-static void _add_connection (client_queue_t *node)
-{
-    *_con_queue_tail = node;
-    _con_queue_tail = (volatile client_queue_t **)&node->next;
-}
-
-
-/* this returns queued clients for the connection thread. headers are
- * already provided, but need to be parsed.
- */
-static client_queue_t *_get_connection(void)
-{
-    client_queue_t *node = NULL;
-
-    /* common case, no new connections so don't bother taking locks */
-    if (_con_queue)
-    {
-        node = (client_queue_t *)_con_queue;
-        _con_queue = node->next;
-        if (_con_queue == NULL)
-            _con_queue_tail = &_con_queue;
-        node->next = NULL;
-    }
-    return node;
-}
-
-
-/* run along queue checking for any data that has come in or a timeout */
-static void process_request_queue (int timeout)
-{
-    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
-
-    while (*node_ref)
-    {
-        client_queue_t *node = *node_ref;
-        client_t *client = node->client;
-        int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
-        char *buf = client->refbuf->data + node->offset;
-
-        if (len > 0)
-        {
-            if (client->con->con_time + timeout <= time(NULL))
-                len = 0;
-            else
-                len = client_read_bytes (client, buf, len);
-        }
-
-        if (len > 0)
-        {
-            node->offset += len;
-            if ((len = util_find_eos_delim(client->refbuf, -node->offset,
-			    node->shoutcast?HEADER_READ_LINE:HEADER_READ_ENTIRE)) < 0) {
-                node->stream_offset = len;
-                if ((client_queue_t **)_req_queue_tail == &(node->next))
-                    _req_queue_tail = (volatile client_queue_t **)node_ref;
-                *node_ref = node->next;
-                node->next = NULL;
-                _add_connection (node);
-                continue;
-            }
-        }
-        else
-        {
-            if (len == 0 || client->con->error)
-            {
-                if ((client_queue_t **)_req_queue_tail == &node->next)
-                    _req_queue_tail = (volatile client_queue_t **)node_ref;
-                *node_ref = node->next;
-                client_destroy (client);
-                free (node);
-                continue;
-            }
-        }
-        node_ref = &node->next;
-    }
-    _handle_connection();
-}
-
-
-/* add node to the queue of requests. This is where the clients are when
- * initial http details are read.
- */
-static void _add_request_queue (client_queue_t *node)
-{
-    *_req_queue_tail = node;
-    _req_queue_tail = (volatile client_queue_t **)&node->next;
-}
-
-
 void connection_accept_loop (void)
 {
     connection_t *con;
@@ -663,68 +567,99 @@ void connection_accept_loop (void)
     {
         con = _accept_connection (duration);

-        if (con)
-        {
-            client_queue_t *node;
-            ice_config_t *config;
-            client_t *client = NULL;
-            listener_t *listener;
+        if (!con) {
+            duration = 300; /* use longer timeouts when nothing waiting */
+            continue;
+        }

-            global_lock();
-            if (client_create (&client, con, NULL) < 0)
-            {
-                global_unlock();
-                client_send_403 (client, "Icecast connection limit
reached");
-                /* don't be too eager as this is an imposed hard limit */
-                thread_sleep (400000);
-                continue;
-            }
+        ice_config_t *config;
+        client_t *client = NULL;
+        listener_t *listener;
+        refbuf_t *header = NULL;
+        http_parser_t *parser = NULL;
+        int hdrsize = 0;
+        int shoutcast = 0;
+        char *shoutcast_mount = NULL;
+
+        header = refbuf_new (PER_CLIENT_REFBUF_SIZE);
+        hdrsize = util_read_header (con, header, HEADER_READ_ENTIRE);
+        if (hdrsize < 0)
+        {
+            global_unlock();
+            ERROR ("Header read failed");
+            thread_sleep (400000);
+            continue;
+        }

-            /* setup client for reading incoming http */
-            client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] =
'\000';
+        /* process normal HTTP headers */
+        parser = httpp_create_parser();
+        httpp_initialize(parser, NULL);
+        if (!httpp_parse (parser, header->data, hdrsize))
+        {
+            ERROR0("HTTP request parsing failed");
+            client_destroy (client);
+            continue;
+        }

-            if (sock_set_blocking (client->con->sock, 0) ||
sock_set_nodelay (client->con->sock))
-            {
-                global_unlock();
-                WARN0 ("failed to set tcp options on client connection,
dropping");
-                client_destroy (client);
-                continue;
-            }
+        if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
+        {
+            ERROR("Error(%s)", httpp_getvar(parser,
HTTPP_VAR_ERROR_MESSAGE));
+            break;
+        }

-            node = calloc (1, sizeof (client_queue_t));
-            if (node == NULL)
-            {
-                global_unlock();
-                client_destroy (client);
-                continue;
-            }
-            node->client = client;
+        global_lock();
+        if (client_create (&client, con, parser) < 0)
+        {
+            global_unlock();
+            client_send_403 (client, "Icecast connection limit
reached");
+            /* don't be too eager as this is an imposed hard limit */
+            thread_sleep (400000);
+            continue;
+        }

-            config = config_get_config();
-            listener = config_get_listen_sock (config, client->con);
+        client_set_queue (client, header);

-            if (listener)
-            {
-                if (listener->shoutcast_compat)
-                    node->shoutcast = 1;
-                if (listener->ssl && ssl_ok)
-                    connection_uses_ssl (client->con);
-                if (listener->shoutcast_mount)
-                    node->shoutcast_mount = strdup
(listener->shoutcast_mount);
-            }
+        if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay
(client->con->sock))
+        {
             global_unlock();
-            config_release_config();
-
-            _add_request_queue (node);
-            stats_event_inc (NULL, "connections");
-            duration = 5;
+            WARN0 ("failed to set tcp options on client connection,
dropping");
+            client_destroy (client);
+            continue;
         }
-        else
+
+        header->len -= hdrsize;
+        memmove(header->data, header->data + hdrsize, header->len);
+        client_set_queue (client, header);
+        refbuf_release(header);
+
+//        client->pos = hdrsize;
+
+        config = config_get_config();
+        listener = config_get_listen_sock (config, client->con);
+
+        if (listener)
         {
-            if (_req_queue == NULL)
-                duration = 300; /* use longer timeouts when nothing waiting */
+            if (listener->shoutcast_compat)
+                shoutcast = 1;
+            if (listener->ssl && ssl_ok)
+                connection_uses_ssl (client->con);
+            if (listener->shoutcast_mount)
+                shoutcast_mount = strdup (listener->shoutcast_mount);
+        }
+        global_unlock();
+        config_release_config();
+
+        stats_event_inc (NULL, "connections");
+        duration = 5;
+
+        if (client->con->con_time + timeout <= time(NULL))
+            continue;
+
+        if (shoutcast) {
+            _handle_shoutcast_compatible (shoutcast, shoutcast_mount);
+        } else {
+            _handle_client (client);
         }
-        process_request_queue (timeout);
     }

     /* Give all the other threads notification to shut down */
@@ -1113,22 +1048,22 @@ static void _handle_get_request (client_t *client, char
*passed_uri)
     if (uri != passed_uri) free (uri);
 }

-static void _handle_shoutcast_compatible (client_queue_t *node)
+static void _handle_shoutcast_compatible (int shoutcast, char *shoutcast_mount)
 {
+/*
+  SHOUTCAST IS BROKEN
+*/
     char *http_compliant;
     int http_compliant_len = 0;
     http_parser_t *parser;
     ice_config_t *config = config_get_config ();
-    char *shoutcast_mount;
-    client_t *client = node->client;
+    client_t *client = NULL; //node->client;
     refbuf_t *refbuf = client->refbuf;

-    if (node->shoutcast_mount)
-        shoutcast_mount = node->shoutcast_mount;
-    else
+    if (!shoutcast_mount)
         shoutcast_mount = config->shoutcast_mount;

-    if (node->shoutcast == 1)
+    if (shoutcast == 1)
     {
         char *source_password;
         mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount);
@@ -1143,8 +1078,6 @@ static void _handle_shoutcast_compatible (client_queue_t
*node)
         if ((hdrlen = util_find_eos_delim (client->refbuf, 0,
HEADER_READ_LINE)) < 0) {
             client_destroy (client);
             free (source_password);
-            free (node->shoutcast_mount);
-            free (node);
             return;
         }

@@ -1153,20 +1086,17 @@ static void _handle_shoutcast_compatible (client_queue_t
*node)
             /* send this non-blocking but if there is only a partial write
              * then leave to header timeout */
             sock_write (client->con->sock,
"OK2\r\nicy-caps:11\r\n\r\n");
-            node->offset -= hdrlen;
-            memmove (refbuf->data, refbuf->data + hdrlen,
node->offset+1);
-            node->shoutcast = 2;
+            shoutcast = 2;
             /* we've checked the password, now send it back for reading
headers */
-            _add_request_queue (node);
+//            _add_request_queue (node);
             free (source_password);
             return;
         }

-	INFO1 ("password does not match \"%s\"",
client->refbuf->data);
+        INFO1 ("password does not match \"%s\"",
refbuf->data);
+
         client_destroy (client);
         free (source_password);
-        free (node->shoutcast_mount);
-        free (node);
         return;
     }
     /* actually make a copy as we are dropping the config lock */
@@ -1175,7 +1105,7 @@ static void _handle_shoutcast_compatible (client_queue_t
*node)
     /* Here we create a valid HTTP request based of the information
        that was passed in via the non-HTTP style protocol above. This
        means we can use some of our existing code to handle this case */
-    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
+//    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
     http_compliant = (char *)calloc(1, http_compliant_len);
     snprintf (http_compliant, http_compliant_len,
             "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount,
refbuf->data);
@@ -1183,15 +1113,6 @@ static void _handle_shoutcast_compatible (client_queue_t
*node)
     httpp_initialize(parser, NULL);
     if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
     {
-        /* we may have more than just headers, so prepare for it */
-        if (node->stream_offset == node->offset)
-            refbuf->len = 0;
-        else
-        {
-            char *ptr = refbuf->data;
-            refbuf->len = node->offset - node->stream_offset;
-            memmove (ptr, ptr + node->stream_offset, refbuf->len);
-        }
         client->parser = parser;
         source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
     }
@@ -1201,106 +1122,52 @@ static void _handle_shoutcast_compatible
(client_queue_t *node)
     }
     free (http_compliant);
     free (shoutcast_mount);
-    free (node->shoutcast_mount);
-    free (node);
     return;
 }

-
-/* Connection thread. Here we take clients off the connection queue and check
- * the contents provided. We set up the parser then hand off to the specific
- * request handler.
- */
-static void _handle_connection(void)
+static int _handle_client (client_t *client)
 {
-    http_parser_t *parser;
     const char *rawuri;
-    client_queue_t *node;
-
-    while (1)
-    {
-        node = _get_connection();
-        if (! node)
-            break;
-
-        client_t *client = node->client;
-        refbuf_t *refbuf = client->refbuf;
-        char *uri;
-
-
-        /* Check for special shoutcast compatability processing */
-        if (node->shoutcast)
-        {
-            _handle_shoutcast_compatible (node);
-            continue;
-        }
-
-        /* process normal HTTP headers */
-        parser = httpp_create_parser();
-        httpp_initialize(parser, NULL);
-        client->parser = parser;
-        if (!httpp_parse (parser, refbuf->data, node->offset))
-        {
-            free (node);
-            ERROR0("HTTP request parsing failed");
-            client_destroy (client);
-            continue;
-        }
-
-        /* we may have more than just headers, so prepare for it */
-        if (node->stream_offset == node->offset) {
-            refbuf->len = 0;
-        } else {
-            char *ptr = refbuf->data;
-            refbuf->len = node->offset - node->stream_offset;
-            memmove (ptr, ptr + node->stream_offset, refbuf->len);
-        }
-
-        rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
+    http_parser_t *parser = client->parser;
+    char *uri;

-        /* assign a port-based shoutcast mountpoint if required */
-        if (node->shoutcast_mount && strcmp (rawuri,
"/admin.cgi") == 0)
-            httpp_set_query_param (client->parser, "mount",
node->shoutcast_mount);
+    rawuri = httpp_getvar(parser, HTTPP_VAR_URI);

-        free (node->shoutcast_mount);
-        free (node);
-
-        if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL))
&&
-            strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)))
{
-            ERROR0("Bad HTTP protocol detected");
-            client_destroy (client);
-            continue;
-        }
+    if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL))
&&
+        strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
+        ERROR0("Bad HTTP protocol detected");
+        client_destroy (client);
+        return 0;
+    }

-        uri = util_normalise_uri(rawuri);
+    uri = util_normalise_uri(rawuri);

-        if (uri == NULL)
-        {
-            client_destroy (client);
-            continue;
-        }
-
-        if (parser->req_type == httpp_req_source) {
-            _handle_source_request (client, uri);
-        }
-        else if (parser->req_type == httpp_req_post) {
-            _handle_post_request (client, uri);
-        }
-        else if (parser->req_type == httpp_req_stats) {
-            _handle_stats_request (client, uri);
-        }
-        else if (parser->req_type == httpp_req_get) {
-            _handle_get_request (client, uri);
-        }
-        else {
-            ERROR0("Wrong request type from client");
-            client_send_400 (client, "unknown request");
-        }
+    if (uri == NULL)
+    {
+        client_destroy (client);
+        return 0;
+    }

-        free(uri);
+    if (parser->req_type == httpp_req_source) {
+        _handle_source_request (client, uri);
+    }
+    else if (parser->req_type == httpp_req_post) {
+        _handle_post_request (client, uri);
+    }
+    else if (parser->req_type == httpp_req_stats) {
+        _handle_stats_request (client, uri);
+    }
+    else if (parser->req_type == httpp_req_get) {
+        _handle_get_request (client, uri);
+    }
+    else {
+        ERROR0("Wrong request type from client");
+        client_send_400 (client, "unknown request");
     }
-}

+    free(uri);
+    return 1;
+}

 /* called when listening thread is not checking for incoming connections */
 int connection_setup_sockets (ice_config_t *config)
--
1.7.1