Make it header-only, add routing support
This commit is contained in:
@@ -10,16 +10,14 @@ SOURCES += \
|
||||
uSockets/src/socket.c \
|
||||
uSockets/src/eventing/libuv.c \
|
||||
uSockets/src/ssl.c \
|
||||
uSockets/src/loop.c \
|
||||
src/Hub.cpp \
|
||||
src/Http.cpp \
|
||||
src/Context.cpp
|
||||
uSockets/src/loop.c
|
||||
|
||||
HEADERS += \
|
||||
src/Hub.h \
|
||||
src/Http.h \
|
||||
src/Context.h \
|
||||
src/uWS.h
|
||||
src/uWS.h \
|
||||
src/HttpRouter.h
|
||||
#uSockets/libusockets.h \
|
||||
#uSockets/internal/eventing/epoll.h \
|
||||
#uSockets/internal/networking/bsd.h \
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// much speaks for a header-only or header-mostly implementation now that uSockets is properly isolating its internal headers
|
||||
|
||||
#include "Context.h"
|
||||
#include "HttpRouter.h"
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -14,13 +15,16 @@ int main() {
|
||||
options.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem";
|
||||
options.passphrase = "1234";
|
||||
|
||||
uWS::SSLContext(options).onHttpRequest([buffer](auto *s, HttpRequest *req) {
|
||||
//uWS::SSLContext c(options);
|
||||
uWS::Context c;
|
||||
|
||||
if (req->getUrl() == "/") {
|
||||
s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512);
|
||||
} else {
|
||||
std::cout << "Got HTTP request at URL: " << req->getUrl() << std::endl;
|
||||
}
|
||||
c.route("GET", "/", [buffer](auto *s, HttpRequest *req, auto *args) {
|
||||
|
||||
s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512);
|
||||
|
||||
}).route("GET", "/wrong", [buffer](auto *s, HttpRequest *req, auto *args) {
|
||||
|
||||
std::cout << "Wrong way!" << std::endl;
|
||||
|
||||
}).listen("localhost", 3000, 0);
|
||||
|
||||
|
||||
+24
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "Http.h"
|
||||
|
||||
//#define SWAP_F [](auto a, auto b){ if constexpr(SSL) return a; else return b; }
|
||||
#include "HttpRouter.h"
|
||||
|
||||
namespace uWS {
|
||||
|
||||
@@ -52,6 +52,15 @@ protected:
|
||||
|
||||
// client protocols
|
||||
|
||||
// we have a router too
|
||||
struct UserData {
|
||||
// pass whatever you need as user data
|
||||
HttpSocket<SSL> *httpSocket;
|
||||
HttpRequest *httpRequest;
|
||||
};
|
||||
|
||||
HttpRouter<UserData *> r;
|
||||
|
||||
public:
|
||||
|
||||
// the shared constructor
|
||||
@@ -140,6 +149,20 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContextBase &route(std::string method, std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
|
||||
// calling this function overrides any other onHttpRequest!
|
||||
onHttpRequest([this](auto *s, HttpRequest *req) {
|
||||
UserData user = {s, req};
|
||||
r.route("GET", 3, req->getUrl().data(), req->getUrl().length(), &user);
|
||||
});
|
||||
|
||||
r.add(method.c_str(), pattern.c_str(), [handler](UserData *user, auto *args) {
|
||||
handler(user->httpSocket, user->httpRequest, args);
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// for client and server
|
||||
ContextBase &onWebSocketConnection() {
|
||||
return *this;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#ifndef HTTPROUTER_HPP
|
||||
#define HTTPROUTER_HPP
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
template <class USERDATA>
|
||||
class HttpRouter {
|
||||
private:
|
||||
std::vector<std::function<void(USERDATA, std::vector<std::string_view> *)>> handlers;
|
||||
std::vector<std::string_view> params;
|
||||
|
||||
struct Node {
|
||||
std::string name;
|
||||
std::map<std::string, Node *> children;
|
||||
short handler;
|
||||
};
|
||||
|
||||
Node *tree = new Node({"GET", {}, -1});
|
||||
std::string compiled_tree;
|
||||
|
||||
void add(std::vector<std::string> route, short handler) {
|
||||
Node *parent = tree;
|
||||
for (std::string node : route) {
|
||||
if (parent->children.find(node) == parent->children.end()) {
|
||||
parent->children[node] = new Node({node, {}, handler});
|
||||
}
|
||||
parent = parent->children[node];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short compile_tree(Node *n) {
|
||||
unsigned short nodeLength = 6 + n->name.length();
|
||||
for (auto c : n->children) {
|
||||
nodeLength += compile_tree(c.second);
|
||||
}
|
||||
|
||||
unsigned short nodeNameLength = n->name.length();
|
||||
|
||||
std::string compiledNode;
|
||||
compiledNode.append((char *) &nodeLength, sizeof(nodeLength));
|
||||
compiledNode.append((char *) &nodeNameLength, sizeof(nodeNameLength));
|
||||
compiledNode.append((char *) &n->handler, sizeof(n->handler));
|
||||
compiledNode.append(n->name.data(), n->name.length());
|
||||
|
||||
compiled_tree = compiledNode + compiled_tree;
|
||||
return nodeLength;
|
||||
}
|
||||
|
||||
inline const char *find_node(const char *parent_node, const char *name, int name_length) {
|
||||
unsigned short nodeLength = *(unsigned short *) &parent_node[0];
|
||||
unsigned short nodeNameLength = *(unsigned short *) &parent_node[2];
|
||||
|
||||
//std::cout << "Finding node: <" << std::string(name, name_length) << ">" << std::endl;
|
||||
|
||||
const char *stoppp = parent_node + nodeLength;
|
||||
for (const char *candidate = parent_node + 6 + nodeNameLength; candidate < stoppp; ) {
|
||||
|
||||
unsigned short nodeLength = *(unsigned short *) &candidate[0];
|
||||
unsigned short nodeNameLength = *(unsigned short *) &candidate[2];
|
||||
|
||||
// whildcard, parameter, equal
|
||||
if (nodeNameLength == 0) {
|
||||
return candidate;
|
||||
} else if (candidate[6] == ':') {
|
||||
// parameter
|
||||
|
||||
// todo: push this pointer on the stack of args!
|
||||
params.push_back(std::string_view(name, name_length));
|
||||
|
||||
return candidate;
|
||||
} else if (nodeNameLength == name_length && !memcmp(candidate + 6, name, name_length)) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
candidate = candidate + nodeLength;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// returns next slash from start or end
|
||||
inline const char *getNextSegment(const char *start, const char *end) {
|
||||
const char *stop = (const char *) memchr(start, '/', end - start);
|
||||
return stop ? stop : end;
|
||||
}
|
||||
|
||||
// should take method also!
|
||||
inline int lookup(const char *url, int length) {
|
||||
// all urls start with /
|
||||
url++;
|
||||
length--;
|
||||
|
||||
const char *treeStart = (char *) compiled_tree.data();
|
||||
|
||||
const char *stop, *start = url, *end_ptr = url + length;
|
||||
do {
|
||||
stop = getNextSegment(start, end_ptr);
|
||||
|
||||
//std::cout << "Matching(" << std::string(start, stop - start) << ")" << std::endl;
|
||||
|
||||
if(nullptr == (treeStart = find_node(treeStart, start, stop - start))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
start = stop + 1;
|
||||
} while (stop != end_ptr);
|
||||
|
||||
return *(short *) &treeStart[4];
|
||||
}
|
||||
|
||||
public:
|
||||
HttpRouter() {
|
||||
// maximum 100 parameters
|
||||
params.reserve(100);
|
||||
}
|
||||
|
||||
HttpRouter *add(const char *method, const char *pattern, std::function<void(USERDATA, std::vector<std::string_view> *)> handler) {
|
||||
|
||||
// step over any initial slash
|
||||
if (pattern[0] == '/') {
|
||||
pattern++;
|
||||
}
|
||||
|
||||
std::vector<std::string> nodes;
|
||||
//nodes.push_back(method);
|
||||
|
||||
const char *stop, *start = pattern, *end_ptr = pattern + strlen(pattern);
|
||||
do {
|
||||
stop = getNextSegment(start, end_ptr);
|
||||
|
||||
//std::cout << "Segment(" << std::string(start, stop - start) << ")" << std::endl;
|
||||
|
||||
nodes.push_back(std::string(start, stop - start));
|
||||
|
||||
start = stop + 1;
|
||||
} while (stop != end_ptr);
|
||||
|
||||
|
||||
// if pattern starts with / then move 1+ and run inline slash parser
|
||||
|
||||
add(nodes, handlers.size());
|
||||
handlers.push_back(handler);
|
||||
|
||||
compile();
|
||||
return this;
|
||||
}
|
||||
|
||||
void compile() {
|
||||
compiled_tree.clear();
|
||||
compile_tree(tree);
|
||||
}
|
||||
|
||||
void route(const char *method, unsigned int method_length, const char *url, unsigned int url_length, USERDATA userData) {
|
||||
handlers[lookup(url, url_length)](userData, ¶ms);
|
||||
params.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HTTPROUTER_HPP
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
Reference in New Issue
Block a user