Apache Mesos
encoder.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 __ENCODER_HPP__
14 #define __ENCODER_HPP__
15 
16 #include <stdint.h>
17 #include <time.h>
18 
19 #include <limits>
20 #include <map>
21 #include <sstream>
22 
23 #include <process/http.hpp>
24 #include <process/process.hpp>
25 
26 #include <stout/foreach.hpp>
27 #include <stout/gzip.hpp>
28 #include <stout/hashmap.hpp>
29 #include <stout/numify.hpp>
30 #include <stout/os.hpp>
31 
32 
33 namespace process {
34 
35 const uint32_t GZIP_MINIMUM_BODY_LENGTH = 1024;
36 
37 
38 class Encoder
39 {
40 public:
41  enum Kind
42  {
45  };
46 
47  Encoder() = default;
48 
49  virtual ~Encoder() {}
50 
51  virtual Kind kind() const = 0;
52 
53  virtual void backup(size_t length) = 0;
54 
55  virtual size_t remaining() const = 0;
56 };
57 
58 
59 class DataEncoder : public Encoder
60 {
61 public:
62  DataEncoder(const std::string& _data)
63  : data(_data), index(0) {}
64 
65  DataEncoder(std::string&& _data)
66  : data(std::move(_data)), index(0) {}
67 
68  ~DataEncoder() override {}
69 
70  Kind kind() const override
71  {
72  return Encoder::DATA;
73  }
74 
75  virtual const char* next(size_t* length)
76  {
77  size_t temp = index;
78  index = data.size();
79  *length = data.size() - temp;
80  return data.data() + temp;
81  }
82 
83  void backup(size_t length) override
84  {
85  if (index >= length) {
86  index -= length;
87  }
88  }
89 
90  size_t remaining() const override
91  {
92  return data.size() - index;
93  }
94 
95 private:
96  const std::string data;
97  size_t index;
98 };
99 
100 
102 {
103 public:
104  MessageEncoder(const Message& message)
105  : DataEncoder(encode(message)) {}
106 
107  static std::string encode(const Message& message)
108  {
109  std::ostringstream out;
110 
111  out << "POST ";
112  // Nothing keeps the 'id' component of a PID from being an empty
113  // string which would create a malformed path that has two
114  // '//' unless we check for it explicitly.
115  // TODO(benh): Make the 'id' part of a PID optional so when it's
116  // missing it's clear that we're simply addressing an ip:port.
117  if (message.to.id != "") {
118  out << "/" << message.to.id;
119  }
120 
121  out << "/" << message.name << " HTTP/1.1\r\n"
122  << "User-Agent: libprocess/" << message.from << "\r\n"
123  << "Libprocess-From: " << message.from << "\r\n"
124  << "Connection: Keep-Alive\r\n"
125  << "Host: \r\n";
126 
127  if (message.body.size() > 0) {
128  out << "Transfer-Encoding: chunked\r\n\r\n"
129  << std::hex << message.body.size() << "\r\n";
130  out.write(message.body.data(), message.body.size());
131  out << "\r\n"
132  << "0\r\n"
133  << "\r\n";
134  } else {
135  out << "\r\n";
136  }
137 
138  return out.str();
139  }
140 };
141 
142 
144 {
145 public:
147  const http::Response& response,
148  const http::Request& request)
149  : DataEncoder(encode(response, request)) {}
150 
151  static std::string encode(
152  const http::Response& response,
153  const http::Request& request)
154  {
155  std::ostringstream out;
156 
157  // TODO(benh): Check version?
158 
159  out << "HTTP/1.1 " << response.status << "\r\n";
160 
161  auto headers = response.headers;
162 
163  // HTTP 1.1 requires the "Date" header. In the future once we
164  // start checking the version (above) then we can conditionally
165  // add this header, but for now, we always do.
166  time_t rawtime;
167  time(&rawtime);
168 
169  char date[256];
170 
171  tm tm_;
172  PCHECK(os::gmtime_r(&rawtime, &tm_) != nullptr)
173  << "Failed to convert the current time to a tm struct "
174  << "using os::gmtime_r()";
175 
176  // TODO(benh): Check return code of strftime!
177  strftime(date, 256, "%a, %d %b %Y %H:%M:%S GMT", &tm_);
178 
179  headers["Date"] = date;
180 
181  // Should we compress this response?
182  std::string body = response.body;
183 
184  if (response.type == http::Response::BODY &&
185  response.body.length() >= GZIP_MINIMUM_BODY_LENGTH &&
186  !headers.contains("Content-Encoding") &&
187  request.acceptsEncoding("gzip")) {
188  Try<std::string> compressed = gzip::compress(body);
189  if (compressed.isError()) {
190  LOG(WARNING) << "Failed to gzip response body: " << compressed.error();
191  } else {
192  body = std::move(compressed.get());
193 
194  headers["Content-Length"] = stringify(body.length());
195  headers["Content-Encoding"] = "gzip";
196  }
197  }
198 
199  foreachpair (const std::string& key, const std::string& value, headers) {
200  out << key << ": " << value << "\r\n";
201  }
202 
203  // Add a Content-Length header if the response is of type "none"
204  // or "body" and no Content-Length header has been supplied.
205  if (response.type == http::Response::NONE &&
206  !headers.contains("Content-Length")) {
207  out << "Content-Length: 0\r\n";
208  } else if (response.type == http::Response::BODY &&
209  !headers.contains("Content-Length")) {
210  out << "Content-Length: " << body.size() << "\r\n";
211  }
212 
213  // Use a CRLF to mark end of headers.
214  out << "\r\n";
215 
216  // Add the body if necessary.
217  if (response.type == http::Response::BODY) {
218  // If the Content-Length header was supplied, only write as much data
219  // as the length specifies.
220  Result<uint32_t> length = numify<uint32_t>(headers.get("Content-Length"));
221  if (length.isSome() && length.get() <= body.length()) {
222  out.write(body.data(), length.get());
223  } else {
224  out.write(body.data(), body.size());
225  }
226  }
227 
228  return out.str();
229  }
230 };
231 
232 
233 class FileEncoder : public Encoder
234 {
235 public:
236  FileEncoder(int_fd _fd, size_t _size)
237  : fd(_fd), size(static_cast<off_t>(_size)), index(0)
238  {
239  // NOTE: For files, we expect the size to be derived from `stat`-ing
240  // the file. The `struct stat` returns the size in `off_t` form,
241  // meaning that it is a programmer error to construct the `FileEncoder`
242  // with a size greater the max value of `off_t`.
243  CHECK_LE(_size, static_cast<size_t>(std::numeric_limits<off_t>::max()));
244  }
245 
246  ~FileEncoder() override
247  {
248  CHECK_SOME(os::close(fd)) << "Failed to close file descriptor";
249  }
250 
251  Kind kind() const override
252  {
253  return Encoder::FILE;
254  }
255 
256  virtual int_fd next(off_t* offset, size_t* length)
257  {
258  off_t temp = index;
259  index = size;
260  *offset = temp;
261  *length = size - temp;
262  return fd;
263  }
264 
265  void backup(size_t length) override
266  {
267  if (index >= static_cast<off_t>(length)) {
268  index -= static_cast<off_t>(length);
269  }
270  }
271 
272  size_t remaining() const override
273  {
274  return static_cast<size_t>(size - index);
275  }
276 
277 private:
278  int_fd fd;
279  off_t size;
280  off_t index;
281 };
282 
283 } // namespace process {
284 
285 #endif // __ENCODER_HPP__
size_t remaining() const override
Definition: encoder.hpp:272
virtual ~Encoder()
Definition: encoder.hpp:49
FileEncoder(int_fd _fd, size_t _size)
Definition: encoder.hpp:236
DataEncoder(std::string &&_data)
Definition: encoder.hpp:65
Encoder()=default
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:121
Kind kind() const override
Definition: encoder.hpp:251
Future< Response > request(const Request &request, bool streamedResponse=false)
Asynchronously sends an HTTP request to the process and returns the HTTP response once the entire res...
T & get()&
Definition: try.hpp:73
bool acceptsEncoding(const std::string &encoding) const
Returns whether the encoding is considered acceptable in the response.
Definition: check.hpp:33
std::string status
Definition: http.hpp:624
Definition: message.hpp:22
DataEncoder(const std::string &_data)
Definition: encoder.hpp:62
~DataEncoder() override
Definition: encoder.hpp:68
virtual Kind kind() const =0
virtual int_fd next(off_t *offset, size_t *length)
Definition: encoder.hpp:256
Definition: type_utils.hpp:510
Definition: http.hpp:651
void backup(size_t length) override
Definition: encoder.hpp:83
Definition: check.hpp:30
Kind
Definition: encoder.hpp:41
Definition: http.hpp:520
Try< std::string > compress(const std::string &decompressed, int level=Z_DEFAULT_COMPRESSION)
Definition: gzip.hpp:179
static std::string encode(const http::Response &response, const http::Request &request)
Definition: encoder.hpp:151
Definition: encoder.hpp:143
virtual void backup(size_t length)=0
Definition: http.hpp:652
UPID to
Definition: message.hpp:26
Try< Nothing > encode(const Netlink< struct rtnl_cls > &cls, const Classifier &classifier)
UPID from
Definition: message.hpp:25
#define CHECK_SOME(expression)
Definition: check.hpp:50
std::string body
Definition: http.hpp:657
Try< Nothing > close(int fd)
Definition: close.hpp:24
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:208
Definition: encoder.hpp:38
virtual const char * next(size_t *length)
Definition: encoder.hpp:75
void backup(size_t length) override
Definition: encoder.hpp:265
size_t remaining() const override
Definition: encoder.hpp:90
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
const T & get() const
Definition: result.hpp:115
~FileEncoder() override
Definition: encoder.hpp:246
static Try error(const E &e)
Definition: try.hpp:42
Definition: encoder.hpp:43
virtual size_t remaining() const =0
Definition: encoder.hpp:233
bool isError() const
Definition: try.hpp:71
static std::string encode(const Message &message)
Definition: encoder.hpp:107
Definition: executor.hpp:48
HttpResponseEncoder(const http::Response &response, const http::Request &request)
Definition: encoder.hpp:146
Definition: http.hpp:599
Definition: encoder.hpp:59
std::string body
Definition: message.hpp:27
MessageEncoder(const Message &message)
Definition: encoder.hpp:104
std::string temp()
Definition: temp.hpp:27
bool isSome() const
Definition: result.hpp:111
Definition: encoder.hpp:101
struct process::UPID::ID id
int int_fd
Definition: int_fd.hpp:35
std::string stringify(int flags)
Headers headers
Definition: http.hpp:626
enum process::http::Response::@4 type
std::string name
Definition: message.hpp:24
const uint32_t GZIP_MINIMUM_BODY_LENGTH
Definition: encoder.hpp:35
Try< std::vector< Value > > time(const std::string &hierarchy, const std::string &cgroup)
Definition: encoder.hpp:44
tm * gmtime_r(const time_t *timep, tm *result)
Definition: os.hpp:431
Kind kind() const override
Definition: encoder.hpp:70