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 }