aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-04-01 21:25:16 +0900
committeripknHama <ipknhama@gmail.com>2014-04-01 21:25:16 +0900
commit7056550bbb3a46b5f1ea077999cc3cd374af4b9e (patch)
treef9126fa815f8c9c1aa1f9a946f881558823560f0
parent4b8c67e2300205200f4f846400d73a03cb3da854 (diff)
downloadcrow-7056550bbb3a46b5f1ea077999cc3cd374af4b9e.tar.gz
crow-7056550bbb3a46b5f1ea077999cc3cd374af4b9e.zip
working http server
-rw-r--r--Makefile5
-rw-r--r--flask.h13
-rw-r--r--http_connection.h89
-rw-r--r--http_response.h40
-rw-r--r--parser.h83
5 files changed, 212 insertions, 18 deletions
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 <future>
#include <stdint.h>
+#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<std::string()> f)
+ template <typename F>
+ 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<response()> 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 <boost/asio.hpp>
#include <http_parser.h>
+#include <atomic>
+#include <boost/asio.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
#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<boost::asio::const_buffer> 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<boost::asio::const_buffer>& 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<char, 8192> buffer_;
- HTTPParser parser_;
+ HTTPParser<Connection> parser_;
+ response res;
+
+ std::atomic<int> 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 <string>
+#include <unordered_map>
+
+namespace flask
+{
+ std::unordered_map<int, std::string> 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<std::string, std::string> 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 <iostream>
#include <string>
+#include <unordered_map>
+
namespace flask
{
+ template <typename Handler>
struct HTTPParser : public http_parser
{
static int on_message_begin(http_parser* self_)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
+ self->clear();
return 0;
}
static int on_url(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(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<HTTPParser*>(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<HTTPParser*>(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<HTTPParser*>(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<HTTPParser*>(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<HTTPParser*>(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<HTTPParser*>(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<std::string, std::string> headers;
+ std::string body;
+
http_parser_settings settings_;
+
+ Handler* handler_;
};
}