logo

class

web::WebUtil

sys::Obj
  web::WebUtil
   1  //
   2  // Copyright (c) 2007, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   27 Jun 07  Brian Frank  Creation
   7  //
   8  
   9  **
  10  ** WebUtil encapsulates several useful utility web methods.
  11  **
  12  class WebUtil
  13  {
  14  
  15  //////////////////////////////////////////////////////////////////////////
  16  // Parsing
  17  //////////////////////////////////////////////////////////////////////////
  18  
  19    **
  20    ** Parse a list of comma separated tokens.  Any leading
  21    ** or trailing whitespace is trimmed from the list of tokens.
  22    **
  23    static Str[] parseList(Str s)
  24    {
  25      // TODO regex when available
  26      toks := s.split(",")
  27      toks.each |Str tok, Int i| { toks[i] = tok.trim }
  28      return toks
  29    }
  30  
  31    **
  32    ** Parse a series of HTTP headers according to RFC 2616 section
  33    ** 4.2.  The final CRLF which terminates headers is consumed with
  34    ** the stream positioned immediately following.  The headers are
  35    ** returned as a [case insensitive]`sys::Map.caseInsensitive` map.
  36    ** Throw IOErr if headers are malformed.
  37    **
  38    static Str:Str parseHeaders(InStream in)
  39    {
  40      headers := Str:Str[:]
  41      headers.caseInsensitive = true
  42      Str last := null
  43  
  44      // read headers into map
  45      while (true)
  46      {
  47        peek := in.peek
  48  
  49        // CRLF is end of headers
  50        if (peek === CR) break
  51  
  52        // if line starts with space it is
  53        // continuation of last header field
  54        if (peek.isSpace && last != null)
  55        {
  56          headers[last] += " " + in.readLine.trim
  57          continue
  58        }
  59  
  60        // key/value pair
  61        key := token(in, ':').trim
  62        val := token(in, CR).trim
  63        if (in.read != LF)
  64          throw IOErr.make("Invalid CRLF line ending")
  65  
  66        // check if key already defined in which case
  67        // this is an append, otherwise its a new pair
  68        dup := headers[key]
  69        if (dup == null)
  70          headers[key] = val
  71        else
  72          headers[key] = dup + "," + val
  73        last = key
  74      }
  75  
  76      // consume final CRLF
  77      if (in.read !== CR || in.read !== LF)
  78        throw IOErr.make("Invalid CRLF headers ending")
  79  
  80      return headers
  81    }
  82  
  83    **
  84    ** Read the next token from the stream up to the specified
  85    ** separator. We place a limit of 512 bytes on a single token.
  86    ** Consume the separate char too.
  87    **
  88    private static Str token(InStream in, Int sep)
  89    {
  90      // read up to separator
  91      tok := in.readStrToken(maxTokenSize) |Int ch->Bool| { return ch == sep }
  92  
  93      // sanity checking
  94      if (tok == null) throw IOErr.make("Unexpected end of stream")
  95      if (tok.size >= 512) throw IOErr.make("Token too big")
  96  
  97      // read separator
  98      in.read
  99  
 100      return tok
 101    }
 102  
 103  //////////////////////////////////////////////////////////////////////////
 104  // Fields
 105  //////////////////////////////////////////////////////////////////////////
 106  
 107    internal const static Int CR  := '\r'
 108    internal const static Int LF  := '\n'
 109    internal const static Int HT  := '\t'
 110    internal const static Int SP  := ' '
 111    internal const static Int maxTokenSize := 512
 112  
 113  }