HttpsServer.cpp 7.22 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
77
78
79
80
81
82
83
84
85
86
87
		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
			if (check_authkey(auth_value)) {
				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
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
			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
			if (check_authkey(auth_value)) {
				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
206
207
208
bool HttpsServer::requestHandler::check_authkey(const std::string& authkey) {
	return authkey == "qwertz" ? true : false;
}

209
210
HttpsServer::HttpsServer(restAPISettings_t restAPISettings, pluginVector_t& plugins) :
		_host(restAPISettings.restHost), _port(restAPISettings.restPort), _plugins(plugins), _handler(*this) {
211

212
213
214
215
216
217
218
219
220
221
	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);

	// Set keys
	// Currently we are only using the demo certificates provided by the OpenSSL lib...
	//ctx->set_password_callback(HttpsServer::password_callback);
	ctx->use_certificate_chain_file("../deps/openssl-1.0.2l/certs/demo/ca-cert.pem");
	ctx->use_private_key_file("../deps/openssl-1.0.2l/certs/demo/ca-cert.pem", asio::ssl::context::pem);
	ctx->use_tmp_dh_file("../deps/openssl-1.0.2l/crypto/dh/dh2048.pem");

222
	server::options options(_handler);
223
	_server = new server(options.address(_host).port(_port).context(ctx));
224
225
226
227
228
229
}

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

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