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
15 
16 *******************************************************************************/
17 
18 module ocean.net.http.HttpHeaders;
19 
20 import ocean.meta.types.Qualifiers;
21 
22 import ocean.time.Time;
23 
24 import ocean.io.stream.Lines;
25 
26 import ocean.io.model.IConduit;
27 
28 public  import ocean.net.http.HttpConst;
29 
30 import ocean.net.http.HttpTokens;
31 
32 /******************************************************************************
33 
34         Exposes reachable HttpHeader instances
35 
36 ******************************************************************************/
37 
38 struct HeaderElement
39 {
40         HttpHeaderName  name;
41         cstring         value;
42 }
43 
44 /******************************************************************************
45 
46         Maintains a set of input headers. These are placed into an input
47         buffer and indexed via a HttpStack.
48 
49 ******************************************************************************/
50 
51 class HttpHeadersView : HttpTokens
52 {
53         // tell compiler to used super.parse() also
54         alias HttpTokens.parse parse;
55 
56         private Lines        line;
57         private bool         preserve;
58 
59         /**********************************************************************
60 
61                 Construct this set of headers, using a HttpStack based
62                 upon a ':' delimiter
63 
64         **********************************************************************/
65 
66         this ()
67         {
68                 // separator is a ':', and specify we want it included as
69                 // part of the name whilst iterating
70                 super (':', true);
71 
72                 // construct a line tokenizer for later usage
73                 line = new Lines;
74         }
75 
76         /**********************************************************************
77 
78                 Clone a source set of HttpHeaders
79 
80         **********************************************************************/
81 
82         this (HttpHeadersView source)
83         {
84                 super (source);
85                 this.preserve = source.preserve;
86         }
87 
88         /**********************************************************************
89 
90                 Clone this set of HttpHeadersView
91 
92         **********************************************************************/
93 
94         HttpHeadersView clone ()
95         {
96                 return new HttpHeadersView (this);
97         }
98 
99         /***********************************************************************
100 
101                 Control whether headers are duplicated or not. Default
102                 behaviour is aliasing instead of copying, avoiding any
103                 allocation overhead. However, the default won't preserve
104                 those headers once additional content has been read.
105 
106                 To retain headers across arbitrary buffering, you should
107                 set this option to true
108 
109         ***********************************************************************/
110 
111         HttpHeadersView retain (bool yes = true)
112         {
113                 preserve = yes;
114                 return this;
115         }
116 
117         /**********************************************************************
118 
119                 Read all header lines. Everything is mapped rather
120                 than being allocated & copied
121 
122         **********************************************************************/
123 
124         override void parse (InputBuffer input)
125         {
126                 setParsed (true);
127                 line.set (input);
128 
129                 while (line.next && line.get.length)
130                        stack.push (preserve ? line.get.dup : line.get);
131         }
132 
133         /**********************************************************************
134 
135                 Return the value of the provided header, or null if the
136                 header does not exist
137 
138         **********************************************************************/
139 
140         cstring get (HttpHeaderName name, cstring def = null)
141         {
142                 return super.get (name.value, def);
143         }
144 
145         /**********************************************************************
146 
147                 Return the integer value of the provided header, or -1
148                 if the header does not exist
149 
150         **********************************************************************/
151 
152         int getInt (HttpHeaderName name, int def = -1)
153         {
154                 return super.getInt (name.value, def);
155         }
156 
157         /**********************************************************************
158 
159                 Return the date value of the provided header, or Time.epoch
160                 if the header does not exist
161 
162         **********************************************************************/
163 
164         Time getDate (HttpHeaderName name, Time def = Time.epoch)
165         {
166                 return super.getDate (name.value, def);
167         }
168 
169         /**********************************************************************
170 
171                 Iterate over the set of headers. This is a shell around
172                 the superclass, where we can convert the HttpToken into
173                 a HeaderElement instead.
174 
175         **********************************************************************/
176 
177         int opApply (scope int delegate(ref HeaderElement) dg)
178         {
179                 HeaderElement   element;
180                 int             result = 0;
181 
182                 foreach (HttpToken token; super)
183                         {
184                         element.name.value = token.name;
185                         element.value = token.value;
186                         result = dg (element);
187                         if (result)
188                             break;
189                         }
190                 return result;
191         }
192 
193         /**********************************************************************
194 
195                 Create a filter for iterating of a set of named headers.
196                 We have to create a filter since we can't pass additional
197                 arguments directly to an opApply() method.
198 
199         **********************************************************************/
200 
201         FilteredHeaders createFilter (HttpHeaderName header)
202         {
203                 return new FilteredHeaders (this, header);
204         }
205 
206         /**********************************************************************
207 
208                 Filter class for isolating a set of named headers.
209 
210         **********************************************************************/
211 
212         private static class FilteredHeaders : FilteredTokens
213         {
214                 /**************************************************************
215 
216                         Construct a filter upon the specified headers, for
217                         the given header name.
218 
219                 **************************************************************/
220 
221                 this (HttpHeadersView headers, HttpHeaderName header)
222                 {
223                         super (headers, header.value);
224                 }
225 
226                 /**************************************************************
227 
228                         Iterate over all headers matching the given name.
229                         This wraps the HttpToken iterator to convert the
230                         output into a HeaderElement instead.
231 
232                 **************************************************************/
233 
234                 int opApply (scope int delegate(ref HeaderElement) dg)
235                 {
236                         HeaderElement   element;
237                         int             result = 0;
238 
239                         foreach (HttpToken token; super)
240                                 {
241                                 element.name.value = token.name;
242                                 element.value = token.value;
243                                 result = dg (element);
244                                 if (result)
245                                     break;
246                                 }
247                         return result;
248                 }
249 
250         }
251 }
252 
253 
254 /******************************************************************************
255 
256         Maintains a set of output headers. These are held in an output
257         buffer, and indexed via a HttpStack. Deleting a header could be
258         supported by setting the HttpStack entry to null, and ignoring
259         such values when it's time to write the headers.
260 
261 ******************************************************************************/
262 
263 class HttpHeaders : HttpHeadersView
264 {
265         /**********************************************************************
266 
267                 Construct output headers
268 
269         **********************************************************************/
270 
271         this ()
272         {
273         }
274 
275         /**********************************************************************
276 
277                 Clone a source set of HttpHeaders
278 
279         **********************************************************************/
280 
281         this (HttpHeaders source)
282         {
283                 super (source);
284         }
285 
286         /**********************************************************************
287 
288                 Clone this set of HttpHeaders
289 
290         **********************************************************************/
291 
292         override HttpHeaders clone ()
293         {
294                 return new HttpHeaders (this);
295         }
296 
297         /**********************************************************************
298 
299                 Add the specified header, and use a callback to provide
300                 the content.
301 
302         **********************************************************************/
303 
304         void add (HttpHeaderName name, scope void delegate(OutputBuffer) dg)
305         {
306                 super.add (name.value, dg);
307         }
308 
309         /**********************************************************************
310 
311                 Add the specified header and text
312 
313         **********************************************************************/
314 
315         void add (HttpHeaderName name, cstring value)
316         {
317                 super.add (name.value, value);
318         }
319 
320         /**********************************************************************
321 
322                 Add the specified header and integer value
323 
324         **********************************************************************/
325 
326         void addInt (HttpHeaderName name, int value)
327         {
328                 super.addInt (name.value, value);
329         }
330 
331         /**********************************************************************
332 
333                 Add the specified header and long/date value
334 
335         **********************************************************************/
336 
337         void addDate (HttpHeaderName name, Time value)
338         {
339                 super.addDate (name.value, value);
340         }
341 
342         /**********************************************************************
343 
344                 Remove the specified header header. Returns false if not
345                 found.
346 
347         **********************************************************************/
348 
349         bool remove (HttpHeaderName name)
350         {
351                 return super.remove (name.value);
352         }
353 }