Skip to content

Commit

Permalink
Merge branch 'uNetworking:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
partyblob authored Oct 23, 2024
2 parents 3fc21c5 + 5b6d685 commit a748c71
Show file tree
Hide file tree
Showing 17 changed files with 327 additions and 94 deletions.
2 changes: 1 addition & 1 deletion build.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ int main(int argc, char **argv) {
char *CXX = strcpy(calloc(1024, 1), or_else(getenv("CXX"), "g++"));
char *EXEC_SUFFIX = strcpy(calloc(1024, 1), maybe(getenv("EXEC_SUFFIX")));

char *EXAMPLE_FILES[] = {"HelloWorldThreaded", "Http3Server", "Broadcast", "HelloWorld", "Crc32", "ServerName",
char *EXAMPLE_FILES[] = {"CachingApp", "HelloWorldThreaded", "Http3Server", "Broadcast", "HelloWorld", "Crc32", "ServerName",
"EchoServer", "BroadcastingEchoServer", "UpgradeSync", "UpgradeAsync", "ParameterRoutes"};

strcat(CXXFLAGS, " -march=native -O3 -Wpedantic -Wall -Wextra -Wsign-conversion -Wconversion -std=c++20 -Isrc -IuSockets/src");
Expand Down
23 changes: 23 additions & 0 deletions examples/CachingApp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "App.h"
#include <iostream>

int main() {
uWS::App app;

/* Regular, non-cached response */
app.get("/not-cached", [](auto *res, auto */*req*/) {
res->end("Responding without a cache");
}).get("/*", [](auto *res, auto */*req*/) {
/* A cached response with 5 seconds of lifetime */
std::cout << "Filling cache now" << std::endl;
res->end("This is a response");
}, 5).listen(8080, [](bool success) {
if (success) {
std::cout << "Listening on port 8080" << std::endl;
} else {
std::cerr << "Failed to listen on port 8080" << std::endl;
}
});

app.run();
}
186 changes: 123 additions & 63 deletions src/App.h

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/AsyncSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct AsyncSocket {
/* This guy is promiscuous */
template <bool> friend struct HttpContext;
template <bool, bool, typename> friend struct WebSocketContext;
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct TemplatedAppBase;
template <bool, typename> friend struct WebSocketContextData;
template <typename, typename> friend struct TopicTree;
template <bool> friend struct HttpResponse;
Expand Down Expand Up @@ -141,7 +141,7 @@ struct AsyncSocket {
getLoopData()->corkedSocket = this;
}

/* Returns wheter we are corked or not */
/* Returns whether we are corked or not */
bool isCorked() {
return getLoopData()->corkedSocket == this;
}
Expand Down Expand Up @@ -231,7 +231,7 @@ struct AsyncSocket {
}

/* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible.
* Returns pair of bytes written (anywhere) and wheter or not this call resulted in the polling for
* Returns pair of bytes written (anywhere) and whether or not this call resulted in the polling for
* writable (or we are in a state that implies polling for writable). */
std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) {
/* Fake success if closed, simple fix to allow uncork of closed socket to succeed */
Expand Down
3 changes: 2 additions & 1 deletion src/BloomFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/* This filter has no false positives or collisions for the standard
* and non-standard common request headers */

#include <cstdint>
#include <string_view>
#include <bitset>

Expand All @@ -30,7 +31,7 @@ struct BloomFilter {
private:
std::bitset<256> filter;
static inline uint32_t perfectHash(uint32_t features) {
return features *= 1843993368;
return features * 1843993368;
}

union ScrambleArea {
Expand Down
115 changes: 115 additions & 0 deletions src/CachingApp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#ifndef UWS_CACHINGAPP_H
#define UWS_CACHINGAPP_H

#include "App.h"
#include <unordered_map>
#include <string>
#include <functional>
#include <string_view>

namespace uWS {

struct StringViewHash {
size_t operator()(std::string_view sv) const {
return std::hash<std::string_view>{}(sv);
}
};

struct StringViewEqual {
bool operator()(std::string_view sv1, std::string_view sv2) const {
return sv1 == sv2;
}
};



class CachingHttpResponse {
public:
CachingHttpResponse(uWS::HttpResponse<false> *res)
: res(res) {}

void write(std::string_view data) {
buffer.append(data);
}

void end(std::string_view data = "", bool closeConnection = false) {
buffer.append(data);

// end for all queued up sockets also
res->end(buffer);

created = time(0);

std::ignore = closeConnection;
}

public:
uWS::HttpResponse<false>* res; // should be a vector of waiting sockets


std::string buffer; // body
time_t created;
};

typedef std::unordered_map<std::string_view, CachingHttpResponse *,
StringViewHash,
StringViewEqual> CacheType;

// we can also derive from H3app later on
template <bool SSL>
struct CachingApp : public uWS::TemplatedAppBase<SSL, CachingApp<SSL>> {
public:
CachingApp(SocketContextOptions options = {}) : uWS::TemplatedAppBase<SSL, CachingApp<SSL>>(options) {}

using uWS::TemplatedAppBase<SSL, CachingApp<SSL>>::get;

CachingApp(const CachingApp &other) = delete;
CachingApp(CachingApp<SSL> &&other) : uWS::TemplatedAppBase<SSL, CachingApp<SSL>>(std::move(other)) {
// also move the cache
}

~CachingApp() {

}

// variant 1: only taking URL into account
CachingApp &&get(const std::string& url, uWS::MoveOnlyFunction<void(CachingHttpResponse*, uWS::HttpRequest*)> &&handler, unsigned int secondsToExpiry) {
((uWS::TemplatedAppBase<SSL, CachingApp<SSL>> *)this)->get(url, [this, handler = std::move(handler), secondsToExpiry](auto* res, auto* req) mutable {
/* We need to know the cache key and the time of now */
std::string_view cache_key = req->getFullUrl();
time_t now = static_cast<LoopData *>(us_loop_ext((us_loop_t *)uWS::Loop::get()))->cacheTimepoint;

auto it = cache.find(cache_key);
if (it != cache.end()) {

if (it->second->created + secondsToExpiry > now) {
res->end(it->second->buffer); // tryEnd!
return;
}

/* We are no longer valid, delete old cache and fall through to create a new entry */
delete it->second;

// is the cache completed? if not, add yourself to the waiting list of sockets to that cache

// if the cache completed? ok, is it still valid? use it
}

// immediately take the place in the cache
CachingHttpResponse *cachingRes;
cache[cache_key] = (cachingRes = new CachingHttpResponse(res));

handler(cachingRes, req);
});
return std::move(*this);
}

// variant 2: taking URL and a list of headers into account
// todo

private:
CacheType cache;
};

}
#endif
8 changes: 5 additions & 3 deletions src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ template<bool> struct HttpResponse;

template <bool SSL>
struct HttpContext {
template<bool> friend struct TemplatedApp;
template<bool, typename> friend struct TemplatedAppBase;
template<bool> friend struct HttpResponse;
private:
HttpContext() = delete;
Expand Down Expand Up @@ -149,7 +149,9 @@ struct HttpContext {
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
httpResponseData->offset = 0;

/* Are we not ready for another request yet? Terminate the connection. */
/* Are we not ready for another request yet? Terminate the connection.
* Important for denying async pipelining until, if ever, we want to suppot it.
* Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
return nullptr;
Expand Down Expand Up @@ -487,7 +489,7 @@ struct HttpContext {
return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>));
}

void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(struct us_socket_context_t *, LIBUS_SOCKET_DESCRIPTOR)) {
us_socket_context_on_pre_open(SSL, getSocketContext(), handler);
}

Expand Down
6 changes: 5 additions & 1 deletion src/HttpContextData.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ template <bool SSL>
struct alignas(16) HttpContextData {
template <bool> friend struct HttpContext;
template <bool> friend struct HttpResponse;
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct TemplatedAppBase;
private:
std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;

Expand All @@ -49,6 +49,10 @@ struct alignas(16) HttpContextData {
HttpRouter<RouterData> router;
void *upgradedWebSocket = nullptr;
bool isParsingHttp = false;

/* If we are main acceptor, distribute to these apps */
std::vector<void *> childApps;
unsigned int roundRobin = 0;
};

}
Expand Down
18 changes: 14 additions & 4 deletions src/HttpParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,20 @@ struct HttpParser {
if (memcmp(" HTTP/1.1\r\n", data, 11) == 0) {
return data + 11;
}
return nullptr;
/* If we stand at the post padded CR, we have fragmented input so try again later */
if (data[0] == '\r') {
return nullptr;
}
/* This is an error */
return (char *) 0x1;
}
}
}
return nullptr;
/* If we stand at the post padded CR, we have fragmented input so try again later */
if (data[0] == '\r') {
return nullptr;
}
return (char *) 0x1;
}

/* RFC 9110: 5.5 Field Values (TLDR; anything above 31 is allowed; htab (9) is also allowed)
Expand Down Expand Up @@ -364,10 +373,10 @@ struct HttpParser {
* which is then removed, and our counters to flip due to overflow and we end up with a crash */

/* The request line is different from the field names / field values */
if (!(postPaddedBuffer = consumeRequestLine(postPaddedBuffer, headers[0]))) {
if ((char *) 2 > (postPaddedBuffer = consumeRequestLine(postPaddedBuffer, headers[0]))) {
/* Error - invalid request line */
/* Assuming it is 505 HTTP Version Not Supported */
err = HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED;
err = postPaddedBuffer ? HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED : 0;
return 0;
}
headers++;
Expand Down Expand Up @@ -437,6 +446,7 @@ struct HttpParser {
}
}
/* We ran out of header space, too large request */
err = HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/HttpResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static const int HTTP_TIMEOUT_S = 10;
template <bool SSL>
struct HttpResponse : public AsyncSocket<SSL> {
/* Solely used for getHttpResponseData() */
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct TemplatedAppBase;
typedef AsyncSocket<SSL> Super;
private:
HttpResponseData<SSL> *getHttpResponseData() {
Expand Down
4 changes: 3 additions & 1 deletion src/LocalCluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ struct LocalCluster {

cb(*app);

app->preOpen([](LIBUS_SOCKET_DESCRIPTOR fd) -> LIBUS_SOCKET_DESCRIPTOR {
app->preOpen([](struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd) -> LIBUS_SOCKET_DESCRIPTOR {

std::ignore = context;

/* Distribute this socket in round robin fashion */
//std::cout << "About to load balance " << fd << " to " << roundRobin << std::endl;
Expand Down
7 changes: 4 additions & 3 deletions src/LoopData.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ struct alignas(16) LoopData {
}

void updateDate() {
time_t now = time(0);
cacheTimepoint = time(0);
struct tm tstruct = {};
#ifdef _WIN32
/* Micro, fucking soft never follows spec. */
gmtime_s(&tstruct, &now);
gmtime_s(&tstruct, &cacheTimepoint);
#else
gmtime_r(&now, &tstruct);
gmtime_r(&cacheTimepoint, &tstruct);
#endif
static const char wday_name[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
Expand All @@ -87,6 +87,7 @@ struct alignas(16) LoopData {
}

char date[32];
time_t cacheTimepoint = 0;

/* Be silent */
bool noMark = false;
Expand Down
12 changes: 12 additions & 0 deletions src/MoveOnlyFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ SOFTWARE.
#define _ANY_INVOKABLE_H_

#include <functional>

#if !defined(__cpp_lib_move_only_function) || __cpp_lib_move_only_function < 202110L

#include <memory>
#include <type_traits>

Expand Down Expand Up @@ -374,4 +377,13 @@ namespace uWS {
using MoveOnlyFunction = ofats::any_invocable<T>;
}

#else // !defined(__cpp_lib_move_only_function) || __cpp_lib_move_only_function < 202110L

namespace uWS {
template <class T>
using MoveOnlyFunction = std::move_only_function<T>;
}

#endif

#endif // _ANY_INVOKABLE_H_
2 changes: 1 addition & 1 deletion src/WebSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace uWS {

template <bool SSL, bool isServer, typename USERDATA>
struct WebSocket : AsyncSocket<SSL> {
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct TemplatedAppBase;
template <bool> friend struct HttpResponse;
private:
typedef AsyncSocket<SSL> Super;
Expand Down
6 changes: 3 additions & 3 deletions src/WebSocketContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace uWS {

template <bool SSL, bool isServer, typename USERDATA>
struct WebSocketContext {
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct TemplatedAppBase;
template <bool, typename> friend struct WebSocketProtocol;
private:
WebSocketContext() = delete;
Expand Down Expand Up @@ -371,11 +371,11 @@ struct WebSocketContext {
return s;
});

/* Handle FIN, HTTP does not support half-closed sockets, so simply close */
/* Handle FIN, WebSocket does not support half-closed sockets, so simply close */
us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) {

/* If we get a fin, we just close I guess */
us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data());

return s;
});
Expand Down
Loading

0 comments on commit a748c71

Please sign in to comment.