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 }