1 /******************************************************************************* 2 3 Copyright: 4 Copyright (c) 2004 Kris Bell. 5 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 6 All rights reserved. 7 8 License: 9 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 10 See LICENSE_TANGO.txt for details. 11 12 Version: Initial release: April 2004 13 14 Authors: Kris, John Reimer 15 16 *******************************************************************************/ 17 18 module ocean.net.http.HttpStack; 19 20 import ocean.core.ExceptionDefinitions; 21 import ocean.meta.types.Qualifiers; 22 23 import core.stdc.string: memmove; 24 import core.stdc.stdio; 25 import core.sys.posix.strings: strncasecmp; 26 27 /****************************************************************************** 28 29 Internal representation of a token 30 31 ******************************************************************************/ 32 33 class Token 34 { 35 private cstring value; 36 37 Token set (cstring value) 38 { 39 this.value = value; 40 return this; 41 } 42 43 cstring get() 44 { 45 return this.value; 46 } 47 48 // disabled because to work in D2 this requires 49 // hidden allocation 50 override istring toString () 51 { 52 printf(("Use Token.get instead of Token.toString :" ~ 53 " latter allocates each time. Aborting\n").ptr); 54 assert(0); 55 } 56 } 57 58 /****************************************************************************** 59 60 A stack of Tokens, used for capturing http headers. The tokens 61 themselves are typically mapped onto the content of a Buffer, 62 or some other external content, so there's minimal allocation 63 involved (typically zero). 64 65 ******************************************************************************/ 66 67 class HttpStack 68 { 69 private int depth; 70 private Token[] tokens; 71 72 private static immutable int MaxHttpStackSize = 256; 73 74 /********************************************************************** 75 76 Construct a HttpStack with the specified initial size. 77 The stack will later be resized as necessary. 78 79 **********************************************************************/ 80 81 this (int size = 10) 82 { 83 tokens = new Token[0]; 84 resize (tokens, size); 85 } 86 87 /********************************************************************** 88 89 Clone this stack of tokens 90 91 **********************************************************************/ 92 93 HttpStack clone () 94 { 95 // setup a new HttpStack of the same depth 96 HttpStack clone = new HttpStack(depth); 97 98 clone.depth = depth; 99 100 // duplicate the content of each original token 101 for (int i=0; i < depth; ++i) 102 clone.tokens[i].set (tokens[i].get().dup); 103 104 return clone; 105 } 106 107 /********************************************************************** 108 109 Iterate over all tokens in stack 110 111 **********************************************************************/ 112 113 int opApply (scope int delegate(ref Token) dg) 114 { 115 int result = 0; 116 117 for (int i=0; i < depth; ++i) 118 if ((result = dg (tokens[i])) != 0) 119 break; 120 return result; 121 } 122 123 /********************************************************************** 124 125 Pop the stack all the way back to zero 126 127 **********************************************************************/ 128 129 final void reset () 130 { 131 depth = 0; 132 } 133 134 /********************************************************************** 135 136 Scan the tokens looking for the first one with a matching 137 name. Returns the matching Token, or null if there is no 138 such match. 139 140 **********************************************************************/ 141 142 final Token findToken (cstring match) 143 { 144 Token tok; 145 146 for (int i=0; i < depth; ++i) 147 { 148 tok = tokens[i]; 149 if (isMatch (tok, match)) 150 return tok; 151 } 152 return null; 153 } 154 155 /********************************************************************** 156 157 Scan the tokens looking for the first one with a matching 158 name, and remove it. Returns true if a match was found, or 159 false if not. 160 161 **********************************************************************/ 162 163 final bool removeToken (cstring match) 164 { 165 for (int i=0; i < depth; ++i) 166 if (isMatch (tokens[i], match)) 167 { 168 tokens[i].value = null; 169 return true; 170 } 171 return false; 172 } 173 174 /********************************************************************** 175 176 Return the current stack depth 177 178 **********************************************************************/ 179 180 final int size () 181 { 182 return depth; 183 } 184 185 /********************************************************************** 186 187 Push a new token onto the stack, and set it content to 188 that provided. Returns the new Token. 189 190 **********************************************************************/ 191 192 final Token push (cstring content) 193 { 194 return push().set (content); 195 } 196 197 /********************************************************************** 198 199 Push a new token onto the stack, and set it content to 200 be that of the specified token. Returns the new Token. 201 202 **********************************************************************/ 203 204 final Token push (ref Token token) 205 { 206 return push (token.get()); 207 } 208 209 /********************************************************************** 210 211 Push a new token onto the stack, and return it. 212 213 **********************************************************************/ 214 215 final Token push () 216 { 217 if (depth == tokens.length) 218 resize (tokens, depth * 2); 219 return tokens[depth++]; 220 } 221 222 /********************************************************************** 223 224 Pop the stack by one. 225 226 **********************************************************************/ 227 228 final void pop () 229 { 230 if (depth) 231 --depth; 232 else 233 throw new IOException ("illegal attempt to pop Token stack"); 234 } 235 236 /********************************************************************** 237 238 See if the given token matches the specified text. The 239 two must match the minimal extent exactly. 240 241 **********************************************************************/ 242 243 final static bool isMatch (ref Token token, cstring match) 244 { 245 auto target = token.get(); 246 247 auto length = target.length; 248 if (length > match.length) 249 length = match.length; 250 251 if (length is 0) 252 return false; 253 254 return strncasecmp (target.ptr, match.ptr, length) is 0; 255 } 256 257 /********************************************************************** 258 259 Resize this stack by extending the array. 260 261 **********************************************************************/ 262 263 final static void resize (ref Token[] tokens, int size) 264 { 265 auto i = tokens.length; 266 267 // this should *never* realistically happen 268 if (size > MaxHttpStackSize) 269 throw new IOException ("Token stack exceeds maximum depth"); 270 271 for (tokens.length=size; i < tokens.length; ++i) 272 tokens[i] = new Token(); 273 } 274 }