summaryrefslogtreecommitdiffstats
path: root/GUI/GUI/GUI/WebServer.cpp
blob: 6cce5d6597c3851cc45889568d67ef2e57eec466 (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
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include "ScopeGuard.h"
#include "WebServer.h"

#include <stdint.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

using ::Logging::Log;

namespace WebServer {
	WebServer::WebServer(wxTextCtrl* out, uint16_t port)
		: out_(out), port_(port)
	{}

	bool WebServer::RegisterPathHandler(const std::string& method,
		const std::string& path, handler_t&& handler) {
		dispatch_key_t key = GetDispatchKey(method, path);
		if (dispatch_map_.contains(key)) {
			Log(out_, "Failed to register path handler at {} {}: "
				"Handler already exists!\n", method, path);
			return false;
		}

		dispatch_map_[key] = std::move(handler);
		return true;
	}

	bool WebServer::Run(volatile bool* run) {
		WSADATA wsaData;
		int result = WSAStartup(/*version=*/MAKEWORD(2, 2), &wsaData);
		if (result) {
			Log(out_, "Failed to start winsock: {}\n", result);
			return false;
		}
		ScopeGuard wsa_cleanup([]() { WSACleanup(); });

		SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (sock == INVALID_SOCKET) {
			Log(out_, "Failed to create socket: {}\n", WSAGetLastError());
			return false;
		}
		ScopeGuard sock_cleanup([sock]() { closesocket(sock); });

		sockaddr_in saddr;
		saddr.sin_family = AF_INET;
		saddr.sin_addr.s_addr = INADDR_ANY;  // TODO(yum) loopback?
		saddr.sin_port = htons(port_);
		if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) == SOCKET_ERROR) {
			Log(out_, "Failed to bind to port {}: {}\n", port_, WSAGetLastError());
			return false;
		}

		u_long enable_nonblock = 1;
		if (ioctlsocket(sock, FIONBIO, &enable_nonblock) == SOCKET_ERROR) {
			Log(out_, "Failed to enable non-blocking socket: {}\n", WSAGetLastError());
			return false;
		}

		if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
			Log(out_, "Failed to listen on port {}: {}\n", port_, WSAGetLastError());
			return false;
		}

		Log(out_, "Server running on port {}\n", port_);

		sockaddr_in peer_addr;
		while (*run) {
			int peer_addr_sz = sizeof(peer_addr);
			SOCKET csock = accept(sock, (sockaddr*)&peer_addr, &peer_addr_sz);
			if (csock == INVALID_SOCKET) {
				int err = WSAGetLastError();
				if (err == WSAEWOULDBLOCK) {
					std::this_thread::sleep_for(std::chrono::milliseconds(10));
					continue;
				}
				Log(out_, "Accept failed: {}\n", WSAGetLastError());
				return false;
			}
			ScopeGuard csock_cleanup([csock]() { closesocket(csock); });
			char peer_ip_str[INET_ADDRSTRLEN]{};
			inet_ntop(AF_INET, &peer_addr.sin_addr, peer_ip_str, sizeof(peer_ip_str));
			Log(out_, "Connection get: peer: {}:{}\n", peer_ip_str, ntohs(peer_addr.sin_port));
			// TODO(yum) parse and send a response
		}

		return true;
	}
}