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 }