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