Apache Mesos
net.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_NET_HPP__
14 #define __STOUT_NET_HPP__
15 
16 #ifndef __WINDOWS__
17 #include <netdb.h>
18 #endif // __WINDOWS__
19 
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #ifndef __WINDOWS__
25 #include <arpa/inet.h>
26 #endif // __WINDOWS__
27 
28 #ifdef __APPLE__
29 #include <net/if.h>
30 #include <net/if_dl.h>
31 #include <net/if_types.h>
32 #endif // __APPLE__
33 
34 #ifdef __FreeBSD__
35 #include <ifaddrs.h>
36 #endif // __FreeBSD__
37 
38 // Note: Header grouping and ordering is considered before
39 // inclusion/exclusion by platform.
40 #ifndef __WINDOWS__
41 #include <sys/param.h>
42 #endif // __WINDOWS__
43 
44 #include <curl/curl.h>
45 
46 #include <iostream>
47 #include <set>
48 #include <string>
49 
50 #include <stout/bytes.hpp>
51 #include <stout/duration.hpp>
52 #include <stout/error.hpp>
53 #include <stout/ip.hpp>
54 #include <stout/option.hpp>
55 #include <stout/stringify.hpp>
56 #include <stout/try.hpp>
57 
58 #include <stout/os/int_fd.hpp>
59 #include <stout/os/close.hpp>
60 #include <stout/os/open.hpp>
61 
62 #ifdef __WINDOWS__
63 #include <stout/windows/net.hpp>
64 #else
65 #include <stout/posix/net.hpp>
66 #endif // __WINDOWS__
67 
68 
69 // Network utilities.
70 namespace net {
71 
72 // Initializes libraries that net:: functions depend on, in a
73 // thread-safe way. This does not have to be called explicitly by
74 // the user of any functions in question. They will call this
75 // themselves by need.
76 inline void initialize()
77 {
78  // We use a static struct variable to initialize in a thread-safe
79  // way, at least with respect to calls within net::*, since there
80  // is no way to guarantee that another library is not concurrently
81  // initializing CURL. Thread safety is provided by the fact that
82  // the value 'curl' should get constructed (i.e., the CURL
83  // constructor invoked) in a thread safe way (as of GCC 4.3 and
84  // required for C++11).
85  struct CURL
86  {
87  CURL()
88  {
89  // This is the only one function in libcurl that is not deemed
90  // thread-safe. (And it must be called at least once before any
91  // other libcurl function is used.)
92  curl_global_init(CURL_GLOBAL_ALL);
93  }
94  };
95 
96  static CURL curl;
97 }
98 
99 
100 // Downloads the header of the specified HTTP URL with a HEAD request
101 // and queries its "content-length" field. (Note that according to the
102 // HTTP specification there is no guarantee that this field contains
103 // any useful value.)
104 inline Try<Bytes> contentLength(const std::string& url)
105 {
106  initialize();
107 
108  CURL* curl = curl_easy_init();
109  if (curl == nullptr) {
110  curl_easy_cleanup(curl);
111  return Error("Failed to initialize libcurl");
112  }
113 
114  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
115  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
116  curl_easy_setopt(curl, CURLOPT_HEADER, 1);
117  curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
118 
119  CURLcode curlErrorCode = curl_easy_perform(curl);
120  if (curlErrorCode != 0) {
121  curl_easy_cleanup(curl);
122  return Error(curl_easy_strerror(curlErrorCode));
123  }
124 
125  double result;
126  curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &result);
127 
128  curl_easy_cleanup(curl);
129 
130  if (result < 0) {
131  return Error("No URL content-length available");
132  }
133 
134  return Bytes(uint64_t(result));
135 }
136 
137 
138 // Returns the HTTP response code resulting from attempting to
139 // download the specified HTTP or FTP URL into a file at the specified
140 // path. The `stall_timeout` parameter controls how long the download
141 // waits before aborting when the download speed keeps below 1 byte/sec.
143  const std::string& url,
144  const std::string& path,
145  const Option<Duration>& stall_timeout = None())
146 {
147  initialize();
148 
149  Try<int_fd> fd = os::open(
150  path,
151  O_CREAT | O_WRONLY | O_CLOEXEC,
153 
154  if (fd.isError()) {
155  return Error(fd.error());
156  }
157 
158  CURL* curl = curl_easy_init();
159 
160  if (curl == nullptr) {
161  curl_easy_cleanup(curl);
162  os::close(fd.get());
163  return Error("Failed to initialize libcurl");
164  }
165 
166  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
167  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, nullptr);
168  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
169 
170  // We don't bother introducing a `os::fdopen()` since this is the
171  // only place we use `fdopen()` in the entire codebase as of writing
172  // this comment.
173 #ifdef __WINDOWS__
174  // This explicitly allocates a CRT integer file descriptor, which
175  // when closed, also closes the underlying handle, so we do not call
176  // `CloseHandle()` (or `os::close()`).
177  const int crt = fd->crt();
178  // We open in "binary" mode on Windows to avoid line-ending translation.
179  FILE* file = ::_fdopen(crt, "wb");
180  if (file == nullptr) {
181  curl_easy_cleanup(curl);
182  // NOTE: This is not `os::close()` because we allocated a CRT int
183  // fd earlier.
184  ::_close(crt);
185  return ErrnoError("Failed to open file handle of '" + path + "'");
186  }
187 #else
188  FILE* file = ::fdopen(fd.get(), "w");
189  if (file == nullptr) {
190  curl_easy_cleanup(curl);
191  os::close(fd.get());
192  return ErrnoError("Failed to open file handle of '" + path + "'");
193  }
194 #endif // __WINDOWS__
195 
196  curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
197 
198  if (stall_timeout.isSome()) {
199  // Set the options to abort the download if the speed keeps below
200  // 1 byte/sec during the timeout. See:
201  // https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_LIMIT.html
202  // https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_TIME.html
203  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
204  curl_easy_setopt(
205  curl, CURLOPT_LOW_SPEED_TIME, static_cast<long>(stall_timeout->secs()));
206  }
207 
208  CURLcode curlErrorCode = curl_easy_perform(curl);
209  if (curlErrorCode != 0) {
210  curl_easy_cleanup(curl);
211  // NOTE: `fclose()` also closes the associated file descriptor, so
212  // we do not call `close()`.
213  ::fclose(file);
214  return Error(curl_easy_strerror(curlErrorCode));
215  }
216 
217  long code;
218  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
219  curl_easy_cleanup(curl);
220 
221  if (::fclose(file) != 0) {
222  return ErrnoError("Failed to close file handle of '" + path + "'");
223  }
224 
225  return Try<int>::some(code);
226 }
227 
228 } // namespace net {
229 
230 #endif // __STOUT_NET_HPP__
Definition: path.hpp:29
Definition: errorbase.hpp:36
const mode_t S_IRGRP
Definition: windows.hpp:313
T & get()&
Definition: try.hpp:80
Definition: check.hpp:33
const mode_t S_IWUSR
Definition: windows.hpp:306
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:35
Definition: errorbase.hpp:50
const mode_t S_IRUSR
Definition: windows.hpp:305
void initialize()
Definition: net.hpp:76
constexpr int O_CLOEXEC
Definition: open.hpp:41
URI file(const std::string &path)
Creates a file URI with the given path on the local host.
Definition: file.hpp:33
Try< Nothing > close(int fd)
Definition: close.hpp:24
Definition: ip.hpp:70
static Try error(const E &e)
Definition: try.hpp:43
static Try some(const T &t)
Definition: try.hpp:42
Definition: none.hpp:27
bool isError() const
Definition: try.hpp:78
Try< Bytes > contentLength(const std::string &url)
Definition: net.hpp:104
Definition: bytes.hpp:30
Try< int > download(const std::string &url, const std::string &path, const Option< Duration > &stall_timeout=None())
Definition: net.hpp:142
const mode_t S_IROTH
Definition: windows.hpp:321