1
0
mirror of synced 2025-04-20 11:47:43 +03:00

Removed Connection class.

This commit is contained in:
yhirose 2012-10-12 16:09:39 -04:00
parent 44a682cd3e
commit e8a18ad447
5 changed files with 107 additions and 105 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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);
} }
} }

View File

@ -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)