You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2015/06/03 20:21:33 UTC

svn commit: r1683405 - in /httpcomponents/httpcore/trunk: httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/ httpcore-nio/src/main/java/org/apache/http/nio/reactor/ httpcore-nio/src/...

Author: olegk
Date: Wed Jun  3 18:21:33 2015
New Revision: 1683405

URL: http://svn.apache.org/r1683405
Log:
RFC 7230: permit some empty lines before message head

Modified:
    httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/AbstractMessageParser.java
    httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpRequestParser.java
    httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpResponseParser.java
    httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionInputBufferImpl.java
    httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/reactor/SessionInputBuffer.java
    httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestHttpMessageParser.java
    httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/MessageConstraintException.java
    httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/config/MessageConstraints.java
    httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java
    httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpRequestParser.java
    httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpResponseParser.java
    httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestRequestParser.java
    httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestResponseParser.java

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/AbstractMessageParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/AbstractMessageParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/AbstractMessageParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/AbstractMessageParser.java Wed Jun  3 18:21:33 2015
@@ -34,8 +34,6 @@ import java.util.List;
 import org.apache.http.HttpException;
 import org.apache.http.HttpMessage;
 import org.apache.http.MessageConstraintException;
-import org.apache.http.ParseException;
-import org.apache.http.ProtocolException;
 import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.config.MessageConstraints;
 import org.apache.http.message.LazyLineParser;
@@ -63,24 +61,25 @@ public abstract class AbstractMessagePar
     private T message;
     private CharArrayBuffer lineBuf;
     private final List<CharArrayBuffer> headerBufs;
+    private int emptyLineCount;
 
     private final LineParser lineParser;
-    private final MessageConstraints constraints;
+    private final MessageConstraints messageConstraints;
 
     /**
      * Creates an instance of AbstractMessageParser.
      *
      * @param lineParser the line parser. If {@code null}
      *   {@link org.apache.http.message.LazyLineParser#INSTANCE} will be used.
-     * @param constraints Message constraints. If {@code null}
+     * @param messageConstraints Message constraints. If {@code null}
      *   {@link MessageConstraints#DEFAULT} will be used.
      *
      * @since 4.3
      */
-    public AbstractMessageParser(final LineParser lineParser, final MessageConstraints constraints) {
+    public AbstractMessageParser(final LineParser lineParser, final MessageConstraints messageConstraints) {
         super();
         this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE;
-        this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
+        this.messageConstraints = messageConstraints != null ? messageConstraints : MessageConstraints.DEFAULT;
         this.headerBufs = new ArrayList<>();
         this.state = READ_HEAD_LINE;
     }
@@ -93,6 +92,7 @@ public abstract class AbstractMessagePar
     public void reset() {
         this.state = READ_HEAD_LINE;
         this.headerBufs.clear();
+        this.emptyLineCount = 0;
         this.message = null;
     }
 
@@ -103,13 +103,19 @@ public abstract class AbstractMessagePar
      * @param buffer the line buffer.
      * @return HTTP message.
      * @throws HttpException in case of HTTP protocol violation
-     * @throws ParseException in case of a parse error.
      */
-    protected abstract T createMessage(CharArrayBuffer buffer)
-        throws HttpException, ParseException;
+    protected abstract T createMessage(CharArrayBuffer buffer) throws HttpException;
 
-    private void parseHeadLine() throws HttpException, ParseException {
-        this.message = createMessage(this.lineBuf);
+    private T parseHeadLine() throws IOException, HttpException {
+        if (this.lineBuf.length() == 0) {
+            this.emptyLineCount++;
+            if (this.emptyLineCount >= this.messageConstraints.getMaxEmptyLineCount()) {
+                throw new MessageConstraintException("Maximum empty line limit exceeded");
+            }
+            return null;
+        } else {
+            return createMessage(this.lineBuf);
+        }
     }
 
     private void parseHeader() throws IOException {
@@ -126,7 +132,7 @@ public abstract class AbstractMessagePar
                 }
                 i++;
             }
-            final int maxLineLen = this.constraints.getMaxLineLength();
+            final int maxLineLen = this.messageConstraints.getMaxLineLength();
             if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) {
                 throw new MessageConstraintException("Maximum line length limit exceeded");
             }
@@ -149,7 +155,7 @@ public abstract class AbstractMessagePar
                 this.lineBuf.clear();
             }
             final boolean lineComplete = sessionBuffer.readLine(this.lineBuf, endOfStream);
-            final int maxLineLen = this.constraints.getMaxLineLength();
+            final int maxLineLen = this.messageConstraints.getMaxLineLength();
             if (maxLineLen > 0 &&
                     (this.lineBuf.length() > maxLineLen ||
                             (!lineComplete && sessionBuffer.length() > maxLineLen))) {
@@ -161,16 +167,14 @@ public abstract class AbstractMessagePar
 
             switch (this.state) {
             case READ_HEAD_LINE:
-                try {
-                    parseHeadLine();
-                } catch (final ParseException px) {
-                    throw new ProtocolException(px.getMessage(), px);
+                this.message = parseHeadLine();
+                if (this.message != null) {
+                    this.state = READ_HEADERS;
                 }
-                this.state = READ_HEADERS;
                 break;
             case READ_HEADERS:
                 if (this.lineBuf.length() > 0) {
-                    final int maxHeaderCount = this.constraints.getMaxHeaderCount();
+                    final int maxHeaderCount = this.messageConstraints.getMaxHeaderCount();
                     if (maxHeaderCount > 0 && headerBufs.size() >= maxHeaderCount) {
                         throw new MessageConstraintException("Maximum header count exceeded");
                     }
@@ -187,11 +191,7 @@ public abstract class AbstractMessagePar
         }
         if (this.state == COMPLETED) {
             for (final CharArrayBuffer buffer : this.headerBufs) {
-                try {
-                    this.message.addHeader(lineParser.parseHeader(buffer));
-                } catch (final ParseException ex) {
-                    throw new ProtocolException(ex.getMessage(), ex);
-                }
+                this.message.addHeader(this.lineParser.parseHeader(buffer));
             }
             return this.message;
         } else {

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpRequestParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpRequestParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpRequestParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpRequestParser.java Wed Jun  3 18:21:33 2015
@@ -31,7 +31,6 @@ import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestFactory;
 import org.apache.http.HttpVersion;
-import org.apache.http.ParseException;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.RequestLine;
 import org.apache.http.UnsupportedHttpVersionException;
@@ -87,8 +86,7 @@ public class DefaultHttpRequestParser ex
     }
 
     @Override
-    protected HttpRequest createMessage(final CharArrayBuffer buffer)
-            throws HttpException, ParseException {
+    protected HttpRequest createMessage(final CharArrayBuffer buffer) throws HttpException {
         final RequestLine requestLine = getLineParser().parseRequestLine(buffer);
         final ProtocolVersion version = requestLine.getProtocolVersion();
         if (version.greaterEquals(HttpVersion.HTTP_2)) {

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpResponseParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpResponseParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpResponseParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/DefaultHttpResponseParser.java Wed Jun  3 18:21:33 2015
@@ -31,7 +31,6 @@ import org.apache.http.HttpException;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponseFactory;
 import org.apache.http.HttpVersion;
-import org.apache.http.ParseException;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.StatusLine;
 import org.apache.http.UnsupportedHttpVersionException;
@@ -88,8 +87,7 @@ public class DefaultHttpResponseParser e
     }
 
     @Override
-    protected HttpResponse createMessage(final CharArrayBuffer buffer)
-            throws HttpException, ParseException {
+    protected HttpResponse createMessage(final CharArrayBuffer buffer) throws HttpException {
         final StatusLine statusLine = getLineParser().parseStatusLine(buffer);
         final ProtocolVersion version = statusLine.getProtocolVersion();
         if (version.greaterEquals(HttpVersion.HTTP_2)) {

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionInputBufferImpl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionInputBufferImpl.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionInputBufferImpl.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionInputBufferImpl.java Wed Jun  3 18:21:33 2015
@@ -32,7 +32,6 @@ import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
-import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CoderResult;
@@ -226,7 +225,7 @@ public class SessionInputBufferImpl exte
     @Override
     public boolean readLine(
             final CharArrayBuffer linebuffer,
-            final boolean endOfStream) throws CharacterCodingException {
+            final boolean endOfStream) throws IOException {
 
         setOutputMode();
         // See if there is LF char present in the buffer

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/reactor/SessionInputBuffer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/reactor/SessionInputBuffer.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/reactor/SessionInputBuffer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/reactor/SessionInputBuffer.java Wed Jun  3 18:21:33 2015
@@ -31,7 +31,6 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
-import java.nio.charset.CharacterCodingException;
 
 import org.apache.http.util.CharArrayBuffer;
 
@@ -162,10 +161,10 @@ public interface SessionInputBuffer {
      *  line has been transferred to the destination buffer, {@code false}
      *  otherwise.
      *
-     * @throws CharacterCodingException in case a character encoding or decoding
+     * @throws java.nio.charset.CharacterCodingException in case a character encoding or decoding
      *   error occurs.
+     * @throws org.apache.http.MessageConstraintException in case a message constraint violation.
      */
-    boolean readLine(CharArrayBuffer dst, boolean endOfStream)
-        throws CharacterCodingException;
+    boolean readLine(CharArrayBuffer dst, boolean endOfStream) throws IOException;
 
 }

Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestHttpMessageParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestHttpMessageParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestHttpMessageParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestHttpMessageParser.java Wed Jun  3 18:21:33 2015
@@ -39,6 +39,7 @@ import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpVersion;
+import org.apache.http.MessageConstraintException;
 import org.apache.http.UnsupportedHttpVersionException;
 import org.apache.http.config.MessageConstraints;
 import org.apache.http.impl.nio.reactor.SessionInputBufferImpl;
@@ -307,6 +308,30 @@ public class TestHttpMessageParser {
         requestParser.parse(inbuf, false);
     }
 
+    @Test
+    public void testParsingEmptyLines() throws Exception {
+        final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
+        final MessageConstraints constraints = MessageConstraints.custom()
+                .setMaxEmptyLineCount(3).build();
+        final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(constraints);
+        inbuf.fill(newChannel("\r\n\r\nGET /whatever HTTP/1.1\r\nSome header: stuff\r\n\r\n"));
+        final HttpRequest request = requestParser.parse(inbuf, false);
+        Assert.assertNotNull(request);
+        Assert.assertEquals("/whatever", request.getRequestLine().getUri());
+        Assert.assertEquals(1, request.getAllHeaders().length);
+    }
+
+    @Test(expected = MessageConstraintException.class)
+    public void testParsingTooManyEmptyLines() throws Exception {
+        final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
+
+        final MessageConstraints constraints = MessageConstraints.custom()
+                .setMaxEmptyLineCount(3).build();
+        final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(constraints);
+        inbuf.fill(newChannel("\r\n\r\n\r\nGET /whatever HTTP/1.0\r\nHeader: one\r\nHeader: two\r\n\r\n"));
+        requestParser.parse(inbuf, false);
+    }
+
     @Test(expected = UnsupportedHttpVersionException.class)
     public void testParsingUnsupportedRequestVersion() throws Exception {
         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);

Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/MessageConstraintException.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/MessageConstraintException.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/MessageConstraintException.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/MessageConstraintException.java Wed Jun  3 18:21:33 2015
@@ -27,20 +27,21 @@
 
 package org.apache.http;
 
-import java.nio.charset.CharacterCodingException;
+import java.io.IOException;
 
 /**
  * Signals a message constraint violation.
  *
  * @since 4.3
  */
-public class MessageConstraintException extends CharacterCodingException {
+public class MessageConstraintException extends IOException {
 
     private static final long serialVersionUID = 6077207720446368695L;
 
     private final String message;
+
     /**
-     * Creates a TruncatedChunkException with the specified detail message.
+     * Creates a MessageConstraintException with the specified detail message.
      *
      * @param message The exception detail message
      */

Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/config/MessageConstraints.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/config/MessageConstraints.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/config/MessageConstraints.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/config/MessageConstraints.java Wed Jun  3 18:21:33 2015
@@ -45,11 +45,13 @@ public class MessageConstraints {
 
     private final int maxLineLength;
     private final int maxHeaderCount;
+    private final int maxEmptyLineCount;
 
-    MessageConstraints(final int maxLineLength, final int maxHeaderCount) {
+    MessageConstraints(final int maxLineLength, final int maxHeaderCount, final int maxEmptyLineCount) {
         super();
         this.maxLineLength = maxLineLength;
         this.maxHeaderCount = maxHeaderCount;
+        this.maxEmptyLineCount = maxEmptyLineCount;
     }
 
     public int getMaxLineLength() {
@@ -60,6 +62,13 @@ public class MessageConstraints {
         return maxHeaderCount;
     }
 
+    /**
+     * @since 5.0
+     */
+    public int getMaxEmptyLineCount() {
+        return this.maxEmptyLineCount;
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
@@ -69,10 +78,6 @@ public class MessageConstraints {
         return builder.toString();
     }
 
-    public static MessageConstraints lineLen(final int max) {
-        return new MessageConstraints(Args.notNegative(max, "Max line length"), -1);
-    }
-
     public static MessageConstraints.Builder custom() {
         return new Builder();
     }
@@ -88,10 +93,12 @@ public class MessageConstraints {
 
         private int maxLineLength;
         private int maxHeaderCount;
+        private int maxEmptyLineCount;
 
         Builder() {
             this.maxLineLength = -1;
             this.maxHeaderCount = -1;
+            this.maxEmptyLineCount = 10;
         }
 
         public Builder setMaxLineLength(final int maxLineLength) {
@@ -104,10 +111,19 @@ public class MessageConstraints {
             return this;
         }
 
+        public Builder setMaxEmptyLineCount(final int maxEmptyLineCount) {
+            this.maxEmptyLineCount = maxEmptyLineCount;
+            return this;
+        }
+
         public MessageConstraints build() {
-            return new MessageConstraints(maxLineLength, maxHeaderCount);
+            return new MessageConstraints(maxLineLength, maxHeaderCount, maxEmptyLineCount);
         }
 
     }
 
+    public static MessageConstraints lineLen(final int max) {
+        return custom().setMaxLineLength(Args.notNegative(max, "Max line length")).build();
+    }
+
 }

Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java Wed Jun  3 18:21:33 2015
@@ -35,8 +35,6 @@ import org.apache.http.Header;
 import org.apache.http.HttpException;
 import org.apache.http.HttpMessage;
 import org.apache.http.MessageConstraintException;
-import org.apache.http.ParseException;
-import org.apache.http.ProtocolException;
 import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.config.MessageConstraints;
 import org.apache.http.io.HttpMessageParser;
@@ -60,6 +58,7 @@ public abstract class AbstractMessagePar
 
     private final MessageConstraints messageConstraints;
     private final List<CharArrayBuffer> headerLines;
+    private final CharArrayBuffer headLine;
     private final LineParser lineParser;
 
     private int state;
@@ -80,6 +79,7 @@ public abstract class AbstractMessagePar
         this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE;
         this.messageConstraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
         this.headerLines = new ArrayList<>();
+        this.headLine = new CharArrayBuffer(128);
         this.state = HEAD_LINE;
     }
 
@@ -195,11 +195,7 @@ public abstract class AbstractMessagePar
         final Header[] headers = new Header[headerLines.size()];
         for (int i = 0; i < headerLines.size(); i++) {
             final CharArrayBuffer buffer = headerLines.get(i);
-            try {
-                headers[i] = parser.parseHeader(buffer);
-            } catch (final ParseException ex) {
-                throw new ProtocolException(ex.getMessage());
-            }
+            headers[i] = parser.parseHeader(buffer);
         }
         return headers;
     }
@@ -216,10 +212,18 @@ public abstract class AbstractMessagePar
      * @return HTTP message based on the input from the session buffer.
      * @throws IOException in case of an I/O error.
      * @throws HttpException in case of HTTP protocol violation.
-     * @throws ParseException in case of a parse error.
+     *
+     * @since 5.0
      */
-    protected abstract T parseHead(SessionInputBuffer buffer)
-        throws IOException, HttpException, ParseException;
+    protected abstract T createMessage(CharArrayBuffer buffer) throws IOException, HttpException;
+
+    /**
+     * Subclasses must override this method to generate an appropriate exception
+     * in case of unexpected connection termination by the peer endpoint.
+     *
+     * @since 5.0
+     */
+    protected abstract IOException createConnectionClosedException();
 
     @Override
     public T parse(final SessionInputBuffer buffer) throws IOException, HttpException {
@@ -227,10 +231,19 @@ public abstract class AbstractMessagePar
         final int st = this.state;
         switch (st) {
         case HEAD_LINE:
-            try {
-                this.message = parseHead(buffer);
-            } catch (final ParseException px) {
-                throw new ProtocolException(px.getMessage(), px);
+            for (int n = 0; n < this.messageConstraints.getMaxEmptyLineCount(); n++) {
+                this.headLine.clear();
+                final int i = buffer.readLine(this.headLine);
+                if (i == -1) {
+                    throw createConnectionClosedException();
+                }
+                if (this.headLine.length() > 0) {
+                    this.message = createMessage(this.headLine);
+                    break;
+                }
+            }
+            if (this.message == null) {
+                throw new MessageConstraintException("Maximum empty line limit exceeded");
             }
             this.state = HEADERS;
             //$FALL-THROUGH$

Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpRequestParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpRequestParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpRequestParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpRequestParser.java Wed Jun  3 18:21:33 2015
@@ -34,14 +34,12 @@ import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestFactory;
 import org.apache.http.HttpVersion;
-import org.apache.http.ParseException;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.RequestLine;
 import org.apache.http.UnsupportedHttpVersionException;
 import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.config.MessageConstraints;
 import org.apache.http.impl.DefaultHttpRequestFactory;
-import org.apache.http.io.SessionInputBuffer;
 import org.apache.http.message.LineParser;
 import org.apache.http.util.CharArrayBuffer;
 
@@ -55,7 +53,6 @@ import org.apache.http.util.CharArrayBuf
 public class DefaultHttpRequestParser extends AbstractMessageParser<HttpRequest> {
 
     private final HttpRequestFactory requestFactory;
-    private final CharArrayBuffer lineBuf;
 
     /**
      * Creates new instance of DefaultHttpRequestParser.
@@ -74,9 +71,7 @@ public class DefaultHttpRequestParser ex
             final HttpRequestFactory requestFactory,
             final MessageConstraints constraints) {
         super(lineParser, constraints);
-        this.requestFactory = requestFactory != null ? requestFactory :
-            DefaultHttpRequestFactory.INSTANCE;
-        this.lineBuf = new CharArrayBuffer(128);
+        this.requestFactory = requestFactory != null ? requestFactory : DefaultHttpRequestFactory.INSTANCE;
     }
 
     /**
@@ -94,15 +89,13 @@ public class DefaultHttpRequestParser ex
     }
 
     @Override
-    protected HttpRequest parseHead(final SessionInputBuffer sessionBuffer)
-        throws IOException, HttpException, ParseException {
+    protected IOException createConnectionClosedException() {
+        return new ConnectionClosedException("Client closed connection");
+    }
 
-        this.lineBuf.clear();
-        final int i = sessionBuffer.readLine(this.lineBuf);
-        if (i == -1) {
-            throw new ConnectionClosedException("Client closed connection");
-        }
-        final RequestLine requestLine = getLineParser().parseRequestLine(this.lineBuf);
+    @Override
+    protected HttpRequest createMessage(final CharArrayBuffer buffer) throws IOException, HttpException {
+        final RequestLine requestLine = getLineParser().parseRequestLine(buffer);
         final ProtocolVersion version = requestLine.getProtocolVersion();
         if (version.greaterEquals(HttpVersion.HTTP_2)) {
             throw new UnsupportedHttpVersionException("Unsupported version: " + version);

Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpResponseParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpResponseParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpResponseParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/DefaultHttpResponseParser.java Wed Jun  3 18:21:33 2015
@@ -34,14 +34,12 @@ import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponseFactory;
 import org.apache.http.HttpVersion;
 import org.apache.http.NoHttpResponseException;
-import org.apache.http.ParseException;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.StatusLine;
 import org.apache.http.UnsupportedHttpVersionException;
 import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.config.MessageConstraints;
 import org.apache.http.impl.DefaultHttpResponseFactory;
-import org.apache.http.io.SessionInputBuffer;
 import org.apache.http.message.LineParser;
 import org.apache.http.util.CharArrayBuffer;
 
@@ -55,7 +53,6 @@ import org.apache.http.util.CharArrayBuf
 public class DefaultHttpResponseParser extends AbstractMessageParser<HttpResponse> {
 
     private final HttpResponseFactory responseFactory;
-    private final CharArrayBuffer lineBuf;
 
     /**
      * Creates new instance of DefaultHttpResponseParser.
@@ -75,7 +72,6 @@ public class DefaultHttpResponseParser e
             final MessageConstraints constraints) {
         super(lineParser, constraints);
         this.responseFactory = responseFactory != null ? responseFactory : DefaultHttpResponseFactory.INSTANCE;
-        this.lineBuf = new CharArrayBuffer(128);
     }
 
     /**
@@ -93,16 +89,13 @@ public class DefaultHttpResponseParser e
     }
 
     @Override
-    protected HttpResponse parseHead(
-            final SessionInputBuffer sessionBuffer) throws IOException, HttpException, ParseException {
+    protected IOException createConnectionClosedException() {
+        return new NoHttpResponseException("The target server failed to respond");
+    }
 
-        this.lineBuf.clear();
-        final int i = sessionBuffer.readLine(this.lineBuf);
-        if (i == -1) {
-            throw new NoHttpResponseException("The target server failed to respond");
-        }
-        //create the status line from the status string
-        final StatusLine statusline = getLineParser().parseStatusLine(this.lineBuf);
+    @Override
+    protected HttpResponse createMessage(final CharArrayBuffer buffer) throws IOException, HttpException {
+        final StatusLine statusline = getLineParser().parseStatusLine(buffer);
         final ProtocolVersion version = statusline.getProtocolVersion();
         if (version.greaterEquals(HttpVersion.HTTP_2)) {
             throw new UnsupportedHttpVersionException("Unsupported version: " + version);

Modified: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestRequestParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestRequestParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestRequestParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestRequestParser.java Wed Jun  3 18:21:33 2015
@@ -33,8 +33,10 @@ import org.apache.http.Consts;
 import org.apache.http.Header;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpVersion;
+import org.apache.http.MessageConstraintException;
 import org.apache.http.RequestLine;
 import org.apache.http.UnsupportedHttpVersionException;
+import org.apache.http.config.MessageConstraints;
 import org.apache.http.impl.SessionInputBufferMock;
 import org.apache.http.io.SessionInputBuffer;
 import org.junit.Assert;
@@ -75,6 +77,45 @@ public class TestRequestParser {
         parser.parse(inbuffer);
     }
 
+    @Test
+    public void testBasicMessageParsingLeadingEmptyLines() throws Exception {
+        final String s =
+                "\r\n" +
+                "\r\n" +
+                "GET / HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "\r\n";
+        final SessionInputBuffer inbuffer = new SessionInputBufferMock(s, Consts.ASCII);
+
+        final DefaultHttpRequestParser parser = new DefaultHttpRequestParser(
+                MessageConstraints.custom().setMaxEmptyLineCount(3).build());
+        final HttpRequest httprequest = parser.parse(inbuffer);
+
+        final RequestLine reqline = httprequest.getRequestLine();
+        Assert.assertNotNull(reqline);
+        Assert.assertEquals("GET", reqline.getMethod());
+        Assert.assertEquals("/", reqline.getUri());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, reqline.getProtocolVersion());
+        final Header[] headers = httprequest.getAllHeaders();
+        Assert.assertEquals(1, headers.length);
+    }
+
+    @Test(expected = MessageConstraintException.class)
+    public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception {
+        final String s =
+                "\r\n" +
+                "\r\n" +
+                "\r\n" +
+                "GET / HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "\r\n";
+        final SessionInputBuffer inbuffer = new SessionInputBufferMock(s, Consts.ASCII);
+
+        final DefaultHttpRequestParser parser = new DefaultHttpRequestParser(
+                MessageConstraints.custom().setMaxEmptyLineCount(3).build());
+        parser.parse(inbuffer);
+    }
+
     @Test
     public void testMessageParsingTimeout() throws Exception {
         final String s =

Modified: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestResponseParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestResponseParser.java?rev=1683405&r1=1683404&r2=1683405&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestResponseParser.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestResponseParser.java Wed Jun  3 18:21:33 2015
@@ -33,9 +33,11 @@ import org.apache.http.Consts;
 import org.apache.http.Header;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpVersion;
+import org.apache.http.MessageConstraintException;
 import org.apache.http.NoHttpResponseException;
 import org.apache.http.StatusLine;
 import org.apache.http.UnsupportedHttpVersionException;
+import org.apache.http.config.MessageConstraints;
 import org.apache.http.impl.SessionInputBufferMock;
 import org.apache.http.io.SessionInputBuffer;
 import org.junit.Assert;
@@ -76,6 +78,45 @@ public class TestResponseParser {
         parser.parse(inbuffer);
     }
 
+    @Test
+    public void testBasicMessageParsingLeadingEmptyLines() throws Exception {
+        final String s =
+                "\r\n" +
+                "\r\n" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Server: whatever\r\n" +
+                "\r\n";
+        final SessionInputBuffer inbuffer = new SessionInputBufferMock(s, Consts.ASCII);
+
+        final DefaultHttpResponseParser parser = new DefaultHttpResponseParser(
+                MessageConstraints.custom().setMaxEmptyLineCount(3).build());
+        final HttpResponse httpresponse = parser.parse(inbuffer);
+
+        final StatusLine statusline = httpresponse.getStatusLine();
+        Assert.assertNotNull(statusline);
+        Assert.assertEquals(200, statusline.getStatusCode());
+        Assert.assertEquals("OK", statusline.getReasonPhrase());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, statusline.getProtocolVersion());
+        final Header[] headers = httpresponse.getAllHeaders();
+        Assert.assertEquals(1, headers.length);
+    }
+
+    @Test(expected = MessageConstraintException.class)
+    public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception {
+        final String s =
+                "\r\n" +
+                "\r\n" +
+                "\r\n" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Server: whatever\r\n" +
+                "\r\n";
+        final SessionInputBuffer inbuffer = new SessionInputBufferMock(s, Consts.ASCII);
+
+        final DefaultHttpResponseParser parser = new DefaultHttpResponseParser(
+                MessageConstraints.custom().setMaxEmptyLineCount(3).build());
+        parser.parse(inbuffer);
+    }
+
     @Test
     public void testMessageParsingTimeout() throws Exception {
         final String s =