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

Code before:
                sb[start + 2] << 16 |
                sb[start + 3] << 24;
        return maybePost == POST_AS_INT;
    }

    private static int firstInvalidMethodOctet(final byte[] sb, int start, int length) {
        int end = start + length;
        for (int i = start; i < end; i++) {
            if (sb[i] < 0 || !VALID_HTTP_METHOD_OCTETS.get(sb[i])) {
                return i;
            }
        }
        return -1;
    }

    private static final BitSet VALID_HTTP_METHOD_OCTETS = validHttpMethodOctets();

    private static BitSet validHttpMethodOctets() {
        BitSet bits = new BitSet();
        // https://datatracker.ietf.org/doc/html/rfc9110#name-tokens
        // token          = 1*tchar
        // tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
        //                 / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
        //                 / DIGIT / ALPHA
        //                 ; any VCHAR, except delimiters
        //Many HTTP field values are defined using common syntax components,
        // separated by whitespace or specific delimiting characters. Delimiters
        // are chosen from the set of US-ASCII visual characters not allowed in
        // a token (DQUOTE and "(),/:;<=>?@[\]{}").
        //VCHAR          =  %x21-7E
        bits.set(0x21, 0x7f);
        int[] delimiters = { '(', ')', ',', '/', ':', ';', '<', '=', '>', '?', '@', '"', '[', '\\', ']', '{', '}' };
        for (int delimiter : delimiters) {
            bits.set(delimiter, false);
        }
        return bits;
    }

    @Override
    protected String splitFirstWordInitialLine(final byte[] sb, final int start, final int length) {
        int firstInvalidOctet = firstInvalidMethodOctet(sb, start, length);
        if (firstInvalidOctet != -1) {
            throw new IllegalArgumentException(
                    "HTTP Method contains an invalid character: " + sb[firstInvalidOctet]);
        }
        if (length == 3) {
            if (isGetMethod(sb, start)) {
                return HttpMethod.GET.name();
            }
        } else if (length == 4) {
            if (isPostMethod(sb, start)) {

Review comment (suggestion):
```suggestion
        // Many HTTP field values are defined using common syntax components,
```

Code after:
        return maybePost == POST_AS_INT;
    }

    @Override
    protected String splitFirstWordInitialLine(final byte[] sb, final int start, final int length) {
        if (length == 3) {
            if (isGetMethod(sb, start)) {
                return HttpMethod.GET.name();
            }
        } else if (length == 4) {
            if (isPostMethod(sb, start)) {
                return HttpMethod.POST.name();
            }
        }
        return super.splitFirstWordInitialLine(sb, start, length);
    }

    @Override
    protected String splitThirdWordInitialLine(final byte[] sb, final int start, final int length) {
        if (length == 8) {
            final long maybeHttp1_x = sb[start] |
                    sb[start + 1] << 8 |
                    sb[start + 2] << 16 |
                    sb[start + 3] << 24 |
                    (long) sb[start + 4] << 32 |
                    (long) sb[start + 5] << 40 |
                    (long) sb[start + 6] << 48 |
                    (long) sb[start + 7] << 56;
            if (maybeHttp1_x == HTTP_1_1_AS_LONG) {
                return HttpVersion.HTTP_1_1_STRING;
            } else if (maybeHttp1_x == HTTP_1_0_AS_LONG) {
                return HttpVersion.HTTP_1_0_STRING;
            }
        }
        return super.splitThirdWordInitialLine(sb, start, length);
    }

    @Override
    protected HttpMessage createInvalidMessage() {
        return new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request",
                Unpooled.buffer(0), headersFactory, trailersFactory);
    }

    @Override
    protected boolean isDecodingRequest() {
        return true;
    }

    @Override
    protected boolean isContentAlwaysEmpty(final HttpMessage msg) {
        // fast-path to save expensive O(n) checks; users can override createMessage