-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_server.cpp
177 lines (151 loc) · 5.36 KB
/
http_server.cpp
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <iostream>
#include <algorithm>
#include <experimental/net>
#include "http_pages.h"
#pragma clang diagnostic push
#pragma ide diagnostic ignored "CannotResolve"
#pragma clang diagnostic ignored "-Wmissing-noreturn"
using namespace std;
using namespace std::chrono;
using namespace std::experimental::net;
using namespace std::experimental::net::ip;
using namespace std::placeholders;
int const HTTP_PORT = 8080;
int const MAX_REQUEST_SIZE = 1024;
class http_server;
class http_connection;
class http_connection {
public:
http_connection(http_server* server, tcp::socket&& client_socket) :
server{server},
client_socket{std::move(client_socket)},
request_buffer{},
request_length_read{0},
response_length_written{0}
{}
void start() {
client_socket.async_read_some(
buffer(request_buffer, MAX_REQUEST_SIZE),
[this](const error_code& error, size_t bytes_transferred) {
read_handler(error, bytes_transferred);
});
}
void read_handler(const error_code& error, size_t bytes_transferred) {
if (!error) {
request_length_read += bytes_transferred;
if (has_full_request()) {
request_handler();
}
else {
client_socket.async_read_some(
buffer(request_buffer, MAX_REQUEST_SIZE),
[this](const error_code& error2, size_t bytes_transferred2) {
read_handler(error2, bytes_transferred2);
});
}
}
}
void request_handler() {
string request_string{begin(request_buffer), begin(request_buffer) + request_length_read};
// get first line
istringstream iss{request_string};
string line;
getline(iss, line);
// parse it
istringstream isl{line};
vector<string> tokens{istream_iterator<string>{isl}, istream_iterator<string>{}};
string http_verb = tokens[0];
string path = tokens[1];
string http_version = tokens[2];
if (http_verb == "GET") {
http_get_request_handler(path);
}
else {
close();
}
}
void http_get_request_handler(const string &path) {
response_buffer +=
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=ISO-8859-1\r\nConnection: close\r\n\r\n";
response_buffer += get_page(path);
client_socket.async_write_some(
buffer(response_buffer),
[this](const error_code& error2, size_t bytes_transferred2) {
write_handler(error2, bytes_transferred2);
});
}
void write_handler(const error_code& error, size_t bytes_transferred) {
if (!error) {
response_length_written += bytes_transferred;
if (response_length_written < response_buffer.length()) {
client_socket.async_write_some(
buffer(response_buffer.substr(response_length_written)),
[this](const error_code& error2, size_t bytes_transferred2) {
write_handler(error2, bytes_transferred2);
});
} else {
close();
}
}
}
bool has_full_request() const {
// For this toy server, we're happy if we just have the
// initial request line
auto last = begin(request_buffer) + request_length_read;
return (std::find(begin(request_buffer), last, '\n') != last);
}
void close();
private:
http_server* server;
tcp::socket client_socket;
std::array<char,MAX_REQUEST_SIZE> request_buffer;
string response_buffer;
size_t request_length_read;
size_t response_length_written;
};
class http_server {
public:
http_server(shared_ptr<io_context> io, unsigned short listen_port) :
io{io}, acceptor{*io, tcp::endpoint{tcp::v4(), listen_port}},
connections{}
{
cout << "Listening on TCP port " << listen_port << "..." << endl;
start_accept();
}
void start_accept() {
acceptor.async_accept(
[this](const error_code& error, tcp::socket client_socket) {
accept_handler(error, std::move(client_socket));
});
}
void accept_handler(const error_code& error, tcp::socket&& client_socket) {
if (!error) {
auto client_connection = make_shared<http_connection>(this, std::move(client_socket));
connections.push_back(client_connection);
client_connection->start();
}
start_accept();
}
void close(const http_connection* connection) {
connections.erase(
std::remove_if(
connections.begin(),
connections.end(),
[connection](auto& conn_ptr) { return (conn_ptr.get() == connection); }),
connections.end()
);
}
private:
shared_ptr<io_context> io;
tcp::acceptor acceptor;
std::vector<shared_ptr<http_connection>> connections;
};
void http_connection::close() {
server->close(this);
}
int main() {
auto io = make_shared<io_context>();
http_server tcpServer{io, HTTP_PORT};
io->run();
}
#pragma clang diagnostic pop