refactored speedy and http server

This commit is contained in:
Dominik Tomičević 2015-10-09 01:24:12 +02:00
parent 9f943917ce
commit 9f8761032f
32 changed files with 513 additions and 411 deletions

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
CXX=clang++
CFLAGS=-std=c++1y -Wall -O2
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre
INC=-I./
SOURCES=$(wildcard *.cpp)
EXECUTABLE=memgraph
all: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES)
$(CXX) $(CFLAGS) $(SOURCES) -o $(EXECUTABLE) $(INC) $(LDFLAGS)
.PHONY:
clean:
rm -f memgraph
rm -f *.o

View File

@ -2,6 +2,8 @@
import re
import os
print "generating include.hpp file"
resource_path = 'resources'
template_path = 'resources/include.hpp.template'
include_path = 'resources/include.hpp'
@ -16,6 +18,7 @@ class Resource(object):
def __init__(self, filename):
self.filename = filename
self.class_name = None
with open(os.path.join(resource_path, filename)) as f:
class_name = re.compile('\s*class\s*(\w+)\s*\:')
@ -32,8 +35,10 @@ class Resource(object):
def load_resources():
return [Resource(f) for f in os.listdir(resource_path)
if f.endswith('.hpp')]
resources = [Resource(f) for f in os.listdir(resource_path)
if f.endswith('.hpp')]
return [r for r in resources if r.class_name is not None]
def write_includes(file, resources):

View File

@ -4,20 +4,21 @@
#include "speedy/speedy.hpp"
#include "api/restful/resource.hpp"
class Animal : public api::Resource<Animal, api::GET, api::POST>
{
public:
Animal(speedy::Speedy& app) : Resource(app, "/animal") {}
/* class Ani mal : public Resource<Animal, GET, POST> */
/* { */
/* public: */
/* Animal(sp::Speedy& app) */
/* : Resource(app, "/animal/{id:\\d+}/{name:\\w+}/commit") {} */
void get(http::Request& req, http::Response& res)
{
return res.send("Ok, here is a Dog");
}
/* void get(sp::Request& req, sp::Response& res) */
/* { */
/* return res.send("Ok, here is a Dog"); */
/* } */
void post(http::Request& req, http::Response& res)
{
return res.send("Oh, you gave me an animal?");
}
};
/* void post(sp::Request& req, sp::Response& res) */
/* { */
/* return res.send("Oh, you gave me an animal?"); */
/* } */
/* }; */
#endif

View File

@ -15,19 +15,19 @@
#include "api/restful/resource.hpp"
#include "speedy/speedy.hpp"
#include "animal.hpp"
#include "node.hpp"
static std::list<std::unique_ptr<api::RestfulResource>> resources;
static std::list<std::unique_ptr<RestfulResource>> resources;
template <class T>
void insert(speedy::Speedy& app)
void insert(sp::Speedy& app)
{
resources.push_back(std::unique_ptr<api::RestfulResource>(new T(app)));
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
}
void init(speedy::Speedy& app)
void init(sp::Speedy& app)
{
insert<Animal>(app);
insert<Node>(app);
}
#endif

View File

@ -17,15 +17,15 @@
<INCLUDE>
static std::list<std::unique_ptr<api::RestfulResource>> resources;
static std::list<std::unique_ptr<RestfulResource>> resources;
template <class T>
void insert(speedy::Speedy& app)
void insert(sp::Speedy& app)
{
resources.push_back(std::unique_ptr<api::RestfulResource>(new T(app)));
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
}
void init(speedy::Speedy& app)
void init(sp::Speedy& app)
{
<INIT>
}

32
api/resources/node.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef MEMGRAPH_API_RESOURCES_NODE_HPP
#define MEMGRAPH_API_RESOURCES_NODE_HPP
#include "api/restful/resource.hpp"
class Node : public Resource<Node, GET, POST>
{
public:
Node(sp::Speedy& app) : Resource(app, "/node") {}
void get(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
}
void post(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
}
void put(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
}
void del(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
}
};
#endif

View File

@ -5,9 +5,6 @@
#include "speedy/speedy.hpp"
#include "utils/crtp.hpp"
namespace api
{
/** @brief GET handler method for the resource
* Contains the code for registering GET handler for a URL to Speedy
*/
@ -21,7 +18,7 @@ struct GET
* @param resource Object containing ::get http::request_cb_t handler
*/
template <class T>
void link(speedy::Speedy& app, const std::string& path, T& resource)
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.get(path, std::bind(&T::get, resource, _1, _2));
@ -41,13 +38,33 @@ struct POST
* @param resource Object containing ::post http::request_cb_t handler
*/
template <class T>
void link(speedy::Speedy& app, const std::string& path, T& resource)
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.post(path, std::bind(&T::post, resource, _1, _2));
}
};
struct PUT
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.put(path, std::bind(&T::put, resource, _1, _2));
}
};
struct DELETE
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.del(path, std::bind(&T::del, resource, _1, _2));
}
};
namespace detail
{
@ -66,7 +83,7 @@ struct Method : public M
* @param app instance of speedy to register the method to
* @param path URL of the resource being registered
*/
Method(speedy::Speedy& app, const std::string& path)
Method(sp::Speedy& app, const std::string& path)
{
M::link(app, path, static_cast<T&>(*this));
}
@ -95,7 +112,7 @@ struct Methods;
template <class T, class M, class... Ms>
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
{
Methods(speedy::Speedy& app, const std::string& path)
Methods(sp::Speedy& app, const std::string& path)
: Method<T, M>(app, path), Methods<T, Ms...>(app, path) {}
};
@ -122,7 +139,7 @@ struct RestfulResource {};
* class T. Methods are given as a template parameter to the class. Valid
* template parameters are classes which implement a function
*
* void link(speedy::Speedy&, const std::string&, T& resource)
* void link(sp::Speedy&, const std::string&, T& resource)
*
* which registers a method you want to use with speedy
*
@ -133,10 +150,8 @@ template <class T, class... Ms>
class Resource : public detail::Methods<T, Ms...>, public RestfulResource
{
public:
Resource(speedy::Speedy& app, const std::string& path)
Resource(sp::Speedy& app, const std::string& path)
: detail::Methods<T, Ms...>(app, path) {}
};
}
#endif

4
build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
cd api && python link_resources.py && cd ..
make clean && make

View File

@ -0,0 +1,11 @@
#ifndef MEMGRAPH_DATA_STRUCTURES_STACK_ARRAY_STACK_HPP
#define MEMGRAPH_DATA_STRUCTURES_STACK_ARRAY_STACK_HPP
template <class T>
class ArrayStack
{
private:
};
#endif

View File

@ -1,55 +0,0 @@
#include <iostream>
#include <thread>
#include "threading/sync/spinlock.hpp"
#include "transaction/transaction_engine.hpp"
#include "memory/memory_engine.hpp"
#include "storage/storage_engine.hpp"
constexpr int K = 10000000;
constexpr int N = 64;
void insert(TransactionEngine* tx, StorageEngine* storage)
{
for(int i = 0; i < (K / N); ++i)
{
auto t = tx->begin();
Vertex* v;
storage->insert(&v, t);
tx->commit(t);
}
}
int main(void)
{
auto tx = new TransactionEngine(0);
auto mem = new MemoryEngine();
auto storage = new StorageEngine(*mem);
// auto t1 = tx->begin();
//
// Vertex* v1 = nullptr;
// storage->insert(&v1, t1);
//
// tx->commit(t1);
//
// auto t2 = tx->begin();
//
// Vertex* v2 = nullptr;
// storage->insert(&v2, t2);
//
// tx->commit(t2);
std::vector<std::thread> threads;
for(int i = 0; i < N; ++i)
threads.push_back(std::thread(insert, tx, storage));
for(auto& thread : threads)
thread.join();
return 0;
}

BIN
memgraph Executable file

Binary file not shown.

22
memgraph.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <iostream>
#include <vector>
#include "speedy/speedy.hpp"
#include "api/resources/include.hpp"
#include "utils/auto_scope.hpp"
int main(int argc, char** argv)
{
uv::UvLoop loop;
sp::Speedy app(loop);
init(app);
http::Ipv4 ip("0.0.0.0", 3400);
app.listen(ip);
loop.run(uv::UvLoop::Mode::Default);
return 0;
}

View File

@ -1,38 +0,0 @@
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
#include "debug/log.hpp"
static constexpr int N = 1000;
using q_t = lockfree::MpscQueue<std::string>;
void produce()
{
std::hash<std::thread::id> hasher;
for(int i = 0; i < N; ++i)
{
auto str = "Hello number " + std::to_string(i) + " from thread " +
std::to_string(hasher(std::this_thread::get_id()));
LOG_DEBUG(str);
}
}
int main(void)
{
constexpr int THREADS = 256;
std::vector<std::thread> threads;
for(int i = 0; i < THREADS; ++i)
threads.push_back(std::thread([]() { produce(); }));
for(auto& thread : threads)
thread.join();
return 0;
}

View File

@ -1,115 +0,0 @@
#include <iostream>
#include <atomic>
#include "server/uv/uv.hpp"
#include "server/http/http.hpp"
template <class T, class... Args>
class Promise
{
public:
Promise(std::function<T(Args...)> f, Args&&... args)
{
result = f(std::forward<Args>(args)...);
}
template <class R>
Promise<R, T> then(std::function<R(T)> f)
{
return Promise(f, std::forward<T>(result));
}
T get()
{
return result;
}
private:
T result;
std::atomic<bool> completed;
};
class TaskPool
{
template <class R, class... Args>
class Task
{
public:
using task_t = Task<R, Args...>;
using work_f = std::function<R(Args...)>;
using after_work_f = std::function<void(R)>;
Task(work_f work, after_work_f callback)
: work(work), callback(callback)
{
req.data = this;
}
void launch(uv::UvLoop& loop)
{
uv_queue_work(loop, &req, work_cb, after_work_cb);
}
private:
std::function<R(Args...)> work;
std::function<void(R&)> callback;
R result;
uv_work_t req;
static void work_cb(uv_work_t* req)
{
auto& task = *reinterpret_cast<task_t*>(req->data);
}
static void after_work_cb(uv_work_t* req, int)
{
auto task = reinterpret_cast<task_t>(req->data);
delete task;
}
};
public:
TaskPool(uv::UvLoop& loop) : loop(loop) {}
template <class R, class...Args>
void launch(std::function<R(Args...)> func,
std::function<void(R&)> callback)
{
auto task = new Task<R, Args...>(func, callback);
task->launch(loop);
}
private:
uv::UvLoop& loop;
};
int main(void)
{
uv::UvLoop loop;
TaskPool tp(loop);
tp.launch([](void) -> int { return 3 },
[](int x) -> void { std::cout << x << std::endl; });
// http::HttpServer server(loop);
//
// http::Ipv4 ip("0.0.0.0", 3400);
//
// server.listen(ip, [](http::Request& req, http::Response& res) {
//
//
//
// res.send(req.url);
// });
//
// loop.run(uv::UvLoop::Mode::Default);
return 0;
}

View File

@ -7,25 +7,35 @@
#include "request.hpp"
#include "response.hpp"
#include "utils/memory/block_allocator.hpp"
namespace http
{
template <class Req, class Res>
class HttpServer;
template <class Req, class Res>
class HttpConnection
{
friend class HttpServer<Req, Res>;
using server_t = HttpServer<Req, Res>;
using connection_t = HttpConnection<Req, Res>;
using parser_t = HttpParser<Req, Res>;
public:
HttpConnection(uv::UvLoop& loop, HttpServer& server);
HttpConnection(uv::UvLoop& loop, HttpServer<Req, Res>& server);
void close();
HttpServer& server;
server_t& server;
uv::TcpStream client;
HttpParser parser;
parser_t parser;
Request request;
Response response;
Req request;
Res response;
bool keep_alive;
};

View File

@ -8,17 +8,19 @@
namespace http
{
HttpConnection::HttpConnection(uv::UvLoop& loop, HttpServer& server)
template <class Req, class Res>
HttpConnection<Req, Res>::HttpConnection(uv::UvLoop& loop, server_t& server)
: server(server), client(loop), response(*this)
{
client.data(this);
parser.data(this);
}
void HttpConnection::close()
template <class Req, class Res>
void HttpConnection<Req, Res>::close()
{
client.close([](uv_handle_t* client) -> void {
delete reinterpret_cast<HttpConnection*>(client->data);
delete reinterpret_cast<connection_t*>(client->data);
});
}

View File

@ -9,19 +9,25 @@
namespace http
{
template <class Req, class Res>
class HttpConnection;
template <class Req, class Res>
class HttpParserSettings;
template <class Req, class Res>
class HttpParser
{
friend class HttpParserSettings;
using connection_t = HttpConnection<Req, Res>;
using settings_t = HttpParserSettings<Req, Res>;
friend class HttpParserSettings<Req, Res>;
public:
HttpParser();
static void init();
size_t execute(HttpParserSettings& settings,
const char* data,
size_t size);
size_t execute(settings_t& settings, const char* data, size_t size);
template <typename T>
T* data();

View File

@ -9,73 +9,80 @@
namespace http
{
HttpParser::HttpParser()
template <class Req, class Res>
HttpParser<Req, Res>::HttpParser()
{
http_parser_init(&parser, HTTP_REQUEST);
}
size_t HttpParser::execute(HttpParserSettings& settings,
const char* data,
size_t size)
template <class Req, class Res>
size_t HttpParser<Req, Res>::
execute(settings_t& settings, const char* data, size_t size)
{
return http_parser_execute(&parser, settings, data, size);
}
template <class Req, class Res>
template <typename T>
T* HttpParser::data()
T* HttpParser<Req, Res>::data()
{
return reinterpret_cast<T*>(parser.data);
}
template <class Req, class Res>
template <typename T>
void HttpParser::data(T* value)
void HttpParser<Req, Res>::data(T* value)
{
parser.data = reinterpret_cast<void*>(value);
}
int HttpParser::on_message_begin(http_parser*)
template <class Req, class Res>
int HttpParser<Req, Res>::on_message_begin(http_parser*)
{
return 0;
}
int HttpParser::on_url(http_parser* parser,
const char* at,
size_t length)
template <class Req, class Res>
int HttpParser<Req, Res>::
on_url(http_parser* parser, const char* at, size_t length)
{
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.request.url = std::string(at, length);
return 0;
}
int HttpParser::on_status_complete(http_parser*, const char*, size_t)
template <class Req, class Res>
int HttpParser<Req, Res>::
on_status_complete(http_parser*, const char*, size_t)
{
return 0;
}
int HttpParser::on_header_field(http_parser* parser,
const char* at,
size_t length)
template <class Req, class Res>
int HttpParser<Req, Res>::
on_header_field(http_parser* parser, const char* at, size_t length)
{
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.parser.last_field = std::string(at, length);
return 0;
}
int HttpParser::on_header_value(http_parser* parser,
const char* at,
size_t length)
template <class Req, class Res>
int HttpParser<Req, Res>::
on_header_value(http_parser* parser, const char* at, size_t length)
{
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.request.headers[conn.parser.last_field] = std::string(at, length);
return 0;
}
int HttpParser::on_headers_complete(http_parser* parser)
template <class Req, class Res>
int HttpParser<Req, Res>::on_headers_complete(http_parser* parser)
{
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.request.version.major = parser->http_major;
conn.request.version.minor = parser->http_minor;
@ -86,20 +93,23 @@ int HttpParser::on_headers_complete(http_parser* parser)
return 0;
}
int HttpParser::on_body(http_parser* parser, const char* at, size_t length)
template <class Req, class Res>
int HttpParser<Req, Res>::
on_body(http_parser* parser, const char* at, size_t length)
{
if(length == 0)
return 0;
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.request.body.append(at, length);
return 0;
}
int HttpParser::on_message_complete(http_parser* parser)
template <class Req, class Res>
int HttpParser<Req, Res>::on_message_complete(http_parser* parser)
{
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
conn.server.respond(conn);
return 0;

View File

@ -6,6 +6,7 @@
namespace http
{
template <class Req, class Res>
class HttpParserSettings
{
public:

View File

@ -7,19 +7,21 @@
namespace http
{
HttpParserSettings::HttpParserSettings()
template <class Req, class Res>
HttpParserSettings<Req, Res>::HttpParserSettings()
{
settings.on_header_field = HttpParser::on_header_field;
settings.on_header_value = HttpParser::on_header_value;
settings.on_headers_complete = HttpParser::on_headers_complete;
settings.on_body = HttpParser::on_body;
settings.on_status = HttpParser::on_status_complete;
settings.on_message_begin = HttpParser::on_message_begin;
settings.on_message_complete = HttpParser::on_message_complete;
settings.on_url = HttpParser::on_url;
settings.on_header_field = HttpParser<Req, Res>::on_header_field;
settings.on_header_value = HttpParser<Req, Res>::on_header_value;
settings.on_headers_complete = HttpParser<Req, Res>::on_headers_complete;
settings.on_body = HttpParser<Req, Res>::on_body;
settings.on_status = HttpParser<Req, Res>::on_status_complete;
settings.on_message_begin = HttpParser<Req, Res>::on_message_begin;
settings.on_message_complete = HttpParser<Req, Res>::on_message_complete;
settings.on_url = HttpParser<Req, Res>::on_url;
}
HttpParserSettings::operator http_parser_settings*()
template <class Req, class Res>
HttpParserSettings<Req, Res>::operator http_parser_settings*()
{
return &settings;
}

View File

@ -11,14 +11,17 @@
#include "response.hpp"
#include "ipv4.hpp"
#include "utils/memory/block_allocator.hpp"
namespace http
{
typedef std::function<void(Request&, Response&)> request_cb_t;
template <class Req, class Res>
class HttpServer
{
friend class HttpParser;
public:
using request_cb_t = std::function<void(Req&, Res&)>;
HttpServer(uv::UvLoop& loop);
void listen(const Ipv4& ip, request_cb_t callback);
@ -27,9 +30,14 @@ public:
operator uv_stream_t*();
private:
using server_t = HttpServer<Req, Res>;
using connection_t = HttpConnection<Req, Res>;
friend class HttpParser<Req, Res>;
uv::UvLoop& loop;
uv::TcpStream stream;
HttpParserSettings settings;
HttpParserSettings<Req, Res> settings;
request_cb_t request_cb;
@ -43,7 +51,7 @@ private:
size_t suggested_size,
uv_buf_t* buf);
static void respond(HttpConnection& conn);
static void respond(HttpConnection<Req, Res>& conn);
};
}

View File

@ -6,63 +6,71 @@
namespace http
{
HttpServer::HttpServer(uv::UvLoop& loop)
static BlockAllocator<65536> buffer_allocator;
template <class Req, class Res>
HttpServer<Req, Res>::HttpServer(uv::UvLoop& loop)
: loop(loop), stream(loop) {}
void HttpServer::listen(const Ipv4& ip, request_cb_t callback)
template <class Req, class Res>
void HttpServer<Req, Res>::listen(const Ipv4& ip, request_cb_t callback)
{
request_cb = callback;
stream.data(this);
uv_tcp_bind(stream, ip, 0);
uv_listen(stream, 128, HttpServer::on_connect);
uv_listen(stream, 128, server_t::on_connect);
}
HttpServer::operator uv_tcp_t*()
template <class Req, class Res>
HttpServer<Req, Res>::operator uv_tcp_t*()
{
return stream;
}
HttpServer::operator uv_stream_t*()
template <class Req, class Res>
HttpServer<Req, Res>::operator uv_stream_t*()
{
return stream;
}
void HttpServer::on_connect(uv_stream_t* tcp_server, int)
template <class Req, class Res>
void HttpServer<Req, Res>::on_connect(uv_stream_t* tcp_server, int)
{
HttpServer& server = *reinterpret_cast<HttpServer*>(tcp_server->data);
server_t& server = *reinterpret_cast<server_t*>(tcp_server->data);
auto connection = new HttpConnection(server.loop, server);
auto connection = new connection_t(server.loop, server);
uv_accept(server, connection->client);
uv_read_start(connection->client,
HttpServer::on_alloc,
HttpServer::on_read);
uv_read_start(connection->client, server_t::on_alloc, server_t::on_read);
}
void HttpServer::on_alloc(uv_handle_t*, size_t suggested_size, uv_buf_t* buf)
template <class Req, class Res>
void HttpServer<Req, Res>::
on_alloc(uv_handle_t*, size_t, uv_buf_t* buf)
{
buf->base = static_cast<char*>(malloc(sizeof(char) * suggested_size));
buf->len = suggested_size;
buf->base = static_cast<char*>(buffer_allocator.acquire());
buf->len = buffer_allocator.size;
}
void HttpServer::on_read(uv_stream_t* tcp_client,
ssize_t nread,
const uv_buf_t* buf)
template <class Req, class Res>
void HttpServer<Req, Res>::
on_read(uv_stream_t* tcp_client, ssize_t nread, const uv_buf_t* buf)
{
HttpConnection& conn =
*reinterpret_cast<HttpConnection*>(tcp_client->data);
connection_t& conn = *reinterpret_cast<connection_t*>(tcp_client->data);
if(nread >= 0)
conn.parser.execute(conn.server.settings, buf->base, nread);
else
conn.close();
delete buf->base;
buffer_allocator.release(buf->base);
//delete buf->base;
}
void HttpServer::respond(HttpConnection& conn)
template <class Req, class Res>
void HttpServer<Req, Res>::respond(connection_t& conn)
{
conn.server.request_cb(conn.request, conn.response);
}

View File

@ -16,7 +16,9 @@ struct Request
Method method;
std::string url;
std::map<std::string, std::string> headers;
std::string body;
};

View File

@ -9,25 +9,28 @@
namespace http
{
template <class Req, class Res>
class HttpConnection;
template <class Req, class Res>
class Response
{
using connection_t = HttpConnection<Req, Res>;
using response_t = Response<Req, Res>;
public:
Response(HttpConnection& connection);
Response(connection_t& connection);
void send(const std::string& body);
void send(Status code, const std::string& body);
Response& status(Status code);
std::map<std::string, std::string> headers;
private:
HttpConnection& connection;
uv::UvBuffer buffer;
Status status;
Status code;
private:
connection_t& connection;
uv::UvBuffer buffer;
};
}

View File

@ -4,59 +4,62 @@
#include "response.hpp"
#include "httpconnection.hpp"
#include "utils/memory/block_allocator.hpp"
namespace http
{
Response::Response(HttpConnection& connection)
: connection(connection), buffer(65536), code(Status::Ok) {}
constexpr size_t buffer_size = 65536;
void Response::send(Status code, const std::string& body)
static BlockAllocator<sizeof(uv_write_t)> write_req_allocator;
template <class Req, class Res>
Response<Req, Res>::Response(connection_t& connection)
: status(Status::Ok), connection(connection),
buffer(buffer_size) {}
template <class Req, class Res>
void Response<Req, Res>::send(Status status, const std::string& body)
{
this->status(code).send(body);
this->status = status;
this->send(body);
}
void Response::send(const std::string& body)
template <class Req, class Res>
void Response<Req, Res>::send(const std::string& body)
{
uv_write_t* write_req =
static_cast<uv_write_t*>(malloc(sizeof(uv_write_t)));
static_cast<uv_write_t*>(write_req_allocator.acquire());
write_req->data = &connection;
// set the appropriate content length
headers["Content-Length"] = std::to_string(body.size());
buffer << "HTTP/1.1 " << to_string[status] << "\r\n";
// set the appropriate connection type
headers["Connection"] = connection.keep_alive ? "Keep-Alive" : "Close";
buffer << "Content-Length:" << std::to_string(body.size()) << "\r\n";
buffer << "HTTP/1.1 " << to_string[code] << "\r\n";
buffer << "Connection:" << (connection.keep_alive ? "Keep-Alive" : "Close")
<< "\r\n";
for(auto it = headers.begin(); it != headers.end(); ++it)
buffer << it->first << ":" << it->second << "\r\n";
buffer << "\r\n" << body;
uv_write(write_req, connection.client, buffer, 1,
uv_write(write_req, connection.client, buffer, 1,
[](uv_write_t* write_req, int) {
HttpConnection& conn =
*reinterpret_cast<HttpConnection*>(write_req->data);
connection_t& conn = *reinterpret_cast<connection_t*>(write_req->data);
if(!conn.keep_alive)
conn.close();
conn.response.code = Status::Ok;
conn.response.status = Status::Ok;
conn.response.buffer.clear();
conn.response.headers.clear();
std::free(write_req);
write_req_allocator.release(write_req);
});
}
Response& Response::status(Status code)
{
this->code = code;
return *this;
}
}
#endif

View File

@ -4,15 +4,25 @@
#include <list>
#include <cassert>
#include "request.hpp"
#include "response.hpp"
#include "utils/auto_scope.hpp"
#include "http/http.hpp"
namespace r3 {
#include "r3_include.h"
}
namespace speedy
namespace sp
{
class R3Error : public std::runtime_error
{
public:
using runtime_error::runtime_error;
};
class R3
{
public:
@ -73,11 +83,11 @@ public:
return route != nullptr;
}
void operator()(http::Request& req, http::Response& res)
void operator()(sp::Request& req, sp::Response& res)
{
assert(route != nullptr);
auto cb = reinterpret_cast<http::request_cb_t*>(route->data);
auto cb = reinterpret_cast<sp::request_cb_t*>(route->data);
cb->operator()(req, res);
}
@ -104,7 +114,7 @@ public:
other.root = nullptr;
}
void insert(Method method, const std::string& path, http::request_cb_t cb)
void insert(Method method, const std::string& path, sp::request_cb_t cb)
{
routes.push_back(cb);
@ -114,12 +124,41 @@ public:
Route match(Method method, const std::string& path)
{
auto entry = MatchEntry(method, path);
return Route(r3::r3_tree_match_route(root, entry));
auto entry_m = MatchEntry(method, path);
return Route(r3::r3_tree_match_route(root, entry_m));
/* if(!route.exists()) */
/* return route; */
/* r3::match_entry* entry = entry_m; */
/* std::cout << "Eetry Matched!" << std::endl */
/* << "path = " << std::string(entry->path, entry->path_len) << std::endl */
/* << "host = " << std::string(entry->host, entry->host_len) << std::endl */
/* << "remote_addr = " << std::string(entry->remote_addr, entry->remote_addr_len) << std::endl */
/* << "tokens_len = " << entry->vars->len << std::endl; */
/* for(int i = 0; i < entry->vars->len; ++i) */
/* std::cout << "token " << i << " = " << std::string(entry->vars->tokens[i]) << std::endl; */
//return route;
}
void compile()
{
char* error_string = nullptr;
bool error = r3_tree_compile(root, &error_string);
if(!error)
return;
Auto(free(error_string));
throw R3Error(std::string(error_string));
}
private:
std::list<http::request_cb_t> routes;
std::list<sp::request_cb_t> routes;
r3::node* root;
};

17
speedy/request.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef MEMGRAPH_SPEEDY_REQUEST_HPP
#define MEMGRAPH_SPEEDY_REQUEST_HPP
#include "http/request.hpp"
namespace sp
{
class Request : public http::Request
{
public:
using http::Request::Request;
};
}
#endif

20
speedy/response.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef MEMGRAPH_SPEEDY_RESPONSE_HPP
#define MEMGRAPH_SPEEDY_RESPONSE_HPP
#include "request.hpp"
#include "http/response.hpp"
namespace sp
{
class Response : public http::Response<Request, Response>
{
public:
using http::Response<Request, Response>::Response;
};
using request_cb_t = std::function<void(Request&, Response&)>;
}
#endif

View File

@ -14,13 +14,17 @@
#include "http/http.hpp"
#include "r3.hpp"
namespace speedy
#include "request.hpp"
#include "response.hpp"
namespace sp
{
class Speedy
{
public:
using sptr = std::shared_ptr<Speedy>;
using server_t = http::HttpServer<Request, Response>;
using request_cb_t = server_t::request_cb_t;
Speedy(uv::UvLoop& loop, size_t capacity = 100)
: server(loop), router(capacity) {}
@ -28,29 +32,33 @@ public:
Speedy(Speedy&) = delete;
Speedy(Speedy&&) = delete;
void get(const std::string& path, http::request_cb_t cb)
void get(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::GET, path, cb);
}
void post(const std::string& path, http::request_cb_t cb)
void post(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::POST, path, cb);
}
void put(const std::string& path, http::request_cb_t cb)
void put(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::PUT, path, cb);
}
void del(const std::string& path, http::request_cb_t cb)
void del(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::DELETE, path, cb);
}
void listen(const http::Ipv4& ip)
{
server.listen(ip, [this](http::Request& req, http::Response& res) {
router.compile();
server.listen(ip, [this](Request& req, Response& res) {
return res.send("Hello World");
auto route = router.match(R3::to_r3_method(req.method), req.url);
if(!route.exists())
@ -61,7 +69,7 @@ public:
}
private:
http::HttpServer server;
server_t server;
R3 router;
};

View File

@ -1,50 +0,0 @@
#include <iostream>
#include "data_structures/skiplist/skiplist.hpp"
int main(void)
{
xorshift::init();
SkipList<int, int> skiplist;
int k1 = 1;
int i1 = 10;
int k2 = 2;
int i2 = 20;
std::cout << "find: " << k1 << " => " << (skiplist.find(&k1) == nullptr) << std::endl;
std::cout << skiplist.insert(&k1, &i1) << std::endl;
std::cout << "find: " << k1 << " => " << *skiplist.find(&k1)->item << std::endl;
std::cout << "find: " << k2 << " => " << (skiplist.find(&k2) == nullptr) << std::endl;
std::cout << skiplist.insert(&k2, &i2) << std::endl;
std::cout << skiplist.insert(&k2, &i2) << std::endl;
std::cout << skiplist.insert(&k1, &i1) << std::endl;
std::cout << skiplist.insert(&k2, &i2) << std::endl;
std::cout << "DELETE K1 " << skiplist.remove(&k1) << std::endl;
std::cout << "find: " << k1 << " => " << (skiplist.find(&k1) == nullptr) << std::endl;
std::cout << "find: " << k2 << " => " << *skiplist.find(&k2)->item << std::endl;
std::cout << skiplist.size() << std::endl;
auto* node = skiplist.header.load(std::memory_order_consume);
for(size_t i = 0; i < skiplist.size(); ++i)
{
node = node->forward(0);
if(node == nullptr)
std::cout << "node == nullptr" << std::endl;
if(node->key == nullptr)
std::cout << "node->key == nullptr" << std::endl;
if(node->item == nullptr)
std::cout << "node->item == nullptr" << std::endl;
std::cout << *node->key << " => " << *node->item << std::endl;
}
return 0;
}

60
utils/auto_scope.hpp Normal file
View File

@ -0,0 +1,60 @@
#ifndef MEMGRAPH_UTILS_AUTO_SCOPE_HPP
#define MEMGRAPH_UTILS_AUTO_SCOPE_HPP
#include <utility>
/* @brief Calls a cleanup function on scope exit
*
* consider this example:
*
* void hard_worker()
* {
* resource.enable();
* do_stuff(); // throws exception
* resource.disable();
* }
*
* if do_stuff throws an exception, resource.disable is never called
* and the app is left in an inconsistent state. ideally, you would like
* to call resource.disable regardles of the exception being thrown.
* OnScopeExit makes this possible and very convenient via a 'Auto' macro
*
* void hard_worker()
* {
* resource.enable();
* Auto(resource.disable());
* do_stuff(); // throws exception
* }
*
* now, resource.disable will be called every time it goes out of scope
* regardless of the exception
*
* @tparam F Lambda which holds a wrapper function around the cleanup code
*/
template <class F>
class OnScopeExit
{
public:
OnScopeExit(F& f) : f(f) {}
~OnScopeExit()
{
// calls the cleanup function
f();
}
private:
F& f;
};
#define TOKEN_PASTEx(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTEx(x, y)
#define Auto_INTERNAL(Destructor, counter) \
auto TOKEN_PASTE(auto_func_, counter) = [&]() { Destructor; }; \
OnScopeExit<decltype(TOKEN_PASTE(auto_func_, counter))> \
TOKEN_PASTE(auto_, counter)(TOKEN_PASTE(auto_func_, counter));
#define Auto(Destructor) Auto_INTERNAL(Destructor, __COUNTER__)
#endif

View File

@ -0,0 +1,54 @@
#ifndef MEMGRAPH_UTILS_MEMORY_BLOCK_ALLOCATOR_HPP
#define MEMGRAPH_UTILS_MEMORY_BLOCK_ALLOCATOR_HPP
#include <memory>
#include <vector>
#include "utils/auto_scope.hpp"
template <size_t block_size>
class BlockAllocator
{
struct Block
{
Block()
{
data = malloc(block_size);
}
Block(void* ptr)
{
data = ptr;
}
void* data;
};
public:
static constexpr size_t size = block_size;
BlockAllocator(size_t capacity = 0)
{
for(size_t i = 0; i < capacity; ++i)
blocks.emplace_back();
}
void* acquire()
{
if(blocks.size() == 0)
blocks.emplace_back();
Auto(blocks.pop_back());
return blocks.back().data;
}
void release(void* ptr)
{
blocks.emplace_back(ptr);
}
private:
std::vector<Block> blocks;
};
#endif