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 }