ndn-fch-discovery.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "ndn-fch-discovery.hpp"
27 #include <boost/regex.hpp>
28 #include <boost/algorithm/string.hpp>
29 
30 namespace ndn {
31 namespace tools {
32 namespace autoconfig {
33 
40 class Url
41 {
42 public:
43  Url(const std::string& url)
44  : m_isValid(false)
45  {
46  static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
47  boost::smatch protocolMatch;
48  if (!boost::regex_match(url, protocolMatch, protocolExp)) {
49  return;
50  }
51  m_scheme = protocolMatch[1];
52  const std::string& authority = protocolMatch[3];
53  m_path = protocolMatch[4];
54 
55  // pattern for IPv6 address enclosed in [ ], with optional port number
56  static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
57  // pattern for IPv4-mapped IPv6 address, with optional port number
58  static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
59  // pattern for IPv4/hostname/fd/ifname, with optional port number
60  static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
61 
62  if (authority.empty()) {
63  // UNIX, internal
64  }
65  else {
66  boost::smatch match;
67  bool isV6 = boost::regex_match(authority, match, v6Exp);
68  if (isV6 ||
69  boost::regex_match(authority, match, v4MappedV6Exp) ||
70  boost::regex_match(authority, match, v4HostExp)) {
71  m_host = match[1];
72  m_port = match[2];
73  }
74  else {
75  return;
76  }
77  }
78  if (m_port.empty()) {
79  m_port = "80";
80  }
81  if (m_path.empty()) {
82  m_path = "/";
83  }
84  m_isValid = true;
85  }
86 
87  bool
88  isValid() const
89  {
90  return m_isValid;
91  }
92 
93  const std::string&
94  getScheme() const
95  {
96  return m_scheme;
97  }
98 
99  const std::string&
100  getHost() const
101  {
102  return m_host;
103  }
104 
105  const std::string&
106  getPort() const
107  {
108  return m_port;
109  }
110 
111  const std::string&
112  getPath() const
113  {
114  return m_path;
115  }
116 
117 private:
118  bool m_isValid;
119  std::string m_scheme;
120  std::string m_host;
121  std::string m_port;
122  std::string m_path;
123 };
124 
125 class HttpException : public std::runtime_error
126 {
127 public:
128  explicit
129  HttpException(const std::string& what)
130  : std::runtime_error(what)
131  {
132  }
133 };
134 
135 NdnFchDiscovery::NdnFchDiscovery(Face& face, KeyChain& keyChain,
136  const std::string& url,
137  const NextStageCallback& nextStageOnFailure)
138  : Base(face, keyChain, nextStageOnFailure)
139  , m_url(url)
140 {
141 }
142 
143 void
145 {
146  try {
147  using namespace boost::asio::ip;
148  tcp::iostream requestStream;
149 
150  requestStream.expires_from_now(boost::posix_time::milliseconds(3000));
151 
152  Url url(m_url);
153  if (!url.isValid()) {
154  BOOST_THROW_EXCEPTION(HttpException("Invalid NDN-FCH URL: " + m_url));
155  }
156 
157  if (!boost::iequals(url.getScheme(), "http")) {
158  BOOST_THROW_EXCEPTION(HttpException("Only http:// NDN-FCH URLs are supported"));
159  }
160 
161  requestStream.connect(url.getHost(), url.getPort());
162 
163  if (!requestStream) {
164  BOOST_THROW_EXCEPTION(HttpException("HTTP connection error to " + m_url));
165  }
166 
167  requestStream << "GET " << url.getPath() << " HTTP/1.0\r\n";
168  requestStream << "Host: " << url.getHost() << ":" << url.getPort() << "\r\n";
169  requestStream << "Accept: */*\r\n";
170  requestStream << "Cache-Control: no-cache\r\n";
171  requestStream << "Connection: close\r\n\r\n";
172  requestStream.flush();
173 
174  std::string statusLine;
175  std::getline(requestStream, statusLine);
176  if (!requestStream) {
177  BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
178  }
179 
180  std::stringstream responseStream(statusLine);
181  std::string httpVersion;
182  responseStream >> httpVersion;
183  unsigned int statusCode;
184  responseStream >> statusCode;
185  std::string statusMessage;
186 
187  std::getline(responseStream, statusMessage);
188  if (!static_cast<bool>(requestStream) || httpVersion.substr(0, 5) != "HTTP/") {
189  throw HttpException("HTTP communication error");
190  }
191  if (statusCode != 200) {
192  boost::trim(statusMessage);
193  throw HttpException("HTTP request failed: " +
194  std::to_string(statusCode) + " " + statusMessage);
195  }
196  std::string header;
197  while (std::getline(requestStream, header) && header != "\r")
198  ;
199 
200  std::string hubHost;
201  requestStream >> hubHost;
202 
203  if (hubHost.empty()) {
204  throw HttpException("NDN-FCH did not return hub host");
205  }
206 
207  this->connectToHub("udp://" + hubHost);
208  }
209  catch (const std::runtime_error& e) {
210  m_nextStageOnFailure(std::string("Failed to find NDN router using NDN-FCH service (") + e.what() + ")");
211  }
212 }
213 
214 } // namespace autoconfig
215 } // namespace tools
216 } // namespace ndn
Copyright (c) 2014-2016, Regents of the University of California, Arizona Board of Regents...
Definition: nfd.hpp:35
void start() override
Start the stage.
STL namespace.
NextStageCallback m_nextStageOnFailure
Definition: base.hpp:114
void connectToHub(const std::string &uri)
Attempt to connect to local hub using the uri FaceUri.
Definition: base.cpp:41
std::function< void(const std::string &)> NextStageCallback
Callback to be called when the stage fails.
Definition: base.hpp:64
Base class for discovery stages.
Definition: base.hpp:48
NdnFchDiscovery(Face &face, KeyChain &keyChain, const std::string &url, const NextStageCallback &nextStageOnFailure)
Create stage to discover NDN hub using NDN-FCH protocol.