Simple Http Server

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 a 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: a little worried about C++ best practices, since I’m diving too fast into C++ (Am I abusing of std items?, since this require 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.

IMPORTANT: I’m using nlohmann/json (https://github.com/nlohmann/json) and j-ulrich status-codes (https://github.com/j-ulrich/http-status-codes-cpp).

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, '', _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

https://github.com/j-ulrich/http-status-codes-cpp

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?

Thanks in advance.

PowerPivot Management Dashboard Processing Timer Job failed with error, “We cannot locate a server to load the workbook Data Model”

All of sudden PowerPivot Management Dashboard Processing Timer Job has started failing in my SharePoint 2013 server.

I have verified below:

  1. The Power Pivot SQL server instance is running SQL Server.
  2. Data Model Settings of the Excel Services application in SharePoint Central Administration has the PivotPoint server configuration.

What could be the reason for this failure and how to resolve this?

I found below errors in SharePoint hive logs.

Opening workbook model for refresh for url http://central admin site url/PowerPivot Management/GUID/PowerPivot Management Data.xlsx (session policy: CloseSessionOnDispose, KeepActiveSessionAlive)

Setting the ECS context scope to the following url: http://central admin site url/PowerPivot Management/GUID/PowerPivot Management Data.xlsx

EXCEPTION: NoAvaialbleStreamingServerException: we cannot locate a server to load the workbook data model. -> Microsoft.AnalysisServices.SPClient.Interfaces.ExcelServicesException: we cannot locate a server to load the workbook data model…..

The Execute method of job definition Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob (ID 292a186b-c54e-4f4b-9e41-4592bd3c300a) threw an exception. More information is included below.

We cannot locate a server to load the workbook Data Model.

Get the best Cheap Dedicated Server hosting from the leading server providers

With the help of numerous online portals out there in the market, you can easily acquire the Cheap Dedicated Server Hosting solution in a hassle-free manner. Starting from a decent amount, the dedicated servers are made available to you so that your business can grow and reach the highest level of success. No matter how small or big an organization is, the need for getting efficient servers is inevitable as it helps in enhancing the productivity of your company.

If you are planning to get the Cheap Dedicated Servers online, it is suggested to ensure the fact that the chosen website is a completely reliable one so as to avoid any major problem in the long run. There are a number of online stores which have a professional team of experts who are responsible for handling each and every aspect of your server. They are at your service round the clock and you can always contact the customer service department in case of any confusion or query as well. Moreover, you should also check out the pages like About Us, Terms and Conditions, Privacy, and Refund Policy of the chosen virtual store so that you know exactly what you are getting yourself into.

Yes, choosing the platform of Dedicated Server Hosting means that you might have to spend a lot of bucks from your pocket but the end results are definitely worth every penny of yours. Before taking the final decision, consider your budget, needs, and requirements in mind.

How to access multiple websites from server on virtualbox to the host machine?

I am trying to access multiple websites that I have on my virtual-box server setup(Ubuntu 18.04.2) to my host machine(Ubuntu 19.04). the websites works fine in the virtual environment but when I try to access them in host machine via IP address(192.168.1.13), only files inside /var/www/html folder are accessed and are accessible on LAN.

BUT I have failed to find a way to access files inside (for instance) /var/www/test. Is there any sort of simple way to resolve the issue?

Thanks

how to Migrate the website from GoDaddy to local server

i want to migrate the website which is hosted in godaddy . i took backup of website with database but when i installed the wordpress in Centos but now when i want to import the backup it shows only 2MB space but my backup is almost 140MB. i have tried many solution but doesnt work and then i took complete copy of wordpress which was installed on server in past that in html directory and now when i open that i can import the anything it says you unable to create directory so please suggest any solution

nohup on amazon Linux Cpanel server stops running after exit [on hold]

When logged in as a non root user and executing the command below from terminal and then exit command. after I log back in; it is not running. This doesn’t happen on amazon Linux non Cpanel server

nohup php index.php ecommerce cron http://example.com > results_ecom.txt 2>&1 </dev/null & 

and then exiting terminal causes process stop. I am executing this for the freelance user inside public html

nohup should keep the process running even after exit

I tail the results file after I logout then log back in and it stops outputting