1 /*************************************************************************
6 * [2011] - [2016] Realm Inc
9 * NOTICE: All information contained herein is, and remains
10 * the property of Realm Incorporated and its suppliers,
11 * if any. The intellectual and technical concepts contained
12 * herein are proprietary to Realm Incorporated
13 * and its suppliers and may be covered by U.S. and Foreign Patents,
14 * patents in process, and are protected by trade secret or copyright law.
15 * Dissemination of this information or reproduction of this material
16 * is strictly forbidden unless prior written permission is obtained
17 * from Realm Incorporated.
19 **************************************************************************/
21 #ifndef REALM_UTIL_WEBSOCKET_HPP
22 #define REALM_UTIL_WEBSOCKET_HPP
25 #include <system_error>
28 #include <realm/util/logger.hpp>
29 #include <realm/util/http.hpp>
36 using WriteCompletionHandler =
37 std::function<void(std::error_code, size_t num_bytes_transferred)>;
38 using ReadCompletionHandler =
39 std::function<void(std::error_code, size_t num_bytes_transferred)>;
43 /// The Socket uses the caller supplied logger for logging.
44 virtual util::Logger& websocket_get_logger() noexcept = 0;
46 /// The Socket needs random numbers to satisfy the Websocket protocol.
47 /// The caller must supply a random number generator.
48 virtual std::mt19937_64& websocket_get_random() noexcept = 0;
51 /// The three functions below are used by the Socket to read and write to the underlying
52 /// stream. The functions will typically be implemented as wrappers to a TCP/TLS stream,
53 /// but could also map to pure memory streams. These functions abstract away the details of
54 /// the underlying sockets.
55 /// The functions have the same semantics as util::Socket.
57 /// FIXME: Require that implementations ensure no callback reentrance, i.e.,
58 /// that the completion handler is never called from within the execution of
59 /// async_write(), async_read(), or async_read_until(). This guarantee is
60 /// provided by both network::Socket and network::ssl::Stream.
61 virtual void async_write(const char* data, size_t size, WriteCompletionHandler handler) = 0;
62 virtual void async_read(char* buffer, size_t size, ReadCompletionHandler handler) = 0;
63 virtual void async_read_until(char* buffer, size_t size, char delim, ReadCompletionHandler handler) = 0;
66 /// websocket_handshake_completion_handler() is called when the websocket is connected, .i.e.
67 /// after the handshake is done. It is not allowed to send messages on the socket before the
68 /// handshake is done. No message_received callbacks will be called before the handshake is done.
69 virtual void websocket_handshake_completion_handler(const HTTPHeaders&) = 0;
72 /// websocket_read_error_handler() and websocket_write_error_handler() are called when an
73 /// error occurs on the underlying stream given by the async_read and async_write functions above.
74 /// The error_code is passed through.
75 /// websocket_handshake_error_handler() will be called when there is an error in the handshake
76 /// such as "404 Not found".
77 /// websocket_protocol_error_handler() is called when there is an protocol error in the incoming
78 /// websocket messages.
79 /// After calling any of these error callbacks, the Socket will move into the stopped state, and
80 /// no more messages should be sent, or will be received.
81 /// It is safe to destroy the WebSocket object in these handlers.
82 virtual void websocket_read_error_handler(std::error_code) = 0;
83 virtual void websocket_write_error_handler(std::error_code) = 0;
84 virtual void websocket_handshake_error_handler(std::error_code, const HTTPHeaders&) = 0;
85 virtual void websocket_protocol_error_handler(std::error_code) = 0;
89 /// The five callback functions below are called whenever a full message has arrived.
90 /// The Socket defragments fragmented messages internally and delivers a full message.
91 /// The message is delivered in the buffer \param data of size \param size.
92 /// The buffer is only valid until the function returns.
93 /// The return value designates whether the WebSocket object should continue
94 /// processing messages. The normal return value is true. False must be returned if the
95 /// websocket object is destroyed during execution of the function.
96 virtual bool websocket_text_message_received(const char* data, size_t size);
97 virtual bool websocket_binary_message_received(const char* data, size_t size);
98 virtual bool websocket_close_message_received(const char* data, size_t size);
99 virtual bool websocket_ping_message_received(const char* data, size_t size);
100 virtual bool websocket_pong_message_received(const char* data, size_t size);
118 Socket(Socket&&) noexcept;
121 /// initiate_client_handshake() starts the Socket in client mode. The Socket
122 /// will send the HTTP request that initiates the WebSocket protocol and
123 /// wait for the HTTP response from the server. The HTTP request will
124 /// contain the \param request_uri in the HTTP request line. The \param host
125 /// will be sent as the value in a HTTP Host header line.
126 /// \param sec_websocket_protocol will be set as header value for
127 /// Sec-WebSocket-Protocol. Extra HTTP headers can be provided in \a headers.
129 /// When the server responds with a valid HTTP response, the callback
130 /// function websocket_handshake_completion_handler() is called. Messages
131 /// can only be sent and received after the handshake has completed.
132 void initiate_client_handshake(const std::string& request_uri,
133 const std::string& host,
134 const std::string& sec_websocket_protocol,
135 HTTPHeaders headers = HTTPHeaders{});
137 /// initiate_server_handshake() starts the Socket in server mode. It will
138 /// wait for a HTTP request from a client and respond with a HTTP response.
139 /// After sending a HTTP response, websocket_handshake_completion_handler()
140 /// is called. Messages can only be sent and received after the handshake
142 void initiate_server_handshake();
144 /// initiate_server_websocket_after_handshake() starts the Socket in a state
145 /// where it will read and write WebSocket messages but it will expect the
146 /// handshake to have been completed by the caller. The use of this
147 /// function is to perform HTTP routing externally and then start the
148 /// WebSocket in case the HTTP request is an Upgrade to WebSocket.
149 /// Typically, the caller will have used make_http_response() to send the
150 /// HTTP response itself.
151 void initiate_server_websocket_after_handshake();
153 /// The async_write_* functions send frames. Only one frame should be sent at a time,
154 /// meaning that the user must wait for the handler to be called before sending the next frame.
155 /// The handler is type std::function<void()> and is called when the frame has been successfully
156 /// sent. In case of errors, the Config::websocket_write_error_handler() is called.
158 /// async_write_frame() sends a single frame with the fin bit set to 0 or 1 from \param fin, and the opcode
159 /// set by \param opcode. The frame payload is taken from \param data of size \param size. \param handler is
160 /// called when the frame has been successfully sent. Error s are reported through
161 /// websocket_write_error_handler() in Config.
162 /// This function is rather low level and should only be used with knowledge of the WebSocket protocol.
163 /// The five utility functions below are recommended for message sending.
165 /// FIXME: Guarantee no callback reentrance, i.e., that the completion
166 /// handler, or the error handler in case an error occurs, is never called
167 /// from within the execution of async_write_frame().
168 void async_write_frame(bool fin, Opcode opcode, const char* data, size_t size, std::function<void()> handler);
171 /// Five utility functions used to send whole messages. These five functions are implemented in terms of
172 /// async_write_frame(). These functions send whole unfragmented messages. These functions should be
173 /// preferred over async_write_frame() for most use cases.
175 /// FIXME: Guarantee no callback reentrance, i.e., that the completion
176 /// handler, or the error handler in case an error occurs, is never called
177 /// from within the execution of async_write_text(), and its friends. This
178 /// is already assumed by the client and server implementations of the sync
180 void async_write_text(const char* data, size_t size, std::function<void()> handler);
181 void async_write_binary(const char* data, size_t size, std::function<void()> handler);
182 void async_write_close(const char* data, size_t size, std::function<void()> handler);
183 void async_write_ping(const char* data, size_t size, std::function<void()> handler);
184 void async_write_pong(const char* data, size_t size, std::function<void()> handler);
187 /// stop() stops the socket. The socket will stop processing incoming data, sending data, and calling callbacks.
188 /// It is an error to attempt to send a message after stop() has been called. stop() will typically be called
189 /// before the underlying TCP/TLS connection is closed. The Socket can be restarted with
190 /// initiate_client_handshake() and initiate_server_handshake().
191 void stop() noexcept;
195 std::unique_ptr<Impl> m_impl;
199 /// read_sec_websocket_protocol() returns the value of the
200 /// header Sec-WebSocket-Protocol in the http request \a request.
201 /// None is returned if the header Sec-WebSocket-Protocol is absent
203 util::Optional<std::string> read_sec_websocket_protocol(const HTTPRequest& request);
205 /// make_http_response() takes \a request as a WebSocket handshake request,
206 /// validates it, and makes a HTTP response. If the request is invalid, the
207 /// return value is None, and ec is set to Error::bad_handshake_request.
208 util::Optional<HTTPResponse> make_http_response(const HTTPRequest& request,
209 const std::string& sec_websocket_protocol,
210 std::error_code& ec);
213 bad_request_header_upgrade = 1,
214 bad_request_header_connection = 2,
215 bad_request_header_websocket_version = 3,
216 bad_request_header_websocket_key = 4,
217 bad_handshake_request = 5,
218 bad_handshake_response = 6,
219 bad_handshake_response_404_not_found = 7,
220 bad_handshake_response_50x_temporary = 8,
224 const std::error_category& error_category() noexcept;
226 std::error_code make_error_code(Error) noexcept;
228 } // namespace websocket
234 template<> struct is_error_code_enum<realm::util::websocket::Error> {
235 static const bool value = true;
240 #endif // REALM_UTIL_WEBSOCKET_HPP