I'm working on a project (studying) and I have a lot of time for proof of concepts and write something from scratch.

Basically I'm creating an HTTP server (simple, but not too simple) in C++ using sockets and multi-threading.

There are two topics that I'm concerned about: the design pattern of my code structure and the efficiency of my thread implementation. Obs: I'm a little worried about C++ best practices, since I'm diving too fast into C++ (am I abusing of std items, since this requires a low-level implementation).

Server is the main file, that calls Routes, Request and Response. Request and Response contains Struct (that contains Status Code). And Routes contains Request and Response.

[![][1]][1]

**IMPORTANT**: I'm using [nlohmann/json][2] and [j-ulrich status-codes][3].

### Server (Accepting Sockets and Multi-threading):

    #pragma once

	#include <unistd.h>
	#include <stdio.h>
	#include <sys/socket.h>
	#include <stdlib.h>
	#include <netinet/in.h>
	#include <string.h>
	#include <iostream>
	#include <unordered_map>
	#include <thread>
	#include <mutex>
	#include <condition_variable>

	#include "Server/Request.h"
	#include "Server/Response.h"
	#include "Server/Struct.h"
	#include "Server/Routes.h"
	#include "Tools/Logger.h"

	class Server {
		public:
			Server(unsigned int port, unsigned int max_connections = 64, unsigned int thread_count = 5);
			~Server();

			bool setRoute(std::string path, Struct::Methods method, void (*callback)(Request*, Response*));
			bool doListen();
			bool doStop();

		private:
			unsigned int _port;
			unsigned int _max_connections;
			unsigned int _thread_count;

			std::mutex _mutex;
			std::condition_variable _condition;

			bool _signal;

			std::vector<unsigned int> _queue;

			std::thread* _thread_consume;
			std::thread* _thread_process;

			Routes* _routes;

			int _socket;
			struct sockaddr_in _address;
			bool _listen;

			bool _doStop();
			bool _doCreateSocket(int& socket_in);
			bool _doBindSocket(int file_descriptor);
			void _doConsumeSocket();
			void _doProcessSocket(int id);
			bool _doProcessRequest(Request* request, Response* response);
	};

***
	#include "Server/Server.h"

	Server::Server(unsigned int port, unsigned int max_connections, unsigned int thread_count) {
		if (port > 65535) {
			Logger::doSendMessage(Logger::TYPES::ERROR, "[Port must be something between 0 and 65535 on Server::Constructor.");
		}

		if (max_connections < 1) {
			Logger::doSendMessage(Logger::TYPES::ERROR, "Max connections can't be lower than 1 on Server::Constructor.");
		}

		_port = port;
		_max_connections = max_connections;
		_thread_count = thread_count;

		_routes = new Routes();

		int status = _doCreateSocket(_socket);
		if (!status) {
			Logger::doSendMessage(Logger::TYPES::ERROR, "Failed to create socket on Server::Constructor.");
		}

		if (!_doBindSocket(_socket)) {
			Logger::doSendMessage(Logger::TYPES::ERROR, "Failed to bind socket on Server::Constructor.");
		};

		_signal = false;
		_listen = false;
	}

	Server::~Server() {
		_doStop();

		shutdown(_socket, SHUT_RD);
		close(_socket);

		try {
			_thread_consume->join();
			for (size_t i = 0; i < _thread_count; i++) {
				_thread_process[i].join();
			}
		} catch (...) {}

		delete _thread_consume;
		delete[] _thread_process;
		delete _routes;
	}

	bool Server::setRoute(std::string path, Struct::Methods method, void (*callback)(Request*, Response*)) {
		return _routes->setRoute(path, method, callback);
	}

	bool Server::doListen() {
		if (_listen) return false;

		int status;

		status = listen(_socket, _max_connections);
		if (status < 0) return false;

		Logger::doSendMessage(Logger::TYPES::INFO, "Server running with success at port " + std::to_string(_port) + ".");
		
		_listen = true;

		_thread_consume = new std::thread(&Server::_doConsumeSocket, this);
		_thread_process = new std::thread[_thread_count];
		for (size_t i = 0; i < _thread_count; i++) {
			_thread_process[i] = std::thread(&Server::_doProcessSocket, this, i);
		}

		return true;
	}

	bool Server::doStop() {
		return _doStop();
	}

	bool Server::_doStop() {
		if (!_listen) return false;
		{
			std::lock_guard<std::mutex> lock(_mutex);
			_listen = false;
		}
		_condition.notify_one();
		return true;
	}

	bool Server::_doCreateSocket(int& socket_in) {
		int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
		if (file_descriptor == 0) return false;

		int error;
		int opt = 1;

		error = setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
		if (error) return false;

		socket_in = file_descriptor;
		return true;
	}

	bool Server::_doBindSocket(int file_descriptor) {
		if (!file_descriptor) return false;

		_address.sin_family = AF_INET;
	    _address.sin_addr.s_addr = INADDR_ANY;
	    _address.sin_port = htons(_port);

		int status;

		status = bind(file_descriptor, (struct sockaddr*) &_address, sizeof(_address));
		if (status < 0) return false;

		return true;
	}

	void Server::_doConsumeSocket() {
		int socket_in;
		int address_size = sizeof(_address);

		while (_listen) {
			socket_in = accept(_socket, (struct sockaddr*) &_address, (socklen_t*) &address_size);
			if (socket_in < 0) continue;

			{
				std::lock_guard<std::mutex> lock(_mutex);
				_queue.push_back(socket_in);
				_signal = true;
			}

			_condition.notify_one();
		}
	}

	void Server::_doProcessSocket(int id) {
		while (_listen) {
			int queue_size = 0;

			{
				std::unique_lock<std::mutex> lock(_mutex);
				_condition.wait(lock,
					[this] {
						if (this->_signal) return true;
						if (!this->_listen && !this->_queue.size()) return true;
						return false;
					}
				);
				queue_size = _queue.size();
			}

			if (!queue_size) {
				{
					std::lock_guard<std::mutex> lock(_mutex);
					_signal = false;
				}
				
				_condition.notify_one();
				continue;
			}

			int socket_in = 0;
			{
				std::lock_guard<std::mutex> lock(_mutex);
				socket_in = _queue[0];
				_queue.erase(_queue.begin());
			}

			Request* request = new Request(socket_in);
			Response* response = new Response(socket_in);
			
			int status = _doProcessRequest(request, response);

			delete request;
			delete response;
			close(socket_in);
		}
	}

	bool Server::_doProcessRequest(Request* request, Response* response) {
		if (!request->isValid()) {
			response->doSendError(HttpStatus::Code::BadRequest, "Invalid request.");
			return false;
		}

		std::string path = request->getPath();
		Struct::Methods method = request->getMethod();

		Routes::Route route;
		if (!(_routes->getRoute(path, method, route) && route.isValid())) {
			response->doSendError(HttpStatus::Code::Forbidden, "Path invalid/not found.");
			return false;
		}

		if (route.method != method) {
			response->doSendError(HttpStatus::Code::MethodNotAllowed, "Method invalid/not found.");
			return false;
		}

		void (*callback)(Request*, Response*) = route.callback;
		callback(request, response);

		if (!response->isSent()) {
			response->doSendError(HttpStatus::Code::ServiceUnavailable, "Resource was not found or can't respond now.");
		}

		return true;
	}

### Request (parsing) and Response (sending)

**Request**

	#pragma once

	#include <unistd.h>
	#include <string.h>
	#include <iostream>
	#include <string>
	#include <vector>
	#include <unordered_map>
	#include <regex>
	#include <json.hpp>

	#include "Server/Struct.h"

	using json = nlohmann::json;

	class Request {
		public:
			Request(int socket, unsigned int buffer_size = 1024);
			~Request();

			bool isValid();
			std::string getPath() {return _attributes.path;}
			Struct::Methods getMethod() {return _attributes.method;}
			std::unordered_map<std::string, std::string> getHeaders() {return _attributes.headers;}
			std::string getHeader(std::string header) {return _attributes.headers[header];}
			json getBody() {return _attributes.body;}

		private:
			int _socket;
			unsigned int _buffer_size;
			std::string _data;
			Struct::Attributes _attributes;
			bool _status;

			std::string _doReceiveData(int sock_in);
			bool _doParseData(std::string data, Struct::Attributes& attributes);
			std::vector<std::string> _doSplitText(std::string text, std::string delimiter);
			std::vector<std::string> _doSplitText(std::string text, std::string delimiter, int lock);
	};

***

    Request::Request(int socket, unsigned int buffer_size) {
		_socket = socket;
		_buffer_size = buffer_size;
		_status = false;

		_data = _doReceiveData(_socket);
		if (!_data.length()) return;

		bool result;
		result = _doParseData(_data, _attributes);

		if (!result) return;
		if (!_attributes.isValidRequest()) return;

		_status = true;
	}

	Request::~Request() {

	}

	bool Request::isValid() {
		return _status;
	}

	std::string Request::_doReceiveData(int sock_in) {
		char* buffer = new char[_buffer_size];
		memset(buffer, '\0', _buffer_size);
		read(sock_in, buffer, _buffer_size);

		std::string data;
		data.assign(buffer);
		delete[] buffer;
		return data;
	}

	bool Request::_doParseData(std::string data, Struct::Attributes& attributes) {
		std::string delimiter = "\r\n";
		std::vector<std::string> rows = _doSplitText(data, delimiter);
		if (!rows.size()) return false;

		std::string header = rows[0];
		rows.erase(rows.begin());

		if (!header.length()) return false;

		std::vector<std::string> parsed_header = _doSplitText(header, std::string(" "));
		if (parsed_header.size() < 2) return false;

		Struct::Methods method = Struct::doParseHttpMethod(parsed_header[0]);
		if (method == Struct::Methods::NONE) return false;

		std::string path = parsed_header[1];

		std::unordered_map<std::string, std::string> headers;
		for (size_t i = 0; i < rows.size(); i++) {
			std::string row = rows[i];
			delimiter = ":";

			std::vector<std::string> splited = _doSplitText(row, delimiter, true);
			if (splited.size() != 2) continue;

			headers[splited[0]] = splited[1];
		}

		_attributes.method = method;
		_attributes.path = path;
		_attributes.headers = headers;

		std::string content_length = headers["Content-Length"];
		int content_size = 0;

		if (content_size = atoi(content_length.c_str())) {
			std::string body = data.substr(data.length() - content_size, data.length());
			json parsed_body = json::parse(body, nullptr, false);
			if (parsed_body != NULL && !parsed_body.is_discarded()) _attributes.body = parsed_body;
		}

		return true;
	}

	std::vector<std::string> Request::_doSplitText(std::string text, std::string delimiter) {
	    std::vector<std::string> result;
	    int delimiter_length = delimiter.length();
	    
	    std::string block;
	    std::string region;
	    int index = 0;

	    for (size_t i = 0; i < text.length(); i++) {
	        block = text.substr(i, delimiter_length);
	        if (block.length() != delimiter_length) continue;
	        
	        if (block == delimiter) {
	            region = text.substr(index, i - index);
	            result.push_back(region);
	            index = i + delimiter_length;
	        }
	    }

	    return result;
	}

	std::vector<std::string> Request::_doSplitText(std::string text, std::string delimiter, int lock) {
        ...
	}

**Response**

	#pragma once

	#include <unistd.h>
	#include <iostream>
	#include <string>
	#include <unordered_map>

	#include "Server/Struct.h"

	class Response {
		public:
			Response(int socket_in);
			~Response();

			bool isSent() {return _sent;}
			void setCode(HttpStatus::Code code) {_attributes.code = code;}
			void setHeader(std::string key, std::string value) {_attributes.headers[key] = value;}
			void setBody(json body) {_attributes.body = body;}
			void doClearHeaders() {_attributes.headers.clear();}
			void doClearBody() {_attributes.body = json::value_t::object;}

			bool doSendSuccess();
			bool doSendError(HttpStatus::Code code, const std::string& message);

		private:
			int _socket;
			bool _sent;

			Struct::Attributes _attributes;

			bool _doSendPayload();
			bool _doCreatePayload(std::string& payload);
	};

***

	#include "Response.h"

	Response::Response(int socket_in) {
		_socket = socket_in;
		_sent = false;
	}

	Response::~Response() {
		
	}

	bool Response::doSendSuccess() {
		setCode(HttpStatus::Code::OK);
		setHeader("Connection", "Closed");
		return _doSendPayload();
	}

	bool Response::doSendError(HttpStatus::Code code, const std::string& message) {
		setCode(code);
		doClearHeaders();
		doClearBody();

		setHeader("Connection", "Closed");

		json body;
		body["error"] = {};
		body["error"]["code"] = code;
		body["error"]["message"] = message;

		setBody(body);
		return _doSendPayload();
	}

	bool Response::_doSendPayload() {
		if (_sent) return false;

		int status;

		setHeader("Server", "Dark");
		setHeader("Content-Type", "application/json");

		std::string payload;
		status = _doCreatePayload(payload);
		if (!status) return false;

		status = write(_socket, payload.c_str(), payload.size());
		if (status < 1) return false;

		_sent = true;
		return true;
	}

	bool Response::_doCreatePayload(std::string& payload) {
		std::string current_payload;
	    std::string data = _attributes.body.dump(4);

	    int data_length = data.size();

		if (data_length) {
			_attributes.headers["Content-Length"] = std::to_string(data_length);
		}

		current_payload += _attributes.version + " " + std::to_string((int) _attributes.code) + " " + HttpStatus::getReasonPhrase(_attributes.code) + "\r\n";

	    std::unordered_map<std::string, std::string>::iterator iterator;
	    for (iterator = _attributes.headers.begin(); iterator != _attributes.headers.end(); iterator++){
	        std::string key = iterator->first;
	        std::string value = iterator->second;

	        current_payload += key + ": " + value + "\r\n";
	    }

	    if (data_length) current_payload += "\r\n" + data + "\r\n\r\n";

	    payload = current_payload;
	    return true;
	}


### Routes

	#pragma once

	#include <iostream>
	#include <string>
	#include <vector>

	#include "Server/Request.h"
	#include "Server/Response.h"

	class Routes {
		public:
			Routes();
			~Routes();

			struct Route {
				std::string path;
				Struct::Methods method;
				void (*callback)(Request*, Response*);

				bool isValid() {
					if (!path.length()) return false;
					if (method < Struct::Methods::FIRST || method > Struct::Methods::LAST) return false;
					if (callback == nullptr) return false;
					return true;
				}
			};

			bool setRoute(std::string path, Struct::Methods method, void (*callback)(Request*, Response*));
			bool getRoute(std::string path, Struct::Methods method, Route& route);

		private:
			std::vector<Route> _routes;

			int _getRouteIndex(std::string path, Struct::Methods method);
	};

***

	#include "Routes.h"

	Routes::Routes() {

	}

	Routes::~Routes() {

	}

	bool Routes::setRoute(std::string path, Struct::Methods method, void (*callback)(Request*, Response*)) {
		if (path.length() < 1) return false;
		if (_getRouteIndex(path, method) >= 0) return false;
		if (method < Struct::Methods::FIRST || method > Struct::Methods::LAST) return false;
		if (callback == nullptr) return false;

		Routes::Route route = {path, method, callback};
		_routes.push_back(route);
		return true;
	}

	bool Routes::getRoute(std::string path, Struct::Methods method, Routes::Route& route) {
		int index = _getRouteIndex(path, method);
		if (index < 0) return false;
		route = _routes[index];
		return true;
	}

	int Routes::_getRouteIndex(std::string path, Struct::Methods method) {
		for (size_t i = 0; i < _routes.size(); i++) {
			Route* route = &_routes[i];
			if (route->path == path && route->method == method) {
				return i;
			}
		}
		return -1;
	}


### Struct and StatusCode

**StatusCode**

[http-status-codes-cpp][4]

**Struct**

 	#pragma once

	#include <json.hpp>

	#include "Server/StatusCode.h"

	using json = nlohmann::json;

	class Struct {
		public:
			enum class Methods {
				NONE = 0, GET = 1, POST = 2, FIRST = GET, LAST = POST
			};

			struct Attributes {
				const std::string version = "HTTP/1.1";

				std::string path;
				Methods method;
				HttpStatus::Code code;
				std::unordered_map<std::string, std::string> headers;
				json body;

				Attributes() {
					code = HttpStatus::Code::InternalServerError;
					body = json::value_t::object;
				}

				bool isValidRequest() {
					if (!path.length()) return false;
					if (method < Methods::FIRST || method > Methods::LAST) return false;
					if (!headers.size()) return false;
					return true;
				}

				bool isValidResponse() {
					if (!headers.size()) return false;
					return true;	
				}
			};

			static Methods doParseHttpMethod(std::string value) {
				Methods target = Methods::NONE;

				if (value == "GET") target = Methods::GET;
				if (value == "POST") target = Methods::POST;

				return target;
			}
			
		private:
	};

## Main (usage):

	#include "Server/Server.h"

	void exec(Request* request, Response* response) {
		json body;

		body["foo"] = 123;
		body["bar"] = true;
		
		response->setBody(body);
		response->doSendSuccess();
	}

	int main(int argc, char* argv[]) {
		Server* server = new Server(5000);
		server->setRoute("/getStatus", Struct::Methods::GET, exec);
		server->setRoute("/postStatus", Struct::Methods::POST, exec);
		server->doListen();

		// let threads live for some time before they eternaly be gone
        /*
           actually I'm stuck with this sleep, I don't know how to hold 
           this until caller call doStop() without using while and
           consuming process power
        */
		sleep(30);

		delete server;
		return 1;
	}

Basically I'm mirroring NodeJS express API Usage: `server.setRoute(path, route, callback);`

So, what can be done to improve my code in terms of optimization and efficiency?

[1]: https://i.sstatic.net/Qfhjh.png
[2]: https://github.com/nlohmann/json
[3]: https://github.com/j-ulrich/http-status-codes-cpp
[4]: https://github.com/j-ulrich/http-status-codes-cpp