├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── images │ ├── mitol.png │ └── mitol.svg └── index.html ├── main.cpp ├── misc └── Mitol_Bench.jpg ├── node ├── Makefile ├── binding.gyp ├── http.h ├── index.js ├── mns.cc └── package.json └── src ├── Http.cpp ├── Http.h ├── Process.h ├── Request.cpp ├── Request.h ├── Response.cpp ├── Response.h ├── Server.cpp ├── Server.h ├── Socket.cpp ├── Socket.h └── Variables.h /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | 3 | language: c++ 4 | 5 | os: 6 | - linux 7 | # - osx 8 | 9 | env: 10 | global: 11 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 12 | # via the "travis encrypt" command using the project repo's public key 13 | - secure: "BS1Rzv3Z3jo17l5XLb+gggkQ1j6HJguUW2h0D72lPkbWVdndIMhH+Cr+RRcaznCH4CbQ1csK9KtpsqUDMSP4NRV/r1lHFR/s4ILz1kB9HmGwBy8jgX5hhFvNmQ91QdMmS1HDhkW5QMi0RVSbdWQlaFWrzb+g2VlpnxDZtY9DkTVvGJ8y7TAnrxPPukNE7XeQRfL5QSeYzRqEdpMd4aOZ5SZd97q4fYUFId9mjN7/kfMaeTF32WmVdGogNn/TkYZi3dO3mPJRvxJlK0kv0uc94Wy6CCxhy5CQS9OjFFNV/x+L4GA3bsnMCxWpgM+XIqmodfdnjuLeAOeVZN+Z/zhwUTSuqqyYcSjdAlsxpwByX9diocxeLokJmwR7G/B9xB3nxtbQjWDq7vJ4CDPfe7Xfy735h8VO6kLC45PhHs/tl2UX7Qsy0mSTiriIpCmnV74UVX+M/BKqtctymqXHjpUney/ZCNdyi2HpD/W3F8U90rhDaEMVh6n/a1jSs8BTvlmdfeBgVcP/q/q86FXzRapsTms8pneTh1hg0Cjtaa+03u1YTQQPP0XoYefiujq94P1M+DW3fJxsBLtJAvP9QA9uGLKXdi/xziphq0bPx+WZbEWTPODhY0UL9CN+bDLJeDj3lKkhmGbE0ztULKoHGegQHKXYCrCqPhfyaT7M+npa0OM=" 14 | 15 | compiler: 16 | - g++ 17 | 18 | before_install: 19 | # install libuv >= 1.0.0 20 | - curl -L https://github.com/libuv/libuv/archive/v1.0.0.tar.gz | tar xzf - 21 | - (cd libuv-1.0.0 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install) 22 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 23 | # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi 24 | 25 | script: 26 | - cp -R src node/src 27 | - cd node 28 | - make 29 | 30 | addons: 31 | coverity_scan: 32 | project: 33 | name: "Helidium/Mitol" 34 | description: "Build submitted via Travis CI" 35 | notification_email: helidium@gmail.com 36 | build_command_prepend: "cmake ." 37 | build_command: "make" 38 | branch_pattern: master -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(MNS) 3 | 4 | include_directories(./src /usr/include) 5 | link_directories(/usr/lib) 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -fno-builtin -march=native -mtune=native" ) 9 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -luv -g3 -Wall" ) 10 | 11 | set(SOURCE_FILES main.cpp src/Http.cpp src/Http.h src/Server.cpp src/Server.h src/Process.h src/Socket.h src/Request.cpp src/Request.h src/Response.cpp src/Response.h src/Variables.h src/Socket.cpp) 12 | add_executable(MNS ${SOURCE_FILES}) 13 | target_link_libraries(MNS uv) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ZLIB License 2 | 3 | Copyright (c) 2017 Marko Biškup 4 | 5 | Parts of the code are based on software developed as µWebSockets, 6 | Copyright (c) 2016 Alex Hultman and contributors. 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **unmaintained** It was great developing the high performance HTTP server alternative for NodeJS, but I don't have the time to continue development of the project. If anyone wants to continue development, feel free to contact me... 2 | 3 | 4 | Mitol 5 | 6 | 7 | Lightweight, high performance NodeJS Server. 8 | 9 | [![Build Status](https://travis-ci.org/Helidium/Mitol.svg?branch=master)](https://travis-ci.org/Helidium/Mitol) 10 | [![Coverity Status](https://img.shields.io/coverity/scan/12489.svg)](https://scan.coverity.com/projects/helidium-mitol) 11 | 12 | *** 13 | 14 | ## Project description 15 | Project was born out of the need for **faster** performing server using NodeJS. 16 | Current implementation lacks focus on **performance**, which can be achieved by moving parts of code to native C++ bindings. 17 | 18 | **Aim of the project** is to offer an alternative solution, which is using less **memory** and **CPU** power, giving you available resources for your code or handling higher number of requests. 19 | 20 | *** 21 | 22 | ### Advantages 23 | + **No changes required**: Just replace require('http') with require('mitol') 24 | + **Top Speed**: Roughly 3x better performance than integrated server 25 | + **Additional features**: Work in progress (Static file server, Router, ...) 26 | 27 | *** 28 | 29 | ## Benchmarks 30 | ![Benchmark](https://github.com/Helidium/Mitol/raw/master/misc/Mitol_Bench.jpg) 31 | 32 | *** 33 | 34 | ## How to use 35 | Currently only Linux has been tested. To install the project make sure you have node-gyp, python and build-tools (GCC, etc.) installed. 36 | To build the node package do the following: 37 | ```bash 38 | npm i mitol 39 | ``` 40 | 41 | To test the project you can use the following script 42 | 43 | (Single process): 44 | ```javascript 45 | const http = require('mitol'); 46 | 47 | let server = http.createServer((req, res) => { 48 | res.end('Hello World!'); 49 | }); 50 | 51 | server.listen(8080, () => { 52 | console.log('Example app listening on port 8080!') 53 | }); 54 | ``` 55 | 56 | (Multi process): 57 | ```javascript 58 | const cluster = require('cluster'); 59 | const numCPUs = require('os').cpus().length; 60 | const http = require('mitol'); 61 | 62 | if (cluster.isMaster) { 63 | // Fork workers. 64 | for (let i = 0; i < numCPUs; i++) { 65 | cluster.fork(); 66 | } 67 | 68 | cluster.on('exit', (worker, code, signal) => { 69 | console.log(`worker ${worker.process.pid} died`); 70 | }); 71 | } else { 72 | let server = http.createServer((req, res) => { 73 | res.end('Hello World!'); 74 | }); 75 | 76 | server.listen(8080, () => { 77 | console.log('Example app listening on port 8080!') 78 | }); 79 | } 80 | ``` 81 | 82 | *** 83 | ## Like the project? 84 | 85 | You can support me by donating: https://www.paypal.com/paypalme/helidium 86 | *** 87 | 88 | ## Thanks 89 | My Family and Friends for supporting me!
90 | All the fans for believing in the project!
91 | [Leandro A. F. Pereira](https://github.com/lpereira) For his wonderful Lwan project.
92 | [Alex Hultman](https://github.com/alexhultman) For his wonderful uWebSockets project.
93 | [TechEmpower](https://www.techempower.com/benchmarks/) For Server benchmarks.
94 | [GitHub](https://github.com) For code hosting.
95 | [Travis](https://travis-ci.org) For code testing.
96 | [Coverity](https://scan.coverity.com) For code defects check
97 | 98 | *** 99 | Copyright (c) 2017 Mitol Project - Released under the Zlib license. 100 | -------------------------------------------------------------------------------- /docs/images/mitol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Helidium/Mitol/b49ba228bbe2d486916551f2479e35b8dcab4635/docs/images/mitol.png -------------------------------------------------------------------------------- /docs/images/mitol.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mitol 6 | 7 | 8 |

Mitol...

9 | 10 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "src/Socket.h" 2 | #include "src/Http.h" 3 | #include "src/Response.h" 4 | #include 5 | 6 | MNS::Server *server = NULL; 7 | 8 | void onSignalReceived(int signum) { 9 | if (server) 10 | server->stop(); 11 | } 12 | 13 | int main() { 14 | signal(SIGTERM, onSignalReceived); 15 | signal(SIGINT, onSignalReceived); 16 | signal(SIGALRM, onSignalReceived); 17 | 18 | MNS::Http *http = new MNS::Http(); 19 | server = http->createServer(); 20 | server->onHttpConnection([](MNS::SocketData *data) { 21 | }); 22 | 23 | server->onHttpRequest([](MNS::SocketData *data) { 24 | MNS::Response *response = data->response; 25 | 26 | response->end("Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!", 120); 27 | }); 28 | server->listen(8080); 29 | server->run(); 30 | 31 | delete (http); 32 | return 0; 33 | } -------------------------------------------------------------------------------- /misc/Mitol_Bench.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Helidium/Mitol/b49ba228bbe2d486916551f2479e35b8dcab4635/misc/Mitol_Bench.jpg -------------------------------------------------------------------------------- /node/Makefile: -------------------------------------------------------------------------------- 1 | CPP_SHARED := -std=c++11 -O3 -I ../src -shared -fPIC ../src/Http.cpp ../src/Request.cpp ../src/Response.cpp ../src/Server.cpp ../src/Socket.cpp mns.cc 2 | CPP_DARWIN := -stdlib=libc++ -mmacosx-version-min=10.7 -undefined dynamic_lookup 3 | 4 | default: 5 | make targets 6 | NODE=targets/node-v5.12.0 ABI=47 make `(uname -s)` 7 | NODE=targets/node-v6.10.2 ABI=48 make `(uname -s)` 8 | NODE=targets/node-v7.8.0 ABI=51 make `(uname -s)` 9 | NODE=targets/node-v8.0.0 ABI=57 make `(uname -s)` 10 | cp ../README.md dist/README.md 11 | cp ../LICENSE dist/LICENSE 12 | cp package.json dist/package.json 13 | cp index.js dist/index.js 14 | for f in dist/bin/*.node; do chmod +x $$f; done 15 | targets: 16 | mkdir dist 17 | mkdir dist/bin 18 | mkdir targets 19 | curl https://nodejs.org/dist/v4.8.2/node-v4.8.2-headers.tar.gz | tar xz -C targets 20 | curl https://nodejs.org/dist/v5.12.0/node-v5.12.0-headers.tar.gz | tar xz -C targets 21 | curl https://nodejs.org/dist/v6.10.2/node-v6.10.2-headers.tar.gz | tar xz -C targets 22 | curl https://nodejs.org/dist/v7.8.0/node-v7.8.0-headers.tar.gz | tar xz -C targets 23 | curl https://nodejs.org/dist/v8.0.0/node-v8.0.0-headers.tar.gz | tar xz -C targets 24 | Linux: 25 | $(CXX) $(CPP_SHARED) -static-libstdc++ -static-libgcc -I $$NODE/include/node -s -o dist/bin/mns_linux_$$ABI.node 26 | Darwin: 27 | $(CXX) $(CPP_SHARED) $(CPP_DARWIN) -I $$NODE/include/node -o dist/bin/mns_darwin_$$ABI.node 28 | .PHONY: clean 29 | clean: 30 | rm -rf dist 31 | rm -rf targets 32 | -------------------------------------------------------------------------------- /node/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "module_name": "mitol", 4 | "module_path": "./bin" 5 | }, 6 | "targets": [ 7 | { 8 | "target_name": "mitol", 9 | "sources": [ 10 | "mns.cc", 11 | "src/Http.cpp", 12 | "src/Request.cpp", 13 | "src/Response.cpp", 14 | "src/Server.cpp", 15 | "src/Socket.cpp" 16 | ], 17 | "conditions": [ 18 | ["OS==\"linux\" or OS==\"freebsd\" or OS==\"openbsd\" or OS==\"solaris\" or OS==\"aix\"", { 19 | "cflags_cc": [ 20 | "-std=c++11", 21 | "-O3", 22 | "-I src", 23 | "-fPIC", 24 | "-Wno-format-contains-nul", 25 | "-Wno-deprecated-declarations" 26 | ] 27 | }] 28 | ] 29 | }, { 30 | "target_name": "action_after_build", 31 | "type": "none", 32 | "dependencies": [ 33 | "<(module_name)" 34 | ], 35 | "copies": [ 36 | { 37 | "files": [ 38 | "<(PRODUCT_DIR)/<(module_name).node" 39 | ], 40 | "destination": "<(module_path)" 41 | } 42 | ] 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /node/http.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/28/17. 3 | // 4 | // Parts of the code are based on software developed as µWebSockets, 5 | // Copyright (c) 2016 Alex Hultman and contributors. 6 | // 7 | 8 | #ifndef MNS_HTTP_H 9 | #define MNS_HTTP_H 10 | 11 | #define SERVER_CLOSE_EVENT 1 12 | #define SERVER_CONNECTION_EVENT 2 13 | #define SERVER_ERROR_EVENT 3 14 | #define SERVER_LISTENING_EVENT 4 15 | 16 | #include 17 | #include 18 | #include "src/Server.h" 19 | 20 | using namespace v8; 21 | 22 | Persistent requestObjectTemplate, responseObjectTemplate; 23 | Persistent httpPersistent; 24 | Persistent httpRequestCallback; 25 | 26 | struct HttpRequest { 27 | static void On(const FunctionCallbackInfo &args) { 28 | if (args.Length() == 2 && args[0]->IsString()) { 29 | String::Utf8Value EventName(args[0]); 30 | if (strncmp(*EventName, "data", 4) == 0) { 31 | args.Holder()->SetInternalField(1, args[1]); 32 | } else if (strncmp(*EventName, "end", 3) == 0) { 33 | args.Holder()->SetInternalField(2, args[1]); 34 | } 35 | } 36 | } 37 | 38 | static void RemoveListener(const FunctionCallbackInfo &args) { 39 | } 40 | 41 | static void Unpipe(const FunctionCallbackInfo &args) { 42 | 43 | } 44 | 45 | static void Resume(const FunctionCallbackInfo &args) { 46 | 47 | } 48 | 49 | static void GetUrl(Local property, const PropertyCallbackInfo &args) { 50 | MNS::SocketData *data = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 51 | 52 | if(data) { 53 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) data->request->url)); 54 | } 55 | } 56 | 57 | static void GetMethod(Local property, const PropertyCallbackInfo &args) { 58 | MNS::SocketData *data = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 59 | 60 | if(data) { 61 | switch (data->request->method) { 62 | case MNS::HTTP_METHOD::GET: 63 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "GET", String::kNormalString, 3)); 64 | break; 65 | case MNS::HTTP_METHOD::POST: 66 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "POST", String::kNormalString, 4)); 67 | break; 68 | case MNS::HTTP_METHOD::HEAD: 69 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "HEAD", String::kNormalString, 4)); 70 | break; 71 | case MNS::HTTP_METHOD::OPTIONS: 72 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "OPTIONS", String::kNormalString, 7)); 73 | break; 74 | case MNS::HTTP_METHOD::CONNECT: 75 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "CONNECT", String::kNormalString, 7)); 76 | break; 77 | case MNS::HTTP_METHOD::DELETE: 78 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "DELETE", String::kNormalString, 6)); 79 | break; 80 | case MNS::HTTP_METHOD::PATCH: 81 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PATCH", String::kNormalString, 5)); 82 | break; 83 | case MNS::HTTP_METHOD::PUT: 84 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PUT", String::kNormalString, 3)); 85 | break; 86 | case MNS::HTTP_METHOD::TRACE: 87 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "TRACE", String::kNormalString, 5)); 88 | break; 89 | case MNS::HTTP_METHOD::UNKNOWN_METHOD: 90 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "UNKNOWN", String::kNormalString, 8)); 91 | break; 92 | } 93 | } 94 | } 95 | 96 | static void GetHttpVersion(Local property, const PropertyCallbackInfo &args) { 97 | MNS::SocketData *data = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 98 | 99 | if(data) { 100 | switch (data->request->httpVersion) { 101 | case MNS::HTTP_VERSION::HTTP_1_0: 102 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "1.0", String::kNormalString, 3)); 103 | break; 104 | case MNS::HTTP_VERSION::HTTP_1_1: 105 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "1.1", String::kNormalString, 3)); 106 | break; 107 | case MNS::HTTP_VERSION::UNKNOWN_VERSION: 108 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "UNKNOWN", String::kNormalString, 8)); 109 | break; 110 | } 111 | } 112 | } 113 | 114 | static void GetHeader(Local property, const PropertyCallbackInfo &args) { 115 | String::Utf8Value name(property); 116 | 117 | MNS::SocketData *data = static_cast(args.Holder()->GetPrototype()->ToObject()->GetAlignedPointerFromInternalField(0)); 118 | 119 | if(data) { 120 | std::map::const_iterator it = data->request->headers.find(std::string(*name, name.length())); 121 | 122 | if (it != data->request->headers.end()) { 123 | args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) it->second.c_str(), String::kNormalString, it->second.length())); 124 | } else { 125 | args.GetReturnValue().Set(Undefined(args.GetIsolate())); 126 | } 127 | } 128 | } 129 | 130 | static void GetSocket(Local property, const PropertyCallbackInfo &args) { 131 | // TODO 132 | args.GetReturnValue().Set(Object::New(args.GetIsolate())); 133 | } 134 | 135 | static Local getObjectTemplate(Isolate *isolate) { 136 | Local proto = ObjectTemplate::New(isolate); 137 | proto->SetInternalFieldCount(3); 138 | Local proto_i = proto->NewInstance(); 139 | 140 | Local headersTemplate = ObjectTemplate::New(isolate); 141 | headersTemplate->SetNamedPropertyHandler(HttpRequest::GetHeader); 142 | 143 | Local obj_t = ObjectTemplate::New(isolate); 144 | obj_t->SetInternalFieldCount(3); // 0->SocketData;1->data callback;2->end callback 145 | 146 | obj_t->Set(isolate, "unpipe", FunctionTemplate::New(isolate, HttpRequest::Unpipe)); 147 | obj_t->Set(isolate, "resume", FunctionTemplate::New(isolate, HttpRequest::Resume)); 148 | obj_t->Set(isolate, "on", FunctionTemplate::New(isolate, HttpRequest::On)); 149 | obj_t->Set(isolate, "removelistener", FunctionTemplate::New(isolate, HttpRequest::RemoveListener)); 150 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "httpVersion"), HttpRequest::GetHttpVersion); 151 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "method"), HttpRequest::GetMethod); 152 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "url"), HttpRequest::GetUrl); 153 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "socket"), HttpRequest::GetSocket); 154 | 155 | Local obj_i = obj_t->NewInstance(); 156 | obj_i->SetPrototype(proto_i); 157 | 158 | Local ht_i = headersTemplate->NewInstance(); 159 | ht_i->SetPrototype(proto_i); 160 | 161 | obj_i->Set(String::NewFromUtf8(isolate, "headers"), ht_i); 162 | 163 | return obj_i; 164 | } 165 | }; 166 | 167 | struct HttpResponse { 168 | static void Write(const FunctionCallbackInfo &args) { 169 | MNS::SocketData *data = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 170 | 171 | // TODO: Make sure it is heap allocated; GC not released the value 172 | if(data) { 173 | if(!(args[0]->IsNull() || args[0]->IsUndefined())) { 174 | String::Utf8Value str(args[0]); 175 | data->response->write(*str, str.length()); 176 | } 177 | } 178 | } 179 | 180 | static void End(const FunctionCallbackInfo &args) { 181 | Isolate *isolate = args.GetIsolate(); 182 | MNS::SocketData *data = (MNS::SocketData *) args.Holder()->GetAlignedPointerFromInternalField(0); 183 | 184 | if(data) { 185 | // Release the handle 186 | static_cast *>(data->nodeRequestPlaceholder)->Reset(); 187 | delete (static_cast *>(data->nodeRequestPlaceholder)); 188 | //static_cast *>(data->nodeRequestPlaceholder)->~Persistent(); 189 | 190 | static_cast *>(data->nodeResponsePlaceholder)->Reset(); 191 | delete (static_cast *>(data->nodeResponsePlaceholder)); 192 | //static_cast *>(data->nodeResponsePlaceholder)->~Persistent(); 193 | 194 | if(args[0]->IsNull() || args[0]->IsUndefined()) { 195 | data->response->end(nullptr, 0); 196 | } else { 197 | // TODO: Make sure it is heap allocated; GC not released the value 198 | String::Utf8Value str(args[0]); 199 | data->response->end(*str, str.length()); 200 | } 201 | 202 | data->nodeRequestPlaceholder = nullptr; 203 | data->nodeResponsePlaceholder = nullptr; 204 | 205 | Local finishCallback = args.Holder()->GetInternalField(1); 206 | if (!finishCallback->IsUndefined()) { 207 | Local::Cast(finishCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); 208 | } 209 | 210 | Local endCallback = args.Holder()->GetInternalField(2); 211 | if (!endCallback->IsUndefined()) { 212 | Local::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); 213 | } 214 | 215 | args.Holder()->SetAlignedPointerInInternalField(0, nullptr); 216 | } 217 | } 218 | 219 | static void AddTrailers(const FunctionCallbackInfo &args) { 220 | } 221 | 222 | static void IsFinished(Local property, const PropertyCallbackInfo &args) { 223 | MNS::SocketData *data = (MNS::SocketData *) args.Holder()->GetAlignedPointerFromInternalField(0); 224 | 225 | if(data) { 226 | args.GetReturnValue().Set(Boolean::New(args.GetIsolate(), data->response->finished)); 227 | } 228 | } 229 | 230 | static void SetHeader(const FunctionCallbackInfo &args) { 231 | if (args.Length() == 2 && args[0]->IsString() && args[1]->IsString()) { 232 | MNS::SocketData *data = (MNS::SocketData *) args.Holder()->GetAlignedPointerFromInternalField(0); 233 | 234 | if(data) { 235 | String::Utf8Value name(args[0]->ToString()); 236 | String::Utf8Value value(args[1]->ToString()); 237 | 238 | data->response->setHeader(std::string(*name, name.length()), std::string(*value, value.length())); 239 | } 240 | } 241 | } 242 | 243 | static void GetStatusCode(Local property, const PropertyCallbackInfo& info) { 244 | MNS::SocketData *data = (MNS::SocketData *) info.Holder()->GetAlignedPointerFromInternalField(0); 245 | 246 | info.GetReturnValue().Set(data->response->statusCode); 247 | } 248 | 249 | static void SetStatusCode(Local property, Local value, const PropertyCallbackInfo& info) { 250 | MNS::SocketData *data = (MNS::SocketData *) info.Holder()->GetAlignedPointerFromInternalField(0); 251 | 252 | data->response->statusCode = value->Int32Value(); 253 | } 254 | 255 | static void WriteHead(const FunctionCallbackInfo &args) { 256 | printf("Writing Head\n"); 257 | } 258 | 259 | static void SetTimeout(const FunctionCallbackInfo &args) { 260 | } 261 | 262 | static void RemoveListener(const FunctionCallbackInfo &args) {} 263 | 264 | static void On(const FunctionCallbackInfo &args) { 265 | if (args.Length() == 2 && args[0]->IsString()) { 266 | String::Utf8Value EventName(args[0]); 267 | if (strncmp(*EventName, "finish", 6) == 0) { 268 | args.Holder()->SetInternalField(1, args[1]); 269 | } else if (strncmp(*EventName, "end", 3) == 0) { 270 | args.Holder()->SetInternalField(2, args[1]); 271 | } 272 | } 273 | } 274 | 275 | static Local getObjectTemplate(Isolate *isolate) { 276 | Local obj_t = ObjectTemplate::New(isolate); 277 | obj_t->SetInternalFieldCount(3); 278 | 279 | obj_t->Set(isolate, "write", FunctionTemplate::New(isolate, HttpResponse::Write)); 280 | obj_t->Set(isolate, "end", FunctionTemplate::New(isolate, HttpResponse::End)); 281 | obj_t->Set(isolate, "addTrailers", FunctionTemplate::New(isolate, HttpResponse::AddTrailers)); 282 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "finished"), HttpResponse::IsFinished); 283 | obj_t->SetAccessor(String::NewFromUtf8(isolate, "statusCode"), HttpResponse::GetStatusCode, HttpResponse::SetStatusCode); 284 | obj_t->Set(isolate, "setHeader", FunctionTemplate::New(isolate, HttpResponse::SetHeader)); 285 | obj_t->Set(isolate, "setTimeout", FunctionTemplate::New(isolate, HttpResponse::SetTimeout)); 286 | obj_t->Set(isolate, "writeHead", FunctionTemplate::New(isolate, HttpResponse::WriteHead)); 287 | obj_t->Set(isolate, "on", FunctionTemplate::New(isolate, HttpResponse::On)); 288 | obj_t->Set(isolate, "removeListener", FunctionTemplate::New(isolate, HttpResponse::RemoveListener)); 289 | 290 | return obj_t->NewInstance(); 291 | } 292 | }; 293 | 294 | struct Http { 295 | static Local getObjectTemplate(Isolate *isolate) { 296 | Local obj_t = ObjectTemplate::New(isolate); 297 | obj_t->SetInternalFieldCount(6); 298 | 299 | obj_t->Set(isolate, "on", FunctionTemplate::New(isolate, Http::On)); 300 | obj_t->Set(isolate, "close", FunctionTemplate::New(isolate, Http::stopServer)); 301 | obj_t->Set(isolate, "listen", FunctionTemplate::New(isolate, Http::listen)); 302 | obj_t->Set(isolate, "get", FunctionTemplate::New(isolate, Http::get)); 303 | obj_t->Set(isolate, "request", FunctionTemplate::New(isolate, Http::request)); 304 | 305 | return obj_t->NewInstance(); 306 | } 307 | 308 | static void createServer(const FunctionCallbackInfo &args) { 309 | MNS::Server *server = new MNS::Server(); 310 | 311 | Isolate *isolate = args.GetIsolate(); 312 | 313 | Local obj = Http::getObjectTemplate(isolate); 314 | obj->SetAlignedPointerInInternalField(0, server); 315 | 316 | httpPersistent.Reset(isolate, obj); 317 | 318 | server->onHttpConnection([isolate](MNS::SocketData *data) { 319 | HandleScope hs(isolate); 320 | 321 | Local connectionCallback = httpPersistent.Get(isolate)->GetInternalField(SERVER_CONNECTION_EVENT); 322 | 323 | if(!connectionCallback->IsUndefined()) { 324 | // TODO: Return the socket object 325 | Local::Cast(connectionCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); 326 | } 327 | }); 328 | 329 | if (args.Length() == 1 && args[0]->IsFunction()) { 330 | httpRequestCallback.Reset(isolate, Local::Cast(args[0])); 331 | 332 | server->onHttpRequest([isolate](MNS::SocketData *data) { 333 | HandleScope hs(isolate); 334 | 335 | Local req = Local::New(isolate, requestObjectTemplate)->Clone(); 336 | req->SetAlignedPointerInInternalField(0, data); 337 | Local reqProto = req->GetPrototype()->ToObject(); 338 | reqProto->SetAlignedPointerInInternalField(0, data); 339 | 340 | Local res = Local::New(isolate, responseObjectTemplate)->Clone(); 341 | res->SetAlignedPointerInInternalField(0, data); 342 | Local argv[] = { 343 | req, 344 | res 345 | }; 346 | 347 | data->nodeRequestPlaceholder = new Persistent(isolate, req); 348 | data->nodeResponsePlaceholder = new Persistent(isolate, res); 349 | 350 | Local::New(isolate, httpRequestCallback)->Call(isolate->GetCurrentContext()->Global(), 2, argv); 351 | 352 | Local dataCallback = req->GetInternalField(1); 353 | if (!dataCallback->IsUndefined()) { 354 | Local argv[] = {String::NewFromUtf8(isolate, data->request->getBodyBuffer(), v8::String::kNormalString, data->request->getBodyBufferLen())}; 355 | Local::Cast(dataCallback)->Call(isolate->GetCurrentContext()->Global(), 1, argv); 356 | } 357 | 358 | Local endCallback = req->GetInternalField(2); 359 | if (!endCallback->IsUndefined()) { 360 | Local::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); 361 | } 362 | 363 | isolate->RunMicrotasks(); 364 | }); 365 | 366 | server->onHttpCancel([isolate](MNS::SocketData *data) { 367 | HandleScope hs(isolate); 368 | 369 | if(data->nodeRequestPlaceholder) { 370 | Local reqObject = Local::New(isolate, *(Persistent *)data->nodeRequestPlaceholder); 371 | reqObject->SetAlignedPointerInInternalField(0, nullptr); 372 | reqObject->GetPrototype()->ToObject()->SetAlignedPointerInInternalField(0, nullptr); 373 | 374 | // Release the handle 375 | static_cast *>(data->nodeRequestPlaceholder)->Reset(); 376 | delete(static_cast *>(data->nodeRequestPlaceholder)); 377 | } 378 | 379 | if(data->nodeResponsePlaceholder) { 380 | Local resObject = Local::New(isolate, *(Persistent *)data->nodeResponsePlaceholder); 381 | resObject->SetAlignedPointerInInternalField(0, nullptr); 382 | 383 | // Release the handle 384 | static_cast *>(data->nodeResponsePlaceholder)->Reset(); 385 | delete(static_cast *>(data->nodeResponsePlaceholder)); 386 | } 387 | }); 388 | } 389 | 390 | requestObjectTemplate.Reset(isolate, HttpRequest::getObjectTemplate(isolate)); 391 | responseObjectTemplate.Reset(isolate, HttpResponse::getObjectTemplate(isolate)); 392 | 393 | args.GetReturnValue().Set(obj); 394 | } 395 | 396 | static void stopServer(const FunctionCallbackInfo &args) { 397 | MNS::Server *server = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 398 | 399 | if(server) { 400 | server->stop(); 401 | } 402 | } 403 | 404 | static void On(const FunctionCallbackInfo &args) { 405 | if (args.Length() == 2 && args[0]->IsString()) { 406 | String::Utf8Value EventName(args[0]); 407 | if (strncmp(*EventName, "close", 5) == 0) { 408 | args.Holder()->SetInternalField(SERVER_CLOSE_EVENT, args[1]); 409 | } else if (strncmp(*EventName, "connection", 10) == 0) { 410 | args.Holder()->SetInternalField(SERVER_CONNECTION_EVENT, args[1]); 411 | } else if (strncmp(*EventName, "error", 5) == 0) { 412 | args.Holder()->SetInternalField(SERVER_ERROR_EVENT, args[1]); 413 | } else if (strncmp(*EventName, "listening", 10) == 0) { 414 | args.Holder()->SetInternalField(SERVER_LISTENING_EVENT, args[1]); 415 | } 416 | } 417 | } 418 | 419 | static void listen(const FunctionCallbackInfo &args) { 420 | MNS::Server *server = static_cast(args.Holder()->GetAlignedPointerFromInternalField(0)); 421 | 422 | // Commence listening 423 | server->listen(args[0]->IntegerValue()); 424 | 425 | // If callback supplied, make the callback 426 | if (args[args.Length() - 1]->IsFunction()) { 427 | Local::Cast(args[args.Length() - 1])->Call(args.GetIsolate()->GetCurrentContext()->Global(), 0, nullptr); 428 | } 429 | 430 | Isolate::GetCurrent()->RunMicrotasks(); 431 | }; 432 | 433 | static void get(const FunctionCallbackInfo &args) { 434 | args.GetReturnValue().Set(Null(args.GetIsolate())); 435 | } 436 | 437 | static void request(const FunctionCallbackInfo &args) { 438 | args.GetReturnValue().Set(Null(args.GetIsolate())); 439 | } 440 | }; 441 | 442 | #endif //MNS_HTTP_H 443 | -------------------------------------------------------------------------------- /node/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by helidium on 3/29/17. 3 | */ 4 | module.exports = (function() { 5 | "use strict"; 6 | try { // NODE-GYP 7 | return require(`./bin/mitol`); 8 | } catch (e) { 9 | throw new Error('Error loading MitolServer module'); 10 | } 11 | })(); -------------------------------------------------------------------------------- /node/mns.cc: -------------------------------------------------------------------------------- 1 | #include "http.h" 2 | #include 3 | #include 4 | 5 | using namespace v8; 6 | 7 | void Init(Handle exports, Handle module) { 8 | NODE_SET_METHOD(exports, "createServer", Http::createServer); 9 | } 10 | 11 | NODE_MODULE(mns, Init) -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mitol", 3 | "version": "0.0.11", 4 | "description": "Lightweight, high performance NodeJS Server", 5 | "main": "index.js", 6 | "homepage": "https://helidium.github.io/Mitol/", 7 | "repository": { 8 | "type" : "git", 9 | "url" : "https://github.com/Helidium/Mitol.git" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "install": "node-gyp rebuild > build_log.txt 2>&1 || exit 0" 14 | }, 15 | "author": { 16 | "name" : "Marko Biškup", 17 | "email" : "marko.biskup@helidium.net", 18 | "url" : "http://www.helidium.net/" 19 | }, 20 | "license": "ZLIB" 21 | } 22 | -------------------------------------------------------------------------------- /src/Http.cpp: -------------------------------------------------------------------------------- 1 | #include "Http.h" 2 | 3 | MNS::Http::Http() { 4 | this->server = NULL; 5 | } 6 | 7 | MNS::Server *MNS::Http::createServer() { 8 | if(this->server) return this->server; 9 | 10 | this->server = new MNS::Server(); 11 | 12 | return this->server; 13 | } 14 | 15 | MNS::Http::~Http() { 16 | if(this->server) { 17 | delete(this->server); 18 | this->server = NULL; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Http.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/22/17. 3 | // 4 | 5 | #ifndef MNS_HTTP_H 6 | #define MNS_HTTP_H 7 | 8 | #include "Server.h" 9 | 10 | namespace MNS { 11 | /** 12 | * Wrapper class for the Server object 13 | */ 14 | class Http { 15 | public: 16 | /// Default constructor 17 | Http(); 18 | 19 | /** 20 | * Created the Server instance 21 | * @return Server instance 22 | */ 23 | MNS::Server *createServer(); 24 | 25 | /// Default destructor 26 | ~Http(); 27 | private: 28 | /// Server instance 29 | MNS::Server *server; 30 | protected: 31 | }; 32 | } 33 | 34 | 35 | #endif //MNS_HTTP_H 36 | -------------------------------------------------------------------------------- /src/Process.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/22/17. 3 | // 4 | 5 | #ifndef MNS_PROCESS_H 6 | #define MNS_PROCESS_H 7 | 8 | #include 9 | 10 | namespace MNS { 11 | /** 12 | * Helper struct for process affinity 13 | */ 14 | struct Process { 15 | /** 16 | * Tie the process to CPU core 17 | * @return 18 | */ 19 | static int setAffinity() { 20 | #ifdef __linux__ 21 | cpu_set_t mask; 22 | CPU_ZERO(&mask); 23 | CPU_SET(1, &mask); 24 | 25 | return sched_setaffinity(0, sizeof(mask), &mask); 26 | #else 27 | return 0; 28 | #endif 29 | } 30 | }; 31 | } 32 | 33 | #endif //MNS_PROCESS_H 34 | -------------------------------------------------------------------------------- /src/Request.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/24/17. 3 | // 4 | // Todo: Parse Request BODY; Chunked encoding 5 | 6 | #include "Request.h" 7 | 8 | enum PARSER_STATE { 9 | METHOD, 10 | URL, 11 | HTTP_VERSION, 12 | HEADER_NAME, 13 | BODY, 14 | FINISHED 15 | }; 16 | 17 | MNS::Request::Request(const MNS::SocketData *socketData) { 18 | this->socketData = socketData; 19 | 20 | this->finished = false; 21 | this->lastParsePos = 0; 22 | this->buffer = (char *) malloc(4096); 23 | this->bodyBuffer = NULL; 24 | this->bufferSize = 4096; 25 | this->bufferLen = 0; 26 | this->bodyBufferLen = 0; 27 | this->state = REQUEST_STATE::CONNECTING; 28 | 29 | this->httpVersion = MNS::HTTP_VERSION::UNKNOWN_VERSION; 30 | this->method = MNS::HTTP_METHOD::UNKNOWN_METHOD; 31 | this->socket = -1; 32 | this->url = NULL; 33 | } 34 | 35 | int MNS::Request::clear() { 36 | this->finished = false; 37 | this->lastParsePos = 0; 38 | this->state = REQUEST_STATE::CONNECTING; 39 | this->bufferLen = 0; 40 | if(this->bufferSize != 4096) { 41 | this->buffer = (char *)realloc(this->buffer, 4096); // TODO: Sanity check, realloc might run out of memory 42 | this->bufferSize = 4096; 43 | } 44 | this->bodyBuffer = NULL; 45 | this->bodyBufferLen = 0; 46 | 47 | this->httpVersion = MNS::HTTP_VERSION::UNKNOWN_VERSION; 48 | this->method = MNS::HTTP_METHOD::UNKNOWN_METHOD; 49 | this->url = NULL; 50 | 51 | return 0; 52 | } 53 | 54 | bool MNS::Request::isFinished() { 55 | return this->finished; 56 | } 57 | 58 | char *MNS::Request::getBuffer() { 59 | return this->buffer; 60 | } 61 | 62 | char *MNS::Request::getBodyBuffer() { 63 | return this->bodyBuffer; 64 | } 65 | 66 | ssize_t MNS::Request::getBufferSize() { 67 | return this->bufferSize; 68 | } 69 | 70 | void MNS::Request::resizeBuffer(int ns) { 71 | this->buffer = (char *)realloc(this->buffer, ns); 72 | this->bufferSize = ns; 73 | } 74 | 75 | ssize_t MNS::Request::getBufferLen() { 76 | return this->bufferLen; 77 | } 78 | 79 | ssize_t MNS::Request::getBodyBufferLen() { 80 | return this->bodyBufferLen; 81 | } 82 | 83 | int MNS::Request::Parse(ssize_t requestLen) { 84 | if(requestLen >= 0) { 85 | this->finished = false; 86 | this->lastParsePos = 0; 87 | this->bufferLen = requestLen; 88 | } 89 | 90 | char *tailPos = NULL; 91 | this->state = REQUEST_STATE::PARSING_HEADERS; 92 | 93 | PARSER_STATE parserState = PARSER_STATE::METHOD; 94 | 95 | for (int i = this->lastParsePos; i < this->bufferLen; i++) { 96 | char c = this->buffer[i]; 97 | 98 | switch (parserState) { 99 | case PARSER_STATE::METHOD: 100 | if (c == 'G' || c == 'g') { 101 | this->method = HTTP_METHOD::GET; 102 | i += 3; 103 | } else if (c == 'O' || c == 'o') { 104 | this->method = HTTP_METHOD::OPTIONS; 105 | i += 7; 106 | } else if (c == 'P' || c == 'p') { 107 | char c1 = this->buffer[i + 1]; 108 | if (c1 == 'O' || c1 == 'o') { 109 | this->method = HTTP_METHOD::POST; 110 | i += 4; 111 | } else if (c1 == 'U' || c1 == 'u') { 112 | this->method = HTTP_METHOD::PUT; 113 | i += 3; 114 | } else { 115 | this->method = HTTP_METHOD::PATCH; 116 | i += 5; 117 | // Todo: This may be unsafe and offset should be found 118 | } 119 | } else if (c == 'H' || c == 'h') { 120 | this->method = HTTP_METHOD::HEAD; 121 | i += 4; 122 | } else if (c == 'D' || c == 'd') { 123 | this->method = HTTP_METHOD::DELETE; 124 | i += 6; 125 | } else if (c == 'C' || c == 'c') { 126 | this->method = HTTP_METHOD::CONNECT; 127 | i += 7; 128 | } else if (c == 'T' || c == 't') { 129 | this->method = HTTP_METHOD::TRACE; 130 | i += 5; 131 | } 132 | 133 | parserState = PARSER_STATE::URL; 134 | break; 135 | case PARSER_STATE::URL: 136 | this->url = this->buffer + i; 137 | tailPos = (char*)memchr(this->url, ' ', this->bufferLen - i); 138 | if(tailPos == NULL) { // ERROR, TRY AND GET MROE DATA 139 | this->state = REQUEST_STATE::NEED_MORE_DATA; 140 | 141 | return 1; 142 | } 143 | i = (int)(tailPos - this->buffer); 144 | buffer[i] = '\0'; 145 | 146 | i += 7; 147 | parserState = PARSER_STATE::HTTP_VERSION; 148 | break; 149 | case PARSER_STATE::HTTP_VERSION: 150 | if (c == '1') this->httpVersion = HTTP_VERSION::HTTP_1_1; 151 | else if (c == '0') this->httpVersion = HTTP_VERSION::HTTP_1_0; 152 | parserState = PARSER_STATE::HEADER_NAME; 153 | i += 2; 154 | break; 155 | case PARSER_STATE::HEADER_NAME: { 156 | char *name = buffer + i; 157 | while (c != ':') { 158 | if (c >= 'A') buffer[i] |= 0x20;//-= 'A' - 'a'; 159 | 160 | c = buffer[++i]; 161 | } 162 | 163 | buffer[i] = '\0'; 164 | i += (buffer[i + 1] == ' '?2:1); 165 | 166 | char *value = buffer + i; 167 | tailPos = (char *)memchr(buffer+i, '\r', this->bufferLen - i); 168 | if(tailPos == NULL) { // ERROR, TRY AND GET MORE DATA 169 | this->state = REQUEST_STATE::NEED_MORE_DATA; 170 | 171 | return 1; 172 | } else { 173 | i = (int) (tailPos - buffer); 174 | buffer[i++] = '\0'; 175 | } 176 | 177 | //this->headers[std::string(name, nameLen)] = std::string(value, valueLen); 178 | this->headers[name] = value; 179 | 180 | // Finished parsing headers 181 | if (buffer[i + 1] == '\r' && buffer[i + 2] == '\n') { 182 | // Possibility of pipelining 183 | if(this->method == HTTP_METHOD::GET || this->method == HTTP_METHOD::HEAD) { 184 | // More pipelined requests 185 | if(i + 3 < this->bufferLen) { 186 | this->lastParsePos = i+3; 187 | 188 | // Return back to handler 189 | return 0; 190 | } else { 191 | parserState = PARSER_STATE::FINISHED; 192 | this->finished = true; 193 | 194 | return 0; 195 | } 196 | } else { 197 | // Is request not drained 198 | if(i + 3 < this->bufferLen) { 199 | parserState = PARSER_STATE::BODY; 200 | } else { 201 | parserState = PARSER_STATE::FINISHED; 202 | this->finished = true; 203 | 204 | return 0; 205 | } 206 | } 207 | } 208 | 209 | break; 210 | } 211 | case PARSER_STATE::BODY: 212 | i += 2; 213 | // TODO: Copy the body into the message buffer to send to onData event 214 | this->bodyBuffer = buffer + i; 215 | this->bodyBufferLen = this->bufferLen - i; 216 | parserState = PARSER_STATE::FINISHED; 217 | break; 218 | default: 219 | i = this->bufferLen; 220 | this->finished = true; 221 | break; 222 | } 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | bool MNS::Request::hasHeader(const std::string &name) { 229 | //return false; 230 | return this->headers.find(name) != this->headers.end(); 231 | } 232 | 233 | std::string MNS::Request::getHeader(const std::string &name) { 234 | //return ""; 235 | return std::string(this->headers[name]); 236 | } 237 | 238 | MNS::Request::~Request() { 239 | free(this->buffer); 240 | 241 | this->socketData = NULL; 242 | this->buffer = NULL; 243 | this->state = REQUEST_STATE::FINISHED; 244 | } -------------------------------------------------------------------------------- /src/Request.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/24/17. 3 | // 4 | #ifndef MNS_REQUEST_H 5 | #define MNS_REQUEST_H 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "Variables.h" 13 | #include "Socket.h" 14 | 15 | namespace MNS { 16 | // Forward declaration 17 | class SocketData; 18 | 19 | /** 20 | * Request class. Implements the methods for request manipulation. 21 | */ 22 | class Request { 23 | public: 24 | /** 25 | * State of the request
26 | *
27 | * CONNECTING - The peer connected, but no data yet received...
28 | * READING_SOCKET - The peer sent data, reading...
29 | * PARSING_HEADERS - All the required data received, parsing the headers...
30 | * PARSING_BODY - All the headers parsed, parsing the body of the request...
31 | * FINISHED - Request is read and parsed, ready to be responded to... 32 | */ 33 | enum REQUEST_STATE { 34 | CONNECTING, 35 | READING_SOCKET, 36 | PARSING_HEADERS, 37 | PARSING_BODY, 38 | FINISHED, 39 | NEED_MORE_DATA 40 | }; 41 | 42 | /** 43 | * Default constructor 44 | * @param socketData - SocketData for the request 45 | */ 46 | Request(const MNS::SocketData* socketData); 47 | 48 | /// Default destructor 49 | ~Request(); 50 | 51 | /** 52 | * Clears the request to be reused in case of persistent connections 53 | * @return 0 on success, non zero on error 54 | */ 55 | int clear(); 56 | 57 | /** 58 | * Returns the finish state of the request 59 | * @return true if request finished; false otherwise 60 | */ 61 | bool isFinished(); 62 | /** 63 | * Returns the request buffer 64 | * @return Buffer containing the request data 65 | */ 66 | char* getBuffer(); 67 | 68 | /** 69 | * Returns the body of the request 70 | * @return Buffer containing the body of the request 71 | */ 72 | char* getBodyBuffer(); 73 | 74 | /** 75 | * Returns the size of the request buffer 76 | * @return Size of the buffer 77 | */ 78 | ssize_t getBufferSize(); 79 | 80 | /** 81 | * Resizes the request buffer in case the buffer is smaller than the data 82 | * @param newSize New size of the buffer 83 | */ 84 | void resizeBuffer(int newSize); 85 | 86 | /** 87 | * Returns the lenght of the request data 88 | * @return Length of the read request 89 | */ 90 | ssize_t getBufferLen(); 91 | 92 | /** 93 | * Returns the lenght of the request body data 94 | * @return Length of the read request body 95 | */ 96 | ssize_t getBodyBufferLen(); 97 | 98 | /** 99 | * Parse the request 100 | * @param requestLen - Length of the request to parse 101 | * @return 0 on success, non zero on error 102 | */ 103 | int Parse(ssize_t requestLen); 104 | 105 | /** 106 | * Checks if the request contains header 107 | * @param name - Name of the header to check 108 | * @return TRUE if request contains the header, FALSE otherwise 109 | */ 110 | bool hasHeader(const std::string &name); 111 | 112 | /** 113 | * Returns the header value 114 | * @param name - Name of the header 115 | * @return Value of the header. NULL if not found 116 | */ 117 | std::string getHeader(const std::string &name); 118 | 119 | /// map of the read headers 120 | std::map headers; 121 | 122 | /// Parsed HTTP_VERSION (1.1, 1.0) 123 | MNS::HTTP_VERSION httpVersion; 124 | 125 | /// Parsed HTTP_METHOD (GET, POST, PUT, ...) 126 | MNS::HTTP_METHOD method; 127 | 128 | /// Fd of the socket 129 | int socket; 130 | 131 | /// Parsed URL (/...) 132 | const char *url; 133 | 134 | /// State of the request 135 | REQUEST_STATE state; 136 | 137 | private: 138 | /// Internal buffers 139 | char* buffer; 140 | char* bodyBuffer; 141 | 142 | /// Mark the finish of the request processing 143 | bool finished; 144 | 145 | ssize_t lastParsePos; 146 | /// Internal buffer length 147 | ssize_t bufferLen; 148 | 149 | /// Internal body buffer length 150 | ssize_t bodyBufferLen; 151 | 152 | ssize_t bufferSize; 153 | 154 | /// SocketData 155 | const MNS::SocketData *socketData; 156 | 157 | protected: 158 | }; 159 | } 160 | 161 | #endif //MNS_REQUEST_H 162 | -------------------------------------------------------------------------------- /src/Response.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/24/17. 3 | // 4 | 5 | #include "Response.h" 6 | 7 | #ifdef __APPLE__ /* Fails for Macs, need to define mempcpy 8 | explicitly */ 9 | /* From Gnulib */ 10 | void *mempcpy(void *dest, const void *src, size_t n) { 11 | return (char *) memcpy(dest, src, n) + n; 12 | } 13 | 14 | #endif 15 | 16 | MNS::Response::Response(const MNS::SocketData *socketData) { 17 | this->finished = false; 18 | this->socketData = socketData; 19 | 20 | this->statusCode = 200; 21 | 22 | this->bufferLen = 0; 23 | this->buffer = (char *)malloc(4096); 24 | this->responseBuffer = (char *)malloc(4096); 25 | this->bufferSize = 4096; 26 | this->headersSent = false; 27 | this->sendDate = true; 28 | this->statusMessage = NULL; 29 | //this->response.reserve(1024); 30 | } 31 | 32 | int MNS::Response::clear() { 33 | this->statusCode = 200; 34 | 35 | this->finished = false; 36 | this->headersSent = false; 37 | this->sendDate = true; 38 | this->statusMessage = NULL; 39 | //this->response.clear(); 40 | 41 | this->bufferLen = 0; 42 | if(this->bufferSize != 4096) { 43 | this->buffer = (char *)realloc(this->buffer, 4096); 44 | this->responseBuffer = (char *)realloc(this->responseBuffer, 4096); 45 | this->bufferSize = 4096; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | void MNS::Response::startResponse() { 52 | MNS::Request *request = this->socketData->request; 53 | if(request->httpVersion == HTTP_VERSION::HTTP_1_0 && request->headers["connection"] == "close") { 54 | this->headers = { 55 | {std::string("Server", 6), std::string("Mitol", 5)}, 56 | {std::string("Content-Type", 12), std::string("text/html", 9)}, 57 | {std::string("Connection", 10), std::string("close", 5)} 58 | }; 59 | } else { 60 | this->headers = { 61 | {std::string("Server", 6), std::string("Mitol", 5)}, 62 | {std::string("Content-Type", 12), std::string("text/html", 9)}, 63 | {std::string("Connection", 10), std::string("keep-alive", 10)} 64 | }; 65 | } 66 | } 67 | 68 | const char* MNS::Response::getBuffer() { 69 | return this->responseBuffer; 70 | //return this->response.c_str(); 71 | } 72 | 73 | unsigned int MNS::Response::getBufferLen() { 74 | return this->bufferLen; 75 | //return this->response.length(); 76 | } 77 | 78 | int MNS::Response::addTrailers(void *headers) {return 0;} 79 | 80 | int MNS::Response::write(const char *data, unsigned int dataLen) { 81 | if(data) { 82 | //unsigned int dataLen = strlen(data); 83 | if(this->bufferSize < dataLen + 512) { 84 | int ns = std::max(dataLen + 512, (unsigned int)4096); 85 | this->bufferSize = this->bufferSize + ns; 86 | this->buffer = (char *)realloc(this->buffer, this->bufferSize); 87 | } 88 | 89 | memcpy(this->buffer + this->bufferLen, data, dataLen); 90 | bufferLen += dataLen; 91 | 92 | return 0; 93 | } 94 | 95 | return -1; 96 | } 97 | 98 | int MNS::Response::end(const char *data, unsigned int dataLen) { 99 | // int offset = std::sprintf(this->buffer, "HTTP/1.1 200 OK\r\nContent-Length: %u\r\n\r\n", (unsigned int) dataLen); 100 | // memcpy(this->buffer+offset, data, dataLen); 101 | // 102 | // this->bufferLen = offset + dataLen; 103 | // 104 | // uv_poll_start(this->socketData->poll_h, UV_WRITABLE, MNS::Server::onWriteData); 105 | // return 0; 106 | 107 | if(this->bufferSize < dataLen + 512) { 108 | int ns = std::max(dataLen + 512, (unsigned int)4096); 109 | this->bufferSize = this->bufferSize + ns; 110 | this->responseBuffer = (char *)realloc(this->responseBuffer, this->bufferSize); 111 | } 112 | 113 | int offset = 0; 114 | //this->setHeader(std::string("Date", 4), MNS::Server::currTime); 115 | //this->setHeader(std::string("Content-Length", 14), std::to_string(dataLen)); 116 | 117 | MNS::Request *request = this->socketData->request; 118 | char *rb = this->responseBuffer; 119 | // First print the status code 120 | rb = (char *)mempcpy(rb, (request->httpVersion==HTTP_VERSION::HTTP_1_1)?"HTTP/1.1 ":"HTTP/1.0 ", 9); 121 | // Copy the response MSG 122 | std::string msg = MNS::Server::response_msgs[this->statusCode]; 123 | rb = (char *)mempcpy(rb, msg.c_str(), msg.length()); 124 | offset += msg.length() + 9; 125 | 126 | // Set the static headers 127 | 128 | // Content-Length 129 | std::string strDataLen = std::to_string(bufferLen + dataLen); 130 | rb = (char *)mempcpy(rb, "Content-Length", 14); 131 | rb = (char *)mempcpy(rb, ": ", 2); 132 | rb = (char *)mempcpy(rb, strDataLen.c_str(), strDataLen.length()); 133 | rb = (char *)mempcpy(rb, "\r\n", 2); 134 | offset += strDataLen.length() + 18; 135 | 136 | // Date 137 | if(this->sendDate) { 138 | rb = (char *) mempcpy(rb, "Date", 4); 139 | rb = (char *) mempcpy(rb, ": ", 2); 140 | rb = (char *) mempcpy(rb, MNS::Server::currTime.c_str(), MNS::Server::currTime.length()); 141 | rb = (char *) mempcpy(rb, "\r\n", 2); 142 | offset += MNS::Server::currTime.length() + 8; 143 | } 144 | 145 | // Copy the headers 146 | for(std::map::const_iterator header = this->headers.begin(); header != this->headers.end(); header++) { 147 | rb = (char *)mempcpy(rb, header->first.c_str(), header->first.length()); 148 | rb = (char *)mempcpy(rb, ": ", 2); 149 | rb = (char *)mempcpy(rb, header->second.c_str(), header->second.length()); 150 | rb = (char *)mempcpy(rb, "\r\n", 2); 151 | offset += header->first.length() + header->second.length() + 4; 152 | } 153 | 154 | // Copy trailing line end 155 | rb = (char *)mempcpy(rb, "\r\n", 2); 156 | offset += 2; 157 | 158 | // Finally copy the buffer 159 | if(this->bufferLen) { 160 | rb = (char *)mempcpy(rb, this->buffer, this->bufferLen); 161 | offset += this->bufferLen; 162 | } 163 | 164 | // Copy the sent data 165 | if(data) { 166 | rb = (char *)mempcpy(rb, data, dataLen); 167 | offset += dataLen; 168 | } 169 | 170 | this->bufferLen = offset; 171 | 172 | if(!uv_is_closing((uv_handle_t*)this->socketData->poll_h)) 173 | uv_poll_start(this->socketData->poll_h, UV_WRITABLE, MNS::Server::onWriteData); 174 | this->finished = true; 175 | 176 | return 0; 177 | 178 | /* 179 | MNS::Request *request = this->socketData->request; 180 | 181 | if(data) { 182 | //unsigned int dataLen = strlen(data); 183 | if(this->bufferSize - bufferLen < dataLen) { 184 | this->bufferSize = (dataLen>1024)?this->bufferSize + dataLen:this->bufferSize + 1024; 185 | this->buffer = (char *)realloc(this->buffer, this->bufferSize); 186 | } 187 | 188 | memcpy(this->buffer + this->bufferLen, data, dataLen); 189 | this->bufferLen += dataLen; 190 | } 191 | 192 | 193 | this->setHeader(std::string("Content-Length", 14), std::to_string(this->bufferLen)); 194 | this->setHeader("Date", MNS::Server::currTime); 195 | 196 | response.append((request->httpVersion==HTTP_VERSION::HTTP_1_1)?"HTTP/1.1 ":"HTTP/1.0 ", 9); 197 | response.append(MNS::Server::response_msgs[this->statusCode]); 198 | 199 | for(std::map::const_iterator header = this->headers.begin(); header != this->headers.end(); header++) { 200 | response.append(header->first); 201 | response.append(": ", 2); 202 | response.append(header->second); 203 | response.append("\r\n", 2); 204 | } 205 | 206 | response.append("\r\n", 2); 207 | response.append(this->buffer, this->bufferLen); 208 | 209 | // ssize_t numSent = send(this->socketData->fd, response.c_str(), response.length(), 0); 210 | // if(numSent == (int)response.length()) { 211 | // this->bufferLen = 0; 212 | // response.clear(); 213 | // //uv_poll_start(handle, UV_READABLE, onReadData); 214 | // } 215 | // uv_poll_start(this->socketData->poll_h, UV_READABLE, MNS::Server::onReadData); 216 | uv_poll_start(this->socketData->poll_h, UV_WRITABLE, MNS::Server::onWriteData); 217 | this->finished = true; 218 | 219 | return 0;*/ 220 | } 221 | 222 | const char* MNS::Response::getHeader(const char *name) { 223 | auto headerItem = this->headers.find(name); 224 | if(headerItem != this->headers.end()) { 225 | return headerItem->second.c_str(); 226 | } 227 | 228 | return NULL; 229 | } 230 | 231 | std::set MNS::Response::getHeaderNames() { 232 | std::set headerNames; 233 | for(auto headerItem = this->headers.begin(); headerItem != this->headers.end(); headerItem++) { 234 | headerNames.insert(headerItem->first.c_str()); 235 | } 236 | 237 | return headerNames; 238 | } 239 | 240 | std::map MNS::Response::getHeaders() { 241 | return this->headers; 242 | } 243 | 244 | bool MNS::Response::hasHeader(const char *name) { 245 | return this->headers.find(name) != this->headers.end(); 246 | } 247 | 248 | int MNS::Response::removeHeader(const char *name) { 249 | this->headers.erase(name); 250 | 251 | return 0; 252 | } 253 | 254 | int MNS::Response::setHeader(const std::string &name, const std::string &value) { 255 | this->headers[name] = value; 256 | 257 | return 0; 258 | } 259 | 260 | int MNS::Response::setTimeout(int msecs, void *callback) { 261 | return 0; 262 | } 263 | 264 | int MNS::Response::writeContinue() { 265 | return 0; 266 | } 267 | 268 | int MNS::Response::writeHead(int statusCode, const char *statusMessage, const std::map &headers) { 269 | return 0; 270 | } 271 | 272 | MNS::Response::~Response() { 273 | this->socketData = NULL; 274 | 275 | free(this->buffer); 276 | this->buffer = NULL; 277 | 278 | free(this->responseBuffer); 279 | this->responseBuffer = NULL; 280 | 281 | this->bufferLen = 0; 282 | this->bufferSize = 0; 283 | } -------------------------------------------------------------------------------- /src/Response.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/24/17. 3 | // 4 | 5 | #ifndef MNS_RESPONSE_H 6 | #define MNS_RESPONSE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "Variables.h" 15 | #include "Request.h" 16 | #include "Socket.h" 17 | 18 | namespace MNS { 19 | // Forward declaration 20 | class SocketData; 21 | 22 | /** 23 | * Response class. Implements the methods for response manipulation. 24 | */ 25 | class Response { 26 | public: 27 | /** 28 | * Default constructor 29 | * @param socketData - SocketData needed for the response object 30 | */ 31 | Response(const SocketData *socketData); 32 | 33 | /** 34 | * Default destructor 35 | */ 36 | ~Response(); 37 | 38 | /** 39 | * Clears the response to be reused in case of persistent connections 40 | * @return 0 on success, non zero on error 41 | */ 42 | int clear(); 43 | 44 | /** 45 | * Returns the response buffer that is to be sent by the server 46 | * @return Buffer 47 | */ 48 | const char *getBuffer(); 49 | 50 | /** 51 | * Returns the length of the repsonse buffer 52 | * @return 53 | */ 54 | unsigned int getBufferLen(); 55 | 56 | /** 57 | * !NOT IMPLEMENTED 58 | * Add the trailers to the response 59 | * @param headers 60 | * @return 61 | */ 62 | int addTrailers(void *headers); 63 | 64 | /** 65 | * Ends the response 66 | * @param data - Buffer to finally send. Can be NULL. 67 | * @param dataLen - The length of the buffer to send 68 | * @return 0 on success. Non zero on error. 69 | */ 70 | int end(const char *data, unsigned int dataLen); 71 | 72 | /** 73 | * Is the response finished 74 | */ 75 | bool finished; 76 | 77 | /** 78 | * Returns the value of the header 79 | * @param name - Name of the header to be returned 80 | * @return Header value; NULL if not found 81 | */ 82 | const char *getHeader(const char *name); 83 | 84 | /** 85 | * Returns all the header names used in the response 86 | * @return std::set holding header names 87 | */ 88 | std::set getHeaderNames(); 89 | 90 | /** 91 | * Returns all the headers in the response 92 | * @return std::map holding all the headers 93 | */ 94 | std::map getHeaders(); 95 | 96 | /** 97 | * Checks if the response has the requested header 98 | * @param name - Name of the header to check 99 | * @return TRUE if header existing, FALSE otherwise 100 | */ 101 | bool hasHeader(const char *name); 102 | 103 | /** 104 | * Were headers sent 105 | */ 106 | bool headersSent; 107 | 108 | /** 109 | * Removes the header from the map 110 | * @param name - Name of the header to remove 111 | * @return 0 on success, non zero on error 112 | */ 113 | int removeHeader(const char *name); 114 | 115 | /** 116 | * Send the date header with the response 117 | */ 118 | bool sendDate; 119 | 120 | /** 121 | * Adds or changes the header to be sent with the response 122 | * @param name - Name of the header 123 | * @param value - Value of the header 124 | * @return 0 on success, non zero on error 125 | */ 126 | int setHeader(const std::string &name, const std::string &value); 127 | 128 | /** 129 | * Sets the timeout for the response object 130 | * @param msecs - Duration before the timeout kicks in 131 | * @param callback - Callback to call in case of timeout 132 | * @return 0 on success, non zero on error 133 | */ 134 | int setTimeout(int msecs, void *callback); 135 | 136 | /** 137 | * Status code of the response
138 | * 200 - OK
139 | * 404 - Not Found
140 | * 500 - Internal Server Error 141 | */ 142 | int statusCode; 143 | 144 | /** 145 | * Custom status message 146 | */ 147 | char *statusMessage; 148 | 149 | /** 150 | * Write the data to the response buffer 151 | * @param data - Data to write to responseBuffer 152 | * @param dataLen - Length of the data to write 153 | * @return 0 on success, non zero on error 154 | */ 155 | int write(const char *data, unsigned int dataLen); 156 | 157 | /** 158 | * Return the CONTINUE 101 response for body data 159 | * @return 0 on success, non zero on error 160 | */ 161 | int writeContinue(); 162 | 163 | /** 164 | * Method for writing the head of the response 165 | * @param statusCode - Status code of the response 166 | * @param statusMessage - Status message of the response 167 | * @param headers - Headers for the response 168 | * @return 0 on success, non zero on error 169 | */ 170 | int writeHead(int statusCode, const char *statusMessage, const std::map &headers); 171 | 172 | /** 173 | * Starts the response by filling the default headers 174 | */ 175 | void startResponse(); 176 | 177 | private: 178 | /// Length of the internal buffer 179 | unsigned int bufferLen; 180 | 181 | /// Size of the internal buffer 182 | unsigned int bufferSize; 183 | 184 | /// Internal buffer 185 | char *buffer; 186 | 187 | /// Internal response buffer 188 | char *responseBuffer; 189 | 190 | /// Map of the headers to send with the response 191 | std::map headers; 192 | 193 | /// SocketData 194 | const SocketData *socketData; 195 | protected: 196 | }; 197 | } 198 | 199 | 200 | #endif //MNS_RESPONSE_H 201 | -------------------------------------------------------------------------------- /src/Server.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/22/17. 3 | // 4 | 5 | #include 6 | #include "Server.h" 7 | 8 | static uv_loop_t *loop; 9 | static std::unordered_map polls; 10 | std::string MNS::Server::currTime = ""; 11 | std::map MNS::Server::response_msgs; 12 | 13 | void MNS::Server::onConnect(uv_poll_t *handle, int status, int events) { 14 | MNS::SocketData *data = static_cast(handle->data); 15 | if (data && status==0) { 16 | const MNS::Server *server = data->server; 17 | socklen_t addr_size = 0; 18 | sockaddr_in sadr; 19 | #ifdef __linux__ 20 | int csock = accept4(data->fd, (sockaddr *) &sadr, &addr_size, SOCK_NONBLOCK); 21 | #elif __APPLE__ 22 | int csock = accept(data->fd, (sockaddr *) &sadr, &addr_size); 23 | if(csock >= 0) MNS::Socket::makeNonBlocking(csock); 24 | #endif 25 | 26 | if(csock >= 0) { // If valid socket 27 | int y_int = 1; 28 | if(setsockopt(csock, SOL_SOCKET, SO_KEEPALIVE, &y_int, sizeof(int)) == -1) { 29 | // NOOP 30 | } 31 | #ifdef __linux__ 32 | if(setsockopt(csock, IPPROTO_TCP, TCP_NODELAY, &y_int, sizeof(int)) == -1) { 33 | // NOOP 34 | } 35 | #endif 36 | //setsockopt(csock, IPPROTO_TCP, TCP_QUICKACK, &y_int, sizeof(int)); 37 | 38 | uv_poll_t *socket_poll_h = (uv_poll_t *) malloc(sizeof(uv_poll_t)); 39 | socket_poll_h->data = new MNS::SocketData(socket_poll_h, csock, MNS::SOCKET_TYPE::PEER, data->server); 40 | 41 | uv_poll_init(loop, socket_poll_h, csock); 42 | uv_poll_start(socket_poll_h, UV_READABLE, MNS::Server::onReadData); 43 | 44 | polls[csock] = socket_poll_h; 45 | 46 | if (server && server->onHttpConnectionHandler) { 47 | server->onHttpConnectionHandler(static_cast(socket_poll_h->data)); 48 | } 49 | } 50 | } 51 | } 52 | 53 | void MNS::Server::onClose(uv_handle_t *handle) { 54 | MNS::SocketData *data = static_cast(handle->data); 55 | // Deregister NodeJS Persistent 56 | MNS::Server *server = data->server; 57 | if(server && server->onHttpCancelHandler) server->onHttpCancelHandler(data); 58 | 59 | polls.erase(data->fd); 60 | close(data->fd); 61 | delete (data); 62 | free(handle); 63 | } 64 | 65 | void MNS::Server::onCloseTimer(uv_handle_t *handle) { 66 | uv_timer_stop((uv_timer_t *) handle); 67 | delete (handle); 68 | } 69 | 70 | void MNS::Server::onReadDataPipelined(uv_poll_t *handle, int status, int events) { 71 | MNS::SocketData *data = static_cast(handle->data); 72 | MNS::Server *server = data->server; 73 | 74 | int parseResult = data->request->Parse(-1); 75 | if (!parseResult) { // PARSING OK 76 | data->response->startResponse(); 77 | 78 | if (server->onHttpRequestHandler) { 79 | server->onHttpRequestHandler(data); 80 | } else { 81 | fflush(stdout); 82 | 83 | uv_poll_stop(handle); 84 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 85 | } 86 | } else { // ERROR PARSING 87 | if(data->request->state == MNS::Request::REQUEST_STATE::NEED_MORE_DATA) { // MORE DATA REQUIRED 88 | if(!uv_is_closing((uv_handle_t*)handle)) 89 | uv_poll_start(handle, UV_READABLE, onReadData); 90 | } else { 91 | //printf("Parse ERROR! Closing socket!\n"); 92 | 93 | uv_poll_stop(handle); 94 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 95 | } 96 | } 97 | } 98 | 99 | void MNS::Server::onReadData(uv_poll_t *handle, int status, int events) { 100 | if (status < 0 && errno != EAGAIN) { 101 | uv_poll_stop(handle); 102 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 103 | 104 | return; 105 | } 106 | 107 | MNS::SocketData *data = static_cast(handle->data); 108 | MNS::Server *server = data->server; 109 | 110 | ssize_t requestLen = data->request->isFinished()?0:data->request->getBufferLen(); 111 | ssize_t bytesRead = 0; 112 | // Todo: Offset and grow buffer in case of larger requests 113 | // Todo: Mark request state as reading socket 114 | while ((bytesRead = recv(data->fd, data->request->getBuffer() + requestLen, 1024, 0)) > 0) { // WHILE DATA READ:: POTENTIAL ERROR SIZE OF READ DATA 115 | requestLen += bytesRead; 116 | 117 | // If buffer too small, resize 118 | if(requestLen + 1024 >= data->request->getBufferSize()) { 119 | data->request->resizeBuffer(data->request->getBufferSize() + 4096); 120 | } 121 | } 122 | 123 | if (requestLen <= 0) { 124 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 125 | } else { 126 | uv_poll_stop(handle); 127 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 128 | 129 | return; 130 | } 131 | } else { 132 | int parseResult = data->request->Parse(requestLen); 133 | if (!parseResult) { // PARSING OK 134 | data->response->startResponse(); 135 | 136 | if (server->onHttpRequestHandler) { 137 | server->onHttpRequestHandler(data); 138 | } else { 139 | fflush(stdout); 140 | 141 | uv_poll_stop(handle); 142 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 143 | } 144 | } else { // ERROR PARSING 145 | if(data->request->state == MNS::Request::REQUEST_STATE::NEED_MORE_DATA) { // MORE DATA REQUIRED 146 | if(!uv_is_closing((uv_handle_t*)handle)) 147 | uv_poll_start(handle, UV_READABLE, onReadData); 148 | } else { 149 | //printf("Parse ERROR! Closing socket!\n"); 150 | 151 | uv_poll_stop(handle); 152 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 153 | } 154 | } 155 | } 156 | } 157 | 158 | void MNS::Server::onWriteData(uv_poll_t *handle, int status, int events) { 159 | MNS::SocketData *data = static_cast(handle->data); 160 | 161 | if (status < 0) { 162 | if (errno != EAGAIN) { 163 | uv_poll_stop(handle); 164 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 165 | return; 166 | } 167 | } 168 | 169 | if (data) { 170 | MNS::Response *response = data->response; 171 | 172 | ssize_t numSent = send(data->fd, response->getBuffer(), response->getBufferLen(), 0); 173 | if (numSent == (int) response->getBufferLen()) { 174 | response->clear(); 175 | 176 | if (data->request->isFinished()) { 177 | data->request->clear(); 178 | if(!uv_is_closing((uv_handle_t*)handle)) 179 | uv_poll_start(handle, UV_READABLE, onReadData); 180 | } else { 181 | // Request not finished, pipelining, continue serving data 182 | if(!uv_is_closing((uv_handle_t*)handle)) 183 | uv_poll_start(handle, UV_WRITABLE, onReadDataPipelined); 184 | //onReadDataPipelined(handle, 0, 0); 185 | } 186 | } else if (numSent < 0) { 187 | int err = errno; 188 | 189 | if (err != EAGAIN) { 190 | uv_poll_stop(handle); 191 | uv_close((uv_handle_t *) handle, MNS::Server::onClose); 192 | return; 193 | } 194 | } 195 | 196 | } 197 | } 198 | 199 | MNS::Server::Server() { 200 | MNS::Server::response_msgs[100] = "100 Continue\r\n"; 201 | MNS::Server::response_msgs[101] = "101 Switching Protocols\r\n"; 202 | MNS::Server::response_msgs[102] = "102 Checkpoint\r\n"; 203 | 204 | MNS::Server::response_msgs[200] = "200 OK\r\n"; 205 | MNS::Server::response_msgs[201] = "201 Created\r\n"; 206 | MNS::Server::response_msgs[202] = "202 Accepted\r\n"; 207 | MNS::Server::response_msgs[203] = "203 Non-Authoritative Information\r\n"; 208 | MNS::Server::response_msgs[204] = "204 No Content\r\n"; 209 | MNS::Server::response_msgs[205] = "205 Reset Content\r\n"; 210 | MNS::Server::response_msgs[206] = "206 Partial Content\r\n"; 211 | 212 | MNS::Server::response_msgs[300] = "300 Multiple Choices\r\n"; 213 | MNS::Server::response_msgs[301] = "301 Moved Permanently\r\n"; 214 | MNS::Server::response_msgs[302] = "302 Found\r\n"; 215 | MNS::Server::response_msgs[303] = "303 See Other\r\n"; 216 | MNS::Server::response_msgs[304] = "304 Not Modified\r\n"; 217 | MNS::Server::response_msgs[306] = "306 Switch Proxy\r\n"; 218 | MNS::Server::response_msgs[307] = "307 Temporary Redirect\r\n"; 219 | MNS::Server::response_msgs[308] = "308 Resume Incomplete\r\n"; 220 | 221 | 222 | MNS::Server::response_msgs[400] = "400 Bad Request\r\n"; 223 | MNS::Server::response_msgs[401] = "401 Unauthorized\r\n"; 224 | MNS::Server::response_msgs[402] = "402 Payment Required\r\n"; 225 | MNS::Server::response_msgs[403] = "403 Forbidden\r\n"; 226 | MNS::Server::response_msgs[404] = "404 Not Found\r\n"; 227 | MNS::Server::response_msgs[405] = "405 Method Not Allowed\r\n"; 228 | MNS::Server::response_msgs[406] = "406 Not Acceptable\r\n"; 229 | MNS::Server::response_msgs[407] = "407 Proxy Authentication Required\r\n"; 230 | MNS::Server::response_msgs[408] = "408 Request Timeout\r\n"; 231 | MNS::Server::response_msgs[409] = "409 Conflict\r\n"; 232 | MNS::Server::response_msgs[410] = "410 Gone\r\n"; 233 | MNS::Server::response_msgs[411] = "411 Length Required\r\n"; 234 | MNS::Server::response_msgs[412] = "412 Precondition Failed\r\n"; 235 | MNS::Server::response_msgs[413] = "413 Request Entity Too Large\r\n"; 236 | MNS::Server::response_msgs[414] = "414 Request-URI Too Long\r\n"; 237 | MNS::Server::response_msgs[415] = "415 Unsupported Media Type\r\n"; 238 | MNS::Server::response_msgs[416] = "416 Requested Range Not Satisfiable\r\n"; 239 | MNS::Server::response_msgs[417] = "417 Expectation Failed\r\n"; 240 | 241 | MNS::Server::response_msgs[500] = "500 Internal Server Error\r\n"; 242 | MNS::Server::response_msgs[501] = "501 Not Implemented\r\n"; 243 | MNS::Server::response_msgs[502] = "502 Bad Gateway\r\n"; 244 | MNS::Server::response_msgs[503] = "503 Service Unavailable\r\n"; 245 | MNS::Server::response_msgs[504] = "504 Gateway Timeout\r\n"; 246 | MNS::Server::response_msgs[505] = "505 HTTP Version Not Supported\r\n"; 247 | MNS::Server::response_msgs[511] = "511 Network Authentication Required\r\n"; 248 | 249 | loop = uv_default_loop(); 250 | 251 | this->onHttpCancelHandler = NULL; 252 | this->onHttpConnectionHandler = NULL; 253 | this->onHttpRequestHandler = NULL; 254 | this->listeningSocket = 0; 255 | this->timer_h = NULL; 256 | } 257 | 258 | void MNS::Server::listen(int port) { 259 | listeningSocket = Socket::createListening(port); 260 | 261 | if (listeningSocket != -1) { 262 | // Start the timer 263 | this->timer_h = new uv_timer_t;//(uv_timer_t *)malloc(sizeof(uv_timer_t)); 264 | uv_timer_init(loop, timer_h); 265 | uv_timer_start(timer_h, MNS::Server::onSecondTimer, 0, 1000); 266 | 267 | // Register the listening socket 268 | uv_poll_t *listening_poll_h = (uv_poll_t *) malloc(sizeof(uv_poll_t)); 269 | listening_poll_h->data = new SocketData(listening_poll_h, listeningSocket, SOCKET_TYPE::LISTENING, this); 270 | 271 | uv_poll_init(loop, listening_poll_h, listeningSocket); 272 | 273 | uv_poll_start(listening_poll_h, UV_READABLE, onConnect); 274 | polls[listeningSocket] = listening_poll_h; 275 | 276 | if(this->onHttpListeningHandler) { 277 | this->onHttpListeningHandler(); 278 | } 279 | } 280 | } 281 | 282 | void MNS::Server::run() { 283 | uv_run(loop, UV_RUN_DEFAULT); 284 | 285 | uv_loop_close(loop); 286 | } 287 | 288 | void MNS::Server::stop() { 289 | uv_close((uv_handle_t *) this->timer_h, MNS::Server::onCloseTimer); 290 | 291 | for (auto it = polls.begin(); it != polls.end(); ++it) { 292 | uv_poll_stop(it->second); 293 | uv_close((uv_handle_t *) it->second, MNS::Server::onClose); 294 | } 295 | 296 | polls.clear(); 297 | } 298 | 299 | void MNS::Server::onSecondTimer(uv_timer_t *handle) { 300 | char strt[100]; 301 | std::time_t t = std::time(NULL); 302 | std::strftime(strt, 100, "%a, %e %b %Y %H:%M:%S GMT\0", std::gmtime(&t)); 303 | 304 | MNS::Server::currTime = strt; 305 | } 306 | 307 | void MNS::Server::onHttpListening(const std::function &callback) { 308 | this->onHttpListeningHandler = callback; 309 | } 310 | 311 | void MNS::Server::onHttpConnection(const std::function &callback) { 312 | this->onHttpConnectionHandler = callback; 313 | } 314 | 315 | void MNS::Server::onHttpRequest(const std::function &callback) { 316 | this->onHttpRequestHandler = callback; 317 | } 318 | 319 | void MNS::Server::onHttpCancel(const std::function &callback) { 320 | this->onHttpCancelHandler = callback; 321 | } 322 | 323 | 324 | MNS::Server::~Server() { 325 | } -------------------------------------------------------------------------------- /src/Server.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/22/17. 3 | // 4 | 5 | #ifndef MNS_SERVER_H 6 | #define MNS_SERVER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "Process.h" 24 | #include "Socket.h" 25 | #include "Request.h" 26 | #include "Response.h" 27 | 28 | #ifndef SOCK_NONBLOCK 29 | #define SOCK_NONBLOCK O_NONBLOCK 30 | #endif 31 | 32 | namespace MNS { 33 | // Forward declaration 34 | class SocketData; 35 | 36 | /** 37 | * Server class 38 | */ 39 | class Server { 40 | public: 41 | /** 42 | * Connection callback 43 | * @param handle - Poll handle 44 | * @param status - Status of the call, 0 OK 45 | * @param events - Poll events 46 | */ 47 | static void onConnect(uv_poll_t *handle, int status, int events); 48 | 49 | /** 50 | * Close callback 51 | * @param handle - Poll handle 52 | */ 53 | static void onClose(uv_handle_t *handle); 54 | 55 | /** 56 | * Close timer callback 57 | * @param handle - Poll handle 58 | */ 59 | static void onCloseTimer(uv_handle_t *handle); 60 | 61 | /** 62 | * Data available callback 63 | * @param handle - Poll handle 64 | * @param status - Status of the call, 0 OK 65 | * @param events - Poll events 66 | */ 67 | static void onReadData(uv_poll_t *handle, int status, int events); 68 | 69 | /** 70 | * Pipeline data callback 71 | * @param handle - Poll handle 72 | * @param status - Status of the call, 0 OK 73 | * @param events - Poll events 74 | */ 75 | static void onReadDataPipelined(uv_poll_t *handle, int status, int events); 76 | /** 77 | * Callback for writing data on the socket 78 | * @param handle - Poll handle 79 | * @param status - Status of the call, 0 OK 80 | * @param events - Poll events 81 | */ 82 | static void onWriteData(uv_poll_t *handle, int status, int events); 83 | 84 | /** 85 | * Timer callback. Returns every second. 86 | * @param handle - Timer handle 87 | */ 88 | static void onSecondTimer(uv_timer_t *handle); 89 | 90 | /// Current GMT time. Formatted for responses 91 | static std::string currTime; 92 | 93 | /// Prepopulated defaut response messages 94 | static std::map response_msgs; 95 | 96 | /// Default constructor 97 | Server(); 98 | 99 | /** 100 | * Listens on specified port 101 | * @param port - Port number to listen to 102 | */ 103 | void listen(int port); 104 | 105 | /// Callback holder 106 | std::function onHttpListeningHandler; 107 | 108 | ///Registers a callback function, when a server starts listening 109 | void onHttpListening(const std::function &); 110 | 111 | /// Callback holder 112 | std::function onHttpConnectionHandler; 113 | 114 | ///Registers a callback function, when a connection is received 115 | void onHttpConnection(const std::function &); 116 | 117 | /// Callback holder 118 | std::function onHttpRequestHandler; 119 | 120 | ///Registers a callback function, when a request is received 121 | void onHttpRequest(const std::function &); 122 | 123 | /// Callback holder 124 | std::function onHttpCancelHandler; 125 | 126 | ///Registers a callback function, when a request/response is canceled 127 | void onHttpCancel(const std::function &); 128 | 129 | /// Runs the poll 130 | void run(); 131 | 132 | /// Stops the poll and releases all the handles 133 | void stop(); 134 | 135 | /// Default destructor 136 | ~Server(); 137 | private: 138 | /// Fd of the listening socket 139 | int listeningSocket; 140 | 141 | /// Handle for second timer 142 | uv_timer_t *timer_h; 143 | protected: 144 | }; 145 | } 146 | 147 | 148 | #endif //MNS_SERVER_H 149 | -------------------------------------------------------------------------------- /src/Socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/27/17. 3 | // 4 | 5 | #include "Socket.h" 6 | 7 | MNS::SocketData::SocketData(uv_poll_t *poll_h, int fd, SOCKET_TYPE type, MNS::Server *server) { 8 | this->poll_h = poll_h; 9 | this->fd = fd; 10 | this->type = type; 11 | this->server = server; 12 | this->request = NULL; 13 | this->response = NULL; 14 | this->nodeRequestPlaceholder = nullptr; 15 | this->nodeResponsePlaceholder = nullptr; 16 | if (type != SOCKET_TYPE::LISTENING) { 17 | this->request = new MNS::Request(this); 18 | this->response = new MNS::Response(this); 19 | } 20 | } 21 | 22 | MNS::SocketData::~SocketData() { 23 | if (this->request) delete (this->request); 24 | if (this->response) delete (this->response); 25 | this->fd = -1; 26 | this->request = NULL; 27 | this->response = NULL; 28 | this->server = NULL; 29 | this->nodeRequestPlaceholder = nullptr; 30 | this->nodeResponsePlaceholder = nullptr; 31 | } 32 | 33 | int MNS::Socket::createListening(int port) { 34 | struct addrinfo hints; 35 | struct addrinfo *result, *rp; 36 | int s, sfd=-1; 37 | 38 | memset(&hints, 0, sizeof(struct addrinfo)); 39 | hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ 40 | hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ 41 | hints.ai_flags = AI_PASSIVE; /* All interfaces */ 42 | 43 | s = getaddrinfo(NULL, std::to_string(port).c_str(), &hints, &result); 44 | if (s != 0) { 45 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); 46 | return -1; 47 | } 48 | 49 | for (rp = result; rp != NULL; rp = rp->ai_next) { 50 | sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 51 | if (sfd == -1) 52 | continue; 53 | 54 | int y_int = 1; 55 | if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &y_int, sizeof(int)) == -1) { 56 | printf("Unable to reuse addr\n"); fflush(stdout); 57 | } 58 | 59 | if(setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &y_int, sizeof(int)) == -1) { 60 | printf("Unable to reuse port!n\n"); fflush(stdout); 61 | } 62 | 63 | s = bind(sfd, rp->ai_addr, rp->ai_addrlen); 64 | if (s == 0) { 65 | /* We managed to bind successfully! */ 66 | break; 67 | } 68 | 69 | close(sfd); 70 | } 71 | 72 | freeaddrinfo(result); 73 | 74 | if (rp == NULL) { 75 | fprintf(stderr, "Could not bind\n"); 76 | return -1; 77 | } 78 | 79 | int y_int = 1; 80 | #ifdef __linux__ 81 | if ((setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &y_int, sizeof(int)) == -1) || 82 | (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &y_int, sizeof(int)) == -1) || 83 | (setsockopt(sfd, IPPROTO_TCP, TCP_QUICKACK, &y_int, sizeof(int)) == -1)) { 84 | // TODO: Return an error 85 | close(sfd); 86 | return -1; 87 | } 88 | #elif __APPLE__ 89 | if ((setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &y_int, sizeof(int)) == -1) || 90 | (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &y_int, sizeof(int)) == -1)) { 91 | // TODO: Return an error 92 | return -1; 93 | } 94 | #endif 95 | Socket::makeNonBlocking(sfd); 96 | 97 | if (::listen(sfd, 4096) == -1) { 98 | close(sfd); 99 | // TODO: Return an error 100 | return -1; 101 | } 102 | 103 | return sfd; 104 | } 105 | 106 | int MNS::Socket::makeNonBlocking(int sfd) { 107 | int flags, s; 108 | 109 | flags = fcntl(sfd, F_GETFL, 0); 110 | if (flags == -1) { 111 | perror("fcntl"); 112 | return -1; 113 | } 114 | 115 | flags |= O_NONBLOCK; 116 | s = fcntl(sfd, F_SETFL, flags); 117 | if (s == -1) { 118 | perror("fcntl"); 119 | return -1; 120 | } 121 | 122 | return 0; 123 | } -------------------------------------------------------------------------------- /src/Socket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/22/17. 3 | // 4 | 5 | #ifndef MNS_SOCKET_H 6 | #define MNS_SOCKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Variables.h" 16 | #include "Request.h" 17 | #include "Response.h" 18 | #include "Server.h" 19 | 20 | namespace MNS { 21 | // Forward declaration 22 | class Server; 23 | // Forward declaration 24 | class Request; 25 | // Forward declaration 26 | class Response; 27 | 28 | /// Type of the socket (LISTENING - Listening socket, PEER - Client socket) 29 | enum SOCKET_TYPE { 30 | LISTENING, 31 | PEER 32 | }; 33 | 34 | /// SocketData class. Used as a glue of server - request - response 35 | class SocketData { 36 | public: 37 | /// Fd of the socket 38 | int fd; 39 | 40 | /// Type of the socket 41 | SOCKET_TYPE type; 42 | 43 | /// Request object of the socket 44 | MNS::Request *request; 45 | 46 | /// Response object of the socket 47 | MNS::Response *response; 48 | 49 | /// Reference to Server 50 | MNS::Server *server; 51 | 52 | /// Poll handle of the socket 53 | uv_poll_t *poll_h; 54 | 55 | /// Request Persistent placeholder 56 | void *nodeRequestPlaceholder; 57 | 58 | /// Response Persistent placeholder 59 | void *nodeResponsePlaceholder; 60 | 61 | /** 62 | * Default contructor 63 | * @param poll_h - Poll handle 64 | * @param fd - File descriptor of the socket 65 | * @param type - Type of the socket 66 | * @param server - Server reference 67 | */ 68 | SocketData(uv_poll_t *poll_h, int fd, SOCKET_TYPE type, MNS::Server *server = NULL); 69 | 70 | /// Default destructor 71 | ~SocketData(); 72 | }; 73 | 74 | /** 75 | * Helper socket class 76 | */ 77 | class Socket { 78 | public: 79 | /** 80 | * Create a listening socket 81 | * @param port - Port number to listen on 82 | * @return Fd of the listening socket. -1 On error 83 | */ 84 | static int createListening(int port); 85 | 86 | /** 87 | * Flags socket as NON_BLOCKING 88 | * @param sfd - File descriptor of socket 89 | * @return 0 on sucess, -1 on error 90 | */ 91 | static int makeNonBlocking(int sfd); 92 | }; 93 | } 94 | #endif //MNS_SOCKET_H 95 | -------------------------------------------------------------------------------- /src/Variables.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by helidium on 3/24/17. 3 | // 4 | 5 | #ifndef MNS_VARIABLES_H 6 | #define MNS_VARIABLES_H 7 | 8 | #include 9 | #include 10 | 11 | namespace MNS { 12 | /// HTTP_VERSION 13 | enum HTTP_VERSION { 14 | UNKNOWN_VERSION, 15 | HTTP_1_0, 16 | HTTP_1_1 17 | }; 18 | 19 | /// HTTP_METHOD 20 | enum HTTP_METHOD { 21 | UNKNOWN_METHOD, 22 | OPTIONS, 23 | GET, 24 | HEAD, 25 | POST, 26 | PUT, 27 | DELETE, 28 | TRACE, 29 | CONNECT, 30 | PATCH 31 | }; 32 | } 33 | 34 | #endif //MNS_VARIABLES_H 35 | --------------------------------------------------------------------------------