Home » HttpComponents-Core-4.0.1 » org.apache.http.impl.entity » [javadoc | source]

    1   /*
    2    * $HeadURL: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/tags/4.0-alpha2/src/java/org/apache/http/impl/entity/DefaultEntityDeserializer.java $
    3    * $Revision: 390875 $
    4    * $Date: 2006-04-02 20:06:20 +0200 (Sun, 02 Apr 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 1999-2006 The Apache Software Foundation
    9    *
   10    *  Licensed under the Apache License, Version 2.0 (the "License");
   11    *  you may not use this file except in compliance with the License.
   12    *  You may obtain a copy of the License at
   13    *
   14    *      http://www.apache.org/licenses/LICENSE-2.0
   15    *
   16    *  Unless required by applicable law or agreed to in writing, software
   17    *  distributed under the License is distributed on an "AS IS" BASIS,
   18    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   19    *  See the License for the specific language governing permissions and
   20    *  limitations under the License.
   21    * ====================================================================
   22    *
   23    * This software consists of voluntary contributions made by many
   24    * individuals on behalf of the Apache Software Foundation.  For more
   25    * information on the Apache Software Foundation, please see
   26    * <http://www.apache.org/>.
   27    *
   28    */
   29   
   30   package org.apache.http.impl.entity;
   31   
   32   import java.io.IOException;
   33   
   34   import org.apache.http.Header;
   35   import org.apache.http.HeaderElement;
   36   import org.apache.http.HttpException;
   37   import org.apache.http.HttpMessage;
   38   import org.apache.http.HttpEntity;
   39   import org.apache.http.ProtocolException;
   40   import org.apache.http.entity.BasicHttpEntity;
   41   import org.apache.http.entity.EntityDeserializer;
   42   import org.apache.http.io.ChunkedInputStream;
   43   import org.apache.http.io.ContentLengthInputStream;
   44   import org.apache.http.io.HttpDataInputStream;
   45   import org.apache.http.io.HttpDataReceiver;
   46   import org.apache.http.params.HttpParams;
   47   import org.apache.http.params.HttpProtocolParams;
   48   import org.apache.http.protocol.HTTP;
   49   
   50   /**
   51    * Default implementation of an entity generator.
   52    * <p>
   53    * This entity generator comforms to the entity transfer rules outlined in the 
   54    * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec4.4">Section 4.4</a>, 
   55    * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>, 
   56    * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41">Section 14.41</a>
   57    * and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec14.13">Section 14.13</a>
   58    * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
   59    * </p>
   60    * <h>4.4 Message Length</h>
   61    * <p>
   62    * The transfer-length of a message is the length of the message-body as it appears in the 
   63    * message; that is, after any transfer-codings have been applied. When a message-body is 
   64    * included with a message, the transfer-length of that body is determined by one of the 
   65    * following (in order of precedence):
   66    * </p>
   67    * <p>
   68    * 1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204, 
   69    * and 304 responses and any response to a HEAD request) is always terminated by the first 
   70    * empty line after the header fields, regardless of the entity-header fields present in the 
   71    * message.
   72    * </p>
   73    * <p>
   74    * 2.If a Transfer-Encoding header field (section 14.41) is present and has any value other 
   75    * than "identity", then the transfer-length is defined by use of the "chunked" transfer-
   76    * coding (section 3.6), unless the message is terminated by closing the connection.
   77    * </p>
   78    * <p>
   79    * 3.If a Content-Length header field (section 14.13) is present, its decimal value in 
   80    * OCTETs represents both the entity-length and the transfer-length. The Content-Length 
   81    * header field MUST NOT be sent if these two lengths are different (i.e., if a 
   82    * Transfer-Encoding
   83    * </p>
   84    * <pre>
   85    *    header field is present). If a message is received with both a
   86    *    Transfer-Encoding header field and a Content-Length header field,
   87    *    the latter MUST be ignored.
   88    * </pre>
   89    * <p>
   90    * 4.If the message uses the media type "multipart/byteranges", and the ransfer-length is not 
   91    * otherwise specified, then this self- elimiting media type defines the transfer-length. 
   92    * This media type UST NOT be used unless the sender knows that the recipient can arse it; the 
   93    * presence in a request of a Range header with ultiple byte- range specifiers from a 1.1 
   94    * client implies that the lient can parse multipart/byteranges responses.
   95    * </p>
   96    * <pre>
   97    *     A range header might be forwarded by a 1.0 proxy that does not
   98    *     understand multipart/byteranges; in this case the server MUST
   99    *     delimit the message using methods defined in items 1,3 or 5 of
  100    *     this section.
  101    * </pre>
  102    * <p>
  103    * 5.By the server closing the connection. (Closing the connection cannot be used to indicate 
  104    * the end of a request body, since that would leave no possibility for the server to send back 
  105    * a response.)
  106    * </p>
  107    * <p>
  108    * For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body 
  109    * MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1 
  110    * compliant. If a request contains a message-body and a Content-Length is not given, the 
  111    * server SHOULD respond with 400 (bad request) if it cannot determine the length of the 
  112    * message, or with 411 (length required) if it wishes to insist on receiving a valid 
  113    * Content-Length.
  114    * </p>
  115    * <p>All HTTP/1.1 applications that receive entities MUST accept the "chunked" transfer-coding 
  116    * (section 3.6), thus allowing this mechanism to be used for messages when the message 
  117    * length cannot be determined in advance. 
  118    * </p>
  119    * <h>3.6 Transfer Codings</h>
  120    * <p>
  121    * Transfer-coding values are used to indicate an encoding transformation that 
  122    * has been, can be, or may need to be applied to an entity-body in order to ensure 
  123    * "safe transport" through the network. This differs from a content coding in that 
  124    * the transfer-coding is a property of the message, not of the original entity.
  125    * </p>
  126    * <pre>
  127    * transfer-coding         = "chunked" | transfer-extension
  128    * transfer-extension      = token *( ";" parameter )
  129    * </pre>
  130    * <p>
  131    * Parameters are in the form of attribute/value pairs.
  132    * </p>
  133    * <pre>
  134    * parameter               = attribute "=" value
  135    * attribute               = token
  136    * value                   = token | quoted-string
  137    * </pre>
  138    * <p>
  139    * All transfer-coding values are case-insensitive. HTTP/1.1 uses transfer-coding values in 
  140    * the TE header field (section 14.39) and in the Transfer-Encoding header field (section 14.41).
  141    * </p>
  142    * <p>
  143    * Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST 
  144    * include "chunked", unless the message is terminated by closing the connection. When the 
  145    * "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the 
  146    * message-body. The "chunked" transfer-coding MUST NOT be applied more than once to a 
  147    * message-body. These rules allow the recipient to determine the transfer-length of the 
  148    * message (section 4.4).
  149    * </p>
  150    * <h>14.41 Transfer-Encoding</h>
  151    * <p>
  152    * The Transfer-Encoding general-header field indicates what (if any) type of transformation has 
  153    * been applied to the message body in order to safely transfer it between the sender and the 
  154    * recipient. This differs from the content-coding in that the transfer-coding is a property of 
  155    * the message, not of the entity.
  156    * </p>
  157    * <pre>
  158    *   Transfer-Encoding       = "Transfer-Encoding" ":" 1#transfer-coding
  159    * </pre>
  160    * <p>
  161    * If multiple encodings have been applied to an entity, the transfer- codings MUST be listed in 
  162    * the order in which they were applied. Additional information about the encoding parameters 
  163    * MAY be provided by other entity-header fields not defined by this specification.
  164    * </p> 
  165    * <h>14.13 Content-Length</h>
  166    * <p>
  167    * The Content-Length entity-header field indicates the size of the entity-body, in decimal 
  168    * number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of 
  169    * the entity-body that would have been sent had the request been a GET.
  170    * </p>
  171    * <pre>
  172    *   Content-Length    = "Content-Length" ":" 1*DIGIT
  173    * </pre>
  174    * <p>
  175    * Applications SHOULD use this field to indicate the transfer-length of the message-body, 
  176    * unless this is prohibited by the rules in section 4.4. 
  177    * </p>
  178    * <p>
  179    * This entity generator currently supports only "chunked" and "identitiy" transfer-coding</a>
  180    * </p>
  181    * 
  182    * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
  183    *
  184    * @version $Revision: 390875 $
  185    * 
  186    * @since 4.0
  187    */
  188   public class DefaultEntityDeserializer implements EntityDeserializer {
  189   
  190       public DefaultEntityDeserializer() {
  191           super();
  192       }
  193   
  194       protected BasicHttpEntity doDeserialize(
  195               final HttpDataReceiver datareceiver,
  196               final HttpMessage message) throws HttpException, IOException {
  197           if (datareceiver == null) {
  198               throw new IllegalArgumentException("HTTP data receiver may not be null");
  199           }
  200           if (message == null) {
  201               throw new IllegalArgumentException("HTTP message may not be null");
  202           }
  203   
  204           BasicHttpEntity entity = new BasicHttpEntity();
  205           
  206           HttpParams params = message.getParams(); 
  207           boolean strict = params.isParameterTrue(HttpProtocolParams.STRICT_TRANSFER_ENCODING);
  208           
  209           Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE);
  210           Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING);
  211           Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
  212           Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
  213           // We use Transfer-Encoding if present and ignore Content-Length.
  214           // RFC2616, 4.4 item number 3
  215           if (transferEncodingHeader != null) {
  216               HeaderElement[] encodings = transferEncodingHeader.getElements();
  217               if (strict) {
  218                   // Currently only chunk and identity are supported
  219                   for (int i = 0; i < encodings.length; i++) {
  220                       String encoding = encodings[i].getName();
  221                       if (encoding != null && !encoding.equals("") 
  222                           && !encoding.equalsIgnoreCase(HTTP.CHUNK_CODING)
  223                           && !encoding.equalsIgnoreCase(HTTP.IDENTITY_CODING)) {
  224                           throw new ProtocolException("Unsupported transfer encoding: " + encoding);
  225                       }
  226                   }
  227               }
  228               // The chunked encoding must be the last one applied RFC2616, 14.41
  229               int len = encodings.length;
  230               if (HTTP.IDENTITY_CODING.equalsIgnoreCase(transferEncodingHeader.getValue())) {
  231                   entity.setChunked(false);
  232                   entity.setContentLength(-1);
  233                   entity.setContent(new HttpDataInputStream(datareceiver));                            
  234               } else if ((len > 0) && (HTTP.CHUNK_CODING.equalsIgnoreCase(
  235                       encodings[len - 1].getName()))) { 
  236                   entity.setChunked(true);
  237                   entity.setContentLength(-1);
  238                   entity.setContent(new ChunkedInputStream(datareceiver));
  239               } else {
  240                   if (strict) {
  241                       throw new ProtocolException("Chunk-encoding must be the last one applied");
  242                   }
  243                   entity.setChunked(false);
  244                   entity.setContentLength(-1);
  245                   entity.setContent(new HttpDataInputStream(datareceiver));                            
  246               }
  247           } else if (contentLengthHeader != null) {
  248               long contentlen = -1;
  249               Header[] headers = message.getHeaders(HTTP.CONTENT_LEN);
  250               if (strict && headers.length > 1) {
  251                   throw new ProtocolException("Multiple content length headers");
  252               }
  253               for (int i = headers.length - 1; i >= 0; i--) {
  254                   Header header = headers[i];
  255                   try {
  256                       contentlen = Long.parseLong(header.getValue());
  257                       break;
  258                   } catch (NumberFormatException e) {
  259                       if (strict) {
  260                           throw new ProtocolException("Invalid content length: " + header.getValue());
  261                       }
  262                   }
  263                   // See if we can have better luck with another header, if present
  264               }
  265               entity.setChunked(false);
  266               entity.setContentLength(contentlen);
  267               if (contentlen >= 0) {
  268                   entity.setContent(new ContentLengthInputStream(datareceiver, contentlen));
  269               } else {
  270                   entity.setContent(new HttpDataInputStream(datareceiver));
  271               }
  272           } else {
  273               entity.setChunked(false);
  274               entity.setContentLength(-1);
  275               entity.setContent(new HttpDataInputStream(datareceiver));                            
  276           }
  277           if (contentTypeHeader != null) {
  278               entity.setContentType(contentTypeHeader);    
  279           }
  280           if (contentEncodingHeader != null) {
  281               entity.setContentEncoding(contentEncodingHeader);    
  282           }
  283           return entity;
  284       }
  285           
  286       public HttpEntity deserialize(
  287               final HttpDataReceiver datareceiver,
  288               final HttpMessage message) throws HttpException, IOException {
  289           return doDeserialize(datareceiver, message);
  290       }
  291       
  292   }

Home » HttpComponents-Core-4.0.1 » org.apache.http.impl.entity » [javadoc | source]