You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2008/02/21 11:46:43 UTC
svn commit: r629740 - in /geronimo/sandbox/AsyncHttpClient/src:
main/java/org/apache/ahc/codec/HttpDecoder.java
main/java/org/apache/ahc/codec/HttpResponseDecoder.java
test/java/org/apache/ahc/ResponseHeaderParsingTest.java
Author: rickmcguire
Date: Thu Feb 21 02:46:41 2008
New Revision: 629740
URL: http://svn.apache.org/viewvc?rev=629740&view=rev
Log:
GERONIMO-3860 HttpResponseDecoder does not handle folded headers properly
Patch provided by Sangjin Lee
Added:
geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java (with props)
Modified:
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseDecoder.java
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java?rev=629740&r1=629739&r2=629740&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java Thu Feb 21 02:46:41 2008
@@ -137,6 +137,75 @@
}
/**
+ * Finds a header line from a ByteBuffer that ends with a CR/LF and returns
+ * the line as a String. Header lines are different from other lines in one
+ * important aspect; a single header can span multiple lines. Such folding
+ * occurs if a CR/LF is followed by a LWSP characters (a space or a
+ * horizontal tab). This method returns complete lines that correspond to
+ * a single header if the header is folded.
+ *
+ * @param in ByteBuffer containing data
+ *
+ * @return a <code>String</code> representing the decoded line
+ *
+ * @throws Exception for any Exception that is encountered
+ */
+ public String decodeHeaderLine(ByteBuffer in) throws Exception {
+ int beginPos = in.position();
+ int limit = in.limit();
+ boolean lastIsCR = false;
+ boolean lastIsCRLF = false;
+ int terminatorPos = -1;
+
+ for (int i = beginPos; i < limit; i++) {
+ byte b = in.get(i);
+ if (!lastIsCR && b == CR) {
+ lastIsCR = true;
+ } else if (lastIsCR && !lastIsCRLF && b == LF) {
+ // still need to peek one more byte... unless we begin with
+ // CRLF in which case we have encountered the end of the header
+ // section
+ if (i != beginPos + 1) {
+ lastIsCRLF = true;
+ } else {
+ // it is an empty line
+ terminatorPos = i;
+ break;
+ }
+ } else if (lastIsCRLF) {
+ // we have read until CRLF: see if there is continuation
+ if (isLWSPChar((char)b)) {
+ // folded header; reset the flags and continue
+ lastIsCR = false;
+ lastIsCRLF = false;
+ } else {
+ // we have reached the end of the line; move back by one and
+ // break
+ terminatorPos = i - 1;
+ break;
+ }
+ }
+ }
+
+ // Check if we don't have enough data to process or found a full
+ // readable line
+ if (terminatorPos == -1) {
+ return null;
+ }
+
+ String result = null;
+ if (terminatorPos > 1) {
+ ByteBuffer line = in.slice();
+ line.limit(terminatorPos - beginPos - 1);
+ result = line.getString(decoder);
+ }
+
+ in.position(terminatorPos + 1);
+
+ return result;
+ }
+
+ /**
* Decodes the status code and message from a HTTP response and places the values in a
* {@link HttpResponseMessage} object.
*
@@ -167,6 +236,8 @@
* @throws Exception if any exception occurs
*/
public void decodeHeader(String line, HttpResponseMessage msg) throws Exception {
+ // first, get rid of the CRLF from linear whitespace
+ line = line.replaceAll("\\r\\n([ \\t])", "$1");
int pos = line.indexOf(":");
String name = line.substring(0, pos);
String value = trimHeaderValue(line.substring(pos + 1));
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseDecoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseDecoder.java?rev=629740&r1=629739&r2=629740&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseDecoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseDecoder.java Thu Feb 21 02:46:41 2008
@@ -197,7 +197,7 @@
private boolean findHeaders(HttpResponseMessage response, ByteBuffer in) throws Exception {
//Read the headers and process them
while (true) {
- String line = httpDecoder.decodeLine(in);
+ String line = httpDecoder.decodeHeaderLine(in);
//Check if the entire response has been read
if (line == null) {
Added: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java?rev=629740&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java Thu Feb 21 02:46:41 2008
@@ -0,0 +1,47 @@
+package org.apache.ahc;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.apache.ahc.codec.HttpResponseDecoder;
+import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+
+public class ResponseHeaderParsingTest extends TestCase {
+ private static final String TEST_RESPONSE =
+ "HTTP/1.1 200 OK\r\n"
+ + "Date: Fri, 15 Feb 2008 20:00:00 GMT\r\n"
+ + "Server:foo-bar\r\n"
+ + "Content-Type: text/html\r\n"
+ + "Content-Length: 13\r\n"
+ + "Connection: close \r\n"
+ + "Private-Header: test-continue\r\n"
+ + "\tit-keeps-going\r\n"
+ + " and-going-and-going\r\n"
+ + "\r\n"
+ + "<html></html>";
+
+ public void testParsing() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(TEST_RESPONSE.length());
+ buffer.put(TEST_RESPONSE.getBytes());
+ buffer.flip();
+
+ IoSession session = new FakeIoSession();
+// session.setAttribute(HttpIoHandler.CURRENT_REQUEST, new HttpRequestMessage(null, null));
+ HttpResponseDecoder decoder = new HttpResponseDecoder();
+ FakeProtocolDecoderOutput out = new FakeProtocolDecoderOutput();
+ decoder.decode(session, buffer, out);
+
+ HttpResponseMessage response = (HttpResponseMessage)out.getObject();
+ assertEquals("Fri, 15 Feb 2008 20:00:00 GMT", response.getHeader("Date"));
+ assertEquals("foo-bar", response.getHeader("Server"));
+ assertEquals("text/html", response.getHeader("Content-Type"));
+ assertEquals("13", response.getHeader("Content-Length"));
+ assertEquals("close", response.getHeader("Connection"));
+ assertEquals("test-continue\tit-keeps-going and-going-and-going", response.getHeader("Private-Header"));
+
+ assertTrue(Arrays.equals(response.getContent(), "<html></html>".getBytes()));
+ }
+}
Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ResponseHeaderParsingTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain