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