Store headers in bloom filter, respond to 100-continue

This commit is contained in:
Alex Hultman
2020-04-06 02:48:11 +02:00
parent 19db2093e5
commit c7ac8c7e56
4 changed files with 94 additions and 6 deletions
+65
View File
@@ -0,0 +1,65 @@
/*
* Authored by Alex Hultman, 2018-2019.
* Intellectual property of third-party.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UWS_BLOOMFILTER_H
#define UWS_BLOOMFILTER_H
/* This filter has a decently low amount of false positives for the
* standard and non-standard common request headers */
#include <string_view>
#include <bitset>
namespace uWS {
struct BloomFilter {
private:
std::bitset<512> filter;
unsigned int hash1(std::string_view key) {
return ((unsigned int)key[key.length() - 1] - (key.length() << 3)) & 511;
}
unsigned int hash2(std::string_view key) {
return (((unsigned int)key[0] + (key.length() << 4)) & 511);
}
unsigned int hash3(std::string_view key) {
return ((unsigned int)key[key.length() - 2] - 97 - (key.length() << 5)) & 511;
}
public:
bool mightHave(std::string_view key) {
return 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() >= 2) {
filter.set(hash3(key));
}
}
void reset() {
filter.reset();
}
};
}
#endif // UWS_BLOOMFILTER_H
+7
View File
@@ -363,6 +363,13 @@ public:
auto user = r->getUserData();
user.httpRequest->setYield(false);
user.httpRequest->setParameters(r->getParameters());
/* Middleware? Automatically respond to expectations */
std::string_view expect = user.httpRequest->getHeader("expect");
if (expect.length() && expect == "100-continue") {
user.httpResponse->writeContinue();
}
handler(user.httpResponse, user.httpRequest);
/* If any handler yielded, the router will keep looking for a suitable handler. */
+16 -6
View File
@@ -27,6 +27,8 @@
#include <algorithm>
#include "f2/function2.hpp"
#include "BloomFilter.h"
namespace uWS {
/* We require at least this much post padding */
@@ -43,7 +45,7 @@ private:
} headers[MAX_HEADERS];
int querySeparator;
bool didYield;
BloomFilter bf;
std::pair<int, std::string_view *> currentParameters;
public:
@@ -87,9 +89,11 @@ public:
}
std::string_view getHeader(std::string_view lowerCasedHeader) {
for (Header *h = headers; (++h)->key.length(); ) {
if (h->key.length() == lowerCasedHeader.length() && !strncmp(h->key.data(), lowerCasedHeader.data(), lowerCasedHeader.length())) {
return h->value;
if (bf.mightHave(lowerCasedHeader)) {
for (Header *h = headers; (++h)->key.length(); ) {
if (h->key.length() == lowerCasedHeader.length() && !strncmp(h->key.data(), lowerCasedHeader.data(), lowerCasedHeader.length())) {
return h->value;
}
}
}
return std::string_view(nullptr, 0);
@@ -142,7 +146,7 @@ private:
return unsignedIntegerValue;
}
static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers) {
static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, BloomFilter *bf) {
char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer;
for (unsigned int i = 0; i < HttpRequest::MAX_HEADERS; i++) {
@@ -177,13 +181,19 @@ private:
int consumedTotal = 0;
data[length] = '\r';
for (int consumed; length && (consumed = getHeaders(data, data + length, req->headers)); ) {
for (int consumed; length && (consumed = getHeaders(data, data + length, req->headers, &req->bf)); ) {
data += consumed;
length -= consumed;
consumedTotal += consumed;
/* Strip away tail of first "header value" aka URL */
req->headers->value = std::string_view(req->headers->value.data(), std::max<int>(0, (int) req->headers->value.length() - 9));
/* Add all headers to bloom filter */
for (HttpRequest::Header *h = req->headers; (++h)->key.length(); ) {
req->bf.add(h->key);
}
/* Parse query */
const char *querySeparatorPtr = (const char *) memchr(req->headers->value.data(), '?', req->headers->value.length());
req->querySeparator = (int) ((querySeparatorPtr ? querySeparatorPtr : req->headers->value.data() + req->headers->value.length()) - req->headers->value.data());
+6
View File
@@ -174,6 +174,12 @@ public:
/* Note: Headers are not checked in regards to timeout.
* We only check when you actively push data or end the request */
/* Write 100 Continue, can be done any amount of times */
HttpResponse *writeContinue() {
Super::write("HTTP/1.1 100 Continue\r\n\r\n", 25);
return this;
}
/* Write the HTTP status */
HttpResponse *writeStatus(std::string_view status) {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();