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 }