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 overridable method to handle the case where a token does 00304 not have a separator. Apparently, this can happen in HTTP 00305 usage 00306 00307 **********************************************************************/ 00308 00309 protected bool handleMissingSeparator (char[] s, inout HttpToken element) 00310 { 00311 return false; 00312 } 00313 00314 /********************************************************************** 00315 00316 split basic token into an HttpToken 00317 00318 **********************************************************************/ 00319 00320 final private bool split (Token t, inout HttpToken element) 00321 { 00322 char[] s = t.toString(); 00323 00324 if (s.length) 00325 { 00326 int i = Text.indexOf (s, separator); 00327 00328 // we should always find the separator 00329 if (i > 0) 00330 { 00331 int j = (inclusive) ? i+1 : i; 00332 element.name = s[0..j]; 00333 element.value = (i < s.length) ? s[i+1..s.length] : emptyString; 00334 return true; 00335 } 00336 else 00337 // allow override to specialize this case 00338 return handleMissingSeparator (s, element); 00339 } 00340 return false; 00341 } 00342 00343 /********************************************************************** 00344 00345 Create a filter for iterating over the tokens matching 00346 a particular name. 00347 00348 **********************************************************************/ 00349 00350 FilteredTokens createFilter (char[] match) 00351 { 00352 return new FilteredTokens (this, match); 00353 } 00354 00355 /********************************************************************** 00356 00357 Implements a filter for iterating over tokens matching 00358 a particular name. We do it like this because there's no 00359 means of passing additional information to an opApply() 00360 method. 00361 00362 **********************************************************************/ 00363 00364 private static class FilteredTokens 00365 { 00366 private char[] match; 00367 private HttpTokens tokens; 00368 00369 /************************************************************** 00370 00371 Construct this filter upon the given tokens, and 00372 set the pattern to match against. 00373 00374 **************************************************************/ 00375 00376 this (HttpTokens tokens, char[] match) 00377 { 00378 this.match = match; 00379 this.tokens = tokens; 00380 } 00381 00382 /************************************************************** 00383 00384 Iterate over all tokens matching the given name 00385 00386 **************************************************************/ 00387 00388 int opApply (int delegate(inout HttpToken) dg) 00389 { 00390 HttpToken element; 00391 int result = 0; 00392 00393 foreach (Token token; tokens.stack) 00394 if (tokens.stack.isMatch (token, match)) 00395 if (tokens.split (token, element)) 00396 { 00397 result = dg (element); 00398 if (result) 00399 break; 00400 } 00401 return result; 00402 } 00403 00404 } 00405 00406 00407 /********************************************************************** 00408 ****************** these should be exposed carefully ****************** 00409 **********************************************************************/ 00410 00411 00412 /********************************************************************** 00413 00414 Set the output buffer for adding tokens to. This is used 00415 by the various MutableXXXX classes. 00416 00417 **********************************************************************/ 00418 00419 protected void setOutputBuffer (IBuffer output) 00420 { 00421 this.output = output; 00422 } 00423 00424 /********************************************************************** 00425 00426 Return the buffer used for output. 00427 00428 **********************************************************************/ 00429 00430 protected IBuffer getOutputBuffer () 00431 { 00432 return output; 00433 } 00434 00435 /********************************************************************** 00436 00437 Return a char[] representing the output. An empty array 00438 is returned if output was not configured. 00439 00440 **********************************************************************/ 00441 00442 char[] toOutputString () 00443 { 00444 static const char[0] Null; 00445 00446 if (output) 00447 return output.toString; 00448 return Null; 00449 } 00450 00451 /********************************************************************** 00452 00453 Add a token with the given name. The content is provided 00454 via the specified delegate. We stuff this name & content 00455 into the output buffer, and map a new Token onto the 00456 appropriate buffer slice. 00457 00458 **********************************************************************/ 00459 00460 protected void add (char[] name, void delegate (IBuffer) dg) 00461 { 00462 // save the buffer write-position 00463 int prior = output.getLimit; 00464 00465 // add the name 00466 output.append (name); 00467 00468 // don't append separator if it's already part of the name 00469 if (! inclusive) 00470 output.append (sepString); 00471 00472 // add the value 00473 dg (output); 00474 00475 // map new token onto buffer slice 00476 int limit = output.getLimit; 00477 stack.push (output.toString[prior..limit]); 00478 } 00479 00480 /********************************************************************** 00481 00482 Add a simple name/value pair to the output 00483 00484 **********************************************************************/ 00485 00486 protected void add (char[] name, char[] value) 00487 { 00488 void addValue (IBuffer buffer) 00489 { 00490 buffer.append (value); 00491 } 00492 00493 add (name, &addValue); 00494 } 00495 00496 /********************************************************************** 00497 00498 Add a name/integer pair to the output 00499 00500 **********************************************************************/ 00501 00502 protected void addInt (char[] name, int value) 00503 { 00504 char[16] tmp; 00505 00506 add (name, Int.format (tmp, value)); 00507 } 00508 00509 00510 /********************************************************************** 00511 00512 Add a name/date(long) pair to the output 00513 00514 **********************************************************************/ 00515 00516 protected void addDate (char[] name, long value) 00517 { 00518 char[40] tmp; 00519 00520 add (name, Rfc1123.format (tmp, value)); 00521 } 00522 }