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/open.hpp>
60 
61 #ifdef __WINDOWS__
62 #include <stout/windows/net.hpp>
63 #else
64 #include <stout/posix/net.hpp>
65 #endif // __WINDOWS__
66 
67 
68 // Network utilities.
69 namespace net {
70 
71 // Initializes libraries that net:: functions depend on, in a
72 // thread-safe way. This does not have to be called explicitly by
73 // the user of any functions in question. They will call this
74 // themselves by need.
75 inline void initialize()
76 {
77  // We use a static struct variable to initialize in a thread-safe
78  // way, at least with respect to calls within net::*, since there
79  // is no way to guarantee that another library is not concurrently
80  // initializing CURL. Thread safety is provided by the fact that
81  // the value 'curl' should get constructed (i.e., the CURL
82  // constructor invoked) in a thread safe way (as of GCC 4.3 and
83  // required for C++11).
84  struct CURL
85  {
86  CURL()
87  {
88  // This is the only one function in libcurl that is not deemed
89  // thread-safe. (And it must be called at least once before any
90  // other libcurl function is used.)
91  curl_global_init(CURL_GLOBAL_ALL);
92  }
93  };
94 
95  static CURL curl;
96 }
97 
98 
99 // Downloads the header of the specified HTTP URL with a HEAD request
100 // and queries its "content-length" field. (Note that according to the
101 // HTTP specification there is no guarantee that this field contains
102 // any useful value.)
103 inline Try<Bytes> contentLength(const std::string& url)
104 {
105  initialize();
106 
107  CURL* curl = curl_easy_init();
108  if (curl == nullptr) {
109  curl_easy_cleanup(curl);
110  return Error("Failed to initialize libcurl");
111  }
112 
113  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
114  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
115  curl_easy_setopt(curl, CURLOPT_HEADER, 1);
116  curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
117 
118  CURLcode curlErrorCode = curl_easy_perform(curl);
119  if (curlErrorCode != 0) {
120  curl_easy_cleanup(curl);
121  return Error(curl_easy_strerror(curlErrorCode));
122  }
123 
124  double result;
125  curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &result);
126 
127  curl_easy_cleanup(curl);
128 
129  if (result < 0) {
130  return Error("No URL content-length available");
131  }
132 
133  return Bytes(uint64_t(result));
134 }
135 
136 
137 // Returns the HTTP response code resulting from attempting to
138 // download the specified HTTP or FTP URL into a file at the specified
139 // path. The `stall_timeout` parameter controls how long the download
140 // waits before aborting when the download speed keeps below 1 byte/sec.
142  const std::string& url,
143  const std::string& path,
144  const Option<Duration>& stall_timeout = None())
145 {
146  initialize();
147 
148  Try<int_fd> fd = os::open(
149  path,
152 
153  if (fd.isError()) {
154  return Error(fd.error());
155  }
156 
157  CURL* curl = curl_easy_init();
158 
159  if (curl == nullptr) {
160  curl_easy_cleanup(curl);
161  os::close(fd.get());
162  return Error("Failed to initialize libcurl");
163  }
164 
165  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
166  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, nullptr);
167  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
168 
169  // We don't bother introducing a `os::fdopen` since this is the only place
170  // we use `fdopen` in the entire codebase as of writing this comment.
171 #ifdef __WINDOWS__
172  // We open in "binary" mode on Windows to avoid line-ending translation.
173  FILE* file = ::_fdopen(fd->crt(), "wb");
174 #else
175  FILE* file = ::fdopen(fd.get(), "w");
176 #endif
177  if (file == nullptr) {
178  curl_easy_cleanup(curl);
179  os::close(fd.get());
180  return ErrnoError("Failed to open file handle of '" + path + "'");
181  }
182  curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
183 
184  if (stall_timeout.isSome()) {
185  // Set the options to abort the download if the speed keeps below
186  // 1 byte/sec during the timeout. See:
187  // https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_LIMIT.html
188  // https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_TIME.html
189  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
190  curl_easy_setopt(
191  curl, CURLOPT_LOW_SPEED_TIME, static_cast<long>(stall_timeout->secs()));
192  }
193 
194  CURLcode curlErrorCode = curl_easy_perform(curl);
195  if (curlErrorCode != 0) {
196  curl_easy_cleanup(curl);
197  fclose(file);
198  return Error(curl_easy_strerror(curlErrorCode));
199  }
200 
201  long code;
202  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
203  curl_easy_cleanup(curl);
204 
205  if (fclose(file) != 0) {
206  return ErrnoError("Failed to close file handle of '" + path + "'");
207  }
208 
209  return Try<int>::some(code);
210 }
211 
212 } // namespace net {
213 
214 #endif // __STOUT_NET_HPP__
Definition: path.hpp:26
#define O_WRONLY
Definition: fcntl.hpp:26
Definition: errorbase.hpp:35
const mode_t S_IRGRP
Definition: windows.hpp:319
T & get()&
Definition: try.hpp:73
Definition: check.hpp:33
#define O_CLOEXEC
Definition: fcntl.hpp:33
const mode_t S_IWUSR
Definition: windows.hpp:312
Definition: errorbase.hpp:49
#define O_CREAT
Definition: fcntl.hpp:28
const mode_t S_IRUSR
Definition: windows.hpp:311
void initialize()
Definition: net.hpp:75
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:42
static Try some(const T &t)
Definition: try.hpp:41
Definition: none.hpp:27
bool isError() const
Definition: try.hpp:71
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:40
Try< Bytes > contentLength(const std::string &url)
Definition: net.hpp:103
Definition: bytes.hpp:30
Try< int > download(const std::string &url, const std::string &path, const Option< Duration > &stall_timeout=None())
Definition: net.hpp:141
const mode_t S_IROTH
Definition: windows.hpp:327