From d6f4e60bf5df325090b6132cc27fc7251371bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 10:42:34 +0200 Subject: [PATCH 01/23] network/http-launch: Initial commit --- network/http-launch/http-launch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 network/http-launch/http-launch.c diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c new file mode 100644 index 0000000000..3b44ad2d80 --- /dev/null +++ b/network/http-launch/http-launch.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int +main (gint argc, gchar **argv) +{ + + return 0; +} From d72c7a85baf1d26e217a86d971f68e1591b0d422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 12:58:43 +0200 Subject: [PATCH 02/23] network/http-launch: Add initial, working version of the code --- network/http-launch/http-launch.c | 402 +++++++++++++++++++++++++++++- 1 file changed, 400 insertions(+), 2 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 3b44ad2d80..064d506c90 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -1,10 +1,408 @@ +/* + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include -int -main (gint argc, gchar **argv) +typedef struct { + gchar *name; + GSocketConnection *connection; + GSocket *socket; + GInputStream *istream; + GOutputStream *ostream; + GSource *isource; + GByteArray *current_message; +} Client; + +static GMainLoop *loop = NULL; +G_LOCK_DEFINE_STATIC (clients); +static GList *clients = NULL; +static GstElement *pipeline = NULL; +static GstElement *multisocketsink = NULL; +static gboolean started = FALSE; + +static void +remove_client (Client * client) +{ + g_print ("Removing connection %s\n", client->name); + + g_free (client->name); + + if (client->isource) { + g_source_destroy (client->isource); + g_source_unref (client->isource); + } + g_object_unref (client->connection); + g_byte_array_unref (client->current_message); + + G_LOCK (clients); + clients = g_list_remove (clients, client); + G_UNLOCK (clients); + + g_slice_free (Client, client); +} + +static void +write_bytes (Client * client, const gchar * data, guint len) +{ + gssize w; + GError *err = NULL; + + /* TODO: We assume this never blocks */ + do { + w = g_output_stream_write (client->ostream, data, len, NULL, &err); + if (w > 0) + len -= w; + } while (w >= 0 && len > 0); + + if (w < 0) { + remove_client (client); + } +} + +static void +client_message (Client * client, const gchar * data, guint len) +{ + gchar **lines = g_strsplit_set (data, "\r\n", -1); + + if (g_str_has_prefix (lines[0], "HEAD")) { + gchar **parts = g_strsplit (lines[0], " ", -1); + gchar *response; + const gchar *http_version; + + /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ + if (*parts[2] != '\0') + http_version = parts[2]; + else + http_version = "HTTP/1.1"; + + if (strcmp (parts[1], "/") == 0) { + response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); + } else { + response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); + } + write_bytes (client, response, strlen (response)); + g_free (response); + g_strfreev (parts); + } else if (g_str_has_prefix (lines[0], "GET")) { + gchar **parts = g_strsplit (lines[0], " ", -1); + gchar *response; + const gchar *http_version; + gboolean ok = FALSE; + + /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ + if (*parts[2] != '\0') + http_version = parts[2]; + else + http_version = "HTTP/1.1"; + + if (strcmp (parts[1], "/") == 0) { + response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); + ok = TRUE; + } else { + response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); + } + write_bytes (client, response, strlen (response)); + g_free (response); + g_strfreev (parts); + + if (ok) { + g_source_destroy (client->isource); + g_source_unref (client->isource); + client->isource = NULL; + g_print ("Starting to stream to %s\n", client->name); + g_signal_emit_by_name (multisocketsink, "add", client->socket); + + if (!started) { + g_print ("Starting pipeline\n"); + if (gst_element_set_state (pipeline, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to start pipeline\n"); + g_main_loop_quit (loop); + } + started = TRUE; + } + } + } else { + gchar **parts = g_strsplit (lines[0], " ", -1); + gchar *response; + const gchar *http_version; + + /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ + if (*parts[2] != '\0') + http_version = parts[2]; + else + http_version = "HTTP/1.1"; + + response = g_strdup_printf ("%s 400 Bad Request\r\n\r\n", http_version); + write_bytes (client, response, strlen (response)); + g_free (response); + g_strfreev (parts); + remove_client (client); + } + + g_strfreev (lines); +} + +static gboolean +on_read_bytes (GPollableInputStream * stream, Client * client) +{ + gssize r; + gchar data[4096]; + GError *err = NULL; + + do { + r = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM + (client->istream), data, sizeof (data), NULL, &err); + if (r > 0) + g_byte_array_append (client->current_message, (guint8 *) data, r); + } while (r > 0); + + if (r == 0 || g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + guint8 *tmp = client->current_message->data; + + /* Read everything */ + g_clear_error (&err); + + while (client->current_message->len > 3) { + if (tmp[0] == 0x0d && tmp[1] == 0x0a && tmp[2] == 0x0d && tmp[3] == 0x0a) { + guint len = tmp - client->current_message->data + 4; + + client_message (client, (gchar *) client->current_message->data, len); + g_byte_array_remove_range (client->current_message, 0, len); + tmp = client->current_message->data; + } else { + tmp++; + } + } + + if (r == 0) { + remove_client (client); + return FALSE; + } + + return TRUE; + } else { + g_assert_not_reached (); + } + + return FALSE; +} + +static gboolean +on_new_connection (GSocketService * service, GSocketConnection * connection, + GObject * source_object, gpointer user_data) +{ + Client *client = g_slice_new0 (Client); + GSocketAddress *addr; + GInetAddress *iaddr; + gchar *ip; + guint16 port; + + addr = g_socket_connection_get_remote_address (connection, NULL); + iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); + port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)); + ip = g_inet_address_to_string (iaddr); + client->name = g_strdup_printf ("%s:%u", ip, port); + g_free (ip); + g_object_unref (addr); + + g_print ("New connection %s\n", client->name); + + client->connection = g_object_ref (connection); + client->socket = g_socket_connection_get_socket (connection); + client->istream = + g_io_stream_get_input_stream (G_IO_STREAM (client->connection)); + client->ostream = + g_io_stream_get_output_stream (G_IO_STREAM (client->connection)); + client->current_message = g_byte_array_sized_new (1024); + + client->isource = + g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM + (client->istream), NULL); + g_source_set_callback (client->isource, (GSourceFunc) on_read_bytes, client, + NULL); + g_source_attach (client->isource, NULL); + + G_LOCK (clients); + clients = g_list_prepend (clients, client); + G_UNLOCK (clients); + + return TRUE; +} + +static gboolean +on_message (GstBus * bus, GstMessage * message, gpointer user_data) +{ + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR:{ + gchar *debug; + GError *err; + + gst_message_parse_error (message, &err, &debug); + g_print ("Error %s\n", err->message); + g_error_free (err); + g_free (debug); + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_WARNING:{ + gchar *debug; + GError *err; + + gst_message_parse_error (message, &err, &debug); + g_print ("Warning %s\n", err->message); + g_error_free (err); + g_free (debug); + } + default: + break; + } + + return TRUE; +} + +static void +on_client_socket_removed (GstElement * element, GSocket * socket, + gpointer user_data) +{ + GList *l; + Client *client = NULL; + + G_LOCK (clients); + for (l = clients; l; l = l->next) { + Client *tmp = l->data; + if (socket == tmp->socket) { + client = tmp; + break; + } + } + G_UNLOCK (clients); + + if (client) + remove_client (client); +} + +int +main (gint argc, gchar ** argv) +{ + GSocketService *service; + GstElement *bin, *stream; + GstPad *srcpad, *ghostpad, *sinkpad; + GError *err = NULL; + GstBus *bus; + + gst_init (&argc, &argv); + + if (argc < 2) { + g_print ("usage: %s \n" + "example: %s \"( videotestsrc ! theoraenc ! oggmux name=stream )\"\n", + argv[0], argv[0]); + return -1; + } + + bin = gst_parse_launchv ((const gchar **) argv + 1, &err); + if (!bin) { + g_print ("invalid pipeline: %s\n", err->message); + g_clear_error (&err); + return -2; + } + + stream = gst_bin_get_by_name (GST_BIN (bin), "stream"); + if (!stream) { + g_print ("no element with name \"stream\" found\n"); + gst_object_unref (bin); + return -3; + } + + srcpad = gst_element_get_static_pad (stream, "src"); + if (!srcpad) { + g_print ("no \"src\" pad in element \"stream\" found\n"); + gst_object_unref (stream); + gst_object_unref (bin); + return -4; + } + + ghostpad = gst_ghost_pad_new ("src", srcpad); + gst_element_add_pad (GST_ELEMENT (bin), ghostpad); + gst_object_unref (srcpad); + + pipeline = gst_pipeline_new (NULL); + + multisocketsink = gst_element_factory_make ("multisocketsink", NULL); + g_object_set (multisocketsink, + "unit-format", GST_FORMAT_TIME, + "units-max", (gint64) 7 * GST_SECOND, + "units-soft-max", (gint64) 3 * GST_SECOND, + "recover-policy", 3 /* keyframe */ , + "timeout", (guint64) 7 * GST_SECOND, + "sync-method", 1 /* next-keyframe */ , + NULL); + + gst_bin_add_many (GST_BIN (pipeline), bin, multisocketsink, NULL); + + sinkpad = gst_element_get_static_pad (multisocketsink, "sink"); + gst_pad_link (ghostpad, sinkpad); + gst_object_unref (sinkpad); + + bus = gst_element_get_bus (pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", G_CALLBACK (on_message), NULL); + gst_object_unref (bus); + + g_signal_connect (multisocketsink, "client-socket-removed", + G_CALLBACK (on_client_socket_removed), NULL); + + loop = g_main_loop_new (NULL, FALSE); + + if (gst_element_set_state (pipeline, + GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) { + gst_object_unref (pipeline); + g_main_loop_unref (loop); + g_print ("Failed to set pipeline to ready\n"); + return -5; + } + + service = g_socket_service_new (); + g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), 8080, NULL, + NULL); + + g_signal_connect (service, "incoming", G_CALLBACK (on_new_connection), NULL); + + g_socket_service_start (service); + + g_print ("Listening on http://127.0.0.1:8080/\n"); + + g_main_loop_run (loop); + + g_socket_service_stop (service); + g_object_unref (service); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + g_main_loop_unref (loop); return 0; } From 7c1b72ef488e98df2f68ab78a3f77e7a4bc65842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 13:03:51 +0200 Subject: [PATCH 03/23] network/http-launch: Fix example commandline --- network/http-launch/http-launch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 064d506c90..9b49003512 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -317,7 +317,7 @@ main (gint argc, gchar ** argv) if (argc < 2) { g_print ("usage: %s \n" - "example: %s \"( videotestsrc ! theoraenc ! oggmux name=stream )\"\n", + "example: %s ( videotestsrc ! theoraenc ! oggmux name=stream )\n", argv[0], argv[0]); return -1; } From 1cbacffb52da51d9e7f7e62596952621b02474db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 13:27:20 +0200 Subject: [PATCH 04/23] network/http-launch: Adjust timeout a bit --- network/http-launch/http-launch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 9b49003512..07fbbca898 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -356,7 +356,7 @@ main (gint argc, gchar ** argv) "units-max", (gint64) 7 * GST_SECOND, "units-soft-max", (gint64) 3 * GST_SECOND, "recover-policy", 3 /* keyframe */ , - "timeout", (guint64) 7 * GST_SECOND, + "timeout", (guint64) 10 * GST_SECOND, "sync-method", 1 /* next-keyframe */ , NULL); From f132da65a7b5eeee15afa6824aa8a5c73a14eb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 13:54:58 +0200 Subject: [PATCH 05/23] network/http-launch: Add EOS handling Just quit on EOS for now --- network/http-launch/http-launch.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 07fbbca898..fd104fd668 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -275,6 +275,11 @@ on_message (GstBus * bus, GstMessage * message, gpointer user_data) g_print ("Warning %s\n", err->message); g_error_free (err); g_free (debug); + break; + } + case GST_MESSAGE_EOS:{ + g_print ("EOS\n"); + g_main_loop_quit (loop); } default: break; From 1445b2bf9205c3d99ec55e1ce61f678e1ee7c7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:24:53 +0200 Subject: [PATCH 06/23] network/http-launch: Fix copyright year --- network/http-launch/http-launch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index fd104fd668..7c6c8a4684 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Sebastian Dröge + * Copyright (C) 2013 Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public From c8cfba9acc09c61197c637707e8aa28754e3f0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:32:16 +0200 Subject: [PATCH 07/23] network/http-launch: Improve error reporting a bit and add some FIXME/TODO comments here and there --- network/http-launch/http-launch.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 7c6c8a4684..4bf9cffcfc 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -74,9 +74,13 @@ write_bytes (Client * client, const gchar * data, guint len) w = g_output_stream_write (client->ostream, data, len, NULL, &err); if (w > 0) len -= w; - } while (w >= 0 && len > 0); + } while (w > 0 && len > 0); - if (w < 0) { + if (w <= 0) { + if (err) { + g_print ("Write error %s\n", err->message); + g_clear_error (&err); + } remove_client (client); } } @@ -86,6 +90,7 @@ client_message (Client * client, const gchar * data, guint len) { gchar **lines = g_strsplit_set (data, "\r\n", -1); + /* TODO: Make HTTP handling more robust, add proper HTTP 1.0 support */ if (g_str_has_prefix (lines[0], "HEAD")) { gchar **parts = g_strsplit (lines[0], " ", -1); gchar *response; @@ -179,10 +184,12 @@ on_read_bytes (GPollableInputStream * stream, Client * client) g_byte_array_append (client->current_message, (guint8 *) data, r); } while (r > 0); - if (r == 0 || g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + if (r == 0) { + remove_client (client); + return FALSE; + } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { guint8 *tmp = client->current_message->data; - /* Read everything */ g_clear_error (&err); while (client->current_message->len > 3) { @@ -197,14 +204,14 @@ on_read_bytes (GPollableInputStream * stream, Client * client) } } - if (r == 0) { - remove_client (client); - return FALSE; - } + /* FIXME: If too large, disconnect client */ return TRUE; } else { - g_assert_not_reached (); + g_print ("Read error %s\n", err->message); + g_clear_error (&err); + remove_client (client); + return FALSE; } return FALSE; @@ -230,6 +237,7 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, g_print ("New connection %s\n", client->name); + /* TODO: Need timeout */ client->connection = g_object_ref (connection); client->socket = g_socket_connection_get_socket (connection); client->istream = From 9228d05bf340ec70951c20b626b885162608d9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:35:20 +0200 Subject: [PATCH 08/23] network/http-launch: Timeout if client did not do a GET after 5 seconds --- network/http-launch/http-launch.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 4bf9cffcfc..238385ff7b 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -31,7 +31,7 @@ typedef struct GSocket *socket; GInputStream *istream; GOutputStream *ostream; - GSource *isource; + GSource *isource, *tosource; GByteArray *current_message; } Client; @@ -53,6 +53,10 @@ remove_client (Client * client) g_source_destroy (client->isource); g_source_unref (client->isource); } + if (client->tosource) { + g_source_destroy (client->tosource); + g_source_unref (client->tosource); + } g_object_unref (client->connection); g_byte_array_unref (client->current_message); @@ -136,6 +140,9 @@ client_message (Client * client, const gchar * data, guint len) g_source_destroy (client->isource); g_source_unref (client->isource); client->isource = NULL; + g_source_destroy (client->tosource); + g_source_unref (client->tosource); + client->tosource = NULL; g_print ("Starting to stream to %s\n", client->name); g_signal_emit_by_name (multisocketsink, "add", client->socket); @@ -170,6 +177,15 @@ client_message (Client * client, const gchar * data, guint len) g_strfreev (lines); } +static gboolean +on_timeout (Client * client) +{ + g_print ("Timeout\n"); + remove_client (client); + + return FALSE; +} + static gboolean on_read_bytes (GPollableInputStream * stream, Client * client) { @@ -237,7 +253,6 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, g_print ("New connection %s\n", client->name); - /* TODO: Need timeout */ client->connection = g_object_ref (connection); client->socket = g_socket_connection_get_socket (connection); client->istream = @@ -246,6 +261,11 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, g_io_stream_get_output_stream (G_IO_STREAM (client->connection)); client->current_message = g_byte_array_sized_new (1024); + client->tosource = g_timeout_source_new_seconds (5); + g_source_set_callback (client->isource, (GSourceFunc) on_timeout, client, + NULL); + g_source_attach (client->tosource, NULL); + client->isource = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (client->istream), NULL); From e38e9ce609c5aae7404b71f14cc76492c3f2c2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:38:01 +0200 Subject: [PATCH 09/23] network/http-launch: Disconnect clients if there was no valid HTTP request after 1MB of data --- network/http-launch/http-launch.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 238385ff7b..7b0289056d 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -220,7 +220,11 @@ on_read_bytes (GPollableInputStream * stream, Client * client) } } - /* FIXME: If too large, disconnect client */ + if (client->current_message->len >= 1024 * 1024) { + g_print ("No complete request after 1MB of data\n"); + remove_client (client); + return FALSE; + } return TRUE; } else { From 466d2496479a4391952866e4cada00be80d7e426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:39:07 +0200 Subject: [PATCH 10/23] network/http-launch: Fix writing response if the write could not be finished in one rurun --- network/http-launch/http-launch.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 7b0289056d..13341d9a35 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -76,8 +76,10 @@ write_bytes (Client * client, const gchar * data, guint len) /* TODO: We assume this never blocks */ do { w = g_output_stream_write (client->ostream, data, len, NULL, &err); - if (w > 0) + if (w > 0) { len -= w; + data += w; + } } while (w > 0 && len > 0); if (w <= 0) { From c8a308eee2664b6c0e6ec3341f6b2a3590155db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 18:53:51 +0200 Subject: [PATCH 11/23] network/http-launch: Make HTTP handling a bit more robust --- network/http-launch/http-launch.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 13341d9a35..5d741e2e54 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -96,19 +96,17 @@ client_message (Client * client, const gchar * data, guint len) { gchar **lines = g_strsplit_set (data, "\r\n", -1); - /* TODO: Make HTTP handling more robust, add proper HTTP 1.0 support */ if (g_str_has_prefix (lines[0], "HEAD")) { gchar **parts = g_strsplit (lines[0], " ", -1); gchar *response; const gchar *http_version; - /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ - if (*parts[2] != '\0') + if (parts[1] && parts[2] && *parts[2] != '\0') http_version = parts[2]; else - http_version = "HTTP/1.1"; + http_version = "HTTP/1.0"; - if (strcmp (parts[1], "/") == 0) { + if (parts[1] && strcmp (parts[1], "/") == 0) { response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); } else { response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); @@ -122,13 +120,12 @@ client_message (Client * client, const gchar * data, guint len) const gchar *http_version; gboolean ok = FALSE; - /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ - if (*parts[2] != '\0') + if (parts[1] && parts[2] && *parts[2] != '\0') http_version = parts[2]; else - http_version = "HTTP/1.1"; + http_version = "HTTP/1.0"; - if (strcmp (parts[1], "/") == 0) { + if (parts[1] && strcmp (parts[1], "/") == 0) { response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); ok = TRUE; } else { @@ -163,11 +160,10 @@ client_message (Client * client, const gchar * data, guint len) gchar *response; const gchar *http_version; - /* FIXME: Assume that 3 parts at least, probably wrong for HTTP 1.0 */ - if (*parts[2] != '\0') + if (parts[1] && parts[2] && *parts[2] != '\0') http_version = parts[2]; else - http_version = "HTTP/1.1"; + http_version = "HTTP/1.0"; response = g_strdup_printf ("%s 400 Bad Request\r\n\r\n", http_version); write_bytes (client, response, strlen (response)); @@ -268,7 +264,7 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, client->current_message = g_byte_array_sized_new (1024); client->tosource = g_timeout_source_new_seconds (5); - g_source_set_callback (client->isource, (GSourceFunc) on_timeout, client, + g_source_set_callback (client->tosource, (GSourceFunc) on_timeout, client, NULL); g_source_attach (client->tosource, NULL); From dcf3da3d5d98771b45c871965e07c9045d0dbdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 28 Jul 2013 19:01:34 +0200 Subject: [PATCH 12/23] network/http-launch: Append \0 at the end of the requests to make the string functions happy --- network/http-launch/http-launch.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 5d741e2e54..4ab7a275b5 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -208,8 +208,10 @@ on_read_bytes (GPollableInputStream * stream, Client * client) while (client->current_message->len > 3) { if (tmp[0] == 0x0d && tmp[1] == 0x0a && tmp[2] == 0x0d && tmp[3] == 0x0a) { - guint len = tmp - client->current_message->data + 4; + guint len; + g_byte_array_append (client->current_message, (const guint8 *) "\0", 1); + len = tmp - client->current_message->data + 5; client_message (client, (gchar *) client->current_message->data, len); g_byte_array_remove_range (client->current_message, 0, len); tmp = client->current_message->data; From 5a2104e89da99c845f89323e0a078a5a382f3320 Mon Sep 17 00:00:00 2001 From: Guillaume Seguin Date: Fri, 17 Oct 2014 14:39:30 +0200 Subject: [PATCH 13/23] network/http-launch: Add PORT command line argument --- network/http-launch/http-launch.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 4ab7a275b5..63c575a7c2 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -352,14 +352,17 @@ main (gint argc, gchar ** argv) gst_init (&argc, &argv); - if (argc < 2) { - g_print ("usage: %s \n" - "example: %s ( videotestsrc ! theoraenc ! oggmux name=stream )\n", + if (argc < 4) { + g_print ("usage: %s PORT \n" + "example: %s 8080 ( videotestsrc ! theoraenc ! oggmux name=stream )\n", argv[0], argv[0]); return -1; } - bin = gst_parse_launchv ((const gchar **) argv + 1, &err); + const gchar *port_str = argv[1]; + const int port = (int) g_ascii_strtoll(port_str, NULL, 10); + + bin = gst_parse_launchv ((const gchar **) argv + 2, &err); if (!bin) { g_print ("invalid pipeline: %s\n", err->message); g_clear_error (&err); @@ -422,14 +425,14 @@ main (gint argc, gchar ** argv) } service = g_socket_service_new (); - g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), 8080, NULL, + g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), port, NULL, NULL); g_signal_connect (service, "incoming", G_CALLBACK (on_new_connection), NULL); g_socket_service_start (service); - g_print ("Listening on http://127.0.0.1:8080/\n"); + g_print ("Listening on http://127.0.0.1:%d/\n", port); g_main_loop_run (loop); From 28507ed0cfab7f56c5ffc09a8c9d2329da56d278 Mon Sep 17 00:00:00 2001 From: Guillaume Seguin Date: Fri, 17 Oct 2014 14:43:25 +0200 Subject: [PATCH 14/23] network/http-launch: Fix segfault Message len was not updated when iterating in on_read_bytes --- network/http-launch/http-launch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 63c575a7c2..44871d3927 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -203,10 +203,11 @@ on_read_bytes (GPollableInputStream * stream, Client * client) return FALSE; } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { guint8 *tmp = client->current_message->data; + guint tmp_len = client->current_message->len; g_clear_error (&err); - while (client->current_message->len > 3) { + while (tmp_len > 3) { if (tmp[0] == 0x0d && tmp[1] == 0x0a && tmp[2] == 0x0d && tmp[3] == 0x0a) { guint len; @@ -215,8 +216,10 @@ on_read_bytes (GPollableInputStream * stream, Client * client) client_message (client, (gchar *) client->current_message->data, len); g_byte_array_remove_range (client->current_message, 0, len); tmp = client->current_message->data; + tmp_len = client->current_message->len; } else { tmp++; + tmp_len--; } } From aaaea24c216f36e7a9d9601c88174dca63ec59f4 Mon Sep 17 00:00:00 2001 From: Victor Sergienko Date: Tue, 27 Jan 2015 16:15:10 +0200 Subject: [PATCH 15/23] network/http-launch: segfault on GStreamer warnings because of wrong parsing function --- network/http-launch/http-launch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 44871d3927..7071b1e1ed 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -306,7 +306,7 @@ on_message (GstBus * bus, GstMessage * message, gpointer user_data) gchar *debug; GError *err; - gst_message_parse_error (message, &err, &debug); + gst_message_parse_warning (message, &err, &debug); g_print ("Warning %s\n", err->message); g_error_free (err); g_free (debug); From 9da0c1427371cb2f7498c986577041b07c3f7ed7 Mon Sep 17 00:00:00 2001 From: Luis de Arquer Date: Sat, 21 Feb 2015 12:33:17 +0000 Subject: [PATCH 16/23] network/http-launch: Add support for MJPEG streams using multipartmux --- network/http-launch/http-launch.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 7071b1e1ed..23519f14bf 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -41,6 +41,7 @@ static GList *clients = NULL; static GstElement *pipeline = NULL; static GstElement *multisocketsink = NULL; static gboolean started = FALSE; +static gchar content_type[256]; static void remove_client (Client * client) @@ -107,7 +108,7 @@ client_message (Client * client, const gchar * data, guint len) http_version = "HTTP/1.0"; if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); + response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, content_type); } else { response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); } @@ -126,7 +127,7 @@ client_message (Client * client, const gchar * data, guint len) http_version = "HTTP/1.0"; if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n" "\r\n", http_version); + response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, content_type); ok = TRUE; } else { response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); @@ -378,6 +379,17 @@ main (gint argc, gchar ** argv) gst_object_unref (bin); return -3; } + + /* + * Make the HTTP header 'Content-type' if we are trying to create a + * MJPEG (or any other multipart) stream. + */ + if ( strcmp ("GstMultipartMux", g_type_name(G_OBJECT_TYPE(stream)) ) == 0 ) { + strcpy (content_type, "Content-Type: " + "multipart/x-mixed-replace;boundary=--ThisRandomString\r\n"); + } else { + strcpy (content_type, ""); + } srcpad = gst_element_get_static_pad (stream, "src"); if (!srcpad) { From bb90ddfad112f6b5178afe40befe76c268aed546 Mon Sep 17 00:00:00 2001 From: Luis de Arquer Date: Sat, 21 Feb 2015 13:04:19 +0000 Subject: [PATCH 17/23] network/http-launch: Content-type boundary tag is now taken from the multipartmux "boundary" property --- network/http-launch/http-launch.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 23519f14bf..c2eea6c968 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -41,7 +41,7 @@ static GList *clients = NULL; static GstElement *pipeline = NULL; static GstElement *multisocketsink = NULL; static gboolean started = FALSE; -static gchar content_type[256]; +static gchar *content_type; static void remove_client (Client * client) @@ -384,13 +384,22 @@ main (gint argc, gchar ** argv) * Make the HTTP header 'Content-type' if we are trying to create a * MJPEG (or any other multipart) stream. */ + content_type = g_strdup (""); + if ( strcmp ("GstMultipartMux", g_type_name(G_OBJECT_TYPE(stream)) ) == 0 ) { - strcpy (content_type, "Content-Type: " - "multipart/x-mixed-replace;boundary=--ThisRandomString\r\n"); - } else { - strcpy (content_type, ""); + gchar *boundary = NULL; + g_object_get (stream, "boundary", &boundary, NULL); + if (boundary == NULL) { + g_print ("Warning: \"boundary\" property not found in mutipartmux\n"); + } else { + /* Free default empty string "" created above, and create new string */ + g_free(content_type); + content_type = g_strdup_printf ("Content-Type: " + "multipart/x-mixed-replace;boundary=--%s\r\n", boundary); + } } + srcpad = gst_element_get_static_pad (stream, "src"); if (!srcpad) { g_print ("no \"src\" pad in element \"stream\" found\n"); From d72375c22d070774704adf6856dc942c17493238 Mon Sep 17 00:00:00 2001 From: Luis de Arquer Date: Sat, 21 Feb 2015 13:49:58 +0000 Subject: [PATCH 18/23] network/http-launch: Minor format changes --- network/http-launch/http-launch.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index c2eea6c968..8732326843 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -108,7 +108,8 @@ client_message (Client * client, const gchar * data, guint len) http_version = "HTTP/1.0"; if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, content_type); + response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, + content_type); } else { response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); } @@ -127,7 +128,8 @@ client_message (Client * client, const gchar * data, guint len) http_version = "HTTP/1.0"; if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, content_type); + response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, + content_type); ok = TRUE; } else { response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); @@ -379,9 +381,9 @@ main (gint argc, gchar ** argv) gst_object_unref (bin); return -3; } - - /* - * Make the HTTP header 'Content-type' if we are trying to create a + + /* + * Make the HTTP header 'Content-type' if we are trying to create a * MJPEG (or any other multipart) stream. */ content_type = g_strdup (""); @@ -395,11 +397,10 @@ main (gint argc, gchar ** argv) /* Free default empty string "" created above, and create new string */ g_free(content_type); content_type = g_strdup_printf ("Content-Type: " - "multipart/x-mixed-replace;boundary=--%s\r\n", boundary); + "multipart/x-mixed-replace;boundary=--%s\r\n", boundary); } } - srcpad = gst_element_get_static_pad (stream, "src"); if (!srcpad) { g_print ("no \"src\" pad in element \"stream\" found\n"); From ebe2883488b39f7311fdb65875f844e752239a7b Mon Sep 17 00:00:00 2001 From: Luis de Arquer Date: Wed, 18 Mar 2015 21:39:26 +0000 Subject: [PATCH 19/23] network/http-launch: Content-type header creation now happens into notify::caps signal handler --- network/http-launch/http-launch.c | 75 +++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 8732326843..38b4400627 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -35,6 +35,12 @@ typedef struct GByteArray *current_message; } Client; +static const char *known_mimetypes[] = { + "video/webm", + "multipart/x-mixed-replace", + NULL +}; + static GMainLoop *loop = NULL; G_LOCK_DEFINE_STATIC (clients); static GList *clients = NULL; @@ -347,6 +353,51 @@ on_client_socket_removed (GstElement * element, GSocket * socket, remove_client (client); } +static void on_stream_caps_changed (GObject *obj, GParamSpec *pspec, + gpointer user_data) +{ + GstPad *src_pad; + GstCaps *src_caps; + GstStructure *gstrc; + + src_pad = (GstPad *) obj; + src_caps = gst_pad_get_current_caps (src_pad); + gstrc = gst_caps_get_structure (src_caps, 0); + + /* + * Include a Content-type header in the case we know the mime + * type is OK in HTTP. Required for MJPEG streams. + */ + int i = 0; + const gchar *mimetype = gst_structure_get_name(gstrc); + while (known_mimetypes[i] != NULL) + { + if (strcmp(mimetype, known_mimetypes[i]) == 0) + { + if (content_type) + g_free(content_type); + + /* Handle the (maybe not so) especial case of multipart to add boundary */ + if (strcmp(mimetype, "multipart/x-mixed-replace") == 0 && + gst_structure_has_field_typed(gstrc, "boundary", G_TYPE_STRING)) + { + const gchar *boundary = gst_structure_get_string(gstrc, "boundary"); + content_type = g_strdup_printf ("Content-Type: " + "multipart/x-mixed-replace;boundary=--%s\r\n", boundary); + } + else + { + content_type = g_strdup_printf ("Content-Type: %s\r\n", mimetype); + } + g_print("%s", content_type); + break; + } + i++; + } + + gst_caps_unref (src_caps); +} + int main (gint argc, gchar ** argv) { @@ -382,25 +433,6 @@ main (gint argc, gchar ** argv) return -3; } - /* - * Make the HTTP header 'Content-type' if we are trying to create a - * MJPEG (or any other multipart) stream. - */ - content_type = g_strdup (""); - - if ( strcmp ("GstMultipartMux", g_type_name(G_OBJECT_TYPE(stream)) ) == 0 ) { - gchar *boundary = NULL; - g_object_get (stream, "boundary", &boundary, NULL); - if (boundary == NULL) { - g_print ("Warning: \"boundary\" property not found in mutipartmux\n"); - } else { - /* Free default empty string "" created above, and create new string */ - g_free(content_type); - content_type = g_strdup_printf ("Content-Type: " - "multipart/x-mixed-replace;boundary=--%s\r\n", boundary); - } - } - srcpad = gst_element_get_static_pad (stream, "src"); if (!srcpad) { g_print ("no \"src\" pad in element \"stream\" found\n"); @@ -409,6 +441,11 @@ main (gint argc, gchar ** argv) return -4; } + content_type = g_strdup (""); + g_signal_connect (srcpad, "notify::caps", + G_CALLBACK (on_stream_caps_changed), + NULL); + ghostpad = gst_ghost_pad_new ("src", srcpad); gst_element_add_pad (GST_ELEMENT (bin), ghostpad); gst_object_unref (srcpad); From 58a2c722d9c833aa2c583572f5a269a027f0fc8a Mon Sep 17 00:00:00 2001 From: luis Date: Tue, 5 May 2015 13:45:49 +0200 Subject: [PATCH 20/23] network/http-launch: Make GList clients consistent (items valid always). --- network/http-launch/http-launch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 38b4400627..2521f3b00c 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -54,6 +54,10 @@ remove_client (Client * client) { g_print ("Removing connection %s\n", client->name); + G_LOCK (clients); + clients = g_list_remove (clients, client); + G_UNLOCK (clients); + g_free (client->name); if (client->isource) { @@ -67,10 +71,6 @@ remove_client (Client * client) g_object_unref (client->connection); g_byte_array_unref (client->current_message); - G_LOCK (clients); - clients = g_list_remove (clients, client); - G_UNLOCK (clients); - g_slice_free (Client, client); } From 911a010532bb74a2e0c3b4638b3a0ad2a413ef45 Mon Sep 17 00:00:00 2001 From: luis Date: Sun, 10 May 2015 14:28:15 +0200 Subject: [PATCH 21/23] network/http-launch: Encapsulate sending http responses in their own function, so they can be easily deferred (future work) --- network/http-launch/http-launch.c | 52 ++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 2521f3b00c..eba4077f67 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -33,6 +33,7 @@ typedef struct GOutputStream *ostream; GSource *isource, *tosource; GByteArray *current_message; + gchar *http_version; } Client; static const char *known_mimetypes[] = { @@ -59,6 +60,7 @@ remove_client (Client * client) G_UNLOCK (clients); g_free (client->name); + g_free (client->http_version); if (client->isource) { g_source_destroy (client->isource); @@ -98,6 +100,25 @@ write_bytes (Client * client, const gchar * data, guint len) } } +static void +send_response_200_ok (Client * client) +{ + gchar *response; + response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", client->http_version, + content_type); + write_bytes (client, response, strlen (response)); + g_free (response); +} + +static void +send_response_404_not_found (Client * client) +{ + gchar *response; + response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", client->http_version); + write_bytes (client, response, strlen (response)); + g_free (response); +} + static void client_message (Client * client, const gchar * data, guint len) { @@ -105,43 +126,37 @@ client_message (Client * client, const gchar * data, guint len) if (g_str_has_prefix (lines[0], "HEAD")) { gchar **parts = g_strsplit (lines[0], " ", -1); - gchar *response; - const gchar *http_version; + + g_free (client->http_version); if (parts[1] && parts[2] && *parts[2] != '\0') - http_version = parts[2]; + client->http_version = g_strdup (parts[2]); else - http_version = "HTTP/1.0"; + client->http_version = g_strdup ("HTTP/1.0"); if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, - content_type); + send_response_200_ok (client); } else { - response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); + send_response_404_not_found (client); } - write_bytes (client, response, strlen (response)); - g_free (response); g_strfreev (parts); } else if (g_str_has_prefix (lines[0], "GET")) { gchar **parts = g_strsplit (lines[0], " ", -1); - gchar *response; - const gchar *http_version; gboolean ok = FALSE; + g_free (client->http_version); + if (parts[1] && parts[2] && *parts[2] != '\0') - http_version = parts[2]; + client->http_version = g_strdup (parts[2]); else - http_version = "HTTP/1.0"; + client->http_version = g_strdup ("HTTP/1.0"); if (parts[1] && strcmp (parts[1], "/") == 0) { - response = g_strdup_printf ("%s 200 OK\r\n%s\r\n", http_version, - content_type); + send_response_200_ok (client); ok = TRUE; } else { - response = g_strdup_printf ("%s 404 Not Found\r\n\r\n", http_version); + send_response_404_not_found (client); } - write_bytes (client, response, strlen (response)); - g_free (response); g_strfreev (parts); if (ok) { @@ -269,6 +284,7 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, g_print ("New connection %s\n", client->name); + client->http_version = g_strdup (""); client->connection = g_object_ref (connection); client->socket = g_socket_connection_get_socket (connection); client->istream = From 82c788e77f286afe4916f5e86d8594b687e679ad Mon Sep 17 00:00:00 2001 From: luis Date: Sun, 10 May 2015 17:53:03 +0200 Subject: [PATCH 22/23] network/http-launch: Defer the "200 OK" response until caps have been resolved, in order to populate the HTTP content-type header, both on a "HEAD" or a "GET" request. --- network/http-launch/http-launch.c | 49 +++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index eba4077f67..0921ce7c4c 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -34,6 +34,7 @@ typedef struct GSource *isource, *tosource; GByteArray *current_message; gchar *http_version; + gboolean waiting_200_ok; } Client; static const char *known_mimetypes[] = { @@ -49,6 +50,8 @@ static GstElement *pipeline = NULL; static GstElement *multisocketsink = NULL; static gboolean started = FALSE; static gchar *content_type; +G_LOCK_DEFINE_STATIC (caps); +static gboolean caps_resolved = FALSE; static void remove_client (Client * client) @@ -126,6 +129,7 @@ client_message (Client * client, const gchar * data, guint len) if (g_str_has_prefix (lines[0], "HEAD")) { gchar **parts = g_strsplit (lines[0], " ", -1); + gboolean ok = FALSE; g_free (client->http_version); @@ -135,11 +139,27 @@ client_message (Client * client, const gchar * data, guint len) client->http_version = g_strdup ("HTTP/1.0"); if (parts[1] && strcmp (parts[1], "/") == 0) { - send_response_200_ok (client); + G_LOCK (caps); + if (caps_resolved) + send_response_200_ok (client); + else + client->waiting_200_ok = TRUE; + G_UNLOCK (caps); + ok = TRUE; } else { send_response_404_not_found (client); } g_strfreev (parts); + + if (ok && !started) { + g_print ("Starting pipeline\n"); + if (gst_element_set_state (pipeline, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to start pipeline\n"); + g_main_loop_quit (loop); + } + started = TRUE; + } } else if (g_str_has_prefix (lines[0], "GET")) { gchar **parts = g_strsplit (lines[0], " ", -1); gboolean ok = FALSE; @@ -152,7 +172,12 @@ client_message (Client * client, const gchar * data, guint len) client->http_version = g_strdup ("HTTP/1.0"); if (parts[1] && strcmp (parts[1], "/") == 0) { - send_response_200_ok (client); + G_LOCK (caps); + if (caps_resolved) + send_response_200_ok (client); + else + client->waiting_200_ok = TRUE; + G_UNLOCK (caps); ok = TRUE; } else { send_response_404_not_found (client); @@ -284,6 +309,7 @@ on_new_connection (GSocketService * service, GSocketConnection * connection, g_print ("New connection %s\n", client->name); + client->waiting_200_ok = FALSE; client->http_version = g_strdup (""); client->connection = g_object_ref (connection); client->socket = g_socket_connection_get_socket (connection); @@ -375,6 +401,7 @@ static void on_stream_caps_changed (GObject *obj, GParamSpec *pspec, GstPad *src_pad; GstCaps *src_caps; GstStructure *gstrc; + GList *l; src_pad = (GstPad *) obj; src_caps = gst_pad_get_current_caps (src_pad); @@ -412,6 +439,24 @@ static void on_stream_caps_changed (GObject *obj, GParamSpec *pspec, } gst_caps_unref (src_caps); + + /* Send 200 OK to those clients waiting for it */ + G_LOCK (caps); + + caps_resolved = TRUE; + + G_LOCK (clients); + for (l = clients; l; l = l->next) { + Client *cl = l->data; + if (cl->waiting_200_ok) { + send_response_200_ok (cl); + cl->waiting_200_ok = FALSE; + break; + } + } + G_UNLOCK (clients); + + G_UNLOCK (caps); } int From fdf0d5814318103bf34aaf80d32184797232e987 Mon Sep 17 00:00:00 2001 From: luis Date: Sun, 10 May 2015 18:11:16 +0200 Subject: [PATCH 23/23] network/http-launch: Reduce duplicated code, using the same code for the HEAD and GET requests. --- network/http-launch/http-launch.c | 59 ++++++++++--------------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/network/http-launch/http-launch.c b/network/http-launch/http-launch.c index 0921ce7c4c..91cf8d0d71 100644 --- a/network/http-launch/http-launch.c +++ b/network/http-launch/http-launch.c @@ -125,42 +125,16 @@ send_response_404_not_found (Client * client) static void client_message (Client * client, const gchar * data, guint len) { + gboolean http_head_request = FALSE; + gboolean http_get_request = FALSE; gchar **lines = g_strsplit_set (data, "\r\n", -1); - if (g_str_has_prefix (lines[0], "HEAD")) { - gchar **parts = g_strsplit (lines[0], " ", -1); - gboolean ok = FALSE; + if (g_str_has_prefix (lines[0], "HEAD")) + http_head_request = TRUE; + else if (g_str_has_prefix (lines[0], "GET")) + http_get_request = TRUE; - g_free (client->http_version); - - if (parts[1] && parts[2] && *parts[2] != '\0') - client->http_version = g_strdup (parts[2]); - else - client->http_version = g_strdup ("HTTP/1.0"); - - if (parts[1] && strcmp (parts[1], "/") == 0) { - G_LOCK (caps); - if (caps_resolved) - send_response_200_ok (client); - else - client->waiting_200_ok = TRUE; - G_UNLOCK (caps); - ok = TRUE; - } else { - send_response_404_not_found (client); - } - g_strfreev (parts); - - if (ok && !started) { - g_print ("Starting pipeline\n"); - if (gst_element_set_state (pipeline, - GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - g_print ("Failed to start pipeline\n"); - g_main_loop_quit (loop); - } - started = TRUE; - } - } else if (g_str_has_prefix (lines[0], "GET")) { + if (http_head_request || http_get_request) { gchar **parts = g_strsplit (lines[0], " ", -1); gboolean ok = FALSE; @@ -185,14 +159,17 @@ client_message (Client * client, const gchar * data, guint len) g_strfreev (parts); if (ok) { - g_source_destroy (client->isource); - g_source_unref (client->isource); - client->isource = NULL; - g_source_destroy (client->tosource); - g_source_unref (client->tosource); - client->tosource = NULL; - g_print ("Starting to stream to %s\n", client->name); - g_signal_emit_by_name (multisocketsink, "add", client->socket); + if (http_get_request) { + /* Start streaming to client socket */ + g_source_destroy (client->isource); + g_source_unref (client->isource); + client->isource = NULL; + g_source_destroy (client->tosource); + g_source_unref (client->tosource); + client->tosource = NULL; + g_print ("Starting to stream to %s\n", client->name); + g_signal_emit_by_name (multisocketsink, "add", client->socket); + } if (!started) { g_print ("Starting pipeline\n");