Experimental parser optimizations
This commit is contained in:
@@ -141,8 +141,8 @@ void test() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* Trigger some context functions */
|
/* Trigger some context functions */
|
||||||
app.addServerName("", {});
|
app.addServerName("servername", {});
|
||||||
app.removeServerName("");
|
app.removeServerName("servername");
|
||||||
app.missingServerName(nullptr);
|
app.missingServerName(nullptr);
|
||||||
app.getNativeHandle();
|
app.getNativeHandle();
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -44,12 +44,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
bool mightHave(std::string_view key) {
|
bool mightHave(std::string_view key) {
|
||||||
return filter.test(hash1(key)) && filter.test(hash2(key)) && (key.length() < 2 || filter.test(hash3(key)));
|
return !key.length() || (filter.test(hash1(key)) && filter.test(hash2(key)) && (key.length() < 2 || filter.test(hash3(key))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(std::string_view key) {
|
void add(std::string_view key) {
|
||||||
filter.set(hash1(key));
|
if (key.length()) {
|
||||||
filter.set(hash2(key));
|
filter.set(hash1(key));
|
||||||
|
filter.set(hash2(key));
|
||||||
|
}
|
||||||
if (key.length() >= 2) {
|
if (key.length() >= 2) {
|
||||||
filter.set(hash3(key));
|
filter.set(hash3(key));
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-36
@@ -162,26 +162,19 @@ private:
|
|||||||
return unsignedIntegerValue;
|
return unsignedIntegerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *memchr_r(const char *p, const char *end) {
|
/* Find carriage return will scan forever. But we "fence" the end margin part of the receive buffer,
|
||||||
uint64_t mask = *(uint64_t *)"\r\r\r\r\r\r\r\r";
|
* by putting a CR there in case one isn't found before it, so this optimizing assumption is fine. */
|
||||||
if (p <= end - 8) {
|
static inline void *find_cr(char *p, char */*end*/) {
|
||||||
for (; p <= end - 8; p += 8) {
|
for (uint64_t mask = 0x0d0d0d0d0d0d0d0d; true; p += 8) {
|
||||||
uint64_t val = *(uint64_t *)p ^ mask;
|
uint64_t val = *(uint64_t *)p ^ mask;
|
||||||
if ((val + 0xfefefefefefefeffull) & (~val & 0x8080808080808080ull)) {
|
if ((val + 0xfefefefefefefeffull) & (~val & 0x8080808080808080ull)) {
|
||||||
break;
|
while (*(unsigned char *)p != 0x0d) p++;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; p < end; p++) {
|
|
||||||
if (*(unsigned char *)p == '\r') {
|
|
||||||
return (void *)p;
|
return (void *)p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
/* End is only used for the proxy parser. The HTTP parser recognizes "\ra" as invalid "\r\n" scan and breaks. */
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved) {
|
static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved) {
|
||||||
char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer;
|
char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer;
|
||||||
|
|
||||||
@@ -210,29 +203,45 @@ private:
|
|||||||
* for PROXY means we can end up succeeding, yet leaving bytes in the fallback buffer
|
* for PROXY means we can end up succeeding, yet leaving bytes in the fallback buffer
|
||||||
* which is then removed, and our counters to flip due to overflow and we end up with a crash */
|
* which is then removed, and our counters to flip due to overflow and we end up with a crash */
|
||||||
|
|
||||||
for (unsigned int i = 0; i < HttpRequest::MAX_HEADERS; i++) {
|
for (unsigned int i = 0; i < HttpRequest::MAX_HEADERS - 1; i++) {
|
||||||
for (preliminaryKey = postPaddedBuffer; (*postPaddedBuffer != ':') & (*(unsigned char *)postPaddedBuffer > 32); *(postPaddedBuffer++) |= 32);
|
/* Lower case and short scan until ':', or stop at \r (from previous scan) */
|
||||||
if (*postPaddedBuffer == '\r') {
|
for (preliminaryKey = postPaddedBuffer; (*postPaddedBuffer != ':') && (*(unsigned char *)postPaddedBuffer > 32); *(postPaddedBuffer++) |= 32);
|
||||||
if ((postPaddedBuffer != end) & (postPaddedBuffer[1] == '\n') & (i > 0)) {
|
headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey));
|
||||||
headers->key = std::string_view(nullptr, 0);
|
/* Assume colon, space follows (this is fine as we have at least 2 bytes past) */
|
||||||
return (unsigned int) ((postPaddedBuffer + 2) - start);
|
if (postPaddedBuffer[0] == ':' && postPaddedBuffer[1] == ' ') {
|
||||||
} else {
|
postPaddedBuffer += 2;
|
||||||
return 0;
|
} else {
|
||||||
|
/* Trim until value starts */
|
||||||
|
for (; (*postPaddedBuffer == ':' || *(unsigned char *)postPaddedBuffer < 33) && *postPaddedBuffer != '\r'; postPaddedBuffer++);
|
||||||
|
}
|
||||||
|
preliminaryValue = postPaddedBuffer;
|
||||||
|
/* The goal of this call is to find next "\r\n", fast */
|
||||||
|
postPaddedBuffer = (char *) find_cr(postPaddedBuffer, end);
|
||||||
|
/* We fence end[0] with \r, followed by end[1] being something that is "not \n", to signify "not found".
|
||||||
|
* This way we can have this one single check to see if we found \r\n WITHIN our allowed search space. */
|
||||||
|
if (postPaddedBuffer[1] == '\n') {
|
||||||
|
/* Store this header, it is valid */
|
||||||
|
headers->value = std::string_view(preliminaryValue, (size_t) (postPaddedBuffer - preliminaryValue));
|
||||||
|
postPaddedBuffer += 2;
|
||||||
|
headers++;
|
||||||
|
|
||||||
|
/* We definitely have at least one header (or request line), so check if we are done */
|
||||||
|
if (*postPaddedBuffer == '\r') {
|
||||||
|
if (postPaddedBuffer[1] == '\n') {
|
||||||
|
/* This cann take the very last header space */
|
||||||
|
headers->key = std::string_view(nullptr, 0);
|
||||||
|
return (unsigned int) ((postPaddedBuffer + 2) - start);
|
||||||
|
} else {
|
||||||
|
/* \r\n\r plus non-\n letter is malformed request, or simply out of search space */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey));
|
/* We are either out of search space or this is a malformed request */
|
||||||
for (postPaddedBuffer++; (*postPaddedBuffer == ':' || *(unsigned char *)postPaddedBuffer < 33) && *postPaddedBuffer != '\r'; postPaddedBuffer++);
|
return 0;
|
||||||
preliminaryValue = postPaddedBuffer;
|
|
||||||
postPaddedBuffer = (char *) memchr_r(postPaddedBuffer, end);
|
|
||||||
if (postPaddedBuffer && postPaddedBuffer[1] == '\n') {
|
|
||||||
headers->value = std::string_view(preliminaryValue, (size_t) (postPaddedBuffer - preliminaryValue));
|
|
||||||
postPaddedBuffer += 2;
|
|
||||||
headers++;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* We ran out of header space, too large request */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,8 +252,10 @@ private:
|
|||||||
/* How much data we CONSUMED (to throw away) */
|
/* How much data we CONSUMED (to throw away) */
|
||||||
unsigned int consumedTotal = 0;
|
unsigned int consumedTotal = 0;
|
||||||
|
|
||||||
/* Fence one byte past end of our buffer (buffer has post padded margins) */
|
/* Fence two bytes past end of our buffer (buffer has post padded margins).
|
||||||
|
* This is to always catch scan for \r but not for \r\n. */
|
||||||
data[length] = '\r';
|
data[length] = '\r';
|
||||||
|
data[length + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */
|
||||||
|
|
||||||
for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved)); ) {
|
for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved)); ) {
|
||||||
data += consumed;
|
data += consumed;
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
#include "../src/HttpParser.h"
|
#include "../src/HttpParser.h"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
unsigned char data[] = {0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0xd, 0xa, 0x61, 0x73, 0x63, 0x69, 0x69, 0x3a, 0x20, 0x74, 0x65, 0x73, 0x74, 0xd, 0xa, 0x75, 0x74, 0x66, 0x38, 0x3a, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd, 0xa, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xd, 0xa, 0xd, 0xa, 'E'};
|
/* Parser needs at least 8 bytes post padding */
|
||||||
int size = sizeof(data) - 1;
|
unsigned char data[] = {0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0xd, 0xa, 0x61, 0x73, 0x63, 0x69, 0x69, 0x3a, 0x20, 0x74, 0x65, 0x73, 0x74, 0xd, 0xa, 0x75, 0x74, 0x66, 0x38, 0x3a, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd, 0xa, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xd, 0xa, 0xd, 0xa, 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'};
|
||||||
|
int size = sizeof(data) - 8;
|
||||||
void *user = nullptr;
|
void *user = nullptr;
|
||||||
void *reserved = nullptr;
|
void *reserved = nullptr;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user