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