mirror of
https://github.com/typesense/typesense.git
synced 2025-05-18 04:32:38 +08:00
Use hashmap for storing routes instead of an array.
The use of array index makes rolling updates tricky since requests might be forwarded to an instance running an older/newer version having a different route index.
This commit is contained in:
parent
88192820de
commit
75a1fe1e1d
@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include "json.hpp"
|
||||
#include "string_utils.h"
|
||||
|
||||
#define H2O_USE_LIBUV 0
|
||||
extern "C" {
|
||||
@ -96,26 +97,25 @@ struct http_res {
|
||||
|
||||
enum class ROUTE_CODES {
|
||||
NOT_FOUND = -1,
|
||||
RETURN_EARLY = -2,
|
||||
ALREADY_HANDLED = -2,
|
||||
};
|
||||
|
||||
struct http_req {
|
||||
h2o_req_t* _req;
|
||||
std::string http_method;
|
||||
int route_index;
|
||||
int route_hash;
|
||||
std::map<std::string, std::string> params;
|
||||
std::string body;
|
||||
|
||||
http_req(): route_index(-1) {}
|
||||
http_req(): route_hash(-1) {}
|
||||
|
||||
http_req(h2o_req_t* _req, const std::string & http_method, size_t route_index,
|
||||
const std::map<std::string, std::string> & params,
|
||||
std::string body): _req(_req), http_method(http_method), route_index(route_index),
|
||||
params(params), body(body) {}
|
||||
http_req(h2o_req_t* _req, const std::string & http_method, size_t route_hash,
|
||||
const std::map<std::string, std::string> & params, std::string body):
|
||||
_req(_req), http_method(http_method), route_hash(route_hash), params(params), body(body) {}
|
||||
|
||||
void deserialize(const std::string& serialized_content) {
|
||||
nlohmann::json content = nlohmann::json::parse(serialized_content);
|
||||
route_index = content["route_index"];
|
||||
route_hash = content["route_hash"];
|
||||
body = content["body"];
|
||||
|
||||
for (nlohmann::json::iterator it = content["params"].begin(); it != content["params"].end(); ++it) {
|
||||
@ -127,7 +127,7 @@ struct http_req {
|
||||
|
||||
std::string serialize() const {
|
||||
nlohmann::json content;
|
||||
content["route_index"] = route_index;
|
||||
content["route_hash"] = route_hash;
|
||||
content["params"] = params;
|
||||
content["body"] = body;
|
||||
|
||||
@ -149,6 +149,12 @@ struct route_path {
|
||||
inline bool operator< (const route_path& rhs) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t route_hash() {
|
||||
std::string path = StringUtils::join(path_parts, "/");
|
||||
std::string method_path = http_method + path;
|
||||
return StringUtils::hash_wy(method_path.c_str(), method_path.size());
|
||||
}
|
||||
};
|
||||
|
||||
struct h2o_custom_generator_t {
|
||||
|
@ -34,7 +34,7 @@ private:
|
||||
|
||||
std::string version;
|
||||
|
||||
std::vector<route_path> routes; // TODO: must be a hashmap?
|
||||
std::unordered_map<uint32_t, route_path> routes;
|
||||
|
||||
const std::string listen_address;
|
||||
|
||||
|
@ -46,7 +46,7 @@ struct StringUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string join(std::vector<std::string> vec, std::string delimiter, size_t start_index = 0) {
|
||||
static std::string join(std::vector<std::string> vec, const std::string& delimiter, size_t start_index = 0) {
|
||||
std::stringstream ss;
|
||||
for(size_t i = start_index; i < vec.size(); i++) {
|
||||
if(i != start_index) {
|
||||
|
@ -613,21 +613,16 @@ bool async_write_request(void *data) {
|
||||
AsyncIndexArg* index_arg = static_cast<AsyncIndexArg*>(data);
|
||||
std::unique_ptr<AsyncIndexArg> index_arg_guard(index_arg);
|
||||
|
||||
if(index_arg->req->route_index == static_cast<int>(ROUTE_CODES::NOT_FOUND)) {
|
||||
if(index_arg->req->route_hash == static_cast<int>(ROUTE_CODES::NOT_FOUND)) {
|
||||
// route not found
|
||||
return false;
|
||||
} else if(index_arg->req->route_index == static_cast<int>(ROUTE_CODES::RETURN_EARLY)) {
|
||||
// respond without calling internal route
|
||||
server->send_response(index_arg->req, index_arg->res);
|
||||
return true;
|
||||
index_arg->res->send_400("Not found.");
|
||||
} else if(index_arg->req->route_hash != static_cast<int>(ROUTE_CODES::ALREADY_HANDLED)) {
|
||||
// call the underlying http handler
|
||||
route_path* found_rpath = nullptr;
|
||||
server->get_route(index_arg->req->route_hash, &found_rpath);
|
||||
found_rpath->handler(*index_arg->req, *index_arg->res);
|
||||
}
|
||||
|
||||
route_path* found_rpath;
|
||||
server->get_route(index_arg->req->route_index, &found_rpath);
|
||||
|
||||
// call the underlying http handler
|
||||
found_rpath->handler(*index_arg->req, *index_arg->res);
|
||||
|
||||
if(index_arg->req->_req != nullptr) {
|
||||
// we have to return a response to the client
|
||||
server->send_response(index_arg->req, index_arg->res);
|
||||
|
@ -24,6 +24,8 @@ HttpServer::HttpServer(const std::string & version, const std::string & listen_a
|
||||
hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), 65535);
|
||||
register_handler(hostconf, "/", catch_all_handler);
|
||||
|
||||
listener_socket = nullptr; // initialized later
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
h2o_context_init(&ctx, h2o_evloop_create(), &config);
|
||||
|
||||
@ -161,8 +163,10 @@ void HttpServer::clear_timeouts(const std::vector<h2o_timeout_t*> & timeouts) {
|
||||
}
|
||||
|
||||
void HttpServer::stop() {
|
||||
h2o_socket_read_stop(listener_socket);
|
||||
h2o_socket_close(listener_socket);
|
||||
if(listener_socket != nullptr) {
|
||||
h2o_socket_read_stop(listener_socket);
|
||||
h2o_socket_close(listener_socket);
|
||||
}
|
||||
|
||||
// this will break the event loop
|
||||
exit_loop = true;
|
||||
@ -209,8 +213,9 @@ std::map<std::string, std::string> HttpServer::parse_query(const std::string& qu
|
||||
}
|
||||
|
||||
int HttpServer::find_route(const std::vector<std::string> & path_parts, const std::string & http_method, route_path** found_rpath) {
|
||||
for(size_t i = 0; i < routes.size(); i++) {
|
||||
const route_path & rpath = routes[i];
|
||||
for (const auto& index_route : routes) {
|
||||
const route_path & rpath = index_route.second;
|
||||
|
||||
if(rpath.path_parts.size() != path_parts.size() || rpath.http_method != http_method) {
|
||||
continue;
|
||||
}
|
||||
@ -228,7 +233,7 @@ int HttpServer::find_route(const std::vector<std::string> & path_parts, const st
|
||||
|
||||
if(found) {
|
||||
*found_rpath = const_cast<route_path *>(&rpath);
|
||||
return i;
|
||||
return index_route.first;
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,9 +309,9 @@ int HttpServer::catch_all_handler(h2o_handler_t *_self, h2o_req_t *req) {
|
||||
}
|
||||
|
||||
route_path *rpath = nullptr;
|
||||
int route_index = self->http_server->find_route(path_parts, http_method, &rpath);
|
||||
int route_hash = self->http_server->find_route(path_parts, http_method, &rpath);
|
||||
|
||||
if(route_index != -1) {
|
||||
if(route_hash != -1) {
|
||||
bool authenticated = self->http_server->auth_handler(*rpath, auth_key_from_header);
|
||||
if(!authenticated) {
|
||||
return send_401_unauthorized(req);
|
||||
@ -320,7 +325,7 @@ int HttpServer::catch_all_handler(h2o_handler_t *_self, h2o_req_t *req) {
|
||||
}
|
||||
}
|
||||
|
||||
http_req* request = new http_req(req, http_method, route_index, query_map, req_body);
|
||||
http_req* request = new http_req(req, http_method, route_hash, query_map, req_body);
|
||||
http_res* response = new http_res();
|
||||
|
||||
// for writes, we defer to replication_state
|
||||
@ -391,28 +396,28 @@ void HttpServer::get(const std::string & path, bool (*handler)(http_req &, http_
|
||||
std::vector<std::string> path_parts;
|
||||
StringUtils::split(path, path_parts, "/");
|
||||
route_path rpath = {"GET", path_parts, handler, async};
|
||||
routes.push_back(rpath);
|
||||
routes.emplace(rpath.route_hash(), rpath);
|
||||
}
|
||||
|
||||
void HttpServer::post(const std::string & path, bool (*handler)(http_req &, http_res &), bool async) {
|
||||
std::vector<std::string> path_parts;
|
||||
StringUtils::split(path, path_parts, "/");
|
||||
route_path rpath = {"POST", path_parts, handler, async};
|
||||
routes.push_back(rpath);
|
||||
routes.emplace(rpath.route_hash(), rpath);
|
||||
}
|
||||
|
||||
void HttpServer::put(const std::string & path, bool (*handler)(http_req &, http_res &), bool async) {
|
||||
std::vector<std::string> path_parts;
|
||||
StringUtils::split(path, path_parts, "/");
|
||||
route_path rpath = {"PUT", path_parts, handler, async};
|
||||
routes.push_back(rpath);
|
||||
routes.emplace(rpath.route_hash(), rpath);
|
||||
}
|
||||
|
||||
void HttpServer::del(const std::string & path, bool (*handler)(http_req &, http_res &), bool async) {
|
||||
std::vector<std::string> path_parts;
|
||||
StringUtils::split(path, path_parts, "/");
|
||||
route_path rpath = {"DELETE", path_parts, handler, async};
|
||||
routes.push_back(rpath);
|
||||
routes.emplace(rpath.route_hash(), rpath);
|
||||
}
|
||||
|
||||
void HttpServer::on(const std::string & message, bool (*handler)(void*)) {
|
||||
@ -456,7 +461,7 @@ ReplicationState* HttpServer::get_replication_state() const {
|
||||
}
|
||||
|
||||
void HttpServer::get_route(size_t index, route_path** found_rpath) {
|
||||
if(index >= 0 && index < routes.size()) {
|
||||
if(routes.count(index) > 0) {
|
||||
*found_rpath = &routes[index];
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ void ReplicationState::write(http_req* request, http_res* response) {
|
||||
LOG(ERROR) << "Rejecting write: could not find a leader.";
|
||||
response->send_500("Could not find a leader.");
|
||||
auto replication_arg = new AsyncIndexArg{request, response, nullptr};
|
||||
replication_arg->req->route_index = static_cast<int>(ROUTE_CODES::RETURN_EARLY);
|
||||
replication_arg->req->route_hash = static_cast<int>(ROUTE_CODES::ALREADY_HANDLED);
|
||||
return message_dispatcher->send_message(REPLICATION_MSG, replication_arg);
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ void ReplicationState::write(http_req* request, http_res* response) {
|
||||
}
|
||||
|
||||
auto replication_arg = new AsyncIndexArg{request, response, nullptr};
|
||||
replication_arg->req->route_index = static_cast<int>(ROUTE_CODES::RETURN_EARLY);
|
||||
replication_arg->req->route_hash = static_cast<int>(ROUTE_CODES::ALREADY_HANDLED);
|
||||
message_dispatcher->send_message(REPLICATION_MSG, replication_arg);
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user