package com.firefly.codec.http2.model; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import com.firefly.utils.collection.ArrayTrie; import com.firefly.utils.collection.Trie; public enum HttpMethod { GET, POST, HEAD, PUT, OPTIONS, DELETE, TRACE, CONNECT, MOVE, PROXY, PRI; /** * Optimized lookup to find a method name and trailing space in a byte * array. * * @param bytes * Array containing ISO-8859-1 characters * @param position * The first valid index * @param limit * The first non valid index * @return A HttpMethod if a match or null if no easy match. */ public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit) { int length = limit - position; if (length < 4) return null; switch (bytes[position]) { case 'G': if (bytes[position + 1] == 'E' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') return GET; break; case 'P': if (bytes[position + 1] == 'O' && bytes[position + 2] == 'S' && bytes[position + 3] == 'T' && length >= 5 && bytes[position + 4] == ' ') return POST; if (bytes[position + 1] == 'R' && bytes[position + 2] == 'O' && bytes[position + 3] == 'X' && length >= 6 && bytes[position + 4] == 'Y' && bytes[position + 5] == ' ') return PROXY; if (bytes[position + 1] == 'U' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') return PUT; if (bytes[position + 1] == 'R' && bytes[position + 2] == 'I' && bytes[position + 3] == ' ') return PRI; break; case 'H': if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'D' && length >= 5 && bytes[position + 4] == ' ') return HEAD; break; case 'O': if (bytes[position + 1] == 'P' && bytes[position + 2] == 'T' && bytes[position + 3] == 'I' && length >= 8 && bytes[position + 4] == 'O' && bytes[position + 5] == 'N' && bytes[position + 6] == 'S' && bytes[position + 7] == ' ') return OPTIONS; break; case 'D': if (bytes[position + 1] == 'E' && bytes[position + 2] == 'L' && bytes[position + 3] == 'E' && length >= 7 && bytes[position + 4] == 'T' && bytes[position + 5] == 'E' && bytes[position + 6] == ' ') return DELETE; break; case 'T': if (bytes[position + 1] == 'R' && bytes[position + 2] == 'A' && bytes[position + 3] == 'C' && length >= 6 && bytes[position + 4] == 'E' && bytes[position + 5] == ' ') return TRACE; break; case 'C': if (bytes[position + 1] == 'O' && bytes[position + 2] == 'N' && bytes[position + 3] == 'N' && length >= 8 && bytes[position + 4] == 'E' && bytes[position + 5] == 'C' && bytes[position + 6] == 'T' && bytes[position + 7] == ' ') return CONNECT; break; case 'M': if (bytes[position + 1] == 'O' && bytes[position + 2] == 'V' && bytes[position + 3] == 'E' && length >= 5 && bytes[position + 4] == ' ') return MOVE; break; default: break; } return null; } /** * Optimized lookup to find a method name and trailing space in a byte * array. * * @param buffer * buffer containing ISO-8859-1 characters, it is not modified. * @return A HttpMethod if a match or null if no easy match. */ public static HttpMethod lookAheadGet(ByteBuffer buffer) { if (buffer.hasArray()) return lookAheadGet(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.arrayOffset() + buffer.limit()); int l = buffer.remaining(); if (l >= 4) { HttpMethod m = CACHE.getBest(buffer, 0, l); if (m != null) { int ml = m.asString().length(); if (l > ml && buffer.get(buffer.position() + ml) == ' ') return m; } } return null; } public final static Trie<HttpMethod> CACHE = new ArrayTrie<>(); static { for (HttpMethod method : HttpMethod.values()) CACHE.put(method.toString(), method); } private final ByteBuffer buffer; private final byte[] bytes; HttpMethod() { bytes = toString().getBytes(StandardCharsets.UTF_8); buffer = ByteBuffer.wrap(bytes); } public byte[] getBytes() { return bytes; } public boolean is(String s) { return toString().equalsIgnoreCase(s); } public ByteBuffer asBuffer() { return buffer.asReadOnlyBuffer(); } public String asString() { return toString(); } /** * Converts the given String parameter to an HttpMethod * * @param method * the String to get the equivalent HttpMethod from * @return the HttpMethod or null if the parameter method is unknown */ public static HttpMethod fromString(String method) { return CACHE.get(method); } }