Introduce experimental support for on-demand bundling via HTML imports and Bun.serve() (#16395)

This commit is contained in:
Jarred Sumner
2025-01-15 01:00:23 -08:00
committed by GitHub
parent 6cb0d49afb
commit 522f2b91a0
25 changed files with 1735 additions and 769 deletions

View File

@@ -466,12 +466,12 @@ public:
/* Register an HTTP route handler acording to URL pattern */
void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
HttpContextData<SSL> *httpContextData = getSocketContextData();
HttpContextData<SSL> *httpContextData = getSocketContextData();
/* Todo: This is ugly, fix */
std::vector<std::string> methods;
if (method == "*") {
methods = httpContextData->currentRouter->upperCasedMethods;
methods = {"*"};
} else {
methods = {method};
}

View File

@@ -35,43 +35,7 @@ namespace uWS {
template <class USERDATA>
struct HttpRouter {
/* These are public for now */
std::vector<std::string> upperCasedMethods = {
"ACL",
"BIND",
"CHECKOUT",
"CONNECT",
"COPY",
"DELETE",
"GET",
"HEAD",
"LINK",
"LOCK",
"M-SEARCH" ,
"MERGE",
"MKACTIVITY",
"MKCALENDAR",
"MKCOL",
"MOVE",
"NOTIFY",
"OPTIONS",
"PATCH",
"POST",
"PROPFIND",
"PROPPATCH",
"PURGE",
"PUT",
"REBIND",
"REPORT",
"SEARCH",
"SOURCE",
"SUBSCRIBE",
"TRACE",
"UNBIND",
"UNLINK",
"UNLOCK",
"UNSUBSCRIBE",
};
static constexpr std::string_view ANY_METHOD_TOKEN = "*";
static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
private:
@@ -81,23 +45,20 @@ private:
/* Handler ids are 32-bit */
static const uint32_t HANDLER_MASK = 0x0fffffff;
/* Methods and their respective priority */
std::map<std::string, int> priority;
/* List of handlers */
std::vector<MoveOnlyFunction<bool(HttpRouter *)>> handlers;
/* Current URL cache */
std::string_view currentUrl;
std::string_view urlSegmentVector[MAX_URL_SEGMENTS];
int urlSegmentTop;
std::string_view currentUrl = {};
std::string_view urlSegmentVector[MAX_URL_SEGMENTS] = {};
int urlSegmentTop = -1;
/* The matching tree */
struct Node {
std::string name;
std::vector<std::unique_ptr<Node>> children;
std::vector<uint32_t> handlers;
bool isHighPriority;
std::string name = {};
std::vector<std::unique_ptr<Node>> children = {};
std::vector<uint32_t> handlers = {};
bool isHighPriority = false;
Node(std::string name) : name(name) {}
} root = {"rootNode"};
@@ -141,8 +102,8 @@ private:
struct RouteParameters {
friend struct HttpRouter;
private:
std::string_view params[MAX_URL_SEGMENTS];
int paramsTop;
std::string_view params[MAX_URL_SEGMENTS] = {};
int paramsTop = -1;
void reset() {
paramsTop = -1;
@@ -173,7 +134,7 @@ private:
inline std::pair<std::string_view, bool> getUrlSegment(int urlSegment) {
if (urlSegment > urlSegmentTop) {
/* Signal as STOP when we have no more URL or stack space */
if (!currentUrl.length() || urlSegment > 99) {
if (!currentUrl.length() || urlSegment > int(MAX_URL_SEGMENTS - 1)) {
return {{}, true};
}
@@ -280,10 +241,8 @@ private:
public:
HttpRouter() {
int p = 0;
for (std::string &method : upperCasedMethods) {
priority[method] = p++;
}
/* Always have ANY route */
getNode(&root, std::string(ANY_METHOD_TOKEN.data(), ANY_METHOD_TOKEN.length()), false);
}
std::pair<int, std::string_view *> getParameters() {
@@ -323,14 +282,19 @@ public:
void add(std::vector<std::string> methods, std::string pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
/* First remove existing handler */
remove(methods[0], pattern, priority);
for (std::string method : methods) {
/* Lookup method */
Node *node = getNode(&root, method, false);
/* Iterate over all segments */
setUrl(pattern);
for (int i = 0; !getUrlSegment(i).second; i++) {
node = getNode(node, std::string(getUrlSegment(i).first), priority == HIGH_PRIORITY);
std::string strippedSegment(getUrlSegment(i).first);
if (strippedSegment.length() && strippedSegment[0] == ':') {
/* Parameter routes must be named only : */
strippedSegment = ":";
}
node = getNode(node, strippedSegment, priority == HIGH_PRIORITY);
}
/* Insert handler in order sorted by priority (most significant 1 byte) */
node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), (uint32_t) (priority | handlers.size())), (uint32_t) (priority | handlers.size()));
@@ -339,11 +303,20 @@ public:
/* Alloate this handler */
handlers.emplace_back(std::move(handler));
/* Assume can find this handler again */
if (((handlers.size() - 1) | priority) != findHandler(methods[0], pattern, priority)) {
std::cerr << "Error: Internal routing error" << std::endl;
std::abort();
}
/* ANY method must be last, GET must be first */
std::sort(root.children.begin(), root.children.end(), [](const auto &a, const auto &b) {
if (a->name == "GET" && b->name != "GET") {
return true;
} else if (b->name == "GET" && a->name != "GET") {
return false;
} else if (a->name == ANY_METHOD_TOKEN && b->name != ANY_METHOD_TOKEN) {
return false;
} else if (b->name == ANY_METHOD_TOKEN && a->name != ANY_METHOD_TOKEN) {
return true;
} else {
return a->name < b->name;
}
});
}
bool cullNode(Node *parent, Node *node, uint32_t handler) {
@@ -408,4 +381,4 @@ public:
}
#endif // UWS_HTTPROUTER_HPP
#endif // UWS_HTTPROUTER_HPP