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 }