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