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 =