Skip to content
This repository was archived by the owner on Jun 12, 2018. It is now read-only.

Commit 28ece52

Browse files
committed
Initial commit
1 parent 4b0ad7a commit 28ece52

File tree

6 files changed

+761
-2
lines changed

6 files changed

+761
-2
lines changed

README.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,44 @@
1-
Simple-WebSocket-Server
2-
=======================
1+
Simple-Web-Server
2+
=================
3+
4+
A very simple, fast, multithreaded, platform independent WebSocket (WS) and WebSocket Secure (WSS) server implemented using C++11 and Boost.Asio. Created to be an easy way to make WebSocket endpoints in C++.
5+
6+
See also https://github.com/eidheim/Simple-Web-Server for an easy way to make REST resources available from C++ applications.
7+
8+
### Features
9+
10+
* Thread pool
11+
* Platform independent
12+
* WebSocket Secure support
13+
* Simple way to add WebSocket endpoints using regex for path, and anonymous functions
14+
* C++ bindings to the following OpenSSL methods: Base64, MD5, SHA1, SHA256 and SHA512 (found in crypt.hpp)
15+
16+
###Usage
17+
18+
See main_ws.cpp or main_wss.cpp for example usage.
19+
20+
### Dependencies
21+
22+
Boost C++ libraries must be installed, go to http://www.boost.org for download and instructions.
23+
24+
OpenSSL libraries from https://www.openssl.org are required.
25+
26+
Will update to use C++17 networking instead in the future when it is supported by g++.
27+
28+
### Compile and run
29+
30+
Compile with a C++11 compiler supporting regex (for instance g++ 4.9):
31+
32+
#### WS
33+
34+
g++ -O3 -std=c++11 -lboost_system -lcrypto main_ws.cpp -o ws_server
35+
36+
Then to run the server: ./ws_server
37+
38+
#### WSS
39+
40+
g++ -O3 -std=c++11 -lboost_system -lssl -lcrypto main_wss.cpp -o wss_server
41+
42+
Before running the server, an RSA private key (server.key) and an SSL certificate (server.crt) must be created. Follow, for instance, the instructions given here (for a self-signed certificate): http://www.akadia.com/services/ssh_test_certificate.html
43+
44+
Then to run the server: ./wss_server

crypt.hpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#ifndef CRYPT_HPP
2+
#define CRYPT_HPP
3+
4+
#include <openssl/evp.h>
5+
#include <openssl/buffer.h>
6+
7+
#include <openssl/sha.h>
8+
#include <openssl/md5.h>
9+
10+
#include <string>
11+
#include <vector>
12+
#include <cmath>
13+
14+
namespace SimpleWeb {
15+
namespace Crypt {
16+
namespace Base64 {
17+
std::string encode(const std::string& ascii) {
18+
BIO *bio, *b64;
19+
BUF_MEM *bptr;
20+
21+
b64 = BIO_new(BIO_f_base64());
22+
bio = BIO_new(BIO_s_mem());
23+
BIO_push(b64, bio);
24+
BIO_get_mem_ptr(b64, &bptr);
25+
26+
//Write directly to base64-string buffer to avoid copy
27+
std::string base64;
28+
int base64_length=round(4*ceil((double)ascii.length()/3.0));
29+
base64.resize(base64_length);
30+
bptr->length=0;
31+
bptr->max=base64_length+1;
32+
bptr->data=&base64[0];
33+
34+
BIO_write(b64, ascii.data(), ascii.length());
35+
BIO_flush(b64);
36+
37+
//To not keep &base64[0] through BIO_free_all(b64)
38+
bptr->length=0;
39+
bptr->max=0;
40+
bptr->data=nullptr;
41+
42+
BIO_free_all(b64);
43+
44+
return base64;
45+
};
46+
std::string decode(const std::string& base64) {
47+
std::string ascii;
48+
//Resize resulting ascii-string, however, the size is a up to two bytes too large.
49+
ascii.resize((6*base64.length())/8);
50+
BIO *b64, *bio;
51+
52+
b64 = BIO_new(BIO_f_base64());
53+
BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
54+
bio = BIO_new_mem_buf((char*)&base64[0], base64.length());
55+
bio = BIO_push(b64, bio);
56+
57+
int decoded_length = BIO_read(bio, &ascii[0], ascii.length());
58+
ascii.resize(decoded_length);
59+
60+
BIO_free_all(b64);
61+
62+
return ascii;
63+
};
64+
}
65+
66+
std::string MD5(const std::string& text) {
67+
std::string encoded;
68+
encoded.resize(128/8);
69+
70+
MD5_CTX context;
71+
MD5_Init(&context);
72+
MD5_Update(&context, (char*)&text[0], text.length());
73+
MD5_Final((unsigned char*)&encoded[0], &context);
74+
return encoded;
75+
}
76+
77+
std::string SHA1(const std::string& text) {
78+
std::string encoded;
79+
encoded.resize(160/8);
80+
81+
SHA_CTX context;
82+
SHA1_Init(&context);
83+
SHA1_Update(&context, (char*)&text[0], text.length());
84+
SHA1_Final((unsigned char*)&encoded[0], &context);
85+
return encoded;
86+
}
87+
88+
std::string SHA256(const std::string& text) {
89+
std::string encoded;
90+
encoded.resize(256/8);
91+
92+
SHA256_CTX context;
93+
SHA256_Init(&context);
94+
SHA256_Update(&context, (char*)&text[0], text.length());
95+
SHA256_Final((unsigned char*)&encoded[0], &context);
96+
return encoded;
97+
}
98+
99+
std::string SHA512(const std::string& text) {
100+
std::string encoded;
101+
encoded.resize(512/8);
102+
103+
SHA512_CTX context;
104+
SHA512_Init(&context);
105+
SHA512_Update(&context, (char*)&text[0], text.length());
106+
SHA512_Final((unsigned char*)&encoded[0], &context);
107+
return encoded;
108+
}
109+
}
110+
}
111+
#endif /* CRYPT_HPP */
112+

main_ws.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "server_ws.hpp"
2+
3+
using namespace std;
4+
using namespace SimpleWeb;
5+
6+
int main() {
7+
//WebSocket (WS)-server at port 8080 using 4 threads
8+
Server<WS> server(8080, 4);
9+
10+
//Example 1: echo WebSocket endpoint
11+
// Added debug messages for example use of the callbacks
12+
// Test with the following JavaScript:
13+
// var ws=new WebSocket("ws://localhost:8080/echo");
14+
// ws.onmessage=function(evt){console.log(evt.data);};
15+
// ws.send("test");
16+
auto& callbacks_echo=server.endpoint["^/echo/?$"];
17+
18+
callbacks_echo.onmessage=[&server](Connection& connection) {
19+
//To receive message from client as string (message_stream.str())
20+
stringstream message_stream;
21+
*connection.message >> message_stream.rdbuf();
22+
23+
string response=message_stream.str()+" from "+to_string((size_t)connection.id);
24+
25+
stringstream response_stream;
26+
response_stream << response;
27+
28+
//server.send is an asynchronous function
29+
server.send(connection.id, response_stream, [](const boost::system::error_code& ec){
30+
if(!ec)
31+
cout << "Message sent successfully" << endl;
32+
else {
33+
cout << "Error sending message. ";
34+
//See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
35+
cout << "Error: " << ec << ", error message: " << ec.message() << endl;
36+
}
37+
});
38+
};
39+
40+
callbacks_echo.onopen=[&server](Connection& connection) {
41+
cout << "Opened connection to " << (size_t)connection.id << endl;
42+
};
43+
44+
//See RFC 6455 7.4.1. for status codes
45+
callbacks_echo.onclose=[](Connection& connection, int status) {
46+
cout << "Closed connection to " << (size_t)connection.id << " with status code " << status << endl;
47+
};
48+
49+
//See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
50+
callbacks_echo.onerror=[](Connection& connection, const boost::system::error_code& ec) {
51+
cout << "Error in connection " << (size_t)connection.id << ". ";
52+
cout << "Error: " << ec << ", error message: " << ec.message() << endl;
53+
};
54+
55+
56+
//Example 2: Echo to all WebSocket endpoints
57+
// Sending received messages to all connected clients
58+
// Test with the following JavaScript on more than one browser windows:
59+
// var ws=new WebSocket("ws://localhost:8080/echo_all");
60+
// ws.onmessage=function(evt){console.log(evt.data);};
61+
// ws.send("test");
62+
auto& callbacks_echo_all=server.endpoint["^/echo_all/?$"];
63+
callbacks_echo_all.onmessage=[&server](Connection& connection) {
64+
//To receive message from client as string (message_stream.str())
65+
stringstream message_stream;
66+
*connection.message >> message_stream.rdbuf();
67+
68+
string response=message_stream.str()+" from "+to_string((size_t)connection.id);
69+
70+
for(auto connection_id: server.get_connection_ids()) {
71+
stringstream response_stream;
72+
response_stream << response;
73+
74+
//server.send is an asynchronous function
75+
server.send(connection_id, response_stream);
76+
}
77+
};
78+
79+
//Start WS-server
80+
server.start();
81+
82+
return 0;
83+
}

main_wss.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "server_wss.hpp"
2+
3+
using namespace std;
4+
using namespace SimpleWeb;
5+
6+
int main() {
7+
//WebSocket Secure (WSS)-server at port 8080 using 4 threads
8+
Server<WSS> server(8080, 4, "server.crt", "server.key");
9+
10+
//Example 1: echo WebSocket Secure endpoint
11+
// Added debug messages for example use of the callbacks
12+
// Test with the following JavaScript:
13+
// var wss=new WebSocket("wss://localhost:8080/echo");
14+
// wss.onmessage=function(evt){console.log(evt.data);};
15+
// wss.send("test");
16+
auto& callbacks_echo=server.endpoint["^/echo/?$"];
17+
18+
callbacks_echo.onmessage=[&server](Connection& connection) {
19+
//To receive message from client as string (message_stream.str())
20+
stringstream message_stream;
21+
*connection.message >> message_stream.rdbuf();
22+
23+
string response=message_stream.str()+" from "+to_string((size_t)connection.id);
24+
25+
stringstream response_stream;
26+
response_stream << response;
27+
28+
//server.send is an asynchronous function
29+
server.send(connection.id, response_stream, [](const boost::system::error_code& ec){
30+
if(!ec)
31+
cout << "Message sent successfully" << endl;
32+
else {
33+
cout << "Error sending message. ";
34+
//See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
35+
cout << "Error: " << ec << ", error message: " << ec.message() << endl;
36+
}
37+
});
38+
};
39+
40+
callbacks_echo.onopen=[&server](Connection& connection) {
41+
cout << "Opened connection to " << (size_t)connection.id << endl;
42+
};
43+
44+
//See RFC 6455 7.4.1. for status codes
45+
callbacks_echo.onclose=[](Connection& connection, int status) {
46+
cout << "Closed connection to " << (size_t)connection.id << " with status code " << status << endl;
47+
};
48+
49+
//See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
50+
callbacks_echo.onerror=[](Connection& connection, const boost::system::error_code& ec) {
51+
cout << "Error in connection " << (size_t)connection.id << ". ";
52+
cout << "Error: " << ec << ", error message: " << ec.message() << endl;
53+
};
54+
55+
56+
//Example 2: Echo to all WebSocket Secure endpoints
57+
// Sending received messages to all connected clients
58+
// Test with the following JavaScript on more than one browser windows:
59+
// var wss=new WebSocket("wss://localhost:8080/echo_all");
60+
// wss.onmessage=function(evt){console.log(evt.data);};
61+
// wss.send("test");
62+
auto& callbacks_echo_all=server.endpoint["^/echo_all/?$"];
63+
callbacks_echo_all.onmessage=[&server](Connection& connection) {
64+
//To receive message from client as string (message_stream.str())
65+
stringstream message_stream;
66+
*connection.message >> message_stream.rdbuf();
67+
68+
string response=message_stream.str()+" from "+to_string((size_t)connection.id);
69+
70+
for(auto connection_id: server.get_connection_ids()) {
71+
stringstream response_stream;
72+
response_stream << response;
73+
74+
//server.send is an asynchronous function
75+
server.send(connection_id, response_stream);
76+
}
77+
};
78+
79+
//Start WSS-server
80+
server.start();
81+
82+
return 0;
83+
}

0 commit comments

Comments
 (0)