diff options
author | Sebastian Dröge <sebastian@centricular.com> | 2017-03-16 13:58:41 +0200 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2017-05-09 14:27:00 +0200 |
commit | 7cb70e7aeaf5fafadb07e0f30ca7b4ccfd85f695 (patch) | |
tree | 3ad956b0c2c6cf46b4cae3dff731a1205310b556 /ext | |
parent | 55f949676e4f284ead222e61362e1e12e071c819 (diff) |
souphttpsrc: Implement soup session sharing
souphttpsrc now shares its SoupSession with other elements in the
pipeline via GstContext if possible (session-wide settings are all the
defaults), or if the context was forced by the application.
This allows multiple souphttpsrcs to reuse connections, cookies, etc.
https://bugzilla.gnome.org/show_bug.cgi?id=780140
Diffstat (limited to 'ext')
-rw-r--r-- | ext/soup/gstsouphttpsrc.c | 190 | ||||
-rw-r--r-- | ext/soup/gstsouphttpsrc.h | 2 |
2 files changed, 161 insertions, 31 deletions
diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 2d08e135f..986c4c09c 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -86,6 +86,8 @@ GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug); #define GST_CAT_DEFAULT souphttpsrc_debug +#define GST_SOUP_SESSION_CONTEXT "gst.soup.session" + static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -123,7 +125,7 @@ enum #define DEFAULT_IRADIO_MODE TRUE #define DEFAULT_SOUP_LOG_LEVEL SOUP_LOGGER_LOG_HEADERS #define DEFAULT_COMPRESS FALSE -#define DEFAULT_KEEP_ALIVE FALSE +#define DEFAULT_KEEP_ALIVE TRUE #define DEFAULT_SSL_STRICT TRUE #define DEFAULT_SSL_CA_FILE NULL #define DEFAULT_SSL_USE_SYSTEM_CA_FILE TRUE @@ -152,6 +154,8 @@ static void gst_soup_http_src_get_property (GObject * object, guint prop_id, static GstStateChangeReturn gst_soup_http_src_change_state (GstElement * element, GstStateChange transition); +static void gst_soup_http_src_set_context (GstElement * element, + GstContext * context); static GstFlowReturn gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf); static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc); @@ -413,6 +417,8 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass) "Wouter Cloetens <wouter@mind.be>"); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_soup_http_src_change_state); + gstelement_class->set_context = + GST_DEBUG_FUNCPTR (gst_soup_http_src_set_context); gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_src_start); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_src_stop); @@ -481,9 +487,13 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src) src->cookies = NULL; src->iradio_mode = DEFAULT_IRADIO_MODE; src->session = NULL; + src->external_session = NULL; + src->forced_external_session = FALSE; src->msg = NULL; src->timeout = DEFAULT_TIMEOUT; src->log_level = DEFAULT_SOUP_LOG_LEVEL; + src->compress = DEFAULT_COMPRESS; + src->keep_alive = DEFAULT_KEEP_ALIVE; src->ssl_strict = DEFAULT_SSL_STRICT; src->ssl_use_system_ca_file = DEFAULT_SSL_USE_SYSTEM_CA_FILE; src->tls_database = DEFAULT_TLS_DATABASE; @@ -512,6 +522,11 @@ gst_soup_http_src_dispose (GObject * gobject) gst_soup_http_src_session_close (src); + if (src->external_session) { + g_object_unref (src->external_session); + src->external_session = NULL; + } + G_OBJECT_CLASS (parent_class)->dispose (gobject); } @@ -898,25 +913,84 @@ gst_soup_http_src_session_open (GstSoupHTTPSrc * src) } if (!src->session) { - GST_DEBUG_OBJECT (src, "Creating session"); - if (src->proxy == NULL) { - src->session = - soup_session_new_with_options (SOUP_SESSION_USER_AGENT, - src->user_agent, SOUP_SESSION_TIMEOUT, src->timeout, - SOUP_SESSION_SSL_STRICT, src->ssl_strict, - SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL); + GstQuery *query; + gboolean can_share = (src->timeout == DEFAULT_TIMEOUT) + && (src->ssl_strict == DEFAULT_SSL_STRICT) + && (src->tls_interaction == NULL) && (src->proxy == NULL) + && (src->tls_database == DEFAULT_TLS_DATABASE) + && (src->ssl_ca_file == DEFAULT_SSL_CA_FILE) + && (src->ssl_use_system_ca_file == DEFAULT_SSL_USE_SYSTEM_CA_FILE); + + query = gst_query_new_context (GST_SOUP_SESSION_CONTEXT); + if (gst_pad_peer_query (GST_BASE_SRC_PAD (src), query)) { + GstContext *context; + + gst_query_parse_context (query, &context); + gst_element_set_context (GST_ELEMENT_CAST (src), context); } else { - src->session = - soup_session_new_with_options (SOUP_SESSION_PROXY_URI, src->proxy, - SOUP_SESSION_TIMEOUT, src->timeout, - SOUP_SESSION_SSL_STRICT, src->ssl_strict, - SOUP_SESSION_USER_AGENT, src->user_agent, - SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL); + GstMessage *message; + + message = + gst_message_new_need_context (GST_OBJECT_CAST (src), + GST_SOUP_SESSION_CONTEXT); + gst_element_post_message (GST_ELEMENT_CAST (src), message); + } + gst_query_unref (query); + + if (src->external_session && (can_share || src->forced_external_session)) { + GST_DEBUG_OBJECT (src, "Using external session %p", + src->external_session); + src->session = g_object_ref (src->external_session); + } else { + GST_DEBUG_OBJECT (src, "Creating session (can share %d)", can_share); + + /* We explicitly set User-Agent to NULL here and overwrite it per message + * to be able to have the same session with different User-Agents per + * source */ + if (src->proxy == NULL) { + src->session = + soup_session_new_with_options (SOUP_SESSION_USER_AGENT, + NULL, SOUP_SESSION_TIMEOUT, src->timeout, + SOUP_SESSION_SSL_STRICT, src->ssl_strict, + SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL); + } else { + src->session = + soup_session_new_with_options (SOUP_SESSION_PROXY_URI, src->proxy, + SOUP_SESSION_TIMEOUT, src->timeout, + SOUP_SESSION_SSL_STRICT, src->ssl_strict, + SOUP_SESSION_USER_AGENT, NULL, + SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL); + } + + if (src->session) { + gst_soup_util_log_setup (src->session, src->log_level, + GST_ELEMENT (src)); + soup_session_add_feature_by_type (src->session, + SOUP_TYPE_CONTENT_DECODER); + + if (can_share) { + GstContext *context; + GstMessage *message; + GstStructure *s; + + GST_DEBUG_OBJECT (src, "Sharing session %p", src->session); + + context = gst_context_new (GST_SOUP_SESSION_CONTEXT, TRUE); + s = gst_context_writable_structure (context); + gst_structure_set (s, "session", SOUP_TYPE_SESSION, src->session, + "force", G_TYPE_BOOLEAN, FALSE, NULL); + + gst_element_set_context (GST_ELEMENT_CAST (src), context); + message = + gst_message_new_have_context (GST_OBJECT_CAST (src), context); + gst_element_post_message (GST_ELEMENT_CAST (src), message); + } + } } if (!src->session) { GST_ELEMENT_ERROR (src, LIBRARY, INIT, - (NULL), ("Failed to create async session")); + (NULL), ("Failed to create session")); return FALSE; } @@ -924,24 +998,19 @@ gst_soup_http_src_session_open (GstSoupHTTPSrc * src) G_CALLBACK (gst_soup_http_src_authenticate_cb), src); /* Set up logging */ - gst_soup_util_log_setup (src->session, src->log_level, GST_ELEMENT (src)); - if (src->tls_database) - g_object_set (src->session, "tls-database", src->tls_database, NULL); - else if (src->ssl_ca_file) - g_object_set (src->session, "ssl-ca-file", src->ssl_ca_file, NULL); - else - g_object_set (src->session, "ssl-use-system-ca-file", - src->ssl_use_system_ca_file, NULL); + if (src->session != src->external_session) { + if (src->tls_database) + g_object_set (src->session, "tls-database", src->tls_database, NULL); + else if (src->ssl_ca_file) + g_object_set (src->session, "ssl-ca-file", src->ssl_ca_file, NULL); + else + g_object_set (src->session, "ssl-use-system-ca-file", + src->ssl_use_system_ca_file, NULL); + } } else { GST_DEBUG_OBJECT (src, "Re-using session"); } - if (src->compress) - soup_session_add_feature_by_type (src->session, SOUP_TYPE_CONTENT_DECODER); - else - soup_session_remove_feature_by_type (src->session, - SOUP_TYPE_CONTENT_DECODER); - return TRUE; } @@ -958,10 +1027,14 @@ gst_soup_http_src_session_close (GstSoupHTTPSrc * src) } if (src->session) { - soup_session_abort (src->session); + if (src->session != src->external_session) + soup_session_abort (src->session); + g_signal_handlers_disconnect_by_func (src->session, + G_CALLBACK (gst_soup_http_src_authenticate_cb), src); g_object_unref (src->session); src->session = NULL; } + g_mutex_unlock (&src->mutex); } @@ -969,6 +1042,10 @@ static void gst_soup_http_src_authenticate_cb (SoupSession * session, SoupMessage * msg, SoupAuth * auth, gboolean retrying, GstSoupHTTPSrc * src) { + /* Might be from another user of the shared session */ + if (!GST_IS_SOUP_HTTP_SRC (src) || msg != src->msg) + return; + if (!retrying) { /* First time authentication only, if we fail and are called again with retry true fall through */ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { @@ -1362,6 +1439,29 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method) ("Error parsing URL."), ("URL: %s", src->location)); return FALSE; } + + /* Duplicating the defaults of libsoup here. We don't want to set a + * User-Agent in the session as each source might have its own User-Agent + * set */ + if (!src->user_agent || !*src->user_agent) { + gchar *user_agent = + g_strdup_printf ("libsoup/%u.%u.%u", soup_get_major_version (), + soup_get_minor_version (), soup_get_micro_version ()); + soup_message_headers_append (src->msg->request_headers, "User-Agent", + user_agent); + g_free (user_agent); + } else if (g_str_has_suffix (src->user_agent, " ")) { + gchar *user_agent = g_strdup_printf ("%slibsoup/%u.%u.%u", src->user_agent, + soup_get_major_version (), + soup_get_minor_version (), soup_get_micro_version ()); + soup_message_headers_append (src->msg->request_headers, "User-Agent", + user_agent); + g_free (user_agent); + } else { + soup_message_headers_append (src->msg->request_headers, "User-Agent", + src->user_agent); + } + if (!src->keep_alive) { soup_message_headers_append (src->msg->request_headers, "Connection", "close"); @@ -1379,6 +1479,9 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method) } } + if (!src->compress) + soup_message_disable_feature (src->msg, SOUP_TYPE_CONTENT_DECODER); + soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS | (src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT)); @@ -1724,7 +1827,7 @@ gst_soup_http_src_stop (GstBaseSrc * bsrc) src = GST_SOUP_HTTP_SRC (bsrc); GST_DEBUG_OBJECT (src, "stop()"); - if (src->keep_alive && !src->msg) + if (src->keep_alive && !src->msg && src->session != src->external_session) gst_soup_http_src_cancel_message (src); else gst_soup_http_src_session_close (src); @@ -1754,6 +1857,31 @@ gst_soup_http_src_change_state (GstElement * element, GstStateChange transition) return ret; } +static void +gst_soup_http_src_set_context (GstElement * element, GstContext * context) +{ + GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (element); + + if (g_strcmp0 (gst_context_get_context_type (context), + GST_SOUP_SESSION_CONTEXT) == 0) { + const GstStructure *s = gst_context_get_structure (context); + + if (src->external_session) + g_object_unref (src->external_session); + src->external_session = NULL; + gst_structure_get (s, "session", SOUP_TYPE_SESSION, &src->external_session, + NULL); + src->forced_external_session = FALSE; + gst_structure_get (s, "force", G_TYPE_BOOLEAN, + &src->forced_external_session, NULL); + + GST_DEBUG_OBJECT (src, "Setting external session %p (force: %d)", + src->external_session, src->forced_external_session); + } + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + /* Interrupt a blocking request. */ static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc) diff --git a/ext/soup/gstsouphttpsrc.h b/ext/soup/gstsouphttpsrc.h index f140f80c5..6bbf74ff8 100644 --- a/ext/soup/gstsouphttpsrc.h +++ b/ext/soup/gstsouphttpsrc.h @@ -60,6 +60,8 @@ struct _GstSoupHTTPSrc { gchar *proxy_pw; /* Authentication user password for proxy URI. */ gchar **cookies; /* HTTP request cookies. */ SoupSession *session; /* Async context. */ + SoupSession *external_session; /* Shared via GstContext */ + gboolean forced_external_session; /* If session was explicitly set from application */ SoupMessage *msg; /* Request message. */ gint retry_count; /* Number of retries since we received data */ gint max_retries; /* Maximum number of retries */ |