1 module hunt.http.codec.websocket.model.ExtensionConfig; 2 3 import hunt.string.QuoteUtil; 4 5 import hunt.container; 6 import hunt.lang.exception; 7 import hunt.string; 8 9 import std.array; 10 import std.container.array; 11 import std.conv; 12 import std.range; 13 import std.string; 14 15 /** 16 * Represents an Extension Configuration, as seen during the connection Handshake process. 17 */ 18 class ExtensionConfig { 19 /** 20 * Parse a single parameterized name. 21 * 22 * @param parameterizedName the parameterized name 23 * @return the ExtensionConfig 24 */ 25 static ExtensionConfig parse(string parameterizedName) { 26 return new ExtensionConfig(parameterizedName); 27 } 28 29 /** 30 * Parse enumeration of <code>Sec-WebSocket-Extensions</code> header values into a {@link ExtensionConfig} list 31 * 32 * @param valuesEnum the raw header values enum 33 * @return the list of extension configs 34 */ 35 static Array!ExtensionConfig parseEnum(InputRange!string valuesEnum) { 36 Array!(ExtensionConfig) configs; 37 38 if (valuesEnum !is null) { 39 foreach(string value; valuesEnum) { 40 InputRange!string extTokenIter = QuoteUtil.splitAt(value, ","); 41 foreach(string extToken; extTokenIter) { 42 // configs.add(ExtensionConfig.parse(extToken)); 43 configs.insert(ExtensionConfig.parse(extToken)); 44 } 45 } 46 } 47 48 return configs; 49 } 50 51 /** 52 * Parse 1 or more raw <code>Sec-WebSocket-Extensions</code> header values into a {@link ExtensionConfig} list 53 * 54 * @param rawSecWebSocketExtensions the raw header values 55 * @return the list of extension configs 56 */ 57 // static List<ExtensionConfig> parseList(string... rawSecWebSocketExtensions) { 58 // List<ExtensionConfig> configs = new ArrayList<>(); 59 60 // for (string rawValue : rawSecWebSocketExtensions) { 61 // Iterator<string> extTokenIter = QuoteUtil.splitAt(rawValue, ","); 62 // while (extTokenIter.hasNext()) { 63 // string extToken = extTokenIter.next(); 64 // configs.add(ExtensionConfig.parse(extToken)); 65 // } 66 // } 67 68 // return configs; 69 // } 70 71 /** 72 * Convert a list of {@link ExtensionConfig} to a header value 73 * 74 * @param configs the list of extension configs 75 * @return the header value (null if no configs present) 76 */ 77 static string toHeaderValue(ExtensionConfig[] configs) { 78 if (configs.empty()) { 79 return null; 80 } 81 Appender!(string) parameters; 82 bool needsDelim = false; 83 foreach (ExtensionConfig ext ; configs) { 84 if (needsDelim) { 85 parameters.put(", "); 86 } 87 parameters.put(ext.getParameterizedName()); 88 needsDelim = true; 89 } 90 return parameters.data; 91 } 92 93 private string name; 94 private Map!(string, string) parameters; 95 96 /** 97 * Copy constructor 98 * 99 * @param copy the extension config to copy 100 */ 101 this(ExtensionConfig copy) { 102 this.name = copy.name; 103 this.parameters = new HashMap!(string, string)(); 104 this.parameters.putAll(copy.parameters); 105 } 106 107 this(string parameterizedName) { 108 InputRange!string extListIter = QuoteUtil.splitAt(parameterizedName, ";"); 109 this.name = extListIter.front(); 110 this.parameters = new HashMap!(string, string)(); 111 112 // now for parameters 113 extListIter.popFront(); 114 while (!extListIter.empty()) { 115 string extParam = extListIter.front(); 116 InputRange!string extParamIter = QuoteUtil.splitAt(extParam, "="); 117 118 string key = extParamIter.front().strip(); 119 extParamIter.popFront(); 120 121 string value = null; 122 if (!extParamIter.empty()) { 123 value = extParamIter.front(); 124 } 125 parameters.put(key, value); 126 extListIter.popFront(); 127 } 128 } 129 130 string getName() { 131 return name; 132 } 133 134 final int getParameter(string key, int defValue) { 135 string val = parameters.get(key); 136 if (val is null) { 137 return defValue; 138 } 139 return to!int(val); 140 } 141 142 final string getParameter(string key, string defValue) { 143 string val = parameters.get(key); 144 if (val is null) { 145 return defValue; 146 } 147 return val; 148 } 149 150 final string getParameterizedName() { 151 StringBuilder str = new StringBuilder(); 152 str.append(name); 153 foreach (string param ; parameters.byKey) { 154 str.append(';'); 155 str.append(param); 156 string value = parameters.get(param); 157 if (value !is null) { 158 str.append('='); 159 QuoteUtil.quoteIfNeeded(str, value, ";="); 160 } 161 } 162 return str.toString(); 163 } 164 165 final InputRange!string getParameterKeys() { 166 return parameters.byKey; 167 } 168 169 /** 170 * Return parameters found in request URI. 171 * 172 * @return the parameter map 173 */ 174 final Map!(string, string) getParameters() { 175 return parameters; 176 } 177 178 /** 179 * Initialize the parameters on this config from the other configuration. 180 * 181 * @param other the other configuration. 182 */ 183 final void initialize(ExtensionConfig other) { 184 this.parameters.clear(); 185 this.parameters.putAll(other.parameters); 186 } 187 188 final void setParameter(string key) { 189 parameters.put(key, null); 190 } 191 192 final void setParameter(string key, int value) { 193 parameters.put(key, value.to!string()); 194 } 195 196 final void setParameter(string key, string value) { 197 parameters.put(key, value); 198 } 199 200 override 201 string toString() { 202 return getParameterizedName(); 203 } 204 }