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