Merge branch 'master' of https://phabricator.tomicevic.com/diffusion/MG/memgraph
* 'master' of https://phabricator.tomicevic.com/diffusion/MG/memgraph: speedy: first let say usefull implementation, GET, POST, PUT, DELETE methods are implemented r3 library is included, route registration still doesn't work because of some reference issue, TODO: do a better implementation
This commit is contained in:
commit
43e975d3cc
3
speedy/.gitignore
vendored
3
speedy/.gitignore
vendored
@ -1 +1,4 @@
|
||||
*.out
|
||||
*.dSYM/
|
||||
*.gdb
|
||||
.gdb_history
|
||||
|
@ -1,7 +1,8 @@
|
||||
CXX=clang++
|
||||
CFLAGS=-std=c++11 -Wall
|
||||
LDFLAGS=-luv -lhttp_parser
|
||||
INC=-I../
|
||||
LDFLAGS=-luv -lhttp_parser -lr3
|
||||
# debug only
|
||||
INC=-I../ -g -O0 -fno-inline-functions
|
||||
SOURCES=$(wildcard *.cpp)
|
||||
EXECUTABLE=test.out
|
||||
# OBJECTS=$(SOURCES:.cpp=.o)
|
||||
|
@ -21,5 +21,11 @@ make install
|
||||
* https://github.com/c9s/r3
|
||||
|
||||
```
|
||||
brew install r3 (OSX)
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## NOTE
|
||||
r3_include.h is custom r3 header file because of compilation problem related to redefinition of bool
|
||||
|
228
speedy/r3_include.h
Normal file
228
speedy/r3_include.h
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* r3.h
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef R3_NODE_H
|
||||
#define R3_NODE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pcre.h>
|
||||
#include <stdbool.h>
|
||||
#include <r3/str_array.h>
|
||||
#include <r3/r3_str.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct _edge;
|
||||
struct _node;
|
||||
struct _route;
|
||||
typedef struct _edge edge;
|
||||
typedef struct _node node;
|
||||
typedef struct _route route;
|
||||
|
||||
struct _node {
|
||||
edge ** edges;
|
||||
// edge ** edge_table;
|
||||
|
||||
// edges are mostly less than 255
|
||||
unsigned char compare_type; // compare_type: pcre, opcode, string
|
||||
unsigned char edge_len;
|
||||
unsigned char endpoint; // endpoint, should be zero for non-endpoint nodes
|
||||
unsigned char ov_cnt; // capture vector array size for pcre
|
||||
|
||||
// almost less than 255
|
||||
unsigned char edge_cap;
|
||||
unsigned char route_len;
|
||||
unsigned char route_cap;
|
||||
// <-- here comes a char[1] struct padding for alignment since we have 4 char above.
|
||||
|
||||
|
||||
/** compile-time variables here.... **/
|
||||
|
||||
/* the combined regexp pattern string from pattern_tokens */
|
||||
pcre * pcre_pattern;
|
||||
pcre_extra * pcre_extra;
|
||||
|
||||
route ** routes;
|
||||
|
||||
char * combined_pattern;
|
||||
|
||||
/**
|
||||
* the pointer of route data
|
||||
*/
|
||||
void * data;
|
||||
};
|
||||
|
||||
#define r3_node_edge_pattern(node,i) node->edges[i]->pattern
|
||||
#define r3_node_edge_pattern_len(node,i) node->edges[i]->pattern_len
|
||||
|
||||
struct _edge {
|
||||
char * pattern; // 8 bytes
|
||||
node * child; // 8 bytes
|
||||
unsigned char pattern_len; // 1 byte
|
||||
unsigned char opcode:4; // 4 bit
|
||||
unsigned char has_slug:1; // 1 bit
|
||||
};
|
||||
|
||||
struct _route {
|
||||
char * path;
|
||||
int path_len;
|
||||
|
||||
int request_method; // can be (GET || POST)
|
||||
|
||||
char * host; // required host name
|
||||
int host_len;
|
||||
|
||||
void * data;
|
||||
|
||||
char * remote_addr_pattern;
|
||||
int remote_addr_pattern_len;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
str_array * vars;
|
||||
const char * path; // current path to dispatch
|
||||
int path_len; // the length of the current path
|
||||
int request_method; // current request method
|
||||
|
||||
void * data; // route ptr
|
||||
|
||||
char * host; // the request host
|
||||
int host_len;
|
||||
|
||||
char * remote_addr;
|
||||
int remote_addr_len;
|
||||
} match_entry;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
node * r3_tree_create(int cap);
|
||||
|
||||
node * r3_node_create();
|
||||
|
||||
void r3_tree_free(node * tree);
|
||||
|
||||
edge * r3_node_connectl(node * n, const char * pat, int len, int strdup, node *child);
|
||||
|
||||
#define r3_node_connect(n, pat, child) r3_node_connectl(n, pat, strlen(pat), 0, child)
|
||||
|
||||
edge * r3_node_find_edge(const node * n, const char * pat, int pat_len);
|
||||
|
||||
void r3_node_append_edge(node *n, edge *child);
|
||||
|
||||
|
||||
edge * r3_node_find_common_prefix(node *n, const char *path, int path_len, int *prefix_len, char **errstr);
|
||||
|
||||
node * r3_tree_insert_pathl(node *tree, const char *path, int path_len, void * data);
|
||||
|
||||
#define r3_tree_insert_pathl(tree, path, path_len, data) r3_tree_insert_pathl_ex(tree, path, path_len, NULL , data, NULL)
|
||||
|
||||
|
||||
|
||||
route * r3_tree_insert_routel(node *tree, int method, const char *path, int path_len, void *data);
|
||||
|
||||
route * r3_tree_insert_routel_ex(node *tree, int method, const char *path, int path_len, void *data, char **errstr);
|
||||
|
||||
#define r3_tree_insert_routel(n, method, path, path_len, data) r3_tree_insert_routel_ex(n, method, path, path_len, data, NULL)
|
||||
|
||||
#define r3_tree_insert_path(n,p,d) r3_tree_insert_pathl_ex(n,p,strlen(p), NULL, d, NULL)
|
||||
|
||||
#define r3_tree_insert_route(n,method,path,data) r3_tree_insert_routel(n, method, path, strlen(path), data)
|
||||
|
||||
|
||||
/**
|
||||
* The private API to insert a path
|
||||
*/
|
||||
node * r3_tree_insert_pathl_ex(node *tree, const char *path, int path_len, route * route, void * data, char ** errstr);
|
||||
|
||||
void r3_tree_dump(const node * n, int level);
|
||||
|
||||
|
||||
edge * r3_node_find_edge_str(const node * n, const char * str, int str_len);
|
||||
|
||||
|
||||
int r3_tree_compile(node *n, char** errstr);
|
||||
|
||||
int r3_tree_compile_patterns(node * n, char** errstr);
|
||||
|
||||
node * r3_tree_matchl(const node * n, const char * path, int path_len, match_entry * entry);
|
||||
|
||||
#define r3_tree_match(n,p,e) r3_tree_matchl(n,p, strlen(p), e)
|
||||
|
||||
// node * r3_tree_match_entry(node * n, match_entry * entry);
|
||||
#define r3_tree_match_entry(n, entry) r3_tree_matchl(n, entry->path, entry->path_len, entry)
|
||||
|
||||
bool r3_node_has_slug_edges(const node *n);
|
||||
|
||||
edge * r3_edge_createl(const char * pattern, int pattern_len, node * child);
|
||||
|
||||
node * r3_edge_branch(edge *e, int dl);
|
||||
|
||||
void r3_edge_free(edge * edge);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
route * r3_route_create(const char * path);
|
||||
|
||||
route * r3_route_createl(const char * path, int path_len);
|
||||
|
||||
|
||||
void r3_node_append_route(node * n, route * route);
|
||||
|
||||
void r3_route_free(route * route);
|
||||
|
||||
int r3_route_cmp(const route *r1, const match_entry *r2);
|
||||
|
||||
route * r3_tree_match_route(const node *n, match_entry * entry);
|
||||
|
||||
#define r3_route_create(p) r3_route_createl(p, strlen(p))
|
||||
|
||||
|
||||
#define METHOD_GET 2
|
||||
#define METHOD_POST 2<<1
|
||||
#define METHOD_PUT 2<<2
|
||||
#define METHOD_DELETE 2<<3
|
||||
#define METHOD_PATCH 2<<4
|
||||
#define METHOD_HEAD 2<<5
|
||||
#define METHOD_OPTIONS 2<<6
|
||||
|
||||
|
||||
|
||||
int r3_pattern_to_opcode(const char * pattern, int pattern_len);
|
||||
|
||||
enum { NODE_COMPARE_STR, NODE_COMPARE_PCRE, NODE_COMPARE_OPCODE };
|
||||
|
||||
enum { OP_EXPECT_MORE_DIGITS = 1, OP_EXPECT_MORE_WORDS, OP_EXPECT_NOSLASH, OP_EXPECT_NODASH, OP_EXPECT_MORE_ALPHA };
|
||||
|
||||
|
||||
|
||||
match_entry * match_entry_createl(const char * path, int path_len);
|
||||
|
||||
#define match_entry_create(path) match_entry_createl(path,strlen(path))
|
||||
|
||||
void match_entry_free(match_entry * entry);
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* !R3_NODE_H */
|
@ -1,22 +1,32 @@
|
||||
#ifndef MEMGRAPH_SPEEDY_HPP
|
||||
#define MEMGRAPH_SPEEDY_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "io/uv/uv.hpp"
|
||||
#include "http/http.hpp"
|
||||
#include "r3_include.h"
|
||||
|
||||
namespace speedy
|
||||
{
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
class Speedy
|
||||
{
|
||||
private:
|
||||
http::HttpServer server;
|
||||
http::Ipv4 ip;
|
||||
node *n;
|
||||
std::vector<http::request_cb_t> callbacks;
|
||||
void store_index(int method, const std::string &path);
|
||||
public:
|
||||
Speedy(uv::UvLoop& loop, const http::Ipv4& ip);
|
||||
void get(const std::string path, http::request_cb_t callback);
|
||||
void get(const std::string &path, http::request_cb_t callback);
|
||||
void post(const std::string &path, http::request_cb_t callback);
|
||||
void put(const std::string &path, http::request_cb_t callback);
|
||||
void del(const std::string &path, http::request_cb_t callback);
|
||||
void listen();
|
||||
~Speedy();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,31 +2,94 @@
|
||||
#define MEMGRAPH_SPEEDY_INL
|
||||
|
||||
#include "speedy.hpp"
|
||||
#include <http_parser.h>
|
||||
|
||||
namespace speedy
|
||||
{
|
||||
|
||||
Speedy::Speedy(uv::UvLoop& loop, const http::Ipv4& ip) : server(loop), ip(ip)
|
||||
int r3_request_method(http::Method method)
|
||||
{
|
||||
switch (method) {
|
||||
case http::Method::GET: return METHOD_GET;
|
||||
case http::Method::POST: return METHOD_POST;
|
||||
case http::Method::PUT: return METHOD_PUT;
|
||||
case http::Method::DELETE: return METHOD_DELETE;
|
||||
case http::Method::HEAD: return METHOD_HEAD;
|
||||
}
|
||||
}
|
||||
|
||||
void Speedy::get(const std::string path, http::request_cb_t callback)
|
||||
// TODO: better implementation
|
||||
|
||||
Speedy::Speedy(uv::UvLoop& loop, const http::Ipv4& ip) : server(loop), ip(ip)
|
||||
{
|
||||
n = r3_tree_create(100);
|
||||
}
|
||||
|
||||
void Speedy::store_index(int method, const std::string &path)
|
||||
{
|
||||
void *ptr = malloc(sizeof(uint));
|
||||
*((uint *)ptr) = callbacks.size() - 1;
|
||||
r3_tree_insert_routel(n, method, path.c_str(), path.size(), ptr);
|
||||
}
|
||||
|
||||
void Speedy::get(const std::string &path, http::request_cb_t callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
store_index(METHOD_GET, path);
|
||||
|
||||
// TODO: something like this
|
||||
// this solution doesn't work, currenlty I don't know why
|
||||
// r3_tree_insert_pathl(n, path.c_str(), path.size(), &callbacks.back());
|
||||
}
|
||||
|
||||
void Speedy::post(const std::string &path, http::request_cb_t callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
store_index(METHOD_POST, path);
|
||||
}
|
||||
|
||||
void Speedy::put(const std::string &path, http::request_cb_t callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
store_index(METHOD_PUT, path);
|
||||
}
|
||||
|
||||
void Speedy::del(const std::string &path, http::request_cb_t callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
store_index(METHOD_DELETE, path);
|
||||
}
|
||||
|
||||
void Speedy::listen()
|
||||
{
|
||||
server.listen(ip, [](http::Request& req, http::Response& res) {
|
||||
res.send(req.url);
|
||||
char *errstr = NULL;
|
||||
int err = r3_tree_compile(n, &errstr);
|
||||
if (err) {
|
||||
std::cout << "R3 compile error" << std::endl;
|
||||
}
|
||||
|
||||
server.listen(ip, [this](http::Request& req, http::Response& res) {
|
||||
auto url = req.url;
|
||||
auto c_url = url.c_str();
|
||||
match_entry *entry = match_entry_create(c_url);
|
||||
entry->request_method = r3_request_method(req.method);
|
||||
route *r = r3_tree_match_route(this->n, entry);
|
||||
match_entry_free(entry);
|
||||
if (r) {
|
||||
int index = *((int *)r->data);
|
||||
auto callback = this->callbacks[index];
|
||||
callback(req, res);
|
||||
// TODO: and something like this
|
||||
// auto callback = *reinterpret_cast<http::request_cb_t*>(n->data);
|
||||
// callback(req, res);
|
||||
} else {
|
||||
res.send("Not found");
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "Server is UP" << std::endl;
|
||||
}
|
||||
|
||||
Speedy::~Speedy()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -2,14 +2,65 @@
|
||||
|
||||
#include "speedy.hpp"
|
||||
|
||||
const char *test_url_1 = "/test1";
|
||||
const char *test_url_2 = "/test2";
|
||||
const char *test_url_3 = "/test3";
|
||||
const char *test_response = "test";
|
||||
|
||||
void test_get(const http::request_cb_t &&callback, speedy::Speedy &app) {
|
||||
app.get(test_url_3, callback);
|
||||
}
|
||||
|
||||
void test_post(const http::request_cb_t &&callback, speedy::Speedy &app) {
|
||||
app.post(test_url_3, callback);
|
||||
}
|
||||
|
||||
void test_put(const http::request_cb_t &&callback, speedy::Speedy &app) {
|
||||
app.put(test_url_3, callback);
|
||||
}
|
||||
|
||||
void test_delete(const http::request_cb_t &&callback, speedy::Speedy &app) {
|
||||
app.del(test_url_3, callback);
|
||||
}
|
||||
|
||||
auto test_callback = [](http::Request& req, http::Response& res) {
|
||||
res.send(test_response);
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// speedy init
|
||||
uv::UvLoop loop;
|
||||
http::Ipv4 ip("0.0.0.0", 3400);
|
||||
|
||||
speedy::Speedy app(loop, ip);
|
||||
app.listen();
|
||||
|
||||
// GET methods
|
||||
app.get(test_url_1, test_callback);
|
||||
app.get(test_url_2, [](http::Request& req, http::Response& res) {
|
||||
res.send(test_response);
|
||||
});
|
||||
test_get(test_callback, app);
|
||||
// POST examples
|
||||
app.post(test_url_1, test_callback);
|
||||
app.post(test_url_2, [](http::Request& req, http::Response& res) {
|
||||
res.send(test_response);
|
||||
});
|
||||
test_post(test_callback, app);
|
||||
// PUT examples
|
||||
app.put(test_url_1, test_callback);
|
||||
app.put(test_url_2, [](http::Request& req, http::Response& res) {
|
||||
res.send(test_response);
|
||||
});
|
||||
test_put(test_callback, app);
|
||||
// DELETE examples
|
||||
app.del(test_url_1, test_callback);
|
||||
app.del(test_url_2, [](http::Request& req, http::Response& res) {
|
||||
res.send(test_response);
|
||||
});
|
||||
test_delete(test_callback, app);
|
||||
|
||||
// app run
|
||||
app.listen();
|
||||
loop.run(uv::UvLoop::Mode::Default);
|
||||
|
||||
return 0;
|
||||
|
22
speedy/test.py
Normal file
22
speedy/test.py
Normal file
@ -0,0 +1,22 @@
|
||||
import requests
|
||||
|
||||
endpoint = 'http://localhost:3400/test%s'
|
||||
methods = [('GET', requests.get), ('POST', requests.post), ('PUT', requests.put), ('DELETE', requests.delete)]
|
||||
|
||||
print ''
|
||||
isAllFine = True
|
||||
for index in range(1, 4):
|
||||
for name, method in methods:
|
||||
url = endpoint % index
|
||||
r = method(url)
|
||||
if r.status_code == 200 and r.text == 'test':
|
||||
print name, url, 'PASS'
|
||||
else:
|
||||
print name, url, 'FAIL'
|
||||
isAllFine = False
|
||||
print ''
|
||||
if isAllFine:
|
||||
print 'Great. All tests have passed :)'
|
||||
else:
|
||||
print 'Fuckup. Something went wrong!'
|
||||
print ''
|
Loading…
Reference in New Issue
Block a user