DownloadProject,idea ignored
Summary: as above Test Plan: manual Reviewers: buda Reviewed By: buda Subscribers: buda Differential Revision: https://phabricator.memgraph.io/D36
This commit is contained in:
parent
d7de5cfe08
commit
f097b91430
3
.gitignore
vendored
3
.gitignore
vendored
@ -22,3 +22,6 @@ release/libs/
|
|||||||
release/barrier/
|
release/barrier/
|
||||||
release/barrier_*
|
release/barrier_*
|
||||||
build/compiled/
|
build/compiled/
|
||||||
|
cmake-build-*
|
||||||
|
.idea
|
||||||
|
cmake/DownloadProject/
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Subproject commit bbc6bef03d194cf75492ef4881156f472cb78858
|
|
6
src/speedy/.gitignore
vendored
6
src/speedy/.gitignore
vendored
@ -1,6 +0,0 @@
|
|||||||
*.out
|
|
||||||
*.dSYM/
|
|
||||||
*.gdb
|
|
||||||
.gdb_history
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
@ -1,21 +0,0 @@
|
|||||||
CXX=clang++
|
|
||||||
CFLAGS=-std=c++11 -Wall -O2
|
|
||||||
LDFLAGS=-luv -lhttp_parser ./r3/.libs/libr3.a -L/usr/local/lib -lpcre
|
|
||||||
# debug only
|
|
||||||
INC=-I../
|
|
||||||
SOURCES=$(wildcard *.cpp)
|
|
||||||
EXECUTABLE=test.out
|
|
||||||
# OBJECTS=$(SOURCES:.cpp=.o)
|
|
||||||
|
|
||||||
all: $(EXECUTABLE)
|
|
||||||
|
|
||||||
$(EXECUTABLE): $(SOURCES)
|
|
||||||
$(CXX) $(CFLAGS) $(SOURCES) -o $(EXECUTABLE) $(INC) $(LDFLAGS)
|
|
||||||
|
|
||||||
# .cpp.o:
|
|
||||||
# $(CXX) $(CFLAGS) $< -o $@ $(LDFLAGS) $(INC)
|
|
||||||
|
|
||||||
.PHONY:
|
|
||||||
clean:
|
|
||||||
rm -f *.out
|
|
||||||
rm -f *.o
|
|
@ -1,39 +0,0 @@
|
|||||||
## Dependencies
|
|
||||||
|
|
||||||
* https://github.com/libuv/libuv
|
|
||||||
|
|
||||||
```
|
|
||||||
sh autogen.sh
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
make check
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
* https://github.com/joyent/http-parser
|
|
||||||
|
|
||||||
```
|
|
||||||
make
|
|
||||||
make install -stdlib=libstdc++ -lstdc++ (OSX)
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
* https://github.com/c9s/r3 (git submodule)
|
|
||||||
|
|
||||||
```
|
|
||||||
./autogen.sh
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
* http://pcre.org/ (version 8.35, R3 dependency)
|
|
||||||
|
|
||||||
```
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## NOTE
|
|
||||||
r3_include.h is custom r3 header file because of compilation problem related to redefinition of bool
|
|
@ -1,2 +0,0 @@
|
|||||||
# dump of all commands used in development
|
|
||||||
clang++ -std=c++1y -g test_2.cpp -o test_2 -luv -lhttp_parser r3/.libs/libr3.a -lpcre -L/usr/local/lib -I../ -I./rapidjson/include
|
|
@ -1,17 +0,0 @@
|
|||||||
#include "httpconnection.hpp"
|
|
||||||
#include "method.hpp"
|
|
||||||
#include "httpparser.hpp"
|
|
||||||
#include "httpparsersettings.hpp"
|
|
||||||
#include "httpserver.hpp"
|
|
||||||
#include "version.hpp"
|
|
||||||
#include "ipv4.hpp"
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
#include "status_codes.hpp"
|
|
||||||
|
|
||||||
#include "httpconnection.inl"
|
|
||||||
#include "httpparser.inl"
|
|
||||||
#include "httpparsersettings.inl"
|
|
||||||
#include "httpserver.inl"
|
|
||||||
#include "ipv4.inl"
|
|
||||||
#include "response.inl"
|
|
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
class HttpError : public std::runtime_error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit HttpError(const std::string& message)
|
|
||||||
: std::runtime_error(message) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,43 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_CONNECTION_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_CONNECTION_HPP
|
|
||||||
|
|
||||||
#include "io/uv/uv.hpp"
|
|
||||||
|
|
||||||
#include "httpparser.hpp"
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.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<Req, Res>& server);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
server_t& server;
|
|
||||||
uv::TcpStream client;
|
|
||||||
|
|
||||||
parser_t parser;
|
|
||||||
|
|
||||||
Req request;
|
|
||||||
Res response;
|
|
||||||
|
|
||||||
bool keep_alive;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_CONNECTION_INL
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_CONNECTION_INL
|
|
||||||
|
|
||||||
#include <uv.h>
|
|
||||||
|
|
||||||
#include "httpconnection.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void HttpConnection<Req, Res>::close()
|
|
||||||
{
|
|
||||||
client.close([](uv_handle_t* client) -> void {
|
|
||||||
// terrible bug, this can happen even though a query is running and
|
|
||||||
// then the query is using memory which is deallocated
|
|
||||||
|
|
||||||
//delete reinterpret_cast<connection_t*>(client->data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,72 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSER_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSER_HPP
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <http_parser.h>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
class HttpConnection;
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
class HttpParserSettings;
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
class HttpParser
|
|
||||||
{
|
|
||||||
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(settings_t& settings, const char* data, size_t size);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T* data();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void data(T* value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
http_parser parser;
|
|
||||||
|
|
||||||
std::string last_field;
|
|
||||||
|
|
||||||
static int on_message_begin(http_parser* parser);
|
|
||||||
|
|
||||||
static int on_url(http_parser* parser,
|
|
||||||
const char* at,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
static int on_status_complete(http_parser* parser,
|
|
||||||
const char* at,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
static int on_header_field(http_parser* parser,
|
|
||||||
const char* at,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
static int on_header_value(http_parser* parser,
|
|
||||||
const char* at,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
static int on_headers_complete(http_parser* parser);
|
|
||||||
|
|
||||||
static int on_body(http_parser* parser,
|
|
||||||
const char* at,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
static int on_message_complete(http_parser* parser);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,120 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPSERVER_INL
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_HTTPSERVER_INL
|
|
||||||
|
|
||||||
#include "httpparser.hpp"
|
|
||||||
#include "httpserver.hpp"
|
|
||||||
#include "httpconnection.hpp"
|
|
||||||
#include "httpparsersettings.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpParser<Req, Res>::HttpParser()
|
|
||||||
{
|
|
||||||
http_parser_init(&parser, HTTP_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Req, Res>::data()
|
|
||||||
{
|
|
||||||
return reinterpret_cast<T*>(parser.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
template <typename T>
|
|
||||||
void HttpParser<Req, Res>::data(T* value)
|
|
||||||
{
|
|
||||||
parser.data = reinterpret_cast<void*>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::on_message_begin(http_parser*)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::
|
|
||||||
on_url(http_parser* parser, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
conn.request.url = std::string(at, length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::
|
|
||||||
on_status_complete(http_parser*, const char*, size_t)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::
|
|
||||||
on_header_field(http_parser* parser, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
conn.parser.last_field = std::string(at, length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::
|
|
||||||
on_header_value(http_parser* parser, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
conn.request.headers[conn.parser.last_field] = std::string(at, length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::on_headers_complete(http_parser* parser)
|
|
||||||
{
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
|
|
||||||
conn.request.version.major = parser->http_major;
|
|
||||||
conn.request.version.minor = parser->http_minor;
|
|
||||||
|
|
||||||
conn.request.method = static_cast<Method>(parser->method);
|
|
||||||
conn.keep_alive = http_should_keep_alive(parser) == true;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
conn.request.body.append(at, length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
int HttpParser<Req, Res>::on_message_complete(http_parser* parser)
|
|
||||||
{
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
|
||||||
conn.server.respond(conn);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,23 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_HPP
|
|
||||||
|
|
||||||
#include <http_parser.h>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
class HttpParserSettings
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpParserSettings();
|
|
||||||
|
|
||||||
operator http_parser_settings*();
|
|
||||||
|
|
||||||
private:
|
|
||||||
http_parser_settings settings;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_INL
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_INL
|
|
||||||
|
|
||||||
#include "httpparsersettings.hpp"
|
|
||||||
#include "httpparser.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpParserSettings<Req, Res>::HttpParserSettings()
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpParserSettings<Req, Res>::operator http_parser_settings*()
|
|
||||||
{
|
|
||||||
return &settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,59 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPSERVER_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_HTTPSERVER_HPP
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <uv.h>
|
|
||||||
|
|
||||||
#include "io/uv/uv.hpp"
|
|
||||||
#include "httpparsersettings.hpp"
|
|
||||||
#include "httpconnection.hpp"
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
#include "ipv4.hpp"
|
|
||||||
|
|
||||||
#include "utils/memory/block_allocator.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
class HttpServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using request_cb_t = std::function<void(Req&, Res&)>;
|
|
||||||
|
|
||||||
explicit HttpServer(uv::UvLoop& loop);
|
|
||||||
|
|
||||||
void listen(const Ipv4& ip, request_cb_t callback);
|
|
||||||
|
|
||||||
operator uv_tcp_t*();
|
|
||||||
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<Req, Res> settings;
|
|
||||||
|
|
||||||
request_cb_t request_cb;
|
|
||||||
|
|
||||||
static void on_connect(uv_stream_t* tcp_server, int status);
|
|
||||||
|
|
||||||
static void on_read(uv_stream_t* tcp_client,
|
|
||||||
ssize_t nread,
|
|
||||||
const uv_buf_t* buf);
|
|
||||||
|
|
||||||
static void on_alloc(uv_handle_t* tcp_client,
|
|
||||||
size_t suggested_size,
|
|
||||||
uv_buf_t* buf);
|
|
||||||
|
|
||||||
static void respond(HttpConnection<Req, Res>& conn);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,79 +0,0 @@
|
|||||||
#ifndef UVMACHINE_HTTP_HTTPSERVER_INL
|
|
||||||
#define UVMACHINE_HTTP_HTTPSERVER_INL
|
|
||||||
|
|
||||||
#include "httpserver.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
static BlockAllocator<65536> buffer_allocator;
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpServer<Req, Res>::HttpServer(uv::UvLoop& l)
|
|
||||||
: loop(l), stream(l) {}
|
|
||||||
|
|
||||||
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, server_t::on_connect);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpServer<Req, Res>::operator uv_tcp_t*()
|
|
||||||
{
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
HttpServer<Req, Res>::operator uv_stream_t*()
|
|
||||||
{
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void HttpServer<Req, Res>::on_connect(uv_stream_t* tcp_server, int)
|
|
||||||
{
|
|
||||||
server_t& server = *reinterpret_cast<server_t*>(tcp_server->data);
|
|
||||||
|
|
||||||
auto connection = new connection_t(server.loop, server);
|
|
||||||
|
|
||||||
uv_accept(server, connection->client);
|
|
||||||
|
|
||||||
uv_read_start(connection->client, server_t::on_alloc, server_t::on_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
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*>(buffer_allocator.acquire());
|
|
||||||
buf->len = buffer_allocator.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void HttpServer<Req, Res>::
|
|
||||||
on_read(uv_stream_t* tcp_client, ssize_t nread, const uv_buf_t* buf)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
buffer_allocator.release(buf->base);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void HttpServer<Req, Res>::respond(connection_t& conn)
|
|
||||||
{
|
|
||||||
conn.server.request_cb(conn.request, conn.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,33 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_IPV4_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_IPV4_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <ostream>
|
|
||||||
#include <uv.h>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
class Ipv4
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Ipv4(const std::string& address, uint16_t port);
|
|
||||||
|
|
||||||
operator const sockaddr_in&() const;
|
|
||||||
|
|
||||||
operator const sockaddr*() const;
|
|
||||||
|
|
||||||
friend std::ostream& operator<< (std::ostream& stream, const Ipv4& ip) {
|
|
||||||
return stream << ip.address << ':' << ip.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
sockaddr_in socket_address;
|
|
||||||
|
|
||||||
const std::string& address;
|
|
||||||
uint16_t port;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_IPV4_INL
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_IPV4_INL
|
|
||||||
|
|
||||||
#include "ipv4.hpp"
|
|
||||||
#include "http_error.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
Ipv4::Ipv4(const std::string& address, uint16_t port)
|
|
||||||
: address(address), port(port)
|
|
||||||
{
|
|
||||||
auto status = uv_ip4_addr(address.c_str(), port, &socket_address);
|
|
||||||
|
|
||||||
if(status != 0)
|
|
||||||
throw HttpError("Not a valid IP address/port (" + address + ":"
|
|
||||||
+ std::to_string(port) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ipv4::operator const sockaddr_in&() const
|
|
||||||
{
|
|
||||||
return socket_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ipv4::operator const sockaddr*() const
|
|
||||||
{
|
|
||||||
return (const struct sockaddr*)&socket_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_METHOD_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_METHOD_HPP
|
|
||||||
|
|
||||||
#include <http_parser.h>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
enum Method
|
|
||||||
{
|
|
||||||
GET = HTTP_GET,
|
|
||||||
HEAD = HTTP_HEAD,
|
|
||||||
POST = HTTP_POST,
|
|
||||||
PUT = HTTP_PUT,
|
|
||||||
DELETE = HTTP_DELETE,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,24 +0,0 @@
|
|||||||
#ifndef UVMACHINE_HTTP_REQUEST_HPP
|
|
||||||
#define UVMACHINE_HTTP_REQUEST_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "version.hpp"
|
|
||||||
#include "method.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Request
|
|
||||||
{
|
|
||||||
Version version;
|
|
||||||
Method method;
|
|
||||||
std::string url;
|
|
||||||
std::map<std::string, std::string> headers;
|
|
||||||
std::string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,40 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_RESPONSE_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_RESPONSE_HPP
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "io/uv/uv.hpp"
|
|
||||||
#include "status_codes.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
static constexpr size_t buffer_size = 65536;
|
|
||||||
|
|
||||||
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:
|
|
||||||
explicit Response(connection_t& connection);
|
|
||||||
|
|
||||||
void send(const std::string& body);
|
|
||||||
void send(Status code, const std::string& body);
|
|
||||||
|
|
||||||
std::map<std::string, std::string> headers;
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
|
|
||||||
private:
|
|
||||||
connection_t& connection;
|
|
||||||
uv::BlockBuffer<buffer_size> buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,77 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_RESPONSE_INL
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_RESPONSE_INL
|
|
||||||
|
|
||||||
#include "response.hpp"
|
|
||||||
#include "httpconnection.hpp"
|
|
||||||
|
|
||||||
#include "utils/memory/block_allocator.hpp"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
static BlockAllocator<sizeof(uv_write_t)> write_req_allocator;
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
Response<Req, Res>::Response(connection_t& conn)
|
|
||||||
: status(Status::Ok), connection(conn), buffer() {}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void Response<Req, Res>::send(Status status, const std::string& body)
|
|
||||||
{
|
|
||||||
this->status = status;
|
|
||||||
this->send(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Req, class Res>
|
|
||||||
void Response<Req, Res>::send(const std::string& body)
|
|
||||||
{
|
|
||||||
// terrible bug. if the client closes the connection, buffer is cleared and
|
|
||||||
// there is noone to respond to and you start writing to memory that isn't
|
|
||||||
// yours anymore
|
|
||||||
if(buffer.count() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uv_write_t* write_req =
|
|
||||||
static_cast<uv_write_t*>(write_req_allocator.acquire());
|
|
||||||
|
|
||||||
write_req->data = &connection;
|
|
||||||
|
|
||||||
buffer << "HTTP/1.1 " << to_string(status) << "\r\n";
|
|
||||||
|
|
||||||
buffer << "Content-Length:" << std::to_string(body.size()) << "\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;
|
|
||||||
/* std::cout << "SALJEM RESPONSE" << std::endl; */
|
|
||||||
/* std::cout << body << std::endl; */
|
|
||||||
/* std::cout << connection.request.body << std::endl; */
|
|
||||||
/* std::cout << "buffer count:" << buffer.count() << std::endl; */
|
|
||||||
|
|
||||||
uv_write(write_req, connection.client, buffer, buffer.count(),
|
|
||||||
[](uv_write_t* write_req, int) {
|
|
||||||
|
|
||||||
/* std::cout << "POSLAO RESPONSE" << std::endl; */
|
|
||||||
|
|
||||||
connection_t& conn = *reinterpret_cast<connection_t*>(write_req->data);
|
|
||||||
|
|
||||||
if(!conn.keep_alive)
|
|
||||||
conn.close();
|
|
||||||
|
|
||||||
conn.request.headers.clear();
|
|
||||||
conn.request.body.clear();
|
|
||||||
|
|
||||||
conn.response.status = Status::Ok;
|
|
||||||
conn.response.buffer.clear();
|
|
||||||
conn.response.headers.clear();
|
|
||||||
write_req_allocator.release(write_req);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,87 +0,0 @@
|
|||||||
#ifndef MEMGRAPH_SERVER_HTTP_STATUS_CODES_HPP
|
|
||||||
#define MEMGRAPH_SERVER_HTTP_STATUS_CODES_HPP
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
#define HTTP_STATUS_CODES \
|
|
||||||
CODE(Continue, 100, CONTINUE) \
|
|
||||||
CODE(SwitchingProtocols, 101, SWITCHING PROTOCOLS) \
|
|
||||||
CODE(Processing, 102, PROCESSING) \
|
|
||||||
CODE(Ok, 200, OK) \
|
|
||||||
CODE(Created, 201, CREATED) \
|
|
||||||
CODE(Accepted, 202, ACCEPTED) \
|
|
||||||
CODE(NonAuthoritativeInformation, 203, NON AUTHORITATIVE INFORMATION) \
|
|
||||||
CODE(NoContent, 204, NO CONTENT) \
|
|
||||||
CODE(ResetContent, 205, RESET CONTENT) \
|
|
||||||
CODE(PartialContent, 206, PARTIAL CONTENT) \
|
|
||||||
CODE(MultiStatus, 207, MULTI STATUS) \
|
|
||||||
CODE(MultipleChoices, 300, MULTIPLE CHOICES) \
|
|
||||||
CODE(MovedPermanently, 301, MOVED PERMANENTLY) \
|
|
||||||
CODE(MovedTemporarily, 302, MOVED TEMPORARILY) \
|
|
||||||
CODE(SeeOther, 303, SEE OTHER) \
|
|
||||||
CODE(NotModified, 304, NOT MODIFIED) \
|
|
||||||
CODE(UseProxy, 305, USE PROXY) \
|
|
||||||
CODE(TemporaryRedirect, 307, TEMPORARY REDIRECT) \
|
|
||||||
CODE(BadRequest, 400, BAD REQUEST) \
|
|
||||||
CODE(Unauthorized, 401, UNAUTHORIZED) \
|
|
||||||
CODE(PaymentRequired, 402, PAYMENT REQUIRED) \
|
|
||||||
CODE(Forbidden, 403, FORBIDDEN) \
|
|
||||||
CODE(NotFound, 404, NOT FOUND) \
|
|
||||||
CODE(MethodNotAllowed, 405, METHOD NOT ALLOWED) \
|
|
||||||
CODE(NotAcceptable, 406, NOT ACCEPTABLE) \
|
|
||||||
CODE(ProxyAuthenticationRequired, 407, PROXY AUTHENTICATION REQUIRED) \
|
|
||||||
CODE(RequestTimeOut, 408, REQUEST TIMEOUT) \
|
|
||||||
CODE(Conflict, 409, CONFLICT) \
|
|
||||||
CODE(Gone, 410, GONE) \
|
|
||||||
CODE(LengthRequired, 411, LENGTH REQUIRED) \
|
|
||||||
CODE(PreconditionFailed, 412, PRECONDITION FAILED) \
|
|
||||||
CODE(RequestEntityTooLarge, 413, REQUEST ENTITY TOO LARGE) \
|
|
||||||
CODE(RequestUriTooLarge, 414, REQUEST URI TOO LARGE) \
|
|
||||||
CODE(UnsupportedMediaType, 415, UNSUPPORTED MEDIA TYPE) \
|
|
||||||
CODE(RequestedRangeNotSatisfiable, 416, REQUESTED RANGE NOT SATISFIABLE) \
|
|
||||||
CODE(ExpectationFailed, 417, EXPECTATION FAILED) \
|
|
||||||
CODE(ImATeapot, 418, IM A TEAPOT) \
|
|
||||||
CODE(UnprocessableEntity, 422, UNPROCESSABLE ENTITY) \
|
|
||||||
CODE(Locked, 423, LOCKED) \
|
|
||||||
CODE(FailedDependency, 424, FAILED DEPENDENCY) \
|
|
||||||
CODE(UnorderedCollection, 425, UNORDERED COLLECTION) \
|
|
||||||
CODE(UpgradeRequired, 426, UPGRADE REQUIRED) \
|
|
||||||
CODE(PreconditionRequired, 428, PRECONDITION REQUIRED) \
|
|
||||||
CODE(TooManyRequests, 429, TOO MANY REQUESTS) \
|
|
||||||
CODE(RequestHeaderFieldsTooLarge, 431, REQUEST HEADER FIELDS TOO LARGE) \
|
|
||||||
CODE(InternalServerError, 500, INTERNAL SERVER ERROR) \
|
|
||||||
CODE(NotImplemented, 501, NOT IMPLEMENTED) \
|
|
||||||
CODE(BadGateway, 502, BAD GATEWAY) \
|
|
||||||
CODE(ServiceUnavailable, 503, SERVICE UNAVAILABLE) \
|
|
||||||
CODE(GatewayTimeOut, 504, GATEWAY TIME OUT) \
|
|
||||||
CODE(HttpVersionNotSupported, 505, HTTP VERSION NOT SUPPORTED) \
|
|
||||||
CODE(VariantAlsoNegotiates, 506, VARIANT ALSO NEGOTIATES) \
|
|
||||||
CODE(InsufficientStorage, 507, INSUFFICIENT STORAGE) \
|
|
||||||
CODE(BandwidthLimitExceeded, 508, BANDWIDTH LIMIT EXCEEDED) \
|
|
||||||
CODE(NotExtended, 510, NOT EXTENDED) \
|
|
||||||
CODE(NetworkAuthenticationRequired, 511, NETWORK AUTHENTICATION REQUIRED)
|
|
||||||
|
|
||||||
enum Status
|
|
||||||
{
|
|
||||||
#define CODE(a, b, c) a = b,
|
|
||||||
HTTP_STATUS_CODES
|
|
||||||
#undef CODE
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string to_string(Status status)
|
|
||||||
{
|
|
||||||
switch(status)
|
|
||||||
{
|
|
||||||
#define CODE(a, b, c) case a: return #b " " #c;
|
|
||||||
HTTP_STATUS_CODES
|
|
||||||
#undef CODE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,15 +0,0 @@
|
|||||||
#ifndef UVMACHINE_HTTP_HTTPVERSION_HPP
|
|
||||||
#define UVMACHINE_HTTP_HTTPVERSION_HPP
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Version
|
|
||||||
{
|
|
||||||
unsigned short major;
|
|
||||||
unsigned short minor;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
|
|
||||||
namespace sp
|
|
||||||
{
|
|
||||||
|
|
||||||
class Middlewares
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using middleware_cb_t = std::function<bool(sp::Request&, sp::Response&)>;
|
|
||||||
|
|
||||||
bool run(sp::Request& req, sp::Response& res)
|
|
||||||
{
|
|
||||||
for(auto& middleware : middlewares)
|
|
||||||
if(!middleware(req, res))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(middleware_cb_t cb)
|
|
||||||
{
|
|
||||||
middlewares.push_back(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<middleware_cb_t> middlewares;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#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 sp
|
|
||||||
{
|
|
||||||
|
|
||||||
class R3Error : public std::runtime_error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using runtime_error::runtime_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
class R3
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Method : int
|
|
||||||
{
|
|
||||||
GET = METHOD_GET,
|
|
||||||
POST = METHOD_POST,
|
|
||||||
PUT = METHOD_PUT,
|
|
||||||
DELETE = METHOD_DELETE,
|
|
||||||
HEAD = METHOD_HEAD
|
|
||||||
};
|
|
||||||
|
|
||||||
static Method to_r3_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Route
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Route(r3::node* node, Method method, const std::string& path)
|
|
||||||
{
|
|
||||||
entry = r3::match_entry_createl(path.c_str(), path.size());
|
|
||||||
entry->request_method = method;
|
|
||||||
route = r3::r3_tree_match_route(node, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Route()
|
|
||||||
{
|
|
||||||
r3::match_entry_free(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator r3::match_entry*()
|
|
||||||
{
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exists() const
|
|
||||||
{
|
|
||||||
return route != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void populate(sp::Request& req)
|
|
||||||
{
|
|
||||||
req.params.clear();
|
|
||||||
for(int i = 0; i < entry->vars->len; ++i)
|
|
||||||
req.params.emplace_back(entry->vars->tokens[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(sp::Request& req, sp::Response& res)
|
|
||||||
{
|
|
||||||
assert(route != nullptr);
|
|
||||||
|
|
||||||
auto cb = reinterpret_cast<sp::request_cb_t*>(route->data);
|
|
||||||
cb->operator()(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
r3::match_entry* entry;
|
|
||||||
r3::route* route;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit R3(size_t capacity)
|
|
||||||
{
|
|
||||||
root = r3::r3_tree_create(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
~R3()
|
|
||||||
{
|
|
||||||
r3::r3_tree_free(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
R3(R3&) = delete;
|
|
||||||
|
|
||||||
R3(R3&& other) : routes(std::move(other.routes), root(other.root)
|
|
||||||
{
|
|
||||||
other.root = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(Method method, const std::string& path, sp::request_cb_t cb)
|
|
||||||
{
|
|
||||||
routes.push_back(cb);
|
|
||||||
|
|
||||||
r3::r3_tree_insert_routel(root, method, path.c_str(), path.size(),
|
|
||||||
&routes.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
Route match(Method method, const std::string& path)
|
|
||||||
{
|
|
||||||
return Route(root, method, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<sp::request_cb_t> routes;
|
|
||||||
r3::node* root;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "str_array.h"
|
|
||||||
#include "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 +0,0 @@
|
|||||||
Subproject commit c02d52ad56595dc70b38daf46b5f315d3a7115fa
|
|
@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
#include "http/status_codes.hpp"
|
|
||||||
#include "rapidjson/document.h"
|
|
||||||
#include "rapidjson/error/en.h"
|
|
||||||
|
|
||||||
namespace sp
|
|
||||||
{
|
|
||||||
|
|
||||||
bool rapidjson_middleware(sp::Request& req, sp::Response& res)
|
|
||||||
{
|
|
||||||
// TODO-buda: sometimes req.body is unvalid
|
|
||||||
// when python requests lib send {} as data
|
|
||||||
// req.body is broken and the next if is not executed
|
|
||||||
// as it supposed to be
|
|
||||||
|
|
||||||
// the body is empty and json parsing isn't necessary
|
|
||||||
if (req.body.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// the body is successfuly parsed
|
|
||||||
if(!req.json.Parse(req.body.c_str()).HasParseError())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// some kind of parse error occurred
|
|
||||||
// return the error message to the client
|
|
||||||
auto error_str = rapidjson::GetParseError_En(req.json.GetParseError());
|
|
||||||
std::string parse_error = "JSON parse error: " + std::string(error_str);
|
|
||||||
|
|
||||||
std::cout << "'" << req.body << "'" << std::endl;
|
|
||||||
std::cout << parse_error << std::endl;
|
|
||||||
|
|
||||||
res.send(http::Status::BadRequest, parse_error);
|
|
||||||
|
|
||||||
// stop further execution
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "rapidjson/document.h"
|
|
||||||
|
|
||||||
#include "http/request.hpp"
|
|
||||||
|
|
||||||
namespace sp
|
|
||||||
{
|
|
||||||
|
|
||||||
class Request : public http::Request
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using http::Request::Request;
|
|
||||||
|
|
||||||
// http://rapidjson.org/md_doc_dom.html
|
|
||||||
rapidjson::Document json;
|
|
||||||
|
|
||||||
std::vector<std::string> params;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "http/response.hpp"
|
|
||||||
|
|
||||||
#include "rapidjson/document.h"
|
|
||||||
#include "rapidjson/writer.h"
|
|
||||||
#include "rapidjson/stringbuffer.h"
|
|
||||||
|
|
||||||
namespace sp
|
|
||||||
{
|
|
||||||
|
|
||||||
class Response : public http::Response<Request, Response>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using http::Response<Request, Response>::Response;
|
|
||||||
|
|
||||||
void json(http::Status code, const rapidjson::Document& document)
|
|
||||||
{
|
|
||||||
rapidjson::StringBuffer strbuf;
|
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
|
|
||||||
document.Accept(writer);
|
|
||||||
// TODO: error handling
|
|
||||||
this->send(code, strbuf.GetString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void json(const rapidjson::Document& document)
|
|
||||||
{
|
|
||||||
this->json(http::Status::Ok, document);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using request_cb_t = std::function<void(Request&, Response&)>;
|
|
||||||
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
/** @file speedy.hpp
|
|
||||||
* @brief Speedy - Cpp Web Application Framework
|
|
||||||
*
|
|
||||||
* Blazingly fast web application framework. Designed like
|
|
||||||
* http://expressjs.com, one of its mail goal is also simple usage.
|
|
||||||
*
|
|
||||||
* @author Dominik Tomicevic (domko)
|
|
||||||
* @author Marko Budiselic (buda)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "io/uv/uv.hpp"
|
|
||||||
#include "http/http.hpp"
|
|
||||||
#include "r3.hpp"
|
|
||||||
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
|
|
||||||
#include "middleware.hpp"
|
|
||||||
#include "rapidjson_middleware.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::sptr loop, const std::string& prefix = "",
|
|
||||||
size_t capacity = 100)
|
|
||||||
: server(*loop), prefix(std::move(prefix)), router(capacity)
|
|
||||||
{
|
|
||||||
use(rapidjson_middleware);
|
|
||||||
}
|
|
||||||
|
|
||||||
Speedy(const Speedy&) = delete;
|
|
||||||
Speedy(Speedy&&) = delete;
|
|
||||||
|
|
||||||
void use(Middlewares::middleware_cb_t cb)
|
|
||||||
{
|
|
||||||
middlewares.push_back(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void get(const std::string& path, server_t::request_cb_t cb)
|
|
||||||
{
|
|
||||||
router.insert(R3::Method::GET, join(prefix, path), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void post(const std::string& path, server_t::request_cb_t cb)
|
|
||||||
{
|
|
||||||
router.insert(R3::Method::POST, join(prefix, path), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void put(const std::string& path, server_t::request_cb_t cb)
|
|
||||||
{
|
|
||||||
router.insert(R3::Method::PUT, join(prefix, path), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void del(const std::string& path, server_t::request_cb_t cb)
|
|
||||||
{
|
|
||||||
router.insert(R3::Method::DELETE, join(prefix, path), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listen(const http::Ipv4& ip)
|
|
||||||
{
|
|
||||||
router.compile();
|
|
||||||
|
|
||||||
server.listen(ip, [this](Request& req, Response& res) {
|
|
||||||
auto route = router.match(R3::to_r3_method(req.method), req.url);
|
|
||||||
|
|
||||||
if(!route.exists())
|
|
||||||
return res.send(http::Status::NotFound, "Resource not found");
|
|
||||||
|
|
||||||
// parse url params
|
|
||||||
route.populate(req);
|
|
||||||
|
|
||||||
// run middlewares
|
|
||||||
auto result = middlewares.run(req, res);
|
|
||||||
|
|
||||||
// if they signaled false, stop the further execution
|
|
||||||
if(!result)
|
|
||||||
return;
|
|
||||||
|
|
||||||
route(req, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
server_t server;
|
|
||||||
std::string prefix;
|
|
||||||
R3 router;
|
|
||||||
Middlewares middlewares;
|
|
||||||
|
|
||||||
std::string join(const std::string& prefix, const std::string& path)
|
|
||||||
{
|
|
||||||
// check if prefix has a trailing /
|
|
||||||
if(prefix.back() == '/')
|
|
||||||
{
|
|
||||||
// prefix has a / on the end so remove the / from the path
|
|
||||||
// if it has it too. e.g /db/data/ + /node = /db/data/node
|
|
||||||
if(path.front() == '/')
|
|
||||||
return prefix + path.substr(1);
|
|
||||||
|
|
||||||
// there is only one / so it's ok to concat them
|
|
||||||
return prefix + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the prefix doesn't have a slash
|
|
||||||
if(path.front() == '/')
|
|
||||||
// the path has though, so it's ok to concat them
|
|
||||||
return prefix + path;
|
|
||||||
|
|
||||||
// neither the prefix or the path have a / so we need to add it
|
|
||||||
return prefix + '/' + path;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
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 ''
|
|
@ -1,67 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
|
|
||||||
// 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(ip);
|
|
||||||
loop.run(uv::UvLoop::Mode::Default);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#include "speedy.hpp"
|
|
||||||
|
|
||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
#include "rapidjson/document.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
uv::UvLoop::sptr loop(new uv::UvLoop());
|
|
||||||
http::Ipv4 ip("0.0.0.0", 8765);
|
|
||||||
sp::Speedy app(loop);
|
|
||||||
|
|
||||||
app.get("/bla", [](sp::Request& req, sp::Response& res) {
|
|
||||||
rapidjson::Document document;
|
|
||||||
document.Parse("{ \"test\": \"test\" }");
|
|
||||||
res.json(http::Status::Ok, document);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(ip);
|
|
||||||
loop->run(uv::UvLoop::Mode::Default);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user