summaryrefslogtreecommitdiffstats
path: root/src/uscxml/server/HTTPServer.h
blob: 3d7f6b437ed48c6f2d91994bb4f0616abc58481e (plain)
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
/**
 *  @file
 *  @author     2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
 *  @copyright  Simplified BSD
 *
 *  @cond
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the FreeBSD license as published by the FreeBSD
 *  project.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 *  You should have received a copy of the FreeBSD license along with this
 *  program. If not, see <http://www.opensource.org/licenses/bsd-license>.
 *  @endcond
 */

#ifndef HTTPSERVER_H_AIH108EG
#define HTTPSERVER_H_AIH108EG

#include <stddef.h>                     // for NULL

#include <map>                          // for map, map<>::iterator, etc
#include <string>                       // for string, operator<
#include <thread>
#include <mutex>

extern "C" {
#include "event2/util.h"                // for evutil_socket_t
#include <event2/http.h>                // for evhttp_request
#include <evws.h>
}

#include "uscxml/Common.h"              // for USCXML_API
#include "uscxml/messages/Event.h"      // for Data, Event
#include "uscxml/config.h"              // for OPENSSL_FOUND

namespace uscxml {

class HTTPServlet;
class WebSocketServlet;

class USCXML_API HTTPServer {
public:
	class Request : public Event {
	public:
		Request() : evhttpReq(NULL) {}
		std::string content;
		struct evhttp_request* evhttpReq;

		operator bool() {
			return evhttpReq != NULL;
		}
	};

	class USCXML_API SSLConfig {
	public:
		SSLConfig() : port(8443) {}
		std::string privateKey;
		std::string publicKey;
		unsigned short port;
	};

	class WSFrame : public Event {
	public:
		WSFrame() : evwsConn(NULL) {}
		std::string content;
		struct evws_connection* evwsConn;
	};

	class USCXML_API Reply {
	public:
		Reply() : status(200), type("get"), evhttpReq(NULL) {}
		Reply(Request req) : status(200), type(req.data.compound["type"].atom), evhttpReq(req.evhttpReq) {}

		void setRequest(Request req) {
			type = req.data.compound["type"].atom;
			evhttpReq = req.evhttpReq;
		}

		int status;
		std::string type;
		std::map<std::string, std::string> headers;
		std::string content;
		struct evhttp_request* evhttpReq;
	};

	struct CallbackData {
		HTTPServlet* servlet;
		evhttp_request* httpReq;
	};

	enum ServerType {
		HTTPS,
		HTTP,
		WebSockets
	};

	static HTTPServer* getInstance(unsigned short port, unsigned short wsPort, SSLConfig* sslConf = NULL);
	static HTTPServer* getInstance() {
		return getInstance(0, 0, NULL);
	}

	static std::string getBaseURL(ServerType type = HTTP);

	static void reply(const Reply& reply);
	static void wsSend(struct evws_connection *conn, enum evws_opcode opcode, const char *data, uint64_t length);
	static void wsBroadcast(const char *uri, enum evws_opcode opcode, const char *data, uint64_t length);

	static bool registerServlet(const std::string& path, HTTPServlet* servlet); ///< Register a servlet, returns false if path is already taken
	static void unregisterServlet(HTTPServlet* servlet);

	static bool registerServlet(const std::string& path, WebSocketServlet* servlet); ///< Register a servlet, returns false if path is already taken
	static void unregisterServlet(WebSocketServlet* servlet);

private:

	class WSData {
	public:
		WSData(struct evws_connection *conn_, const char *uri_, enum evws_opcode opcode_, const char *data_, uint64_t length_) {
			conn = conn_;
			if (uri_)
				uri = uri_;
			opcode = opcode_;
			data = std::string(data_, length_);
		}
		struct evws_connection *conn;
		std::string data;
		std::string uri;
		evws_opcode opcode;
	};

	struct comp_strsize_less {
		bool operator()(std::string const& l, std::string const& r) const {
			if (l.size() < r.size())
				return false;
			return true;
//			return l < r;
		};
	};

	HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* sslConf);
	virtual ~HTTPServer();

	void start();
	void stop();
	static void run(void* instance);

	void determineAddress();

	static void replyCallback(evutil_socket_t fd, short what, void *arg);
	static void wsSendCallback(evutil_socket_t fd, short what, void *arg);

	static void httpRecvReqCallback(struct evhttp_request *req, void *callbackData);
	static void wsRecvReqCallback(struct evws_connection *conn, struct evws_frame *, void *callbackData);

	void processByMatchingServlet(const Request& request);
	void processByMatchingServlet(evws_connection* conn, const WSFrame& frame);

	static std::map<std::string, std::string> mimeTypes;
	std::map<std::string, HTTPServlet*> _httpServlets;
	typedef std::map<std::string, HTTPServlet*>::iterator http_servlet_iter_t;

	std::map<std::string, WebSocketServlet*> _wsServlets;
	typedef std::map<std::string, WebSocketServlet*>::iterator ws_servlet_iter_t;

	struct event_base* _base;
	struct evhttp* _http;
	struct evws* _evws;
    struct event* _dummyEvent;

	struct evhttp_bound_socket* _httpHandle;
	evutil_socket_t _wsHandle;

	unsigned short _port;
	unsigned short _wsPort;
	std::string _address;

	static HTTPServer* _instance;

	static std::recursive_mutex _instanceMutex;
	std::thread* _thread;
	std::recursive_mutex _mutex;
	bool _isRunning;

	friend class HTTPServlet;
	friend class WebSocketServlet;

#ifdef HTTPS_ENABLED
	struct evhttp* _https;
	struct evhttp_bound_socket* _sslHandle;
	unsigned short _sslPort;

	static struct bufferevent* sslBufferEventCallback(struct event_base *base, void *arg);
#endif
};

class USCXML_API HTTPServlet {
public:
	virtual ~HTTPServlet() {}
	virtual bool requestFromHTTP(const HTTPServer::Request& request) = 0;
	virtual void setURL(const std::string& url) = 0; /// Called by the server with the actual URL
	virtual bool canAdaptPath() {
		return true;
	}
};

class USCXML_API WebSocketServlet {
public:
	virtual ~WebSocketServlet() {}
	virtual bool requestFromWS(struct evws_connection *conn, const HTTPServer::WSFrame& frame) = 0;
	virtual void setURL(const std::string& url) = 0; /// Called by the server with the actual URL
	virtual bool canAdaptPath() {
		return true;
	}
};

}

#endif /* end of include guard: HTTPSERVER_H_AIH108EG */