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 
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 }

Generated on Sun Nov 7 19:06:51 2004 for Mango by doxygen 1.3.6