-
Notifications
You must be signed in to change notification settings - Fork 0
/
websocket_server.h
233 lines (191 loc) · 5.91 KB
/
websocket_server.h
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
223
224
225
226
227
228
229
230
231
232
233
/*********************************************************************
* WebsocketServer class *
* *
* Version: 1.0 *
* Date: 23-07-2022 *
* Author: Dan Machado <[email protected]> *
**********************************************************************/
#ifndef WEBSOCKET_SERVER_H
#define WEBSOCKET_SERVER_H
#include <iostream>
#include <string>
#include <cstring>
#include <array>
#include <queue>
#include <map>
#include <functional>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include "sha1.hpp"
const unsigned int MAX_SIZE=2048;
//======================================================================
typedef std::function<std::string(const std::string&)> Command;
class WebsocketServer
{
public:
WebsocketServer(const char* ip, uint16_t port);
WebsocketServer(unsigned int inaddr, uint16_t port);
virtual ~WebsocketServer();
/**
* Stop the server from listening and accepting new requests
* and stop executing commands.
*/
void stop();
/**
* Return the current number of clients.
*/
int clientsListening() const;
/**
* Subscribe commands to be executed on the server side and result
* to be sent back to the specific client who requested it.
*
* Clients would be able to send requests like:
* "Command_Name: extra_data"
*
* @param commandName unique name for the command
* @param cmd a function that takes a std::string as argument and
* returns a std::string which the reserver will send back to the client
* who request the command.
*
* @see example_cmds.cpp
* @see README
*/
void subscribeCommand(const char* commandName, Command cmd);
/**
* Send data to all the clients listening.
*
* @param data string to be sent to all the clients.
* @return return true if data was set to all the clients,
* false otherwise.
*
* @see example_gps.cpp
*/
bool pushData(const std::string& data);
/**
* Loop through client requests, execute respective subscribed commands
* and send back the result to the respective client.
*
* This is a blocking method.
*/
void processRequests();
private:
struct Sockaddr
{
Sockaddr(const char* ip){
m_addr.sin_addr.s_addr=inet_addr(ip);
}
Sockaddr(unsigned int inaddr){
m_addr.sin_addr.s_addr=htonl(inaddr);
}
sockaddr_in m_addr;
};
struct Client
{
Client(int fd, std::string&& command)
:m_command(std::move(command)),
m_fd(fd)
{}
std::string m_command;
int m_fd;
};
Sockaddr m_sockAddr;
std::queue<Client> m_requests;
std::map<const std::string, Command> m_commands;
SHA1 m_sha1;
std::mutex m_controlMutex;
std::condition_variable m_cv;
std::unique_lock<std::mutex> m_ulock;
std::array<char, MAX_SIZE> m_recBuffer;
unsigned char m_pack[MAX_SIZE];
pollfd m_fds[200];
char m_hashSha1[21];
std::thread* m_trd; // look mummy he is using a raw pointer!
char* m_encode64; // look mummy look! he did it again!
unsigned int m_dataLength;
int m_fd;
int m_nfds;
int m_recDataLength;
int m_buffer64Size;
bool m_exit;
static const char* c_base64Index;
bool getData(int client);
ssize_t sendData(int client, const std::string& text);
void encoder(unsigned char a, unsigned char b, unsigned char c, int j);
WebsocketServer(WebsocketServer::Sockaddr sockAddr, uint16_t port);
void listening();
void encodeData(const std::string& text);
bool handShake(int client);
std::string unmask();
const char* base64Encode(const char* input);
void rawHash(const std::string& str);
};
//----------------------------------------------------------------------
inline WebsocketServer::WebsocketServer(const char* ip, uint16_t port)
:WebsocketServer(Sockaddr(ip), port)
{}
//----------------------------------------------------------------------
inline WebsocketServer::WebsocketServer(unsigned int inaddr, uint16_t port)
:WebsocketServer(Sockaddr(inaddr), port)
{}
//----------------------------------------------------------------------
inline WebsocketServer::~WebsocketServer()
{
delete[] m_encode64;
stop();
m_trd->join();
delete m_trd;
for (int i = 0; i<m_nfds; i++){
if(m_fds[i].fd>-1){
close(m_fds[i].fd);
}
}
}
//----------------------------------------------------------------------
inline void WebsocketServer::stop()
{
m_exit=true;
m_cv.notify_one();
}
//----------------------------------------------------------------------
inline int WebsocketServer::clientsListening() const
{
return m_nfds;
}
//----------------------------------------------------------------------
inline void WebsocketServer::subscribeCommand(const char* commandName, Command cmd)
{
m_commands[std::string(commandName)]=cmd;
}
//----------------------------------------------------------------------
inline bool WebsocketServer::getData(int client)
{
m_recDataLength=recv(client, m_recBuffer.data(), m_recBuffer.size()-1, 0);
m_recBuffer[MAX_SIZE-1]=0;
return m_recDataLength<1;
}
//----------------------------------------------------------------------
inline ssize_t WebsocketServer::sendData(int client, const std::string& data)
{
if(data.length()==0){
return 0;
}
encodeData(data);
return ::send(client, m_pack, m_dataLength, 0);
}
//----------------------------------------------------------------------
inline void WebsocketServer::encoder(unsigned char a, unsigned char b, unsigned char c, int j)
{
m_encode64[j++]=c_base64Index[a>>2];
m_encode64[j++]=c_base64Index[((a&3)<<4) | (b>>4)];
m_encode64[j++]=c_base64Index[((b&15)<<2) | (c>>6)];
m_encode64[j++]=c_base64Index[c & 63];
}
//----------------------------------------------------------------------
#endif