-
Notifications
You must be signed in to change notification settings - Fork 131
/
Copy pathHTTPServer.cpp
211 lines (176 loc) · 5.2 KB
/
HTTPServer.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
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
#include "HTTPServer.hpp"
namespace httpsserver {
HTTPServer::HTTPServer(const uint16_t port, const uint8_t maxConnections, const in_addr_t bindAddress):
_port(port),
_maxConnections(maxConnections),
_bindAddress(bindAddress) {
// Create space for the connections
_connections = new HTTPConnection*[maxConnections];
for(uint8_t i = 0; i < maxConnections; i++) _connections[i] = NULL;
// Configure runtime data
_socket = -1;
_running = false;
}
HTTPServer::~HTTPServer() {
// Stop the server.
// This will also remove all existing connections
if(_running) {
stop();
}
// Delete connection pointers
delete[] _connections;
}
/**
* This method starts the server and begins to listen on the port
*/
uint8_t HTTPServer::start() {
if (!_running) {
if (setupSocket()) {
_running = true;
return 1;
}
return 0;
} else {
return 1;
}
}
bool HTTPServer::isRunning() {
return _running;
}
/**
* This method stops the server
*/
void HTTPServer::stop() {
if (_running) {
// Set the flag that the server is stopped
_running = false;
// Clean up the connections
bool hasOpenConnections = true;
while(hasOpenConnections) {
hasOpenConnections = false;
for(int i = 0; i < _maxConnections; i++) {
if (_connections[i] != NULL) {
_connections[i]->closeConnection();
// Check if closing succeeded. If not, we need to call the close function multiple times
// and wait for the client
if (_connections[i]->isClosed()) {
delete _connections[i];
_connections[i] = NULL;
} else {
hasOpenConnections = true;
}
}
}
delay(1);
}
teardownSocket();
}
}
/**
* Adds a default header that is included in every response.
*
* This could be used for example to add a Server: header or for CORS options
*/
void HTTPServer::setDefaultHeader(std::string name, std::string value) {
_defaultHeaders.set(new HTTPHeader(name, value));
}
/**
* The loop method can either be called by periodical interrupt or in the main loop and handles processing
* of data
*/
void HTTPServer::loop() {
// Only handle requests if the server is still running
if(!_running) return;
// Step 1: Process existing connections
// Process open connections and store the index of a free connection
// (we might use that later on)
int freeConnectionIdx = -1;
for (int i = 0; i < _maxConnections; i++) {
// Fetch a free index in the pointer array
if (_connections[i] == NULL) {
freeConnectionIdx = i;
} else {
// if there is a connection (_connections[i]!=NULL), check if its open or closed:
if (_connections[i]->isClosed()) {
// if it's closed, clean up:
delete _connections[i];
_connections[i] = NULL;
freeConnectionIdx = i;
} else {
// if not, process it:
_connections[i]->loop();
}
}
}
// Step 2: Check for new connections
// This makes only sense if there is space to store the connection
if (freeConnectionIdx > -1) {
// We create a file descriptor set to be able to use the select function
fd_set sockfds;
// Out socket is the only socket in this set
FD_ZERO(&sockfds);
FD_SET(_socket, &sockfds);
// We define a "immediate" timeout
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0; // Return immediately, if possible
// Wait for input
// As by 2017-12-14, it seems that FD_SETSIZE is defined as 0x40, but socket IDs now
// start at 0x1000, so we need to use _socket+1 here
select(_socket + 1, &sockfds, NULL, NULL, &timeout);
// There is input
if (FD_ISSET(_socket, &sockfds)) {
int socketIdentifier = createConnection(freeConnectionIdx);
// If initializing did not work, discard the new socket immediately
if (socketIdentifier < 0) {
delete _connections[freeConnectionIdx];
_connections[freeConnectionIdx] = NULL;
}
}
}
}
int HTTPServer::createConnection(int idx) {
HTTPConnection * newConnection = new HTTPConnection(this);
_connections[idx] = newConnection;
return newConnection->initialize(_socket, &_defaultHeaders);
}
/**
* This method prepares the tcp server socket
*/
uint8_t HTTPServer::setupSocket() {
// (AF_INET = IPv4, SOCK_STREAM = TCP)
_socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket>=0) {
_sock_addr.sin_family = AF_INET;
// Listen on all interfaces
_sock_addr.sin_addr.s_addr = _bindAddress;
// Set the server port
_sock_addr.sin_port = htons(_port);
// Now bind the TCP socket we did create above to the socket address we specified
// (The TCP-socket now listens on 0.0.0.0:port)
int err = bind(_socket, (struct sockaddr* )&_sock_addr, sizeof(_sock_addr));
if(!err) {
err = listen(_socket, _maxConnections);
if (!err) {
return 1;
} else {
close(_socket);
_socket = -1;
return 0;
}
} else {
close(_socket);
_socket = -1;
return 0;
}
} else {
_socket = -1;
return 0;
}
}
void HTTPServer::teardownSocket() {
// Close the actual server sockets
close(_socket);
_socket = -1;
}
} /* namespace httpsserver */