Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

HttpTokens.d

Go to the documentation of this file.
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.text.Text;
00042 
00043 private import  mango.io.Token,
00044                 mango.io.Buffer,
00045                 mango.io.Tokenizer;
00046 
00047 private import  mango.convert.Integer,
00048                 mango.convert.Rfc1123;
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, char[] ret = null)
00217         {
00218                 Token token = stack.findToken (name);
00219                 if (token)
00220                    {
00221                    HttpToken element;
00222 
00223                    if (split (token, element))
00224                        ret = trim (element.value);
00225                    }
00226                 return ret;
00227         }
00228 
00229         /**********************************************************************
00230                 
00231                 Return the integer value of the provided header, or the 
00232                 provided default-vaule if the header does not exist
00233 
00234         **********************************************************************/
00235 
00236         int getInt (char[] name, int ret = -1)
00237         {       
00238                 char[] value = get (name);
00239 
00240                 if (value.length)
00241                     ret = cast(int) Integer.parse (value);
00242 
00243                 return ret;
00244         }
00245 
00246         /**********************************************************************
00247                 
00248                 Return the date value of the provided header, or the 
00249                 provided default-value if the header does not exist
00250 
00251         **********************************************************************/
00252 
00253         ulong getDate (char[] name, ulong date = Rfc1123.InvalidEpoch)
00254         {
00255                 char[] value = get (name);
00256 
00257                 if (value.length)
00258                     date = Rfc1123.parse (value);
00259 
00260                 return date;
00261         }
00262 
00263         /**********************************************************************
00264 
00265                 Iterate over the set of tokens
00266 
00267         **********************************************************************/
00268 
00269         int opApply (int delegate(inout HttpToken) dg)
00270         {
00271                 HttpToken element;
00272                 int       result = 0;
00273 
00274                 foreach (Token t; stack)
00275                          if (split (t, element))
00276                             {
00277                             result = dg (element);
00278                             if (result)
00279                                 break;
00280                             }
00281                 return result;
00282         }
00283 
00284         /**********************************************************************
00285 
00286                 Output the token list to the provided writer
00287 
00288         **********************************************************************/
00289 
00290         void write (IWriter writer)
00291         {
00292                 foreach (Token token; stack)
00293                         {
00294                         char[] content = token.toString;
00295                         if (content.length)
00296                             writer.put(content).cr();
00297                         }                           
00298         }
00299 
00300         /**********************************************************************
00301 
00302                 overridable method to handle the case where a token does
00303                 not have a separator. Apparently, this can happen in HTTP 
00304                 usage
00305 
00306         **********************************************************************/
00307 
00308         protected bool handleMissingSeparator (char[] s, inout HttpToken element)
00309         {
00310                 return false;
00311         }
00312 
00313         /**********************************************************************
00314 
00315                 split basic token into an HttpToken
00316 
00317         **********************************************************************/
00318 
00319         final private bool split (Token t, inout HttpToken element)
00320         {
00321                 char[] s = t.toString();
00322 
00323                 if (s.length)
00324                    {
00325                    int i = Text.indexOf (s, separator);
00326 
00327                    // we should always find the separator
00328                    if (i > 0)
00329                       {
00330                       int j = (inclusive) ? i+1 : i;
00331                       element.name = s[0..j];
00332                       element.value = (i < s.length) ? s[i+1..s.length] : emptyString;
00333                       return true;
00334                       }
00335                    else
00336                       // allow override to specialize this case
00337                       return handleMissingSeparator (s, element);
00338                    }
00339                 return false;                           
00340         }
00341 
00342         /**********************************************************************
00343 
00344                 Create a filter for iterating over the tokens matching
00345                 a particular name. 
00346         
00347         **********************************************************************/
00348 
00349         FilteredTokens createFilter (char[] match)
00350         {
00351                 return new FilteredTokens (this, match);
00352         }
00353 
00354         /**********************************************************************
00355 
00356                 Implements a filter for iterating over tokens matching
00357                 a particular name. We do it like this because there's no 
00358                 means of passing additional information to an opApply() 
00359                 method.
00360         
00361         **********************************************************************/
00362 
00363         private static class FilteredTokens 
00364         {       
00365                 private char[]          match;
00366                 private HttpTokens      tokens;
00367 
00368                 /**************************************************************
00369 
00370                         Construct this filter upon the given tokens, and
00371                         set the pattern to match against.
00372 
00373                 **************************************************************/
00374 
00375                 this (HttpTokens tokens, char[] match)
00376                 {
00377                         this.match = match;
00378                         this.tokens = tokens;
00379                 }
00380 
00381                 /**************************************************************
00382 
00383                         Iterate over all tokens matching the given name
00384 
00385                 **************************************************************/
00386 
00387                 int opApply (int delegate(inout HttpToken) dg)
00388                 {
00389                         HttpToken       element;
00390                         int             result = 0;
00391                         
00392                         foreach (Token token; tokens.stack)
00393                                  if (tokens.stack.isMatch (token, match))
00394                                      if (tokens.split (token, element))
00395                                         {
00396                                         result = dg (element);
00397                                         if (result)
00398                                             break;
00399                                         }
00400                         return result;
00401                 }
00402 
00403         }
00404 
00405         /**********************************************************************
00406 
00407                 Is the argument a whitespace character?
00408 
00409         **********************************************************************/
00410 
00411         private bool isSpace (char c)
00412         {
00413                 return cast(bool) (c is ' ' || c is '\t' || c is '\r' || c is '\n');
00414         }
00415 
00416         /**********************************************************************
00417 
00418                 Trim the provided string by stripping whitespace from 
00419                 both ends. Returns a slice of the original content.
00420 
00421         **********************************************************************/
00422 
00423         private char[] trim (char[] source)
00424         {
00425                 int  front,
00426                      back = source.length;
00427 
00428                 if (back)
00429                    {
00430                    while (front < back && isSpace(source[front]))
00431                           ++front;
00432 
00433                    while (back > front && isSpace(source[back-1]))
00434                           --back;
00435                    } 
00436                 return source [front .. back];
00437         }
00438 
00439 
00440         /**********************************************************************
00441         ****************** these should be exposed carefully ******************
00442         **********************************************************************/
00443 
00444 
00445         /**********************************************************************
00446                 
00447                 Set the output buffer for adding tokens to. This is used
00448                 by the various MutableXXXX classes.
00449 
00450         **********************************************************************/
00451 
00452         protected void setOutputBuffer (IBuffer output)
00453         {
00454                 this.output = output;
00455         }
00456 
00457         /**********************************************************************
00458                 
00459                 Return the buffer used for output.
00460 
00461         **********************************************************************/
00462 
00463         protected IBuffer getOutputBuffer ()
00464         {
00465                 return output;
00466         }
00467 
00468         /**********************************************************************
00469                 
00470                 Return a char[] representing the output. An empty array
00471                 is returned if output was not configured.
00472 
00473         **********************************************************************/
00474 
00475         char[] toOutputString ()
00476         {
00477                 static const char[0] Null;
00478 
00479                 if (output)
00480                     return output.toString;
00481                 return Null;
00482         }
00483 
00484         /**********************************************************************
00485                 
00486                 Add a token with the given name. The content is provided
00487                 via the specified delegate. We stuff this name & content
00488                 into the output buffer, and map a new Token onto the
00489                 appropriate buffer slice.
00490 
00491         **********************************************************************/
00492 
00493         protected void add (char[] name, void delegate (IBuffer) dg)
00494         {
00495                 // save the buffer write-position
00496                 int prior = output.getLimit;
00497 
00498                 // add the name
00499                 output.append (name);
00500 
00501                 // don't append separator if it's already part of the name
00502                 if (! inclusive)
00503                       output.append (sepString);
00504                 
00505                 // add the value
00506                 dg (output);
00507 
00508                 // map new token onto buffer slice
00509                 int limit = output.getLimit;
00510                 stack.push (output.toString[prior..limit]);
00511         }
00512 
00513         /**********************************************************************
00514                 
00515                 Add a simple name/value pair to the output
00516 
00517         **********************************************************************/
00518 
00519         protected void add (char[] name, char[] value)
00520         {
00521                 void addValue (IBuffer buffer)
00522                 {
00523                         buffer.append (value);
00524                 }
00525 
00526                 add (name, &addValue);
00527         }
00528 
00529         /**********************************************************************
00530                 
00531                 Add a name/integer pair to the output
00532 
00533         **********************************************************************/
00534 
00535         protected void addInt (char[] name, int value)
00536         {
00537                 char[16] tmp;
00538 
00539                 add (name, Integer.format (tmp, value));
00540         }
00541 
00542 
00543         /**********************************************************************
00544                
00545                Add a name/date(long) pair to the output
00546                 
00547         **********************************************************************/
00548 
00549         protected void addDate (char[] name, ulong value)
00550         {
00551                 char[40] tmp;
00552 
00553                 add (name, Rfc1123.format (tmp, value));
00554         }
00555 }

Generated on Sat Dec 24 17:28:33 2005 for Mango by  doxygen 1.4.0