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 }