Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/http/impl/entity/DefaultEntityDeserializer.java


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 }