Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
28 package ch.boye.httpclientandroidlib.impl.io;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.List;
34 import ch.boye.httpclientandroidlib.Header;
35 import ch.boye.httpclientandroidlib.HttpException;
36 import ch.boye.httpclientandroidlib.HttpMessage;
37 import ch.boye.httpclientandroidlib.ParseException;
38 import ch.boye.httpclientandroidlib.ProtocolException;
39 import ch.boye.httpclientandroidlib.io.HttpMessageParser;
40 import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
41 import ch.boye.httpclientandroidlib.message.LineParser;
42 import ch.boye.httpclientandroidlib.message.BasicLineParser;
43 import ch.boye.httpclientandroidlib.params.CoreConnectionPNames;
44 import ch.boye.httpclientandroidlib.params.HttpParams;
45 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
47 /**
48 * Abstract base class for HTTP message parsers that obtain input from
49 * an instance of {@link SessionInputBuffer}.
50 * <p>
51 * The following parameters can be used to customize the behavior of this
52 * class:
53 * <ul>
54 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
55 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
56 * </ul>
57 *
58 * @since 4.0
59 */
60 public abstract class AbstractMessageParser implements HttpMessageParser {
62 private static final int HEAD_LINE = 0;
63 private static final int HEADERS = 1;
65 private final SessionInputBuffer sessionBuffer;
66 private final int maxHeaderCount;
67 private final int maxLineLen;
68 private final List headerLines;
69 protected final LineParser lineParser;
71 private int state;
72 private HttpMessage message;
74 /**
75 * Creates an instance of this class.
76 *
77 * @param buffer the session input buffer.
78 * @param parser the line parser.
79 * @param params HTTP parameters.
80 */
81 public AbstractMessageParser(
82 final SessionInputBuffer buffer,
83 final LineParser parser,
84 final HttpParams params) {
85 super();
86 if (buffer == null) {
87 throw new IllegalArgumentException("Session input buffer may not be null");
88 }
89 if (params == null) {
90 throw new IllegalArgumentException("HTTP parameters may not be null");
91 }
92 this.sessionBuffer = buffer;
93 this.maxHeaderCount = params.getIntParameter(
94 CoreConnectionPNames.MAX_HEADER_COUNT, -1);
95 this.maxLineLen = params.getIntParameter(
96 CoreConnectionPNames.MAX_LINE_LENGTH, -1);
97 this.lineParser = (parser != null) ? parser : BasicLineParser.DEFAULT;
98 this.headerLines = new ArrayList();
99 this.state = HEAD_LINE;
100 }
102 /**
103 * Parses HTTP headers from the data receiver stream according to the generic
104 * format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3.
105 *
106 * @param inbuffer Session input buffer
107 * @param maxHeaderCount maximum number of headers allowed. If the number
108 * of headers received from the data stream exceeds maxCount value, an
109 * IOException will be thrown. Setting this parameter to a negative value
110 * or zero will disable the check.
111 * @param maxLineLen maximum number of characters for a header line,
112 * including the continuation lines. Setting this parameter to a negative
113 * value or zero will disable the check.
114 * @return array of HTTP headers
115 * @param parser line parser to use. Can be <code>null</code>, in which case
116 * the default implementation of this interface will be used.
117 *
118 * @throws IOException in case of an I/O error
119 * @throws HttpException in case of HTTP protocol violation
120 */
121 public static Header[] parseHeaders(
122 final SessionInputBuffer inbuffer,
123 int maxHeaderCount,
124 int maxLineLen,
125 LineParser parser)
126 throws HttpException, IOException {
127 if (parser == null) {
128 parser = BasicLineParser.DEFAULT;
129 }
130 List headerLines = new ArrayList();
131 return parseHeaders(inbuffer, maxHeaderCount, maxLineLen, parser, headerLines);
132 }
134 /**
135 * Parses HTTP headers from the data receiver stream according to the generic
136 * format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3.
137 *
138 * @param inbuffer Session input buffer
139 * @param maxHeaderCount maximum number of headers allowed. If the number
140 * of headers received from the data stream exceeds maxCount value, an
141 * IOException will be thrown. Setting this parameter to a negative value
142 * or zero will disable the check.
143 * @param maxLineLen maximum number of characters for a header line,
144 * including the continuation lines. Setting this parameter to a negative
145 * value or zero will disable the check.
146 * @param parser line parser to use.
147 * @param headerLines List of header lines. This list will be used to store
148 * intermediate results. This makes it possible to resume parsing of
149 * headers in case of a {@link java.io.InterruptedIOException}.
150 *
151 * @return array of HTTP headers
152 *
153 * @throws IOException in case of an I/O error
154 * @throws HttpException in case of HTTP protocol violation
155 *
156 * @since 4.1
157 */
158 public static Header[] parseHeaders(
159 final SessionInputBuffer inbuffer,
160 int maxHeaderCount,
161 int maxLineLen,
162 final LineParser parser,
163 final List headerLines)
164 throws HttpException, IOException {
166 if (inbuffer == null) {
167 throw new IllegalArgumentException("Session input buffer may not be null");
168 }
169 if (parser == null) {
170 throw new IllegalArgumentException("Line parser may not be null");
171 }
172 if (headerLines == null) {
173 throw new IllegalArgumentException("Header line list may not be null");
174 }
176 CharArrayBuffer current = null;
177 CharArrayBuffer previous = null;
178 for (;;) {
179 if (current == null) {
180 current = new CharArrayBuffer(64);
181 } else {
182 current.clear();
183 }
184 int l = inbuffer.readLine(current);
185 if (l == -1 || current.length() < 1) {
186 break;
187 }
188 // Parse the header name and value
189 // Check for folded headers first
190 // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
191 // discussion on folded headers
192 if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
193 // we have continuation folded header
194 // so append value
195 int i = 0;
196 while (i < current.length()) {
197 char ch = current.charAt(i);
198 if (ch != ' ' && ch != '\t') {
199 break;
200 }
201 i++;
202 }
203 if (maxLineLen > 0
204 && previous.length() + 1 + current.length() - i > maxLineLen) {
205 throw new IOException("Maximum line length limit exceeded");
206 }
207 previous.append(' ');
208 previous.append(current, i, current.length() - i);
209 } else {
210 headerLines.add(current);
211 previous = current;
212 current = null;
213 }
214 if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) {
215 throw new IOException("Maximum header count exceeded");
216 }
217 }
218 Header[] headers = new Header[headerLines.size()];
219 for (int i = 0; i < headerLines.size(); i++) {
220 CharArrayBuffer buffer = (CharArrayBuffer) headerLines.get(i);
221 try {
222 headers[i] = parser.parseHeader(buffer);
223 } catch (ParseException ex) {
224 throw new ProtocolException(ex.getMessage());
225 }
226 }
227 return headers;
228 }
230 /**
231 * Subclasses must override this method to generate an instance of
232 * {@link HttpMessage} based on the initial input from the session buffer.
233 * <p>
234 * Usually this method is expected to read just the very first line or
235 * the very first valid from the data stream and based on the input generate
236 * an appropriate instance of {@link HttpMessage}.
237 *
238 * @param sessionBuffer the session input buffer.
239 * @return HTTP message based on the input from the session buffer.
240 * @throws IOException in case of an I/O error.
241 * @throws HttpException in case of HTTP protocol violation.
242 * @throws ParseException in case of a parse error.
243 */
244 protected abstract HttpMessage parseHead(SessionInputBuffer sessionBuffer)
245 throws IOException, HttpException, ParseException;
247 public HttpMessage parse() throws IOException, HttpException {
248 int st = this.state;
249 switch (st) {
250 case HEAD_LINE:
251 try {
252 this.message = parseHead(this.sessionBuffer);
253 } catch (ParseException px) {
254 throw new ProtocolException(px.getMessage(), px);
255 }
256 this.state = HEADERS;
257 //$FALL-THROUGH$
258 case HEADERS:
259 Header[] headers = AbstractMessageParser.parseHeaders(
260 this.sessionBuffer,
261 this.maxHeaderCount,
262 this.maxLineLen,
263 this.lineParser,
264 this.headerLines);
265 this.message.setHeaders(headers);
266 HttpMessage result = this.message;
267 this.message = null;
268 this.headerLines.clear();
269 this.state = HEAD_LINE;
270 return result;
271 default:
272 throw new IllegalStateException("Inconsistent parser state");
273 }
274 }
276 }