Main Page | Class Hierarchy | Alphabetical List | Class List | 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.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 }

Generated on Tue Jan 25 21:18:22 2005 for Mango by doxygen 1.3.6