[vlc-devel] [PATCH 2/2] access/http: Share cookies between all playlist items

Antti Ajanki antti.ajanki at iki.fi
Tue Sep 9 18:58:16 CEST 2014


Shared cookies are required, for example, by certain HDS and HLS streams
that set a cookie when the manifest is read and expect it to be sent
back on subsequent fragment requests.

A cookie jar is created on the playlist when the first HTTP stream is
created. Later HTTP requests inherit the cookie jar from the playlist.
---
 include/vlc_playlist.h       |    2 +
 modules/access/http.c        |  105 ++++++++++++++++++++++++++++++++++++------
 modules/access/httpcookies.c |   73 +++++++++++++++++++++++++----
 modules/access/httpcookies.h |   20 +++++++-
 src/libvlccore.sym           |    1 +
 src/playlist/engine.c        |   28 +++++++++++
 6 files changed, 203 insertions(+), 26 deletions(-)

diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h
index 384221b..c33c601 100644
--- a/include/vlc_playlist.h
+++ b/include/vlc_playlist.h
@@ -387,6 +387,8 @@ static inline int playlist_MuteToggle( playlist_t *pl )
 
 VLC_API void playlist_EnableAudioFilter( playlist_t *, const char *, bool );
 
+VLC_API int vlc_EnsurePlaylistVarExists( libvlc_int_t *, const char *, int, const variable_management_ops_t * );
+
 /***********************************************************************
  * Inline functions
  ***********************************************************************/
diff --git a/modules/access/http.c b/modules/access/http.c
index 9afca9f..d39f148 100644
--- a/modules/access/http.c
+++ b/modules/access/http.c
@@ -47,6 +47,7 @@
 #include <vlc_input.h>
 #include <vlc_md5.h>
 #include <vlc_http.h>
+#include <vlc_playlist.h>
 #include "httpcookies.h"
 
 #ifdef HAVE_ZLIB_H
@@ -179,19 +180,19 @@ struct access_sys_t
     uint64_t i_remaining;
     uint64_t size;
 
+    http_cookie_jar_t * cookies;
+
     bool b_seekable;
     bool b_reconnect;
     bool b_continuous;
     bool b_pace_control;
     bool b_persist;
     bool b_has_size;
-
-    http_cookie_jar_t * cookies;
 };
 
 /* */
-static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
-                            unsigned i_redirect, http_cookie_jar_t *cookies );
+static int OpenRedirected( vlc_object_t *p_this, const char *psz_access,
+                           unsigned i_redirect );
 
 /* */
 static ssize_t Read( access_t *, uint8_t *, size_t );
@@ -210,13 +211,20 @@ static void AuthReply( access_t *p_acces, const char *psz_prefix,
 static int AuthCheckReply( access_t *p_access, const char *psz_header,
                            vlc_url_t *p_url, http_auth_t *p_auth );
 
+static vlc_value_t CookiesInit( void );
+static int CookiesCmp( vlc_value_t, vlc_value_t );
+static void CookiesRetain( vlc_value_t * );
+static void CookiesRelease( vlc_value_t * );
+static http_cookie_jar_t *GetCookieJarReference( vlc_object_t *p_this );
+static void ReleaseCookieJar( vlc_object_t *p_this, access_sys_t *p_sys );
+
 /*****************************************************************************
  * Open:
  *****************************************************************************/
 static int Open( vlc_object_t *p_this )
 {
     access_t *p_access = (access_t*)p_this;
-    return OpenWithCookies( p_this, p_access->psz_access, 5, NULL );
+    return OpenRedirected( p_this, p_access->psz_access, 5 );
 }
 
 /**
@@ -225,11 +233,10 @@ static int Open( vlc_object_t *p_this )
  * @psz_access: the acces to use (http, https, ...) (this value must be used
  *              instead of p_access->psz_access)
  * @i_redirect: number of redirections remaining
- * @cookies: the available cookies
  * @return vlc error codes
  */
-static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
-                            unsigned i_redirect, http_cookie_jar_t *cookies )
+static int OpenRedirected( vlc_object_t *p_this, const char *psz_access,
+                           unsigned i_redirect )
 {
     access_t     *p_access = (access_t*)p_this;
     access_sys_t *p_sys;
@@ -279,7 +286,7 @@ static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
 
     /* Only forward an store cookies if the corresponding option is activated */
     if( var_CreateGetBool( p_access, "http-forward-cookies" ) )
-        p_sys->cookies = (cookies != NULL) ? cookies : http_cookies_new();
+        p_sys->cookies = GetCookieJarReference( p_this );
     else
         p_sys->cookies = NULL;
 
@@ -510,15 +517,16 @@ connect:
 
         Disconnect( p_access );
         vlc_tls_Delete( p_sys->p_creds );
-        cookies = p_sys->cookies;
+        if ( p_sys->cookies )
+            http_cookies_release( p_sys->cookies );
+
 #ifdef HAVE_ZLIB_H
         inflateEnd( &p_sys->inflate.stream );
 #endif
         free( p_sys );
 
         /* Do new Open() run with new data */
-        return OpenWithCookies( p_this, psz_protocol, i_redirect - 1,
-                                cookies );
+        return OpenRedirected( p_this, psz_protocol, i_redirect - 1 );
     }
 
     if( p_sys->b_mms )
@@ -597,7 +605,7 @@ error:
     Disconnect( p_access );
     vlc_tls_Delete( p_sys->p_creds );
 
-    http_cookies_destroy( p_sys->cookies );
+    ReleaseCookieJar( p_this, p_sys );
 
 #ifdef HAVE_ZLIB_H
     inflateEnd( &p_sys->inflate.stream );
@@ -633,7 +641,7 @@ static void Close( vlc_object_t *p_this )
     Disconnect( p_access );
     vlc_tls_Delete( p_sys->p_creds );
 
-    http_cookies_destroy( p_sys->cookies );
+    ReleaseCookieJar( p_this, p_sys );
 
 #ifdef HAVE_ZLIB_H
     inflateEnd( &p_sys->inflate.stream );
@@ -1583,3 +1591,72 @@ static int AuthCheckReply( access_t *p_access, const char *psz_header,
                                                  p_url->psz_username,
                                                  p_url->psz_password );
 }
+
+/*****************************************************************************
+ * Cookies
+ *****************************************************************************/
+
+static vlc_value_t CookiesInit()
+{
+    vlc_value_t val;
+    val.p_address = http_cookies_new();
+    return val;
+}
+
+static int CookiesCmp( vlc_value_t a, vlc_value_t b )
+{
+    return a.p_address == b.p_address ? 0 : a.p_address > b.p_address ? 1 : -1;
+}
+
+static void CookiesRetain( vlc_value_t *p )
+{
+    http_cookie_jar_t *cookies = (http_cookie_jar_t *) p->p_address;
+    if ( cookies )
+        http_cookies_retain( cookies );
+}
+
+static void CookiesRelease( vlc_value_t *p )
+{
+    http_cookie_jar_t *cookies = (http_cookie_jar_t *) p->p_address;
+    if ( cookies )
+        http_cookies_release( cookies );
+}
+
+/**
+ * Get a cookie jar reference from playlist, or create it on the first call
+ *
+ * The cookie jar is cached as local variable on p_this. The locally
+ * cached instance is returned if this http object is reused
+ * (e.g. HTTP redirects).
+ *
+ * The returned pointer must be disposed of by calling
+ * http_cookies_release.
+ *
+ * @param p_this: http access object
+ * @return A new reference to a http_cookie_jar_t
+ */
+static http_cookie_jar_t *GetCookieJarReference( vlc_object_t *p_this )
+{
+    vlc_value_t val;
+    variable_management_ops_t ops =
+        { CookiesInit, CookiesCmp, CookiesRetain, CookiesRelease };
+
+    if ( var_GetChecked( p_this, "http-cookies", VLC_VAR_ADDRESS, &val ) == VLC_SUCCESS )
+        /* First check if cookies exists as a local variable */
+        return (http_cookie_jar_t *) val.p_address;
+    else if ( vlc_EnsurePlaylistVarExists( p_this->p_libvlc, "http-cookies", VLC_VAR_ADDRESS, &ops ) == VLC_SUCCESS )
+        /* Inherit from the playlist */
+        return var_CreateGetAddress( p_this, "http-cookies" );
+    else
+        /* Error */
+        return NULL;
+}
+
+static void ReleaseCookieJar( vlc_object_t *p_this, access_sys_t *p_sys )
+{
+    var_Destroy( p_this, "http-cookies" );
+    if ( p_sys->cookies ) {
+        http_cookies_release( p_sys->cookies );
+        p_sys->cookies = NULL;
+    }
+}
diff --git a/modules/access/httpcookies.c b/modules/access/httpcookies.c
index 2dc0a6b..fffd602 100644
--- a/modules/access/httpcookies.c
+++ b/modules/access/httpcookies.c
@@ -45,6 +45,14 @@ typedef struct http_cookie_t
     bool b_secure;
 } http_cookie_t;
 
+struct http_cookie_jar_t
+{
+    vlc_array_t cookies;
+    vlc_mutex_t lock;
+    unsigned int i_ref_count;
+};
+
+static void http_cookies_destroy( http_cookie_jar_t * p_jar );
 static http_cookie_t * cookie_parse( const char * cookie_header, const vlc_url_t * url );
 static void cookie_destroy( http_cookie_t * p_cookie );
 static char * cookie_get_content( const char * cookie );
@@ -60,7 +68,40 @@ static char * cookie_default_path( const char *request_path );
 
 http_cookie_jar_t * http_cookies_new()
 {
-    return vlc_array_new();
+    http_cookie_jar_t * jar = malloc( sizeof( http_cookie_jar_t ) );
+    if ( !jar )
+        return NULL;
+
+    vlc_array_init( &jar->cookies );
+    vlc_mutex_init( &jar->lock );
+    jar->i_ref_count = 1;
+
+    return jar;
+}
+void http_cookies_retain( http_cookie_jar_t * p_jar )
+{
+    assert( p_jar );
+    assert( p_jar->i_ref_count < UINT_MAX );
+
+    vlc_mutex_lock( &p_jar->lock );
+    p_jar->i_ref_count++;
+    vlc_mutex_unlock( &p_jar->lock );
+}
+
+void http_cookies_release( http_cookie_jar_t * p_jar )
+{
+    unsigned int refs;
+
+    assert( p_jar );
+    assert( p_jar->i_ref_count > 0 );
+
+    vlc_mutex_lock( &p_jar->lock );
+    p_jar->i_ref_count--;
+    refs = p_jar->i_ref_count;
+    vlc_mutex_unlock( &p_jar->lock );
+
+    if ( refs == 0 )
+        http_cookies_destroy( p_jar );
 }
 
 void http_cookies_destroy( http_cookie_jar_t * p_jar )
@@ -69,9 +110,11 @@ void http_cookies_destroy( http_cookie_jar_t * p_jar )
         return;
 
     int i;
-    for( i = 0; i < vlc_array_count( p_jar ); i++ )
-        cookie_destroy( vlc_array_item_at_index( p_jar, i ) );
-    vlc_array_destroy( p_jar );
+    for( i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
+        cookie_destroy( vlc_array_item_at_index( &p_jar->cookies, i ) );
+
+    vlc_array_clear( &p_jar->cookies );
+    vlc_mutex_destroy( &p_jar->lock );
 }
 
 bool http_cookies_append( http_cookie_jar_t * p_jar, const char * psz_cookie_header, const vlc_url_t *p_url )
@@ -85,9 +128,11 @@ bool http_cookies_append( http_cookie_jar_t * p_jar, const char * psz_cookie_hea
         return false;
     }
 
-    for( i = 0; i < vlc_array_count( p_jar ); i++ )
+    vlc_mutex_lock( &p_jar->lock );
+
+    for( i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
     {
-        http_cookie_t *iter = vlc_array_item_at_index( p_jar, i );
+        http_cookie_t *iter = vlc_array_item_at_index( &p_jar->cookies, i );
 
         assert( iter->psz_name );
         assert( iter->psz_domain );
@@ -100,12 +145,14 @@ bool http_cookies_append( http_cookie_jar_t * p_jar, const char * psz_cookie_hea
         if( domains_match && paths_match && names_match )
         {
             /* Remove previous value for this cookie */
-            vlc_array_remove( p_jar, i );
+            vlc_array_remove( &p_jar->cookies, i );
             cookie_destroy(iter);
             break;
         }
     }
-    vlc_array_append( p_jar, cookie );
+    vlc_array_append( &p_jar->cookies, cookie );
+
+    vlc_mutex_unlock( &p_jar->lock );
 
     return true;
 }
@@ -115,9 +162,12 @@ char *http_cookies_for_url( http_cookie_jar_t * p_jar, const vlc_url_t * p_url )
 {
     int i;
     char *psz_cookiebuf = NULL;
-    for( i = 0; i < vlc_array_count( p_jar ); i++ )
+
+    vlc_mutex_lock( &p_jar->lock );
+
+    for( i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
     {
-        const http_cookie_t * cookie = vlc_array_item_at_index( p_jar, i );
+        const http_cookie_t * cookie = vlc_array_item_at_index( &p_jar->cookies, i );
         if ( cookie_should_be_sent( cookie, p_url ) )
         {
             char *psz_updated_buf = NULL;
@@ -129,6 +179,7 @@ char *http_cookies_for_url( http_cookie_jar_t * p_jar, const vlc_url_t * p_url )
             {
                 // TODO: report error
                 free( psz_cookiebuf );
+                vlc_mutex_unlock( &p_jar->lock );
                 return NULL;
             }
             free( psz_cookiebuf );
@@ -136,6 +187,8 @@ char *http_cookies_for_url( http_cookie_jar_t * p_jar, const vlc_url_t * p_url )
         }
     }
 
+    vlc_mutex_unlock( &p_jar->lock );
+
     return psz_cookiebuf;
 }
 
diff --git a/modules/access/httpcookies.h b/modules/access/httpcookies.h
index bf73191..55f8e7d 100644
--- a/modules/access/httpcookies.h
+++ b/modules/access/httpcookies.h
@@ -31,10 +31,26 @@ extern "C" {
 #include <vlc_url.h>
 #include <vlc_arrays.h>
 
-typedef struct vlc_array_t http_cookie_jar_t;
+typedef struct http_cookie_jar_t http_cookie_jar_t;
 
+/**
+ * Create and initializes a new HTTP cookie jar.
+ *
+ * The initial reference count is 1.
+ *
+ * @return the cookie jar instance or NULL in case of error
+ */
 http_cookie_jar_t * http_cookies_new( void );
-void http_cookies_destroy( http_cookie_jar_t * p_jar );
+
+/**
+ * Increase reference count of a cookie jar.
+ */
+void http_cookies_retain( http_cookie_jar_t * p_jar );
+
+/**
+ * Decrease reference count of a cookie jar and release it if necessary.
+ */
+void http_cookies_release( http_cookie_jar_t * p_jar );
 
 /**
  * Parse a value of an incoming Set-Cookie header and append the
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index bdd7b06..25a114a 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -597,6 +597,7 @@ vlc_epg_SetCurrent
 vlc_epg_Merge
 vlc_gl_Create
 vlc_gl_Destroy
+vlc_EnsurePlaylistVarExists
 vlm_Control
 vlm_Delete
 vlm_ExecuteCommand
diff --git a/src/playlist/engine.c b/src/playlist/engine.c
index de1f76b..5444c14 100644
--- a/src/playlist/engine.c
+++ b/src/playlist/engine.c
@@ -382,6 +382,34 @@ input_thread_t * playlist_CurrentInput( playlist_t * p_playlist )
 }
 
 /**
+ * Create a var on the playlist of a libvlc instance.
+ *
+ * Does nothing if the variable already exists.
+ */
+int vlc_EnsurePlaylistVarExists( libvlc_int_t *p_libvlc, const char *psz_name, int i_type, const variable_management_ops_t *p_ops )
+{
+    playlist_t *p_playlist;
+    int res;
+
+    p_playlist = libvlc_priv(p_libvlc)->playlist;
+    if ( p_playlist )
+    {
+        PL_LOCK;
+        if ( var_Type( p_playlist, psz_name ) == 0 )
+            res = var_CreateOps( p_playlist, psz_name, i_type, p_ops );
+        else
+            res = VLC_SUCCESS;
+        PL_UNLOCK;
+    }
+    else
+    {
+        res = VLC_ENOOBJ;
+    }
+
+    return res;
+}
+
+/**
  * @}
  */
 
-- 
1.7.10.4




More information about the vlc-devel mailing list