00001 /******************************************************************************* 00002 00003 @file HttpTokens.d 00004 00005 Copyright (c) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 4. Derivative works are permitted, but they must carry this notice 00027 in full and credit the original source. 00028 00029 00030 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00031 00032 00033 @version Initial version, April 2004 00034 @author Kris 00035 00036 00037 *******************************************************************************/ 00038 00039 module mango.http.server.HttpTokens; 00040 00041 private import mango.utils.Text; 00042 00043 private import mango.io.Token, 00044 mango.io.Buffer, 00045 mango.io.Tokenizer; 00046 00047 private import mango.format.Int; 00048 00049 private import mango.time.Rfc1123; 00050 00051 private import mango.io.model.IBuffer, 00052 mango.io.model.IWriter; 00053 00054 private import mango.http.utils.TokenStack; 00055 00056 /****************************************************************************** 00057 00058 Struct used to expose freachable HttpToken instances. 00059 00060 ******************************************************************************/ 00061 00062 struct HttpToken 00063 { 00064 char[] name, 00065 value; 00066 } 00067 00068 /****************************************************************************** 00069 00070 Maintains a set of HTTP tokens. These tokens include headers, query- 00071 parameters, and anything else vaguely similar. Both input and output 00072 are supported, though a subclass may choose to expose as read-only. 00073 00074 All tokens are mapped directly onto a buffer, so there is no memory 00075 allocation or copying involved. 00076 00077 Note that this class does not support deleting tokens. Supporting 00078 such operations require a different approach, such as mapping the 00079 tokens into a temporary buffer, and then setting token content in 00080 the stack to be null when it is deleted. This could be implemented 00081 as a wrapper upon the subclasses of HttpToken. 00082 00083 ******************************************************************************/ 00084 00085 class HttpTokens : IWritable 00086 { 00087 protected TokenStack stack; 00088 00089 private IBuffer input, 00090 output; 00091 private bool parsed; 00092 private bool inclusive; 00093 private char separator; 00094 private char[1] sepString; 00095 static private char[] emptyString; 00096 00097 /********************************************************************** 00098 00099 Setup an empty character array for later assignment. 00100 00101 **********************************************************************/ 00102 00103 static this () 00104 { 00105 emptyString = new char[0]; 00106 } 00107 00108 /********************************************************************** 00109 00110 Construct a set of tokens based upon the given delimeter, 00111 and an indication of whether said delimeter should be 00112 considered part of the left side (effectively the name). 00113 00114 The latter is useful with headers, since the seperating 00115 ':' character should really be considered part of the 00116 name for purposes of subsequent token matching. 00117 00118 **********************************************************************/ 00119 00120 this (char separator, bool inclusive = false) 00121 { 00122 stack = new TokenStack(); 00123 00124 this.inclusive = inclusive; 00125 this.separator = separator; 00126 00127 // convert separator into a string, for later use 00128 sepString[0] = separator; 00129 00130 // pre-construct an empty buffer for wrapping char[] parsing 00131 input = new Buffer; 00132 } 00133 00134 /********************************************************************** 00135 00136 Clone a source set of HttpTokens 00137 00138 **********************************************************************/ 00139 00140 this (HttpTokens source) 00141 { 00142 stack = source.stack.clone(); 00143 input = null; 00144 output = source.output; 00145 parsed = true; 00146 inclusive = source.inclusive; 00147 separator = source.separator; 00148 sepString[0] = source.sepString[0]; 00149 } 00150 00151 /********************************************************************** 00152 00153 Read all tokens. Everything is mapped rather than being 00154 allocated & copied 00155 00156 **********************************************************************/ 00157 00158 abstract void parse (IBuffer input); 00159 00160 /********************************************************************** 00161 00162 Parse an input string. 00163 00164 **********************************************************************/ 00165 00166 void parse (char[] content) 00167 { 00168 input.setValidContent (content); 00169 parse (input); 00170 } 00171 00172 /********************************************************************** 00173 00174 Reset this set of tokens. 00175 00176 **********************************************************************/ 00177 00178 void reset () 00179 { 00180 stack.reset(); 00181 parsed = false; 00182 00183 // reset output buffer, if it was configured 00184 if (output) 00185 output.clear(); 00186 } 00187 00188 /********************************************************************** 00189 00190 Have tokens been parsed yet? 00191 00192 **********************************************************************/ 00193 00194 bool isParsed () 00195 { 00196 return parsed; 00197 } 00198 00199 /********************************************************************** 00200 00201 Indicate whether tokens have been parsed or not. 00202 00203 **********************************************************************/ 00204 00205 void setParsed (bool parsed) 00206 { 00207 this.parsed = parsed; 00208 } 00209 00210 /********************************************************************** 00211 00212 Return the value of the provided header, or null if the 00213 header does not exist 00214 00215 **********************************************************************/ 00216 00217 char[] get (char[] name) 00218 { 00219 Token token = stack.findToken (name); 00220 if (token) 00221 { 00222 HttpToken element; 00223 00224 if (split (token, element)) 00225 return element.value; 00226 } 00227 return null; 00228 } 00229 00230 /********************************************************************** 00231 00232 Return the integer value of the provided header, or the 00233 provided default-vaule if the header does not exist 00234 00235 **********************************************************************/ 00236 00237 int getInt (char[] name, int ret = -1) 00238 { 00239 char[] value = get (name); 00240 00241 if (value.length) 00242 ret = Int.parse (value); 00243 00244 return ret; 00245 } 00246 00247 /********************************************************************** 00248 00249 Return the date value of the provided header, or the 00250 provided default-value if the header does not exist 00251 00252 **********************************************************************/ 00253 00254 long getDate (char[] name, long date = Rfc1123.InvalidEpoch) 00255 { 00256 char[] value = get (name); 00257 00258 if (value.length) 00259 date = Rfc1123.parse (value); 00260 00261 return date; 00262 } 00263 00264 /********************************************************************** 00265 00266 Iterate over the set of tokens 00267 00268 **********************************************************************/ 00269 00270 int opApply (int delegate(inout HttpToken) dg) 00271 { 00272 HttpToken element; 00273 int result = 0; 00274 00275 foreach (Token t; stack) 00276 if (split (t, element)) 00277 { 00278 result = dg (element); 00279 if (result) 00280 break; 00281 } 00282 return result; 00283 } 00284 00285 /********************************************************************** 00286 00287 Output the token list to the provided writer 00288 00289 **********************************************************************/ 00290 00291 void write (IWriter writer) 00292 { 00293 foreach (Token token; stack) 00294 { 00295 char[] content = token.toString; 00296 if (content.length) 00297 writer.put(content).cr(); 00298 } 00299 } 00300 00301 /********************************************************************** 00302 00303 split basic token into an HttpToken 00304 00305 **********************************************************************/ 00306 00307 final private bool split (Token t, inout HttpToken element) 00308 { 00309 char[] s = t.toString(); 00310 00311 if (s.length) 00312 { 00313 int i = Text.indexOf (s, separator); 00314 00315 // we should always find the separator 00316 if (i > 0) 00317 { 00318 int j = (inclusive) ? i+1 : i; 00319 element.name = s[0..j]; 00320 element.value = (i < s.length) ? s[i+1..s.length] : emptyString; 00321 return true; 00322 } 00323 else 00324 { 00325 // throw new IOException("Invalid token '"~s~"'"); 00326 } 00327 } 00328 return false; 00329 } 00330 00331 /********************************************************************** 00332 00333 Create a filter for iterating over the tokens matching 00334 a particular name. 00335 00336 **********************************************************************/ 00337 00338 FilteredTokens createFilter (char[] match) 00339 { 00340 return new FilteredTokens (this, match); 00341 } 00342 00343 /********************************************************************** 00344 00345 Implements a filter for iterating over tokens matching 00346 a particular name. We do it like this because there's no 00347 means of passing additional information to an opApply() 00348 method. 00349 00350 **********************************************************************/ 00351 00352 private class FilteredTokens 00353 { 00354 private char[] match; 00355 private HttpTokens tokens; 00356 00357 /************************************************************** 00358 00359 Construct this filter upon the given tokens, and 00360 set the pattern to match against. 00361 00362 **************************************************************/ 00363 00364 this (HttpTokens tokens, char[] match) 00365 { 00366 this.match = match; 00367 this.tokens = tokens; 00368 } 00369 00370 /************************************************************** 00371 00372 Iterate over all tokens matching the given name 00373 00374 **************************************************************/ 00375 00376 int opApply (int delegate(inout HttpToken) dg) 00377 { 00378 HttpToken element; 00379 int result = 0; 00380 00381 foreach (Token token; tokens.stack) 00382 if (tokens.stack.isMatch (token, match)) 00383 if (tokens.split (token, element)) 00384 { 00385 result = dg (element); 00386 if (result) 00387 break; 00388 } 00389 return result; 00390 } 00391 00392 } 00393 00394 00395 /********************************************************************** 00396 ****************** these should be exposed carefully ****************** 00397 **********************************************************************/ 00398 00399 00400 /********************************************************************** 00401 00402 Set the output buffer for adding tokens to. This is used 00403 by the various MutableXXXX classes. 00404 00405 **********************************************************************/ 00406 00407 protected void setOutputBuffer (IBuffer output) 00408 { 00409 this.output = output; 00410 } 00411 00412 /********************************************************************** 00413 00414 Return the buffer used for output. 00415 00416 **********************************************************************/ 00417 00418 protected IBuffer getOutputBuffer () 00419 { 00420 return output; 00421 } 00422 00423 /********************************************************************** 00424 00425 Return a char[] representing the output. An empty array 00426 is returned if output was not configured. 00427 00428 **********************************************************************/ 00429 00430 char[] toOutputString () 00431 { 00432 static const char[0] Null; 00433 00434 if (output) 00435 return output.toString; 00436 return Null; 00437 } 00438 00439 /********************************************************************** 00440 00441 Add a token with the given name. The content is provided 00442 via the specified delegate. We stuff this name & content 00443 into the output buffer, and map a new Token onto the 00444 appropriate buffer slice. 00445 00446 **********************************************************************/ 00447 00448 protected void add (char[] name, void delegate (IBuffer) dg) 00449 { 00450 // save the buffer write-position 00451 int prior = output.getLimit; 00452 00453 // add the name 00454 output.append (name); 00455 00456 // don't append separator if it's already part of the name 00457 if (! inclusive) 00458 output.append (sepString); 00459 00460 // add the value 00461 dg (output); 00462 00463 // map new token onto buffer slice 00464 int limit = output.getLimit; 00465 stack.push (output.toString[prior..limit]); 00466 } 00467 00468 /********************************************************************** 00469 00470 Add a simple name/value pair to the output 00471 00472 **********************************************************************/ 00473 00474 protected void add (char[] name, char[] value) 00475 { 00476 void addValue (IBuffer buffer) 00477 { 00478 buffer.append (value); 00479 } 00480 00481 add (name, &addValue); 00482 } 00483 00484 /********************************************************************** 00485 00486 Add a name/integer pair to the output 00487 00488 **********************************************************************/ 00489 00490 protected void addInt (char[] name, int value) 00491 { 00492 char[16] tmp; 00493 00494 add (name, Int.format (tmp, value)); 00495 } 00496 00497 00498 /********************************************************************** 00499 00500 Add a name/date(long) pair to the output 00501 00502 **********************************************************************/ 00503 00504 protected void addDate (char[] name, long value) 00505 { 00506 char[40] tmp; 00507 00508 add (name, Rfc1123.format (tmp, value)); 00509 } 00510 }