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 2007/10/27 19:17:09 UTC

svn commit: r589158 - in /jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src: main/java/org/apache/http/message/ test/java/org/apache/http/message/

Author: olegk
Date: Sat Oct 27 10:17:08 2007
New Revision: 589158

URL: http://svn.apache.org/viewvc?rev=589158&view=rev
Log:
HTTPCORE-126: Improved HTTP message parsing
* Added ParserCursor class representing a context of a parsing operation
* Improved NameValuePair parsing code  

Added:
    jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java   (with props)
Modified:
    jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java
    jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java
    jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/test/java/org/apache/http/message/TestBasicHeaderValueParser.java

Modified: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java?rev=589158&r1=589157&r2=589158&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java (original)
+++ jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java Sat Oct 27 10:17:08 2007
@@ -298,7 +298,6 @@
             params.toArray(new NameValuePair[params.size()]);
     }
 
-
     /**
      * Parses a name-value-pair with the given parser.
      *
@@ -322,7 +321,8 @@
 
         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
         buffer.append(value);
-        return parser.parseNameValuePair(buffer, 0, buffer.length());
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseNameValuePair(buffer, cursor, new char[] {});
     }
 
 
@@ -342,23 +342,94 @@
             throw new IllegalArgumentException
                 ("Char array buffer may not be null");
         }
-        if (indexFrom < 0) {
-            throw new IndexOutOfBoundsException();
+        ParserCursor cursor = new ParserCursor(indexFrom, indexTo);
+        cursor.updatePos(indexFrom);
+        return parseNameValuePair(buffer, cursor, new char[] {});
+    }
+
+    /**
+     * Parses a name=value specification, where the = and value are optional.
+     *
+     * @param buffer    the buffer holding the name-value pair to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     * @param delimiters array of delimiters that can optionally terminate
+     *                  the parsing operation
+     *
+     * @return  the name-value pair, where the value is <code>null</code>
+     *          if no value is specified
+     */
+    public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
+                                            final ParserCursor cursor,
+                                            final char[] delimiters) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
         }
-        if (indexTo > buffer.length()) {
-            throw new IndexOutOfBoundsException();
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
         }
-        if (indexFrom > indexTo) {
-            throw new IndexOutOfBoundsException();
+        if (delimiters == null) {
+            throw new IllegalArgumentException("Delimiters may not be null");
         }
 
-        int eq = buffer.indexOf('=', indexFrom, indexTo);
-        if (eq < 0) {
-            return createNameValuePair(buffer.substringTrimmed(indexFrom, indexTo), null);
+        boolean terminated = false;
+        
+        int pos = cursor.getPos();
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        // Find name
+        String name = null;
+        while (pos < indexTo) {
+            char ch = buffer.charAt(pos);
+            if (ch == '=') {
+                break;
+            }
+            if (isOneOf(delimiters, ch)) {
+                terminated = true;
+                break;
+            }
+            pos++;
+        }
+        
+        if (pos == indexTo) {
+            terminated = true;
+            name = buffer.substringTrimmed(indexFrom, indexTo);
+        } else {
+            name = buffer.substringTrimmed(indexFrom, pos);
+            pos++;
+        }
+        
+        if (terminated) {
+            cursor.updatePos(pos);
+            return createNameValuePair(name, null);
+        }
+
+        // Find value
+        String value = null;
+        int i1 = pos;
+        
+        boolean qouted = false;
+        boolean escaped = false;
+        while (pos < indexTo) {
+            char ch = buffer.charAt(pos);
+            if (ch == '"' && !escaped) {
+                qouted = !qouted;
+            }
+            if (!qouted && !escaped && isOneOf(delimiters, ch)) {
+                terminated = true;
+                break;
+            }
+            if (escaped) {
+                escaped = false;
+            } else {
+                escaped = qouted && ch == '\\';
+            }
+            pos++;
         }
-        String name = buffer.substringTrimmed(indexFrom, eq);
-        int i1 = eq + 1;
-        int i2 = indexTo;
+        
+        int i2 = pos;
         // Trim leading white spaces
         while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
             i1++;
@@ -374,11 +445,23 @@
             i1++;
             i2--;
         }
-        String value = buffer.substring(i1, i2);
+        value = buffer.substring(i1, i2);
+        if (terminated) {
+            pos++;
+        }
+        cursor.updatePos(pos);
         return createNameValuePair(name, value);
     }
 
-
+    private static boolean isOneOf(final char[] chs, char ch) {
+        for (int i = 0; i < chs.length; i++) {
+            if (chs[i] == ch) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
     /**
      * Creates a name-value pair.
      * Called from {@link #parseNameValuePair}.
@@ -391,7 +474,6 @@
     protected NameValuePair createNameValuePair(final String name, final String value) {
         return new BasicNameValuePair(name, value);
     }
-
 
 }
 

Modified: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java?rev=589158&r1=589157&r2=589158&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java (original)
+++ jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java Sat Oct 27 10:17:08 2007
@@ -202,17 +202,18 @@
      * Parses a name=value specification, where the = and value are optional.
      *
      * @param buffer    the buffer holding the name-value pair to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     * @param delimiters array of delimiters that can optionally terminate
+     *                  the parsing operation
      *
      * @return  the name-value pair, where the value is <code>null</code>
      *          if no value is specified
-     *
-     * @throws ParseException        in case of a parse error
      */
-    NameValuePair parseNameValuePair(CharArrayBuffer buffer,
-                                     int indexFrom,
-                                     int indexTo)
-        throws ParseException
-        ;
-
+    NameValuePair parseNameValuePair(
+            CharArrayBuffer buffer,
+            ParserCursor cursor,
+            char[] delimiters) throws ParseException;
+    
 }
 

Added: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java?rev=589158&view=auto
==============================================================================
--- jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java (added)
+++ jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java Sat Oct 27 10:17:08 2007
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+/**
+ * This class represents a context of a parsing operation: 
+ * <ul>
+ *  <li>the current position the parsing operation is expected to start at</li>
+ *  <li>the bounds limiting the scope of the parsing operation</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ */
+public class ParserCursor {
+
+    private final int lowerBound;
+    private final int upperBound;
+    private int pos;
+    
+    public ParserCursor(int lowerBound, int upperBound) {
+        super();
+        if (lowerBound < 0) {
+            throw new IndexOutOfBoundsException("Lower bound cannot be negative");
+        }
+        if (lowerBound > upperBound) {
+            throw new IndexOutOfBoundsException("Lower bound cannot be greater then upper bound");
+        }
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+        this.pos = 0;
+    }
+
+    public int getLowerBound() {
+        return this.lowerBound;
+    }
+
+    public int getUpperBound() {
+        return this.upperBound;
+    }
+
+    public int getPos() {
+        return this.pos;
+    }
+
+    public void updatePos(int pos) {
+        if (pos < this.lowerBound) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (pos > this.upperBound) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.pos = pos;
+    }
+    
+    public boolean atEnd() {
+        return this.pos == this.upperBound;
+    }
+    
+}

Propchange: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/main/java/org/apache/http/message/ParserCursor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/test/java/org/apache/http/message/TestBasicHeaderValueParser.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/test/java/org/apache/http/message/TestBasicHeaderValueParser.java?rev=589158&r1=589157&r2=589158&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/test/java/org/apache/http/message/TestBasicHeaderValueParser.java (original)
+++ jakarta/httpcomponents/httpcore/branches/parser_refactoring/module-main/src/test/java/org/apache/http/message/TestBasicHeaderValueParser.java Sat Oct 27 10:17:08 2007
@@ -196,8 +196,123 @@
         }
     }
 
+    public void testNVParseUsingCursor() {
+        
+        HeaderValueParser parser = BasicHeaderValueParser.DEFAULT;
+        char[] delimiters = new char[] {';', ','};
+        
+        String s = "test";
+        CharArrayBuffer buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        ParserCursor cursor = new ParserCursor(0, s.length());
+        
+        NameValuePair param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals(null, param.getValue());
+        assertEquals(s.length(), cursor.getPos());
+        assertTrue(cursor.atEnd());
+
+        s = "test;";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals(null, param.getValue());
+        assertEquals(s.length(), cursor.getPos());
+        assertTrue(cursor.atEnd());
+        
+        s = "test  ,12";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals(null, param.getValue());
+        assertEquals(s.length() - 2, cursor.getPos());
+        assertFalse(cursor.atEnd());
+        
+        s = "test=stuff";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals("stuff", param.getValue());
+        assertEquals(s.length(), cursor.getPos());
+        assertTrue(cursor.atEnd());
+        
+        s = "   test  =   stuff ";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals("stuff", param.getValue());
+        assertEquals(s.length(), cursor.getPos());
+        assertTrue(cursor.atEnd());
+
+        s = "   test  =   stuff ;1234";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals("stuff", param.getValue());
+        assertEquals(s.length() - 4, cursor.getPos());
+        assertFalse(cursor.atEnd());
+        
+        s = "test  = \"stuff\"";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals("stuff", param.getValue());
+        
+        s = "test  = \"  stuff\\\"\"";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals("  stuff\\\"", param.getValue());
+        
+        s = "  test";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("test", param.getName());
+        assertEquals(null, param.getValue());
+
+        s = "  ";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("", param.getName());
+        assertEquals(null, param.getValue());
+
+        s = " = stuff ";
+        buffer = new CharArrayBuffer(16);
+        buffer.append(s);
+        cursor = new ParserCursor(0, s.length());
+        
+        param = parser.parseNameValuePair(buffer, cursor, delimiters);
+        assertEquals("", param.getName());
+        assertEquals("stuff", param.getValue());
+    }
 
-    
     public void testNVParse() {
         String s = "test";
         NameValuePair param =