dodo  0.0.1
A C++ library to create containerized Linux services
uri.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * @file uri.cpp
20  * Implements the dodo::network::URI class.
21  */
22 
23 #include "network/uri.hpp"
24 
25 #include <iostream>
26 #include <sstream>
27 
28 namespace dodo {
29 
30  namespace network {
31 
32 
33  void URI::reset() {
34  scheme_ = "";
35  userinfo_ = "";
36  host_ = "";
37  port_ = "";
38  path_ = "";
39  query_ = "";
40  fragment_ = "";
41  }
42 
43  std::string URI::asString() const {
44  std::stringstream ss;
45  ss << scheme_ << ":";
46  if ( host_.size() ) ss << "//";
47  if ( userinfo_.size() ) ss << userinfo_ << '@';
48  if ( host_.size() ) ss << host_;
49  if ( port_.size() ) ss << ':' << port_;
50  ss << path_;
51  if ( query_.size() ) ss << '?' << query_;
52  if ( fragment_.size() ) ss << '#' << fragment_;
53  return ss.str();
54  }
55 
56  bool URI::verifySchemeChar( char c ) {
57  return std::isalpha(c) ||
58  std::isdigit(c) ||
59  c == '+' ||
60  c == '.' ||
61  c == '-';
62  }
63 
64  bool URI::verifyOctetChar( char c ) {
65  return std::isalpha(c) ||
66  std::isdigit(c) ||
67  c == '-' ||
68  c == '.' ||
69  c == '_' ||
70  c == '~';
71  }
72 
73  bool URI::verifyTCP6Char( char c ) {
74  return ( ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) ) ||
75  std::isdigit(c) ||
76  c == ':';
77  }
78 
79  bool URI::verifyUserInfoHostChar( char c ) {
80  return verifyOctetChar(c) ||
81  c == '!' ||
82  c == '$' ||
83  c == '&' ||
84  c == '\'' ||
85  c == '(' ||
86  c == ')' ||
87  c == '*' ||
88  c == '+' ||
89  c == ',' ||
90  c == ';' ||
91  c == '=';
92  }
93 
94  bool URI::verifyPathChar( char c ) {
95  return verifyUserInfoHostChar(c) ||
96  c == ':' ||
97  c == '/' ||
98  c == '@';
99  }
100 
102  return verifyUserInfoHostChar(c) ||
103  c == ':' ||
104  c == '@' ||
105  c == '/' ||
106  c == '?';
107  }
108 
109  bool URI::parse( const std::string &s, size_t &idx ) {
110  //std::cout << s << std::endl;
111  uint16_t prev_state, return_state, state;
112  idx = 0;
113  size_t mark = 0;
114  reset();
115  prev_state = psError;
116  return_state = psError;
117  state = psSchemeStart;
118  while ( state != psDone && state != psError && idx < s.size() ) {
119  //std::cout << idx << " " << s[idx] << " " << s[mark] << " " << state << std::endl;
120  switch ( state ) {
121 
122  case psSchemeStart:
123  if ( prev_state != psSchemeStart && !std::isalpha(s[idx] ) ) state = psError;
124  else if ( ( s[idx] ) == ':' ) state = psSchemeEnd; else {
125  if ( !verifySchemeChar( s[idx] ) ) state = psError; else idx++;
126  }
127  break;
128 
129  case psSchemeEnd:
130  scheme_ = s.substr(mark,idx-mark);
131  //std::cout << "scheme=" << scheme_ << std::endl;
132  idx++;
133  if ( s[idx] == '/' ) {
134  idx++;
135  if ( s[idx] == '/' ) {
136  mark = ++idx;
137  state = psAuthorityStart;
138  } else state = psError;
139  } else {
140  state = psPathStart;
141  mark = idx;
142  }
143  break;
144 
145  case psAuthorityStart:
146  if ( s[idx] == '@' ) {
147  state = psUserInfoEnd;
148  } else if ( s[idx] == '/' ) {
149  state = psHostEnd;
150  } else if ( s[idx] == ':' ) {
151  state = psHostEnd;
152  } else if ( s[idx] == '[' ) {
153  state = psTCP6Start;
154  idx++;
155  } else if ( s[idx] == '%' ) {
156  return_state = state;
157  state = psPCTEncoded;
158  } else {
159  if ( verifyUserInfoHostChar( s[idx] ) ) {
160  idx++;
161  } else state = psError;
162  }
163  break;
164 
165  case psUserInfoEnd:
166  if ( idx > mark ) {
167  userinfo_ = s.substr(mark,idx-mark);
168  //std::cout << "userinfo=" << userinfo_ << std::endl;
169  mark = ++idx;
170  state = psHostStart;
171  } else state = psError;
172  break;
173 
174  case psHostStart:
175  if ( s[idx] == '[' ) {
176  state = psTCP6Start;
177  idx++;
178  }
179  else if ( s[idx] == ':' || s[idx] == '/' ) state = psHostEnd;
180  else if ( verifyUserInfoHostChar( s[idx] ) ) idx++;
181  else if ( s[idx] == '%' ) {
182  return_state = state;
183  state = psPCTEncoded;
184  }
185  else state = psError;
186  break;
187 
188  case psTCP6Start:
189  if ( s[idx] == ']' ) {
190  state = psHostEnd;
191  idx++;
192  }
193  else if ( verifyTCP6Char( s[idx] ) ) idx++;
194  else state = psError;
195  break;
196 
197  case psHostEnd:
198  if ( idx > mark ) {
199  host_ = s.substr(mark,idx-mark);
200  //std::cout << "host=" << host_ << std::endl;
201  if ( s[idx] == ':' ) {
202  state = psPortStart;
203  mark = ++idx;
204  } else {
205  state = psPathStart;
206  mark = idx;
207  }
208  } else state = psError;
209  break;
210 
211  case psPortStart:
212  if ( ! std::isdigit( s[idx] ) ) state = psPortEnd; else idx++;
213  break;
214 
215  case psPortEnd:
216  if ( idx > mark ) {
217  port_ = s.substr(mark,idx-mark);
218  //std::cout << "port=" << port_ << std::endl;
219  state = psPathStart;
220  mark = idx;
221  } else state = psError;
222  break;
223 
224  case psPathStart:
225  if ( s[idx] == '?' ) state = psPathEnd;
226  else if ( s[idx] == '#' ) state = psPathEnd;
227  else if ( verifyPathChar( s[idx] ) ) idx++;
228  else if ( s[idx] == '%' ) {
229  return_state = state;
230  state = psPCTEncoded;
231  } else state = psError;
232  break;
233 
234  case psPathEnd:
235  if ( idx > mark ) {
236  path_ = s.substr(mark,idx-mark);
237  //std::cout << "path=" << path_ << std::endl;
238  if ( s[idx] == '?' ) state = psQueryStart;
239  else if ( s[idx] == '#' ) state = psFragmentStart;
240  mark = ++idx;
241  } else state = psError;
242  break;
243 
244  case psQueryStart:
245  if ( s[idx] == '#' )
246  state = psQueryEnd;
247  else if ( verifyQueryFragmentChar( s[idx] ) ) idx++;
248  else if ( s[idx] == '%' ) {
249  return_state = state;
250  state = psPCTEncoded;
251  }
252  else state = psError;
253  break;
254 
255  case psQueryEnd:
256  if ( idx > mark ) {
257  query_ = s.substr(mark,idx-mark);
258  //std::cout << "query=" << query_ << std::endl;
259  state = psFragmentStart;
260  mark = ++idx;
261  } else state = psError;
262  break;
263 
264  case psFragmentStart:
265  if ( verifyQueryFragmentChar( s[idx] ) ) idx++;
266  else if ( s[idx] == '%' ) {
267  return_state = state;
268  state = psPCTEncoded;
269  }
270  else state = psError;
271  break;
272 
273  case psPCTEncoded:
274  if ( s[idx] == '%' ) {
275  idx++;
276  if ( idx < s.size() && std::isxdigit( s[idx] ) ) {
277  idx++;
278  if ( idx < s.size() && std::isxdigit( s[idx] ) ) {
279  idx++;
280  state = return_state;
281  } else state = psError;
282  } else state = psError;
283  } else state = psError;
284  break;
285 
286  default:
287  idx++;
288 
289  }
290  prev_state = state;
291  }
292  switch ( state ) {
293  case psAuthorityStart:
294  host_ = s.substr(mark,s.size()-mark);
295  //std::cout << "host=" << host_ << std::endl;
296  break;
297  case psPathStart:
298  path_ = s.substr(mark,s.size()-mark);
299  //std::cout << "path=" << path_ << std::endl;
300  break;
301  case psQueryStart:
302  query_ = s.substr(mark,s.size()-mark);
303  //std::cout << "query=" << query_ << std::endl;
304  break;
305  case psFragmentStart:
306  fragment_ = s.substr(mark,s.size()-mark);
307  //std::cout << "fragment=" << fragment_ << std::endl;
308  break;
309  }
310  return state != psError;
311  }
312 
313  }
314 
315 }
316 
dodo::network::URI::verifyPathChar
bool verifyPathChar(char c)
Check if c is a valid Path char.
Definition: uri.cpp:94
dodo::network::URI::asString
std::string asString() const
Return the URI as a std::string.
Definition: uri.cpp:43
dodo::network::URI::userinfo_
std::string userinfo_
The userinfo.
Definition: uri.hpp:314
dodo::network::URI::verifyQueryFragmentChar
bool verifyQueryFragmentChar(char c)
Check if c is a valid Fragment char.
Definition: uri.cpp:101
dodo::network::URI::path_
std::string path_
The path.
Definition: uri.hpp:320
dodo::network::URI::reset
void reset()
Reste all data to empty strings.
Definition: uri.cpp:33
dodo::network::URI::verifyUserInfoHostChar
bool verifyUserInfoHostChar(char c)
Check if c is a valid UserInfo or host char.
Definition: uri.cpp:79
dodo
A C++ platform interface to lean Linux services tailored for containerized deployment.
Definition: application.hpp:29
dodo::network::URI::host_
std::string host_
The host.
Definition: uri.hpp:316
dodo::network::URI::port_
std::string port_
The port.
Definition: uri.hpp:318
dodo::network::URI::parse
bool parse(const std::string &s, size_t &idxfail)
Parse the string as an URI.
Definition: uri.cpp:109
dodo::network::URI::fragment_
std::string fragment_
The fragment.
Definition: uri.hpp:324
dodo::network::URI::scheme_
std::string scheme_
The scheme.
Definition: uri.hpp:312
dodo::network::URI::query_
std::string query_
The query.
Definition: uri.hpp:322
dodo::network::URI::verifyTCP6Char
bool verifyTCP6Char(char c)
Check if c is a valid TCP6 address char.
Definition: uri.cpp:73
dodo::network::URI::verifySchemeChar
bool verifySchemeChar(char c)
Check if c is a valid Scheme char.
Definition: uri.cpp:56
dodo::network::URI::verifyOctetChar
bool verifyOctetChar(char c)
Check if c is a valid Octet.
Definition: uri.cpp:64
uri.hpp