1 module collie.codec.http.headers;
2 
3 import collie.utils..string;
4 import kiss.container.Vector;
5 import core.stdc..string;
6 import std..string;
7 import std.array;
8 
9 public import collie.codec.http.headers.httpcommonheaders;
10 public import collie.codec.http.headers.httpmethod;
11 
12 struct HTTPHeaders
13 {
14 	enum kInitialVectorReserve = 32;
15 	
16 	/**
17    * Remove all instances of the given header, returning true if anything was
18    * removed and false if this header didn't exist in our set.
19    */
20 	bool remove(string name){
21 		HTTPHeaderCode code = headersHash(name);
22 		if(code != HTTPHeaderCode.OTHER)
23 			return remove(code);
24 		bool removed = false;
25 		for(size_t i = 0; i < _headersNames.length; ++i){
26 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
27 			if(isSameIngnoreLowUp(name,_headersNames[i])){
28 				_codes[i] = HTTPHeaderCode.NONE;
29 				_headersNames[i] = null;
30 				_headerValues[i] = null;
31 				_deletedCount ++;
32 				removed = true;
33 			}
34 		}
35 		return removed;
36 	}
37 
38 	bool remove(HTTPHeaderCode code){
39 		bool removed = false;
40 		HTTPHeaderCode[] codes = _codes;
41 		HTTPHeaderCode * ptr = codes.ptr;
42 		const size_t len = codes.length;
43 		while(true)
44 		{
45 			size_t tlen = len - (ptr - codes.ptr);
46 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
47 			if(ptr is null)
48 				break;
49 			tlen = ptr - codes.ptr;
50 			ptr ++;
51 			_codes[tlen] = HTTPHeaderCode.NONE;
52 			_headersNames[tlen] = null;
53 			_headerValues[tlen] = null;
54 			_deletedCount ++;
55 			removed = true;
56 		}
57 		return removed;
58 	}
59 
60 	void add(string name, string value)
61 	in{
62 		assert(name.length > 0);
63 	}
64 	body{
65 		HTTPHeaderCode code = headersHash(name);
66 		_codes ~= (code);
67 		_headersNames ~= ((code == HTTPHeaderCode.OTHER) ? name : HTTPHeaderCodeName[code]);
68 		_headerValues~= value;
69 
70 	}
71 	void add(HTTPHeaderCode code, string value)
72 	{
73 		if(code == HTTPHeaderCode.OTHER || code > HTTPHeaderCode.SEC_WEBSOCKET_ACCEPT)
74 			return;
75 		_codes ~= code;
76 		_headersNames ~= HTTPHeaderCodeName[code];
77 		_headerValues~= value;
78 	}
79 
80 	void set(string name,string value)
81 	{
82 		remove(name);
83 		add(name, value);
84 	}
85 
86 	void set(HTTPHeaderCode code, string value)
87 	{
88 		remove(code);
89 		add(code, value);
90 	}
91 
92 	bool exists(string name)
93 	{
94 		HTTPHeaderCode code = headersHash(name);
95 		if(code != HTTPHeaderCode.OTHER)
96 			return exists(code);
97 		for(size_t i = 0; i < _headersNames.length; ++i){
98 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
99 			if(isSameIngnoreLowUp(name,_headersNames[i])){
100 				return true;
101 			}
102 		}
103 		return false;
104 	}
105 
106 	bool exists(HTTPHeaderCode code)
107 	{
108 		HTTPHeaderCode[] codes = _codes;
109 		return memchr(codes.ptr,code,codes.length) != null;
110 	}
111 
112 	void removeAll()
113 	{
114 		_codes = (HTTPHeaderCode[]).init;
115 		_headersNames = (string[]).init;
116 		_headerValues = (string[]).init;
117 		_deletedCount = 0;
118 	}
119 
120 	int opApply(scope int delegate(string name,string value) opeartions)
121 	{
122 		int result = 0;
123 		for(size_t i = 0; i < _headersNames.length; ++i)
124 		{
125 			result = opeartions(_headersNames[i], _headerValues[i]);
126 			if(result)
127 				break;
128 		}
129 		return result;
130 	}
131 
132 	int opApply(scope int delegate(HTTPHeaderCode code,string name,string value) opeartions)
133 	{
134 		int result = 0;
135 		for(size_t i = 0; i < _headersNames.length; ++i)
136 		{
137 			result = opeartions(_codes[i],_headersNames[i], _headerValues[i]);
138 			if(result)
139 				break;
140 		}
141 		return result;
142 	}
143 
144 	HTTPHeaders dub()
145 	{
146 		HTTPHeaders header;
147 		copyTo(header);
148 		return header;
149 	}
150 
151 	void copyTo(ref HTTPHeaders header)
152 	{
153 		foreach(code,name,value; this)
154 		{
155 			if(code == HTTPHeaderCode.NONE) continue;
156 			if(code == HTTPHeaderCode.OTHER)
157 				header.add(name,value);
158 			else
159 				header.add(code,value);
160 		}
161 	}
162 	/**
163    * Get the total number of headers.
164    */
165 	size_t size() const{
166 		return _codes.length - _deletedCount;
167 	}
168 	/**
169    * combine all the value for this header into a string
170    */
171 	string combine(string separator = ", ")
172 	{
173 		Appender!string data = appender!string();
174 		bool frist = true;
175 		foreach(code,name,value; this)
176 		{
177 			if(code == HTTPHeaderCode.NONE) continue;
178 			if(frist) {
179 				data.put(value);
180 				frist = false;
181 			} else {
182 				data.put(separator);
183 				data.put(value);
184 			}
185 		}
186 		return data.data;
187 	}
188 
189 	size_t getNumberOfValues(string name)
190 	{
191 		HTTPHeaderCode code = headersHash(name);
192 		if(code != HTTPHeaderCode.OTHER)
193 			return remove(code);
194 		size_t index = 0;
195 		for(size_t i = 0; i < _headersNames.length; ++i){
196 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
197 			if(isSameIngnoreLowUp(name,_headersNames[i])){
198 				++index;
199 			}
200 		}
201 		return index;
202 	}
203 
204 	size_t getNumberOfValues(HTTPHeaderCode code)
205 	{
206 		size_t index = 0;
207 		HTTPHeaderCode[] codes = _codes;
208 		HTTPHeaderCode * ptr = codes.ptr;
209 		const size_t len = codes.length;
210 		while(true)
211 		{
212 			size_t tlen = len - (ptr - codes.ptr);
213 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
214 			if(ptr is null)
215 				break;
216 			ptr ++;
217 			++ index;
218 		}
219 		return index;
220 	}
221 
222 	string getSingleOrEmpty(string  name)  {
223 		HTTPHeaderCode code = headersHash(name);
224 		if(code != HTTPHeaderCode.OTHER)
225 			return getSingleOrEmpty(code);
226 		for(size_t i = 0; i < _headersNames.length; ++i){
227 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
228 			if(isSameIngnoreLowUp(name,_headersNames[i])){
229 				return _headerValues[i];
230 			}
231 		}
232 		return string.init;
233 	}
234 
235 	string getSingleOrEmpty(HTTPHeaderCode code)  {
236 		HTTPHeaderCode[] codes = _codes;
237 		HTTPHeaderCode * ptr = cast(HTTPHeaderCode *)memchr(codes.ptr,code,codes.length);
238 		if(ptr !is null){
239 			size_t index = ptr - codes.ptr;
240 			return _headerValues[index];
241 		}
242 		return string.init;
243 	}
244 
245 	/**
246    * Process the ordered list of values for the given header name:
247    * for each value, the function/functor/lambda-expression given as the second
248    * parameter will be executed. It should take one const string & parameter
249    * and return bool (false to keep processing, true to stop it). Example use:
250    *     hdrs.forEachValueOfHeader("someheader", [&] (const string& val) {
251    *       std::cout << val;
252    *       return false;
253    *     });
254    * This method returns true if processing was stopped (by func returning
255    * true), and false otherwise.
256    */
257 	alias LAMBDA = bool delegate(string value);
258 	bool forEachValueOfHeader(string name,scope LAMBDA func)
259 	{
260 		HTTPHeaderCode code = headersHash(name);
261 		if(code != HTTPHeaderCode.OTHER)
262 			return forEachValueOfHeader(code,func);
263 		size_t index = 0;
264 		for(size_t i = 0; i < _headersNames.length; ++i){
265 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
266 			if(isSameIngnoreLowUp(name,_headersNames[i])){
267 				if(func(_headerValues[i]))
268 					return true;
269 			}
270 		}
271 		return false;
272 	}
273 
274 	bool forEachValueOfHeader(HTTPHeaderCode code,scope LAMBDA func)
275 	{
276 		size_t index = 0;
277 		HTTPHeaderCode[] codes = _codes;
278 		HTTPHeaderCode * ptr = codes.ptr;
279 		const size_t len = codes.length;
280 		while(true)
281 		{
282 			size_t tlen = len - (ptr - codes.ptr);
283 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
284 			if(ptr is null)
285 				break;
286 			tlen = ptr - codes.ptr;
287 			ptr ++;
288 			if(func(_headerValues[tlen]))
289 				return true;
290 		}
291 		return false;
292 	}
293 private:
294 	HTTPHeaderCode[]  _codes ;// = Vector!(HTTPHeaderCode)(2);
295 	string[] _headersNames ;
296 	string[] _headerValues ;
297 	size_t _deletedCount = 0;
298 }