Home » apache-tomcat-6.0.26-src » org.apache » tomcat » util » http » [javadoc | source]

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   
   18   package org.apache.tomcat.util.http;
   19   
   20   import java.io.PrintWriter;
   21   import java.io.StringWriter;
   22   import java.util.Enumeration;
   23   
   24   import org.apache.tomcat.util.buf.MessageBytes;
   25   
   26   /* XXX XXX XXX Need a major rewrite  !!!!
   27    */
   28   
   29   /**
   30    * This class is used to contain standard internet message headers,
   31    * used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for
   32    * MIME (RFC 2045) applications such as transferring typed data and
   33    * grouping related items in multipart message bodies.
   34    *
   35    * <P> Message headers, as specified in RFC822, include a field name
   36    * and a field body.  Order has no semantic significance, and several
   37    * fields with the same name may exist.  However, most fields do not
   38    * (and should not) exist more than once in a header.
   39    *
   40    * <P> Many kinds of field body must conform to a specified syntax,
   41    * including the standard parenthesized comment syntax.  This class
   42    * supports only two simple syntaxes, for dates and integers.
   43    *
   44    * <P> When processing headers, care must be taken to handle the case of
   45    * multiple same-name fields correctly.  The values of such fields are
   46    * only available as strings.  They may be accessed by index (treating
   47    * the header as an array of fields), or by name (returning an array
   48    * of string values).
   49    */
   50   
   51   /* Headers are first parsed and stored in the order they are
   52      received. This is based on the fact that most servlets will not
   53      directly access all headers, and most headers are single-valued.
   54      ( the alternative - a hash or similar data structure - will add
   55      an overhead that is not needed in most cases )
   56      
   57      Apache seems to be using a similar method for storing and manipulating
   58      headers.
   59          
   60      Future enhancements:
   61      - hash the headers the first time a header is requested ( i.e. if the
   62      servlet needs direct access to headers).
   63      - scan "common" values ( length, cookies, etc ) during the parse
   64      ( addHeader hook )
   65      
   66   */
   67       
   68   
   69   
   70   /**
   71    *  Memory-efficient repository for Mime Headers. When the object is recycled, it
   72    *  will keep the allocated headers[] and all the MimeHeaderField - no GC is generated.
   73    *
   74    *  For input headers it is possible to use the MessageByte for Fileds - so no GC
   75    *  will be generated.
   76    *
   77    *  The only garbage is generated when using the String for header names/values -
   78    *  this can't be avoided when the servlet calls header methods, but is easy
   79    *  to avoid inside tomcat. The goal is to use _only_ MessageByte-based Fields,
   80    *  and reduce to 0 the memory overhead of tomcat.
   81    *
   82    *  TODO:
   83    *  XXX one-buffer parsing - for http ( other protocols don't need that )
   84    *  XXX remove unused methods
   85    *  XXX External enumerations, with 0 GC.
   86    *  XXX use HeaderName ID
   87    *  
   88    * 
   89    * @author dac@eng.sun.com
   90    * @author James Todd [gonzo@eng.sun.com]
   91    * @author Costin Manolache
   92    * @author kevin seguin
   93    */
   94   public class MimeHeaders {
   95       /** Initial size - should be == average number of headers per request
   96        *  XXX  make it configurable ( fine-tuning of web-apps )
   97        */
   98       public static final int DEFAULT_HEADER_SIZE=8;
   99       
  100       /**
  101        * The header fields.
  102        */
  103       private MimeHeaderField[] headers = new
  104           MimeHeaderField[DEFAULT_HEADER_SIZE];
  105   
  106       /**
  107        * The current number of header fields.
  108        */
  109       private int count;
  110   
  111       /**
  112        * Creates a new MimeHeaders object using a default buffer size.
  113        */
  114       public MimeHeaders() {
  115       }
  116   
  117       /**
  118        * Clears all header fields.
  119        */
  120       // [seguin] added for consistency -- most other objects have recycle().
  121       public void recycle() {
  122           clear();
  123       }
  124   
  125       /**
  126        * Clears all header fields.
  127        */
  128       public void clear() {
  129           for (int i = 0; i < count; i++) {
  130               headers[i].recycle();
  131           }
  132           count = 0;
  133       }
  134   
  135       /**
  136        * EXPENSIVE!!!  only for debugging.
  137        */
  138       public String toString() {
  139           StringWriter sw = new StringWriter();
  140           PrintWriter pw = new PrintWriter(sw);
  141           pw.println("=== MimeHeaders ===");
  142           Enumeration e = names();
  143           while (e.hasMoreElements()) {
  144               String n = (String)e.nextElement();
  145               pw.println(n + " = " + getHeader(n));
  146           }
  147           return sw.toString();
  148       }
  149   
  150       // -------------------- Idx access to headers ----------
  151       
  152       /**
  153        * Returns the current number of header fields.
  154        */
  155       public int size() {
  156           return count;
  157       }
  158   
  159       /**
  160        * Returns the Nth header name, or null if there is no such header.
  161        * This may be used to iterate through all header fields.
  162        */
  163       public MessageBytes getName(int n) {
  164           return n >= 0 && n < count ? headers[n].getName() : null;
  165       }
  166   
  167       /**
  168        * Returns the Nth header value, or null if there is no such header.
  169        * This may be used to iterate through all header fields.
  170        */
  171       public MessageBytes getValue(int n) {
  172           return n >= 0 && n < count ? headers[n].getValue() : null;
  173       }
  174   
  175       /** Find the index of a header with the given name.
  176        */
  177       public int findHeader( String name, int starting ) {
  178           // We can use a hash - but it's not clear how much
  179           // benefit you can get - there is an  overhead 
  180           // and the number of headers is small (4-5 ?)
  181           // Another problem is that we'll pay the overhead
  182           // of constructing the hashtable
  183   
  184           // A custom search tree may be better
  185           for (int i = starting; i < count; i++) {
  186               if (headers[i].getName().equalsIgnoreCase(name)) {
  187                   return i;
  188               }
  189           }
  190           return -1;
  191       }
  192       
  193       // -------------------- --------------------
  194   
  195       /**
  196        * Returns an enumeration of strings representing the header field names.
  197        * Field names may appear multiple times in this enumeration, indicating
  198        * that multiple fields with that name exist in this header.
  199        */
  200       public Enumeration names() {
  201           return new NamesEnumerator(this);
  202       }
  203   
  204       public Enumeration values(String name) {
  205           return new ValuesEnumerator(this, name);
  206       }
  207   
  208       // -------------------- Adding headers --------------------
  209       
  210   
  211       /**
  212        * Adds a partially constructed field to the header.  This
  213        * field has not had its name or value initialized.
  214        */
  215       private MimeHeaderField createHeader() {
  216           MimeHeaderField mh;
  217           int len = headers.length;
  218           if (count >= len) {
  219               // expand header list array
  220               MimeHeaderField tmp[] = new MimeHeaderField[count * 2];
  221               System.arraycopy(headers, 0, tmp, 0, len);
  222               headers = tmp;
  223           }
  224           if ((mh = headers[count]) == null) {
  225               headers[count] = mh = new MimeHeaderField();
  226           }
  227           count++;
  228           return mh;
  229       }
  230   
  231       /** Create a new named header , return the MessageBytes
  232           container for the new value
  233       */
  234       public MessageBytes addValue( String name ) {
  235            MimeHeaderField mh = createHeader();
  236           mh.getName().setString(name);
  237           return mh.getValue();
  238       }
  239   
  240       /** Create a new named header using un-translated byte[].
  241           The conversion to chars can be delayed until
  242           encoding is known.
  243        */
  244       public MessageBytes addValue(byte b[], int startN, int len)
  245       {
  246           MimeHeaderField mhf=createHeader();
  247           mhf.getName().setBytes(b, startN, len);
  248           return mhf.getValue();
  249       }
  250   
  251       /** Create a new named header using translated char[].
  252        */
  253       public MessageBytes addValue(char c[], int startN, int len)
  254       {
  255           MimeHeaderField mhf=createHeader();
  256           mhf.getName().setChars(c, startN, len);
  257           return mhf.getValue();
  258       }
  259   
  260       /** Allow "set" operations - 
  261           return a MessageBytes container for the
  262           header value ( existing header or new
  263           if this .
  264       */
  265       public MessageBytes setValue( String name ) {
  266           for ( int i = 0; i < count; i++ ) {
  267               if(headers[i].getName().equalsIgnoreCase(name)) {
  268                   for ( int j=i+1; j < count; j++ ) {
  269                       if(headers[j].getName().equalsIgnoreCase(name)) {
  270                           removeHeader(j--);
  271                       }
  272                   }
  273                   return headers[i].getValue();
  274               }
  275           }
  276           MimeHeaderField mh = createHeader();
  277           mh.getName().setString(name);
  278           return mh.getValue();
  279       }
  280   
  281       //-------------------- Getting headers --------------------
  282       /**
  283        * Finds and returns a header field with the given name.  If no such
  284        * field exists, null is returned.  If more than one such field is
  285        * in the header, an arbitrary one is returned.
  286        */
  287       public MessageBytes getValue(String name) {
  288           for (int i = 0; i < count; i++) {
  289               if (headers[i].getName().equalsIgnoreCase(name)) {
  290                   return headers[i].getValue();
  291               }
  292           }
  293           return null;
  294       }
  295   
  296       /**
  297        * Finds and returns a unique header field with the given name. If no such
  298        * field exists, null is returned. If the specified header field is not
  299        * unique then an {@link IllegalArgumentException} is thrown. 
  300        */
  301       public MessageBytes getUniqueValue(String name) {
  302           MessageBytes result = null;
  303           for (int i = 0; i < count; i++) {
  304               if (headers[i].getName().equalsIgnoreCase(name)) {
  305                   if (result == null) {
  306                       result = headers[i].getValue();
  307                   } else {
  308                       throw new IllegalArgumentException();
  309                   }
  310               }
  311           }
  312           return result;
  313       }
  314   
  315       // bad shortcut - it'll convert to string ( too early probably,
  316       // encoding is guessed very late )
  317       public String getHeader(String name) {
  318           MessageBytes mh = getValue(name);
  319           return mh != null ? mh.toString() : null;
  320       }
  321   
  322       // -------------------- Removing --------------------
  323       /**
  324        * Removes a header field with the specified name.  Does nothing
  325        * if such a field could not be found.
  326        * @param name the name of the header field to be removed
  327        */
  328       public void removeHeader(String name) {
  329           // XXX
  330           // warning: rather sticky code; heavily tuned
  331   
  332           for (int i = 0; i < count; i++) {
  333               if (headers[i].getName().equalsIgnoreCase(name)) {
  334                   removeHeader(i--);
  335               }
  336           }
  337       }
  338   
  339       /**
  340        * reset and swap with last header
  341        * @param idx the index of the header to remove.
  342        */
  343       private void removeHeader(int idx) {
  344           MimeHeaderField mh = headers[idx];
  345           
  346           mh.recycle();
  347           headers[idx] = headers[count - 1];
  348           headers[count - 1] = mh;
  349           count--;
  350       }
  351   
  352   }
  353   
  354   /** Enumerate the distinct header names.
  355       Each nextElement() is O(n) ( a comparation is
  356       done with all previous elements ).
  357   
  358       This is less frequesnt than add() -
  359       we want to keep add O(1).
  360   */
  361   class NamesEnumerator implements Enumeration {
  362       int pos;
  363       int size;
  364       String next;
  365       MimeHeaders headers;
  366   
  367       public NamesEnumerator(MimeHeaders headers) {
  368           this.headers=headers;
  369           pos=0;
  370           size = headers.size();
  371           findNext();
  372       }
  373   
  374       private void findNext() {
  375           next=null;
  376           for(  ; pos< size; pos++ ) {
  377               next=headers.getName( pos ).toString();
  378               for( int j=0; j<pos ; j++ ) {
  379                   if( headers.getName( j ).equalsIgnoreCase( next )) {
  380                       // duplicate.
  381                       next=null;
  382                       break;
  383                   }
  384               }
  385               if( next!=null ) {
  386                   // it's not a duplicate
  387                   break;
  388               }
  389           }
  390           // next time findNext is called it will try the
  391           // next element
  392           pos++;
  393       }
  394       
  395       public boolean hasMoreElements() {
  396           return next!=null;
  397       }
  398   
  399       public Object nextElement() {
  400           String current=next;
  401           findNext();
  402           return current;
  403       }
  404   }
  405   
  406   /** Enumerate the values for a (possibly ) multiple
  407       value element.
  408   */
  409   class ValuesEnumerator implements Enumeration {
  410       int pos;
  411       int size;
  412       MessageBytes next;
  413       MimeHeaders headers;
  414       String name;
  415   
  416       ValuesEnumerator(MimeHeaders headers, String name) {
  417           this.name=name;
  418           this.headers=headers;
  419           pos=0;
  420           size = headers.size();
  421           findNext();
  422       }
  423   
  424       private void findNext() {
  425           next=null;
  426           for( ; pos< size; pos++ ) {
  427               MessageBytes n1=headers.getName( pos );
  428               if( n1.equalsIgnoreCase( name )) {
  429                   next=headers.getValue( pos );
  430                   break;
  431               }
  432           }
  433           pos++;
  434       }
  435       
  436       public boolean hasMoreElements() {
  437           return next!=null;
  438       }
  439   
  440       public Object nextElement() {
  441           MessageBytes current=next;
  442           findNext();
  443           return current.toString();
  444       }
  445   }
  446   
  447   class MimeHeaderField {
  448       // multiple headers with same name - a linked list will
  449       // speed up name enumerations and search ( both cpu and
  450       // GC)
  451       MimeHeaderField next;
  452       MimeHeaderField prev; 
  453       
  454       protected final MessageBytes nameB = MessageBytes.newInstance();
  455       protected final MessageBytes valueB = MessageBytes.newInstance();
  456   
  457       /**
  458        * Creates a new, uninitialized header field.
  459        */
  460       public MimeHeaderField() {
  461       }
  462   
  463       public void recycle() {
  464           nameB.recycle();
  465           valueB.recycle();
  466           next=null;
  467       }
  468   
  469       public MessageBytes getName() {
  470           return nameB;
  471       }
  472   
  473       public MessageBytes getValue() {
  474           return valueB;
  475       }
  476   }

Home » apache-tomcat-6.0.26-src » org.apache » tomcat » util » http » [javadoc | source]