UrlResolverPlugin implementation
authorDuncan Mac-Vicar P <dmacvicar@suse.de>
Wed, 6 Oct 2010 15:38:00 +0000 (17:38 +0200)
committerDuncan Mac-Vicar P <dmacvicar@suse.de>
Wed, 6 Oct 2010 15:38:34 +0000 (17:38 +0200)
Documentation can be found here: http://en.opensuse.org/openSUSE:Libzypp_plugins

15 files changed:
tests/zypp/RepoManager_test.cc
tests/zypp/data/RepoManager/plugin-service-lib-1/services/service [moved from tests/zypp/data/RepoManager/plugin-service-lib-1/service with 100% similarity]
tests/zypp/data/RepoManager/plugin-service-lib-2/services/service [moved from tests/zypp/data/RepoManager/plugin-service-lib-2/service with 100% similarity]
zypp/CMakeLists.txt
zypp/RepoManager.cc
zypp/RepoManager.h
zypp/ZConfig.cc
zypp/ZConfig.h
zypp/media/MediaAccess.cc
zypp/media/MediaCurl.cc
zypp/media/MediaCurl.h
zypp/media/TransferSettings.cc
zypp/media/TransferSettings.h
zypp/media/UrlResolverPlugin.cc [new file with mode: 0644]
zypp/media/UrlResolverPlugin.h [new file with mode: 0644]

index 06cc948..496d629 100644 (file)
@@ -67,11 +67,11 @@ BOOST_AUTO_TEST_CASE(pluginservices_test)
   TmpDir tmpCachePath;
   RepoManagerOptions opts( RepoManagerOptions::makeTestSetup( tmpCachePath ) ) ;
 
-  filesystem::mkdir( opts.knownReposPath );
-  filesystem::mkdir( opts.servicePluginsPath );
+  filesystem::assert_dir( opts.knownReposPath );
+  filesystem::assert_dir( opts.pluginsPath / "services");
 
-  opts.servicePluginsPath = DATADIR + "/plugin-service-lib-1";
-  BOOST_CHECK(PathInfo(opts.servicePluginsPath / "service").isExist());
+  opts.pluginsPath = DATADIR + "/plugin-service-lib-1";
+  BOOST_CHECK(PathInfo(opts.pluginsPath / "services/service").isExist());
 
   {
     RepoManager manager(opts);
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(pluginservices_test)
 
     ServiceInfo service(*manager.serviceBegin());
     BOOST_CHECK_EQUAL("service", service.alias());
-    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-1/service", service.url().asString());
+    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-1/services/service", service.url().asString());
 
     // now refresh the service
     manager.refreshServices();
@@ -92,14 +92,14 @@ BOOST_AUTO_TEST_CASE(pluginservices_test)
   }
   
   // Now simulate the service changed
-  opts.servicePluginsPath = DATADIR + "/plugin-service-lib-2";
+  opts.pluginsPath = DATADIR + "/plugin-service-lib-2";
   {
     RepoManager manager(opts);
     BOOST_REQUIRE_EQUAL(1, manager.serviceSize());
 
     ServiceInfo service(*manager.serviceBegin());
     BOOST_CHECK_EQUAL("service", service.alias());
-    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-2/service", service.url().asString());
+    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-2/services/service", service.url().asString());
     // now refresh the service
     manager.refreshServices();
     BOOST_CHECK_EQUAL((unsigned) 1, manager.repoSize());
index 0b8519a..c2b6463 100644 (file)
@@ -286,6 +286,7 @@ SET( zypp_media_SRCS
   media/MetaLinkParser.cc
   media/ZsyncParser.cc
   media/MediaBlockList.cc
+  media/UrlResolverPlugin.cc
 )
 
 SET( zypp_media_HEADERS
@@ -315,6 +316,7 @@ SET( zypp_media_HEADERS
   media/MetaLinkParser.h
   media/ZsyncParser.h
   media/MediaBlockList.h
+  media/UrlResolverPlugin.h
 )
 
 INSTALL(  FILES
index 0acb22d..443c917 100644 (file)
@@ -145,7 +145,7 @@ namespace zypp
     repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
     knownReposPath        = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
     knownServicesPath     = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
-    servicePluginsPath     = Pathname::assertprefix( root_r, ZConfig::instance().servicePluginsPath() );
+    pluginsPath           = Pathname::assertprefix( root_r, ZConfig::instance().pluginsPath() );
     probe                 = ZConfig::instance().repo_add_probe();
 
     rootDir = root_r;
@@ -160,7 +160,7 @@ namespace zypp
     ret.repoPackagesCachePath = root_r/"packages";
     ret.knownReposPath        = root_r/"repos.d";
     ret.knownServicesPath     = root_r/"services.d";
-    ret.servicePluginsPath     = root_r/"plugin-services";
+    ret.pluginsPath     = root_r/"plugins";
     ret.rootDir = root_r;
     return ret;
   }
@@ -544,7 +544,7 @@ namespace zypp
       }
     }
 
-    repo::PluginServices(options.servicePluginsPath, ServiceCollector(services));    
+    repo::PluginServices(options.pluginsPath/"services", ServiceCollector(services));    
   }
 
   void RepoManager::Impl::init_knownRepositories()
index 5901b04..5d4119f 100644 (file)
@@ -81,7 +81,7 @@ namespace zypp
     Pathname repoPackagesCachePath;
     Pathname knownReposPath;
     Pathname knownServicesPath;
-    Pathname servicePluginsPath;
+    Pathname pluginsPath;
     bool probe;
     /**
      * Target distro ID to be used when refreshing repo index services.
index e260757..42c44d1 100644 (file)
@@ -828,12 +828,6 @@ namespace zypp
   Pathname ZConfig::pluginsPath() const
   { return _pimpl->pluginsPath.get(); }
 
-  Pathname ZConfig::mediaPluginsPath() const
-  { return pluginsPath() / "media"; }
-
-  Pathname ZConfig::servicePluginsPath() const
-  { return pluginsPath() / "services"; }
-
   ///////////////////////////////////////////////////////////////////
 
   std::ostream & ZConfig::about( std::ostream & str ) const
index 3b03aa1..2d6345b 100644 (file)
@@ -383,15 +383,6 @@ namespace zypp
        */
       Pathname pluginsPath() const;
 
-      /**
-       * Defaults to \ref pluginsPath()/media
-       */
-      Pathname mediaPluginsPath() const;
-
-       /**
-       * Defaults to \ref pluginsPath()/services
-       */
-       Pathname servicePluginsPath() const;
       //@}
     public:
       class Impl;
index fecef40..ff5c96f 100644 (file)
 #include <ctype.h>
 
 #include <iostream>
+#include <map>
 
 #include "zypp/base/Logger.h"
+#include "zypp/ZConfig.h"
+#include "zypp/PluginScript.h"
 #include "zypp/ExternalProgram.h"
 
 #include "zypp/media/MediaException.h"
@@ -31,6 +34,7 @@
 #include "zypp/media/MediaMultiCurl.h"
 #include "zypp/media/MediaISO.h"
 #include "zypp/media/MediaPlugin.h"
+#include "zypp/media/UrlResolverPlugin.h"
 
 using namespace std;
 
@@ -98,18 +102,21 @@ MediaAccess::dependsOnParent(MediaAccessId parentId,
 
 // open URL
 void
-MediaAccess::open (const Url& url, const Pathname & preferred_attach_point)
+MediaAccess::open (const Url& o_url, const Pathname & preferred_attach_point)
 {
-    if(!url.isValid()) {
+    if(!o_url.isValid()) {
        MIL << "Url is not valid" << endl;
-        ZYPP_THROW(MediaBadUrlException(url));
+        ZYPP_THROW(MediaBadUrlException(o_url));
     }
 
     close();
 
+    UrlResolverPlugin::HeaderList custom_headers;
+    Url url = UrlResolverPlugin::resolveUrl(o_url, custom_headers);
+    
     std::string scheme = url.getScheme();
-
     MIL << "Trying scheme '" << scheme << "'" << endl;
+
     /*
     ** WARNING: Don't forget to update MediaAccess::downloads(url)
     **          if you are adding a new url scheme / handler!
@@ -163,12 +170,24 @@ MediaAccess::open (const Url& url, const Pathname & preferred_attach_point)
             use_aria = false;
         }
 
+        MediaCurl *curl;        
+
         if ( use_aria )
-            _handler = new MediaAria2c (url,preferred_attach_point);
-        else if ( use_multicurl )
-            _handler = new MediaMultiCurl (url,preferred_attach_point);
+            curl = new MediaAria2c (url,preferred_attach_point);        
+        else if ( use_multicurl )                     
+            curl = new MediaMultiCurl (url,preferred_attach_point); 
        else
-            _handler = new MediaCurl (url,preferred_attach_point);
+            curl = new MediaCurl (url,preferred_attach_point);
+        
+        UrlResolverPlugin::HeaderList::const_iterator it;
+        for (it = custom_headers.begin();
+             it != custom_headers.end();
+             ++it) {
+            std::string header = it->first + ": " + it->second;            
+            MIL << "Added custom header -> " << header << endl;
+            curl->settings().addHeader(header);
+        }
+        _handler = curl;        
     }
     else if (scheme == "plugin" )
        _handler = new MediaPlugin (url,preferred_attach_point);
index f7cce4e..04bd9a6 100644 (file)
@@ -313,6 +313,11 @@ void fillSettingsFromUrl( const Url &url, TransferSettings &s )
        }
         s.setAuthType(use_auth);
     }
+
+    // workarounds
+    std::string head_requests( url.getQueryParam("head_requests"));
+    if( !head_requests.empty() && head_requests == "no")
+        s.setHeadRequestsAllowed(false);
 }
 
 /**
@@ -436,6 +441,12 @@ MediaCurl::MediaCurl( const Url &      url_r,
   }
 }
 
+TransferSettings & MediaCurl::settings()
+{
+    return _settings;    
+}
+      
+
 void MediaCurl::setCookieFile( const Pathname &fileName )
 {
   _cookieFile = fileName;
@@ -490,13 +501,14 @@ void MediaCurl::setupEasy()
   SET_OPTION(CURLOPT_FAILONERROR, 1L);
   SET_OPTION(CURLOPT_NOSIGNAL, 1L);
 
-  // reset settings in case we are re-attaching
-  _settings.reset();
+  // create non persistant settings
+  // so that we don't add headers twice
+  TransferSettings vol_settings(_settings);  
 
   // add custom headers
-  _settings.addHeader(anonymousIdHeader());
-  _settings.addHeader(distributionFlavorHeader());
-  _settings.addHeader("Pragma:");
+  vol_settings.addHeader(anonymousIdHeader());
+  vol_settings.addHeader(distributionFlavorHeader());
+  vol_settings.addHeader("Pragma:");
 
   _settings.setTimeout(TRANSFER_TIMEOUT);
   _settings.setConnectTimeout(CONNECT_TIMEOUT);
@@ -637,11 +649,12 @@ void MediaCurl::setupEasy()
   SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
 
   // append settings custom headers to curl
-  for ( TransferSettings::Headers::const_iterator it = _settings.headersBegin();
-        it != _settings.headersEnd();
+  for ( TransferSettings::Headers::const_iterator it = vol_settings.headersBegin();
+        it != vol_settings.headersEnd();
         ++it )
   {
-
+      MIL << "HEADER " << *it << std::endl;
+      
       _customHeaders = curl_slist_append(_customHeaders, it->c_str());
       if ( !_customHeaders )
           ZYPP_THROW(MediaCurlInitException(_url));
@@ -962,7 +975,7 @@ void MediaCurl::evaluateCurlCode( const Pathname &filename,
 bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
 {
   DBG << filename.asString() << endl;
-
+  
   if(!_url.isValid())
     ZYPP_THROW(MediaBadUrlException(_url));
 
@@ -1005,7 +1018,8 @@ bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
   // works for ftp as well, because retrieving only headers
   // ftp will return always OK code ?
   // See http://curl.haxx.se/docs/knownbugs.html #58
-  if (  _url.getScheme() == "http" ||  _url.getScheme() == "https" )
+  if (  (_url.getScheme() == "http" ||  _url.getScheme() == "https") &&
+        _settings.headRequestsAllowed() )
     ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
   else
     ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
index bdd4396..ae0eda9 100644 (file)
@@ -99,6 +99,8 @@ class MediaCurl : public MediaHandler
 
     virtual ~MediaCurl() { try { release(); } catch(...) {} }
 
+    TransferSettings & settings();
+
     static void setCookieFile( const Pathname & );
 
     class Callbacks
index 0a164dc..5efdbc2 100644 (file)
@@ -34,6 +34,7 @@ public:
         , _verify_host(false)
         , _verify_peer(false)
         , _ca_path("/etc/ssl/certs")
+        , _head_requests_allowed(true)
     {}
 
     virtual ~Impl()
@@ -75,6 +76,9 @@ public:
     bool _verify_host;
     bool _verify_peer;
     Pathname _ca_path;
+    // workarounds
+    bool _head_requests_allowed;
 };
     
 TransferSettings::TransferSettings()
@@ -299,7 +303,15 @@ std::string TransferSettings::authType() const
     return _impl->_authtype;
 }
 
+void TransferSettings::setHeadRequestsAllowed(bool allowed)
+{
+    _impl->_head_requests_allowed = allowed;    
+}    
 
+bool TransferSettings::headRequestsAllowed() const
+{
+    return _impl->_head_requests_allowed;    
+} 
 
 } // ns media
 } // ns zypp
index 26dc7f0..4806056 100644 (file)
@@ -244,6 +244,16 @@ public:
    */
   std::string authType() const;
 
+  /**
+   * set whether HEAD requests are allowed
+   */
+  void setHeadRequestsAllowed(bool allowed);
+
+  /**
+   * whether HEAD requests are allowed
+   */
+  bool headRequestsAllowed() const;
+
 protected:
   class Impl;
   RWCOW_pointer<Impl> _impl;
diff --git a/zypp/media/UrlResolverPlugin.cc b/zypp/media/UrlResolverPlugin.cc
new file mode 100644 (file)
index 0000000..1c6572d
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/UrlResolverPlugin.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/media/UrlResolverPlugin.h"
+#include "zypp/media/MediaException.h"
+#include "zypp/PluginScript.h"
+#include "zypp/ZConfig.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    /** UrlResolverPlugin implementation. */
+    struct UrlResolverPlugin::Impl
+    {
+
+
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    Url UrlResolverPlugin::resolveUrl(const Url & o_url, HeaderList &headers)
+    {
+        if (o_url.getScheme() != "plugin")
+            return o_url;        
+        
+        Url url(o_url);
+        std::string name = url.getPathName();
+        Pathname plugin_path = (ZConfig::instance().pluginsPath()/"urlresolver")/name;    
+        if (PathInfo(plugin_path).isExist()) {
+            PluginScript scr;
+            scr.open(plugin_path);
+            // send frame to plugin
+            PluginFrame f("RESOLVEURL");
+
+            url::ParamMap params = url.getQueryStringMap();
+            url::ParamMap::const_iterator param_it;
+            for( param_it = params.begin();
+                 param_it != params.end();
+                 ++param_it)
+                f.setHeader(param_it->first, param_it->second);
+            
+            scr.send(f);
+
+            PluginFrame r(scr.receive());
+            if (r.command() == "RESOLVEDURL") {
+                // now set
+                url = Url(r.body());
+                PluginFrame::HeaderListIterator it;
+                
+                for (it = r.headerBegin();
+                     it != r.headerEnd();
+                     ++it) {
+                    std::pair<std::string, std::string> values(*it);
+                    // curl resets headers that are empty, so we use a workaround
+                    if (values.second.empty()) {
+                        values.second = "\nX-libcurl-Empty-Header-Workaround: *";
+                    }                    
+                    headers.insert(values);                    
+                }
+            }
+            else if (r.command() == "ERROR") {
+                ZYPP_THROW(MediaException(r.body()));
+            }            
+        }
+        return url;        
+    }
+
+    /** \relates UrlResolverPlugin::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin::Impl & obj )
+    {
+      return str << "UrlResolverPlugin::Impl";
+    }
+
+    UrlResolverPlugin::~UrlResolverPlugin()
+    {}
+
+    std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj )
+    {
+      return str << *obj._pimpl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/media/UrlResolverPlugin.h b/zypp/media/UrlResolverPlugin.h
new file mode 100644 (file)
index 0000000..b1f5bad
--- /dev/null
@@ -0,0 +1,75 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/UrlResolverPlugin.h
+ *
+*/
+#ifndef ZYPP_MEDIA_URLRESOLVERPLUGIN_H
+#define ZYPP_MEDIA_URLRESOLVERPLUGIN_H
+
+#include <iosfwd>
+#include <map>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    /** 
+     *
+     */
+    class UrlResolverPlugin
+    {
+      friend std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj );
+
+    public:
+
+      class Impl;
+
+      typedef std::multimap<std::string, std::string> HeaderList;
+
+      /**
+       * Resolves an url using the installed plugins
+       * If no plugin is found the url is resolved as
+       * its current value.
+       *
+       * Custom headers are inserted in the provided header list
+       */
+      static Url resolveUrl(const Url &url, HeaderList &headers);
+
+    public:
+      /** Dtor */
+      ~UrlResolverPlugin();
+
+    private:
+
+      /** Default ctor */
+      UrlResolverPlugin();
+
+      /** Pointer to implementation */
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates UrlResolverPlugin Stream output */
+    std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_URLRESOLVERPLUGIN_H