├── .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 |
5 |
6 |
7 | Lightweight, high performance NodeJS Server.
8 |
9 | [](https://travis-ci.org/Helidium/Mitol)
10 | [](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 | 
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 |
--------------------------------------------------------------------------------