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 */
app.addServerName("", {});
app.removeServerName("");
app.addServerName("servername", {});
app.removeServerName("servername");
app.missingServerName(nullptr);
app.getNativeHandle();
+6 -4
View File
@@ -44,12 +44,14 @@ private:
public:
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) {
filter.set(hash1(key));
filter.set(hash2(key));
if (key.length()) {
filter.set(hash1(key));
filter.set(hash2(key));
}
if (key.length() >= 2) {
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;
}
static void *memchr_r(const char *p, const char *end) {
uint64_t mask = *(uint64_t *)"\r\r\r\r\r\r\r\r";
if (p <= end - 8) {
for (; p <= end - 8; p += 8) {
uint64_t val = *(uint64_t *)p ^ mask;
if ((val + 0xfefefefefefefeffull) & (~val & 0x8080808080808080ull)) {
break;
}
}
}
for (; p < end; p++) {
if (*(unsigned char *)p == '\r') {
/* Find carriage return will scan forever. But we "fence" the end margin part of the receive buffer,
* by putting a CR there in case one isn't found before it, so this optimizing assumption is fine. */
static inline void *find_cr(char *p, char */*end*/) {
for (uint64_t mask = 0x0d0d0d0d0d0d0d0d; true; p += 8) {
uint64_t val = *(uint64_t *)p ^ mask;
if ((val + 0xfefefefefefefeffull) & (~val & 0x8080808080808080ull)) {
while (*(unsigned char *)p != 0x0d) 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) {
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
* 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 (preliminaryKey = postPaddedBuffer; (*postPaddedBuffer != ':') & (*(unsigned char *)postPaddedBuffer > 32); *(postPaddedBuffer++) |= 32);
if (*postPaddedBuffer == '\r') {
if ((postPaddedBuffer != end) & (postPaddedBuffer[1] == '\n') & (i > 0)) {
headers->key = std::string_view(nullptr, 0);
return (unsigned int) ((postPaddedBuffer + 2) - start);
} else {
return 0;
for (unsigned int i = 0; i < HttpRequest::MAX_HEADERS - 1; i++) {
/* Lower case and short scan until ':', or stop at \r (from previous scan) */
for (preliminaryKey = postPaddedBuffer; (*postPaddedBuffer != ':') && (*(unsigned char *)postPaddedBuffer > 32); *(postPaddedBuffer++) |= 32);
headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey));
/* Assume colon, space follows (this is fine as we have at least 2 bytes past) */
if (postPaddedBuffer[0] == ':' && postPaddedBuffer[1] == ' ') {
postPaddedBuffer += 2;
} 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 {
headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey));
for (postPaddedBuffer++; (*postPaddedBuffer == ':' || *(unsigned char *)postPaddedBuffer < 33) && *postPaddedBuffer != '\r'; postPaddedBuffer++);
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 are either out of search space or this is a malformed request */
return 0;
}
}
/* We ran out of header space, too large request */
return 0;
}
@@ -243,8 +252,10 @@ private:
/* How much data we CONSUMED (to throw away) */
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 + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */
for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved)); ) {
data += consumed;
+3 -2
View File
@@ -4,8 +4,9 @@
#include "../src/HttpParser.h"
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'};
int size = sizeof(data) - 1;
/* Parser needs at least 8 bytes post padding */
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 *reserved = nullptr;