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 detail
31 : {
32 :
33 10 : struct URIData
34 : {
35 10 : URIData() : port( 0 ) {}
36 :
37 : std::string scheme;
38 : std::string userinfo;
39 : std::string host;
40 : uint16_t port;
41 : std::string path;
42 : std::string query;
43 : std::string fragment;
44 : lunchbox::URI::KVMap queryMap;
45 : };
46 :
47 : class uri_parse : public std::exception
48 : {
49 : public:
50 1 : uri_parse( const std::string& uri )
51 1 : {
52 1 : _error << "Error parsing URI string: " << uri << std::endl;
53 1 : }
54 :
55 : uri_parse( const uri_parse& excep )
56 : {
57 : _error << excep._error;
58 : }
59 :
60 1 : virtual ~uri_parse() throw() {}
61 :
62 0 : virtual const char* what() const throw() { return _error.str().c_str(); }
63 :
64 : private:
65 : std::stringstream _error;
66 : };
67 :
68 9 : class URI
69 : {
70 : public:
71 10 : URI( const std::string& uri )
72 11 : : _uri( uri )
73 : {
74 10 : if( uri.empty( ))
75 11 : return;
76 :
77 8 : boost::match_results< std::string::const_iterator > results;
78 : boost::regex expr(
79 : "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?",
80 16 : boost::regex::perl | boost::regex::icase );
81 :
82 8 : if( !boost::regex_search( uri, results, expr ) )
83 0 : throw uri_parse( _uri );
84 :
85 8 : const std::string& schema = std::string( results[2].first,
86 24 : results[2].second );
87 8 : if( schema.empty() )
88 1 : throw uri_parse( _uri );
89 :
90 7 : _uriData.scheme = schema;
91 7 : const std::string& userHost = std::string( results[4].first,
92 21 : results[4].second );
93 7 : if( !userHost.empty() )
94 : {
95 3 : std::vector< std::string > splitUserHost;
96 6 : std::string hostPort;
97 : boost::algorithm::split( splitUserHost, userHost,
98 3 : boost::is_any_of( "@"));
99 3 : if( splitUserHost.size() == 2 ) // for ex: user:pass@hello.com:port
100 : {
101 1 : _uriData.userinfo = splitUserHost[ 0 ];
102 1 : hostPort = splitUserHost[ 1 ];
103 : }
104 : else
105 2 : hostPort = splitUserHost[ 0 ];
106 :
107 6 : std::vector< std::string > splitHostPort;
108 : boost::algorithm::split( splitHostPort, hostPort,
109 3 : boost::is_any_of( ":" ));
110 3 : _uriData.host = splitHostPort[ 0 ];
111 :
112 3 : if( splitUserHost.size() == 2 ) // for ex: user:pass@hello.com:port
113 : _uriData.port = boost::lexical_cast< uint16_t >(
114 4 : splitHostPort[ 1 ] );
115 : }
116 :
117 7 : _uriData.path = std::string( results[5].first, results[5].second );
118 7 : _uriData.query = std::string( results[7].first, results[7].second );
119 7 : _uriData.fragment = std::string( results[9].first, results[9].second );
120 :
121 : // parse query data into key-value pairs
122 14 : std::string query = _uriData.query;
123 16 : while( !query.empty( ))
124 : {
125 2 : const size_t nextPair = query.find( ',' );
126 2 : if( nextPair == 0 )
127 : {
128 0 : query = query.substr( 1 );
129 0 : continue;
130 : }
131 :
132 2 : const std::string pair = query.substr( 0, nextPair );
133 2 : if( nextPair == std::string::npos )
134 1 : query.clear();
135 : else
136 1 : query = query.substr( nextPair + 1 );
137 :
138 2 : const size_t eq = pair.find( '=' );
139 2 : if( eq == std::string::npos || eq == 0 )
140 0 : continue;
141 2 : _uriData.queryMap[ pair.substr( 0, eq ) ] = pair.substr( eq + 1 );
142 10 : }
143 : }
144 :
145 44 : const URIData& getData() const { return _uriData; }
146 : const std::string& getURI() const { return _uri; }
147 :
148 : private:
149 : std::string _uri;
150 : URIData _uriData;
151 : };
152 :
153 : }
154 :
155 10 : URI::URI( const std::string &uri )
156 11 : : _impl( new detail::URI( uri ) )
157 : {
158 9 : }
159 :
160 0 : URI::URI( const URI& from )
161 0 : : _impl( new detail::URI( *from._impl ))
162 : {
163 0 : }
164 9 : lunchbox::URI::~URI()
165 : {
166 9 : delete _impl;
167 9 : }
168 :
169 0 : URI& URI::operator = ( const URI& rhs )
170 : {
171 0 : if( this != &rhs )
172 0 : *_impl = *rhs._impl;
173 0 : return *this;
174 : }
175 :
176 13 : const std::string &URI::getScheme() const
177 : {
178 13 : return _impl->getData().scheme;
179 : }
180 :
181 2 : const std::string &URI::getHost() const
182 : {
183 2 : return _impl->getData().host;
184 : }
185 :
186 3 : uint16_t URI::getPort() const
187 : {
188 3 : return _impl->getData().port;
189 : }
190 :
191 3 : const std::string &URI::getUserinfo() const
192 : {
193 3 : return _impl->getData().userinfo;
194 : }
195 :
196 10 : const std::string& URI::getPath() const
197 : {
198 10 : return _impl->getData().path;
199 : }
200 :
201 2 : const std::string& URI::getQuery() const
202 : {
203 2 : return _impl->getData().query;
204 : }
205 :
206 3 : const std::string &URI::getFragment() const
207 : {
208 3 : return _impl->getData().fragment;
209 : }
210 :
211 0 : URI::ConstKVIter URI::queryBegin() const
212 : {
213 0 : return _impl->getData().queryMap.begin();
214 : }
215 :
216 3 : URI::ConstKVIter URI::queryEnd() const
217 : {
218 3 : return _impl->getData().queryMap.end();
219 : }
220 :
221 5 : URI::ConstKVIter URI::findQuery( const std::string& key ) const
222 : {
223 5 : return _impl->getData().queryMap.find( key );
224 : }
225 :
226 87 : }
|