ndn-fch-discovery.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2024, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "ndn-fch-discovery.hpp"
27 
28 #include <boost/algorithm/string.hpp>
29 #include <boost/asio/ip/tcp.hpp>
30 
31 #include <regex>
32 #include <sstream>
33 
34 namespace ndn::autoconfig {
35 
42 class Url
43 {
44 public:
45  Url(const std::string& url)
46  : m_isValid(false)
47  {
48  static const std::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
49  std::smatch protocolMatch;
50  if (!std::regex_match(url, protocolMatch, protocolExp)) {
51  return;
52  }
53  m_scheme = protocolMatch[1];
54  std::string authority = protocolMatch[3];
55  m_path = protocolMatch[4];
56 
57  // pattern for IPv6 address enclosed in [ ], with optional port number
58  static const std::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
59  // pattern for IPv4-mapped IPv6 address, with optional port number
60  static const std::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
61  // pattern for IPv4/hostname/fd/ifname, with optional port number
62  static const std::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
63 
64  if (authority.empty()) {
65  // UNIX, internal
66  }
67  else {
68  std::smatch match;
69  bool isV6 = std::regex_match(authority, match, v6Exp);
70  if (isV6 ||
71  std::regex_match(authority, match, v4MappedV6Exp) ||
72  std::regex_match(authority, match, v4HostExp)) {
73  m_host = match[1];
74  m_port = match[2];
75  }
76  else {
77  return;
78  }
79  }
80  if (m_port.empty()) {
81  m_port = "80";
82  }
83  if (m_path.empty()) {
84  m_path = "/";
85  }
86  m_isValid = true;
87  }
88 
89  bool
90  isValid() const
91  {
92  return m_isValid;
93  }
94 
95  const std::string&
96  getScheme() const
97  {
98  return m_scheme;
99  }
100 
101  const std::string&
102  getHost() const
103  {
104  return m_host;
105  }
106 
107  const std::string&
108  getPort() const
109  {
110  return m_port;
111  }
112 
113  const std::string&
114  getPath() const
115  {
116  return m_path;
117  }
118 
119 private:
120  bool m_isValid;
121  std::string m_scheme;
122  std::string m_host;
123  std::string m_port;
124  std::string m_path;
125 };
126 
127 class HttpException : public std::runtime_error
128 {
129 public:
130  using std::runtime_error::runtime_error;
131 };
132 
133 NdnFchDiscovery::NdnFchDiscovery(const std::string& url)
134  : m_url(url)
135 {
136 }
137 
138 void
139 NdnFchDiscovery::doStart()
140 {
141  try {
142  boost::asio::ip::tcp::iostream requestStream;
143  requestStream.expires_after(std::chrono::seconds(3));
144 
145  Url url(m_url);
146  if (!url.isValid()) {
147  NDN_THROW(HttpException("Invalid NDN-FCH URL: " + m_url));
148  }
149  if (!boost::iequals(url.getScheme(), "http")) {
150  NDN_THROW(HttpException("Only http:// NDN-FCH URLs are supported"));
151  }
152 
153  requestStream.connect(url.getHost(), url.getPort());
154  if (!requestStream) {
155  NDN_THROW(HttpException("HTTP connection error to " + m_url));
156  }
157 
158  requestStream << "GET " << url.getPath() << " HTTP/1.0\r\n";
159  requestStream << "Host: " << url.getHost() << ":" << url.getPort() << "\r\n";
160  requestStream << "Accept: */*\r\n";
161  requestStream << "Cache-Control: no-cache\r\n";
162  requestStream << "Connection: close\r\n\r\n";
163  requestStream.flush();
164 
165  std::string statusLine;
166  std::getline(requestStream, statusLine);
167  if (!requestStream) {
168  NDN_THROW(HttpException("HTTP communication error"));
169  }
170 
171  std::stringstream responseStream(statusLine);
172  std::string httpVersion;
173  responseStream >> httpVersion;
174  unsigned int statusCode;
175  responseStream >> statusCode;
176  std::string statusMessage;
177 
178  std::getline(responseStream, statusMessage);
179  if (!static_cast<bool>(requestStream) || httpVersion.substr(0, 5) != "HTTP/") {
180  NDN_THROW(HttpException("HTTP communication error"));
181  }
182  if (statusCode != 200) {
183  boost::trim(statusMessage);
184  NDN_THROW(HttpException("HTTP request failed: " + std::to_string(statusCode) + " " + statusMessage));
185  }
186  std::string header;
187  while (std::getline(requestStream, header) && header != "\r")
188  ;
189 
190  std::string hubHost;
191  requestStream >> hubHost;
192  if (hubHost.empty()) {
193  NDN_THROW(HttpException("NDN-FCH did not return hub host"));
194  }
195 
196  this->provideHubFaceUri("udp://" + hubHost);
197  }
198  catch (const std::runtime_error& e) {
199  this->fail(e.what());
200  }
201 }
202 
203 } // namespace ndn::autoconfig
NdnFchDiscovery(const std::string &url)
Create stage to discover NDN hub using NDN-FCH protocol.
void provideHubFaceUri(const std::string &s)
Parse HUB FaceUri from string and declare success.
Definition: stage.cpp:43
void fail(const std::string &msg)
Definition: stage.cpp:63