blob: 395d9f960b806596ea5074d8f52bcaa3c8c015a0 [file] [log] [blame]
Alexander Afanasyev2a001942016-12-14 18:18:41 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shif748a4e2017-07-05 23:41:48 +00002/*
Qi Zhao9ae88572017-05-23 10:54:01 -07003 * Copyright (c) 2014-2017, Regents of the University of California,
Alexander Afanasyev2a001942016-12-14 18:18:41 -08004 * 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"
Davide Pesaventoa997d292017-08-24 20:16:59 -040027
Alexander Afanasyev2a001942016-12-14 18:18:41 -080028#include <boost/algorithm/string.hpp>
Davide Pesaventoa997d292017-08-24 20:16:59 -040029#include <boost/regex.hpp>
30
31#include <sstream>
Alexander Afanasyev2a001942016-12-14 18:18:41 -080032
33namespace ndn {
34namespace tools {
35namespace autoconfig {
36
37/**
38 * A partial and specialized copy of ndn::FaceUri implementation
39 *
40 * Consider removing in favor of a library-provided URL parsing, if project
41 * includes such a library.
42 */
43class Url
44{
45public:
46 Url(const std::string& url)
47 : m_isValid(false)
48 {
49 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
50 boost::smatch protocolMatch;
51 if (!boost::regex_match(url, protocolMatch, protocolExp)) {
52 return;
53 }
54 m_scheme = protocolMatch[1];
55 const std::string& authority = protocolMatch[3];
56 m_path = protocolMatch[4];
57
58 // pattern for IPv6 address enclosed in [ ], with optional port number
59 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
60 // pattern for IPv4-mapped IPv6 address, with optional port number
61 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
62 // pattern for IPv4/hostname/fd/ifname, with optional port number
63 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
64
65 if (authority.empty()) {
66 // UNIX, internal
67 }
68 else {
69 boost::smatch match;
70 bool isV6 = boost::regex_match(authority, match, v6Exp);
71 if (isV6 ||
72 boost::regex_match(authority, match, v4MappedV6Exp) ||
73 boost::regex_match(authority, match, v4HostExp)) {
74 m_host = match[1];
75 m_port = match[2];
76 }
77 else {
78 return;
79 }
80 }
81 if (m_port.empty()) {
82 m_port = "80";
83 }
84 if (m_path.empty()) {
85 m_path = "/";
86 }
87 m_isValid = true;
88 }
89
90 bool
91 isValid() const
92 {
93 return m_isValid;
94 }
95
96 const std::string&
97 getScheme() const
98 {
99 return m_scheme;
100 }
101
102 const std::string&
103 getHost() const
104 {
105 return m_host;
106 }
107
108 const std::string&
109 getPort() const
110 {
111 return m_port;
112 }
113
114 const std::string&
115 getPath() const
116 {
117 return m_path;
118 }
119
120private:
121 bool m_isValid;
122 std::string m_scheme;
123 std::string m_host;
124 std::string m_port;
125 std::string m_path;
126};
127
128class HttpException : public std::runtime_error
129{
130public:
131 explicit
132 HttpException(const std::string& what)
133 : std::runtime_error(what)
134 {
135 }
136};
137
Junxiao Shicb766862017-07-07 22:21:04 +0000138NdnFchDiscovery::NdnFchDiscovery(const std::string& url)
139 : m_url(url)
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800140{
141}
142
143void
Junxiao Shicb766862017-07-07 22:21:04 +0000144NdnFchDiscovery::doStart()
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800145{
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/") {
Qi Zhao9ae88572017-05-23 10:54:01 -0700189 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800190 }
191 if (statusCode != 200) {
192 boost::trim(statusMessage);
Qi Zhao9ae88572017-05-23 10:54:01 -0700193 BOOST_THROW_EXCEPTION(HttpException("HTTP request failed: " + std::to_string(statusCode) + " " + statusMessage));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800194 }
195 std::string header;
196 while (std::getline(requestStream, header) && header != "\r")
197 ;
198
199 std::string hubHost;
200 requestStream >> hubHost;
201
202 if (hubHost.empty()) {
Qi Zhao9ae88572017-05-23 10:54:01 -0700203 BOOST_THROW_EXCEPTION(HttpException("NDN-FCH did not return hub host"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800204 }
205
Junxiao Shicb766862017-07-07 22:21:04 +0000206 this->provideHubFaceUri("udp://" + hubHost);
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800207 }
208 catch (const std::runtime_error& e) {
Junxiao Shicb766862017-07-07 22:21:04 +0000209 this->fail(e.what());
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800210 }
211}
212
213} // namespace autoconfig
214} // namespace tools
215} // namespace ndn