aboutsummaryrefslogtreecommitdiffstats
path: root/http_connection.h
blob: 7fd9a43761245a0c681dad09bf6632be77bf9342 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#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
{
    using namespace boost;
    using tcp = asio::ip::tcp;
    template <typename Handler>
    class Connection
    {
    public:
        Connection(tcp::socket&& socket, Handler* handler) : socket_(std::move(socket)), handler_(handler), parser_(this)
        {
        }

        void start()
        {
            do_read();
        }

        void handle()
        {
            request req = parser_.to_request();
            res = handler_->handle(req);

            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)
                    {
                        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
                    {
                        should_close = true;
                    }
                    if (should_close)
                    {
                        socket_.close();
                        life_--;
                        if ((int)life_ == 0)
                        {
                            delete this;
                        }
                    }
                });
        }

    private:
        tcp::socket socket_;
        Handler* handler_;

        std::array<char, 8192> buffer_;

        HTTPParser<Connection> parser_;
        response res;

        std::atomic<int> life_;
        bool close_connection_ = false;
    };
}