132
|
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 (char *, char *, uint);
|
|
27 }
|
|
28
|
|
29 version (Posix)
|
|
30 {
|
|
31 extern (C) int strncasecmp (char *, char*, uint);
|
|
32 }
|
|
33
|
|
34 extern (C) void* memmove (void* dst, void* src, int n);
|
|
35
|
|
36
|
|
37 /******************************************************************************
|
|
38
|
|
39 Internal representation of a token
|
|
40
|
|
41 ******************************************************************************/
|
|
42
|
|
43 class Token
|
|
44 {
|
|
45 private char[] value;
|
|
46
|
|
47 Token set (char[] value)
|
|
48 {
|
|
49 this.value = value;
|
|
50 return this;
|
|
51 }
|
|
52
|
|
53 char[] toString ()
|
|
54 {
|
|
55 return value;
|
|
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().dup);
|
|
104
|
|
105 return clone;
|
|
106 }
|
|
107
|
|
108 /**********************************************************************
|
|
109
|
|
110 Iterate over all tokens in stack
|
|
111
|
|
112 **********************************************************************/
|
|
113
|
|
114 int opApply (int delegate(inout 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 (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 (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 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 (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 (inout 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 (inout Token token, char[] match)
|
|
245 {
|
|
246 char[] target = token.toString();
|
|
247
|
|
248 int 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 (inout Token[] tokens, int size)
|
|
268 {
|
|
269 int 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 }
|