1 module hunt.http.codec.websocket.stream.WebSocketPolicy;
2 
3 import hunt.http.codec.websocket.exception;
4 import hunt.http.codec.websocket.model.StatusCode;
5 import hunt.http.codec.websocket.model.common;
6 
7 import hunt.lang.exception;
8 import hunt.string;
9 
10 import std.conv;
11 import std.format;
12 
13 /**
14  * Settings for WebSocket operations.
15  */
16 class WebSocketPolicy {
17     private enum int KB = 1024;
18 
19     static WebSocketPolicy newClientPolicy() {
20         return new WebSocketPolicy(WebSocketBehavior.CLIENT);
21     }
22 
23     static WebSocketPolicy newServerPolicy() {
24         return new WebSocketPolicy(WebSocketBehavior.SERVER);
25     }
26 
27     /**
28      * The maximum size of a text message during parsing/generating.
29      * <p>
30      * Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
31      * <p>
32      * Default: 65536 (64 K)
33      */
34     private int maxTextMessageSize = 64 * KB;
35 
36     /**
37      * The maximum size of a text message buffer.
38      * <p>
39      * Used ONLY for stream based message writing.
40      * <p>
41      * Default: 32768 (32 K)
42      */
43     private int maxTextMessageBufferSize = 32 * KB;
44 
45     /**
46      * The maximum size of a binary message during parsing/generating.
47      * <p>
48      * Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
49      * <p>
50      * Default: 65536 (64 K)
51      */
52     private int maxBinaryMessageSize = 64 * KB;
53 
54     /**
55      * The maximum size of a binary message buffer
56      * <p>
57      * Used ONLY for for stream based message writing
58      * <p>
59      * Default: 32768 (32 K)
60      */
61     private int maxBinaryMessageBufferSize = 32 * KB;
62 
63     /**
64      * The timeout in ms (milliseconds) for async write operations.
65      * <p>
66      * Negative values indicate a disabled timeout.
67      */
68     private long asyncWriteTimeout = 60000;
69 
70     /**
71      * The time in ms (milliseconds) that a websocket may be idle before closing.
72      * <p>
73      * Default: 300000 (ms)
74      */
75     private long idleTimeout = 300000;
76 
77     /**
78      * The size of the input (read from network layer) buffer size.
79      * <p>
80      * Default: 4096 (4 K)
81      */
82     private int inputBufferSize = 4 * KB;
83 
84     /**
85      * Behavior of the websockets
86      */
87     private WebSocketBehavior behavior;
88 
89     this(WebSocketBehavior behavior) {
90         this.behavior = behavior;
91     }
92 
93     private void assertLessThan(string name, long size, string otherName, long otherSize) {
94         if (size > otherSize) {
95             throw new IllegalArgumentException(format("%s [%d] must be less than %s [%d]", name, size, otherName, otherSize));
96         }
97     }
98 
99     private void assertGreaterThan(string name, long size, long minSize) {
100         if (size < minSize) {
101             throw new IllegalArgumentException(format("%s [%d] must be a greater than or equal to %d", name, size, minSize));
102         }
103     }
104 
105     void assertValidBinaryMessageSize(int requestedSize) {
106         if (maxBinaryMessageSize > 0) {
107             // validate it
108             if (requestedSize > maxBinaryMessageSize) {
109                 throw new MessageTooLargeException("Binary message size [" ~ requestedSize.to!string() ~ "] exceeds maximum size [" ~ maxBinaryMessageSize.to!string() ~ "]");
110             }
111         }
112     }
113 
114     void assertValidTextMessageSize(int requestedSize) {
115         if (maxTextMessageSize > 0) {
116             // validate it
117             if (requestedSize > maxTextMessageSize) {
118                 throw new MessageTooLargeException("Text message size [" ~ requestedSize.to!string() ~ "] exceeds maximum size [" ~ maxTextMessageSize.to!string() ~ "]");
119             }
120         }
121     }
122 
123     WebSocketPolicy clonePolicy() {
124         WebSocketPolicy clone = new WebSocketPolicy(this.behavior);
125         clone.idleTimeout = this.idleTimeout;
126         clone.maxTextMessageSize = this.maxTextMessageSize;
127         clone.maxTextMessageBufferSize = this.maxTextMessageBufferSize;
128         clone.maxBinaryMessageSize = this.maxBinaryMessageSize;
129         clone.maxBinaryMessageBufferSize = this.maxBinaryMessageBufferSize;
130         clone.inputBufferSize = this.inputBufferSize;
131         clone.asyncWriteTimeout = this.asyncWriteTimeout;
132         return clone;
133     }
134 
135     /**
136      * The timeout in ms (milliseconds) for async write operations.
137      * <p>
138      * Negative values indicate a disabled timeout.
139      *
140      * @return the timeout for async write operations. negative values indicate disabled timeout.
141      */
142     long getAsyncWriteTimeout() {
143         return asyncWriteTimeout;
144     }
145 
146     WebSocketBehavior getBehavior() {
147         return behavior;
148     }
149 
150     /**
151      * The time in ms (milliseconds) that a websocket connection mad by idle before being closed automatically.
152      *
153      * @return the timeout in milliseconds for idle timeout.
154      */
155     long getIdleTimeout() {
156         return idleTimeout;
157     }
158 
159     /**
160      * The size of the input (read from network layer) buffer size.
161      * <p>
162      * This is the raw read operation buffer size, before the parsing of the websocket frames.
163      *
164      * @return the raw network bytes read operation buffer size.
165      */
166     int getInputBufferSize() {
167         return inputBufferSize;
168     }
169 
170     /**
171      * Get the maximum size of a binary message buffer (for streaming writing)
172      *
173      * @return the maximum size of a binary message buffer
174      */
175     int getMaxBinaryMessageBufferSize() {
176         return maxBinaryMessageBufferSize;
177     }
178 
179     /**
180      * Get the maximum size of a binary message during parsing.
181      * <p>
182      * This is a memory conservation option, memory over this limit will not be
183      * allocated by Hunt for handling binary messages.  This applies to individual frames,
184      * whole message handling, and partial message handling.
185      * </p>
186      * <p>
187      * Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
188      * </p>
189      *
190      * @return the maximum size of a binary message
191      */
192     int getMaxBinaryMessageSize() {
193         return maxBinaryMessageSize;
194     }
195 
196     /**
197      * Get the maximum size of a text message buffer (for streaming writing)
198      *
199      * @return the maximum size of a text message buffer
200      */
201     int getMaxTextMessageBufferSize() {
202         return maxTextMessageBufferSize;
203     }
204 
205     /**
206      * Get the maximum size of a text message during parsing.
207      * <p>
208      * This is a memory conservation option, memory over this limit will not be
209      * allocated by Hunt for handling text messages.  This applies to individual frames,
210      * whole message handling, and partial message handling.
211      * </p>
212      * <p>
213      * Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
214      * </p>
215      *
216      * @return the maximum size of a text message.
217      */
218     int getMaxTextMessageSize() {
219         return maxTextMessageSize;
220     }
221 
222     /**
223      * The timeout in ms (milliseconds) for async write operations.
224      * <p>
225      * Negative values indicate a disabled timeout.
226      *
227      * @param ms the timeout in milliseconds
228      */
229     void setAsyncWriteTimeout(long ms) {
230         assertLessThan("AsyncWriteTimeout", ms, "IdleTimeout", idleTimeout);
231         this.asyncWriteTimeout = ms;
232     }
233 
234     /**
235      * The time in ms (milliseconds) that a websocket may be idle before closing.
236      *
237      * @param ms the timeout in milliseconds
238      */
239     void setIdleTimeout(long ms) {
240         assertGreaterThan("IdleTimeout", ms, 0);
241         this.idleTimeout = ms;
242     }
243 
244     /**
245      * The size of the input (read from network layer) buffer size.
246      *
247      * @param size the size in bytes
248      */
249     void setInputBufferSize(int size) {
250         assertGreaterThan("InputBufferSize", size, 1);
251         this.inputBufferSize = size;
252     }
253 
254     /**
255      * The maximum size of a binary message buffer.
256      * <p>
257      * Used ONLY for stream based binary message writing.
258      *
259      * @param size the maximum size of the binary message buffer
260      */
261     void setMaxBinaryMessageBufferSize(int size) {
262         assertGreaterThan("MaxBinaryMessageBufferSize", size, 1);
263 
264         this.maxBinaryMessageBufferSize = size;
265     }
266 
267     /**
268      * The maximum size of a binary message during parsing.
269      * <p>
270      * This is a memory conservation option, memory over this limit will not be
271      * allocated by Hunt for handling binary messages.  This applies to individual frames,
272      * whole message handling, and partial message handling.
273      * </p>
274      * <p>
275      * Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
276      * </p>
277      *
278      * @param size the maximum allowed size of a binary message.
279      */
280     void setMaxBinaryMessageSize(int size) {
281         assertGreaterThan("MaxBinaryMessageSize", size, -1);
282 
283         this.maxBinaryMessageSize = size;
284     }
285 
286     /**
287      * The maximum size of a text message buffer.
288      * <p>
289      * Used ONLY for stream based text message writing.
290      *
291      * @param size the maximum size of the text message buffer
292      */
293     void setMaxTextMessageBufferSize(int size) {
294         assertGreaterThan("MaxTextMessageBufferSize", size, 1);
295 
296         this.maxTextMessageBufferSize = size;
297     }
298 
299     /**
300      * The maximum size of a text message during parsing.
301      * <p>
302      * This is a memory conservation option, memory over this limit will not be
303      * allocated by Hunt for handling text messages.  This applies to individual frames,
304      * whole message handling, and partial message handling.
305      * </p>
306      * <p>
307      * Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
308      * </p>
309      *
310      * @param size the maximum allowed size of a text message.
311      */
312     void setMaxTextMessageSize(int size) {
313         assertGreaterThan("MaxTextMessageSize", size, -1);
314 
315         this.maxTextMessageSize = size;
316     }
317 
318     override
319     string toString() {
320         StringBuilder builder = new StringBuilder();
321         builder.append("WebSocketPolicy@").append(toHash().to!string(16));
322         builder.append("[behavior=").append(behavior);
323         builder.append(",maxTextMessageSize=").append(maxTextMessageSize);
324         builder.append(",maxTextMessageBufferSize=").append(maxTextMessageBufferSize);
325         builder.append(",maxBinaryMessageSize=").append(maxBinaryMessageSize);
326         builder.append(",maxBinaryMessageBufferSize=").append(maxBinaryMessageBufferSize);
327         builder.append(",asyncWriteTimeout=").append(cast(int)asyncWriteTimeout);
328         builder.append(",idleTimeout=").append(cast(int)idleTimeout);
329         builder.append(",inputBufferSize=").append(inputBufferSize);
330         builder.append("]");
331         return builder.toString();
332     }
333 }