Apache Mesos
http.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 __PROCESS_HTTP_HPP__
14 #define __PROCESS_HTTP_HPP__
15 
16 #include <ctype.h>
17 #include <stdint.h>
18 
19 #include <atomic>
20 #include <initializer_list>
21 #include <iosfwd>
22 #include <memory>
23 #include <queue>
24 #include <string>
25 #include <vector>
26 
27 #include <boost/functional/hash.hpp>
28 
29 #include <process/address.hpp>
30 #include <process/future.hpp>
31 #include <process/owned.hpp>
32 #include <process/pid.hpp>
33 #include <process/socket.hpp>
34 
35 #include <stout/error.hpp>
36 #include <stout/hashmap.hpp>
37 #include <stout/ip.hpp>
38 #include <stout/json.hpp>
39 #include <stout/jsonify.hpp>
40 #include <stout/none.hpp>
41 #include <stout/nothing.hpp>
42 #include <stout/option.hpp>
43 #include <stout/stringify.hpp>
44 #include <stout/strings.hpp>
45 #include <stout/try.hpp>
46 
47 namespace process {
48 
49 // Forward declaration to break cyclic dependency.
50 template <typename T>
51 class Future;
52 
53 namespace http {
54 
55 enum class Scheme {
56  HTTP,
57 #ifdef USE_SSL_SOCKET
58  HTTPS
59 #endif
60 };
61 
62 
63 namespace authentication {
64 
65 class Authenticator;
66 
67 struct Principal;
68 
77  const std::string& realm,
78  Owned<Authenticator> authenticator);
79 
80 
87 Future<Nothing> unsetAuthenticator(const std::string& realm);
88 
89 } // namespace authentication {
90 
91 // Forward declaration.
92 struct Request;
93 
94 namespace authorization {
95 
96 // The `AuthorizationCallbacks` type is used for a set of authorization
97 // callbacks used by libprocess to authorize HTTP endpoints. The key of the map
98 // contains the endpoint's path, while the value contains the callback.
99 typedef hashmap<std::string,
100  lambda::function<process::Future<bool>(
101  const Request,
104 
105 
106 // Set authorization callbacks for HTTP endpoints. These can be used to call out
107 // to an external, application-level authorizer. The callbacks should accept an
108 // HTTP request and an optional principal, and they should return a
109 // `Future<bool>` representing whether or not authorization was successful.
111 
112 
113 // Remove any authorization callbacks which were previously installed in
114 // libprocess.
115 void unsetCallbacks();
116 
117 } // namespace authorization {
118 
119 // Status code reason strings, from the HTTP1.1 RFC:
120 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
122 
123 // Represents a Uniform Resource Locator:
124 // scheme://domain|ip:port/path?query#fragment
125 //
126 // This is actually a URI-reference (see 4.1 of RFC 3986).
127 //
128 // TODO(bmahler): The default port should depend on the scheme!
129 struct URL
130 {
131  URL() = default;
132 
133  URL(const std::string& _scheme,
134  const std::string& _domain,
135  const uint16_t _port = 80,
136  const std::string& _path = "/",
137  const hashmap<std::string, std::string>& _query =
139  const Option<std::string>& _fragment = None())
140  : scheme(_scheme),
141  domain(_domain),
142  port(_port),
143  path(_path),
144  query(_query),
145  fragment(_fragment) {}
146 
147  URL(const std::string& _scheme,
148  const net::IP& _ip,
149  const uint16_t _port = 80,
150  const std::string& _path = "/",
151  const hashmap<std::string, std::string>& _query =
153  const Option<std::string>& _fragment = None())
154  : scheme(_scheme),
155  ip(_ip),
156  port(_port),
157  path(_path),
158  query(_query),
159  fragment(_fragment) {}
160 
161  static Try<URL> parse(const std::string& urlString);
162 
167  bool isAbsolute() const;
168 
170 
171  // TODO(benh): Consider using unrestricted union for 'domain' and 'ip'.
175  std::string path;
178 };
179 
180 
181 std::ostream& operator<<(std::ostream& stream, const URL& url);
182 
183 
185 {
186  size_t operator()(const std::string& key) const
187  {
188  size_t seed = 0;
189  foreach (char c, key) {
190  boost::hash_combine(seed, ::tolower(c));
191  }
192  return seed;
193  }
194 };
195 
196 
198 {
199  bool operator()(const std::string& left, const std::string& right) const
200  {
201  if (left.size() != right.size()) {
202  return false;
203  }
204  for (size_t i = 0; i < left.size(); ++i) {
205  if (::tolower(left[i]) != ::tolower(right[i])) {
206  return false;
207  }
208  }
209  return true;
210  }
211 };
212 
213 
214 struct Status
215 {
216  static const uint16_t CONTINUE;
217  static const uint16_t SWITCHING_PROTOCOLS;
218  static const uint16_t OK;
219  static const uint16_t CREATED;
220  static const uint16_t ACCEPTED;
221  static const uint16_t NON_AUTHORITATIVE_INFORMATION;
222  static const uint16_t NO_CONTENT;
223  static const uint16_t RESET_CONTENT;
224  static const uint16_t PARTIAL_CONTENT;
225  static const uint16_t MULTIPLE_CHOICES;
226  static const uint16_t MOVED_PERMANENTLY;
227  static const uint16_t FOUND;
228  static const uint16_t SEE_OTHER;
229  static const uint16_t NOT_MODIFIED;
230  static const uint16_t USE_PROXY;
231  static const uint16_t TEMPORARY_REDIRECT;
232  static const uint16_t BAD_REQUEST;
233  static const uint16_t UNAUTHORIZED;
234  static const uint16_t PAYMENT_REQUIRED;
235  static const uint16_t FORBIDDEN;
236  static const uint16_t NOT_FOUND;
237  static const uint16_t METHOD_NOT_ALLOWED;
238  static const uint16_t NOT_ACCEPTABLE;
239  static const uint16_t PROXY_AUTHENTICATION_REQUIRED;
240  static const uint16_t REQUEST_TIMEOUT;
241  static const uint16_t CONFLICT;
242  static const uint16_t GONE;
243  static const uint16_t LENGTH_REQUIRED;
244  static const uint16_t PRECONDITION_FAILED;
245  static const uint16_t REQUEST_ENTITY_TOO_LARGE;
246  static const uint16_t REQUEST_URI_TOO_LARGE;
247  static const uint16_t UNSUPPORTED_MEDIA_TYPE;
248  static const uint16_t REQUESTED_RANGE_NOT_SATISFIABLE;
249  static const uint16_t EXPECTATION_FAILED;
250  static const uint16_t INTERNAL_SERVER_ERROR;
251  static const uint16_t NOT_IMPLEMENTED;
252  static const uint16_t BAD_GATEWAY;
253  static const uint16_t SERVICE_UNAVAILABLE;
254  static const uint16_t GATEWAY_TIMEOUT;
255  static const uint16_t HTTP_VERSION_NOT_SUPPORTED;
256 
257  static std::string string(uint16_t code);
258 };
259 
260 
261 // Represents an asynchronous in-memory unbuffered Pipe, currently
262 // used for streaming HTTP responses via chunked encoding. Note that
263 // being an in-memory pipe means that this cannot be used across OS
264 // processes.
265 //
266 // Much like unix pipes, data is read until end-of-file is
267 // encountered; this occurs when the write-end of the pipe is
268 // closed and there is no outstanding data left to read.
269 //
270 // Unlike unix pipes, if the read-end of the pipe is closed before
271 // the write-end is closed, rather than receiving SIGPIPE or EPIPE
272 // during a write, the writer is notified via a future. This future
273 // is discarded if the write-end is closed first.
274 //
275 // No buffering means that each non-empty write to the pipe will
276 // correspond to to an equivalent read from the pipe, and the
277 // reader must "keep up" with the writer in order to avoid
278 // unbounded memory growth.
279 //
280 // The writer can induce a failure on the reader in order to signal
281 // that an error has occurred. For example, if we are receiving a
282 // response but a disconnection occurs before the response is
283 // completed, we want the reader to detect that a disconnection
284 // occurred!
285 //
286 // TODO(bmahler): Consider aggregating writes into larger reads to
287 // help the reader keep up (a process::Stream abstraction with
288 // backpressure would obviate the need for this).
289 //
290 // TODO(bmahler): Add a more general process::Stream<T> abstraction
291 // to represent asynchronous finite/infinite streams (possibly
292 // with "backpressure" on the writer). This is broadly useful
293 // (e.g. allocator can expose Stream<Allocation>, http::Pipe
294 // becomes Stream<string>, process::Queue<T> is just an infinite
295 // Stream<T> (i.e. completion and error semantics hidden)).
296 class Pipe
297 {
298 private:
299  struct Data; // Forward declaration.
300 
301 public:
302  class Reader
303  {
304  public:
305  // Returns data written to the pipe.
306  // Returns an empty read when end-of-file is reached.
307  // Returns Failure if the writer failed, or the read-end
308  // is closed.
310 
311  // Performs a series of asynchronous reads, until EOF is reached.
312  // Returns the concatenated result of the reads.
313  // Returns Failure if the writer failed, or the read-end
314  // is closed.
315  Future<std::string> readAll();
316 
317  // Closing the read-end of the pipe before the write-end closes
318  // or fails will notify the writer that the reader is no longer
319  // interested. Returns false if the read-end was already closed.
320  bool close();
321 
322  // Comparison operators useful for checking connection equality.
323  bool operator==(const Reader& other) const { return data == other.data; }
324  bool operator!=(const Reader& other) const { return !(*this == other); }
325 
326  private:
327  friend class Pipe;
328 
329  enum State
330  {
331  OPEN,
332  CLOSED,
333  };
334 
335  explicit Reader(const std::shared_ptr<Data>& _data) : data(_data) {}
336 
337  std::shared_ptr<Data> data;
338  };
339 
340  class Writer
341  {
342  public:
343  // Returns false if the data could not be written because
344  // either end of the pipe was already closed. Note that an
345  // empty write has no effect.
346  bool write(std::string s);
347 
348  // Closing the write-end of the pipe will send end-of-file
349  // to the reader. Returns false if the write-end of the pipe
350  // was already closed or failed.
351  bool close();
352 
353  // Closes the write-end of the pipe but sends a failure
354  // to the reader rather than end-of-file. Returns false
355  // if the write-end of the pipe was already closed or failed.
356  bool fail(const std::string& message);
357 
358  // Returns Nothing when the read-end of the pipe is closed
359  // before the write-end is closed, which means the reader
360  // was unable to continue reading!
361  Future<Nothing> readerClosed() const;
362 
363  // Comparison operators useful for checking connection equality.
364  bool operator==(const Writer& other) const { return data == other.data; }
365  bool operator!=(const Writer& other) const { return !(*this == other); }
366  private:
367  friend class Pipe;
368 
369  enum State
370  {
371  OPEN,
372  CLOSED,
373  FAILED,
374  };
375 
376  explicit Writer(const std::shared_ptr<Data>& _data) : data(_data) {}
377 
378  std::shared_ptr<Data> data;
379  };
380 
381  Pipe() : data(new Data()) {}
382 
383  Reader reader() const;
384  Writer writer() const;
385 
386  // Comparison operators useful for checking connection equality.
387  bool operator==(const Pipe& other) const { return data == other.data; }
388  bool operator!=(const Pipe& other) const { return !(*this == other); }
389 private:
390  struct Data
391  {
392  Data()
393  : readEnd(Reader::OPEN),
394  writeEnd(Writer::OPEN) {}
395 
396  // Rather than use a process to serialize access to the pipe's
397  // internal data we use a 'std::atomic_flag'.
398  std::atomic_flag lock = ATOMIC_FLAG_INIT;
399 
400  Reader::State readEnd;
401  Writer::State writeEnd;
402 
403  // Represents readers waiting for data from the pipe.
404  std::queue<Owned<Promise<std::string>>> reads;
405 
406  // Represents unread writes in the pipe. Note that we omit
407  // empty strings as they serve as a signal for end-of-file.
408  std::queue<std::string> writes;
409 
410  // Signals when the read-end is closed before the write-end.
411  Promise<Nothing> readerClosure;
412 
413  // Failure reason when the 'writeEnd' is FAILED.
414  Option<Failure> failure;
415  };
416 
417  std::shared_ptr<Data> data;
418 };
419 
420 
421 namespace header {
422 
423 // https://tools.ietf.org/html/rfc2617.
425 {
426 public:
427  static constexpr const char* NAME = "WWW-Authenticate";
428 
430  const std::string& authScheme,
431  const hashmap<std::string, std::string>& authParam)
432  : authScheme_(authScheme),
433  authParam_(authParam) {}
434 
435  static Try<WWWAuthenticate> create(const std::string& value);
436 
437  std::string authScheme();
439 
440 private:
441  // According to RFC, HTTP/1.1 server may return multiple challenges
442  // with a 401 (Authenticate) response. Each challenage is in the
443  // format of 'auth-scheme 1*SP 1#auth-param' and each challenage may
444  // use a different auth-scheme.
445  // https://tools.ietf.org/html/rfc2617#section-4.6
446  //
447  // TODO(gilbert): We assume there is only one authenticate challenge.
448  // Multiple challenges should be supported as well.
449  std::string authScheme_;
451 };
452 
453 } // namespace header {
454 
455 
456 class Headers : public hashmap<
457  std::string,
458  std::string,
459  CaseInsensitiveHash,
460  CaseInsensitiveEqual>
461 {
462 public:
463  Headers() {}
464 
465  Headers(const std::map<std::string, std::string>& map)
466  : hashmap<
467  std::string,
468  std::string,
470  CaseInsensitiveEqual>(map) {}
471 
472  Headers(std::map<std::string, std::string>&& map)
473  : hashmap<
474  std::string,
475  std::string,
478 
479  Headers(std::initializer_list<std::pair<std::string, std::string>> list)
480  : hashmap<
481  std::string,
482  std::string,
485 
486  template <typename T>
487  Result<T> get() const
488  {
489  Option<std::string> value = get(T::NAME);
490  if (value.isNone()) {
491  return None();
492  }
493  Try<T> header = T::create(value.get());
494  if (header.isError()) {
495  return Error(header.error());
496  }
497  return header.get();
498  }
499 
500  Option<std::string> get(const std::string& key) const
501  {
502  return hashmap<
503  std::string,
504  std::string,
507  }
508 
509  Headers operator+(const Headers& that) const
510  {
511  Headers result = *this;
512  result.insert(that.begin(), that.end());
513  return result;
514  }
515 };
516 
517 
518 struct Request
519 {
521  : keepAlive(false), type(BODY) {}
522 
523  std::string method;
524 
525  // TODO(benh): Add major/minor version.
526 
527  // For client requests, the URL should be a URI.
528  // For server requests, the URL may be a URI or a relative reference.
530 
532 
533  // TODO(bmahler): Ensure this is consistent with the 'Connection'
534  // header; perhaps make this a function that checks the header.
535  //
536  // TODO(anand): Ideally, this could default to 'true' since
537  // persistent connections are the default since HTTP 1.1.
538  // Perhaps, we need to go from `keepAlive` to `closeConnection`
539  // to reflect the header more accurately, and to have an
540  // intuitive default of false.
541  //
542  // Default: false.
543  bool keepAlive;
544 
545  // For server requests, this contains the address of the client.
546  // Note that this may correspond to a proxy or load balancer address.
548 
549  // Clients can choose to provide the entire body at once
550  // via BODY or can choose to stream the body over to the
551  // server via PIPE.
552  //
553  // Default: BODY.
554  enum
555  {
557  PIPE
558  } type;
559 
560  // TODO(bmahler): Add a 'query' field which contains both
561  // the URL query and the parsed form data from the body.
562 
563  std::string body;
565 
570  bool acceptsEncoding(const std::string& encoding) const;
571 
576  bool acceptsMediaType(const std::string& mediaType) const;
577 
584  bool acceptsMediaType(
585  const std::string& name,
586  const std::string& mediaType) const;
587 
588 private:
589  bool _acceptsMediaType(
591  const std::string& mediaType) const;
592 };
593 
594 
595 struct Response
596 {
598  : type(NONE)
599  {}
600 
601  Response(uint16_t _code)
602  : type(NONE), code(_code)
603  {
604  status = Status::string(code);
605  }
606 
607  explicit Response(
608  const std::string& _body,
609  uint16_t _code,
610  const std::string& contentType = "text/plain; charset=utf-8")
611  : type(BODY),
612  body(_body),
613  code(_code)
614  {
615  headers["Content-Length"] = stringify(body.size());
616  headers["Content-Type"] = contentType;
617  status = Status::string(code);
618  }
619 
620  // TODO(benh): Add major/minor version.
621  std::string status;
622 
624 
625  // Either provide a 'body', an absolute 'path' to a file, or a
626  // 'pipe' for streaming a response. Distinguish between the cases
627  // using 'type' below.
628  //
629  // BODY: Uses 'body' as the body of the response. These may be
630  // encoded using gzip for efficiency, if 'Content-Encoding' is not
631  // already specified.
632  //
633  // PATH: Attempts to perform a 'sendfile' operation on the file
634  // found at 'path'.
635  //
636  // PIPE: Splices data from the Pipe 'reader' using a "chunked"
637  // 'Transfer-Encoding'. The writer uses a Pipe::Writer to
638  // perform writes and to detect a closed read-end of the Pipe
639  // (i.e. nobody is listening any longer). Once the writer is
640  // finished, it will close its end of the pipe to signal end
641  // of file to the Reader.
642  //
643  // In all cases (BODY, PATH, PIPE), you are expected to properly
644  // specify the 'Content-Type' header, but the 'Content-Length' and
645  // or 'Transfer-Encoding' headers will be filled in for you.
646  enum
647  {
651  PIPE
652  } type;
653 
654  std::string body;
655  std::string path;
657 
658  uint16_t code;
659 };
660 
661 
662 struct OK : Response
663 {
664  OK() : Response(Status::OK) {}
665 
666  explicit OK(const char* body)
667  : Response(std::string(body), Status::OK) {}
668 
669  explicit OK(const std::string& body) : Response(body, Status::OK) {}
670 
671  explicit OK(const std::string& body, const std::string& contentType)
672  : Response(body, Status::OK, contentType) {}
673 
674  OK(const JSON::Value& value, const Option<std::string>& jsonp = None());
675 
676  OK(JSON::Proxy&& value, const Option<std::string>& jsonp = None());
677 };
678 
679 
681 {
682  Accepted() : Response(Status::ACCEPTED) {}
683 
684  explicit Accepted(const std::string& body)
685  : Response(body, Status::ACCEPTED) {}
686 };
687 
688 
690 {
691  explicit TemporaryRedirect(const std::string& url)
692  : Response(Status::TEMPORARY_REDIRECT)
693  {
694  headers["Location"] = url;
695  }
696 };
697 
698 
700 {
701  BadRequest() : Response(Status::BAD_REQUEST) {}
702 
703  explicit BadRequest(const std::string& body)
704  : Response(body, Status::BAD_REQUEST) {}
705 };
706 
707 
709 {
710  explicit Unauthorized(const std::vector<std::string>& challenges)
711  : Response(Status::UNAUTHORIZED)
712  {
713  // TODO(arojas): Many HTTP client implementations do not support
714  // multiple challenges within a single 'WWW-Authenticate' header.
715  // Once MESOS-3306 is fixed, we can use multiple entries for the
716  // same header.
717  headers["WWW-Authenticate"] = strings::join(", ", challenges);
718  }
719 
721  const std::vector<std::string>& challenges,
722  const std::string& body)
723  : Response(body, Status::UNAUTHORIZED)
724  {
725  // TODO(arojas): Many HTTP client implementations do not support
726  // multiple challenges within a single 'WWW-Authenticate' header.
727  // Once MESOS-3306 is fixed, we can use multiple entries for the
728  // same header.
729  headers["WWW-Authenticate"] = strings::join(", ", challenges);
730  }
731 };
732 
733 
735 {
736  Forbidden() : Response(Status::FORBIDDEN) {}
737 
738  explicit Forbidden(const std::string& body)
739  : Response(body, Status::FORBIDDEN) {}
740 };
741 
742 
744 {
745  NotFound() : Response(Status::NOT_FOUND) {}
746 
747  explicit NotFound(const std::string& body)
748  : Response(body, Status::NOT_FOUND) {}
749 };
750 
751 
753 {
754  // According to RFC 2616, "An Allow header field MUST be present in a
755  // 405 (Method Not Allowed) response".
756 
758  const std::initializer_list<std::string>& allowedMethods)
759  : Response(Status::METHOD_NOT_ALLOWED)
760  {
761  headers["Allow"] = strings::join(", ", allowedMethods);
762  }
763 
765  const std::initializer_list<std::string>& allowedMethods,
766  const std::string& requestMethod)
767  : Response(
768  constructBody(allowedMethods, requestMethod),
769  Status::METHOD_NOT_ALLOWED)
770  {
771  headers["Allow"] = strings::join(", ", allowedMethods);
772  }
773 
774 private:
775  static std::string constructBody(
776  const std::initializer_list<std::string>& allowedMethods,
777  const std::string& requestMethod)
778  {
779  return "Expecting one of { '" + strings::join("', '", allowedMethods) +
780  "' }, but received '" + requestMethod + "'";
781  }
782 };
783 
784 
786 {
787  NotAcceptable() : Response(Status::NOT_ACCEPTABLE) {}
788 
789  explicit NotAcceptable(const std::string& body)
790  : Response(body, Status::NOT_ACCEPTABLE) {}
791 };
792 
793 
795 {
796  Conflict() : Response(Status::CONFLICT) {}
797 
798  explicit Conflict(const std::string& body)
799  : Response(body, Status::CONFLICT) {}
800 };
801 
802 
804 {
805  PreconditionFailed() : Response(Status::PRECONDITION_FAILED) {}
806 
807  explicit PreconditionFailed(const std::string& body)
808  : Response(body, Status::PRECONDITION_FAILED) {}
809 };
810 
811 
813 {
814  UnsupportedMediaType() : Response(Status::UNSUPPORTED_MEDIA_TYPE) {}
815 
816  explicit UnsupportedMediaType(const std::string& body)
817  : Response(body, Status::UNSUPPORTED_MEDIA_TYPE) {}
818 };
819 
820 
822 {
823  InternalServerError() : Response(Status::INTERNAL_SERVER_ERROR) {}
824 
825  explicit InternalServerError(const std::string& body)
826  : Response(body, Status::INTERNAL_SERVER_ERROR) {}
827 };
828 
829 
831 {
832  NotImplemented() : Response(Status::NOT_IMPLEMENTED) {}
833 
834  explicit NotImplemented(const std::string& body)
835  : Response(body, Status::NOT_IMPLEMENTED) {}
836 };
837 
838 
840 {
841  ServiceUnavailable() : Response(Status::SERVICE_UNAVAILABLE) {}
842 
843  explicit ServiceUnavailable(const std::string& body)
844  : Response(body, Status::SERVICE_UNAVAILABLE) {}
845 };
846 
847 
848 namespace path {
849 
850 // Parses an HTTP path into a map given a pattern (TODO(benh): Make
851 // the patterns be regular expressions). This returns an error if
852 // 'pattern' doesn't match 'path'. For example:
853 //
854 // parse("/books/{isbn}/chapters/{chapter}",
855 // "/books/0304827484/chapters/3")
856 //
857 // Would return a map with the following:
858 // books: "books"
859 // isbn: "0304827484"
860 // chapters: "chapters"
861 // chapter: "3"
862 //
863 // Another example:
864 //
865 // parse("/books/{isbn}/chapters/{chapter}",
866 // "/books/0304827484")
867 //
868 // Would return a map with the following:
869 // books: "books"
870 // isbn: "0304827484"
871 //
872 // And another:
873 //
874 // parse("/books/{isbn}/chapters/{chapter}",
875 // "/books/0304827484/chapters")
876 //
877 // Would return a map with the following:
878 // books: "books"
879 // isbn: "0304827484"
880 // chapters: "chapters"
882  const std::string& pattern,
883  const std::string& path);
884 
885 } // namespace path {
886 
887 
897 std::string encode(
898  const std::string& s,
899  const std::string& additional_chars = "");
900 
901 
902 // Decodes a percent-encoded string according to RFC 3986.
903 // The input string must not already be decoded.
904 // Returns error on the occurrence of a malformed % escape in s.
905 Try<std::string> decode(const std::string& s);
906 
907 
913 Try<std::vector<Response>> decodeResponses(const std::string& s);
914 
915 
916 namespace query {
917 
918 // Decodes an HTTP query string into a map. For example:
919 //
920 // decode("foo=1&bar=%20&baz&foo=3")
921 //
922 // Would return a map with the following:
923 // bar: " "
924 // baz: ""
925 // foo: "3"
926 //
927 // We use the last value for a key for simplicity, since the RFC does not
928 // specify how to handle duplicate keys:
929 // http://en.wikipedia.org/wiki/Query_string
930 // TODO(bmahler): If needed, investigate populating the query map inline
931 // for better performance.
932 Try<hashmap<std::string, std::string>> decode(const std::string& query);
933 
934 std::string encode(const hashmap<std::string, std::string>& query);
935 
936 } // namespace query {
937 
938 
946 {
947 public:
948  Connection() = delete;
949 
957  Future<Response> send(const Request& request, bool streamedResponse = false);
958 
962  Future<Nothing> disconnect();
963 
967  Future<Nothing> disconnected();
968 
969  bool operator==(const Connection& c) const { return data == c.data; }
970  bool operator!=(const Connection& c) const { return !(*this == c); }
971 
974 
975 private:
976  Connection(
977  const network::Socket& s,
978  const network::Address& _localAddress,
979  const network::Address& _peerAddress);
980 
982  const network::Address& address, Scheme scheme);
983  friend Future<Connection> connect(const URL&);
984 
985  // Forward declaration.
986  struct Data;
987 
988  std::shared_ptr<Data> data;
989 };
990 
991 
993 
994 
995 Future<Connection> connect(const URL& url);
996 
997 
998 namespace internal {
999 
1001  network::Socket s,
1002  std::function<Future<Response>(const Request&)>&& f);
1003 
1004 } // namespace internal {
1005 
1006 
1007 // Serves HTTP requests on the specified socket using the specified
1008 // handler.
1009 //
1010 // Returns `Nothing` after serving has completed, either because (1) a
1011 // failure occurred receiving requests or sending responses or (2) the
1012 // HTTP connection was not persistent (i.e., a 'Connection: close'
1013 // header existed either on the request or the response) or (3)
1014 // serving was discarded.
1015 //
1016 // Doing a `discard()` on the Future returned from `serve` will
1017 // discard any current socket receiving and any current socket
1018 // sending and shutdown the socket in both directions.
1019 //
1020 // NOTE: HTTP pipelining is automatically performed. If you don't want
1021 // pipelining you must explicitly sequence/serialize the requests to
1022 // wait for previous responses yourself.
1023 //
1024 // NOTE: The `Request` passed to the handler is of type `PIPE` and should
1025 // always be read using `Request.reader`.
1026 template <typename F>
1028 {
1029  return internal::serve(
1030  s,
1031  std::function<Future<Response>(const Request&)>(std::forward<F>(f)));
1032 }
1033 
1034 
1035 // Forward declaration.
1036 class ServerProcess;
1037 
1038 
1039 class Server
1040 {
1041 public:
1042  // Options for creating a server.
1043  //
1044  // NOTE: until GCC 5.0 default member initializers prevented the
1045  // class from being an aggregate which prevented you from being able
1046  // to use aggregate initialization, thus we introduce and use
1047  // `DEFAULT_CREATE_OPTIONS` for the default parameter of `create`.
1049  {
1051  size_t backlog;
1052  };
1053 
1055  {
1056  return {
1057  /* .scheme = */ Scheme::HTTP,
1058  /* .backlog = */ 16384,
1059  };
1060  };
1061 
1062  // Options for stopping a server.
1063  //
1064  // NOTE: see note above as to why we have `DEFAULT_STOP_OPTIONS`.
1066  {
1067  // During the grace period:
1068  // * No new sockets will be accepted (but on OS X they'll still queue).
1069  // * Existing sockets will be shut down for reads to prevent new
1070  // requests from arriving.
1071  // * Existing sockets will be shut down after already received
1072  // requests have their responses sent.
1073  // After the grace period connections will be forcibly shut down.
1075  };
1076 
1078  {
1079  return {
1080  /* .grace_period = */ Seconds(0),
1081  };
1082  };
1083 
1084  static Try<Server> create(
1086  std::function<Future<Response>(
1087  const network::Socket& socket,
1088  const Request&)>&& f,
1089  const CreateOptions& options = DEFAULT_CREATE_OPTIONS());
1090 
1091  template <typename F>
1093  network::Socket socket,
1094  F&& f,
1095  const CreateOptions& options = DEFAULT_CREATE_OPTIONS())
1096  {
1097  return create(
1098  std::move(socket),
1099  std::function<Future<Response>(
1100  const network::Socket&,
1101  const Request&)>(std::forward<F>(f)),
1102  options);
1103  }
1104 
1105  static Try<Server> create(
1106  const network::Address& address,
1107  std::function<Future<Response>(
1108  const network::Socket&,
1109  const Request&)>&& f,
1110  const CreateOptions& options = DEFAULT_CREATE_OPTIONS());
1111 
1112  template <typename F>
1114  const network::Address& address,
1115  F&& f,
1116  const CreateOptions& options = DEFAULT_CREATE_OPTIONS())
1117  {
1118  return create(
1119  address,
1120  std::function<Future<Response>(
1121  const network::Socket&,
1122  const Request&)>(std::forward<F>(f)),
1123  options);
1124  }
1125 
1126  // Movable but not copyable, not assignable.
1127  Server(Server&& that) = default;
1128  Server(const Server&) = delete;
1129  Server& operator=(const Server&) = delete;
1130 
1131  ~Server();
1132 
1133  // Runs the server, returns nothing after the server has been
1134  // stopped or a failure if one occured.
1135  Future<Nothing> run();
1136 
1137  // Returns after the server has been stopped and all existing
1138  // connections have been closed.
1139  Future<Nothing> stop(const StopOptions& options = DEFAULT_STOP_OPTIONS());
1140 
1141  // Returns the bound address of the server.
1143 
1144 private:
1145  Server(
1146  network::Socket&& socket,
1147  std::function<Future<Response>(
1148  const network::Socket&,
1149  const Request&)>&& f);
1150 
1153 };
1154 
1155 
1156 // Create a http Request from the specified parameters.
1158  const UPID& upid,
1159  const std::string& method,
1160  bool enableSSL = false,
1161  const Option<std::string>& path = None(),
1162  const Option<Headers>& headers = None(),
1163  const Option<std::string>& body = None(),
1164  const Option<std::string>& contentType = None());
1165 
1166 
1168  const URL& url,
1169  const std::string& method,
1170  const Option<Headers>& headers = None(),
1171  const Option<std::string>& body = None(),
1172  const Option<std::string>& contentType = None());
1173 
1183  const Request& request,
1184  bool streamedResponse = false);
1185 
1186 
1187 // TODO(Yongqiao Wang): Refactor other functions
1188 // (such as post/get/requestDelete) to use the 'request' function.
1189 
1190 // TODO(bmahler): Support discarding the future responses;
1191 // discarding should disconnect from the server.
1192 
1193 // TODO(joerg84): Make names consistent (see Mesos-3256).
1194 
1195 // Asynchronously sends an HTTP GET request to the specified URL
1196 // and returns the HTTP response of type 'BODY' once the entire
1197 // response is received.
1198 Future<Response> get(
1199  const URL& url,
1200  const Option<Headers>& headers = None());
1201 
1202 
1203 // Asynchronously sends an HTTP GET request to the process with the
1204 // given UPID and returns the HTTP response of type 'BODY' once the
1205 // entire response is received.
1206 Future<Response> get(
1207  const UPID& upid,
1208  const Option<std::string>& path = None(),
1209  const Option<std::string>& query = None(),
1210  const Option<Headers>& headers = None(),
1211  const Option<std::string>& scheme = None());
1212 
1213 
1214 // Asynchronously sends an HTTP POST request to the specified URL
1215 // and returns the HTTP response of type 'BODY' once the entire
1216 // response is received.
1218  const URL& url,
1219  const Option<Headers>& headers = None(),
1220  const Option<std::string>& body = None(),
1221  const Option<std::string>& contentType = None());
1222 
1223 
1224 // Asynchronously sends an HTTP POST request to the process with the
1225 // given UPID and returns the HTTP response of type 'BODY' once the
1226 // entire response is received.
1228  const UPID& upid,
1229  const Option<std::string>& path = None(),
1230  const Option<Headers>& headers = None(),
1231  const Option<std::string>& body = None(),
1232  const Option<std::string>& contentType = None(),
1233  const Option<std::string>& scheme = None());
1234 
1235 
1245  const URL& url,
1246  const Option<Headers>& headers = None());
1247 
1248 
1261  const UPID& upid,
1262  const Option<std::string>& path = None(),
1263  const Option<Headers>& headers = None(),
1264  const Option<std::string>& scheme = None());
1265 
1266 
1267 namespace streaming {
1268 
1269 // Asynchronously sends an HTTP GET request to the specified URL
1270 // and returns the HTTP response of type 'PIPE' once the response
1271 // headers are received. The caller must read the response body
1272 // from the Pipe::Reader.
1273 Future<Response> get(
1274  const URL& url,
1275  const Option<Headers>& headers = None());
1276 
1277 // Asynchronously sends an HTTP GET request to the process with the
1278 // given UPID and returns the HTTP response of type 'PIPE' once the
1279 // response headers are received. The caller must read the response
1280 // body from the Pipe::Reader.
1281 Future<Response> get(
1282  const UPID& upid,
1283  const Option<std::string>& path = None(),
1284  const Option<std::string>& query = None(),
1285  const Option<Headers>& headers = None(),
1286  const Option<std::string>& scheme = None());
1287 
1288 // Asynchronously sends an HTTP POST request to the specified URL
1289 // and returns the HTTP response of type 'PIPE' once the response
1290 // headers are received. The caller must read the response body
1291 // from the Pipe::Reader.
1293  const URL& url,
1294  const Option<Headers>& headers = None(),
1295  const Option<std::string>& body = None(),
1296  const Option<std::string>& contentType = None());
1297 
1298 // Asynchronously sends an HTTP POST request to the process with the
1299 // given UPID and returns the HTTP response of type 'PIPE' once the
1300 // response headers are received. The caller must read the response
1301 // body from the Pipe::Reader.
1303  const UPID& upid,
1304  const Option<std::string>& path = None(),
1305  const Option<Headers>& headers = None(),
1306  const Option<std::string>& body = None(),
1307  const Option<std::string>& contentType = None(),
1308  const Option<std::string>& scheme = None());
1309 
1310 } // namespace streaming {
1311 
1312 } // namespace http {
1313 } // namespace process {
1314 
1315 #endif // __PROCESS_HTTP_HPP__
static StopOptions DEFAULT_STOP_OPTIONS()
Definition: http.hpp:1077
Definition: path.hpp:26
Try< hashmap< std::string, std::string > > parse(const std::string &pattern, const std::string &path)
NotImplemented(const std::string &body)
Definition: http.hpp:834
bool operator!=(const Writer &other) const
Definition: http.hpp:365
WWWAuthenticate(const std::string &authScheme, const hashmap< std::string, std::string > &authParam)
Definition: http.hpp:429
size_t operator()(const std::string &key) const
Definition: http.hpp:186
Future< Nothing > serve(network::Socket s, std::function< Future< Response >(const Request &)> &&f)
Forbidden()
Definition: http.hpp:736
NotFound(const std::string &body)
Definition: http.hpp:747
bool operator!=(const Connection &c) const
Definition: http.hpp:970
static const uint16_t GATEWAY_TIMEOUT
Definition: http.hpp:254
static const uint16_t MOVED_PERMANENTLY
Definition: http.hpp:226
static const uint16_t GONE
Definition: http.hpp:242
static const uint16_t UNSUPPORTED_MEDIA_TYPE
Definition: http.hpp:247
Definition: errorbase.hpp:35
Definition: http.hpp:1048
Definition: option.hpp:28
Definition: http.hpp:296
static const uint16_t METHOD_NOT_ALLOWED
Definition: http.hpp:237
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...
F && f
Definition: defer.hpp:270
static const uint16_t BAD_REQUEST
Definition: http.hpp:232
static const uint16_t NO_CONTENT
Definition: http.hpp:222
T & get()&
Definition: try.hpp:73
URL url
Definition: http.hpp:529
static const uint16_t REQUEST_TIMEOUT
Definition: http.hpp:240
static const uint16_t LENGTH_REQUIRED
Definition: http.hpp:243
NotAcceptable(const std::string &body)
Definition: http.hpp:789
std::stringstream & join(std::stringstream &stream, const std::string &separator, T &&...args)
Definition: strings.hpp:306
static CreateOptions DEFAULT_CREATE_OPTIONS()
Definition: http.hpp:1054
Definition: check.hpp:33
std::string status
Definition: http.hpp:621
NotAcceptable()
Definition: http.hpp:787
static const uint16_t PARTIAL_CONTENT
Definition: http.hpp:224
static const uint16_t EXPECTATION_FAILED
Definition: http.hpp:249
static const uint16_t CONFLICT
Definition: http.hpp:241
Option< Pipe::Reader > reader
Definition: http.hpp:564
Unauthorized(const std::vector< std::string > &challenges)
Definition: http.hpp:710
uint16_t code
Definition: http.hpp:658
Definition: http.hpp:184
UnsupportedMediaType(const std::string &body)
Definition: http.hpp:816
Definition: http.hpp:214
OK()
Definition: http.hpp:664
Future< Nothing > serve(const network::Socket &s, F &&f)
Definition: http.hpp:1027
Definition: http.hpp:830
static const uint16_t NON_AUTHORITATIVE_INFORMATION
Definition: http.hpp:221
Result< ProcessStatus > status(pid_t pid)
Definition: proc.hpp:166
Definition: http.hpp:197
Option< std::string > domain
Definition: http.hpp:172
InternalServerError()
Definition: http.hpp:823
Definition: http.hpp:1039
static const uint16_t FOUND
Definition: http.hpp:227
bool operator==(const Writer &other) const
Definition: http.hpp:364
Definition: type_utils.hpp:510
Definition: address.hpp:275
Forbidden(const std::string &body)
Definition: http.hpp:738
Future< Response > get(const URL &url, const Option< Headers > &headers=None())
Definition: http.hpp:556
Option< network::Address > client
Definition: http.hpp:547
const std::string NAME
Definition: logrotate.hpp:38
static const uint16_t MULTIPLE_CHOICES
Definition: http.hpp:225
static const uint16_t REQUEST_ENTITY_TOO_LARGE
Definition: http.hpp:245
Definition: http.hpp:648
Future< Response > requestDelete(const URL &url, const Option< Headers > &headers=None())
Asynchronously sends an HTTP DELETE request to the process with the given UPID and returns the HTTP r...
std::string encode(const std::string &s, const std::string &additional_chars="")
Returns a percent-encoded string according to RFC 3986.
Definition: http.hpp:689
Option< net::IP > ip
Definition: http.hpp:173
Definition: duration.hpp:32
Try< std::string > decode(const std::string &s)
Decode a string that is Base64-encoded with the standard Base64 alphabet.
Definition: base64.hpp:183
Definition: check.hpp:30
BadRequest()
Definition: http.hpp:701
static const uint16_t RESET_CONTENT
Definition: http.hpp:223
Definition: http.hpp:518
PreconditionFailed()
Definition: http.hpp:805
Scheme
Definition: http.hpp:55
bool operator==(const Connection &c) const
Definition: http.hpp:969
ServiceUnavailable()
Definition: http.hpp:841
hashmap< std::string, std::string > query
Definition: http.hpp:176
Definition: http.hpp:649
Definition: ip.hpp:73
ssize_t send(const os::WindowsFD &fd, const void *buf, size_t len, int flags)
Definition: socket.hpp:162
Definition: hashmap.hpp:38
InternalServerError(const std::string &body)
Definition: http.hpp:825
Duration grace_period
Definition: http.hpp:1074
Accepted()
Definition: http.hpp:682
Option< Pipe::Reader > reader
Definition: http.hpp:656
std::string path
Definition: http.hpp:175
Definition: http.hpp:794
Definition: http.hpp:752
static const uint16_t PRECONDITION_FAILED
Definition: http.hpp:244
std::string body
Definition: http.hpp:654
An "untyped" PID, used to encapsulate the process ID for lower-layer abstractions (eg...
Definition: pid.hpp:39
Definition: http.hpp:129
static const uint16_t NOT_ACCEPTABLE
Definition: http.hpp:238
Option< std::string > scheme
Definition: http.hpp:169
Definition: http.hpp:680
Future< Nothing > setAuthenticator(const std::string &realm, Owned< Authenticator > authenticator)
Sets (or overwrites) the authenticator for the realm.
NotImplemented()
Definition: http.hpp:832
OK(const char *body)
Definition: http.hpp:666
static const uint16_t SERVICE_UNAVAILABLE
Definition: http.hpp:253
Definition: http.hpp:340
Try<::mesos::Value::Ranges > fragment(const ::mesos::Value::Range &bounds, size_t numRanges)
Definition: jsonify.hpp:129
Definition: duration.hpp:207
static const uint16_t PAYMENT_REQUIRED
Definition: http.hpp:234
URL(const std::string &_scheme, const net::IP &_ip, const uint16_t _port=80, const std::string &_path="/", const hashmap< std::string, std::string > &_query=(hashmap< std::string, std::string >()), const Option< std::string > &_fragment=None())
Definition: http.hpp:147
Definition: http.hpp:456
static const uint16_t USE_PROXY
Definition: http.hpp:230
Option< uint16_t > port
Definition: http.hpp:174
Headers(std::map< std::string, std::string > &&map)
Definition: http.hpp:472
static const uint16_t ACCEPTED
Definition: http.hpp:220
NotFound()
Definition: http.hpp:745
const T & get() const &
Definition: option.hpp:118
MethodNotAllowed(const std::initializer_list< std::string > &allowedMethods)
Definition: http.hpp:757
ServiceUnavailable(const std::string &body)
Definition: http.hpp:843
Definition: http.hpp:785
Future< Response > post(const URL &url, const Option< Headers > &headers=None(), const Option< std::string > &body=None(), const Option< std::string > &contentType=None())
Request createRequest(const UPID &upid, const std::string &method, bool enableSSL=false, const Option< std::string > &path=None(), const Option< Headers > &headers=None(), const Option< std::string > &body=None(), const Option< std::string > &contentType=None())
Future< Connection > connect(const network::Address &address, Scheme scheme)
Definition: http.hpp:821
bool keepAlive
Definition: http.hpp:543
Accepted(const std::string &body)
Definition: http.hpp:684
bool operator==(const Pipe &other) const
Definition: http.hpp:387
static const uint16_t SEE_OTHER
Definition: http.hpp:228
bool operator!=(const Reader &other) const
Definition: http.hpp:324
static const uint16_t OK
Definition: http.hpp:218
Definition: http.hpp:839
URL(const std::string &_scheme, const std::string &_domain, const uint16_t _port=80, const std::string &_path="/", const hashmap< std::string, std::string > &_query=(hashmap< std::string, std::string >()), const Option< std::string > &_fragment=None())
Definition: http.hpp:133
OK(const std::string &body, const std::string &contentType)
Definition: http.hpp:671
static Try error(const E &e)
Definition: try.hpp:42
Scheme scheme
Definition: http.hpp:1050
Future< R > run(R(*method)())
Definition: run.hpp:55
Conflict()
Definition: http.hpp:796
MethodNotAllowed(const std::initializer_list< std::string > &allowedMethods, const std::string &requestMethod)
Definition: http.hpp:764
Option< std::string > fragment
Definition: http.hpp:177
static const uint16_t CONTINUE
Definition: http.hpp:216
Try< std::vector< Entry > > list(const std::string &hierarchy, const std::string &cgroup)
static const uint16_t PROXY_AUTHENTICATION_REQUIRED
Definition: http.hpp:239
Definition: json.hpp:243
Iterable< V > map(F &&f, const Iterable< U, Us... > &input)
Definition: lambda.hpp:46
BadRequest(const std::string &body)
Definition: http.hpp:703
Result< Process > process(pid_t pid)
Definition: freebsd.hpp:30
Definition: http.hpp:803
bool operator!=(const Pipe &other) const
Definition: http.hpp:388
Definition: none.hpp:27
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:71
Result< Credentials > read(const Path &path)
Definition: credentials.hpp:35
std::string path
Definition: http.hpp:655
TemporaryRedirect(const std::string &url)
Definition: http.hpp:691
OK(const std::string &body)
Definition: http.hpp:669
std::string method
Definition: http.hpp:523
Definition: executor.hpp:47
Definition: http.hpp:595
std::ostream & operator<<(std::ostream &stream, const URL &url)
Pipe()
Definition: http.hpp:381
Definition: http.hpp:812
Headers operator+(const Headers &that) const
Definition: http.hpp:509
std::string body
Definition: http.hpp:563
hashmap< uint16_t, std::string > * statuses
static const uint16_t SWITCHING_PROTOCOLS
Definition: http.hpp:217
static const uint16_t BAD_GATEWAY
Definition: http.hpp:252
Try< std::vector< Response > > decodeResponses(const std::string &s)
Decode HTTP responses from the given string.
static const uint16_t INTERNAL_SERVER_ERROR
Definition: http.hpp:250
Definition: http.hpp:699
UnsupportedMediaType()
Definition: http.hpp:814
Protocol< WriteRequest, WriteResponse > write
Definition: http.hpp:662
Headers(const std::map< std::string, std::string > &map)
Definition: http.hpp:465
Try< uint32_t > type(const std::string &path)
network::inet::Address address()
Returns the socket address associated with this instance of the library.
static const uint16_t NOT_MODIFIED
Definition: http.hpp:229
Definition: http.hpp:708
static Try< Server > create(network::Socket socket, F &&f, const CreateOptions &options=DEFAULT_CREATE_OPTIONS())
Definition: http.hpp:1092
bool isNone() const
Definition: option.hpp:116
const network::Address peerAddress
Definition: http.hpp:973
static const uint16_t REQUESTED_RANGE_NOT_SATISFIABLE
Definition: http.hpp:248
void setCallbacks(const AuthorizationCallbacks &)
static const uint16_t CREATED
Definition: http.hpp:219
Try< Nothing > create(const std::string &hierarchy, const std::string &cgroup, bool recursive=false)
Definition: http.hpp:650
Response()
Definition: http.hpp:597
static const uint16_t UNAUTHORIZED
Definition: http.hpp:233
static const uint16_t NOT_FOUND
Definition: http.hpp:236
URI http(const std::string &host, const std::string &path="/", const Option< int > &port=None(), const Option< std::string > &query=None(), const Option< std::string > &fragment=None(), const Option< std::string > &user=None(), const Option< std::string > &password=None())
Creates an http URI with the given parameters.
Definition: http.hpp:35
bool operator==(const Reader &other) const
Definition: http.hpp:323
Try< Netlink< struct nl_sock > > socket(int protocol=NETLINK_ROUTE)
Definition: internal.hpp:91
std::string stringify(int flags)
static const uint16_t NOT_IMPLEMENTED
Definition: http.hpp:251
Definition: owned.hpp:36
static std::string string(uint16_t code)
Headers()
Definition: http.hpp:463
Headers(std::initializer_list< std::pair< std::string, std::string >> list)
Definition: http.hpp:479
Headers headers
Definition: http.hpp:623
Response(uint16_t _code)
Definition: http.hpp:601
Conflict(const std::string &body)
Definition: http.hpp:798
const network::Address localAddress
Definition: http.hpp:972
size_t backlog
Definition: http.hpp:1051
static const uint16_t REQUEST_URI_TOO_LARGE
Definition: http.hpp:246
Definition: http.hpp:1065
Represents a connection to an HTTP server.
Definition: http.hpp:945
Future< Nothing > unsetAuthenticator(const std::string &realm)
Unsets the authenticator for the realm.
bool operator()(const std::string &left, const std::string &right) const
Definition: http.hpp:199
Definition: http.hpp:302
constexpr const char * name
Definition: shell.hpp:43
static const uint16_t HTTP_VERSION_NOT_SUPPORTED
Definition: http.hpp:255
Unauthorized(const std::vector< std::string > &challenges, const std::string &body)
Definition: http.hpp:720
static Try< Server > create(const network::Address &address, F &&f, const CreateOptions &options=DEFAULT_CREATE_OPTIONS())
Definition: http.hpp:1113
static const uint16_t TEMPORARY_REDIRECT
Definition: http.hpp:231
static const uint16_t FORBIDDEN
Definition: http.hpp:235
Headers headers
Definition: http.hpp:531
Definition: http.hpp:734
Definition: http.hpp:743
PreconditionFailed(const std::string &body)
Definition: http.hpp:807
Response(const std::string &_body, uint16_t _code, const std::string &contentType="text/plain; charset=utf-8")
Definition: http.hpp:607
Request()
Definition: http.hpp:520