Repository: netty/netty
File: codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java (Java)

Code before:
     * derived from HTTP, such as
     * <a href="https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
     * <a href="https://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
     *
     * @param keepAliveDefault
     *        {@code true} if and only if the connection is kept alive unless
     *        the {@code "Connection"} header is set to {@code "close"} explicitly.
     */
    public HttpVersion(String text, boolean keepAliveDefault) {
        this(text, false, keepAliveDefault);
    }

    HttpVersion(String text, boolean strict, boolean keepAliveDefault) {
        text = checkNonEmptyAfterTrim(text, "text").toUpperCase();

        if (strict) {
            // Only single digit major / minor version is allowed.
            // See
            //  - https://datatracker.ietf.org/doc/html/rfc7230#section-2.6
            //  - https://datatracker.ietf.org/doc/html/rfc9110#name-protocol-version
            if (text.length() != 8 || !text.startsWith("HTTP/") || text.charAt(6) != '.') {
                throw new IllegalArgumentException("invalid version format: " + text);
            }
            protocolName = "HTTP";
            majorVersion = Integer.parseInt(text.charAt(5) + "");
            minorVersion = Integer.parseInt(text.charAt(7) + "");
        } else {
            Matcher m = VERSION_PATTERN.matcher(text);
            if (!m.matches()) {
                throw new IllegalArgumentException("invalid version format: " + text);
            }

            protocolName = m.group(1);
            majorVersion = Integer.parseInt(m.group(2));
            minorVersion = Integer.parseInt(m.group(3));
        }

        this.text = protocolName + '/' + majorVersion + '.' + minorVersion;
        this.keepAliveDefault = keepAliveDefault;
        bytes = null;
    }

    /**
     * Creates a new HTTP version with the specified protocol name and version
     * numbers.  You will not need to create a new instance unless you are
     * implementing a protocol derived from HTTP, such as
     * <a href="https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
     * <a href="https://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>
     *
     * @param keepAliveDefault
     *        {@code true} if and only if the connection is kept alive unless

Review comment (suggestion):
Consider:
```java
    private static int toDecimal(final int value) {
        if (value < '0' || value > '9') {
            throw new IllegalCharacterException((byte) value, "0-9 (0x30-0x39)");
        }
        return value - '0';
    }
```

Code after:
     * derived from HTTP, such as
     * <a href="https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
     * <a href="https://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
     *
     * @param keepAliveDefault
     *        {@code true} if and only if the connection is kept alive unless
     *        the {@code "Connection"} header is set to {@code "close"} explicitly.
     */
    public HttpVersion(String text, boolean keepAliveDefault) {
        this(text, false, keepAliveDefault);
    }

    HttpVersion(String text, boolean strict, boolean keepAliveDefault) {
        text = checkNonEmptyAfterTrim(text, "text").toUpperCase();

        if (strict) {
            // Only single digit major / minor version is allowed.
            // See
            //  - https://datatracker.ietf.org/doc/html/rfc7230#section-2.6
            //  - https://datatracker.ietf.org/doc/html/rfc9110#name-protocol-version
            if (text.length() != 8 || !text.startsWith("HTTP/") || text.charAt(6) != '.') {
                throw new IllegalArgumentException("invalid version format: " + text);
            }
            protocolName = "HTTP";
            majorVersion = toDecimal(text.charAt(5));
            minorVersion = toDecimal(text.charAt(7));
        } else {
            Matcher m = VERSION_PATTERN.matcher(text);
            if (!m.matches()) {
                throw new IllegalArgumentException("invalid version format: " + text);
            }

            protocolName = m.group(1);
            majorVersion = Integer.parseInt(m.group(2));
            minorVersion = Integer.parseInt(m.group(3));
        }

        this.text = protocolName + '/' + majorVersion + '.' + minorVersion;
        this.keepAliveDefault = keepAliveDefault;
        bytes = null;
    }

    private static int toDecimal(final int value) {
        if (value < '0' || value > '9') {
            throw new IllegalArgumentException("Invalid version number, only 0-9 (0x30-0x39) allowed," +
                    " but received a '" + (char) value + "' (0x" + Integer.toHexString(value) + ")");
        }
        return value - '0';
    }

    /**