Kea  2.1.7-git
url.cc
Go to the documentation of this file.
1 // Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <http/url.h>
11 #include <boost/lexical_cast.hpp>
12 #include <sstream>
13 
14 #include <iostream>
15 
16 namespace isc {
17 namespace http {
18 
19 Url::Url(const std::string& url)
20  : url_(url), valid_(false), error_message_(), scheme_(Url::HTTPS),
21  hostname_(), port_(0), path_() {
22  parse();
23 }
24 
25 bool
26 Url::operator<(const Url& url) const {
27  return (url_ < url.rawUrl());
28 }
29 
31 Url::getScheme() const {
32  checkValid();
33  return (scheme_);
34 }
35 
36 std::string
37 Url::getHostname() const {
38  checkValid();
39  return (hostname_);
40 }
41 
42 std::string
44  std::string hostname = getHostname();
45  if ((hostname.length() >= 2) && (hostname.at(0) == '[')) {
46  return (hostname.substr(1, hostname.length() - 2));
47  }
48 
49  return (hostname);
50 }
51 
52 unsigned
53 Url::getPort() const {
54  checkValid();
55  return (port_);
56 }
57 
58 std::string
59 Url::getPath() const {
60  checkValid();
61  return (path_);
62 }
63 
64 std::string
65 Url::toText() const {
66  std::ostringstream s;
67  s << (getScheme() == HTTP ? "http" : "https");
68  s << "://" << getHostname();
69 
70  if (getPort() != 0) {
71  s << ":" << getPort();
72  }
73 
74  s << getPath();
75 
76  return (s.str());
77 }
78 
79 void
80 Url::checkValid() const {
81  if (!isValid()) {
82  isc_throw(InvalidOperation, "invalid URL " << url_ << ": " << error_message_);
83  }
84 }
85 
86 void
87 Url::parse() {
88  valid_ = false;
89  error_message_.clear();
90  scheme_ = Url::HTTPS;
91  hostname_.clear();
92  port_ = 0;
93  path_.clear();
94 
95  std::ostringstream error;
96 
97  // Retrieve scheme
98  size_t offset = url_.find(":");
99  if ((offset == 0) || (offset == std::string::npos)) {
100  error << "url " << url_ << " lacks http or https scheme";
101  error_message_ = error.str();
102  return;
103  }
104 
105  // Validate scheme.
106  std::string scheme = url_.substr(0, offset);
107  if (scheme == "http") {
108  scheme_ = Url::HTTP;
109 
110  } else if (scheme == "https") {
111  scheme_ = Url::HTTPS;
112 
113  } else {
114  error << "invalid scheme " << scheme << " in " << url_;
115  error_message_ = error.str();
116  return;
117  }
118 
119  // Colon and two slashes should follow the scheme
120  if (url_.substr(offset, 3) != "://") {
121  error << "expected :// after scheme in " << url_;
122  error_message_ = error.str();
123  return;
124  }
125 
126  // Move forward to hostname.
127  offset += 3;
128  if (offset >= url_.length()) {
129  error << "hostname missing in " << url_;
130  error_message_ = error.str();
131  return;
132  }
133 
134  size_t offset2 = 0;
135 
136  // IPv6 address is specified within [ ].
137  if (url_.at(offset) == '[') {
138  offset2 = url_.find(']', offset);
139  if (offset2 == std::string::npos) {
140  error << "expected ] after IPv6 address in " << url_;
141  error_message_ = error.str();
142  return;
143 
144  } else if (offset2 == offset + 1) {
145  error << "expected IPv6 address within [] in " << url_;
146  error_message_ = error.str();
147  return;
148  }
149 
150  // Move one character beyond the ].
151  ++offset2;
152 
153  } else {
154  // There is a normal hostname or IPv4 address. It is terminated
155  // by the colon (for port number), a slash (if no port number) or
156  // goes up to the end of the URL.
157  offset2 = url_.find(":", offset);
158  if (offset2 == std::string::npos) {
159  offset2 = url_.find("/", offset);
160  if (offset2 == std::string::npos) {
161  // No port number and no slash.
162  offset2 = url_.length();
163  }
164  }
165  }
166 
167  // Extract the hostname.
168  hostname_ = url_.substr(offset, offset2 - offset);
169 
170  // If there is no port number and no path, simply return and mark the
171  // URL as valid.
172  if (offset2 == url_.length()) {
173  valid_ = true;
174  return;
175  }
176 
177  // If there is a port number, we need to read it and convert to
178  // numeric value.
179  if (url_.at(offset2) == ':') {
180  if (offset2 == url_.length() - 1) {
181  error << "expected port number after : in " << url_;
182  error_message_ = error.str();
183  return;
184  }
185  // Move to the port number.
186  ++offset2;
187 
188  // Port number may be terminated by a slash or by the end of URL.
189  size_t slash_offset = url_.find('/', offset2);
190  std::string port_str;
191  if (slash_offset == std::string::npos) {
192  port_str = url_.substr(offset2);
193  } else {
194  port_str = url_.substr(offset2, slash_offset - offset2);
195  }
196 
197  try {
198  // Try to convert the port number to numeric value.
199  port_ = boost::lexical_cast<unsigned>(port_str);
200 
201  } catch (...) {
202  error << "invalid port number " << port_str << " in " << url_;
203  error_message_ = error.str();
204  return;
205  }
206 
207  // Go to the end of the port section.
208  offset2 = slash_offset;
209  }
210 
211  // If there is anything left in the URL, we consider it a path.
212  if (offset2 != std::string::npos) {
213  path_ = url_.substr(offset2);
214  if (path_.empty()) {
215  path_ = "/";
216  }
217  }
218 
219  valid_ = true;
220 }
221 
222 } // end of namespace isc::http
223 } // end of namespace isc
Scheme
Scheme: https or http.
Definition: url.h:24
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Represents an URL.
Definition: url.h:20
std::string getPath() const
Returns path.
Definition: url.cc:59
bool operator<(const Url &url) const
compares URLs lexically.
Definition: url.cc:26
std::string getStrippedHostname() const
Returns hostname stripped from [ ] characters surrounding IPv6 address.
Definition: url.cc:43
bool isValid() const
Checks if the URL is valid.
Definition: url.h:47
unsigned getPort() const
Returns port number.
Definition: url.cc:53
std::string toText() const
Returns textual representation of the URL.
Definition: url.cc:65
Defines the logger used by the top-level component of kea-lfc.
Url(const std::string &url)
Constructor.
Definition: url.cc:19
A generic exception that is thrown if a function is called in a prohibited way.
const std::string & rawUrl() const
Returns the raw, unparsed URL string.
Definition: url.h:87
Scheme getScheme() const
Returns parsed scheme.
Definition: url.cc:31