Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [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.catalina.connector;
   19   
   20   import java.io.IOException;
   21   import java.io.Reader;
   22   import java.security.AccessController;
   23   import java.security.PrivilegedActionException;
   24   import java.security.PrivilegedExceptionAction;
   25   import java.util.HashMap;
   26   
   27   import org.apache.catalina.security.SecurityUtil;
   28   import org.apache.coyote.ActionCode;
   29   import org.apache.coyote.Request;
   30   import org.apache.tomcat.util.buf.B2CConverter;
   31   import org.apache.tomcat.util.buf.ByteChunk;
   32   import org.apache.tomcat.util.buf.CharChunk;
   33   
   34   
   35   /**
   36    * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
   37    * OutputBuffer, adapted to handle input instead of output. This allows 
   38    * complete recycling of the facade objects (the ServletInputStream and the
   39    * BufferedReader).
   40    *
   41    * @author Remy Maucherat
   42    */
   43   public class InputBuffer extends Reader
   44       implements ByteChunk.ByteInputChannel, CharChunk.CharInputChannel,
   45                  CharChunk.CharOutputChannel {
   46   
   47   
   48       // -------------------------------------------------------------- Constants
   49   
   50   
   51       public static final String DEFAULT_ENCODING = 
   52           org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
   53       public static final int DEFAULT_BUFFER_SIZE = 8*1024;
   54   
   55       // The buffer can be used for byte[] and char[] reading
   56       // ( this is needed to support ServletInputStream and BufferedReader )
   57       public final int INITIAL_STATE = 0;
   58       public final int CHAR_STATE = 1;
   59       public final int BYTE_STATE = 2;
   60   
   61   
   62       // ----------------------------------------------------- Instance Variables
   63   
   64   
   65       /**
   66        * The byte buffer.
   67        */
   68       private ByteChunk bb;
   69   
   70   
   71       /**
   72        * The chunk buffer.
   73        */
   74       private CharChunk cb;
   75   
   76   
   77       /**
   78        * State of the output buffer.
   79        */
   80       private int state = 0;
   81   
   82   
   83       /**
   84        * Number of bytes read.
   85        */
   86       private int bytesRead = 0;
   87   
   88   
   89       /**
   90        * Number of chars read.
   91        */
   92       private int charsRead = 0;
   93   
   94   
   95       /**
   96        * Flag which indicates if the input buffer is closed.
   97        */
   98       private boolean closed = false;
   99   
  100   
  101       /**
  102        * Byte chunk used to input bytes.
  103        */
  104       private ByteChunk inputChunk = new ByteChunk();
  105   
  106   
  107       /**
  108        * Encoding to use.
  109        */
  110       private String enc;
  111   
  112   
  113       /**
  114        * Encoder is set.
  115        */
  116       private boolean gotEnc = false;
  117   
  118   
  119       /**
  120        * List of encoders.
  121        */
  122       protected HashMap encoders = new HashMap();
  123   
  124   
  125       /**
  126        * Current byte to char converter.
  127        */
  128       protected B2CConverter conv;
  129   
  130   
  131       /**
  132        * Associated Coyote request.
  133        */
  134       private Request coyoteRequest;
  135   
  136   
  137       /**
  138        * Buffer position.
  139        */
  140       private int markPos = -1;
  141   
  142   
  143       /**
  144        * Buffer size.
  145        */
  146       private int size = -1;
  147   
  148   
  149       // ----------------------------------------------------------- Constructors
  150   
  151   
  152       /**
  153        * Default constructor. Allocate the buffer with the default buffer size.
  154        */
  155       public InputBuffer() {
  156   
  157           this(DEFAULT_BUFFER_SIZE);
  158   
  159       }
  160   
  161   
  162       /**
  163        * Alternate constructor which allows specifying the initial buffer size.
  164        * 
  165        * @param size Buffer size to use
  166        */
  167       public InputBuffer(int size) {
  168   
  169           this.size = size;
  170           bb = new ByteChunk(size);
  171           bb.setLimit(size);
  172           bb.setByteInputChannel(this);
  173           cb = new CharChunk(size);
  174           cb.setLimit(size);
  175           cb.setOptimizedWrite(false);
  176           cb.setCharInputChannel(this);
  177           cb.setCharOutputChannel(this);
  178   
  179       }
  180   
  181   
  182       // ------------------------------------------------------------- Properties
  183   
  184   
  185       /**
  186        * Associated Coyote request.
  187        * 
  188        * @param coyoteRequest Associated Coyote request
  189        */
  190       public void setRequest(Request coyoteRequest) {
  191   	this.coyoteRequest = coyoteRequest;
  192       }
  193   
  194   
  195       /**
  196        * Get associated Coyote request.
  197        * 
  198        * @return the associated Coyote request
  199        */
  200       public Request getRequest() {
  201           return this.coyoteRequest;
  202       }
  203   
  204   
  205       // --------------------------------------------------------- Public Methods
  206   
  207   
  208       /**
  209        * Recycle the output buffer.
  210        */
  211       public void recycle() {
  212           
  213           state = INITIAL_STATE;
  214           bytesRead = 0;
  215           charsRead = 0;
  216           
  217           // If usage of mark made the buffer too big, reallocate it
  218           if (cb.getChars().length > size) {
  219               cb = new CharChunk(size);
  220               cb.setLimit(size);
  221               cb.setOptimizedWrite(false);
  222               cb.setCharInputChannel(this);
  223               cb.setCharOutputChannel(this);
  224           } else {
  225               cb.recycle();
  226           }
  227           markPos = -1;
  228           bb.recycle(); 
  229           closed = false;
  230           
  231           if (conv != null) {
  232               conv.recycle();
  233           }
  234           
  235           gotEnc = false;
  236           enc = null;
  237           
  238       }
  239   
  240   
  241       /**
  242        * Clear cached encoders (to save memory for Comet requests).
  243        */
  244       public void clearEncoders() {
  245           encoders.clear();
  246       }
  247       
  248       
  249       /**
  250        * Close the input buffer.
  251        * 
  252        * @throws IOException An underlying IOException occurred
  253        */
  254       public void close()
  255           throws IOException {
  256           closed = true;
  257       }
  258   
  259   
  260       public int available() {
  261           int available = 0;
  262           if (state == BYTE_STATE) {
  263               available = bb.getLength();
  264           } else if (state == CHAR_STATE) {
  265               available = cb.getLength();
  266           }
  267           if (available == 0) {
  268               coyoteRequest.action(ActionCode.ACTION_AVAILABLE, null);
  269               available = (coyoteRequest.getAvailable() > 0) ? 1 : 0;
  270           }
  271           return available;
  272       }
  273   
  274   
  275       // ------------------------------------------------- Bytes Handling Methods
  276   
  277   
  278       /** 
  279        * Reads new bytes in the byte chunk.
  280        * 
  281        * @param cbuf Byte buffer to be written to the response
  282        * @param off Offset
  283        * @param len Length
  284        * 
  285        * @throws IOException An underlying IOException occurred
  286        */
  287       public int realReadBytes(byte cbuf[], int off, int len)
  288   	throws IOException {
  289   
  290           if (closed)
  291               return -1;
  292           if (coyoteRequest == null)
  293               return -1;
  294   
  295           if(state == INITIAL_STATE)
  296               state = BYTE_STATE;
  297   
  298           int result = coyoteRequest.doRead(bb);
  299   
  300           return result;
  301   
  302       }
  303   
  304   
  305       public int readByte()
  306           throws IOException {
  307           return bb.substract();
  308       }
  309   
  310   
  311       public int read(byte[] b, int off, int len)
  312           throws IOException {
  313           return bb.substract(b, off, len);
  314       }
  315   
  316   
  317       // ------------------------------------------------- Chars Handling Methods
  318   
  319   
  320       /**
  321        * Since the converter will use append, it is possible to get chars to
  322        * be removed from the buffer for "writing". Since the chars have already
  323        * been read before, they are ignored. If a mark was set, then the
  324        * mark is lost.
  325        */
  326       public void realWriteChars(char c[], int off, int len) 
  327           throws IOException {
  328           markPos = -1;
  329           cb.setOffset(0);
  330           cb.setEnd(0);
  331       }
  332   
  333   
  334       public void setEncoding(String s) {
  335           enc = s;
  336       }
  337   
  338   
  339       public int realReadChars(char cbuf[], int off, int len)
  340           throws IOException {
  341   
  342           if (!gotEnc)
  343               setConverter();
  344   
  345           if (bb.getLength() <= 0) {
  346               int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length);
  347               if (nRead < 0) {
  348                   return -1;
  349               }
  350           }
  351   
  352           if (markPos == -1) {
  353               cb.setOffset(0);
  354               cb.setEnd(0);
  355           }
  356   
  357           state = CHAR_STATE;
  358           conv.convert(bb, cb, len);
  359           bb.setOffset(bb.getEnd());
  360   
  361           return cb.getLength();
  362   
  363       }
  364   
  365   
  366       public int read()
  367           throws IOException {
  368           return cb.substract();
  369       }
  370   
  371   
  372       public int read(char[] cbuf)
  373           throws IOException {
  374           return read(cbuf, 0, cbuf.length);
  375       }
  376   
  377   
  378       public int read(char[] cbuf, int off, int len)
  379           throws IOException {
  380           return cb.substract(cbuf, off, len);
  381       }
  382   
  383   
  384       public long skip(long n)
  385           throws IOException {
  386   
  387           if (n < 0) {
  388               throw new IllegalArgumentException();
  389           }
  390   
  391           long nRead = 0;
  392           while (nRead < n) {
  393               if (cb.getLength() >= n) {
  394                   cb.setOffset(cb.getStart() + (int) n);
  395                   nRead = n;
  396               } else {
  397                   nRead += cb.getLength();
  398                   cb.setOffset(cb.getEnd());
  399                   int toRead = 0;
  400                   if (cb.getChars().length < (n - nRead)) {
  401                       toRead = cb.getChars().length;
  402                   } else {
  403                       toRead = (int) (n - nRead);
  404                   }
  405                   int nb = realReadChars(cb.getChars(), 0, toRead);
  406                   if (nb < 0)
  407                       break;
  408               }
  409           }
  410   
  411           return nRead;
  412   
  413       }
  414   
  415   
  416       public boolean ready()
  417           throws IOException {
  418           return (available() > 0);
  419       }
  420   
  421   
  422       public boolean markSupported() {
  423           return true;
  424       }
  425   
  426   
  427       public void mark(int readAheadLimit)
  428           throws IOException {
  429           if (cb.getLength() <= 0) {
  430               cb.setOffset(0);
  431               cb.setEnd(0);
  432           } else {
  433               if ((cb.getBuffer().length > (2 * size)) 
  434                   && (cb.getLength()) < (cb.getStart())) {
  435                   System.arraycopy(cb.getBuffer(), cb.getStart(), 
  436                                    cb.getBuffer(), 0, cb.getLength());
  437                   cb.setEnd(cb.getLength());
  438                   cb.setOffset(0);
  439               }
  440           }
  441           int offset = readAheadLimit;
  442           if (offset < size) {
  443               offset = size;
  444           }
  445           cb.setLimit(cb.getStart() + offset);
  446           markPos = cb.getStart();
  447       }
  448   
  449   
  450       public void reset()
  451           throws IOException {
  452           if (state == CHAR_STATE) {
  453               if (markPos < 0) {
  454                   cb.recycle();
  455                   markPos = -1;
  456                   throw new IOException();
  457               } else {
  458                   cb.setOffset(markPos);
  459               }
  460           } else {
  461               bb.recycle();
  462           }
  463       }
  464   
  465   
  466       public void checkConverter() 
  467           throws IOException {
  468   
  469           if (!gotEnc)
  470               setConverter();
  471   
  472       }
  473   
  474   
  475       protected void setConverter()
  476           throws IOException {
  477   
  478           if (coyoteRequest != null)
  479               enc = coyoteRequest.getCharacterEncoding();
  480   
  481           gotEnc = true;
  482           if (enc == null)
  483               enc = DEFAULT_ENCODING;
  484           conv = (B2CConverter) encoders.get(enc);
  485           if (conv == null) {
  486               if (SecurityUtil.isPackageProtectionEnabled()){
  487                   try{
  488                       conv = (B2CConverter)AccessController.doPrivileged(
  489                               new PrivilegedExceptionAction(){
  490   
  491                                   public Object run() throws IOException{
  492                                       return new B2CConverter(enc);
  493                                   }
  494   
  495                               }
  496                       );              
  497                   }catch(PrivilegedActionException ex){
  498                       Exception e = ex.getException();
  499                       if (e instanceof IOException)
  500                           throw (IOException)e; 
  501                   }
  502               } else {
  503                   conv = new B2CConverter(enc);
  504               }
  505               encoders.put(enc, conv);
  506           }
  507   
  508       }
  509   
  510   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [javadoc | source]