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

Generated on Fri May 27 18:11:56 2005 for Mango by  doxygen 1.4.0