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