-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpsocket.cpp
More file actions
254 lines (215 loc) · 8.51 KB
/
httpsocket.cpp
File metadata and controls
254 lines (215 loc) · 8.51 KB
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#include "httpsocket.hpp"
using namespace nt::http;
#ifdef SS
# error SS is already defined
#endif
#define SS nt::utility::string::Stream()
HttpSocket::HttpSocket() :
socket(-1),
stopped(true),
handlers({})
{
}
HttpSocket::~HttpSocket()
{
for (const auto& handler: this->handlers) {
auto event = handler.first;
auto thread = handler.second;
thread->join();
delete thread;
if (!stopped) {
delete event;
}
}
std::string test = "";
}
void
HttpSocket::bind_http_port(const int port, const int max_connections)
{
this->create_socket();
this->set_socket_options();
this->bind_socket(port);
this->listen(max_connections);
this->set_access_mode();
}
void
HttpSocket::create_socket()
{
this->socket = ::socket(AF_INET, SOCK_STREAM, 0);
if (this->socket < 0) {
switch (errno) {
case EACCES:
throw std::runtime_error(SS << "Permission to create a socket of the specified type and/or protocol is denied. ");
case EAFNOSUPPORT:
throw std::runtime_error(SS << "The implementation does not support the specified address family. ");
case EINVAL:
throw std::runtime_error(SS << "Invalid flags in type. Unknown protocol, or protocol family not available. ");
case EMFILE:
throw std::runtime_error(SS << "Process file table overflow. ");
case ENFILE:
throw std::runtime_error(SS << "The system limit on the total number of open files has been reached. ");
case ENOBUFS: /* fall through */
case ENOMEM:
throw std::runtime_error(SS << "Insufficient memory is available. The socket cannot be created until sufficient resources are freed. ");
case EPROTONOSUPPORT:
throw std::runtime_error(SS << "The protocol type or the specified protocol is not supported within this domain.");
default:
throw std::runtime_error(SS << "Unable to create communication endpoint socket.");
}
}
}
void
HttpSocket::set_socket_options()
{
constexpr int one = 1;
int setoption_result = setsockopt(this->socket,
SOL_SOCKET,
SO_REUSEADDR,
(char*)&one,
sizeof(one));
if (setoption_result < 0) {
switch (errno) {
case EBADF:
throw std::runtime_error(SS << "The socket parameter is not a valid socket descriptor.");
case EFAULT:
throw std::runtime_error(SS << "Using option_value and option_length parameters would result in an attempt to access storage outside the caller's address space.");
case EINVAL:
throw std::runtime_error(SS << "The specified option is invalid at the specified socket level or the socket has been shut down.");
case ENOBUFS:
throw std::runtime_error(SS << "Insufficient system resources are available to complete the call.");
case ENOPROTOOPT:
throw std::runtime_error(SS << "The option_name parameter is unrecognized, or the level parameter is not SOL_SOCKET.");
case ENOSYS:
throw std::runtime_error(SS << "The function is not implemented. You attempted to use a function that is not yet available.");
default:
throw std::runtime_error(SS << "Unable to set socket options.");
}
}
}
void
HttpSocket::bind_socket(const int port)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
int bind_result = ::bind(this->socket, (struct sockaddr*)&addr, sizeof(addr));
if (bind_result < 0) {
switch (errno) {
case EACCES:
throw std::runtime_error(SS << "The address is protected, and the user is not the superuser. Search permission is denied on a component of the path prefix.");
case EADDRINUSE:
throw std::runtime_error(SS << "The given address is already in use.");
case EBADF:
throw std::runtime_error(SS << "sockfd is not a valid descriptor.");
case EINVAL:
throw std::runtime_error(SS << "The socket is already bound to an address or he addrlen is wrong, or the socket was not in the AF_UNIX family.");
case ENOTSOCK:
throw std::runtime_error(SS << "sockfd is a descriptor for a file, not a socket.");
#ifdef __linux__ /* The following errors are specific to UNIX domain (AF_UNIX) sockets: */
case EADDRNOTAVAIL:
throw std::runtime_error(SS << "A nonexistent interface was requested or the requested address was not local.");
case EFAULT:
throw std::runtime_error(SS << "addr points outside the user's accessible address space.");
case ELOOP:
throw std::runtime_error(SS << "Too many symbolic links were encountered in resolving addr.");
case ENAMETOOLONG:
throw std::runtime_error(SS << "addr is too long.");
case ENOENT:
throw std::runtime_error(SS << "The file does not exist.");
case ENOMEM:
throw std::runtime_error(SS << "Insufficient kernel memory was available.");
case ENOTDIR:
throw std::runtime_error(SS << "A component of the path prefix is not a directory.");
case EROFS:
throw std::runtime_error(SS << "The socket inode would reside on a read-only file system.");
#endif
default:
throw std::runtime_error(SS << "Unable to bind to socket.");
}
}
}
void
HttpSocket::listen(const int max_connections)
{
int listen_result = ::listen(this->socket, max_connections);
if (listen_result < 0) {
switch (errno) {
case EADDRINUSE:
throw std::runtime_error(SS << "Another socket is already listening on the same port. ");
case EBADF:
throw std::runtime_error(SS << "The argument sockfd is not a valid descriptor. ");
case ENOTSOCK:
throw std::runtime_error(SS << "The argument sockfd is not a socket. ");
case EOPNOTSUPP:
throw std::runtime_error(SS << "The socket is not of a type that supports the listen() operation.");
default:
throw std::runtime_error(SS << "Unable to listen to socket.");
}
}
stopped = false;
}
void
HttpSocket::set_access_mode()
{
int set_result = 0;
int flags = fcntl(this->socket, F_GETFL, 0);
if (flags >= 0) {
set_result = fcntl(this->socket, F_SETFL, flags | O_NONBLOCK);
}
if (flags < 0 || set_result < 0) {
switch (errno) {
case EACCES : /* fall through */
case EAGAIN:
throw std::runtime_error(SS << "The operation is prohibited because the file has been memory-mapped by another process.");
case EBADF:
throw std::runtime_error(SS << "fd is not an open file descriptor, or the command was F_SETLK or F_SETLKW and the file descriptor open mode doesn't match with the type of lock requested.");
case EDEADLK:
throw std::runtime_error(SS << "It was detected that the specified F_SETLKW command would cause a deadlock. ");
case EFAULT:
throw std::runtime_error(SS << "lock is outside your accessible address space. ");
default:
throw std::runtime_error(SS << "Unable to change socket access mode.");
}
}
}
void
HttpSocket::create_threads(const unsigned int num_threads,
event_callback handler,
void* dispatcher)
{
if (this->socket < 0) {
throw std::runtime_error(SS << "");
}
handlers = std::vector<std::pair<HttpEventListener*, tthread::thread*>>(num_threads);
for (unsigned int i = 0; i < num_threads; i++) {
auto event = new HttpEventListener(handler, dispatcher);
event->listen(this->socket);
auto t = new tthread::thread(HttpSocket::dispatch, (void*)event->base);
this->handlers[i] = {event, t};
}
}
void
HttpSocket::close()
{
for (auto& handler: this->handlers) {
auto event = handler.first;
if (event != nullptr) {
delete event;
handler.first = nullptr;
}
auto thread = handler.second;
if (thread != nullptr) {
// thread->kill();
thread->detach();
}
}
stopped = true;
}
void
HttpSocket::dispatch(void* base)
{
event_base_dispatch((struct event_base*)base);
}
#undef SS