|  | @@ -0,0 +1,150 @@
 | 
	
		
			
				|  |  | +#include <iostream>
 | 
	
		
			
				|  |  | +#include <memory>
 | 
	
		
			
				|  |  | +#include <mutex>
 | 
	
		
			
				|  |  | +#include <sstream>
 | 
	
		
			
				|  |  | +#include <unistd.h>
 | 
	
		
			
				|  |  | +#include <unordered_map>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "CivetServer.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class WebSocketBase : public CivetWebSocketHandler
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  public:
 | 
	
		
			
				|  |  | +	explicit WebSocketBase(std::string name) : name_(std::move(name))
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	bool
 | 
	
		
			
				|  |  | +	handleConnection(CivetServer *server, const mg_connection *conn) override
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		return true;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	void
 | 
	
		
			
				|  |  | +	handleReadyState(CivetServer *server, mg_connection *conn) override
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		std::unique_lock<std::mutex> lock(mutex_);
 | 
	
		
			
				|  |  | +		pool_.emplace(conn, std::make_shared<std::mutex>());
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	bool
 | 
	
		
			
				|  |  | +	handleData(CivetServer *server,
 | 
	
		
			
				|  |  | +	           mg_connection *conn,
 | 
	
		
			
				|  |  | +	           int bits,
 | 
	
		
			
				|  |  | +	           char *data,
 | 
	
		
			
				|  |  | +	           size_t data_len) override
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if ((bits & 0x0F) == MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
 | 
	
		
			
				|  |  | +			return false;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		data_.write(data, data_len);
 | 
	
		
			
				|  |  | +		if (current_opcode_ == 0x00) {
 | 
	
		
			
				|  |  | +			current_opcode_ = bits & 0x7f;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		bool is_final_fragment = bits & 0x80;
 | 
	
		
			
				|  |  | +		if (is_final_fragment) {
 | 
	
		
			
				|  |  | +			switch (current_opcode_) {
 | 
	
		
			
				|  |  | +			case MG_WEBSOCKET_OPCODE_TEXT:
 | 
	
		
			
				|  |  | +				std::cout << data_.str() << std::endl; // print request data
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +			default:
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			current_opcode_ = 0x00;
 | 
	
		
			
				|  |  | +			this->sendData(conn, data_.str()); // response
 | 
	
		
			
				|  |  | +			data_.clear();
 | 
	
		
			
				|  |  | +			data_.str(std::string());
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		return true;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	void
 | 
	
		
			
				|  |  | +	handleClose(CivetServer *server, const mg_connection *conn) override
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		auto *connection = const_cast<mg_connection *>(conn);
 | 
	
		
			
				|  |  | +		std::shared_ptr<std::mutex> user_lock;
 | 
	
		
			
				|  |  | +		{
 | 
	
		
			
				|  |  | +			std::unique_lock<std::mutex> lock(mutex_);
 | 
	
		
			
				|  |  | +			user_lock = pool_[connection];
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		{
 | 
	
		
			
				|  |  | +			std::unique_lock<std::mutex> lock_connection(*user_lock);
 | 
	
		
			
				|  |  | +			std::unique_lock<std::mutex> lock(mutex_);
 | 
	
		
			
				|  |  | +			pool_.erase(connection);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	bool
 | 
	
		
			
				|  |  | +	sendData(mg_connection *conn,
 | 
	
		
			
				|  |  | +	         const std::string &data,
 | 
	
		
			
				|  |  | +	         bool skippable = false,
 | 
	
		
			
				|  |  | +	         int op_code = MG_WEBSOCKET_OPCODE_TEXT)
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		std::shared_ptr<std::mutex> connection_lock;
 | 
	
		
			
				|  |  | +		{
 | 
	
		
			
				|  |  | +			std::unique_lock<std::mutex> lock(mutex_);
 | 
	
		
			
				|  |  | +			connection_lock = pool_[conn];
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if (!connection_lock->try_lock()) {
 | 
	
		
			
				|  |  | +			if (skippable) {
 | 
	
		
			
				|  |  | +				return false;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			connection_lock->lock();
 | 
	
		
			
				|  |  | +			std::unique_lock<std::mutex> lock(mutex_);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		int ret = mg_websocket_write(conn, op_code, data.c_str(), data.size());
 | 
	
		
			
				|  |  | +		connection_lock->unlock();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if (ret != static_cast<int>(data.size())) {
 | 
	
		
			
				|  |  | +			if (data.empty() && ret == 2) {
 | 
	
		
			
				|  |  | +				return true;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			return false;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		return true;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	thread_local static std::stringstream data_;
 | 
	
		
			
				|  |  | +	thread_local static unsigned char current_opcode_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  private:
 | 
	
		
			
				|  |  | +	const std::string name_;
 | 
	
		
			
				|  |  | +	mutable std::mutex mutex_;
 | 
	
		
			
				|  |  | +	std::unordered_map<mg_connection *, std::shared_ptr<std::mutex>> pool_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +thread_local unsigned char WebSocketBase::current_opcode_ = 0x00;
 | 
	
		
			
				|  |  | +thread_local std::stringstream WebSocketBase::data_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int
 | 
	
		
			
				|  |  | +main()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	std::cout << "Hello, Civetweb Websocket port:8080!" << std::endl;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	mg_init_library(0);
 | 
	
		
			
				|  |  | +	std::vector<std::string> options = {
 | 
	
		
			
				|  |  | +	    "document_root",
 | 
	
		
			
				|  |  | +	    ".",
 | 
	
		
			
				|  |  | +	    "listening_ports",
 | 
	
		
			
				|  |  | +	    "8080",
 | 
	
		
			
				|  |  | +	    "access_control_allow_headers",
 | 
	
		
			
				|  |  | +	    "*",
 | 
	
		
			
				|  |  | +	    "access_control_allow_methods",
 | 
	
		
			
				|  |  | +	    "*",
 | 
	
		
			
				|  |  | +	    "access_control_allow_origin",
 | 
	
		
			
				|  |  | +	    "*",
 | 
	
		
			
				|  |  | +	};
 | 
	
		
			
				|  |  | +	auto server = std::make_shared<CivetServer>(options);
 | 
	
		
			
				|  |  | +	auto ws = std::make_shared<WebSocketBase>("simple");
 | 
	
		
			
				|  |  | +	server->addWebSocketHandler("/ws", *ws);
 | 
	
		
			
				|  |  | +	while (true) {
 | 
	
		
			
				|  |  | +		sleep(1);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	server->close();
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 |