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 }