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

Generated on Fri Nov 11 18:44:20 2005 for Mango by  doxygen 1.4.0