LCOV - code coverage report
Current view: top level - lunchbox - uri.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 95 108 88.0 %
Date: 2014-10-01 Functions: 23 32 71.9 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2013-2014, ahmet.bilgili@epfl.ch
       3             :  *
       4             :  * This file is part of Lunchbox <https://github.com/Eyescale/Lunchbox>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include <lunchbox/uri.h>
      21             : 
      22             : #include <boost/regex.hpp>
      23             : #include <boost/algorithm/string_regex.hpp>
      24             : #include <boost/lexical_cast.hpp>
      25             : #include <sstream>
      26             : #include <exception>
      27             : 
      28             : namespace lunchbox
      29             : {
      30             : namespace
      31             : {
      32          19 : struct URIData
      33             : {
      34          19 :     URIData() : port( 0 ) {}
      35             : 
      36             :     std::string scheme;
      37             :     std::string userinfo;
      38             :     std::string host;
      39             :     uint16_t port;
      40             :     std::string path;
      41             :     std::string query;
      42             :     std::string fragment;
      43             :     URI::KVMap queryMap;
      44             : };
      45             : }
      46             : 
      47             : namespace detail
      48             : {
      49             : class uri_parse : public std::exception
      50             : {
      51             : public:
      52           1 :     uri_parse( const std::string& uri )
      53           1 :     {
      54           1 :         _error << "Error parsing URI string: " << uri << std::endl;
      55           1 :     }
      56             : 
      57             :     uri_parse( const uri_parse& excep )
      58             :     {
      59             :         _error << excep._error.str();
      60             :     }
      61             : 
      62           1 :     virtual ~uri_parse() throw() {}
      63             : 
      64           0 :     virtual const char* what() const throw() { return _error.str().c_str(); }
      65             : 
      66             : private:
      67             :     std::stringstream _error;
      68             : };
      69             : 
      70          18 : class URI
      71             : {
      72             : public:
      73          19 :     explicit URI( const std::string& uri )
      74          20 :     {
      75          19 :         if( uri.empty( ))
      76          21 :             return;
      77             : 
      78          16 :         boost::match_results< std::string::const_iterator > results;
      79             :         boost::regex expr(
      80             :             "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#([^?]*))?$",
      81          32 :             boost::regex::perl | boost::regex::icase );
      82             : 
      83          16 :         if( !boost::regex_search( uri, results, expr ) )
      84           1 :             throw uri_parse( uri );
      85             : 
      86          15 :         _uriData.scheme = std::string( results[2].first, results[2].second );
      87             : 
      88          15 :         const std::string& userHost = std::string( results[4].first,
      89          45 :                                                    results[4].second );
      90          15 :         if( !userHost.empty( ))
      91             :         {
      92           7 :             std::vector< std::string > splitUserHost;
      93          14 :             std::string hostPort;
      94             :             boost::algorithm::split( splitUserHost, userHost,
      95           7 :                                      boost::is_any_of( "@"));
      96           7 :             if( splitUserHost.size() == 2 ) // for ex: user:pass@hello.com:port
      97             :             {
      98           1 :                 _uriData.userinfo = splitUserHost[ 0 ];
      99           1 :                 hostPort = splitUserHost[ 1 ];
     100             :             }
     101             :             else
     102           6 :                 hostPort = splitUserHost[ 0 ];
     103             : 
     104          14 :             std::vector< std::string > splitHostPort;
     105             :             boost::algorithm::split( splitHostPort, hostPort,
     106           7 :                                      boost::is_any_of( ":" ));
     107           7 :             _uriData.host = splitHostPort[ 0 ];
     108             : 
     109           7 :             if( splitHostPort.size() == 2 ) // for ex: myhost:port
     110             :                 _uriData.port = boost::lexical_cast< uint16_t >(
     111           9 :                     splitHostPort[ 1 ] );
     112             :         }
     113             : 
     114          15 :         _uriData.path = std::string( results[5].first, results[5].second );
     115          15 :         _uriData.query = std::string( results[7].first, results[7].second );
     116          15 :         _uriData.fragment = std::string( results[9].first, results[9].second );
     117             : 
     118             :         // from http://en.wikipedia.org/wiki/File_URI_scheme:
     119             :         //   "file:///foo.txt" is okay, while "file://foo.txt" is not, although
     120             :         //   some interpreters manage to handle the latter We are "some".
     121          27 :         const bool isFileURI = _uriData.scheme.empty() ||
     122          27 :                                _uriData.scheme == "file";
     123          15 :         const bool hasHost = !_uriData.host.empty();
     124          15 :         const bool hasPath = !_uriData.path.empty();
     125          15 :         if( isFileURI && hasHost && !hasPath )
     126           1 :             _uriData.host.swap( _uriData.path );
     127             : 
     128             :         // parse query data into key-value pairs
     129          30 :         std::string query = _uriData.query;
     130          32 :         while( !query.empty( ))
     131             :         {
     132           2 :             const size_t nextPair = query.find( ',' );
     133           2 :             if( nextPair == 0 )
     134             :             {
     135           0 :                 query = query.substr( 1 );
     136           0 :                 continue;
     137             :             }
     138             : 
     139           2 :             const std::string pair = query.substr( 0, nextPair );
     140           2 :             if( nextPair == std::string::npos )
     141           1 :                 query.clear();
     142             :             else
     143           1 :                 query = query.substr( nextPair + 1 );
     144             : 
     145           2 :             const size_t eq = pair.find( '=' );
     146           2 :             if( eq == std::string::npos || eq == 0 )
     147           0 :                 continue;
     148           2 :             _uriData.queryMap[ pair.substr( 0, eq ) ] = pair.substr( eq + 1 );
     149          18 :         }
     150             :     }
     151             : 
     152         107 :     URIData& getData() { return _uriData; }
     153             :     const URIData& getData() const { return _uriData; }
     154             : 
     155             : private:
     156             :      URIData _uriData;
     157             : };
     158             : 
     159             : }
     160             : 
     161           1 : URI::URI()
     162           1 :     : _impl( new detail::URI( std::string( )))
     163             : {
     164           1 : }
     165             : 
     166          10 : URI::URI( const std::string &uri )
     167          11 :    : _impl( new detail::URI( uri ) )
     168             : {
     169           9 : }
     170             : 
     171           8 : URI::URI( const char* uri )
     172           8 :     : _impl( new detail::URI( std::string( uri )))
     173             : {
     174           8 : }
     175             : 
     176           0 : URI::URI( const URI& from )
     177           0 :     : _impl( new detail::URI( *from._impl ))
     178             : {
     179           0 : }
     180          18 : lunchbox::URI::~URI()
     181             : {
     182          18 :     delete _impl;
     183          18 : }
     184             : 
     185           0 : URI& URI::operator = ( const URI& rhs )
     186             : {
     187           0 :     if( this != &rhs )
     188           0 :         *_impl = *rhs._impl;
     189           0 :     return *this;
     190             : }
     191             : 
     192          28 : const std::string &URI::getScheme() const
     193             : {
     194          28 :     return _impl->getData().scheme;
     195             : }
     196             : 
     197          13 : const std::string &URI::getHost() const
     198             : {
     199          13 :     return _impl->getData().host;
     200             : }
     201             : 
     202           9 : uint16_t URI::getPort() const
     203             : {
     204           9 :     return _impl->getData().port;
     205             : }
     206             : 
     207           8 : const std::string &URI::getUserinfo() const
     208             : {
     209           8 :     return _impl->getData().userinfo;
     210             : }
     211             : 
     212          20 : const std::string& URI::getPath() const
     213             : {
     214          20 :     return _impl->getData().path;
     215             : }
     216             : 
     217           9 : const std::string& URI::getQuery() const
     218             : {
     219           9 :     return _impl->getData().query;
     220             : }
     221             : 
     222           8 : const std::string &URI::getFragment() const
     223             : {
     224           8 :     return _impl->getData().fragment;
     225             : }
     226             : 
     227           0 : URI::ConstKVIter URI::queryBegin() const
     228             : {
     229           0 :     return _impl->getData().queryMap.begin();
     230             : }
     231             : 
     232           3 : URI::ConstKVIter URI::queryEnd() const
     233             : {
     234           3 :     return _impl->getData().queryMap.end();
     235             : }
     236             : 
     237           8 : URI::ConstKVIter URI::findQuery( const std::string& key ) const
     238             : {
     239           8 :     return _impl->getData().queryMap.find( key );
     240             : }
     241             : 
     242           1 : void URI::addQuery( const std::string& key, const std::string& value )
     243             : {
     244           1 :     URIData& data = _impl->getData();
     245             : 
     246           1 :     data.queryMap[ key ] = value;
     247           1 :     data.fragment.clear();
     248             : 
     249             :     // Rebuild fragment string
     250           1 :     data.query.clear();
     251           4 :     BOOST_FOREACH( const URI::KVMap::value_type& pair, data.queryMap )
     252             :     {
     253           3 :         if( data.query.empty( ))
     254           1 :             data.query = pair.first + "=" + pair.second;
     255             :         else
     256           2 :             data.query += std::string( "," ) + pair.first + "=" + pair.second;
     257             :     }
     258           1 : }
     259             : 
     260          90 : }

Generated by: LCOV version 1.10