From 5bb917daf1bf06bcdb8012f1044c29be714a7657 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Fri, 19 Jan 2024 09:45:38 +0100 Subject: [PATCH] Improved protocol version parsing --- .../apache/hc/core5/http/ProtocolVersion.java | 25 ++++ .../hc/core5/http/ProtocolVersionParser.java | 127 ++++++++++++++++++ .../core5/http/message/BasicLineParser.java | 26 +--- .../org/apache/hc/core5/http/ssl/TLS.java | 33 ++++- .../hc/core5/http/ssl/TlsVersionParser.java | 102 -------------- .../hc/core5/http/TestProtocolVersion.java | 31 +++++ .../core5/http/TestProtocolVersionParser.java | 117 ++++++++++++++++ .../http/message/TestBasicLineParser.java | 28 ++-- .../org/apache/hc/core5/http/ssl/TLSTest.java | 32 +++++ .../core5/http/ssl/TestTlsVersionParser.java | 99 -------------- 10 files changed, 378 insertions(+), 242 deletions(-) create mode 100644 httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersionParser.java delete mode 100644 httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsVersionParser.java create mode 100644 httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersionParser.java delete mode 100644 httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsVersionParser.java diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersion.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersion.java index 572bbc5d91..b6247e9535 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersion.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersion.java @@ -30,8 +30,10 @@ import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; +import org.apache.hc.core5.util.Tokenizer; /** * Represents a protocol version. The "major.minor" numbering @@ -239,6 +241,29 @@ public final boolean lessEquals(final ProtocolVersion version) { return isComparable(version) && (compareToVersion(version) <= 0); } + @Internal + public static ProtocolVersion parse( + final CharSequence buffer, + final Tokenizer.Cursor cursor, + final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + return ProtocolVersionParser.INSTANCE.parse(buffer, cursor, delimiterPredicate); + } + + /** + * @since 5.3 + */ + public static ProtocolVersion parse(final String s) throws ParseException { + if (s == null) { + return null; + } + final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); + final ProtocolVersion protocolVersion = ProtocolVersionParser.INSTANCE.parse(s, cursor, null); + Tokenizer.INSTANCE.skipWhiteSpace(s, cursor); + if (!cursor.atEnd()) { + throw new ParseException("Invalid protocol version; trailing content"); + } + return protocolVersion; + } /** * Converts this protocol version to a string. diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersionParser.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersionParser.java new file mode 100644 index 0000000000..e574403c82 --- /dev/null +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersionParser.java @@ -0,0 +1,127 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.hc.core5.http; + +import org.apache.hc.core5.annotation.Internal; +import org.apache.hc.core5.util.TextUtils; +import org.apache.hc.core5.util.Tokenizer; + +@Internal +public class ProtocolVersionParser { + + public final static ProtocolVersionParser INSTANCE = new ProtocolVersionParser(); + + private static final char SLASH = '/'; + private static final char FULL_STOP = '.'; + private static final Tokenizer.Delimiter PROTO_DELIMITER = Tokenizer.delimiters(SLASH); + private static final Tokenizer.Delimiter FULL_STOP_OR_BLANK = Tokenizer.delimiters(FULL_STOP, ' ', '\t'); + private static final Tokenizer.Delimiter BLANK = Tokenizer.delimiters(' ', '\t'); + private final Tokenizer tokenizer; + + public ProtocolVersionParser() { + this.tokenizer = Tokenizer.INSTANCE; + } + + @Internal + @FunctionalInterface + public interface Factory { + + ProtocolVersion create(int major, int minor); + + } + + public ProtocolVersion parse( + final String protocol, + final Factory factory, + final CharSequence buffer, + final Tokenizer.Cursor cursor, + final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + final int lowerBound = cursor.getLowerBound(); + final int upperBound = cursor.getUpperBound(); + final String token1 = tokenizer.parseToken(buffer, cursor, + delimiterPredicate != null ? (ch) -> delimiterPredicate.test(ch) || FULL_STOP_OR_BLANK.test(ch) : FULL_STOP_OR_BLANK); + final int major; + try { + major = Integer.parseInt(token1); + } catch (final NumberFormatException e) { + throw new ParseException("Invalid " + protocol + " major version number", + buffer, lowerBound, upperBound, cursor.getPos()); + } + if (cursor.atEnd()) { + return factory != null ? factory.create(major, major) : new ProtocolVersion(protocol, major, 0); + } + if (buffer.charAt(cursor.getPos()) != FULL_STOP) { + return factory != null ? factory.create(major, major) : new ProtocolVersion(protocol, major, 0); + } else { + cursor.updatePos(cursor.getPos() + 1); + final String token2 = tokenizer.parseToken(buffer, cursor, + delimiterPredicate != null ? (ch) -> delimiterPredicate.test(ch) || BLANK.test(ch) : BLANK); + final int minor; + try { + minor = Integer.parseInt(token2); + } catch (final NumberFormatException e) { + throw new ParseException("Invalid " + protocol + " minor version number", + buffer, lowerBound, upperBound, cursor.getPos()); + } + return factory != null ? factory.create(major, minor) : new ProtocolVersion(protocol, major, minor); + } + } + + public ProtocolVersion parse( + final String protocol, + final CharSequence buffer, + final Tokenizer.Cursor cursor, + final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + return parse(protocol, null, buffer, cursor, delimiterPredicate); + } + + public ProtocolVersion parse( + final String protocol, + final CharSequence buffer, + final Tokenizer.Cursor cursor) throws ParseException { + return parse(protocol, null, buffer, cursor, null); + } + + public ProtocolVersion parse( + final CharSequence buffer, + final Tokenizer.Cursor cursor, + final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + tokenizer.skipWhiteSpace(buffer, cursor); + final String proto = tokenizer.parseToken(buffer, cursor, PROTO_DELIMITER); + if (TextUtils.isBlank(proto)) { + throw new ParseException("Invalid protocol name"); + } + if (!cursor.atEnd() && buffer.charAt(cursor.getPos()) == SLASH) { + cursor.updatePos(cursor.getPos() + 1); + return parse(proto, null, buffer, cursor, delimiterPredicate); + } else { + throw new ParseException("Invalid protocol name"); + } + } + +} diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineParser.java b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineParser.java index f73579efc6..003fd218c1 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineParser.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineParser.java @@ -33,6 +33,7 @@ import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; +import org.apache.hc.core5.http.ProtocolVersionParser; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; @@ -48,7 +49,6 @@ public class BasicLineParser implements LineParser { public final static BasicLineParser INSTANCE = new BasicLineParser(); - private static final Tokenizer.Delimiter FULL_STOP = Tokenizer.delimiters('.'); private static final Tokenizer.Delimiter BLANKS = Tokenizer.delimiters(' ', '\t'); private static final Tokenizer.Delimiter COLON = Tokenizer.delimiters(':'); @@ -108,29 +108,7 @@ ProtocolVersion parseProtocolVersion( } cursor.updatePos(pos + protolength + 1); - - final String token1 = this.tokenizer.parseToken(buffer, cursor, FULL_STOP); - final int major; - try { - major = Integer.parseInt(token1); - } catch (final NumberFormatException e) { - throw new ParseException("Invalid protocol major version number", - buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); - } - if (cursor.atEnd()) { - throw new ParseException("Invalid protocol version", - buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); - } - cursor.updatePos(cursor.getPos() + 1); - final String token2 = this.tokenizer.parseToken(buffer, cursor, BLANKS); - final int minor; - try { - minor = Integer.parseInt(token2); - } catch (final NumberFormatException e) { - throw new ParseException("Invalid protocol minor version number", - buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); - } - return HttpVersion.get(major, minor); + return ProtocolVersionParser.INSTANCE.parse(protoname, HttpVersion::get, buffer, cursor, null); } /** diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TLS.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TLS.java index d1884f146b..9a8021d9a9 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TLS.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TLS.java @@ -30,8 +30,10 @@ import java.util.ArrayList; import java.util.List; +import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; +import org.apache.hc.core5.http.ProtocolVersionParser; import org.apache.hc.core5.util.Tokenizer; /** @@ -90,12 +92,41 @@ public boolean lessEquals(final ProtocolVersion protocolVersion) { return version.lessEquals(protocolVersion); } + @Internal + public static ProtocolVersion parse( + final CharSequence buffer, + final Tokenizer.Cursor cursor, + final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + final int lowerBound = cursor.getLowerBound(); + final int upperBound = cursor.getUpperBound(); + + int pos = cursor.getPos(); + if (pos + 4 > cursor.getUpperBound()) { + throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); + } + if (buffer.charAt(pos) != 'T' || buffer.charAt(pos + 1) != 'L' || buffer.charAt(pos + 2) != 'S' + || buffer.charAt(pos + 3) != 'v') { + throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); + } + pos = pos + 4; + cursor.updatePos(pos); + if (cursor.atEnd()) { + throw new ParseException("Invalid TLS version", buffer, lowerBound, upperBound, pos); + } + return ProtocolVersionParser.INSTANCE.parse("TLS", null, buffer, cursor, delimiterPredicate); + } + public static ProtocolVersion parse(final String s) throws ParseException { if (s == null) { return null; } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); - return TlsVersionParser.INSTANCE.parse(s, cursor, null); + final ProtocolVersion protocolVersion = parse(s, cursor, null); + Tokenizer.INSTANCE.skipWhiteSpace(s, cursor); + if (!cursor.atEnd()) { + throw new ParseException("Invalid TLS protocol version; trailing content"); + } + return protocolVersion; } public static String[] excludeWeak(final String... protocols) { diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsVersionParser.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsVersionParser.java deleted file mode 100644 index 296e5f8145..0000000000 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsVersionParser.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.hc.core5.http.ssl; - -import org.apache.hc.core5.http.ParseException; -import org.apache.hc.core5.http.ProtocolVersion; -import org.apache.hc.core5.util.Tokenizer; - -final class TlsVersionParser { - - public final static TlsVersionParser INSTANCE = new TlsVersionParser(); - - private final Tokenizer tokenizer; - - TlsVersionParser() { - this.tokenizer = Tokenizer.INSTANCE; - } - - ProtocolVersion parse( - final CharSequence buffer, - final Tokenizer.Cursor cursor, - final Tokenizer.Delimiter delimiterPredicate) throws ParseException { - final int lowerBound = cursor.getLowerBound(); - final int upperBound = cursor.getUpperBound(); - - int pos = cursor.getPos(); - if (pos + 4 > cursor.getUpperBound()) { - throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); - } - if (buffer.charAt(pos) != 'T' || buffer.charAt(pos + 1) != 'L' || buffer.charAt(pos + 2) != 'S' - || buffer.charAt(pos + 3) != 'v') { - throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); - } - pos = pos + 4; - cursor.updatePos(pos); - if (cursor.atEnd()) { - throw new ParseException("Invalid TLS version", buffer, lowerBound, upperBound, pos); - } - final String s = this.tokenizer.parseToken(buffer, cursor, delimiterPredicate); - final int idx = s.indexOf('.'); - if (idx == -1) { - final int major; - try { - major = Integer.parseInt(s); - } catch (final NumberFormatException e) { - throw new ParseException("Invalid TLS major version", buffer, lowerBound, upperBound, pos); - } - return new ProtocolVersion("TLS", major, 0); - } else { - final String s1 = s.substring(0, idx); - final int major; - try { - major = Integer.parseInt(s1); - } catch (final NumberFormatException e) { - throw new ParseException("Invalid TLS major version", buffer, lowerBound, upperBound, pos); - } - final String s2 = s.substring(idx + 1); - final int minor; - try { - minor = Integer.parseInt(s2); - } catch (final NumberFormatException e) { - throw new ParseException("Invalid TLS minor version", buffer, lowerBound, upperBound, pos); - } - return new ProtocolVersion("TLS", major, minor); - } - } - - ProtocolVersion parse(final String s) throws ParseException { - if (s == null) { - return null; - } - final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); - return parse(s, cursor, null); - } - -} - diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersion.java b/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersion.java index 827e0a336c..1701ccc35f 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersion.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersion.java @@ -27,6 +27,10 @@ package org.apache.hc.core5.http; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.apache.hc.core5.util.Tokenizer; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -45,4 +49,31 @@ public void testEqualsMajorMinor() { Assertions.assertFalse(PROTOCOL_VERSION_1_2.equals(2, 0)); } + @Test + public void testParseBasic() throws Exception { + assertThat(ProtocolVersion.parse("PROTO/1"), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 1, 0))); + assertThat(ProtocolVersion.parse("PROTO/1.1"), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 1, 1))); + assertThat(ProtocolVersion.parse("PROTO/1.2"), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 1, 2))); + assertThat(ProtocolVersion.parse("PROTO/1.3 "), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 1, 3))); + assertThat(ProtocolVersion.parse("PROTO/000.0000 "), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 0, 0))); + assertThat(ProtocolVersion.parse("PROTO/22.356"), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 22, 356))); + } + + @Test + public void testParseBuffer() throws Exception { + final Tokenizer.Cursor cursor = new Tokenizer.Cursor(1, 13); + assertThat(ProtocolVersion.parse(" PROTO/1.2,0000", cursor, Tokenizer.delimiters(',')), CoreMatchers.equalTo(new ProtocolVersion("PROTO", 1, 2))); + assertThat(cursor.getPos(), CoreMatchers.equalTo(10)); + } + + @Test + public void testParseFailure() throws Exception { + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse("/1")); + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse(" /1")); + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse("PROTO/")); + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse("PROTO/1A")); + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse("PROTO/1.A")); + Assertions.assertThrows(ParseException.class, () -> ProtocolVersion.parse("PROTO/1.1 huh?")); + } + } diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersionParser.java b/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersionParser.java new file mode 100644 index 0000000000..0cbddcfa52 --- /dev/null +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersionParser.java @@ -0,0 +1,117 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.hc.core5.http; + +import static org.hamcrest.MatcherAssert.assertThat; + +import org.apache.hc.core5.http.message.ParserCursor; +import org.apache.hc.core5.util.Tokenizer; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link ProtocolVersionParser}. + */ +public class TestProtocolVersionParser { + + private ProtocolVersionParser impl; + + @BeforeEach + public void setup() { + impl = new ProtocolVersionParser(); + } + + public ProtocolVersion parseStr(final String protocol, final String s) throws ParseException { + return impl.parse(protocol, null, s, new ParserCursor(0, s.length()), null); + } + + public ProtocolVersion parseStr(final String protocol, final String s, final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + return impl.parse(protocol, null, s, new ParserCursor(0, s.length()), delimiterPredicate); + } + + public ProtocolVersion parseStr(final String protocol, final String s, final Tokenizer.Cursor cursor, final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + return impl.parse(protocol, null, s, cursor, delimiterPredicate); + } + + public ProtocolVersion parseStr(final String s) throws ParseException { + return impl.parse(s, new ParserCursor(0, s.length()), null); + } + + public ProtocolVersion parseStr(final String s, final Tokenizer.Delimiter delimiterPredicate) throws ParseException { + return impl.parse(s, new ParserCursor(0, s.length()), delimiterPredicate); + } + + @Test + public void testParseVersion() throws Exception { + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 0), parseStr("PROTO", "1 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 1), parseStr("PROTO", "1.1 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 20), parseStr("PROTO", "1.020 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 22, 356), parseStr("PROTO", "22.356 ")); + + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 0), parseStr("PROTO", "1, ", Tokenizer.delimiters(','))); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 1), parseStr("PROTO", "1.1; ", Tokenizer.delimiters(',', ';'))); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 200), parseStr("PROTO", "1.200 blah; ", Tokenizer.delimiters(','))); + } + + @Test + public void testParseVersionWithCursor() throws Exception { + final Tokenizer.Cursor cursor = new Tokenizer.Cursor(2, 13); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 20), parseStr("PROTO", " 1.20,0000,00000", cursor, Tokenizer.delimiters(','))); + assertThat(cursor.getPos(), CoreMatchers.equalTo(6)); + } + + @Test + public void testParseProtocolVersion() throws Exception { + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 0), parseStr("PROTO/1 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 1), parseStr("PROTO/1.1 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 20), parseStr("PROTO/1.020 ")); + Assertions.assertEquals(new ProtocolVersion("PROTO", 22, 356), parseStr("PROTO/22.356 ")); + + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 0), parseStr("PROTO/1, ", Tokenizer.delimiters(','))); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 1), parseStr("PROTO/1.1; ", Tokenizer.delimiters(',', ';'))); + Assertions.assertEquals(new ProtocolVersion("PROTO", 1, 200), parseStr("PROTO/1.200 blah; ", Tokenizer.delimiters(','))); + } + + @Test + public void testParseFailures() throws Exception { + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO", "blah")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO", "1.blah")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO", "1A.0")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO", "")); + Assertions.assertThrows(ParseException.class, () -> parseStr("")); + Assertions.assertThrows(ParseException.class, () -> parseStr(" ")); + Assertions.assertThrows(ParseException.class, () -> parseStr(" /1.0")); + Assertions.assertThrows(ParseException.class, () -> parseStr(" / ")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO/")); + Assertions.assertThrows(ParseException.class, () -> parseStr("PROTO/ 1.0")); + } + +} diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineParser.java b/httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineParser.java index afc431708f..5037bda344 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineParser.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineParser.java @@ -31,6 +31,7 @@ import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -198,9 +199,9 @@ public void testSLParseFailure() throws Exception { public void testHttpVersionParsing() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("HTTP/1.1"); - ParserCursor cursor = new ParserCursor(0, buffer.length()); + final ParserCursor cursor = new ParserCursor(0, buffer.length()); - HttpVersion version = (HttpVersion) parser.parseProtocolVersion(buffer, cursor); + final ProtocolVersion version = parser.parseProtocolVersion(buffer, cursor); Assertions.assertEquals("HTTP", version.getProtocol(), "HTTP protocol name"); Assertions.assertEquals(1, version.getMajor(), "HTTP major version number"); Assertions.assertEquals(1, version.getMinor(), "HTTP minor version number"); @@ -210,16 +211,16 @@ public void testHttpVersionParsing() throws Exception { buffer.clear(); buffer.append("HTTP/1.123 123"); - cursor = new ParserCursor(0, buffer.length()); + final ParserCursor cursor2 = new ParserCursor(0, buffer.length()); - version = (HttpVersion) parser.parseProtocolVersion(buffer, cursor); - Assertions.assertEquals( "HTTP", version.getProtocol(), "HTTP protocol name"); - Assertions.assertEquals( 1, version.getMajor(), "HTTP major version number"); - Assertions.assertEquals(123, version.getMinor(), "HTTP minor version number"); - Assertions.assertEquals("HTTP/1.123", version.toString(), "HTTP version number"); - Assertions.assertEquals(' ', buffer.charAt(cursor.getPos())); - Assertions.assertEquals(buffer.length() - 4, cursor.getPos()); - Assertions.assertFalse(cursor.atEnd()); + final ProtocolVersion version2 = parser.parseProtocolVersion(buffer, cursor2); + Assertions.assertEquals( "HTTP", version2.getProtocol(), "HTTP protocol name"); + Assertions.assertEquals( 1, version2.getMajor(), "HTTP major version number"); + Assertions.assertEquals(123, version2.getMinor(), "HTTP minor version number"); + Assertions.assertEquals("HTTP/1.123", version2.toString(), "HTTP version number"); + Assertions.assertEquals(' ', buffer.charAt(cursor2.getPos())); + Assertions.assertEquals(buffer.length() - 4, cursor2.getPos()); + Assertions.assertFalse(cursor2.atEnd()); } @Test @@ -251,11 +252,6 @@ public void testInvalidHttpVersionParsing() throws Exception { Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor5)); buffer.clear(); - buffer.append("HTTP/1234"); - final ParserCursor cursor6 = new ParserCursor(0, buffer.length()); - Assertions.assertThrows(ParseException.class, () -> - parser.parseProtocolVersion(buffer, cursor6)); - buffer.clear(); buffer.append("HTTP/1."); final ParserCursor cursor7 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TLSTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TLSTest.java index e9a2730a46..23eb55801e 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TLSTest.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TLSTest.java @@ -27,10 +27,14 @@ package org.apache.hc.core5.http.ssl; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.ProtocolVersion; +import org.apache.hc.core5.util.Tokenizer; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -86,4 +90,32 @@ void excludeWeak() { Assertions.assertTrue(TLS.isSecure(protocol)); } } + + @Test + public void testParseBasic() throws Exception { + assertThat(TLS.parse("TLSv1"), CoreMatchers.equalTo(TLS.V_1_0.getVersion())); + assertThat(TLS.parse("TLSv1.1"), CoreMatchers.equalTo(TLS.V_1_1.getVersion())); + assertThat(TLS.parse("TLSv1.2"), CoreMatchers.equalTo(TLS.V_1_2.getVersion())); + assertThat(TLS.parse("TLSv1.3 "), CoreMatchers.equalTo(TLS.V_1_3.getVersion())); + assertThat(TLS.parse("TLSv22.356"), CoreMatchers.equalTo(new ProtocolVersion("TLS", 22, 356))); + } + + @Test + public void testParseBuffer() throws Exception { + final Tokenizer.Cursor cursor = new Tokenizer.Cursor(1, 13); + assertThat(TLS.parse(" TLSv1.2,0000", cursor, Tokenizer.delimiters(',')), + CoreMatchers.equalTo(TLS.V_1_2.getVersion())); + assertThat(cursor.getPos(), CoreMatchers.equalTo(8)); + } + + @Test + public void testParseFailure() throws Exception { + Assertions.assertThrows(ParseException.class, () -> TLS.parse("Tlsv1")); + Assertions.assertThrows(ParseException.class, () -> TLS.parse("TLSV1")); + Assertions.assertThrows(ParseException.class, () -> TLS.parse("TLSv")); + Assertions.assertThrows(ParseException.class, () -> TLS.parse("TLSv1A")); + Assertions.assertThrows(ParseException.class, () -> TLS.parse("TLSv1.A")); + Assertions.assertThrows(ParseException.class, () -> TLS.parse("TLSv1.1 huh?")); + } + } diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsVersionParser.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsVersionParser.java deleted file mode 100644 index df9bf543e9..0000000000 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsVersionParser.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.hc.core5.http.ssl; - -import static org.hamcrest.MatcherAssert.assertThat; - -import org.apache.hc.core5.http.ParseException; -import org.apache.hc.core5.http.ProtocolVersion; -import org.apache.hc.core5.util.Tokenizer; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link TlsVersionParser}. - */ -public class TestTlsVersionParser { - - private TlsVersionParser impl; - - @BeforeEach - public void setup() { - impl = new TlsVersionParser(); - } - - @Test - public void testParseBasic() throws Exception { - assertThat(impl.parse("TLSv1"), CoreMatchers.equalTo(TLS.V_1_0.getVersion())); - assertThat(impl.parse("TLSv1.1"), CoreMatchers.equalTo(TLS.V_1_1.getVersion())); - assertThat(impl.parse("TLSv1.2"), CoreMatchers.equalTo(TLS.V_1_2.getVersion())); - assertThat(impl.parse("TLSv1.3"), CoreMatchers.equalTo(TLS.V_1_3.getVersion())); - assertThat(impl.parse("TLSv22.356"), CoreMatchers.equalTo(new ProtocolVersion("TLS", 22, 356))); - } - - @Test - public void testParseBuffer() throws Exception { - final Tokenizer.Cursor cursor = new Tokenizer.Cursor(1, 13); - assertThat(impl.parse(" TLSv1.2,0000", cursor, Tokenizer.delimiters(',')), - CoreMatchers.equalTo(TLS.V_1_2.getVersion())); - assertThat(cursor.getPos(), CoreMatchers.equalTo(8)); - } - - @Test - public void testParseFailure1() throws Exception { - Assertions.assertThrows(ParseException.class, () -> - impl.parse("Tlsv1")); - } - - @Test - public void testParseFailure2() throws Exception { - Assertions.assertThrows(ParseException.class, () -> - impl.parse("TLSV1")); - } - - @Test - public void testParseFailure3() throws Exception { - Assertions.assertThrows(ParseException.class, () -> - impl.parse("TLSv")); - } - - @Test - public void testParseFailure4() throws Exception { - Assertions.assertThrows(ParseException.class, () -> - impl.parse("TLSv1A")); - } - - @Test - public void testParseFailure5() throws Exception { - Assertions.assertThrows(ParseException.class, () -> - impl.parse("TLSv1.A")); - } - -}