Removed Connection class.
This commit is contained in:
parent
44a682cd3e
commit
e8a18ad447
10
README.md
10
README.md
@ -10,7 +10,7 @@ It's extremely easy to setup. Just include **httplib.h** file in your code!
|
|||||||
Server Example
|
Server Example
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Inspired by [Sinatra](http://www.sinatrarb.com/)
|
Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express).
|
||||||
|
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
|
|
||||||
@ -18,13 +18,13 @@ Inspired by [Sinatra](http://www.sinatrarb.com/)
|
|||||||
{
|
{
|
||||||
using namespace httplib;
|
using namespace httplib;
|
||||||
|
|
||||||
Server svr("localhost", 1234);
|
Server svr;
|
||||||
|
|
||||||
svr.get("/hi", [](Connection& c) {
|
svr.get("/hi", [](const Request& req, Response& res) {
|
||||||
c.response.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.run();
|
svr.listen("localhost", 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client Example
|
Client Example
|
||||||
|
@ -10,13 +10,13 @@ using namespace httplib;
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
Server svr("localhost", 1234);
|
Server svr;
|
||||||
|
|
||||||
svr.get("/hi", [](Connection& c) {
|
svr.get("/hi", [](const Request& req, Response& res) {
|
||||||
c.response.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.run();
|
svr.listen("localhost", 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef _WIN32
|
using namespace httplib;
|
||||||
#define snprintf sprintf_s
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string dump_headers(const httplib::MultiMap& headers)
|
std::string dump_headers(const MultiMap& headers)
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
@ -26,11 +24,8 @@ std::string dump_headers(const httplib::MultiMap& headers)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string log(const httplib::Connection& c)
|
std::string log(const Request& req, Response& res)
|
||||||
{
|
{
|
||||||
const auto& req = c.request;
|
|
||||||
const auto& res = c.response;
|
|
||||||
|
|
||||||
std::string s;
|
std::string s;
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
@ -68,38 +63,36 @@ std::string log(const httplib::Connection& c)
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
using namespace httplib;
|
Server svr;
|
||||||
|
|
||||||
Server svr("localhost", 8080);
|
svr.get("/", [=](const Request& req, Response& res) {
|
||||||
|
res.set_redirect("/hi");
|
||||||
svr.get("/", [=](Connection& c) {
|
|
||||||
c.response.set_redirect("/hi");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.get("/hi", [](Connection& c) {
|
svr.get("/hi", [](const Request& req, Response& res) {
|
||||||
c.response.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.get("/dump", [](Connection& c) {
|
svr.get("/dump", [](const Request& req, Response& res) {
|
||||||
c.response.set_content(dump_headers(c.request.headers), "text/plain");
|
res.set_content(dump_headers(req.headers), "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.get("/stop", [&](Connection& c) {
|
svr.get("/stop", [&](const Request& req, Response& res) {
|
||||||
svr.stop();
|
svr.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.set_error_handler([](Connection& c) {
|
svr.set_error_handler([](const Request& req, Response& res) {
|
||||||
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
snprintf(buf, sizeof(buf), fmt, c.response.status);
|
snprintf(buf, sizeof(buf), fmt, res.status);
|
||||||
c.response.set_content(buf, "text/html");
|
res.set_content(buf, "text/html");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.set_logger([](const Connection& c) {
|
svr.set_logger([](const Request& req, Response& res) {
|
||||||
printf("%s", log(c).c_str());
|
printf("%s", log(req, res).c_str());
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.run();
|
svr.listen("localhost", 8080);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
114
httplib.h
114
httplib.h
@ -18,6 +18,9 @@
|
|||||||
#ifndef SO_OPENTYPE
|
#ifndef SO_OPENTYPE
|
||||||
#define SO_OPENTYPE 0x7008
|
#define SO_OPENTYPE 0x7008
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef snprintf
|
||||||
|
#define snprintf _snprintf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
@ -78,16 +81,18 @@ struct Response {
|
|||||||
Response() : status(-1) {}
|
Response() : status(-1) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
struct Connection {
|
struct Connection {
|
||||||
Request request;
|
Request request;
|
||||||
Response response;
|
Response response;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void (Connection& c)> Handler;
|
typedef std::function<void (const Request&, Response&)> Handler;
|
||||||
|
|
||||||
Server(const char* host, int port);
|
Server();
|
||||||
~Server();
|
~Server();
|
||||||
|
|
||||||
void get(const char* pattern, Handler handler);
|
void get(const char* pattern, Handler handler);
|
||||||
@ -96,7 +101,7 @@ public:
|
|||||||
void set_error_handler(Handler handler);
|
void set_error_handler(Handler handler);
|
||||||
void set_logger(Handler logger);
|
void set_logger(Handler logger);
|
||||||
|
|
||||||
bool run();
|
bool listen(const char* host, int port);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -104,13 +109,10 @@ private:
|
|||||||
|
|
||||||
void process_request(socket_t sock);
|
void process_request(socket_t sock);
|
||||||
bool read_request_line(FILE* fp, Request& req);
|
bool read_request_line(FILE* fp, Request& req);
|
||||||
bool routing(Connection& c);
|
bool routing(Request& req, Response& res);
|
||||||
bool dispatch_request(Connection& c, Handlers& handlers);
|
bool dispatch_request(Request& req, Response& res, Handlers& handlers);
|
||||||
|
|
||||||
const std::string host_;
|
|
||||||
const int port_;
|
|
||||||
socket_t svr_sock_;
|
|
||||||
|
|
||||||
|
socket_t svr_sock_;
|
||||||
Handlers get_handlers_;
|
Handlers get_handlers_;
|
||||||
Handlers post_handlers_;
|
Handlers post_handlers_;
|
||||||
Handler error_handler_;
|
Handler error_handler_;
|
||||||
@ -358,7 +360,7 @@ inline std::string encode_url(const std::string& s)
|
|||||||
if (s[i] < 0) {
|
if (s[i] < 0) {
|
||||||
result += '%';
|
result += '%';
|
||||||
char hex[4];
|
char hex[4];
|
||||||
size_t len = sprintf(hex, "%02X", (int)(unsigned char)s[i]);
|
size_t len = snprintf(hex, sizeof(hex), "%02X", (unsigned char)s[i]);
|
||||||
assert(len == 2);
|
assert(len == 2);
|
||||||
result.append(hex, len);
|
result.append(hex, len);
|
||||||
} else {
|
} else {
|
||||||
@ -404,35 +406,34 @@ inline int from_hex_to_i(const std::string& s, int i, int cnt, int& val)
|
|||||||
size_t to_utf8(int code, char* buff)
|
size_t to_utf8(int code, char* buff)
|
||||||
{
|
{
|
||||||
if (code < 0x0080) {
|
if (code < 0x0080) {
|
||||||
buff[0] = (uint8_t)(code & 0x7F);
|
buff[0] = (code & 0x7F);
|
||||||
return 1;
|
return 1;
|
||||||
} else if (code < 0x0800) {
|
} else if (code < 0x0800) {
|
||||||
buff[0] = (uint8_t)(0xC0 | ((code >> 6) & 0x1F));
|
buff[0] = (0xC0 | ((code >> 6) & 0x1F));
|
||||||
buff[1] = (uint8_t)(0x80 | (code & 0x3F));
|
buff[1] = (0x80 | (code & 0x3F));
|
||||||
return 2;
|
return 2;
|
||||||
} else if (code < 0xD800) {
|
} else if (code < 0xD800) {
|
||||||
buff[0] = (uint8_t)(0xE0 | ((code >> 12) & 0xF));
|
buff[0] = (0xE0 | ((code >> 12) & 0xF));
|
||||||
buff[1] = (uint8_t)(0x80 | ((code >> 6) & 0x3F));
|
buff[1] = (0x80 | ((code >> 6) & 0x3F));
|
||||||
buff[2] = (uint8_t)(0x80 | (code & 0x3F));
|
buff[2] = (0x80 | (code & 0x3F));
|
||||||
return 3;
|
return 3;
|
||||||
} else if (code < 0xE000) { // D800 - DFFF is invalid...
|
} else if (code < 0xE000) { // D800 - DFFF is invalid...
|
||||||
assert(!"NOTREACHED");
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (code < 0x10000) {
|
} else if (code < 0x10000) {
|
||||||
buff[0] = (uint8_t)(0xE0 | ((code >> 12) & 0xF));
|
buff[0] = (0xE0 | ((code >> 12) & 0xF));
|
||||||
buff[1] = (uint8_t)(0x80 | ((code >> 6) & 0x3F));
|
buff[1] = (0x80 | ((code >> 6) & 0x3F));
|
||||||
buff[2] = (uint8_t)(0x80 | (code & 0x3F));
|
buff[2] = (0x80 | (code & 0x3F));
|
||||||
return 3;
|
return 3;
|
||||||
} else if (code < 0x110000) {
|
} else if (code < 0x110000) {
|
||||||
buff[0] = (uint8_t)(0xF0 | ((code >> 18) & 0x7));
|
buff[0] = (0xF0 | ((code >> 18) & 0x7));
|
||||||
buff[1] = (uint8_t)(0x80 | ((code >> 12) & 0x3F));
|
buff[1] = (0x80 | ((code >> 12) & 0x3F));
|
||||||
buff[2] = (uint8_t)(0x80 | ((code >> 6) & 0x3F));
|
buff[2] = (0x80 | ((code >> 6) & 0x3F));
|
||||||
buff[3] = (uint8_t)(0x80 | (code & 0x3F));
|
buff[3] = (0x80 | (code & 0x3F));
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!"NOTREACHED");
|
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string decode_url(const std::string& s)
|
inline std::string decode_url(const std::string& s)
|
||||||
@ -461,10 +462,10 @@ inline std::string decode_url(const std::string& s)
|
|||||||
result.append(buff, len);
|
result.append(buff, len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ASCII
|
// HEX
|
||||||
int val = 0;
|
int val = 0;
|
||||||
i = from_hex_to_i(s, i, 2, val);
|
i = from_hex_to_i(s, i, 2, val);
|
||||||
result += (char)val;
|
result += val;
|
||||||
}
|
}
|
||||||
} else if (s[i] == '+') {
|
} else if (s[i] == '+') {
|
||||||
result += ' ';
|
result += ' ';
|
||||||
@ -560,10 +561,8 @@ inline void Response::set_content(const std::string& s, const char* content_type
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTTP server implementation
|
// HTTP server implementation
|
||||||
inline Server::Server(const char* host, int port)
|
inline Server::Server()
|
||||||
: host_(host)
|
: svr_sock_(-1)
|
||||||
, port_(port)
|
|
||||||
, svr_sock_(-1)
|
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
@ -598,9 +597,9 @@ inline void Server::set_logger(Handler logger)
|
|||||||
logger_ = logger;
|
logger_ = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Server::run()
|
inline bool Server::listen(const char* host, int port)
|
||||||
{
|
{
|
||||||
svr_sock_ = detail::create_server_socket(host_.c_str(), port_);
|
svr_sock_ = detail::create_server_socket(host, port);
|
||||||
if (svr_sock_ == -1) {
|
if (svr_sock_ == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -663,24 +662,24 @@ inline bool Server::read_request_line(FILE* fp, Request& req)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Server::routing(Connection& c)
|
inline bool Server::routing(Request& req, Response& res)
|
||||||
{
|
{
|
||||||
if (c.request.method == "GET") {
|
if (req.method == "GET") {
|
||||||
return dispatch_request(c, get_handlers_);
|
return dispatch_request(req, res, get_handlers_);
|
||||||
} else if (c.request.method == "POST") {
|
} else if (req.method == "POST") {
|
||||||
return dispatch_request(c, post_handlers_);
|
return dispatch_request(req, res, post_handlers_);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Server::dispatch_request(Connection& c, Handlers& handlers)
|
inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers)
|
||||||
{
|
{
|
||||||
for (auto it = handlers.begin(); it != handlers.end(); ++it) {
|
for (auto it = handlers.begin(); it != handlers.end(); ++it) {
|
||||||
const auto& pattern = it->first;
|
const auto& pattern = it->first;
|
||||||
const auto& handler = it->second;
|
const auto& handler = it->second;
|
||||||
|
|
||||||
if (std::regex_match(c.request.url, c.request.matches, pattern)) {
|
if (std::regex_match(req.url, req.matches, pattern)) {
|
||||||
handler(c);
|
handler(req, res);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -693,40 +692,41 @@ inline void Server::process_request(socket_t sock)
|
|||||||
FILE* fp_write;
|
FILE* fp_write;
|
||||||
detail::get_flie_pointers(sock, fp_read, fp_write);
|
detail::get_flie_pointers(sock, fp_read, fp_write);
|
||||||
|
|
||||||
Connection c;
|
Request req;
|
||||||
|
Response res;
|
||||||
|
|
||||||
if (!read_request_line(fp_read, c.request) ||
|
if (!read_request_line(fp_read, req) ||
|
||||||
!detail::read_headers(fp_read, c.request.headers)) {
|
!detail::read_headers(fp_read, req.headers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.request.method == "POST") {
|
if (req.method == "POST") {
|
||||||
if (!detail::read_content(c.request, fp_read)) {
|
if (!detail::read_content(req, fp_read)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (c.request.get_header_value("Content-Type") == "application/x-www-form-urlencoded") {
|
if (req.get_header_value("Content-Type") == "application/x-www-form-urlencoded") {
|
||||||
detail::parse_query_text(detail::decode_url(c.request.body), c.request.params);
|
detail::parse_query_text(detail::decode_url(req.body), req.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routing(c)) {
|
if (routing(req, res)) {
|
||||||
if (c.response.status == -1) {
|
if (res.status == -1) {
|
||||||
c.response.status = 200;
|
res.status = 200;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.response.status = 404;
|
res.status = 404;
|
||||||
}
|
}
|
||||||
assert(c.response.status != -1);
|
assert(res.status != -1);
|
||||||
|
|
||||||
if (400 <= c.response.status && error_handler_) {
|
if (400 <= res.status && error_handler_) {
|
||||||
error_handler_(c);
|
error_handler_(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::write_response(fp_write, c.response);
|
detail::write_response(fp_write, res);
|
||||||
fflush(fp_write);
|
fflush(fp_write);
|
||||||
|
|
||||||
if (logger_) {
|
if (logger_) {
|
||||||
logger_(c);
|
logger_(req, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
test/test.cc
39
test/test.cc
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
|
#define msleep(n) ::Sleep(n)
|
||||||
|
#else
|
||||||
|
#define msleep(n) ::usleep(n * 1000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -170,46 +173,51 @@ TEST(GetHeaderValueTest, RegularValueInt)
|
|||||||
|
|
||||||
class ServerTest : public ::testing::Test {
|
class ServerTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
ServerTest() : svr_(HOST, PORT), cli_(HOST, PORT) {
|
ServerTest() : cli_(HOST, PORT), up_(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
svr_.get("/hi", [&](Connection& c) {
|
svr_.get("/hi", [&](const Request& req, Response& res) {
|
||||||
c.response.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr_.get("/", [&](httplib::Connection& c) {
|
svr_.get("/", [&](const Request& req, Response& res) {
|
||||||
c.response.set_redirect("/hi");
|
res.set_redirect("/hi");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr_.post("/person", [&](Connection& c) {
|
svr_.post("/person", [&](const Request& req, Response& res) {
|
||||||
const auto& req = c.request;
|
|
||||||
if (req.has_param("name") && req.has_param("note")) {
|
if (req.has_param("name") && req.has_param("note")) {
|
||||||
persons_[req.params.at("name")] = req.params.at("note");
|
persons_[req.params.at("name")] = req.params.at("note");
|
||||||
} else {
|
} else {
|
||||||
c.response.status = 400;
|
res.status = 400;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
svr_.get("/person/(.*)", [&](Connection& c) {
|
svr_.get("/person/(.*)", [&](const Request& req, Response& res) {
|
||||||
const auto& req = c.request;
|
|
||||||
std::string name = req.matches[1];
|
std::string name = req.matches[1];
|
||||||
if (persons_.find(name) != persons_.end()) {
|
if (persons_.find(name) != persons_.end()) {
|
||||||
auto note = persons_[name];
|
auto note = persons_[name];
|
||||||
c.response.set_content(note, "text/plain");
|
res.set_content(note, "text/plain");
|
||||||
} else {
|
} else {
|
||||||
c.response.status = 404;
|
res.status = 404;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
svr_.get("/stop", [&](Connection& c) {
|
svr_.get("/stop", [&](const Request& req, Response& res) {
|
||||||
svr_.stop();
|
svr_.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
persons_["john"] = "programmer";
|
persons_["john"] = "programmer";
|
||||||
|
|
||||||
//f_ = async([&](){ svr_.run(); });
|
//f_ = async([&](){ svr_.listen(HOST, PORT); });
|
||||||
t_ = std::make_shared<thread>([&](){ svr_.run(); });
|
t_ = std::make_shared<thread>([&](){
|
||||||
|
up_ = true;
|
||||||
|
svr_.listen(HOST, PORT);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (!up_) {
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown() {
|
virtual void TearDown() {
|
||||||
@ -225,6 +233,7 @@ protected:
|
|||||||
Client cli_;
|
Client cli_;
|
||||||
//std::future<void> f_;
|
//std::future<void> f_;
|
||||||
std::shared_ptr<thread> t_;
|
std::shared_ptr<thread> t_;
|
||||||
|
bool up_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ServerTest, GetMethod200)
|
TEST_F(ServerTest, GetMethod200)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user