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