Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » coyote » http11 » [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.coyote.http11;
   19   
   20   import java.io.IOException;
   21   import java.io.OutputStream;
   22   import java.security.AccessController;
   23   import java.security.PrivilegedAction;
   24   
   25   import org.apache.tomcat.util.buf.ByteChunk;
   26   import org.apache.tomcat.util.buf.CharChunk;
   27   import org.apache.tomcat.util.buf.MessageBytes;
   28   import org.apache.tomcat.util.http.HttpMessages;
   29   import org.apache.tomcat.util.http.MimeHeaders;
   30   import org.apache.tomcat.util.res.StringManager;
   31   
   32   import org.apache.coyote.ActionCode;
   33   import org.apache.coyote.OutputBuffer;
   34   import org.apache.coyote.Response;
   35   
   36   /**
   37    * Output buffer.
   38    * 
   39    * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
   40    */
   41   public class InternalOutputBuffer 
   42       implements OutputBuffer, ByteChunk.ByteOutputChannel {
   43   
   44       // -------------------------------------------------------------- Constants
   45   
   46   
   47       // ----------------------------------------------------------- Constructors
   48   
   49   
   50       /**
   51        * Default constructor.
   52        */
   53       public InternalOutputBuffer(Response response) {
   54           this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
   55       }
   56   
   57   
   58       /**
   59        * Alternate constructor.
   60        */
   61       public InternalOutputBuffer(Response response, int headerBufferSize) {
   62   
   63           this.response = response;
   64           
   65           headers = response.getMimeHeaders();
   66   
   67           buf = new byte[headerBufferSize];
   68   
   69           outputStreamOutputBuffer = new OutputStreamOutputBuffer();
   70   
   71           filterLibrary = new OutputFilter[0];
   72           activeFilters = new OutputFilter[0];
   73           lastActiveFilter = -1;
   74   
   75           socketBuffer = new ByteChunk();
   76           socketBuffer.setByteOutputChannel(this);
   77   
   78           committed = false;
   79           finished = false;
   80   
   81       }
   82   
   83   
   84       // -------------------------------------------------------------- Variables
   85   
   86   
   87       /**
   88        * The string manager for this package.
   89        */
   90       protected static StringManager sm =
   91           StringManager.getManager(Constants.Package);
   92   
   93   
   94       // ----------------------------------------------------- Instance Variables
   95   
   96   
   97       /**
   98        * Associated Coyote response.
   99        */
  100       protected Response response;
  101   
  102   
  103       /**
  104        * Headers of the associated request.
  105        */
  106       protected MimeHeaders headers;
  107   
  108   
  109       /**
  110        * Committed flag.
  111        */
  112       protected boolean committed;
  113   
  114   
  115       /**
  116        * Finished flag.
  117        */
  118       protected boolean finished;
  119   
  120   
  121       /**
  122        * The buffer used for header composition.
  123        */
  124       protected byte[] buf;
  125   
  126   
  127       /**
  128        * Position in the buffer.
  129        */
  130       protected int pos;
  131   
  132   
  133       /**
  134        * Underlying output stream.
  135        */
  136       protected OutputStream outputStream;
  137   
  138   
  139       /**
  140        * Underlying output buffer.
  141        */
  142       protected OutputBuffer outputStreamOutputBuffer;
  143   
  144   
  145       /**
  146        * Filter library.
  147        * Note: Filter[0] is always the "chunked" filter.
  148        */
  149       protected OutputFilter[] filterLibrary;
  150   
  151   
  152       /**
  153        * Active filter (which is actually the top of the pipeline).
  154        */
  155       protected OutputFilter[] activeFilters;
  156   
  157   
  158       /**
  159        * Index of the last active filter.
  160        */
  161       protected int lastActiveFilter;
  162   
  163   
  164       /**
  165        * Socket buffer.
  166        */
  167       protected ByteChunk socketBuffer;
  168   
  169   
  170       /**
  171        * Socket buffer (extra buffering to reduce number of packets sent).
  172        */
  173       protected boolean useSocketBuffer = false;
  174   
  175   
  176       // ------------------------------------------------------------- Properties
  177   
  178   
  179       /**
  180        * Set the underlying socket output stream.
  181        */
  182       public void setOutputStream(OutputStream outputStream) {
  183   
  184           // FIXME: Check for null ?
  185   
  186           this.outputStream = outputStream;
  187   
  188       }
  189   
  190   
  191       /**
  192        * Get the underlying socket output stream.
  193        */
  194       public OutputStream getOutputStream() {
  195   
  196           return outputStream;
  197   
  198       }
  199   
  200   
  201       /**
  202        * Set the socket buffer size.
  203        */
  204       public void setSocketBuffer(int socketBufferSize) {
  205   
  206           if (socketBufferSize > 500) {
  207               useSocketBuffer = true;
  208               socketBuffer.allocate(socketBufferSize, socketBufferSize);
  209           } else {
  210               useSocketBuffer = false;
  211           }
  212   
  213       }
  214   
  215   
  216       /**
  217        * Add an output filter to the filter library.
  218        */
  219       public void addFilter(OutputFilter filter) {
  220   
  221           OutputFilter[] newFilterLibrary = 
  222               new OutputFilter[filterLibrary.length + 1];
  223           for (int i = 0; i < filterLibrary.length; i++) {
  224               newFilterLibrary[i] = filterLibrary[i];
  225           }
  226           newFilterLibrary[filterLibrary.length] = filter;
  227           filterLibrary = newFilterLibrary;
  228   
  229           activeFilters = new OutputFilter[filterLibrary.length];
  230   
  231       }
  232   
  233   
  234       /**
  235        * Get filters.
  236        */
  237       public OutputFilter[] getFilters() {
  238   
  239           return filterLibrary;
  240   
  241       }
  242   
  243   
  244       /**
  245        * Clear filters.
  246        */
  247       public void clearFilters() {
  248   
  249           filterLibrary = new OutputFilter[0];
  250           lastActiveFilter = -1;
  251   
  252       }
  253   
  254   
  255       /**
  256        * Add an output filter to the filter library.
  257        */
  258       public void addActiveFilter(OutputFilter filter) {
  259   
  260           if (lastActiveFilter == -1) {
  261               filter.setBuffer(outputStreamOutputBuffer);
  262           } else {
  263               for (int i = 0; i <= lastActiveFilter; i++) {
  264                   if (activeFilters[i] == filter)
  265                       return;
  266               }
  267               filter.setBuffer(activeFilters[lastActiveFilter]);
  268           }
  269   
  270           activeFilters[++lastActiveFilter] = filter;
  271   
  272           filter.setResponse(response);
  273   
  274       }
  275   
  276   
  277       // --------------------------------------------------------- Public Methods
  278   
  279   
  280       /**
  281        * Flush the response.
  282        * 
  283        * @throws IOException an undelying I/O error occured
  284        */
  285       public void flush()
  286           throws IOException {
  287   
  288           if (!committed) {
  289   
  290               // Send the connector a request for commit. The connector should
  291               // then validate the headers, send them (using sendHeader) and 
  292               // set the filters accordingly.
  293               response.action(ActionCode.ACTION_COMMIT, null);
  294   
  295           }
  296   
  297           // Flush the current buffer
  298           if (useSocketBuffer) {
  299               socketBuffer.flushBuffer();
  300           }
  301   
  302       }
  303   
  304   
  305       /**
  306        * Reset current response.
  307        * 
  308        * @throws IllegalStateException if the response has already been committed
  309        */
  310       public void reset() {
  311   
  312           if (committed)
  313               throw new IllegalStateException(/*FIXME:Put an error message*/);
  314   
  315           // Recycle Request object
  316           response.recycle();
  317   
  318       }
  319   
  320   
  321       /**
  322        * Recycle the output buffer. This should be called when closing the 
  323        * connection.
  324        */
  325       public void recycle() {
  326   
  327           // Recycle Request object
  328           response.recycle();
  329           socketBuffer.recycle();
  330   
  331           outputStream = null;
  332           pos = 0;
  333           lastActiveFilter = -1;
  334           committed = false;
  335           finished = false;
  336   
  337       }
  338   
  339   
  340       /**
  341        * End processing of current HTTP request.
  342        * Note: All bytes of the current request should have been already 
  343        * consumed. This method only resets all the pointers so that we are ready
  344        * to parse the next HTTP request.
  345        */
  346       public void nextRequest() {
  347   
  348           // Recycle Request object
  349           response.recycle();
  350           socketBuffer.recycle();
  351   
  352           // Recycle filters
  353           for (int i = 0; i <= lastActiveFilter; i++) {
  354               activeFilters[i].recycle();
  355           }
  356   
  357           // Reset pointers
  358           pos = 0;
  359           lastActiveFilter = -1;
  360           committed = false;
  361           finished = false;
  362   
  363       }
  364   
  365   
  366       /**
  367        * End request.
  368        * 
  369        * @throws IOException an undelying I/O error occured
  370        */
  371       public void endRequest()
  372           throws IOException {
  373   
  374           if (!committed) {
  375   
  376               // Send the connector a request for commit. The connector should
  377               // then validate the headers, send them (using sendHeader) and 
  378               // set the filters accordingly.
  379               response.action(ActionCode.ACTION_COMMIT, null);
  380   
  381           }
  382   
  383           if (finished)
  384               return;
  385   
  386           if (lastActiveFilter != -1)
  387               activeFilters[lastActiveFilter].end();
  388   
  389           if (useSocketBuffer) {
  390               socketBuffer.flushBuffer();
  391           }
  392   
  393           finished = true;
  394   
  395       }
  396   
  397   
  398       // ------------------------------------------------ HTTP/1.1 Output Methods
  399   
  400   
  401       /**
  402        * Send an acknoledgement.
  403        */
  404       public void sendAck()
  405           throws IOException {
  406   
  407           if (!committed)
  408               outputStream.write(Constants.ACK_BYTES);
  409   
  410       }
  411   
  412   
  413       /**
  414        * Send the response status line.
  415        */
  416       public void sendStatus() {
  417   
  418           // Write protocol name
  419           write(Constants.HTTP_11_BYTES);
  420           buf[pos++] = Constants.SP;
  421   
  422           // Write status code
  423           int status = response.getStatus();
  424           switch (status) {
  425           case 200:
  426               write(Constants._200_BYTES);
  427               break;
  428           case 400:
  429               write(Constants._400_BYTES);
  430               break;
  431           case 404:
  432               write(Constants._404_BYTES);
  433               break;
  434           default:
  435               write(status);
  436           }
  437   
  438           buf[pos++] = Constants.SP;
  439   
  440           // Write message
  441           String message = response.getMessage();
  442           if (message == null) {
  443               write(getMessage(status));
  444           } else {
  445               write(message);
  446           }
  447   
  448           // End the response status line
  449           if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
  450              AccessController.doPrivileged(
  451                   new PrivilegedAction(){
  452                       public Object run(){
  453                           buf[pos++] = Constants.CR;
  454                           buf[pos++] = Constants.LF;
  455                           return null;
  456                       }
  457                   }
  458              );
  459           } else {
  460               buf[pos++] = Constants.CR;
  461               buf[pos++] = Constants.LF;
  462           }
  463   
  464       }
  465   
  466       private String getMessage(final int message){
  467           if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
  468              return (String)AccessController.doPrivileged(
  469                   new PrivilegedAction(){
  470                       public Object run(){
  471                           return HttpMessages.getMessage(message); 
  472                       }
  473                   }
  474              );
  475           } else {
  476               return HttpMessages.getMessage(message);
  477           }
  478       }
  479   
  480       /**
  481        * Send a header.
  482        * 
  483        * @param name Header name
  484        * @param value Header value
  485        */
  486       public void sendHeader(MessageBytes name, MessageBytes value) {
  487   
  488           write(name);
  489           buf[pos++] = Constants.COLON;
  490           buf[pos++] = Constants.SP;
  491           write(value);
  492           buf[pos++] = Constants.CR;
  493           buf[pos++] = Constants.LF;
  494   
  495       }
  496   
  497   
  498       /**
  499        * Send a header.
  500        * 
  501        * @param name Header name
  502        * @param value Header value
  503        */
  504       public void sendHeader(ByteChunk name, ByteChunk value) {
  505   
  506           write(name);
  507           buf[pos++] = Constants.COLON;
  508           buf[pos++] = Constants.SP;
  509           write(value);
  510           buf[pos++] = Constants.CR;
  511           buf[pos++] = Constants.LF;
  512   
  513       }
  514   
  515   
  516       /**
  517        * Send a header.
  518        * 
  519        * @param name Header name
  520        * @param value Header value
  521        */
  522       public void sendHeader(String name, String value) {
  523   
  524           write(name);
  525           buf[pos++] = Constants.COLON;
  526           buf[pos++] = Constants.SP;
  527           write(value);
  528           buf[pos++] = Constants.CR;
  529           buf[pos++] = Constants.LF;
  530   
  531       }
  532   
  533   
  534       /**
  535        * End the header block.
  536        */
  537       public void endHeaders() {
  538   
  539           buf[pos++] = Constants.CR;
  540           buf[pos++] = Constants.LF;
  541   
  542       }
  543   
  544   
  545       // --------------------------------------------------- OutputBuffer Methods
  546   
  547   
  548       /**
  549        * Write the contents of a byte chunk.
  550        * 
  551        * @param chunk byte chunk
  552        * @return number of bytes written
  553        * @throws IOException an undelying I/O error occured
  554        */
  555       public int doWrite(ByteChunk chunk, Response res) 
  556           throws IOException {
  557   
  558           if (!committed) {
  559   
  560               // Send the connector a request for commit. The connector should
  561               // then validate the headers, send them (using sendHeaders) and 
  562               // set the filters accordingly.
  563               response.action(ActionCode.ACTION_COMMIT, null);
  564   
  565           }
  566   
  567           if (lastActiveFilter == -1)
  568               return outputStreamOutputBuffer.doWrite(chunk, res);
  569           else
  570               return activeFilters[lastActiveFilter].doWrite(chunk, res);
  571   
  572       }
  573   
  574   
  575       // ------------------------------------------------------ Protected Methods
  576   
  577   
  578       /**
  579        * Commit the response.
  580        * 
  581        * @throws IOException an undelying I/O error occured
  582        */
  583       protected void commit()
  584           throws IOException {
  585   
  586           // The response is now committed
  587           committed = true;
  588           response.setCommitted(true);
  589   
  590           if (pos > 0) {
  591               // Sending the response header buffer
  592               if (useSocketBuffer) {
  593                   socketBuffer.append(buf, 0, pos);
  594               } else {
  595                   outputStream.write(buf, 0, pos);
  596               }
  597           }
  598   
  599       }
  600   
  601   
  602       /**
  603        * This method will write the contents of the specyfied message bytes 
  604        * buffer to the output stream, without filtering. This method is meant to
  605        * be used to write the response header.
  606        * 
  607        * @param mb data to be written
  608        */
  609       protected void write(MessageBytes mb) {
  610   
  611           if (mb.getType() == MessageBytes.T_BYTES) {
  612               ByteChunk bc = mb.getByteChunk();
  613               write(bc);
  614           } else if (mb.getType() == MessageBytes.T_CHARS) {
  615               CharChunk cc = mb.getCharChunk();
  616               write(cc);
  617           } else {
  618               write(mb.toString());
  619           }
  620   
  621       }
  622   
  623   
  624       /**
  625        * This method will write the contents of the specyfied message bytes 
  626        * buffer to the output stream, without filtering. This method is meant to
  627        * be used to write the response header.
  628        * 
  629        * @param bc data to be written
  630        */
  631       protected void write(ByteChunk bc) {
  632   
  633           // Writing the byte chunk to the output buffer
  634           int length = bc.getLength();
  635           System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
  636           pos = pos + length;
  637   
  638       }
  639   
  640   
  641       /**
  642        * This method will write the contents of the specyfied char 
  643        * buffer to the output stream, without filtering. This method is meant to
  644        * be used to write the response header.
  645        * 
  646        * @param cc data to be written
  647        */
  648       protected void write(CharChunk cc) {
  649   
  650           int start = cc.getStart();
  651           int end = cc.getEnd();
  652           char[] cbuf = cc.getBuffer();
  653           for (int i = start; i < end; i++) {
  654               char c = cbuf[i];
  655               // Note:  This is clearly incorrect for many strings,
  656               // but is the only consistent approach within the current
  657               // servlet framework.  It must suffice until servlet output
  658               // streams properly encode their output.
  659               if ((c <= 31) && (c != 9)) {
  660                   c = ' ';
  661               } else if (c == 127) {
  662                   c = ' ';
  663               }
  664               buf[pos++] = (byte) c;
  665           }
  666   
  667       }
  668   
  669   
  670       /**
  671        * This method will write the contents of the specyfied byte 
  672        * buffer to the output stream, without filtering. This method is meant to
  673        * be used to write the response header.
  674        * 
  675        * @param b data to be written
  676        */
  677       public void write(byte[] b) {
  678   
  679           // Writing the byte chunk to the output buffer
  680           System.arraycopy(b, 0, buf, pos, b.length);
  681           pos = pos + b.length;
  682   
  683       }
  684   
  685   
  686       /**
  687        * This method will write the contents of the specyfied String to the 
  688        * output stream, without filtering. This method is meant to be used to 
  689        * write the response header.
  690        * 
  691        * @param s data to be written
  692        */
  693       protected void write(String s) {
  694   
  695           if (s == null)
  696               return;
  697   
  698           // From the Tomcat 3.3 HTTP/1.0 connector
  699           int len = s.length();
  700           for (int i = 0; i < len; i++) {
  701               char c = s.charAt (i);
  702               // Note:  This is clearly incorrect for many strings,
  703               // but is the only consistent approach within the current
  704               // servlet framework.  It must suffice until servlet output
  705               // streams properly encode their output.
  706               if ((c <= 31) && (c != 9)) {
  707                   c = ' ';
  708               } else if (c == 127) {
  709                   c = ' ';
  710               }
  711               buf[pos++] = (byte) c;
  712           }
  713   
  714       }
  715   
  716   
  717       /**
  718        * This method will print the specified integer to the output stream, 
  719        * without filtering. This method is meant to be used to write the 
  720        * response header.
  721        * 
  722        * @param i data to be written
  723        */
  724       protected void write(int i) {
  725   
  726           write(String.valueOf(i));
  727   
  728       }
  729   
  730   
  731       /**
  732        * Callback to write data from the buffer.
  733        */
  734       public void realWriteBytes(byte cbuf[], int off, int len)
  735           throws IOException {
  736           if (len > 0) {
  737               outputStream.write(cbuf, off, len);
  738           }
  739       }
  740   
  741   
  742       // ----------------------------------- OutputStreamOutputBuffer Inner Class
  743   
  744   
  745       /**
  746        * This class is an output buffer which will write data to an output
  747        * stream.
  748        */
  749       protected class OutputStreamOutputBuffer 
  750           implements OutputBuffer {
  751   
  752   
  753           /**
  754            * Write chunk.
  755            */
  756           public int doWrite(ByteChunk chunk, Response res) 
  757               throws IOException {
  758   
  759               int length = chunk.getLength();
  760               if (useSocketBuffer) {
  761                   socketBuffer.append(chunk.getBuffer(), chunk.getStart(), 
  762                                       length);
  763               } else {
  764                   outputStream.write(chunk.getBuffer(), chunk.getStart(), 
  765                                      length);
  766               }
  767               return length;
  768   
  769           }
  770   
  771   
  772       }
  773   
  774   
  775   }

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