From 7056550bbb3a46b5f1ea077999cc3cd374af4b9e Mon Sep 17 00:00:00 2001 From: ipknHama Date: Tue, 1 Apr 2014 21:25:16 +0900 Subject: working http server --- Makefile | 5 ++-- flask.h | 13 ++++++-- http_connection.h | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- http_response.h | 40 +++++++++++++++++++++++++ parser.h | 83 ++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 http_response.h diff --git a/Makefile b/Makefile index 66d35d5..9270e44 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ all: example -example: example.cpp flask.h http_server.h http_connection.h parser.h +example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h g++ -g -std=c++11 -o example example.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ test: example + pkill example || exit 0 ./example & - test.py + python test.py || exit 0 pkill example diff --git a/flask.h b/flask.h index c694de1..ca78da3 100644 --- a/flask.h +++ b/flask.h @@ -5,6 +5,7 @@ #include #include +#include "http_response.h" #include "http_server.h" // TEST @@ -19,12 +20,17 @@ namespace flask { } - void handle() + response handle() { + return yameHandler_(); } - void route(const std::string& url, std::function f) + template + void route(const std::string& url, F f) { + yameHandler_ = [f]{ + return response(f()); + }; } Flask& port(std::uint16_t port) @@ -40,6 +46,9 @@ namespace flask } private: uint16_t port_ = 80; + + // Someday I will become real handler! + std::function yameHandler_; }; }; diff --git a/http_connection.h b/http_connection.h index 92b06e1..c25c61d 100644 --- a/http_connection.h +++ b/http_connection.h @@ -1,7 +1,12 @@ #pragma once #include #include +#include +#include +#include + #include "parser.h" +#include "http_response.h" namespace flask { @@ -11,7 +16,7 @@ namespace flask class Connection { public: - Connection(tcp::socket&& socket, Handler* handler) : socket_(std::move(socket)), handler_(handler) + Connection(tcp::socket&& socket, Handler* handler) : socket_(std::move(socket)), handler_(handler), parser_(this) { } @@ -20,19 +25,91 @@ namespace flask do_read(); } + void handle() + { + // request = make_request_from_parser + res = handler_->handle(); + + static std::string seperator = ": "; + static std::string crlf = "\r\n"; + + std::vector buffers; + + buffers.push_back(boost::asio::buffer(statusCodes[res.status])); + + bool has_content_length = false; + for(auto& kv : res.headers) + { + buffers.push_back(boost::asio::buffer(kv.first)); + buffers.push_back(boost::asio::buffer(seperator)); + buffers.push_back(boost::asio::buffer(kv.second)); + buffers.push_back(boost::asio::buffer(crlf)); + + if (boost::iequals(kv.first, "content-length")) + has_content_length = true; + } + + if (!has_content_length) + close_connection_ = true; + + buffers.push_back(boost::asio::buffer(crlf)); + buffers.push_back(boost::asio::buffer(res.body)); + + do_write(buffers); + } + private: void do_read() { + life_++; socket_.async_read_some(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t bytes_transferred) { if (!ec) { - parser_.feed(buffer_.data(), bytes_transferred); + bool ret = parser_.feed(buffer_.data(), bytes_transferred); + do_read(); + } + else + { + bool ret = parser_.done(); + socket_.close(); + + life_--; + if ((int)life_ == 0) + { + delete this; + } + } + }); + } + + void do_write(const std::vector& buffers) + { + life_++; + boost::asio::async_write(socket_, buffers, + [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + { + bool should_close = false; + if (!ec) + { + if (close_connection_) + { + should_close = true; + } } else { - parser_.done(); + should_close = true; + } + if (should_close) + { + socket_.close(); + life_--; + if ((int)life_ == 0) + { + delete this; + } } }); } @@ -43,6 +120,10 @@ namespace flask std::array buffer_; - HTTPParser parser_; + HTTPParser parser_; + response res; + + std::atomic life_; + bool close_connection_ = false; }; } diff --git a/http_response.h b/http_response.h new file mode 100644 index 0000000..1221dd2 --- /dev/null +++ b/http_response.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace flask +{ + std::unordered_map statusCodes = { + {200, "HTTP/1.1 200 OK\r\n"}, + {201, "HTTP/1.1 201 Created\r\n"}, + {202, "HTTP/1.1 202 Accepted\r\n"}, + {204, "HTTP/1.1 204 No Content\r\n"}, + + {300, "HTTP/1.1 300 Multiple Choices\r\n"}, + {301, "HTTP/1.1 301 Moved Permanently\r\n"}, + {302, "HTTP/1.1 302 Moved Temporarily\r\n"}, + {304, "HTTP/1.1 304 Not Modified\r\n"}, + + {400, "HTTP/1.1 400 Bad Request\r\n"}, + {401, "HTTP/1.1 401 Unauthorized\r\n"}, + {403, "HTTP/1.1 403 Forbidden\r\n"}, + {404, "HTTP/1.1 404 Not Found\r\n"}, + + {500, "HTTP/1.1 500 Internal Server Error\r\n"}, + {501, "HTTP/1.1 501 Not Implemented\r\n"}, + {502, "HTTP/1.1 502 Bad Gateway\r\n"}, + {503, "HTTP/1.1 503 Service Unavailable\r\n"}, + }; + + struct response + { + int status = 200; + std::string body; + std::unordered_map headers; + response() {} + response(const std::string& body) : body(body) {} + response(std::string&& body) : body(std::move(body)) {} + response(const std::string& body, int status) : body(body), status(status) {} + response(std::string&& body, int status) : body(std::move(body)), status(status) {} + }; +} diff --git a/parser.h b/parser.h index 2f2626d..b5375a1 100644 --- a/parser.h +++ b/parser.h @@ -1,55 +1,85 @@ -#include #include +#include + namespace flask { + template struct HTTPParser : public http_parser { static int on_message_begin(http_parser* self_) { HTTPParser* self = static_cast(self_); + self->clear(); return 0; } static int on_url(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - std::cout << std::string(at, at+length) << std::endl; + self->url.insert(self->url.end(), at, at+length); return 0; } static int on_status(http_parser* self_, const char* at, size_t length) { - HTTPParser* self = static_cast(self_); - std::cout << std::string(at, at+length) << std::endl; + // will not call while parsing request return 0; } static int on_header_field(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - std::cout << std::string(at, at+length) << std::endl; + switch (self->header_building_state) + { + case 0: + if (!self->header_value.empty()) + { + self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + } + self->header_field.assign(at, at+length); + self->header_building_state = 1; + break; + case 1: + self->header_field.insert(self->header_value.end(), at, at+length); + break; + } return 0; } static int on_header_value(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - std::cout << std::string(at, at+length) << std::endl; + switch (self->header_building_state) + { + case 0: + self->header_value.insert(self->header_value.end(), at, at+length); + break; + case 1: + self->header_building_state = 0; + self->header_value.assign(at, at+length); + break; + } return 0; } static int on_headers_complete(http_parser* self_) { HTTPParser* self = static_cast(self_); + if (!self->header_field.empty()) + { + self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + } return 0; } static int on_body(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - std::cout << std::string(at, at+length) << std::endl; + self->body.insert(self->body.end(), at, at+length); return 0; } static int on_message_complete(http_parser* self_) { HTTPParser* self = static_cast(self_); + self->process_message(); return 0; } - HTTPParser() : + HTTPParser(Handler* handler) : + handler_(handler), settings_ { on_message_begin, on_url, @@ -64,16 +94,49 @@ namespace flask http_parser_init(this, HTTP_REQUEST); } - void feed(const char* buffer, int length) + bool feed(const char* buffer, int length) { + std::cerr << "<|" << std::string(buffer, buffer+length) << "|>" << std::endl; int nparsed = http_parser_execute(this, &settings_, buffer, length); + return nparsed == length; } - void done() + bool done() { + std::cerr << "(done)" << std::endl; int nparsed = http_parser_execute(this, &settings_, nullptr, 0); + return nparsed == 0; } + void clear() + { + url.clear(); + header_building_state = 0; + header_field.clear(); + header_value.clear(); + headers.clear(); + body.clear(); + } + + void process_message() + { + //std::cout << "URL: "<< url << std::endl; + //for(auto& kv : headers) + //{ + //std::cout << kv.first << ": " << kv.second << std::endl; + //} + handler_->handle(); + } + + std::string url; + int header_building_state = 0; + std::string header_field; + std::string header_value; + std::unordered_map headers; + std::string body; + http_parser_settings settings_; + + Handler* handler_; }; } -- cgit v1.2.3-54-g00ecf