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 }