diff --git a/example/server.cc b/example/server.cc index 5b457ae..858c833 100644 --- a/example/server.cc +++ b/example/server.cc @@ -57,13 +57,20 @@ std::string log(const httplib::Connection& c) return s; } +inline void error_handler(httplib::Connection& c) +{ + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), "Error Status: %d\r\n", c.response.status); + c.response.set_content(buf); +} + int main(void) { using namespace httplib; const char* hi = "/hi"; - Server svr("localhost", 1234); + Server svr("localhost", 8080); svr.get("/", [=](Connection& c) { c.response.set_redirect(hi); @@ -74,9 +81,11 @@ int main(void) }); svr.get("/dump", [](Connection& c) { - c.response.set_content(log(c)); + c.response.set_content(httplib::dump_headers(c.request.headers)); }); + svr.error(error_handler); + svr.set_logger([](const Connection& c) { printf("%s", log(c).c_str()); }); diff --git a/httplib.h b/httplib.h index 7c139c4..2eb30f8 100644 --- a/httplib.h +++ b/httplib.h @@ -81,8 +81,8 @@ public: void get(const char* pattern, Handler handler); void post(const char* pattern, Handler handler); + void error(Handler handler); - void on_ready(std::function callback); void set_logger(std::function logger); bool run(); @@ -91,9 +91,8 @@ public: private: void process_request(FILE* fp_read, FILE* fp_write); - bool read_request_line(FILE* fp, Request& request); - void write_response(FILE* fp, const Response& response); - void write_error(FILE* fp, int status); + bool read_request_line(FILE* fp, Request& req); + void write_response(FILE* fp, const Response& res); const std::string host_; const int port_; @@ -101,7 +100,7 @@ private: std::vector> get_handlers_; std::vector> post_handlers_; - std::function on_ready_; + Handler error_handler_; std::function logger_; }; @@ -110,10 +109,10 @@ public: Client(const char* host, int port); ~Client(); - int get(const char* url, Response& response); + int get(const char* url, Response& res); private: - bool read_response_line(FILE* fp, Response& response); + bool read_response_line(FILE* fp, Response& res); const std::string host_; const int port_; @@ -236,6 +235,26 @@ inline int close_client_socket(socket_t sock) #endif } +inline const char* status_message(int status) +{ + const char* s = NULL; + + switch (status) { + case 400: + s = "Bad Request"; + break; + case 404: + s = "Not Found"; + break; + default: + status = 500; + s = "Internal Server Error"; + break; + } + + return s; +} + inline const char* get_header_value(const MultiMap& map, const char* key, const char* def) { auto it = map.find(key); @@ -296,7 +315,6 @@ inline void Response::set_content(const std::string& s, const char* content_type { body = s; headers.insert(std::make_pair("Content-Type", content_type)); - status = 200; } inline Server::Server(const char* host, int port) @@ -327,9 +345,9 @@ inline void Server::post(const char* pattern, Handler handler) post_handlers_.push_back(std::make_pair(pattern, handler)); } -inline void Server::on_ready(std::function callback) +inline void Server::error(Handler handler) { - on_ready_ = callback; + error_handler_ = handler; } inline void Server::set_logger(std::function logger) @@ -344,10 +362,6 @@ inline bool Server::run() return false; } - if (on_ready_) { - on_ready_(); - } - for (;;) { socket_t fd = accept(sock_, NULL, NULL); if (fd == -1) { @@ -379,7 +393,7 @@ inline void Server::stop() sock_ = -1; } -inline bool Server::read_request_line(FILE* fp, Request& request) +inline bool Server::read_request_line(FILE* fp, Request& req) { const size_t BUFSIZ_REQUESTLINE = 2048; char buf[BUFSIZ_REQUESTLINE]; @@ -391,8 +405,8 @@ inline bool Server::read_request_line(FILE* fp, Request& request) std::cmatch m; if (std::regex_match(buf, m, re)) { - request.method = std::string(m[1]); - request.url = std::string(m[2]); + req.method = std::string(m[1]); + req.url = std::string(m[2]); // Parse query text auto len = std::distance(m[3].first, m[3].second); @@ -408,7 +422,7 @@ inline bool Server::read_request_line(FILE* fp, Request& request) val.assign(b, e); } }); - request.query[key] = val; + req.query[key] = val; }); } @@ -418,69 +432,42 @@ inline bool Server::read_request_line(FILE* fp, Request& request) return false; } -inline void Server::write_response(FILE* fp, const Response& response) +inline void Server::write_response(FILE* fp, const Response& res) { - fprintf(fp, "HTTP/1.0 %d OK\r\n", response.status); + fprintf(fp, "HTTP/1.0 %d %s\r\n", res.status, status_message(res.status)); fprintf(fp, "Connection: close\r\n"); - for (auto it = response.headers.begin(); it != response.headers.end(); ++it) { + for (auto it = res.headers.begin(); it != res.headers.end(); ++it) { if (it->first != "Content-Type" && it->second != "Content-Length") { fprintf(fp, "%s: %s\r\n", it->first.c_str(), it->second.c_str()); } } - if (!response.body.empty()) { - auto content_type = get_header_value(response.headers, "Content-Type", "text/plain"); + if (!res.body.empty()) { + auto content_type = get_header_value(res.headers, "Content-Type", "text/plain"); fprintf(fp, "Content-Type: %s\r\n", content_type); - fprintf(fp, "Content-Length: %ld\r\n", response.body.size()); + fprintf(fp, "Content-Length: %ld\r\n", res.body.size()); } fprintf(fp, "\r\n"); - if (!response.body.empty()) { - fprintf(fp, "%s", response.body.c_str()); + if (!res.body.empty()) { + fprintf(fp, "%s", res.body.c_str()); } } -inline void Server::write_error(FILE* fp, int status) -{ - const char* msg = NULL; - - switch (status) { - case 400: - msg = "Bad Request"; - break; - case 404: - msg = "Not Found"; - break; - default: - status = 500; - msg = "Internal Server Error"; - break; - } - - assert(msg); - - fprintf(fp, "HTTP/1.0 %d %s\r\n", status, msg); - fprintf(fp, "Content-type: text/plain\r\n"); - fprintf(fp, "Connection: close\r\n"); - fprintf(fp, "\r\n"); - fprintf(fp, "Status: %d\r\n", status); -} - inline void Server::process_request(FILE* fp_read, FILE* fp_write) { Connection c; if (!read_request_line(fp_read, c.request)) { - write_error(fp_write, 400); return; } read_headers(fp_read, c.request.headers); // Routing - c.response.status = 404; + c.response.status = 0; if (c.request.method == "GET") { for (auto it = get_handlers_.begin(); it != get_handlers_.end(); ++it) { @@ -493,6 +480,9 @@ inline void Server::process_request(FILE* fp_read, FILE* fp_write) c.request.params.push_back(m[i]); } handler(c); + if (!c.response.status) { + c.response.status = 200; + } break; } } @@ -502,15 +492,21 @@ inline void Server::process_request(FILE* fp_read, FILE* fp_write) c.response.status = 400; } + if (!c.response.status) { + c.response.status = 404; + } + + if (400 <= c.response.status) { + if (error_handler_) { + error_handler_(c); + } + } + if (logger_) { logger_(c); } - if (200 <= c.response.status && c.response.status < 400) { - write_response(fp_write, c.response); - } else { - write_error(fp_write, c.response.status); - } + write_response(fp_write, c.response); } // HTTP client implementation @@ -531,7 +527,7 @@ inline Client::~Client() #endif } -inline bool Client::read_response_line(FILE* fp, Response& response) +inline bool Client::read_response_line(FILE* fp, Response& res) { const size_t BUFSIZ_RESPONSELINE = 2048; char buf[BUFSIZ_RESPONSELINE]; @@ -543,13 +539,13 @@ inline bool Client::read_response_line(FILE* fp, Response& response) std::cmatch m; if (std::regex_match(buf, m, re)) { - response.status = std::atoi(std::string(m[1]).c_str()); + res.status = std::atoi(std::string(m[1]).c_str()); } return true; } -inline int Client::get(const char* url, Response& response) +inline int Client::get(const char* url, Response& res) { socket_t sock = create_client_socket(host_.c_str(), port_); if (sock == -1) { @@ -564,17 +560,17 @@ inline int Client::get(const char* url, Response& response) fprintf(fp_write, "GET %s HTTP/1.0\r\n\r\n", url); fflush(fp_write); - if (!read_response_line(fp_read, response)) { + if (!read_response_line(fp_read, res)) { return -1; } - read_headers(fp_read, response.headers); + read_headers(fp_read, res.headers); // Read content body - auto len = get_header_value_int(response.headers, "Content-Length", 0); + auto len = get_header_value_int(res.headers, "Content-Length", 0); if (len) { - response.body.assign(len, 0); - if (!fgets(&response.body[0], response.body.size() + 1, fp_read)) { + res.body.assign(len, 0); + if (!fgets(&res.body[0], res.body.size() + 1, fp_read)) { return -1; } } diff --git a/test/test.cc b/test/test.cc index 64da22a..49a511a 100644 --- a/test/test.cc +++ b/test/test.cc @@ -65,17 +65,20 @@ TEST(ServerTest, GetMethod) c.response.set_content(content); }); - //svr.on_ready([&]() { svr.stop(); }); - auto f = async([&](){ svr.run(); }); - sleep(1); + { + Response res; + Client(host, port).get(url, res); + EXPECT_EQ(200, res.status); + EXPECT_EQ(content, res.body); + } - Client cli(host, port); - - Response res; - cli.get(url, res); - EXPECT_EQ(content, res.body); + { + Response res; + Client(host, port).get("/invalid", res); + EXPECT_EQ(404, res.status); + } svr.stop(); }