1 module hunt.http.WebSocketConnection; 2 3 import hunt.http.HttpRequest; 4 import hunt.http.HttpResponse; 5 import hunt.http.codec.websocket.stream.IOState; 6 import hunt.http.WebSocketFrame; 7 import hunt.http.WebSocketPolicy; 8 import hunt.http.HttpConnection; 9 10 import hunt.io.ByteBuffer; 11 import hunt.concurrency.FuturePromise; 12 import hunt.Exceptions; 13 import hunt.Functions; 14 import hunt.logging; 15 import hunt.net.Connection; 16 import hunt.util.Common; 17 18 19 deprecated("Using WebSocketConnectionState instead.") 20 alias ConnectionState = WebSocketConnectionState; 21 22 /** 23 * Connection states as outlined in <a href="https://tools.ietf.org/html/rfc6455">RFC6455</a>. 24 */ 25 enum WebSocketConnectionState { 26 Unknown, 27 28 /** 29 * [RFC] Initial state of a connection, the upgrade request / response is in progress 30 */ 31 CONNECTING, 32 /** 33 * [Impl] Intermediate state between CONNECTING and OPEN, used to indicate that a upgrade request/response is successful, but the end-user provided socket's 34 * onOpen code has yet to run. 35 * <p> 36 * This state is to allow the local socket to initiate messages and frames, but to NOT start reading yet. 37 */ 38 CONNECTED, 39 /** 40 * [RFC] The websocket connection is established and open. 41 * <p> 42 * This indicates that the Upgrade has succeed, and the end-user provided socket's onOpen code has completed. 43 * <p> 44 * It is now time to start reading from the remote endpoint. 45 */ 46 OPEN, 47 /** 48 * [RFC] The websocket closing handshake is started. 49 * <p> 50 * This can be considered a half-closed state. 51 * <p> 52 * When receiving this as an event on {@link ConnectionStateListener#onConnectionStateChange(ConnectionState)} a close frame should be sent using 53 * the {@link CloseInfo} available from {@link IOState#getCloseInfo()} 54 */ 55 CLOSING, 56 /** 57 * [RFC] The websocket connection is closed. 58 * <p> 59 * Connection should be disconnected and no further reads or writes should occur. 60 */ 61 CLOSED 62 } 63 64 65 /** 66 * Interface for dealing with Incoming Frames. 67 */ 68 interface IncomingFrames { 69 70 void incomingError(Exception t); 71 72 /** 73 * Process the incoming frame. 74 * <p> 75 * Note: if you need to hang onto any information from the frame, be sure 76 * to copy it, as the information contained in the Frame will be released 77 * and/or reused by the implementation. 78 * 79 * @param frame the frame to process 80 */ 81 void incomingFrame(WebSocketFrame frame); 82 } 83 84 85 /** 86 * Interface for dealing with frames outgoing to (eventually) the network layer. 87 */ 88 interface OutgoingFrames { 89 /** 90 * A frame, and optional callback, intended for the network layer. 91 * <p> 92 * Note: the frame can undergo many transformations in the various 93 * layers and extensions present in the implementation. 94 * <p> 95 * If you are implementing a mutation, you are obliged to handle 96 * the incoming WriteCallback appropriately. 97 * 98 * @param frame the frame to eventually write to the network layer. 99 * @param callback the callback to notify when the frame is written. 100 */ 101 void outgoingFrame(WebSocketFrame frame, Callback callback); 102 103 } 104 105 106 /** 107 * 108 */ 109 interface WebSocketConnection : OutgoingFrames, HttpConnection { 110 111 /** 112 * Register the connection close callback. 113 * 114 * @param closedListener The connection close callback. 115 * @return The WebSocket connection. 116 */ 117 // WebSocketConnection onClose(Action1!(WebSocketConnection) closedListener); 118 119 /** 120 * Register the exception callback. 121 * 122 * @param exceptionListener The exception callback. 123 * @return The WebSocket connection. 124 */ 125 // WebSocketConnection onException(Action2!(WebSocketConnection, Exception) exceptionListener); 126 127 /** 128 * Get the read/write idle timeout. 129 * 130 * @return the idle timeout in milliseconds 131 */ 132 // long getIdleTimeout(); 133 134 bool isConnected(); 135 136 /** 137 * Get the IOState of the connection. 138 * 139 * @return the IOState of the connection. 140 */ 141 IOState getIOState(); 142 143 /** 144 * The policy that the connection is running under. 145 * 146 * @return the policy for the connection 147 */ 148 WebSocketPolicy getPolicy(); 149 150 /** 151 * Generate random 4bytes mask key 152 * 153 * @return the mask key 154 */ 155 byte[] generateMask(); 156 157 /** 158 * Send text message. 159 * 160 * @param text The text message. 161 * @return The future result. 162 */ 163 FuturePromise!(bool) sendText(string text); 164 165 /** 166 * Send binary message. 167 * 168 * @param data The binary message. 169 * @return The future result. 170 */ 171 FuturePromise!(bool) sendData(byte[] data); 172 173 /** 174 * Send binary message. 175 * 176 * @param data The binary message. 177 * @return The future result. 178 */ 179 FuturePromise!(bool) sendData(ByteBuffer data); 180 181 /** 182 * Get the websocket upgrade request. 183 * 184 * @return The upgrade request. 185 */ 186 HttpRequest getUpgradeRequest(); 187 188 /** 189 * Get the websocket upgrade response. 190 * 191 * @return The upgrade response. 192 */ 193 HttpResponse getUpgradeResponse(); 194 195 final string getPath() { 196 return getUpgradeRequest().getURI().getPath(); 197 } 198 199 } 200 201 202 /** 203 * 204 */ 205 interface WebSocketMessageHandler { 206 207 void onOpen(WebSocketConnection connection); 208 209 void onClosed(WebSocketConnection connection); // CloseStatus closeStatus 210 211 void onPing(WebSocketConnection connection); 212 213 void onPong(WebSocketConnection connection); 214 215 void onText(WebSocketConnection connection, string text); 216 217 void onBinary(WebSocketConnection connection, ByteBuffer buffer); 218 219 void onContinuation(WebSocketConnection connection, ByteBuffer buffer); 220 221 void onError(WebSocketConnection connection, Exception exception); 222 223 alias onFailure = onError; 224 } 225 226 /** 227 * See_Also: 228 * WebSocketListener from OKHTTP3 229 */ 230 abstract class AbstractWebSocketMessageHandler : WebSocketMessageHandler { 231 232 void onOpen(WebSocketConnection connection) { implementationMissing(false); } 233 234 void onClosed(WebSocketConnection connection) { 235 version(HUNT_HTTP_DEBUG) infof("closed with %s", connection.getRemoteAddress()); 236 } 237 238 void onPing(WebSocketConnection connection) { 239 version(HUNT_HTTP_DEBUG) tracef("ping from %s", connection.getRemoteAddress()); 240 } 241 242 void onPong(WebSocketConnection connection) { 243 version(HUNT_HTTP_DEBUG) tracef("ping from %s", connection.getRemoteAddress()); 244 } 245 246 void onText(WebSocketConnection connection, string text) { 247 version(HUNT_HTTP_DEBUG) tracef("received (from %s): %s", connection.getRemoteAddress(), text); 248 } 249 250 void onBinary(WebSocketConnection connection, ByteBuffer buffer) { implementationMissing(false); } 251 252 void onContinuation(WebSocketConnection connection, ByteBuffer buffer) { implementationMissing(false); } 253 254 void onError(WebSocketConnection connection, Exception ex) { 255 debug warningf("error (from %s): %s", connection.getRemoteAddress(), ex.msg); 256 version(HUNT_DEBUG) warning(ex); 257 } 258 }