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