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 public class HttpParser {
51
52 /*** Log object for this class. */
53 private static final Log LOG = LogFactory.getLog(HttpParser.class);
54
55 /***
56 * Constructor for HttpParser.
57 */
58 private HttpParser() { }
59
60 /***
61 * Return byte array from an (unchunked) input stream.
62 * Stop reading when <tt>"\n"</tt> terminator encountered
63 * If the stream ends before the line terminator is found,
64 * the last part of the string will still be returned.
65 * If no input data available, <code>null</code> is returned.
66 *
67 * @param inputStream the stream to read from
68 *
69 * @throws IOException if an I/O problem occurs
70 * @return a byte array from the stream
71 */
72 public static byte[] readRawLine(InputStream inputStream) throws IOException {
73 LOG.trace("enter HttpParser.readRawLine()");
74
75 ByteArrayOutputStream buf = new ByteArrayOutputStream();
76 int ch;
77 while ((ch = inputStream.read()) >= 0) {
78 buf.write(ch);
79 if (ch == '\n') {
80 break;
81 }
82 }
83 if (buf.size() == 0) {
84 return null;
85 }
86 return buf.toByteArray();
87 }
88
89 /***
90 * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
91 * If the stream ends before the line terminator is found,
92 * the last part of the string will still be returned.
93 * If no input data available, <code>null</code> is returned.
94 *
95 * @param inputStream the stream to read from
96 * @param charset charset of HTTP protocol elements
97 *
98 * @throws IOException if an I/O problem occurs
99 * @return a line from the stream
100 *
101 * @since 3.0
102 */
103 public static String readLine(InputStream inputStream, String charset) throws IOException {
104 LOG.trace("enter HttpParser.readLine(InputStream, String)");
105 byte[] rawdata = readRawLine(inputStream);
106 if (rawdata == null) {
107 return null;
108 }
109
110 int len = rawdata.length;
111 int offset = 0;
112 if (len > 0) {
113 if (rawdata[len - 1] == '\n') {
114 offset++;
115 if (len > 1) {
116 if (rawdata[len - 2] == '\r') {
117 offset++;
118 }
119 }
120 }
121 }
122 return EncodingUtil.getString(rawdata, 0, len - offset, charset);
123 }
124
125 /***
126 * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
127 * If the stream ends before the line terminator is found,
128 * the last part of the string will still be returned.
129 * If no input data available, <code>null</code> is returned
130 *
131 * @param inputStream the stream to read from
132 *
133 * @throws IOException if an I/O problem occurs
134 * @return a line from the stream
135 *
136 * @deprecated use #readLine(InputStream, String)
137 */
138
139 public static String readLine(InputStream inputStream) throws IOException {
140 LOG.trace("enter HttpParser.readLine(InputStream)");
141 return readLine(inputStream, "US-ASCII");
142 }
143
144 /***
145 * Parses headers from the given stream. Headers with the same name are not
146 * combined.
147 *
148 * @param is the stream to read headers from
149 * @param charset the charset to use for reading the data
150 *
151 * @return an array of headers in the order in which they were parsed
152 *
153 * @throws IOException if an IO error occurs while reading from the stream
154 * @throws HttpException if there is an error parsing a header value
155 *
156 * @since 3.0
157 */
158 public static Header[] parseHeaders(InputStream is, String charset) throws IOException, HttpException {
159 LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
160
161 ArrayList headers = new ArrayList();
162 String name = null;
163 StringBuffer value = null;
164 for (; ;) {
165 String line = HttpParser.readLine(is, charset);
166 if ((line == null) || (line.trim().length() < 1)) {
167 break;
168 }
169
170
171
172
173
174 if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
175
176
177 if (value != null) {
178 value.append(' ');
179 value.append(line.trim());
180 }
181 } else {
182
183 if (name != null) {
184 headers.add(new Header(name, value.toString()));
185 }
186
187
188
189 int colon = line.indexOf(":");
190 if (colon < 0) {
191 throw new ProtocolException("Unable to parse header: " + line);
192 }
193 name = line.substring(0, colon).trim();
194 value = new StringBuffer(line.substring(colon + 1).trim());
195 }
196
197 }
198
199
200 if (name != null) {
201 headers.add(new Header(name, value.toString()));
202 }
203
204 return (Header[]) headers.toArray(new Header[headers.size()]);
205 }
206
207 /***
208 * Parses headers from the given stream. Headers with the same name are not
209 * combined.
210 *
211 * @param is the stream to read headers from
212 *
213 * @return an array of headers in the order in which they were parsed
214 *
215 * @throws IOException if an IO error occurs while reading from the stream
216 * @throws HttpException if there is an error parsing a header value
217 *
218 * @deprecated use #parseHeaders(InputStream, String)
219 */
220 public static Header[] parseHeaders(InputStream is) throws IOException, HttpException {
221 LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
222 return parseHeaders(is, "US-ASCII");
223 }
224 }