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 }