Experimental parser optimizations
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user