Skip to content

Commit

Permalink
updates from review
Browse files Browse the repository at this point in the history
Added a fallback int lookup
  • Loading branch information
gregw committed Mar 4, 2024
1 parent 346fdfe commit 741cced
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,34 +163,34 @@ public String toString()
public static HttpMethod lookAheadGet(ByteBuffer buffer)
{
int len = buffer.remaining();
// Short cut for 3 char methods, mostly for GET optimisation
if (len > 3)
{
switch (buffer.getInt(buffer.position()))
{
case ACL_AS_INT:
return ACL;
case GET_AS_INT:
return GET;
case PRI_AS_INT:
return PRI;
case PUT_AS_INT:
return PUT;
case POST_AS_INT:
if (len > 4 && buffer.get(buffer.position() + 4) == ' ')
return POST;
break;
case HEAD_AS_INT:
if (len > 4 && buffer.get(buffer.position() + 4) == ' ')
return HEAD;
break;
default:
break;
}
}
// Shortcut for 3 or 4 char methods, mostly for GET optimisation
if (len > 4)
return lookAheadGet(buffer, buffer.getInt(buffer.position()));
return LOOK_AHEAD.getBest(buffer, 0, len);
}

/**
* 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, which must have at least 5 bytes remaining
* @param lookAhead The integer representation of the first 4 bytes of the buffer that has previously been fetched
* with the equivalent of {@code buffer.getInt(buffer.position())}
* @return An HttpMethod if a match or null if no easy match.
*/
public static HttpMethod lookAheadGet(ByteBuffer buffer, int lookAhead)
{
return switch (lookAhead)
{
case ACL_AS_INT -> ACL;
case GET_AS_INT -> GET;
case PRI_AS_INT -> PRI;
case PUT_AS_INT -> PUT;
case POST_AS_INT -> (buffer.get(buffer.position() + 4) == ' ') ? POST : LOOK_AHEAD.getBest(buffer, 0, buffer.remaining());
case HEAD_AS_INT -> (buffer.get(buffer.position() + 4) == ' ') ? HEAD : LOOK_AHEAD.getBest(buffer, 0, buffer.remaining());
default -> LOOK_AHEAD.getBest(buffer, 0, buffer.remaining());
};
}

/**
* Converts the given String parameter to an HttpMethod.
* The string may differ from the Enum name as a '-' in the method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,13 @@ public class HttpParser
.maxCapacity(0)
.build();

private static final int BYTES_IN_INT = 4;
private static final int BYTES_IN_LONG = 8;
private static final long HTTP_1_0_AS_LONG = stringAsLong("HTTP/1.0");
private static final long HTTP_1_1_AS_LONG = stringAsLong("HTTP/1.1");
private static final long GET_SLASH_HT_AS_LONG = stringAsLong("GET / HT");
private static final long TP_SLASH_1_0_CRLF = stringAsLong("TP/1.0\r\n");
private static final long TP_SLASH_1_1_CRLF = stringAsLong("TP/1.1\r\n");
private static final long TP_SLASH_1_0_CRLF_AS_LONG = stringAsLong("TP/1.0\r\n");
private static final long TP_SLASH_1_1_CRLF_AS_LONG = stringAsLong("TP/1.1\r\n");
private static final long SPACE_200_OK_CR_AS_LONG = stringAsLong(" 200 OK\r");

private static long stringAsLong(String s)
Expand Down Expand Up @@ -527,34 +528,47 @@ private HttpTokens.Token next(ByteBuffer buffer)
private void quickStart(ByteBuffer buffer)
{
int position = buffer.position();
int remaining = buffer.remaining();
if (_requestHandler != null)
{
// Try to match "GET / HTTP/1.x\r\n" with two longs
if (buffer.remaining() >= 16 && buffer.getLong(position) == GET_SLASH_HT_AS_LONG)
if (remaining >= 2 * BYTES_IN_LONG)
{
long v = buffer.getLong(position + 8);
if (v == TP_SLASH_1_1_CRLF)
long lookahead = buffer.getLong(position);
if (lookahead == GET_SLASH_HT_AS_LONG)
{
buffer.position(position + 16);
_methodString = HttpMethod.GET.asString();
_version = HttpVersion.HTTP_1_1;
setState(State.HEADER);
_requestHandler.startRequest(_methodString, "/", _version);
return;
long v = buffer.getLong(position + BYTES_IN_LONG);
if (v == TP_SLASH_1_1_CRLF_AS_LONG)
{
buffer.position(position + 2 * BYTES_IN_LONG);
_methodString = HttpMethod.GET.asString();
_version = HttpVersion.HTTP_1_1;
setState(State.HEADER);
_requestHandler.startRequest(_methodString, "/", _version);
return;
}
if (v == TP_SLASH_1_0_CRLF_AS_LONG)
{
buffer.position(position + 2 * BYTES_IN_LONG);
_methodString = HttpMethod.GET.asString();
_version = HttpVersion.HTTP_1_0;
setState(State.HEADER);
_requestHandler.startRequest(_methodString, "/", _version);
return;
}
}
if (v == TP_SLASH_1_0_CRLF)
else
{
buffer.position(position + 16);
_methodString = HttpMethod.GET.asString();
_version = HttpVersion.HTTP_1_0;
setState(State.HEADER);
_requestHandler.startRequest(_methodString, "/", _version);
return;
// lookup the method using the first 4 bytes of the already fetched long as a lookahead.
_method = HttpMethod.lookAheadGet(buffer, (int)((lookahead >> 32)));
}
}
else
{
// otherwise try a lookahead to match the method
_method = HttpMethod.lookAheadGet(buffer);
}

// otherwise just try to match the method
_method = HttpMethod.lookAheadGet(buffer);
if (_method != null)
{
_methodString = _method.asString();
Expand All @@ -566,7 +580,7 @@ private void quickStart(ByteBuffer buffer)
return;
}
}
else if (_responseHandler != null && buffer.remaining() > BYTES_IN_LONG)
else if (_responseHandler != null && remaining > BYTES_IN_LONG)
{
// Match version as a long
long v = buffer.getLong(position);
Expand All @@ -580,8 +594,8 @@ else if (v == HTTP_1_0_AS_LONG)
position += BYTES_IN_LONG;

// Try to make 200 OK as a long
if (buffer.remaining() > 16 &&
buffer.get(position + 8) == '\n' &&
if (remaining > 2 * BYTES_IN_LONG &&
buffer.get(position + BYTES_IN_LONG) == '\n' &&
buffer.getLong(position) == SPACE_200_OK_CR_AS_LONG)
{
buffer.position(position + 9);
Expand Down

0 comments on commit 741cced

Please sign in to comment.