Experimental parser optimizations

This commit is contained in:
Alex Hultman
2022-10-03 01:16:40 +02:00
parent 09a7b2f093
commit 3a53ada17d
4 changed files with 58 additions and 44 deletions
+2 -2
View File
@@ -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();
+6 -4
View File
@@ -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));
} }
@@ -62,4 +64,4 @@ public:
} }
#endif // UWS_BLOOMFILTER_H #endif // UWS_BLOOMFILTER_H
+47 -36
View File
@@ -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;
+3 -2
View File
@@ -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;