summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/http.h
blob: 6c845757757a217ec2acd75fefcb163fed286e0b (plain) (blame)
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
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

http.cpp

HTTP server handling

***************************************************************************/

#pragma once

#ifndef __EMU_H__
#error Dont include this file directly; include emu.h instead.
#endif

#ifndef MAME_EMU_HTTP_H
#define MAME_EMU_HTTP_H

#include <thread>
#include <time.h>
#include "server_http.hpp"
#include "server_ws.hpp"

//**************************************************************************
//    TYPE DEFINITIONS
//**************************************************************************

// ======================> http_manager
namespace asio
{
	class io_context;
}

class http_manager
{
	DISABLE_COPYING(http_manager);
public:

	/** An HTTP Request. */
	struct http_request
	{
		/** Retrieves the requested resource. */
		virtual const std::string get_resource() = 0; // The entire resource: path, query and fragment.

		/** Returns the path part of the requested resource. */
		virtual const std::string get_path() = 0;

		/** Returns the query part of the requested resource. */
		virtual const std::string get_query() = 0;

		/** Returns the fragment part of the requested resource. */
		virtual const std::string get_fragment() = 0;

		/** Retrieves a header from the HTTP request. */
		virtual const std::string get_header(const std::string &header_name) = 0;

		/** Retrieves a header from the HTTP request. */
		virtual const std::list<std::string> get_headers(const std::string &header_name) = 0;

		/** Returns the body that was submitted with the HTTP request. */
		virtual const std::string get_body() = 0;
	};
	typedef std::shared_ptr<http_request> http_request_ptr;

	/** An HTTP response. */
	struct http_response
	{
		/** Sets the HTTP status to be returned to the client. */
		virtual void set_status(int status) = 0;

		/** Sets the HTTP content type to be returned to the client. */
		virtual void set_content_type(const std::string &type) = 0;

		/** Sets the body to be sent to the client. */
		virtual void set_body(const std::string &body) = 0;

		/** Appends something to the body to be sent to the client. */
		virtual void append_body(const std::string &body) = 0;
	};
	typedef std::shared_ptr<http_response> http_response_ptr;

	/** Identifies a Websocket connection. */
	struct websocket_connection
	{
		/** Sends a message to the client that is connected on the other end of this Websocket connection. */
		virtual void send_message(const std::string &payload, int opcode) = 0;

		/** Closes this open Websocket connection. */
		virtual void close() = 0;

		virtual ~websocket_connection() { }
	};
	typedef std::shared_ptr<websocket_connection> websocket_connection_ptr;

	/** Handles opening an incoming Websocket connection. */
	typedef std::function<void(websocket_connection_ptr connection)> websocket_open_handler;

	/** Handles an incoming message on an open Websocket connection. */
	typedef std::function<void(websocket_connection_ptr connection, const std::string &payload, int opcode)> websocket_message_handler;

	/** Handles when the client has closed a websocket connection. */
	typedef std::function<void(websocket_connection_ptr connection, int status, const std::string& reason)> websocket_close_handler;

	/** Handles when there has been an error on a websocket connection. */
	typedef std::function<void(websocket_connection_ptr connection, const std::error_code& error_code)> websocket_error_handler;

	/** Handles an incoming HTTP request, generating an HTTP response. */
	typedef std::function<void(http_request_ptr, http_response_ptr)> http_handler;

	struct websocket_endpoint : public std::enable_shared_from_this<websocket_endpoint> {
		websocket_open_handler on_open;
		websocket_message_handler on_message;
		websocket_close_handler on_close;
		websocket_error_handler on_error;
	};
	typedef std::shared_ptr<websocket_endpoint> websocket_endpoint_ptr;

	/** Substitutes one string with another, and returns whether the substitution should be performed.
	 * Used when evaluating a template. */
	typedef std::function<bool(std::string &)> substitution;

	http_manager(bool active, short port, const char *root);
	virtual ~http_manager();
	void clear();

	/** Adds a template to the web server. When the path is requested, the template will be read and parsed:
	 * strings between each pair of <init, term> characters will be passed to the substitute function and the result
	 * will be used instead.
	 */
	void add_template(const std::string &path, substitution substitute, char init, char term);

	/** Removes a template from the web server. */
	void remove_template(const std::string &path);

	/** Serves a template at an explicit path, which may be diffrent from the request path, under the document root.
	 * The template will be read and parsed:
	 * strings between each pair of <init, term> characters will be passed to the substitute function and the result
	 * will be used instead.
	 */
	void serve_template(http_request_ptr request, http_response_ptr response, const std::string &path, substitution substitute, char init, char term);

	void serve_document(http_request_ptr request, http_response_ptr response, const std::string &path);

	/** Adds an HTTP handler. When the specified path is requested, the specified HTTP handler will be called. */
	void add_http_handler(const std::string &path, http_handler handler);

	/** Removes the HTTP handler at the specified path. */
	void remove_http_handler(const std::string &path);

	/** Retrieves a websocket endpoint, possibly adding it if it does not already exist. */
	websocket_endpoint_ptr add_endpoint(const std::string &path, websocket_open_handler on_open, websocket_message_handler on_message, websocket_close_handler on_close, websocket_error_handler on_error);

	/** Removes the websocket endpoint at the specified path. */
	void remove_endpoint(const std::string &path);

	bool is_active() {
		return m_active;
	}

private:
	void on_open(http_manager::websocket_endpoint_ptr endpoint, std::shared_ptr<webpp::Connection> connection);

	void on_message(http_manager::websocket_endpoint_ptr endpoint, std::shared_ptr<webpp::Connection> connection, const std::string& payload, int opcode);

	void on_close(http_manager::websocket_endpoint_ptr endpoint, std::shared_ptr<webpp::Connection> connection, int status, const std::string& reason);

	void on_error(http_manager::websocket_endpoint_ptr endpoint, std::shared_ptr<webpp::Connection> connection, const std::error_code& error_code);

	bool read_file(std::ostream &os, const std::string &path);

	bool m_active;

	std::shared_ptr<asio::io_context>   m_io_context;
	std::unique_ptr<webpp::http_server> m_server;
	std::unique_ptr<webpp::ws_server>   m_wsserver;
	std::thread                         m_server_thread;
	std::string                         m_root;

	std::unordered_map<std::string, http_handler> m_handlers;
	std::mutex                                    m_handlers_mutex;

	std::unordered_map<std::string, websocket_endpoint_ptr> m_endpoints;
	std::mutex                                              m_endpoints_mutex;

	std::unordered_map<void *, websocket_connection_ptr> m_connections;  // the keys are really webpp::ws_server::Connection pointers
	std::mutex                                           m_connections_mutex;

};


#endif  /* MAME_EMU_HTTP_H */