aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--http_connection.h96
-rw-r--r--http_server.h7
-rw-r--r--parser.h1
-rw-r--r--test.py19
-rw-r--r--unittest.cpp2
5 files changed, 91 insertions, 34 deletions
diff --git a/http_connection.h b/http_connection.h
index 867196c..fca9c22 100644
--- a/http_connection.h
+++ b/http_connection.h
@@ -1,36 +1,56 @@
#pragma once
#include <boost/asio.hpp>
-#include <http_parser.h>
#include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/array.hpp>
#include <boost/lexical_cast.hpp>
#include <atomic>
#include <chrono>
+#include <array>
+
+#include <http_parser.h>
#include "datetime.h"
#include "parser.h"
#include "http_response.h"
#include "logging.h"
+#include "settings.h"
namespace crow
{
using namespace boost;
using tcp = asio::ip::tcp;
+#ifdef CROW_ENABLE_DEBUG
+ static int connectionCount;
+#endif
template <typename Handler>
- class Connection
+ class Connection : public std::enable_shared_from_this<Connection<Handler>>
{
public:
Connection(tcp::socket&& socket, Handler* handler, const std::string& server_name)
: socket_(std::move(socket)),
handler_(handler),
parser_(this),
- server_name_(server_name)
+ server_name_(server_name),
+ deadline_(socket_.get_io_service())
+ {
+#ifdef CROW_ENABLE_DEBUG
+ connectionCount ++;
+ CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
+#endif
+ }
+#ifdef CROW_ENABLE_DEBUG
+ ~Connection()
{
+ connectionCount --;
+ CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this;
}
+#endif
void start()
{
+ auto self = this->shared_from_this();
+ start_deadline();
+
do_read();
}
@@ -187,71 +207,78 @@ namespace crow
void do_read()
{
- life_++;
+ auto self = this->shared_from_this();
socket_.async_read_some(boost::asio::buffer(buffer_),
- [this](boost::system::error_code ec, std::size_t bytes_transferred)
+ [self, this](const boost::system::error_code& ec, std::size_t bytes_transferred)
{
- bool do_complete_task = false;
+ bool error_while_reading = true;
if (!ec)
{
bool ret = parser_.feed(buffer_.data(), bytes_transferred);
if (ret)
+ {
do_read();
- else
- do_complete_task = true;
+ error_while_reading = false;
+ }
}
- else
- do_complete_task = true;
- if (do_complete_task)
+
+ if (error_while_reading)
{
+ deadline_.cancel();
parser_.done();
socket_.close();
-
- life_--;
- if ((int)life_ == 0)
- delete this;
+ }
+ else
+ {
+ start_deadline();
}
});
}
void do_write()
{
- life_++;
+ auto self = this->shared_from_this();
boost::asio::async_write(socket_, buffers_,
- [this](const boost::system::error_code& ec, std::size_t bytes_transferred)
+ [&, self](const boost::system::error_code& ec, std::size_t bytes_transferred)
{
- bool should_close = false;
if (!ec)
{
+ start_deadline();
if (close_connection_)
{
- should_close = true;
+ socket_.close();
}
}
- else
- {
- should_close = true;
- }
- if (should_close)
- {
- socket_.close();
- life_--;
- if ((int)life_ == 0)
- delete this;
- }
});
}
+ void start_deadline(int timeout = 5)
+ {
+ deadline_.expires_from_now(boost::posix_time::seconds(timeout));
+ auto self = this->shared_from_this();
+ deadline_.async_wait([self, this](const boost::system::error_code& ec)
+ {
+ if (ec || !socket_.is_open())
+ {
+ return;
+ }
+ bool is_deadline_passed = deadline_.expires_at() <= boost::asio::deadline_timer::traits_type::now();
+ if (is_deadline_passed)
+ {
+ socket_.close();
+ }
+ });
+ }
+
private:
tcp::socket socket_;
Handler* handler_;
- boost::array<char, 8192> buffer_;
+ std::array<char, 8192> buffer_;
HTTPParser<Connection> parser_;
response res;
- int life_ {};
bool close_connection_ = false;
const std::string& server_name_;
@@ -259,5 +286,8 @@ namespace crow
std::string content_length_;
std::string date_str_;
+
+ boost::asio::deadline_timer deadline_;
};
+
}
diff --git a/http_server.h b/http_server.h
index 6cf8f4f..9381fdb 100644
--- a/http_server.h
+++ b/http_server.h
@@ -4,6 +4,8 @@
#include <cstdint>
#include <atomic>
+#include <memory>
+
#include "http_connection.h"
#include "datetime.h"
#include "logging.h"
@@ -57,7 +59,10 @@ namespace crow
[this](boost::system::error_code ec)
{
if (!ec)
- (new Connection<Handler>(std::move(socket_), handler_, server_name_))->start();
+ {
+ auto p = std::make_shared<Connection<Handler>>(std::move(socket_), handler_, server_name_);
+ p->start();
+ }
do_accept();
});
}
diff --git a/parser.h b/parser.h
index 87d9562..f504248 100644
--- a/parser.h
+++ b/parser.h
@@ -97,6 +97,7 @@ namespace crow
http_parser_init(this, HTTP_REQUEST);
}
+ // return false on error
bool feed(const char* buffer, int length)
{
int nparsed = http_parser_execute(this, &settings_, buffer, length);
diff --git a/test.py b/test.py
index 357d2d3..09d7a42 100644
--- a/test.py
+++ b/test.py
@@ -6,3 +6,22 @@ assert "3 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/3').
assert "100 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/100').read()
assert 400 == urllib.urlopen('http://localhost:18080/hello/500').getcode()
assert "3" == urllib.urlopen('http://localhost:18080/add_json', data='{"a":1,"b":2}').read()
+
+# test persistent connection
+import socket
+import time
+s = socket.socket()
+s.connect(('localhost', 18080))
+for i in xrange(10):
+ s.send('''GET / HTTP/1.1
+Host: localhost\r\n\r\n''');
+ assert 'Hello World!' in s.recv(1024)
+
+# test timeout
+s = socket.socket()
+s.connect(('localhost', 18080))
+print 'ERROR REQUEST'
+s.send('''GET / HTTP/1.1
+hHhHHefhwjkefhklwejfklwejf
+''')
+print s.recv(1024)
diff --git a/unittest.cpp b/unittest.cpp
index 000e2f5..718d3d2 100644
--- a/unittest.cpp
+++ b/unittest.cpp
@@ -1,4 +1,5 @@
//#define CROW_ENABLE_LOGGING
+#define CROW_ENABLE_DEBUG
#include <iostream>
#include <vector>
#include "routing.h"
@@ -397,6 +398,7 @@ int testmain()
if (failed__)
{
cerr << "F";
+ cerr << '\t' << typeid(*t).name() << endl;
failed = true;
}
else