HttpsServer.cpp 7.25 KB
Newer Older
1
2
3
4
5
6
7
8
/*
 * HttpsServer.cpp
 *
 *  Created on: 25.05.2018
 *      Author: Micha Mueller
 */

#include "HttpsServer.h"
9

10
#include <iostream>
11
12
#include <memory>
#include <functional>
13

14
15
HttpsServer::requestHandler::requestHandler(HttpsServer& httpsServer) :	_httpsServer(httpsServer) {}

16
void HttpsServer::requestHandler::operator()(server::request const &request, server::connection_ptr connection) {
17
	//first log some info about client
18
19
	server::string_type ip = source(request);
	unsigned int port = request.source_port;
20
	server::string_type method = request.method;
21
	std::ostringstream data;
22
	connection->set_status(server::connection::internal_server_error);
23

24
25
	LOG(info) << "HttpsServer: " << ip << ":" << port << " connected";

26
27
28
29
	boost::network::uri::uri uri("https://" + request.destination);
	server::string_type path = uri.path();
	server::string_type query = uri.query();

30
	//select code depending on request
31
32
	if (method == "GET") {
		LOG(info) << "HttpsServer: GET request of " << request.destination << " was made";
33
34
35

		std::string response = "";

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
		std::string plugin = path;
		std::string sensor = "";
		std::string action = "";
		std::string auth_key = query;
		std::string auth_value = "";

		//do some string processing
		//split up query into key and value
		size_t pos = auth_key.find("=");
		if (pos != std::string::npos) {
			auth_value = auth_key.substr(pos+1);
			auth_key.erase(pos);
		}
		//split up path into plugin, sensor and action
		if (plugin.size() >= 2) {
			if (plugin[0] == '/') {
				plugin.erase(0,1);
			}
			if (plugin[plugin.size() -1] == '/') {
				plugin.erase(plugin.size() -1);
			}

			pos = plugin.find("/");
			if (pos != std::string::npos) {
				sensor = plugin.substr(pos+1);
				plugin.erase(pos);
			}

			pos = sensor.find("/");
			if (pos != std::string::npos) {
				action = sensor.substr(pos+1);
				sensor.erase(pos);
			}
		}
		//finished string processing

		//process actual request
		//check if query and action are valid values
		if (auth_key == "authkey" && (action == "avg")) {

			//check if authkey is valid
77
			if (_httpsServer.check_authkey(auth_value, permission::GETReq)) {
78
79
80
81
82
83
84
85
86
87
				response = "Plugin not found!";
				connection->set_status(server::connection::not_found);

				for(auto& p : _httpsServer._plugins) {
					if (p.id == plugin) {
						response = "Sensor not found!";
						for(auto s : p.configurator->getSensors()) {
							if (s->getName() == sensor) {
								uint64_t avg = 0;
								const reading_t * const cache = s->getCache();
88
								unsigned size = s->getCacheSize();
89

90
								for(unsigned i = 0; i < size; i++) {
91
92
									avg += cache[i].value;
								}
93
								avg /= size;
94

95
								response = plugin + "::" + sensor + " Average of last " + std::to_string(size) + " values is " + std::to_string(avg);
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
								connection->set_status(server::connection::ok);
								goto end;
							}
						}
					}
				}
			} else {
				response = "Invalid authentication key!";
				connection->set_status(server::connection::unauthorized);
			}

		} else {
			response = "GET requests should be of the form \"host:port/[plugin]/[sensor]/[action]?authkey=[token]\"\n"
					"Where [plugin] names a plugin, [sensor] a sensor within this plugin, [action] is 'avg' and [token] is your authentication key";
			connection->set_status(server::connection::bad_request);
		}

		end:
		LOG(info) << "HttpsServer: Responding: " << response;
		data << response << std::endl;

	} else if (method == "PUT") {
		LOG(info) << "HttpsServer: PUT request to " << request.destination << " was made";

		std::string response = "";
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

		std::string plugin = path;
		std::string action = "";
		std::string auth_key = query;
		std::string auth_value = "";

		//do some string processing
		//split up query into key and value
		size_t pos = auth_key.find("=");
		if (pos != std::string::npos) {
			auth_value = auth_key.substr(pos+1);
			auth_key.erase(pos);
		}
		//split up path into plugin and action
		if (plugin.size() >= 2) {
			if (plugin[0] == '/') {
				plugin.erase(0,1);
			}
			if (plugin[plugin.size() -1] == '/') {
				plugin.erase(plugin.size() -1);
			}
142

143
144
145
146
147
148
149
150
151
152
153
154
155
			pos = plugin.find("/");
			if (pos != std::string::npos) {
				action = plugin.substr(pos+1);
				plugin.erase(pos);
			}
		}
		//finished string processing

		//process actual request
		//check if query and action are valid values
		if (auth_key == "authkey" && ((action == "start") || (action == "stop"))) {

			//check if authkey is valid
156
			if (_httpsServer.check_authkey(auth_value, permission::PUTReq)) {
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
				response = "Plugin not found!";
				connection->set_status(server::connection::not_found);

				for(auto& p : _httpsServer._plugins) {
					if (p.id == plugin) {
						if (action == "start") {
							for(auto s : p.configurator->getSensors()) {
								s->startPolling();
							}
							response = "Plugin " + plugin + ": Sensors started";
							connection->set_status(server::connection::ok);
						} else if (action == "stop") {
							for(auto s : p.configurator->getSensors()) {
								s->stopPolling();
							}
							response = "Plugin " + plugin + ": Sensors stopped";
							connection->set_status(server::connection::ok);
						}
					}
				}
			} else {
				response = "Invalid authentication key!";
				connection->set_status(server::connection::unauthorized);
			}
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
		} else {
			response = "PUT requests should be of the form \"host:port/[plugin]/[action]?authkey=[token]\"\n"
					"Where [plugin] names a plugin, [action] is either \"start\" or \"stop\" and [token] is your authentication key";
			connection->set_status(server::connection::bad_request);
		}

		LOG(info) << "HttpsServer: Responding: " << response;
		data << response << std::endl;
	} else {
		LOG(info) << "HttpsServer: Received unsupported " << method << " request";
		connection->set_status(server::connection::not_supported);
	}

	//send response
	server::response_header headers[] = { {"Connection", "close"}, {"Content-Type", "text/plain"} };
197
198
199
200
201
202
203
	connection->set_headers(boost::make_iterator_range(headers, headers + 2));
	connection->write(data.str());
}

void HttpsServer::requestHandler::log(const server::string_type& message) {
	LOG(error) << message;
}
204

205
HttpsServer::HttpsServer(restAPISettings_t restAPISettings, pluginVector_t& plugins) :
206
		_plugins(plugins), _handler(*this) {
207

208
209
210
	std::shared_ptr<asio::ssl::context> ctx = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
	ctx->set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv3 | asio::ssl::context::single_dh_use);

211
	// Set certificate, private key and DH parameters
212
	//ctx->set_password_callback(HttpsServer::password_callback);
213
214
215
	ctx->use_certificate_chain_file(restAPISettings.certificate);
	ctx->use_private_key_file(restAPISettings.privateKey, asio::ssl::context::pem);
	ctx->use_tmp_dh_file(restAPISettings.dhFile);
216

217
	server::options options(_handler);
218
	_server = new server(options.address(restAPISettings.restHost).port(restAPISettings.restPort).context(ctx));
219
220
221
222
223
224
}

HttpsServer::~HttpsServer() {
	delete _server;
}

225
226
227
228
229
230
231
232
bool HttpsServer::check_authkey(const std::string& authkey, permission requiredPerm) {
	authkeyMap_t::iterator it = _authkeys.find(authkey);
	if (it != _authkeys.end()) {
		return it->second[requiredPerm];
	}
	return false;
}

233
234
235
236
237
/*
std::string HttpsServer::password_callback(std::size_t max_length, asio::ssl::context_base::password_purpose purpose) {
    return std::string("pwd");
}
*/