http.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /*************************************************************************
  2. *
  3. * Copyright 2022 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #pragma once
  19. #include <cstdint>
  20. #include <type_traits>
  21. #include <map>
  22. #include <system_error>
  23. #include <iosfwd>
  24. #include <locale>
  25. #include <realm/util/optional.hpp>
  26. #include <realm/util/basic_system_errors.hpp>
  27. #include <realm/util/logger.hpp>
  28. #include <realm/string_data.hpp>
  29. namespace realm::sync {
  30. enum class HTTPParserError {
  31. None = 0,
  32. ContentTooLong,
  33. HeaderLineTooLong,
  34. MalformedResponse,
  35. MalformedRequest,
  36. BadRequest,
  37. };
  38. std::error_code make_error_code(HTTPParserError);
  39. } // namespace realm::sync
  40. namespace std {
  41. template <>
  42. struct is_error_code_enum<realm::sync::HTTPParserError> : std::true_type {
  43. };
  44. } // namespace std
  45. namespace realm::sync {
  46. /// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  47. ///
  48. /// It is guaranteed that the backing integer value of this enum corresponds
  49. /// to the numerical code representing the status.
  50. enum class HTTPStatus {
  51. Unknown = 0,
  52. Continue = 100,
  53. SwitchingProtocols = 101,
  54. Ok = 200,
  55. Created = 201,
  56. Accepted = 202,
  57. NonAuthoritative = 203,
  58. NoContent = 204,
  59. ResetContent = 205,
  60. PartialContent = 206,
  61. MultipleChoices = 300,
  62. MovedPermanently = 301,
  63. Found = 302,
  64. SeeOther = 303,
  65. NotModified = 304,
  66. UseProxy = 305,
  67. SwitchProxy = 306,
  68. TemporaryRedirect = 307,
  69. PermanentRedirect = 308,
  70. BadRequest = 400,
  71. Unauthorized = 401,
  72. PaymentRequired = 402,
  73. Forbidden = 403,
  74. NotFound = 404,
  75. MethodNotAllowed = 405,
  76. NotAcceptable = 406,
  77. ProxyAuthenticationRequired = 407,
  78. RequestTimeout = 408,
  79. Conflict = 409,
  80. Gone = 410,
  81. LengthRequired = 411,
  82. PreconditionFailed = 412,
  83. PayloadTooLarge = 413,
  84. UriTooLong = 414,
  85. UnsupportedMediaType = 415,
  86. RangeNotSatisfiable = 416,
  87. ExpectationFailed = 417,
  88. ImATeapot = 418,
  89. MisdirectedRequest = 421,
  90. UpgradeRequired = 426,
  91. PreconditionRequired = 428,
  92. TooManyRequests = 429,
  93. RequestHeaderFieldsTooLarge = 431,
  94. UnavailableForLegalReasons = 451,
  95. InternalServerError = 500,
  96. NotImplemented = 501,
  97. BadGateway = 502,
  98. ServiceUnavailable = 503,
  99. GatewayTimeout = 504,
  100. HttpVersionNotSupported = 505,
  101. VariantAlsoNegotiates = 506,
  102. NotExtended = 510,
  103. NetworkAuthenticationRequired = 511,
  104. };
  105. bool valid_http_status_code(unsigned int code);
  106. /// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
  107. enum class HTTPMethod {
  108. Options,
  109. Get,
  110. Head,
  111. Post,
  112. Put,
  113. Delete,
  114. Trace,
  115. Connect,
  116. };
  117. struct HTTPAuthorization {
  118. std::string scheme;
  119. std::map<std::string, std::string> values;
  120. };
  121. HTTPAuthorization parse_authorization(const std::string&);
  122. class HeterogeneousCaseInsensitiveCompare {
  123. public:
  124. using is_transparent = std::true_type;
  125. template <class A, class B>
  126. bool operator()(const A& a, const B& b) const noexcept
  127. {
  128. return comp(std::string_view(a), std::string_view(b));
  129. }
  130. private:
  131. bool comp(std::string_view a, std::string_view b) const noexcept
  132. {
  133. auto cmp = [](char lhs, char rhs) {
  134. return std::tolower(lhs, std::locale::classic()) < std::tolower(rhs, std::locale::classic());
  135. };
  136. return std::lexicographical_compare(begin(a), end(a), begin(b), end(b), cmp);
  137. }
  138. };
  139. /// Case-insensitive map suitable for storing HTTP headers.
  140. using HTTPHeaders = std::map<std::string, std::string, HeterogeneousCaseInsensitiveCompare>;
  141. struct HTTPRequest {
  142. HTTPMethod method = HTTPMethod::Get;
  143. HTTPHeaders headers;
  144. std::string path;
  145. /// If the request object has a body, the Content-Length header MUST be
  146. /// set to a string representation of the number of bytes in the body.
  147. /// FIXME: Relax this restriction, and also support Transfer-Encoding
  148. /// and other HTTP/1.1 features.
  149. util::Optional<std::string> body;
  150. };
  151. struct HTTPResponse {
  152. HTTPStatus status = HTTPStatus::Unknown;
  153. std::string reason;
  154. HTTPHeaders headers;
  155. // A body is only read from the response stream if the server sent the
  156. // Content-Length header.
  157. // FIXME: Support other transfer methods, including Transfer-Encoding and
  158. // HTTP/1.1 features.
  159. util::Optional<std::string> body;
  160. };
  161. /// Serialize HTTP request to output stream.
  162. std::ostream& operator<<(std::ostream&, const HTTPRequest&);
  163. /// Serialize HTTP response to output stream.
  164. std::ostream& operator<<(std::ostream&, const HTTPResponse&);
  165. /// Serialize HTTP method to output stream ("GET", "POST", etc.).
  166. std::ostream& operator<<(std::ostream&, HTTPMethod);
  167. /// Serialize HTTP status to output stream, include reason string ("200 OK" etc.)
  168. std::ostream& operator<<(std::ostream&, HTTPStatus);
  169. struct HTTPParserBase {
  170. const std::shared_ptr<util::Logger> logger_ptr;
  171. util::Logger& logger;
  172. // FIXME: Generally useful?
  173. struct CallocDeleter {
  174. void operator()(void* ptr)
  175. {
  176. std::free(ptr);
  177. }
  178. };
  179. HTTPParserBase(const std::shared_ptr<util::Logger>& logger_ptr)
  180. : logger_ptr{logger_ptr}
  181. , logger{*logger_ptr}
  182. {
  183. // Allocating read buffer with calloc to avoid accidentally spilling
  184. // data from other sessions in case of a buffer overflow exploit.
  185. m_read_buffer.reset(static_cast<char*>(std::calloc(read_buffer_size, 1)));
  186. }
  187. virtual ~HTTPParserBase() {}
  188. std::string m_write_buffer;
  189. std::unique_ptr<char[], CallocDeleter> m_read_buffer;
  190. util::Optional<size_t> m_found_content_length;
  191. static const size_t read_buffer_size = 8192;
  192. static const size_t max_header_line_length = read_buffer_size;
  193. /// Parses the contents of m_read_buffer as a HTTP header line,
  194. /// and calls on_header() as appropriate. on_header() will be called at
  195. /// most once per invocation.
  196. /// Returns false if the contents of m_read_buffer is not a valid HTTP
  197. /// header line.
  198. bool parse_header_line(size_t len);
  199. virtual std::error_code on_first_line(StringData line) = 0;
  200. virtual void on_header(StringData key, StringData value) = 0;
  201. virtual void on_body(StringData body) = 0;
  202. virtual void on_complete(std::error_code = std::error_code{}) = 0;
  203. /// If the input matches a known HTTP method string, return the appropriate
  204. /// HTTPMethod enum value. Otherwise, returns none.
  205. static util::Optional<HTTPMethod> parse_method_string(StringData method);
  206. /// Interpret line as the first line of an HTTP request. If the return value
  207. /// is true, out_method and out_uri have been assigned the appropriate
  208. /// values found in the request line.
  209. static bool parse_first_line_of_request(StringData line, HTTPMethod& out_method, StringData& out_uri);
  210. /// Interpret line as the first line of an HTTP response. If the return
  211. /// value is true, out_status and out_reason have been assigned the
  212. /// appropriate values found in the response line.
  213. static bool parse_first_line_of_response(StringData line, HTTPStatus& out_status, StringData& out_reason,
  214. util::Logger& logger);
  215. void set_write_buffer(const HTTPRequest&);
  216. void set_write_buffer(const HTTPResponse&);
  217. };
  218. template <class Socket>
  219. struct HTTPParser : protected HTTPParserBase {
  220. explicit HTTPParser(Socket& socket, const std::shared_ptr<util::Logger>& logger_ptr)
  221. : HTTPParserBase(logger_ptr)
  222. , m_socket(socket)
  223. {
  224. }
  225. void read_first_line()
  226. {
  227. auto handler = [this](std::error_code ec, size_t n) {
  228. if (ec == util::error::operation_aborted) {
  229. return;
  230. }
  231. if (ec) {
  232. on_complete(ec);
  233. return;
  234. }
  235. ec = on_first_line(StringData(m_read_buffer.get(), n));
  236. if (ec) {
  237. on_complete(ec);
  238. return;
  239. }
  240. read_headers();
  241. };
  242. m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n', std::move(handler));
  243. }
  244. void read_headers()
  245. {
  246. auto handler = [this](std::error_code ec, size_t n) {
  247. if (ec == util::error::operation_aborted) {
  248. return;
  249. }
  250. if (ec) {
  251. on_complete(ec);
  252. return;
  253. }
  254. if (n <= 2) {
  255. read_body();
  256. return;
  257. }
  258. if (!parse_header_line(n)) {
  259. on_complete(HTTPParserError::BadRequest);
  260. return;
  261. }
  262. // FIXME: Limit the total size of headers. Apache uses 8K.
  263. read_headers();
  264. };
  265. m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n', std::move(handler));
  266. }
  267. void read_body()
  268. {
  269. if (m_found_content_length) {
  270. // FIXME: Support longer bodies.
  271. // FIXME: Support multipart and other body types (no body shaming).
  272. if (*m_found_content_length > read_buffer_size) {
  273. on_complete(HTTPParserError::ContentTooLong);
  274. return;
  275. }
  276. auto handler = [this](std::error_code ec, size_t n) {
  277. if (ec == util::error::operation_aborted) {
  278. return;
  279. }
  280. if (!ec) {
  281. on_body(StringData(m_read_buffer.get(), n));
  282. }
  283. on_complete(ec);
  284. };
  285. m_socket.async_read(m_read_buffer.get(), *m_found_content_length, std::move(handler));
  286. }
  287. else {
  288. // No body, just finish.
  289. on_complete();
  290. }
  291. }
  292. void write_buffer(util::UniqueFunction<void(std::error_code, size_t)> handler)
  293. {
  294. m_socket.async_write(m_write_buffer.data(), m_write_buffer.size(), std::move(handler));
  295. }
  296. Socket& m_socket;
  297. };
  298. template <class Socket>
  299. struct HTTPClient : protected HTTPParser<Socket> {
  300. using Handler = void(HTTPResponse, std::error_code);
  301. explicit HTTPClient(Socket& socket, const std::shared_ptr<util::Logger>& logger_ptr)
  302. : HTTPParser<Socket>(socket, logger_ptr)
  303. {
  304. }
  305. /// Serialize and send \a request over the connected socket asynchronously.
  306. ///
  307. /// When the response has been received, or an error occurs, \a handler will
  308. /// be invoked with the appropriate parameters. The HTTPResponse object
  309. /// passed to \a handler will only be complete in non-error conditions, but
  310. /// may be partially populated.
  311. ///
  312. /// It is an error to start a request before the \a handler of a previous
  313. /// request has been invoked. It is permitted to call async_request() from
  314. /// the handler, unless an error has been reported representing a condition
  315. /// where the underlying socket is no longer able to communicate (for
  316. /// example, if it has been closed).
  317. ///
  318. /// If a request is already in progress, an exception will be thrown.
  319. ///
  320. /// This method is *NOT* thread-safe.
  321. void async_request(const HTTPRequest& request, util::UniqueFunction<Handler> handler)
  322. {
  323. if (REALM_UNLIKELY(m_handler)) {
  324. throw LogicError(ErrorCodes::LogicError, "Request already in progress.");
  325. }
  326. this->set_write_buffer(request);
  327. m_handler = std::move(handler);
  328. this->write_buffer([this](std::error_code ec, size_t bytes_written) {
  329. static_cast<void>(bytes_written);
  330. if (ec == util::error::operation_aborted) {
  331. return;
  332. }
  333. if (ec) {
  334. this->on_complete(ec);
  335. return;
  336. }
  337. this->read_first_line();
  338. });
  339. }
  340. private:
  341. util::UniqueFunction<Handler> m_handler;
  342. HTTPResponse m_response;
  343. std::error_code on_first_line(StringData line) override final
  344. {
  345. HTTPStatus status;
  346. StringData reason;
  347. if (this->parse_first_line_of_response(line, status, reason, this->logger)) {
  348. m_response.status = status;
  349. m_response.reason = reason;
  350. return std::error_code{};
  351. }
  352. return HTTPParserError::MalformedResponse;
  353. }
  354. void on_header(StringData key, StringData value) override final
  355. {
  356. // FIXME: Multiple headers with the same key should show up as a
  357. // comma-separated list of their values, rather than overwriting.
  358. m_response.headers[std::string(key)] = std::string(value);
  359. }
  360. void on_body(StringData body) override final
  361. {
  362. m_response.body = std::string(body);
  363. }
  364. void on_complete(std::error_code ec) override final
  365. {
  366. auto handler = std::move(m_handler);
  367. m_handler = nullptr;
  368. handler(std::move(m_response), ec);
  369. }
  370. };
  371. template <class Socket>
  372. struct HTTPServer : protected HTTPParser<Socket> {
  373. using RequestHandler = void(HTTPRequest, std::error_code);
  374. using RespondHandler = void(std::error_code);
  375. explicit HTTPServer(Socket& socket, const std::shared_ptr<util::Logger>& logger_ptr)
  376. : HTTPParser<Socket>(socket, logger_ptr)
  377. {
  378. }
  379. /// Receive a request on the underlying socket asynchronously.
  380. ///
  381. /// This function starts an asynchronous read operation and keeps reading
  382. /// until an HTTP request has been received. \a handler is invoked when a
  383. /// request has been received, or an error occurs.
  384. ///
  385. /// After a request is received, callers MUST invoke async_send_response()
  386. /// to provide the client with a valid HTTP response, unless the error
  387. /// passed to the handler represents a condition where the underlying socket
  388. /// is no longer able to communicate (for example, if it has been closed).
  389. ///
  390. /// It is an error to attempt to receive a request before any previous
  391. /// requests have been fully responded to, i.e. the \a handler argument of
  392. /// async_send_response() must have been invoked before attempting to
  393. /// receive the next request.
  394. ///
  395. /// This function is *NOT* thread-safe.
  396. void async_receive_request(util::UniqueFunction<RequestHandler> handler)
  397. {
  398. if (REALM_UNLIKELY(m_request_handler)) {
  399. throw LogicError(ErrorCodes::LogicError, "Request already in progress");
  400. }
  401. m_request_handler = std::move(handler);
  402. this->read_first_line();
  403. }
  404. /// Send an HTTP response to a client asynchronously.
  405. ///
  406. /// This function starts an asynchronous write operation on the underlying
  407. /// socket. \a handler is invoked when the response has been written to the
  408. /// socket, or an error occurs.
  409. ///
  410. /// It is an error to call async_receive_request() again before \a handler
  411. /// has been invoked, and it is an error to call async_send_response()
  412. /// before the \a handler of a previous invocation has been invoked.
  413. ///
  414. /// This function is *NOT* thread-safe.
  415. void async_send_response(const HTTPResponse& response, util::UniqueFunction<RespondHandler> handler)
  416. {
  417. if (REALM_UNLIKELY(!m_request_handler)) {
  418. throw LogicError(ErrorCodes::LogicError, "No request in progress");
  419. }
  420. if (m_respond_handler) {
  421. // FIXME: Proper exception type.
  422. throw LogicError(ErrorCodes::LogicError, "Already responding to request");
  423. }
  424. m_respond_handler = std::move(handler);
  425. this->set_write_buffer(response);
  426. this->write_buffer([this](std::error_code ec, size_t) {
  427. if (ec == util::error::operation_aborted) {
  428. return;
  429. }
  430. m_request_handler = nullptr;
  431. auto handler = std::move(m_respond_handler);
  432. handler(ec);
  433. });
  434. ;
  435. }
  436. private:
  437. util::UniqueFunction<RequestHandler> m_request_handler;
  438. util::UniqueFunction<RespondHandler> m_respond_handler;
  439. HTTPRequest m_request;
  440. std::error_code on_first_line(StringData line) override final
  441. {
  442. HTTPMethod method;
  443. StringData uri;
  444. if (this->parse_first_line_of_request(line, method, uri)) {
  445. m_request.method = method;
  446. m_request.path = uri;
  447. return std::error_code{};
  448. }
  449. return HTTPParserError::MalformedRequest;
  450. }
  451. void on_header(StringData key, StringData value) override final
  452. {
  453. // FIXME: Multiple headers with the same key should show up as a
  454. // comma-separated list of their values, rather than overwriting.
  455. m_request.headers[std::string(key)] = std::string(value);
  456. }
  457. void on_body(StringData body) override final
  458. {
  459. m_request.body = std::string(body);
  460. }
  461. void on_complete(std::error_code ec) override final
  462. {
  463. // Deliberately not nullifying m_request_handler so that we can
  464. // check for invariants in async_send_response.
  465. m_request_handler(std::move(m_request), ec);
  466. }
  467. };
  468. } // namespace realm::sync