1. /*
  2. * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpParser.java,v 1.12 2004/05/13 04:03:25 mbecke Exp $
  3. * $Revision: 1.12 $
  4. * $Date: 2004/05/13 04:03:25 $
  5. *
  6. * ====================================================================
  7. *
  8. * Copyright 1999-2004 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. package org.apache.commons.httpclient;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.io.ByteArrayOutputStream;
  33. import java.util.ArrayList;
  34. import org.apache.commons.httpclient.util.EncodingUtil;
  35. import org.apache.commons.logging.Log;
  36. import org.apache.commons.logging.LogFactory;
  37. /**
  38. * A utility class for parsing http header values.
  39. *
  40. * @author Michael Becke
  41. * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  42. *
  43. * @since 2.0beta1
  44. */
  45. public class HttpParser {
  46. /** Log object for this class. */
  47. private static final Log LOG = LogFactory.getLog(HttpParser.class);
  48. /**
  49. * Constructor for HttpParser.
  50. */
  51. private HttpParser() { }
  52. /**
  53. * Return byte array from an (unchunked) input stream.
  54. * Stop reading when <tt>"\n"</tt> terminator encountered
  55. * If the stream ends before the line terminator is found,
  56. * the last part of the string will still be returned.
  57. * If no input data available, <code>null</code> is returned
  58. *
  59. * @param inputStream the stream to read from
  60. *
  61. * @throws IOException if an I/O problem occurs
  62. * @return a byte array from the stream
  63. */
  64. public static byte[] readRawLine(InputStream inputStream) throws IOException {
  65. LOG.trace("enter HttpParser.readRawLine()");
  66. ByteArrayOutputStream buf = new ByteArrayOutputStream();
  67. int ch;
  68. while ((ch = inputStream.read()) >= 0) {
  69. buf.write(ch);
  70. if (ch == '\n') {
  71. break;
  72. }
  73. }
  74. if (buf.size() == 0) {
  75. return null;
  76. }
  77. return buf.toByteArray();
  78. }
  79. /**
  80. * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
  81. * If the stream ends before the line terminator is found,
  82. * the last part of the string will still be returned.
  83. * If no input data available, <code>null</code> is returned
  84. *
  85. * @param inputStream the stream to read from
  86. * @param charset charset of HTTP protocol elements
  87. *
  88. * @throws IOException if an I/O problem occurs
  89. * @return a line from the stream
  90. *
  91. * @since 3.0
  92. */
  93. public static String readLine(InputStream inputStream, String charset) throws IOException {
  94. LOG.trace("enter HttpParser.readLine(InputStream, String)");
  95. byte[] rawdata = readRawLine(inputStream);
  96. if (rawdata == null) {
  97. return null;
  98. }
  99. int len = rawdata.length;
  100. int offset = 0;
  101. if (len > 0) {
  102. if (rawdata[len - 1] == '\n') {
  103. offset++;
  104. if (len > 1) {
  105. if (rawdata[len - 2] == '\r') {
  106. offset++;
  107. }
  108. }
  109. }
  110. }
  111. return EncodingUtil.getString(rawdata, 0, len - offset, charset);
  112. }
  113. /**
  114. * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
  115. * If the stream ends before the line terminator is found,
  116. * the last part of the string will still be returned.
  117. * If no input data available, <code>null</code> is returned
  118. *
  119. * @param inputStream the stream to read from
  120. *
  121. * @throws IOException if an I/O problem occurs
  122. * @return a line from the stream
  123. *
  124. * @deprecated use #readLine(InputStream, String)
  125. */
  126. public static String readLine(InputStream inputStream) throws IOException {
  127. LOG.trace("enter HttpParser.readLine(InputStream)");
  128. return readLine(inputStream, "US-ASCII");
  129. }
  130. /**
  131. * Parses headers from the given stream. Headers with the same name are not
  132. * combined.
  133. *
  134. * @param is the stream to read headers from
  135. * @param charset the charset to use for reading the data
  136. *
  137. * @return an array of headers in the order in which they were parsed
  138. *
  139. * @throws IOException if an IO error occurs while reading from the stream
  140. * @throws HttpException if there is an error parsing a header value
  141. *
  142. * @since 3.0
  143. */
  144. public static Header[] parseHeaders(InputStream is, String charset) throws IOException, HttpException {
  145. LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
  146. ArrayList headers = new ArrayList();
  147. String name = null;
  148. StringBuffer value = null;
  149. for (; ;) {
  150. String line = HttpParser.readLine(is, charset);
  151. if ((line == null) || (line.length() < 1)) {
  152. break;
  153. }
  154. // Parse the header name and value
  155. // Check for folded headers first
  156. // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
  157. // discussion on folded headers
  158. if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
  159. // we have continuation folded header
  160. // so append value
  161. if (value != null) {
  162. value.append(' ');
  163. value.append(line.trim());
  164. }
  165. } else {
  166. // make sure we save the previous name,value pair if present
  167. if (name != null) {
  168. headers.add(new Header(name, value.toString()));
  169. }
  170. // Otherwise we should have normal HTTP header line
  171. // Parse the header name and value
  172. int colon = line.indexOf(":");
  173. if (colon < 0) {
  174. throw new ProtocolException("Unable to parse header: " + line);
  175. }
  176. name = line.substring(0, colon).trim();
  177. value = new StringBuffer(line.substring(colon + 1).trim());
  178. }
  179. }
  180. // make sure we save the last name,value pair if present
  181. if (name != null) {
  182. headers.add(new Header(name, value.toString()));
  183. }
  184. return (Header[]) headers.toArray(new Header[headers.size()]);
  185. }
  186. /**
  187. * Parses headers from the given stream. Headers with the same name are not
  188. * combined.
  189. *
  190. * @param is the stream to read headers from
  191. *
  192. * @return an array of headers in the order in which they were parsed
  193. *
  194. * @throws IOException if an IO error occurs while reading from the stream
  195. * @throws HttpException if there is an error parsing a header value
  196. *
  197. * @deprecated use #parseHeaders(InputStream, String)
  198. */
  199. public static Header[] parseHeaders(InputStream is) throws IOException, HttpException {
  200. LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
  201. return parseHeaders(is, "US-ASCII");
  202. }
  203. }