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