1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.apache.commons.httpclient;
31
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.ByteArrayOutputStream;
35 import java.util.ArrayList;
36
37 import org.apache.commons.httpclient.util.EncodingUtil;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 /***
42 * A utility class for parsing http header values according to
43 * RFC-2616 Section 4 and 19.3.
44 *
45 * @author Michael Becke
46 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
47 *
48 * @since 2.0beta1
49 */
50 @SuppressWarnings("unchecked")
51 public class HttpParser {
52
53 /*** Log object for this class. */
54 private static final Log LOG = LogFactory.getLog(HttpParser.class);
55
56 /***
57 * Constructor for HttpParser.
58 */
59 private HttpParser() { }
60
61 /***
62 * Return byte array from an (unchunked) input stream.
63 * Stop reading when <tt>"\n"</tt> terminator encountered
64 * If the stream ends before the line terminator is found,
65 * the last part of the string will still be returned.
66 * If no input data available, <code>null</code> is returned.
67 *
68 * @param inputStream the stream to read from
69 *
70 * @throws IOException if an I/O problem occurs
71 * @return a byte array from the stream
72 */
73 public static byte[] readRawLine(InputStream inputStream) throws IOException {
74 LOG.trace("enter HttpParser.readRawLine()");
75
76 ByteArrayOutputStream buf = new ByteArrayOutputStream();
77 int ch;
78 while ((ch = inputStream.read()) >= 0) {
79 buf.write(ch);
80 if (ch == '\n') {
81 break;
82 }
83 }
84 if (buf.size() == 0) {
85 return null;
86 }
87 return buf.toByteArray();
88 }
89
90 /***
91 * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
92 * If the stream ends before the line terminator is found,
93 * the last part of the string will still be returned.
94 * If no input data available, <code>null</code> is returned.
95 *
96 * @param inputStream the stream to read from
97 * @param charset charset of HTTP protocol elements
98 *
99 * @throws IOException if an I/O problem occurs
100 * @return a line from the stream
101 *
102 * @since 3.0
103 */
104 public static String readLine(InputStream inputStream, String charset) throws IOException {
105 LOG.trace("enter HttpParser.readLine(InputStream, String)");
106 byte[] rawdata = readRawLine(inputStream);
107 if (rawdata == null) {
108 return null;
109 }
110
111 int len = rawdata.length;
112 int offset = 0;
113 if (len > 0) {
114 if (rawdata[len - 1] == '\n') {
115 offset++;
116 if (len > 1) {
117 if (rawdata[len - 2] == '\r') {
118 offset++;
119 }
120 }
121 }
122 }
123 return EncodingUtil.getString(rawdata, 0, len - offset, charset);
124 }
125
126 /***
127 * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
128 * If the stream ends before the line terminator is found,
129 * the last part of the string will still be returned.
130 * If no input data available, <code>null</code> is returned
131 *
132 * @param inputStream the stream to read from
133 *
134 * @throws IOException if an I/O problem occurs
135 * @return a line from the stream
136 *
137 * @deprecated use #readLine(InputStream, String)
138 */
139
140 public static String readLine(InputStream inputStream) throws IOException {
141 LOG.trace("enter HttpParser.readLine(InputStream)");
142 return readLine(inputStream, "US-ASCII");
143 }
144
145 /***
146 * Parses headers from the given stream. Headers with the same name are not
147 * combined.
148 *
149 * @param is the stream to read headers from
150 * @param charset the charset to use for reading the data
151 *
152 * @return an array of headers in the order in which they were parsed
153 *
154 * @throws IOException if an IO error occurs while reading from the stream
155 * @throws HttpException if there is an error parsing a header value
156 *
157 * @since 3.0
158 */
159 public static Header[] parseHeaders(InputStream is, String charset) throws IOException, HttpException {
160 LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
161
162 ArrayList headers = new ArrayList();
163 String name = null;
164 StringBuffer value = null;
165 for (; ;) {
166 String line = HttpParser.readLine(is, charset);
167 if ((line == null) || (line.trim().length() < 1)) {
168 break;
169 }
170
171
172
173
174
175 if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
176
177
178 if (value != null) {
179 value.append(' ');
180 value.append(line.trim());
181 }
182 } else {
183
184 if (name != null) {
185 headers.add(new Header(name, value.toString()));
186 }
187
188
189
190 int colon = line.indexOf(":");
191
192
193
194
195
196 if (colon < 0) {
197
198
199 name = "HttpClient-Bad-Header-Line-Failed-Parse";
200 value = new StringBuffer(line);
201
202 } else {
203 name = line.substring(0, colon).trim();
204 value = new StringBuffer(line.substring(colon + 1).trim());
205 }
206
207 }
208
209 }
210
211
212 if (name != null) {
213 headers.add(new Header(name, value.toString()));
214 }
215
216 return (Header[]) headers.toArray(new Header[headers.size()]);
217 }
218
219 /***
220 * Parses headers from the given stream. Headers with the same name are not
221 * combined.
222 *
223 * @param is the stream to read headers from
224 *
225 * @return an array of headers in the order in which they were parsed
226 *
227 * @throws IOException if an IO error occurs while reading from the stream
228 * @throws HttpException if there is an error parsing a header value
229 *
230 * @deprecated use #parseHeaders(InputStream, String)
231 */
232 public static Header[] parseHeaders(InputStream is) throws IOException, HttpException {
233 LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
234 return parseHeaders(is, "US-ASCII");
235 }
236 }