├── layout.txt ├── unit ├── conanfile.txt ├── dummy.cpp └── CMakeLists.txt ├── integration └── simple │ ├── conanfile.txt │ ├── CMakeLists.txt │ └── service.cpp ├── include └── mana │ ├── dashboard │ ├── components │ └── dashboard │ │ ├── dashboard │ │ ├── component.hpp │ │ ├── common.hpp │ │ ├── components │ │ ├── logger.hpp │ │ ├── cpusage.hpp │ │ ├── memmap.hpp │ │ ├── statman.hpp │ │ ├── status.hpp │ │ ├── stacksampler.hpp │ │ └── tcp.hpp │ │ └── dashboard.hpp │ ├── bufferwrapper.hpp │ ├── middleware.hpp │ ├── attribute.hpp │ ├── middleware │ ├── parsley.hpp │ ├── cookie_parser.hpp │ ├── butler.hpp │ └── director.hpp │ ├── route.hpp │ ├── attributes │ ├── json.hpp │ └── cookie_jar.hpp │ ├── params.hpp │ ├── server.hpp │ ├── request.hpp │ ├── response.hpp │ └── router.hpp ├── src ├── request.cpp ├── components │ └── dashboard │ │ └── dashboard.cpp ├── middleware │ ├── parsley.cpp │ ├── cookie_parser.cpp │ ├── butler.cpp │ └── director.cpp ├── server.cpp ├── response.cpp └── attributes │ └── cookie_jar.cpp ├── conanfile.py ├── CMakeLists.txt ├── cookie.md ├── Jenkinsfile └── README.md /layout.txt: -------------------------------------------------------------------------------- 1 | [includedirs] 2 | include 3 | 4 | [libdirs] 5 | build/lib 6 | -------------------------------------------------------------------------------- /unit/conanfile.txt: -------------------------------------------------------------------------------- 1 | [build_requires] 2 | lest/[>=1.33.5]@includeos/stable 3 | -------------------------------------------------------------------------------- /integration/simple/conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | mana/[>=0.14.0,include_prerelease=True]@includeos/latest 3 | 4 | [build_requires] 5 | vmbuild/[>=0.14.0,include_prerelease=True]@includeos/latest 6 | 7 | [generators] 8 | virtualenv 9 | cmake 10 | -------------------------------------------------------------------------------- /integration/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8.0) 2 | 3 | project (mana_simple C CXX) 4 | 5 | include(${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake OPTIONAL RESULT_VARIABLE HAS_CONAN) 6 | if (NOT HAS_CONAN) 7 | message(FATAL_ERROR "missing conanbuildinfo.cmake did you forget to run conan install ?") 8 | endif() 9 | conan_basic_setup() 10 | 11 | include(os) 12 | 13 | os_add_executable(mana_simple "Mana Simple Example" service.cpp) 14 | os_add_drivers(mana_simple virtionet) 15 | -------------------------------------------------------------------------------- /include/mana/dashboard: -------------------------------------------------------------------------------- 1 | // -*-C++-*- 2 | // This file is a part of the IncludeOS unikernel - www.includeos.org 3 | // 4 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 5 | // and Alfred Bratterud 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #ifndef MANA_DASHBOARD_API 20 | #define MANA_DASHBOARD_API 21 | 22 | #include "components/dashboard/dashboard" 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/request.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | using namespace mana; 21 | 22 | Request::Request(http::Request_ptr req) 23 | : req_{std::move(req)}, 24 | attributes_{}, 25 | params_{} 26 | { 27 | } 28 | 29 | Request::Request(http::Request&& req) 30 | : Request(std::make_unique(std::forward(req))) 31 | { 32 | } 33 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/dashboard: -------------------------------------------------------------------------------- 1 | // -*-C++-*- 2 | // This file is a part of the IncludeOS unikernel - www.includeos.org 3 | // 4 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 5 | // and Alfred Bratterud 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #ifndef __DASHBOARD__ 20 | #define __DASHBOARD__ 21 | 22 | #include "dashboard.hpp" 23 | 24 | #include "components/memmap.hpp" 25 | #include "components/stacksampler.hpp" 26 | #include "components/statman.hpp" 27 | #include "components/status.hpp" 28 | #include "components/tcp.hpp" 29 | #include "components/cpusage.hpp" 30 | #include "components/logger.hpp" 31 | 32 | #endif //< __DASHBOARD__ 33 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/component.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENT_HPP 20 | #define DASHBOARD_COMPONENT_HPP 21 | 22 | #include "common.hpp" 23 | 24 | namespace mana { 25 | namespace dashboard { 26 | 27 | class Component { 28 | 29 | public: 30 | 31 | virtual std::string key() const = 0; 32 | 33 | virtual void serialize(dashboard::Writer&) = 0; 34 | 35 | virtual ~Component() {} 36 | 37 | }; 38 | 39 | }} //< namespace mana::dashboard 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /unit/dummy.cpp: -------------------------------------------------------------------------------- 1 | #include "lest/lest.hpp" 2 | 3 | using namespace std; 4 | 5 | const lest::test specification[] = 6 | { 7 | CASE( "Empty string has length zero (succeed)" ) 8 | { 9 | EXPECT( 0 == string( ).length() ); 10 | EXPECT( 0 == string("").length() ); 11 | }, 12 | /* 13 | CASE( "Text compares lexically (fail)" ) 14 | { 15 | EXPECT( string("hello") > string("world") ); 16 | }, 17 | 18 | CASE( "Unexpected exception is reported" ) 19 | { 20 | EXPECT( (throw std::runtime_error("surprise!"), true) ); 21 | }, 22 | */ 23 | CASE( "Unspecified expected exception is captured" ) 24 | { 25 | EXPECT_THROWS( throw std::runtime_error("surprise!") ); 26 | }, 27 | 28 | CASE( "Specified expected exception is captured" ) 29 | { 30 | EXPECT_THROWS_AS( throw std::bad_alloc(), std::bad_alloc ); 31 | }, 32 | /* 33 | CASE( "Expected exception is reported missing" ) 34 | { 35 | EXPECT_THROWS( true ); 36 | }, 37 | 38 | CASE( "Specific expected exception is reported missing" ) 39 | { 40 | EXPECT_THROWS_AS( true, std::runtime_error ); 41 | },*/ 42 | }; 43 | 44 | int main( int argc, char * argv[] ) 45 | { 46 | return lest::run( specification, argc, argv ); 47 | } 48 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/common.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMMON_HPP 20 | #define DASHBOARD_COMMON_HPP 21 | 22 | #include 23 | #include // rapidjson 24 | #include 25 | #include 26 | 27 | namespace mana { 28 | namespace dashboard { 29 | using WriteBuffer = rapidjson::StringBuffer; 30 | using Writer = rapidjson::Writer; 31 | using Serialize = delegate; 32 | using RouteCallback = delegate; 33 | } 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/mana/bufferwrapper.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef SERVER_BUFFER_HPP 19 | #define SERVER_BUFFER_HPP 20 | 21 | namespace mana { 22 | 23 | /** 24 | * 25 | */ 26 | template 27 | class BufferWrapper { 28 | private: 29 | using ptr_t = PTR; 30 | 31 | public: 32 | /** 33 | * 34 | */ 35 | BufferWrapper(ptr_t ptr, const size_t sz) 36 | : data_ {ptr} 37 | , size_ {sz} 38 | {} 39 | 40 | /** 41 | * 42 | */ 43 | const ptr_t begin() 44 | { return data_; } 45 | 46 | /** 47 | * 48 | */ 49 | const ptr_t end() 50 | { return data_ + size_; } 51 | 52 | private: 53 | ptr_t data_; 54 | const size_t size_; 55 | }; //< class BufferWrapper 56 | 57 | } //< namespace mana 58 | 59 | #endif //< SERVER_BUFFER_HPP 60 | -------------------------------------------------------------------------------- /include/mana/middleware.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_MIDDLEWARE_HPP 19 | #define MANA_MIDDLEWARE_HPP 20 | 21 | #include "request.hpp" 22 | #include "response.hpp" 23 | 24 | 25 | namespace mana { 26 | 27 | class Middleware; 28 | using Middleware_ptr = std::shared_ptr; 29 | 30 | using next_t = delegate; 31 | using Next = std::shared_ptr; 32 | using Callback = delegate; 33 | 34 | class Middleware { 35 | public: 36 | 37 | virtual Callback handler() = 0; 38 | 39 | virtual void on_mount(const std::string& path) 40 | { mountpath_ = path; } 41 | 42 | virtual ~Middleware() {} 43 | 44 | protected: 45 | std::string mountpath_; 46 | 47 | }; 48 | 49 | }; // << namespace mana 50 | 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/mana/attribute.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_ATTRIBUTE_HPP 19 | #define MANA_ATTRIBUTE_HPP 20 | 21 | #include 22 | 23 | namespace mana { 24 | 25 | class Attribute; 26 | using Attribute_ptr = std::shared_ptr; 27 | using AttrType = size_t; 28 | 29 | class Attribute { 30 | 31 | public: 32 | template 33 | static AttrType type(); 34 | 35 | virtual ~Attribute() {} 36 | 37 | private: 38 | static AttrType next_attr_type() { 39 | static AttrType counter; 40 | return ++counter; 41 | } 42 | }; 43 | 44 | template 45 | AttrType Attribute::type() { 46 | static_assert(std::is_base_of::value, "A is not an Attribute"); 47 | static AttrType id = Attribute::next_attr_type(); 48 | return id; 49 | } 50 | 51 | 52 | }; // < namespace mana 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/mana/middleware/parsley.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_MIDDLEWARE_PARSLEY_HPP 19 | #define MANA_MIDDLEWARE_PARSLEY_HPP 20 | 21 | #include 22 | #include 23 | 24 | namespace mana { 25 | namespace middleware { 26 | 27 | /** 28 | * @brief A vegan way to parse JSON Content in a response 29 | * @details TBC.. 30 | * 31 | */ 32 | class Parsley : public Middleware { 33 | public: 34 | 35 | Callback handler() override { 36 | return {this, &Parsley::process}; 37 | } 38 | /** 39 | * 40 | */ 41 | void process(mana::Request_ptr req, mana::Response_ptr, mana::Next next); 42 | 43 | private: 44 | /** 45 | * 46 | */ 47 | bool has_json(const mana::Request& req) const; 48 | }; //< class Parsley 49 | 50 | }} //< namespace mana::middleware 51 | 52 | #endif //< JSON_PARSLEY_HPP 53 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, python_requires, CMake 2 | 3 | conan_tools = python_requires("conan-tools/[>=1.0.0]@includeos/stable") 4 | 5 | class ManaConan(ConanFile): 6 | settings= "os","arch","build_type","compiler" 7 | name = "mana" 8 | version = conan_tools.git_get_semver() 9 | license = 'Apache-2.0' 10 | description = 'Run your application with zero overhead' 11 | generators = 'cmake' 12 | url = "http://www.includeos.org/" 13 | scm = { 14 | "type" : "git", 15 | "url" : "auto", 16 | "subfolder": ".", 17 | "revision" : "auto" 18 | } 19 | 20 | no_copy_source=True 21 | default_user="includeos" 22 | default_channel="latest" 23 | 24 | def requirements(self): 25 | self.requires("includeos/[>=0.14.0,include_prerelease=True]@{}/{}".format(self.user,self.channel)) 26 | 27 | def _arch(self): 28 | return { 29 | "x86":"i686", 30 | "x86_64":"x86_64", 31 | "armv8" : "aarch64" 32 | }.get(str(self.settings.arch)) 33 | 34 | def _cmake_configure(self): 35 | cmake = CMake(self) 36 | cmake.definitions['ARCH']=self._arch() 37 | cmake.configure(source_folder=self.source_folder) 38 | return cmake 39 | 40 | def build(self): 41 | cmake = self._cmake_configure() 42 | cmake.build() 43 | 44 | def package(self): 45 | cmake = self._cmake_configure() 46 | cmake.install() 47 | 48 | def package_info(self): 49 | self.cpp_info.libs=['mana'] 50 | 51 | def deploy(self): 52 | self.copy("*.a",dst="lib",src="lib") 53 | self.copy("*",dst="include",src="include") 54 | -------------------------------------------------------------------------------- /include/mana/route.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_ROUTE_HPP 19 | #define MANA_ROUTE_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "request.hpp" 26 | #include "response.hpp" 27 | #include 28 | 29 | namespace mana { 30 | 31 | using End_point = delegate; 32 | using Route_expr = std::regex; 33 | 34 | struct Route { 35 | Route(const std::string& ex, End_point e) 36 | : path{ex} 37 | , end_point{e} 38 | { 39 | expr = path2regex::path_to_regex(path, keys); 40 | } 41 | 42 | std::string path; 43 | Route_expr expr; 44 | End_point end_point; 45 | path2regex::Keys keys; 46 | size_t hits {0U}; 47 | }; //< struct Route 48 | 49 | inline bool operator < (const Route& lhs, const Route& rhs) noexcept { 50 | return rhs.hits < lhs.hits; 51 | } 52 | 53 | } //< namespace mana 54 | 55 | #endif //< MANA_ROUTE_HPP 56 | -------------------------------------------------------------------------------- /integration/simple/service.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace mana; 23 | using namespace std::string_literals; 24 | 25 | std::unique_ptr server; 26 | 27 | void Service::start(const std::string&) 28 | { 29 | // Setup stack; try DHCP 30 | auto& stack = net::Interfaces::get(0); 31 | // Static config 32 | stack.network_config({ 10,0,0,42 }, // IP 33 | { 255,255,255,0 }, // Netmask 34 | { 10,0,0,1 }, // Gateway 35 | { 8,8,8,8 }); // DNS 36 | 37 | // Create a router 38 | Router router; 39 | // Setup a route on GET / 40 | router.on_get("/", [](auto, auto res) { 41 | res->source().add_body("

Simple example

"s); 42 | res->send(); 43 | }); 44 | 45 | // Create and setup the server 46 | server = std::make_unique(stack.tcp()); 47 | server->set_routes(router).listen(80); 48 | } 49 | -------------------------------------------------------------------------------- /src/components/dashboard/dashboard.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | namespace mana { 21 | namespace dashboard { 22 | 23 | Dashboard::Dashboard(size_t buffer_capacity) 24 | : router_(), buffer_(0, buffer_capacity), writer_(buffer_) 25 | { 26 | setup_routes(); 27 | } 28 | 29 | void Dashboard::setup_routes() { 30 | router_.on_get("/", {this, &Dashboard::serve}); 31 | } 32 | 33 | void Dashboard::serve(Request_ptr, Response_ptr res) { 34 | writer_.StartObject(); 35 | 36 | for(auto& pair : components_) 37 | { 38 | auto& key = pair.first; 39 | auto& comp = *(pair.second); 40 | 41 | writer_.Key(key.c_str()); 42 | 43 | comp.serialize(writer_); 44 | } 45 | 46 | writer_.EndObject(); 47 | send_buffer(*res); 48 | } 49 | 50 | void Dashboard::send_buffer(Response& res) { 51 | res.send_json(buffer_.GetString()); 52 | reset_writer(); 53 | } 54 | 55 | void Dashboard::reset_writer() { 56 | buffer_.Clear(); 57 | writer_.Reset(buffer_); 58 | } 59 | 60 | }} //< namespace mana::dashboard 61 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/logger.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_LOGGER_HPP 20 | #define DASHBOARD_COMPONENTS_LOGGER_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | 26 | namespace mana { 27 | namespace dashboard { 28 | 29 | class Logger : public Component { 30 | 31 | public: 32 | 33 | Logger(::Logger& logger, size_t entries = 50) 34 | : logger_{logger}, entries_{entries} 35 | {} 36 | 37 | std::string key() const override 38 | { return "logger"; } 39 | 40 | void serialize(Writer& writer) override { 41 | writer.StartArray(); 42 | auto entries = (entries_) ? logger_.entries(entries_) : logger_.entries(); 43 | 44 | auto it = entries.begin(); 45 | 46 | while(it != entries.end()) 47 | writer.String(*it++); 48 | 49 | writer.EndArray(); 50 | } 51 | 52 | private: 53 | const ::Logger& logger_; 54 | const size_t entries_; 55 | 56 | }; 57 | 58 | } // < namespace dashboard 59 | } // < namespace mana 60 | 61 | #endif 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /include/mana/attributes/json.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_ATTRIBUTES_JSON_HPP 19 | #define MANA_ATTRIBUTES_JSON_HPP 20 | 21 | #ifndef RAPIDJSON_HAS_STDSTRING 22 | #define RAPIDJSON_HAS_STDSTRING 1 23 | #endif 24 | 25 | #ifndef RAPIDJSON_THROWPARSEEXCEPTION 26 | #define RAPIDJSON_THROWPARSEEXCEPTION 1 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | namespace mana { 35 | 36 | struct Serializable { 37 | virtual void serialize(rapidjson::Writer& writer) const = 0; 38 | 39 | virtual bool deserialize(const rapidjson::Document& doc) = 0; 40 | }; //< struct Serializable 41 | 42 | namespace attribute { 43 | 44 | class Json_doc : public mana::Attribute { 45 | public: 46 | 47 | rapidjson::Document& doc() 48 | { return document_; } 49 | 50 | private: 51 | rapidjson::Document document_; 52 | 53 | }; //< class Json_doc 54 | 55 | } //< namespace attribute 56 | } //< namespace mana 57 | 58 | #endif //< JSON_JSON_HPP 59 | -------------------------------------------------------------------------------- /include/mana/params.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_PARAMS_HPP 19 | #define MANA_PARAMS_HPP 20 | 21 | #include 22 | #include 23 | 24 | namespace mana { 25 | 26 | class ParamException : public std::exception { 27 | std::string msg; 28 | ParamException(){} 29 | 30 | public: 31 | ParamException(const std::string& s) throw() : msg{s} {} 32 | const char* what() const throw() { return msg.c_str(); } 33 | }; // < class ParamException 34 | 35 | /** 36 | * @brief Class for Request parameters. 37 | */ 38 | class Params { 39 | public: 40 | bool insert(const std::string& name, const std::string& value) { 41 | auto ret = parameters.emplace(std::make_pair(name, value)); 42 | return ret.second; 43 | } 44 | 45 | const std::string& get(const std::string& name) const { 46 | auto it = parameters.find(name); 47 | 48 | if (it != parameters.end()) // if found 49 | return it->second; 50 | 51 | throw ParamException{"Parameter with name " + name + " doesn't exist!"}; 52 | } 53 | 54 | private: 55 | std::map parameters; 56 | }; // < class Params 57 | 58 | }; // < namespace mana 59 | 60 | #endif // < MANA_PARAMS_HPP 61 | -------------------------------------------------------------------------------- /src/middleware/parsley.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | 19 | #include 20 | #include 21 | 22 | namespace mana { 23 | namespace middleware { 24 | 25 | void Parsley::process(mana::Request_ptr req, mana::Response_ptr, mana::Next next) { 26 | 27 | // Request doesn't have JSON attribute 28 | if(has_json(*req) and not req->has_attribute()) 29 | { 30 | // Create attribute 31 | auto json = std::make_shared(); 32 | 33 | // Access the document and parse the body 34 | bool err = json->doc().Parse(req->source().body().data()).HasParseError(); 35 | #ifdef VERBOSE_WEBSERVER 36 | printf(" Parsed JSON data.\n"); 37 | #endif 38 | 39 | if(not err) 40 | req->set_attribute(std::move(json)); 41 | else 42 | printf(" Parsing error\n"); 43 | } 44 | 45 | return (*next)(); 46 | } 47 | 48 | bool Parsley::has_json(const mana::Request& req) const { 49 | auto c_type = http::header::Content_Type; 50 | if(not req.header().has_field(c_type)) return false; 51 | return (req.header().value(c_type).find("application/json") != std::string::npos); 52 | } 53 | 54 | }} // < namespace mana::middleware 55 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/cpusage.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_CPUSAGE_HPP 20 | #define DASHBOARD_COMPONENTS_CPUSAGE_HPP 21 | 22 | #include "../component.hpp" 23 | #include 24 | #include 25 | #include 26 | 27 | namespace mana { 28 | namespace dashboard { 29 | 30 | class CPUsage : public Component { 31 | public: 32 | CPUsage() = default; 33 | ~CPUsage() = default; 34 | 35 | std::string key() const override 36 | { return "cpu_usage"; } 37 | 38 | void serialize(Writer& writer) override 39 | { 40 | uint64_t tdiff = ::StackSampler::samples_total() - last_total; 41 | last_total = ::StackSampler::samples_total(); 42 | uint64_t adiff = ::StackSampler::samples_asleep() - last_asleep; 43 | last_asleep = ::StackSampler::samples_asleep(); 44 | 45 | double asleep = 1.0; 46 | if (tdiff > 0) asleep = adiff / (double) tdiff; 47 | 48 | writer.StartObject(); 49 | writer.Key("idle"); 50 | writer.Uint64(asleep * 100.0); 51 | 52 | writer.Key("active"); 53 | writer.Uint64((1.0 - asleep) * 100.0); 54 | writer.EndObject(); 55 | } 56 | 57 | private: 58 | uint64_t last_asleep = 0; 59 | uint64_t last_total = 0; 60 | }; 61 | 62 | } // < namespace dashboard 63 | } // < namespace mana 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/middleware/cookie_parser.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | namespace mana { 21 | namespace middleware { 22 | 23 | const std::regex Cookie_parser::cookie_pattern_ {"[^;]+"}; 24 | 25 | void Cookie_parser::process(mana::Request_ptr req, mana::Response_ptr, mana::Next next) { 26 | if(has_cookie(req)) { 27 | parse(read_cookies(req)); 28 | auto jar_attr = std::make_shared(req_cookies_); 29 | req->set_attribute(jar_attr); 30 | } 31 | 32 | return (*next)(); 33 | } 34 | 35 | void Cookie_parser::parse(const std::string& cookie_data) { 36 | if(cookie_data.empty()) { 37 | throw http::CookieException{"Cannot parse empty cookie-string!"}; 38 | } 39 | 40 | req_cookies_.clear(); //< Clear {req_cookies_} for new entries 41 | 42 | auto position = std::sregex_iterator(cookie_data.begin(), cookie_data.end(), cookie_pattern_); 43 | auto end = std::sregex_iterator(); 44 | 45 | while (position not_eq end) { 46 | auto cookie = (*position++).str(); 47 | 48 | cookie.erase(std::remove(cookie.begin(), cookie.end(), ' '), cookie.end()); 49 | 50 | auto pos = cookie.find('='); 51 | if (pos not_eq std::string::npos) { 52 | req_cookies_.insert(cookie.substr(0, pos), cookie.substr(pos + 1)); 53 | } else { 54 | req_cookies_.insert(cookie); 55 | } 56 | } 57 | } 58 | 59 | }} //< mana::middleware 60 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/memmap.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_MEMMAP_HPP 20 | #define DASHBOARD_COMPONENTS_MEMMAP_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | 26 | namespace mana { 27 | namespace dashboard { 28 | 29 | class Memmap : public Component { 30 | 31 | public: 32 | 33 | static std::shared_ptr instance() { 34 | static std::weak_ptr instance_; 35 | if(auto p = instance_.lock()) 36 | return p; 37 | 38 | std::shared_ptr p{new Memmap}; 39 | instance_ = p; 40 | return p; 41 | } 42 | 43 | std::string key() const override 44 | { return "memmap"; } 45 | 46 | void serialize(Writer& writer) override { 47 | writer.StartArray(); 48 | for (auto&& i : os::mem::vmmap()) 49 | { 50 | auto& entry = i.second; 51 | writer.StartObject(); 52 | 53 | writer.Key("name"); 54 | writer.String(entry.name()); 55 | 56 | writer.Key("addr_start"); 57 | writer.Uint(entry.addr_start()); 58 | 59 | writer.Key("addr_end"); 60 | writer.Uint(entry.addr_end()); 61 | 62 | writer.Key("in_use"); 63 | writer.Uint(entry.bytes_in_use()); 64 | 65 | writer.EndObject(); 66 | } 67 | writer.EndArray(); 68 | } 69 | 70 | private: 71 | Memmap() {} 72 | 73 | }; 74 | 75 | } // < namespace dashboard 76 | } // < namespace mana 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | project(unittests C CXX) 4 | 5 | enable_testing(ON) 6 | 7 | set(CMAKE_BUILD_TYPE Debug) 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | set(CMAKE_CXX_EXTENSIONS OFF) 11 | 12 | option(COVERAGE "Build with coverage generation" ON) 13 | option(BUILD_MISSING "Build missing conan dependencies" OFF) 14 | option(UPDATE "Force conan to search repo for newer packages" OFF) 15 | 16 | if ("${ARCH}" STREQUAL "") 17 | message(STATUS "CMake detected host arch: ${CMAKE_HOST_SYSTEM_PROCESSOR}") 18 | set (ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR}) 19 | endif("${ARCH}" STREQUAL "") 20 | 21 | #can we get rid of these ? 22 | add_definitions(-DARCH_${ARCH}) 23 | add_definitions(-DARCH="${ARCH}") 24 | 25 | if(NOT CONAN_EXPORTED) 26 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 27 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 28 | file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/develop/conan.cmake" 29 | "${CMAKE_BINARY_DIR}/conan.cmake") 30 | endif() 31 | include(${CMAKE_BINARY_DIR}/conan.cmake) 32 | if (CONAN_PROFILE) 33 | set(CONANPROFILE PROFILE ${CONAN_PROFILE}) 34 | endif() 35 | if (UPDATE) 36 | set(CONAN_UPDATE UPDATE) 37 | endif() 38 | 39 | if (BUILD_MISSING) 40 | set(CONAN_BUILD "BUILD" "missing") 41 | endif() 42 | #not sure this is sane with respect to dependencies on includeos headers 43 | conan_cmake_run( 44 | CONANFILE conanfile.txt 45 | BASIC_SETUP 46 | ${CONAN_UPDATE} 47 | ${CONAN_BUILD} 48 | ${CONANPROFILE} 49 | ) 50 | endif() 51 | 52 | #include_directories(../include) 53 | 54 | #set(MANA_SOURCES 55 | # ../src/middleware/butler.cpp 56 | #) 57 | 58 | #add_library(mana ${MANA_SOURCES}) 59 | 60 | 61 | SET(TEST_SOURCES 62 | dummy.cpp 63 | ) 64 | 65 | foreach(T ${TEST_SOURCES}) 66 | 67 | #get the filename witout extension 68 | get_filename_component(NAME ${T} NAME_WE) 69 | add_executable(${NAME} ${T}) 70 | target_link_libraries(${NAME} m stdc++) 71 | add_test(${NAME} bin/${NAME} -p ) 72 | 73 | #add to list of tests for dependencies 74 | list(APPEND TEST_BINARIES ${NAME}) 75 | endforeach() 76 | -------------------------------------------------------------------------------- /include/mana/middleware/cookie_parser.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_MIDDLEWARE_COOKIE_PARSER_HPP 19 | #define MANA_MIDDLEWARE_COOKIE_PARSER_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace mana { 26 | namespace middleware { 27 | 28 | /** 29 | * @brief A way to parse cookies that the browser is sending to the server 30 | */ 31 | class Cookie_parser : public Middleware { 32 | public: 33 | 34 | Callback handler() override { 35 | return {this, &Cookie_parser::process}; 36 | } 37 | 38 | void process(Request_ptr req, Response_ptr res, Next next); 39 | 40 | private: 41 | attribute::Cookie_jar req_cookies_; 42 | 43 | static const std::regex cookie_pattern_; 44 | 45 | bool has_cookie(mana::Request_ptr req) const noexcept; 46 | 47 | std::string read_cookies(mana::Request_ptr req) const noexcept; 48 | 49 | void parse(const std::string& cookie_data); 50 | 51 | }; // < class CookieParser 52 | 53 | /**--v----------- Implementation Details -----------v--**/ 54 | 55 | inline bool Cookie_parser::has_cookie(mana::Request_ptr req) const noexcept { 56 | return req->header().has_field(http::header::Cookie); 57 | } 58 | 59 | inline std::string Cookie_parser::read_cookies(mana::Request_ptr req) const noexcept { 60 | return std::string(req->header().value(http::header::Cookie)); 61 | } 62 | 63 | /**--^----------- Implementation Details -----------^--**/ 64 | 65 | }} //< namespace mana::middleware 66 | 67 | #endif //< MIDDLEWARE_COOKIE_PARSER_HPP 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | #TODO bump to a newer version that works with the below as minimum 3 | 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | 9 | #get arch the proper cmake way 10 | if (NOT DEFINED ARCH) 11 | set(ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR}) 12 | endif() 13 | 14 | add_definitions(-DARCH_${ARCH}) 15 | add_definitions(-DARCH="${ARCH}") 16 | 17 | #to run outside conan 18 | # the easy way 19 | # mkdir build && cd build 20 | # conan link .. mana/@/ --layout=../layout.txt 21 | # conan install .. --no-imports 22 | # cmake .. 23 | # make 24 | # hard way 25 | # create your own layout file and build out of tree in a different location 26 | # endif 27 | 28 | if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake) 29 | include(${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake) 30 | conan_basic_setup() 31 | else() 32 | if (NOT CMAKE_BUILD_TYPE) 33 | set(CMAKE_BUILD_TYPE Release) 34 | endif() 35 | if (CONAN_PROFILE) 36 | set(CONANPROFILE PROFILE ${CONAN_PROFILE}) 37 | endif() 38 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 39 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 40 | file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.13/conan.cmake" 41 | "${CMAKE_BINARY_DIR}/conan.cmake") 42 | endif() 43 | include(${CMAKE_BINARY_DIR}/conan.cmake) 44 | conan_cmake_run( 45 | CONANFILE conanfile.py 46 | BASIC_SETUP 47 | ${CONANPROFILE} 48 | ) 49 | endif() 50 | 51 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 52 | 53 | set(MANA_OBJ 54 | src/request.cpp 55 | src/response.cpp 56 | src/server.cpp 57 | ) 58 | 59 | set(MANA_COMP 60 | src/components/dashboard/dashboard.cpp 61 | ) 62 | 63 | set(MANA_MWARE 64 | src/middleware/butler.cpp 65 | src/middleware/director.cpp 66 | src/middleware/parsley.cpp 67 | src/middleware/cookie_parser.cpp 68 | ) 69 | 70 | set(MANA_ATTR 71 | src/attributes/cookie_jar.cpp 72 | ) 73 | 74 | add_library(mana STATIC ${MANA_OBJ} ${MANA_COMP} ${MANA_MWARE} ${MANA_ATTR}) 75 | 76 | install(TARGETS mana DESTINATION ${LIB_PREFIX}lib) 77 | install(DIRECTORY include/mana DESTINATION ${INCLUDE_PREFIX}include) 78 | -------------------------------------------------------------------------------- /include/mana/attributes/cookie_jar.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_ATTRIBUTES_COOKIE_JAR_HPP 19 | #define MANA_ATTRIBUTES_COOKIE_JAR_HPP 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | namespace mana { 28 | namespace attribute { 29 | 30 | class Cookie_jar : public mana::Attribute { 31 | public: 32 | 33 | explicit Cookie_jar() = default; 34 | 35 | Cookie_jar(const Cookie_jar&) = default; 36 | 37 | Cookie_jar(Cookie_jar&&) = default; 38 | 39 | Cookie_jar& operator = (Cookie_jar&&) = default; 40 | 41 | ~Cookie_jar() = default; 42 | 43 | size_t size() const noexcept; 44 | 45 | bool empty() const noexcept; 46 | 47 | bool insert(const http::Cookie& c) noexcept; 48 | 49 | bool insert(const std::string& name, const std::string& value = ""); 50 | 51 | Cookie_jar& erase(const http::Cookie& c) noexcept; 52 | 53 | Cookie_jar& erase(const std::string& name) noexcept; 54 | 55 | Cookie_jar& clear() noexcept; 56 | 57 | bool exists(const std::string& name) const noexcept; 58 | 59 | const std::string& cookie_value(const std::string& name) const noexcept; 60 | 61 | const std::map& get_cookies() const noexcept; 62 | 63 | std::map::const_iterator begin() const noexcept; 64 | 65 | std::map::const_iterator end() const noexcept; 66 | 67 | private: 68 | std::map cookies_; 69 | 70 | Cookie_jar& operator = (const Cookie_jar&) = delete; 71 | }; //< class CookieJar 72 | 73 | }} //< namespace mana::attribute 74 | 75 | #endif //< COOKIE_JAR_HPP 76 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/statman.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_STATMAN_HPP 20 | #define DASHBOARD_COMPONENTS_STATMAN_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | 26 | namespace mana { 27 | namespace dashboard { 28 | 29 | class Statman : public Component { 30 | 31 | public: 32 | 33 | Statman(::Statman& statman) 34 | : statman_{statman} 35 | {} 36 | 37 | std::string key() const override 38 | { return "statman"; } 39 | 40 | void serialize(Writer& writer) override { 41 | writer.StartArray(); 42 | for(auto it = statman_.begin(); it != statman_.end(); ++it) { 43 | auto& stat = *it; 44 | writer.StartObject(); 45 | 46 | writer.Key("name"); 47 | writer.String(stat.name()); 48 | 49 | writer.Key("value"); 50 | const std::string type = [&](){ 51 | switch(stat.type()) { 52 | case Stat::UINT64: writer.Uint64(stat.get_uint64()); 53 | return "UINT64"; 54 | case Stat::UINT32: writer.Uint(stat.get_uint32()); 55 | return "UINT32"; 56 | case Stat::FLOAT: writer.Double(stat.get_float()); 57 | return "FLOAT"; 58 | } 59 | }(); 60 | 61 | writer.Key("type"); 62 | writer.String(type); 63 | 64 | writer.Key("index"); 65 | writer.Int(std::distance(statman_.begin(), it)); 66 | 67 | writer.EndObject(); 68 | } 69 | 70 | writer.EndArray(); 71 | } 72 | 73 | private: 74 | ::Statman& statman_; 75 | 76 | }; 77 | 78 | } // < namespace dashboard 79 | } // < namespace mana 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/status.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_STATUS_HPP 20 | #define DASHBOARD_COMPONENTS_STATUS_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | #include 26 | 27 | namespace mana { 28 | namespace dashboard { 29 | 30 | class Status : public Component { 31 | 32 | public: 33 | 34 | static std::shared_ptr instance() { 35 | static std::weak_ptr instance_; 36 | if(auto p = instance_.lock()) 37 | return p; 38 | 39 | std::shared_ptr p{new Status}; 40 | instance_ = p; 41 | return p; 42 | } 43 | 44 | std::string key() const override 45 | { return "status"; } 46 | 47 | void serialize(Writer& writer) override { 48 | writer.StartObject(); 49 | 50 | writer.Key("version"); 51 | writer.String(os::version()); 52 | 53 | writer.Key("service"); 54 | writer.String(Service::name()); 55 | 56 | writer.Key("heap_usage"); 57 | writer.Uint64(os::total_memuse()); 58 | 59 | writer.Key("cpu_freq"); 60 | writer.Double(os::cpu_freq().count()); 61 | 62 | writer.Key("boot_time"); 63 | long hest = os::boot_timestamp(); 64 | struct tm* tt = 65 | gmtime (&hest); 66 | char datebuf[32]; 67 | strftime(datebuf, sizeof datebuf, "%FT%TZ", tt); 68 | writer.String(datebuf); 69 | 70 | writer.Key("current_time"); 71 | hest = RTC::now(); 72 | tt = 73 | gmtime (&hest); 74 | strftime(datebuf, sizeof datebuf, "%FT%TZ", tt); 75 | writer.String(datebuf); 76 | 77 | writer.EndObject(); 78 | } 79 | 80 | private: 81 | 82 | Status() {}; 83 | 84 | }; 85 | 86 | } // < namespace dashboard 87 | } // < namespace mana 88 | 89 | #endif 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /cookie.md: -------------------------------------------------------------------------------- 1 | # cookie 2 | Cookie support for [Mana](https://github.com/includeos/mana). Following [RFC 6265](https://tools.ietf.org/html/rfc6265). 3 | 4 | **Example service:** [Acorn Web Server Appliance](https://github.com/includeos/acorn) with its [routes that use cookies](https://github.com/includeos/acorn/blob/master/app/routes/languages.hpp). 5 | 6 | ## Features 7 | * Create, update and clear cookies 8 | * Easy access to an incoming request's existing cookies 9 | 10 | ## Usage 11 | **Create** a cookie with a name and value on the [response](https://github.com/includeos/mana/blob/master/include/mana/response.hpp): 12 | ```cpp 13 | res->cookie(Cookie{"lang", "nb-NO"}); 14 | 15 | // Or if you want to create a cookie with more options, f.ex. expires, path and domain: 16 | res->cookie(Cookie{"lang", "nb-NO", {"Expires", "Sun, 11 Dec 2016 08:49:37 GMT", 17 | "Path", "/path", "Domain", "domain.com"}}); 18 | ``` 19 | 20 | **Update** an existing cookie's value: 21 | ```cpp 22 | res->update_cookie("lang", "en-US"); 23 | 24 | // Or if you have specified a path and/or domain when creating the cookie: 25 | res->update_cookie("lang", "/path", "domain.com", "en-US"); 26 | ``` 27 | 28 | **Clear** a cookie: 29 | ```cpp 30 | res->clear_cookie("lang"); 31 | 32 | // Or if you have specified a path and/or domain when creating the cookie: 33 | res->clear_cookie("lang", "/path", "domain.com"); 34 | ``` 35 | 36 | The cookie library contains the middleware [CookieParser](https://github.com/includeos/cookie/blob/master/cookie_parser.hpp) that parses a request's cookie header and puts the cookies in a [CookieJar](https://github.com/includeos/cookie/blob/master/cookie_jar.hpp). The CookieJar is then added as an [attribute](https://github.com/includeos/mana/blob/master/include/mana/attribute.hpp) to the request, making the cookies available to the developer: 37 | 38 | ```cpp 39 | if (req->has_attribute()) { 40 | // Get the CookieJar 41 | auto req_cookies = req->get_attribute(); 42 | 43 | { // Print all the request-cookies (name-value pairs) 44 | const auto& all_cookies = req_cookies->get_cookies(); 45 | for (const auto& c : all_cookies) 46 | printf("Cookie: %s=%s\n", c.first.c_str(), c.second.c_str()); 47 | } 48 | 49 | // Get the value of a cookie named lang 50 | const auto& value = req_cookies->cookie_value("lang"); 51 | 52 | // Do actions based on the value 53 | } 54 | ``` 55 | 56 | ## Requirements 57 | * [IncludeOS](https://github.com/hioa-cs/IncludeOS) installed (together with its dependencies) 58 | * [Mana](https://github.com/includeos/mana) 59 | * git 60 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/dashboard.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_DASHBOARD_HPP 20 | #define DASHBOARD_DASHBOARD_HPP 21 | 22 | #include 23 | #include 24 | #include "component.hpp" 25 | #include "common.hpp" 26 | 27 | namespace mana { 28 | namespace dashboard { 29 | 30 | class Dashboard { 31 | 32 | using Component_ptr = std::shared_ptr; 33 | using ComponentCollection = std::unordered_map; 34 | 35 | public: 36 | Dashboard(size_t buffer_capacity = 4096); 37 | 38 | const mana::Router& router() const 39 | { return router_; } 40 | 41 | void add(Component_ptr); 42 | 43 | template 44 | inline void construct(Args&&...); 45 | 46 | private: 47 | 48 | mana::Router router_; 49 | WriteBuffer buffer_; 50 | Writer writer_; 51 | 52 | ComponentCollection components_; 53 | 54 | void setup_routes(); 55 | 56 | void serve(mana::Request_ptr, mana::Response_ptr); 57 | void serialize(Writer&); 58 | 59 | void send_buffer(mana::Response&); 60 | void reset_writer(); 61 | 62 | }; 63 | 64 | inline void Dashboard::add(Component_ptr c) { 65 | components_.emplace(c->key(), c); 66 | 67 | // A really simple way to setup routes, only supports read (GET) 68 | router_.on_get("/" + c->key(), 69 | [this, c] (mana::Request_ptr, mana::Response_ptr res) 70 | { 71 | c->serialize(writer_); 72 | send_buffer(*res); 73 | }); 74 | } 75 | 76 | template 77 | inline void Dashboard::construct(Args&&... args) { 78 | static_assert(std::is_base_of::value, "Template type is not a Component"); 79 | 80 | add(std::make_unique(std::forward(args)...)); 81 | } 82 | 83 | } // < namespace dashboard 84 | } // < namespace mana 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /include/mana/middleware/butler.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_MIDDLEWARE_BUTLER_HPP 19 | #define MANA_MIDDLEWARE_BUTLER_HPP 20 | 21 | #include // inherit middleware 22 | #include 23 | #include 24 | 25 | namespace mana { 26 | namespace middleware { 27 | 28 | /** 29 | * @brief Serves files (not food) 30 | * @details Serves files from a IncludeOS disk. 31 | * 32 | */ 33 | class Butler : public Middleware { 34 | private: 35 | using SharedDisk = std::shared_ptr; 36 | using Entry = fs::Dirent; 37 | using OnStat = fs::on_stat_func; 38 | 39 | public: 40 | 41 | struct Options { 42 | std::vector index; 43 | bool fallthrough; 44 | 45 | Options() = default; 46 | Options(std::initializer_list indices, bool fallth = true) 47 | : index(indices), fallthrough(fallth) {} 48 | }; 49 | 50 | Butler(SharedDisk disk, std::string root, Options opt = {{"index.html"}, true}); 51 | 52 | Callback handler() override { 53 | return {this, &Butler::process}; 54 | } 55 | 56 | void process(Request_ptr req, Response_ptr res, Next next); 57 | 58 | private: 59 | SharedDisk disk_; 60 | std::string root_; 61 | Options options_; 62 | 63 | std::string get_extension(const std::string& path) const; 64 | 65 | inline bool allowed_extension(const std::string& extension) const { 66 | return !extension.empty(); 67 | } 68 | 69 | /** 70 | * @brief Check if the path contains a file request 71 | * @details Very bad but easy way to assume the path is a request for a file. 72 | * 73 | * @param path 74 | * @return whether a path is a file request or not 75 | */ 76 | inline bool is_file_request(const std::string& path) const 77 | { return !get_extension(path).empty(); } 78 | 79 | }; // < class Butler 80 | 81 | }} //< namespace mana::middleware 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { label 'ubuntu-18.04' } 3 | triggers { upstream( upstreamProjects: 'IncludeOS/IncludeOS/master, IncludeOS/IncludeOS/dev', threshold: hudson.model.Result.SUCCESS ) } 4 | options { checkoutToSubdirectory('src') } 5 | environment { 6 | CONAN_USER_HOME = "${env.WORKSPACE}" 7 | PROFILE_x86_64 = 'clang-6.0-linux-x86_64' 8 | CPUS = """${sh(returnStdout: true, script: 'nproc')}""" 9 | PACKAGE = 'mana' 10 | USER = 'includeos' 11 | CHAN_LATEST = 'latest' 12 | CHAN_STABLE = 'stable' 13 | REMOTE = "${env.CONAN_REMOTE}" 14 | BINTRAY_CREDS = credentials('devops-includeos-user-pass-bintray') 15 | SRC = "${env.WORKSPACE}/src" 16 | } 17 | 18 | stages { 19 | stage('Setup') { 20 | steps { 21 | sh script: "ls -A | grep -v src | xargs rm -r || :", label: "Clean workspace" 22 | sh script: "conan config install https://github.com/includeos/conan_config.git", label: "conan config install" 23 | } 24 | } 25 | stage('Unit tests') { 26 | when { changeRequest() } 27 | steps { 28 | dir('unittests') { 29 | sh script: "conan install $SRC/unit -pr $PROFILE_x86_64", label: "Conan install" 30 | sh script: "cmake $SRC/unit", label: "Cmake" 31 | sh script: "make -j $CPUS", label: "build tests" 32 | sh script: "ctest --output-on-failure", label: "run unit tests" 33 | } 34 | } 35 | } 36 | stage('Build package') { 37 | steps { 38 | build_conan_package("$PROFILE_x86_64") 39 | script { VERSION = sh(script: "conan inspect -a version $SRC | cut -d ' ' -f 2", returnStdout: true).trim() } 40 | } 41 | } 42 | stage('build example') { 43 | steps { 44 | dir('build_example') { 45 | sh script: "conan install $SRC/integration/simple -pr $PROFILE_x86_64", label: "Conan install" 46 | sh script: ". ./activate.sh; cmake $SRC/integration/simple",label: "Cmake" 47 | sh script: "make -j $CPUS", label: "building example" 48 | } 49 | } 50 | } 51 | stage('Upload to bintray') { 52 | parallel { 53 | stage('Latest release') { 54 | when { branch 'master' } 55 | steps { 56 | upload_package("$CHAN_LATEST") 57 | } 58 | } 59 | stage('Stable release') { 60 | when { buildingTag() } 61 | steps { 62 | sh script: "conan copy --all $PACKAGE/$VERSION@$USER/$CHAN_LATEST $USER/$CHAN_STABLE", label: "Copy to stable channel" 63 | upload_package("$CHAN_STABLE") 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | def build_conan_package(String profile) { 72 | sh script: "conan create $SRC $USER/$CHAN_LATEST -pr ${profile}", label: "Build with profile: $profile" 73 | } 74 | 75 | def upload_package(String channel) { 76 | sh script: """ 77 | conan user -p $BINTRAY_CREDS_PSW -r $REMOTE $BINTRAY_CREDS_USR 78 | conan upload --all -r $REMOTE $PACKAGE/$VERSION@$USER/$channel 79 | """, label: "Upload to bintray" 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mana 2 | IncludeOS C++ Web Application Framework 3 | 4 | [Acorn](../../examples/acorn) is a web server built with Mana which demonstrates a lot of its potential. 5 | 6 | Some insight in the implementation of mana can be found in [this post](http://blog.includeos.org/2016/10/05/middleware-implementation-in-mana). 7 | 8 | ## Usage 9 | 10 | It's easy to get started - check out the [examples](examples/). 11 | 12 | ```cpp 13 | using namespace mana; 14 | using namespace std::string_literals; 15 | 16 | std::unique_ptr server; 17 | 18 | void Service::start(const std::string&) 19 | { 20 | Router router; 21 | 22 | // GET / 23 | router.on_get("/", [](auto, auto res) { 24 | res->add_body("

Simple example

"s); 25 | res->send(); 26 | }); 27 | 28 | server = std::make_unique(net::Inet4::stack()); 29 | server->set_routes(router).listen(80); 30 | } 31 | ``` 32 | 33 | ### Routes 34 | 35 | Routes is where the server end-points are defined. 36 | 37 | ```cpp 38 | Router router; 39 | 40 | // GET / 41 | router.on_get("/", [] (auto req, auto res) { 42 | // Serve index.html 43 | }); 44 | 45 | // POST /users 46 | router.on_post("/users", [] (auto req, auto res) { 47 | // Register new user 48 | }); 49 | 50 | server.set_routes(router); 51 | ``` 52 | 53 | There is also support for named parameters in routes. 54 | 55 | ```cpp 56 | // GET /users/:id 57 | router.on_get("/users/:id(\\d+)", [](auto req, auto res) { 58 | auto id = req->params().get("id"); 59 | // Do actions according to "id" 60 | if(id == "42") 61 | // ... 62 | }); 63 | ``` 64 | 65 | ### Middleware 66 | 67 | Middleware are tasks which are executed before the user code defined in routes. 68 | 69 | ```cpp 70 | // Declare a new middleware 71 | class MyMiddleware : public mana::Middleware { 72 | // ... 73 | }; 74 | 75 | // Add a middleware object 76 | Middleware_ptr my_mw = std::make_shared(); 77 | server.use(my_mw); 78 | ``` 79 | 80 | It's also possible to just add a simple task with a lambda. 81 | 82 | ```cpp 83 | // Add a middleware lambda 84 | server.use([] (auto req, auto res) { 85 | // My custom middleware function 86 | (*next)(); // Don't forget to call next if no response was sent! 87 | }); 88 | ``` 89 | 90 | *Psst, there is already some [ready-made middleware](include/mana/middleware) for Mana!* 91 | 92 | 93 | ### Attributes 94 | 95 | Attributes is a way to extend the Request object with additional data. 96 | 97 | ```cpp 98 | // Declare a new attribute 99 | class MyAttribute : public Attribute { 100 | // ... 101 | }; 102 | 103 | // Set attribute in middleware 104 | MyMiddleware::process(auto req, auto res, auto next) { 105 | auto attr = std::make_shared(); 106 | req->set_attribute(attr); 107 | (*next)(); 108 | } 109 | 110 | // Use attribute in route 111 | router.use("/my-route", [] (auto req, auto res) { 112 | if(auto attr = req->get_attribute()) { 113 | // Do things with "attr" 114 | } 115 | }); 116 | ``` 117 | 118 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/stacksampler.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_STACKSAMPLER_HPP 20 | #define DASHBOARD_COMPONENTS_STACKSAMPLER_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | 26 | namespace mana { 27 | namespace dashboard { 28 | 29 | class StackSampler : public Component { 30 | 31 | public: 32 | 33 | static std::shared_ptr instance() { 34 | static std::weak_ptr instance_; 35 | if(auto p = instance_.lock()) 36 | return p; 37 | 38 | std::shared_ptr p{new StackSampler}; 39 | instance_ = p; 40 | return p; 41 | } 42 | 43 | std::string key() const override 44 | { return "stack_sampler"; } 45 | 46 | void serialize(Writer& writer) override { 47 | 48 | writer.StartObject(); 49 | 50 | auto samples = ::StackSampler::results(sample_size_); 51 | auto total = ::StackSampler::samples_total(); 52 | auto asleep = ::StackSampler::samples_asleep(); 53 | 54 | writer.Key("active"); 55 | double active = total / (double)(total+asleep) * 100.0; 56 | writer.Double(active); 57 | 58 | writer.Key("asleep"); 59 | double asleep_perc = asleep / (double)(total+asleep) * 100.0; 60 | writer.Double(asleep_perc); 61 | 62 | writer.Key("samples"); 63 | writer.StartArray(); 64 | for (auto& sa : samples) 65 | { 66 | writer.StartObject(); 67 | 68 | writer.Key("address"); 69 | writer.Uint((uintptr_t)sa.addr); 70 | 71 | writer.Key("name"); 72 | writer.String(sa.name); 73 | 74 | writer.Key("total"); 75 | writer.Uint(sa.samp); 76 | 77 | // percentage of total samples 78 | float perc = sa.samp / (float)total * 100.0f; 79 | 80 | writer.Key("percent"); 81 | writer.Double(perc); 82 | 83 | writer.EndObject(); 84 | } 85 | writer.EndArray(); 86 | 87 | writer.EndObject(); 88 | } 89 | 90 | void set_sample_size(int N) 91 | { sample_size_ = N; } 92 | 93 | private: 94 | 95 | StackSampler() 96 | : sample_size_{12} 97 | { 98 | ::StackSampler::begin(); 99 | } 100 | 101 | int sample_size_; 102 | 103 | }; 104 | 105 | } // < namespace dashboard 106 | } // < namespace mana 107 | 108 | #endif 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/server.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include "../include/mana/server.hpp" 19 | #include 20 | 21 | // #define DEBUG 22 | 23 | using namespace mana; 24 | using namespace std::chrono; 25 | 26 | Server::Server(net::TCP& tcp, std::chrono::seconds timeout) 27 | : server_{tcp, {this, &Server::handle_request}, timeout} 28 | { 29 | } 30 | 31 | Router& Server::router() noexcept { 32 | return router_; 33 | } 34 | 35 | void Server::process(Request_ptr req, Response_ptr res) { 36 | auto it_ptr = std::make_shared(middleware_.begin()); 37 | auto next = std::make_shared(); 38 | auto weak_next = std::weak_ptr(next); 39 | // setup Next callback 40 | *next = next_t::make_packed( 41 | [this, it_ptr, weak_next, req, res] 42 | { 43 | // derefence the pointer to the iterator 44 | auto& it = *it_ptr; 45 | 46 | // skip those who don't match 47 | while(it != middleware_.end() and !path_starts_with(req->uri(), it->path)) 48 | it++; 49 | 50 | // while there is more to do 51 | if(it != middleware_.end()) { 52 | // dereference the function 53 | auto& func = it->callback; 54 | // advance the iterator for the next next-call 55 | it++; 56 | auto next = weak_next.lock(); // this should be safe since we're inside next 57 | // execute the function 58 | func(req, res, next); 59 | } 60 | // no more middleware, proceed with route processing 61 | else { 62 | process_route(req, res); 63 | } 64 | }); 65 | // get the party started.. 66 | (*next)(); 67 | } 68 | 69 | void Server::process_route(Request_ptr req, Response_ptr res) { 70 | try { 71 | auto parsed_route = router_.match(req->method(), req->uri()); 72 | req->set_params(parsed_route.parsed_values); 73 | parsed_route.job(req, res); 74 | } 75 | catch (const Router_error& err) { 76 | res->send_code(http::Not_Found, true); 77 | } 78 | } 79 | 80 | void Server::use(const Path& path, Middleware_ptr mw_ptr) { 81 | mw_storage_.push_back(mw_ptr); 82 | mw_ptr->on_mount(path); 83 | use(path, mw_ptr->handler()); 84 | } 85 | 86 | void Server::use(const Path& path, Callback callback) { 87 | middleware_.emplace_back(path, callback); 88 | } 89 | 90 | void Server::handle_request(http::Request_ptr request, http::Response_writer_ptr hrw) 91 | { 92 | auto req = std::make_shared(std::move(request)); 93 | auto res = std::make_shared(std::move(hrw)); 94 | process(std::move(req), std::move(res)); 95 | } 96 | -------------------------------------------------------------------------------- /include/mana/middleware/director.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_MIDDLEWARE_DIRECTOR_HPP 19 | #define MANA_MIDDLEWARE_DIRECTOR_HPP 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | namespace mana { 27 | namespace middleware { 28 | 29 | /** 30 | * @brief Responsible to set the scene of a directory. 31 | * @details Creates a simple html display of a directory entry on a IncludeOS disk. 32 | * 33 | */ 34 | class Director : public Middleware { 35 | private: 36 | using SharedDisk = fs::Disk_ptr; 37 | using Entry = fs::Dirent; 38 | using Entries = fs::Dirvec_ptr; 39 | 40 | public: 41 | 42 | const static std::string HTML_HEADER; 43 | const static std::string HTML_FOOTER; 44 | const static std::string BOOTSTRAP_CSS; 45 | const static std::string FONTAWESOME; 46 | 47 | Director(SharedDisk disk, std::string root) 48 | : disk_(disk), root_(root) {} 49 | 50 | Callback handler() override { 51 | return {this, &Director::process}; 52 | } 53 | 54 | void process(mana::Request_ptr req, mana::Response_ptr res, mana::Next next); 55 | 56 | void on_mount(const std::string& path) override { 57 | Middleware::on_mount(path); 58 | printf(" Mounted on [ %s ]\n", path.c_str()); 59 | } 60 | 61 | private: 62 | SharedDisk disk_; 63 | std::string root_; 64 | 65 | std::string create_html(Entries entries, const std::string& path); 66 | 67 | void build_table(std::ostringstream& ss, Entries entries, const std::string& path); 68 | 69 | void add_table_header(std::ostringstream& ss); 70 | 71 | void add_tr(std::ostringstream& ss, const std::string& path, const Entry& entry); 72 | 73 | std::string get_icon(const Entry& entry); 74 | 75 | std::string create_url(const std::string& path, const std::string& name) { 76 | return "" + name + ""; 77 | } 78 | 79 | template 80 | void add_td(std::ostringstream& ss, std::string&& cl, const T& field) { 81 | ss << "" << field << ""; 82 | } 83 | 84 | void normalize_trailing_slashes(std::string& path) { 85 | if(!path.empty() && path.back() != '/') 86 | path += '/'; 87 | } 88 | 89 | std::string resolve_file_path(std::string path) { 90 | path.replace(0,mountpath_.size(), root_); 91 | return path; 92 | } 93 | 94 | std::string human_readable_size(double sz) const; 95 | 96 | }; // < class Director 97 | 98 | }} //< namespace mana::middleware 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/mana/components/dashboard/components/tcp.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #ifndef DASHBOARD_COMPONENTS_TCP_HPP 20 | #define DASHBOARD_COMPONENTS_TCP_HPP 21 | 22 | #include "../component.hpp" 23 | 24 | #include 25 | #include 26 | 27 | namespace mana { 28 | namespace dashboard { 29 | 30 | class TCP : public Component { 31 | 32 | public: 33 | 34 | TCP(net::TCP& tcp) 35 | : tcp_{tcp} 36 | {} 37 | 38 | std::string key() const override 39 | { return "tcp"; } 40 | 41 | void serialize(Writer& writer) override { 42 | writer.StartObject(); 43 | 44 | writer.Key("address"); 45 | writer.String(tcp_.address().to_string()); 46 | 47 | writer.Key("ifname"); 48 | writer.String(tcp_.stack().ifname()); 49 | 50 | // Listeners 51 | writer.Key("listeners"); 52 | writer.StartArray(); 53 | auto& listeners = tcp_.listeners(); 54 | for(auto it = listeners.begin(); it != listeners.end(); ++it) 55 | { 56 | auto& listener = *(it->second); 57 | serialize_listener(writer, listener); 58 | } 59 | writer.EndArray(); 60 | 61 | // Connections 62 | writer.Key("connections"); 63 | writer.StartArray(); 64 | for(auto it : tcp_.connections()) 65 | { 66 | auto& conn = *(it.second); 67 | serialize_connection(writer, conn); 68 | } 69 | writer.EndArray(); 70 | 71 | writer.EndObject(); 72 | } 73 | 74 | static void serialize_connection(Writer& writer, const net::tcp::Connection& conn) { 75 | writer.StartObject(); 76 | 77 | writer.Key("local"); 78 | writer.String(conn.local().to_string()); 79 | 80 | writer.Key("remote"); 81 | writer.String(conn.remote().to_string()); 82 | 83 | writer.Key("bytes_rx"); 84 | //writer.Uint64(conn.bytes_received()); 85 | writer.Uint(0); 86 | 87 | writer.Key("bytes_tx"); 88 | //writer.Uint64(conn.bytes_transmitted()); 89 | writer.Uint(0); 90 | 91 | writer.Key("state"); 92 | writer.String(conn.state().to_string()); 93 | 94 | writer.EndObject(); 95 | } 96 | 97 | static void serialize_listener(Writer& writer, const net::tcp::Listener& listener) { 98 | writer.StartObject(); 99 | 100 | writer.Key("port"); 101 | writer.Uint(listener.port()); 102 | 103 | writer.Key("syn_queue"); 104 | writer.StartArray(); 105 | for(auto conn_ptr : listener.syn_queue()) 106 | { 107 | serialize_connection(writer, *conn_ptr); 108 | } 109 | writer.EndArray(); 110 | 111 | writer.EndObject(); 112 | } 113 | 114 | private: 115 | net::TCP& tcp_; 116 | 117 | }; 118 | 119 | } // < namespace dashboard 120 | } // < namespace mana 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /src/response.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | #include 20 | 21 | using namespace mana; 22 | using namespace std::string_literals; 23 | 24 | Response::Response(http::Response_writer_ptr res) 25 | : reswriter_(std::move(res)) 26 | { 27 | } 28 | 29 | void Response::send(bool force_close) 30 | { 31 | if(force_close) 32 | header().set_field(http::header::Connection, "close"); 33 | 34 | reswriter_->write(); 35 | } 36 | 37 | void Response::send_code(const Code code, bool force_close) 38 | { 39 | if(force_close) 40 | header().set_field(http::header::Connection, "close"); 41 | 42 | reswriter_->write_header(code); 43 | } 44 | 45 | void Response::send_file(const File& file) 46 | { 47 | auto& entry = file.entry; 48 | 49 | /* Content Length */ 50 | header().set_content_length(entry.size()); 51 | 52 | /* Send header */ 53 | reswriter_->write_header(http::OK); 54 | 55 | /* Send file over connection */ 56 | auto* stream = reswriter_->connection().stream().get(); 57 | #ifdef VERBOSE_WEBSERVER 58 | printf(" Sending file: %s (%llu B).\n", 59 | entry.name().c_str(), entry.size()); 60 | #endif 61 | 62 | Async::upload_file( 63 | file.disk, 64 | file.entry, 65 | stream, 66 | Async::on_after_func::make_packed( 67 | [ 68 | stream, 69 | req{shared_from_this()}, // keep the response (and conn) alive until done 70 | entry 71 | ] (fs::error_t err, bool good) mutable 72 | { 73 | if(good) { 74 | #ifdef VERBOSE_WEBSERVER 75 | printf(" Success sending %s => %s\n", 76 | entry.name().c_str(), stream->remote().to_string().c_str()); 77 | #endif 78 | } 79 | else { 80 | printf(" Error sending %s => %s [%s]\n", 81 | entry.name().c_str(), stream->remote().to_string().c_str(), 82 | stream->is_closing() ? "Connection closing" : err.to_string().c_str()); 83 | } 84 | // remove on_write triggering for other 85 | // writes on the same connection 86 | stream->on_write(nullptr); 87 | }) 88 | ); 89 | } 90 | 91 | void Response::send_json(const std::string& json) 92 | { 93 | header().set_field(http::header::Content_Type, "application/json"); 94 | header().set_content_length(json.size()); 95 | // be simple for now 96 | source().add_body(json); 97 | send(); 98 | } 99 | 100 | void Response::error(Error&& err) { 101 | // NOTE: only cares about JSON (for now) 102 | source().set_status_code(err.code); 103 | send_json(err.json()); 104 | } 105 | 106 | Response::~Response() { 107 | #ifdef VERBOSE_WEBSERVER 108 | printf(" Deleted (%s)\n", conn_->to_string().c_str()); 109 | #endif 110 | } 111 | -------------------------------------------------------------------------------- /src/attributes/cookie_jar.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | namespace mana { 21 | namespace attribute { 22 | 23 | /////////////////////////////////////////////////////////////////////////////// 24 | size_t Cookie_jar::size() const noexcept { 25 | return cookies_.size(); 26 | } 27 | 28 | /////////////////////////////////////////////////////////////////////////////// 29 | bool Cookie_jar::empty() const noexcept { 30 | return cookies_.empty(); 31 | } 32 | 33 | /////////////////////////////////////////////////////////////////////////////// 34 | bool Cookie_jar::insert(const http::Cookie& c) noexcept { 35 | return cookies_.emplace(std::make_pair(c.get_name(), c.get_value())).second; 36 | } 37 | 38 | /////////////////////////////////////////////////////////////////////////////// 39 | bool Cookie_jar::insert(const std::string& name, const std::string& value) { 40 | return cookies_.emplace(std::make_pair(name, value)).second; 41 | } 42 | 43 | /////////////////////////////////////////////////////////////////////////////// 44 | Cookie_jar& Cookie_jar::erase(const http::Cookie& c) noexcept { 45 | cookies_.erase(c.get_name()); 46 | return *this; 47 | } 48 | 49 | /////////////////////////////////////////////////////////////////////////////// 50 | Cookie_jar& Cookie_jar::erase(const std::string& name) noexcept { 51 | cookies_.erase(name); 52 | return *this; 53 | } 54 | 55 | /////////////////////////////////////////////////////////////////////////////// 56 | Cookie_jar& Cookie_jar::clear() noexcept { 57 | cookies_.erase(cookies_.begin(), cookies_.end()); 58 | return *this; 59 | } 60 | 61 | /////////////////////////////////////////////////////////////////////////////// 62 | bool Cookie_jar::exists(const std::string& name) const noexcept { 63 | return cookies_.find(name) not_eq cookies_.end(); 64 | } 65 | 66 | /////////////////////////////////////////////////////////////////////////////// 67 | const std::string& Cookie_jar::cookie_value(const std::string& name) const noexcept { 68 | static const std::string no_entry_value; 69 | 70 | auto it = cookies_.find(name); 71 | 72 | if (it not_eq cookies_.end()) { 73 | return it->second; 74 | } 75 | 76 | return no_entry_value; 77 | } 78 | 79 | /////////////////////////////////////////////////////////////////////////////// 80 | const std::map& Cookie_jar::get_cookies() const noexcept { 81 | return cookies_; 82 | } 83 | 84 | /////////////////////////////////////////////////////////////////////////////// 85 | std::map::const_iterator Cookie_jar::begin() const noexcept { 86 | return cookies_.cbegin(); 87 | } 88 | 89 | /////////////////////////////////////////////////////////////////////////////// 90 | std::map::const_iterator Cookie_jar::end() const noexcept { 91 | return cookies_.cend(); 92 | } 93 | 94 | }} //< mana::attribute 95 | -------------------------------------------------------------------------------- /src/middleware/butler.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | #include 20 | 21 | using namespace std::string_literals; 22 | 23 | namespace mana { 24 | namespace middleware { 25 | 26 | Butler::Butler(SharedDisk disk, std::string root, Options opt) 27 | : disk_(disk), root_(root), options_(opt) 28 | { 29 | Expects(disk != nullptr && disk->fs_ready()); 30 | } 31 | 32 | void Butler::process(mana::Request_ptr req, mana::Response_ptr res, mana::Next next) 33 | { 34 | // if not a valid request 35 | if(req->method() != http::GET && req->method() != http::HEAD) { 36 | if(options_.fallthrough) { 37 | return (*next)(); 38 | } 39 | res->header().set_field(http::header::Allow, "GET, HEAD"s); 40 | res->send_code(http::Method_Not_Allowed); 41 | return; 42 | } 43 | 44 | // get path 45 | std::string path(req->uri()); 46 | // resolve extension 47 | auto ext = get_extension(path); 48 | // concatenate root with path, example: / => /public/ 49 | path = root_ + path; 50 | 51 | // no extension found 52 | if(ext.empty() and !options_.index.empty()) { 53 | if(path.back() != '/') path += '/'; 54 | // lets try to see if we can serve an index 55 | path += options_.index.at(0); // only check first one for now, else we have to use fs().ls 56 | disk_->fs().cstat( 57 | path, 58 | fs::on_stat_func::make_packed( 59 | [this, req, res, next, path](auto err, const auto& entry) 60 | { 61 | //printf(" err=%s path=%s entry=%s\n", 62 | // err.to_string().c_str(), path.c_str(), entry.name().c_str()); 63 | // no index was found on this path, go to next middleware 64 | if(err or !entry.is_file()) { 65 | return (*next)(); 66 | } 67 | // we got an index, lets send it 68 | else { 69 | auto mime = http::ext_to_mime_type(this->get_extension(path)); 70 | res->header().set_field(http::header::Content_Type, std::string(mime)); 71 | return res->send_file({disk_, entry}); 72 | } 73 | }) 74 | ); 75 | } 76 | // we found an extension, this is a (probably) a file request 77 | else { 78 | //printf(" Extension found - assuming request for file.\n"); 79 | disk_->fs().cstat( 80 | path, 81 | fs::on_stat_func::make_packed( 82 | [this, req, res, next, path](auto err, const auto& entry) 83 | { 84 | //printf(" err=%s path=%s entry=%s\n", 85 | // err.to_string().c_str(), path.c_str(), entry.name().c_str()); 86 | if(err or !entry.is_file()) { 87 | #ifdef VERBOSE_WEBSERVER 88 | printf(" File not found. Replying with 404.\n"); 89 | #endif 90 | res->send_code(http::Not_Found); 91 | return; 92 | /* 93 | if(!options_.fallthrough) { 94 | printf(" File not found. Replying with 404.\n"); 95 | return res->send_code(http::Not_Found); 96 | } 97 | else { 98 | return (*next)(); 99 | }*/ 100 | } 101 | else { 102 | #ifdef VERBOSE_WEBSERVER 103 | printf(" Found file: %s (%llu B)\n", entry.name().c_str(), entry.size()); 104 | #endif 105 | auto mime = http::ext_to_mime_type(this->get_extension(path)); 106 | res->header().set_field(http::header::Content_Type, std::string(mime)); 107 | res->send_file({disk_, entry}); 108 | return; 109 | } 110 | }) 111 | ); 112 | } 113 | } 114 | 115 | std::string Butler::get_extension(const std::string& path) const { 116 | std::string ext; 117 | auto idx = path.find_last_of("."); 118 | // Find extension 119 | if(idx != std::string::npos) { 120 | ext = path.substr(idx+1); 121 | } 122 | return ext; 123 | } 124 | 125 | 126 | }} // < namespace mana::middleware 127 | -------------------------------------------------------------------------------- /src/middleware/director.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | namespace mana { 21 | namespace middleware { 22 | 23 | const std::string Director::BOOTSTRAP_CSS = ""; 24 | const std::string Director::FONTAWESOME = ""; 25 | const std::string Director::HTML_HEADER = "" + BOOTSTRAP_CSS + FONTAWESOME + ""; 26 | const std::string Director::HTML_FOOTER = ""; 27 | 28 | void Director::process( 29 | mana::Request_ptr req, 30 | mana::Response_ptr res, 31 | mana::Next next 32 | ) 33 | { 34 | // get path 35 | std::string path = req->uri(); 36 | 37 | auto fpath = resolve_file_path(path); 38 | #ifdef VERBOSE_WEBSERVER 39 | printf(" Path: %s\n", fpath.c_str()); 40 | #endif 41 | 42 | normalize_trailing_slashes(path); 43 | disk_->fs().ls( 44 | fpath, 45 | fs::on_ls_func::make_packed( 46 | [this, req, res, next, path](auto err, auto entries) { 47 | // Path not found on filesystem, go next 48 | if(err) { 49 | return (*next)(); 50 | } 51 | else { 52 | res->source().add_body(this->create_html(entries, path)); 53 | res->send(); 54 | } 55 | }) 56 | ); 57 | } 58 | 59 | std::string Director::create_html(Entries entries, const std::string& path) { 60 | std::ostringstream ss; 61 | ss << HTML_HEADER; 62 | ss << "
"; 63 | ss << "

" << path << "

"; 64 | ss << "
"; 65 | build_table(ss, entries, path); 66 | ss << "
"; // panel 67 | ss << "
Powered by IncludeOS
"; 68 | ss << "
"; // container 69 | 70 | ss << HTML_FOOTER; 71 | return ss.str(); 72 | } 73 | 74 | void Director::build_table(std::ostringstream& ss, Entries entries, const std::string& path) { 75 | ss << ""; 76 | ss << ""; 77 | add_table_header(ss); 78 | ss << ""; 79 | 80 | ss << ""; 81 | for(auto e : *entries) { 82 | add_tr(ss, path, e); 83 | } 84 | ss << ""; 85 | 86 | ss << "
"; 87 | } 88 | 89 | void Director::add_table_header(std::ostringstream& ss) { 90 | ss << "" 91 | << "Type" 92 | << "Name" 93 | << "Size" 94 | << "Modified" 95 | << ""; 96 | } 97 | 98 | void Director::add_tr(std::ostringstream& ss, const std::string& path, const Entry& entry) { 99 | if(entry.name() == ".") 100 | return; 101 | 102 | bool isFile = entry.is_file(); 103 | 104 | ss << ""; 105 | add_td(ss, "type", get_icon(entry)); 106 | add_td(ss, "file", create_url(path, entry.name())); 107 | isFile ? add_td(ss, "size", human_readable_size(entry.size())) : add_td(ss, "size", "-"); 108 | isFile ? add_td(ss, "modified", "N/A") : add_td(ss, "modified", "-"); 109 | ss << ""; 110 | } 111 | 112 | std::string Director::get_icon(const Entry& entry) { 113 | std::ostringstream ss; 114 | ss << ""; 128 | return ss.str(); 129 | } 130 | 131 | std::string Director::human_readable_size(double sz) const { 132 | const char* suffixes[] = {"B", "KB", "MB", "GB"}; 133 | int i = 0; 134 | while(sz >= 1024 && ++i < 4) 135 | sz = sz/1024; 136 | 137 | char str[20]; 138 | snprintf(&str[0], 20, "%.2f %s", sz, suffixes[i]); 139 | return str; 140 | } 141 | 142 | }} // < namespace mana::middleware 143 | -------------------------------------------------------------------------------- /include/mana/server.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_SERVER_HPP 19 | #define MANA_SERVER_HPP 20 | 21 | #include 22 | 23 | #include "middleware.hpp" 24 | #include "request.hpp" 25 | #include "response.hpp" 26 | #include "router.hpp" 27 | 28 | namespace mana { 29 | 30 | inline bool path_starts_with(const std::string& path, const std::string& start) { 31 | if(path.size() < start.size()) 32 | return false; 33 | if(path == start) 34 | return true; 35 | return path.substr(0, std::min(start.size(), path.size())) == start; 36 | } 37 | 38 | //------------------------------- 39 | // This class is a simple dumb 40 | // HTTP server for service testing 41 | //------------------------------- 42 | class Server { 43 | private: 44 | //------------------------------- 45 | // Internal class type aliases 46 | //------------------------------- 47 | using Port = const unsigned; 48 | using Path = std::string; 49 | struct MappedCallback { 50 | Path path; 51 | Callback callback; 52 | MappedCallback(Path pth, Callback cb) : path(pth), callback(cb) {} 53 | }; 54 | using MiddlewareStack = std::vector; 55 | //------------------------------- 56 | public: 57 | 58 | explicit Server(net::TCP&, std::chrono::seconds timeout = http::Server::DEFAULT_IDLE_TIMEOUT); 59 | 60 | //------------------------------- 61 | // Default destructor 62 | //------------------------------- 63 | ~Server() noexcept = default; 64 | 65 | //------------------------------- 66 | // Get the underlying router 67 | // which contain route resolution 68 | // configuration 69 | //------------------------------- 70 | Router& router() noexcept; 71 | 72 | //------------------------------- 73 | // Install a new route table for 74 | // route resolutions 75 | // 76 | // @tparam (http::Router) new_routes - The new route table 77 | // to install 78 | // 79 | // @return - The object that invoked this method 80 | //------------------------------- 81 | template 82 | Server& set_routes(Route_Table&& routes); 83 | 84 | //------------------------------- 85 | // Start the server to listen for 86 | // incoming connections on the 87 | // specified port 88 | //------------------------------- 89 | void listen(Port port) 90 | { server_.listen(port); } 91 | 92 | void use(const Path&, Middleware_ptr); 93 | 94 | void use(Middleware_ptr mw) 95 | { use("/", std::move(mw)); } 96 | 97 | void use(const Path&, Callback); 98 | 99 | void use(Callback cb) 100 | { use("/", std::move(cb)); } 101 | 102 | http::Server& http_server() noexcept 103 | { return server_; } 104 | 105 | const http::Server& http_server() const noexcept 106 | { return server_; } 107 | 108 | size_t connected_clients() const noexcept 109 | { return server_.connected_clients(); } 110 | 111 | private: 112 | //------------------------------- 113 | // Class data members 114 | //------------------------------- 115 | http::Server server_; 116 | Router router_; 117 | MiddlewareStack middleware_; 118 | std::vector mw_storage_; 119 | 120 | //----------------------------------- 121 | // Deleted move and copy operations 122 | //----------------------------------- 123 | Server(const Server&) = delete; 124 | Server(Server&&) = delete; 125 | 126 | //----------------------------------- 127 | // Deleted move and copy assignment operations 128 | //----------------------------------- 129 | Server& operator = (const Server&) = delete; 130 | Server& operator = (Server&&) = delete; 131 | 132 | void handle_request(http::Request_ptr, http::Response_writer_ptr); 133 | 134 | void process(Request_ptr req, Response_ptr res); 135 | 136 | void process_route(Request_ptr, Response_ptr); 137 | 138 | }; //< class Server 139 | 140 | template 141 | inline Server& Server::set_routes(Route_Table&& routes) { 142 | router_.install_new_configuration(std::forward(routes)); 143 | return *this; 144 | } 145 | 146 | } // namespace mana 147 | 148 | #endif //< MANA_SERVER_HPP 149 | -------------------------------------------------------------------------------- /include/mana/request.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_REQUEST_HPP 19 | #define MANA_REQUEST_HPP 20 | 21 | #include 22 | #include 23 | #include "attribute.hpp" 24 | #include "params.hpp" 25 | 26 | #include 27 | 28 | namespace mana { 29 | 30 | class Request; 31 | using Request_ptr = std::shared_ptr; 32 | 33 | class Request_error : public std::runtime_error { 34 | public: 35 | Request_error(http::status_t code, const char* err) 36 | : std::runtime_error{err}, code_{code} 37 | {} 38 | 39 | http::status_t code() const 40 | { return code_; } 41 | 42 | private: 43 | http::status_t code_; 44 | }; 45 | 46 | /** 47 | * @brief A wrapper around a HTTP Request. 48 | * @details Extends the basic HTTP Request by adding n attributes (Attribute) 49 | * 50 | */ 51 | class Request { 52 | public: 53 | /** 54 | * @brief Construct a Request with a given http::Request 55 | * 56 | * @param[in] req The HTTP request 57 | */ 58 | explicit Request(http::Request_ptr req); 59 | 60 | /** 61 | * @brief Construct a Request by internally creating a http::Request 62 | * 63 | * @param[in] req The HTTP request to be created 64 | */ 65 | explicit Request(http::Request&& req); 66 | 67 | /** 68 | * @brief Returns the underlying HTTP header 69 | * 70 | * @return A HTTP header 71 | */ 72 | auto& header() 73 | { return req_->header(); } 74 | 75 | const auto& header() const 76 | { return req_->header(); } 77 | 78 | /** 79 | * @brief Returns the underlying HTTP method 80 | * 81 | * @return The requests HTTP method 82 | */ 83 | auto method() const 84 | { return req_->method(); } 85 | 86 | /** 87 | * @brief Returns the Requests URI 88 | * 89 | * @return The requests URI 90 | */ 91 | const auto& uri() const 92 | { return req_->uri(); } 93 | 94 | /** 95 | * @brief Returns the underlying HTTP Request object 96 | * 97 | * @return The HTTP Request object 98 | */ 99 | auto& source() 100 | { return *req_; } 101 | 102 | /** 103 | * @brief Check if the given attribute exists. 104 | * @details Iterates over map and check if the given 105 | * 106 | * @tparam A : The specific attribute 107 | * @return : If the Request has the specific attribute. 108 | */ 109 | template 110 | bool has_attribute() const; 111 | 112 | /** 113 | * @brief Retrieve a shared ptr to the specific attribute. 114 | * @details Try to retrieve the specific attribute by looking up the type 115 | * as key inside the attribute map. 116 | * 117 | * @tparam A : The specific attribute 118 | * @return : A shared ptr to the specific attribute. (Can be null if not exists.) 119 | */ 120 | template 121 | std::shared_ptr get_attribute(); 122 | 123 | /** 124 | * @brief Add/set a specific attribute. 125 | * @details Inserts a shared ptr of the specific attribute with type as key. (Will replace if already exists) 126 | * 127 | * @param : A shared ptr to the specific attribute 128 | * @tparam A : The specific attribute 129 | */ 130 | template 131 | void set_attribute(std::shared_ptr); 132 | 133 | std::string route_string() const 134 | { return "@" + std::string(http::method::str(req_->method())) + ":" + std::string(req_->uri().path()); } 135 | 136 | void set_params(const Params& params) { params_ = params; } 137 | 138 | const Params& params() const { return params_; } 139 | 140 | private: 141 | http::Request_ptr req_; 142 | /** 143 | * @brief A map with pointers to attributes. 144 | * @details A map with a unique key to a specific attribute 145 | * and a pointer to the base class Attribute. 146 | * (Since we got more than one request, an Attribute can't be static) 147 | */ 148 | std::map attributes_; 149 | 150 | Params params_; 151 | 152 | }; // < class Request 153 | 154 | template 155 | bool Request::has_attribute() const { 156 | return attributes_.find(Attribute::type()) != attributes_.end(); 157 | } 158 | 159 | template 160 | std::shared_ptr Request::get_attribute() { 161 | auto it = attributes_.find(Attribute::type()); 162 | if(it != attributes_.end()) 163 | return std::static_pointer_cast(it->second); 164 | return nullptr; 165 | } 166 | 167 | template 168 | void Request::set_attribute(std::shared_ptr attr) { 169 | attributes_.insert({Attribute::type(), attr}); 170 | } 171 | 172 | }; // < namespace mana 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /include/mana/response.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_RESPONSE_HPP 19 | #define MANA_RESPONSE_HPP 20 | 21 | #include 22 | #include 23 | struct File { 24 | 25 | File(fs::Disk_ptr dptr, const fs::Dirent& ent) 26 | : disk(dptr), entry(ent) 27 | { 28 | assert(entry.is_file()); 29 | } 30 | 31 | fs::Disk_ptr disk; 32 | fs::Dirent entry; 33 | }; 34 | 35 | #include 36 | #include 37 | 38 | namespace mana { 39 | 40 | class Response; 41 | using Response_ptr = std::shared_ptr; 42 | 43 | class Response : public std::enable_shared_from_this { 44 | private: 45 | using Code = http::status_t; 46 | 47 | public: 48 | 49 | /** 50 | * @brief An error to a HTTP Response 51 | */ 52 | struct Error { 53 | Code code; 54 | std::string type; 55 | std::string message; 56 | 57 | /** 58 | * @brief Constructs an error with code "Bad Request" and without type & msg 59 | */ 60 | inline Error(); 61 | 62 | /** 63 | * @brief Constructs an error with code "Bad Request" with the given type & msg 64 | * 65 | * @param[in] type The type of the error as a string 66 | * @param[in] msg The error message as a string 67 | */ 68 | inline Error(std::string&& type, std::string&& msg); 69 | 70 | /** 71 | * @brief Constructs an error with a given code, type & msg 72 | * 73 | * @param[in] code The error code 74 | * @param[in] type The type of the error as a string 75 | * @param[in] msg The error message as a string 76 | */ 77 | inline Error(const Code code, std::string&& type, std::string&& msg); 78 | 79 | /** 80 | * @brief Represent the error's type and message as a json object 81 | * 82 | * @return A json string in the form of { "type": , "message": } 83 | */ 84 | inline std::string json() const; 85 | }; 86 | 87 | /** 88 | * @brief Construct a Response with a given HTTP Response writer 89 | * 90 | * @param[in] reswriter The HTTP response writer 91 | */ 92 | explicit Response(http::Response_writer_ptr reswriter); 93 | 94 | /** 95 | * @brief Returns the underlying HTTP header 96 | * 97 | * @return A HTTP header 98 | */ 99 | auto& header() 100 | { return reswriter_->header(); } 101 | 102 | const auto& header() const 103 | { return reswriter_->header(); } 104 | 105 | /** 106 | * @brief Returns the underlying HTTP Response writer 107 | * 108 | * @return A HTTP Response writer 109 | */ 110 | auto& writer() 111 | { return *reswriter_; } 112 | 113 | /** 114 | * @brief Returns the underlying HTTP Response object 115 | * 116 | * @return The HTTP Response object 117 | */ 118 | auto& source() 119 | { return reswriter_->response(); } 120 | 121 | /** 122 | * @brief Returns the underlying HTTP Connection object 123 | * 124 | * @return The HTTP Connection object 125 | */ 126 | auto& connection() 127 | { return reswriter_->connection(); } 128 | 129 | /** 130 | * @brief Send a HTTP Status code together with headers. 131 | * Mostly used for replying with an error. 132 | * 133 | * @param[in] { parameter_description } 134 | * @param[in] close Whether to close the connection or not. Default = true 135 | */ 136 | void send_code(const Code, bool close = true); 137 | 138 | /** 139 | * @brief Send the underlying Response as is. 140 | * 141 | * @param[in] force_close whether to forcefully close the connection. Default = false 142 | */ 143 | void send(bool force_close = false); 144 | 145 | /** 146 | * @brief Send a file as a response 147 | * 148 | * @param[in] file The file to be sent 149 | */ 150 | void send_file(const File& file); 151 | 152 | /** 153 | * @brief Sends a response where the payload is json formatted data 154 | * 155 | * @param[in] jsonstr The json as a string 156 | */ 157 | void send_json(const std::string& jsonstr); 158 | 159 | /** Cookies */ 160 | 161 | void cookie(const std::string& cookie) 162 | { header().set_field(http::header::Set_Cookie, cookie); } 163 | 164 | template 165 | void cookie(const Cookie& c) 166 | { cookie(c.to_string()); } 167 | 168 | template 169 | inline void clear_cookie(const std::string& name) 170 | { clear_cookie(name, "", ""); } 171 | 172 | template 173 | inline void clear_cookie(const std::string& name, const std::string& path, const std::string& domain); 174 | 175 | template 176 | inline void update_cookie(const std::string& name, const std::string& new_value) 177 | { update_cookie(name, "", "", new_value); } 178 | 179 | template 180 | inline void update_cookie(const std::string& name, const std::string& new_value, 181 | const std::vector& new_options) 182 | { update_cookie(name, "", "", new_value, new_options); } 183 | 184 | template 185 | inline void update_cookie(const std::string& name, const std::string& old_path, const std::string& old_domain, 186 | const std::string& new_value); 187 | 188 | template 189 | inline void update_cookie(const std::string& name, const std::string& old_path, const std::string& old_domain, 190 | const std::string& new_value, const std::vector& new_options); 191 | 192 | /** 193 | * @brief Send an error response 194 | * @details Sends an error response together with the given status code. 195 | * 196 | * @param e Response::Error 197 | */ 198 | void error(Error&&); 199 | 200 | auto& writer_ptr() 201 | { return reswriter_; } 202 | 203 | ~Response(); 204 | 205 | private: 206 | http::Response_writer_ptr reswriter_; 207 | 208 | }; // < class Response 209 | 210 | inline Response::Error::Error() 211 | : code{http::Bad_Request} 212 | { 213 | } 214 | 215 | inline Response::Error::Error(std::string&& type, std::string&& msg) 216 | : code{http::Bad_Request}, type{type}, message{msg} 217 | { 218 | } 219 | 220 | inline Response::Error::Error(const Code code, std::string&& type, std::string&& msg) 221 | : code{code}, type{type}, message{msg} 222 | { 223 | } 224 | 225 | inline std::string Response::Error::json() const 226 | { 227 | return "{ \"type\" : \"" + type + "\", \"message\" : \"" + message + "\" }"; 228 | } 229 | 230 | template 231 | inline void Response::clear_cookie(const std::string& name, const std::string& path, const std::string& domain) { 232 | Cookie c{name, ""}; 233 | c.set_path(path); 234 | c.set_domain(domain); 235 | c.set_expires("Sun, 06 Nov 1994 08:49:37 GMT"); // in the past 236 | 237 | cookie(c); 238 | } 239 | 240 | template 241 | inline void Response::update_cookie(const std::string& name, const std::string& old_path, const std::string& old_domain, 242 | const std::string& new_value) { 243 | // 1. Clear old cookie: 244 | clear_cookie(name, old_path, old_domain); 245 | // 2. Set new cookie: 246 | Cookie new_cookie{name, new_value}; 247 | cookie(new_cookie); 248 | } 249 | 250 | template 251 | inline void Response::update_cookie(const std::string& name, const std::string& old_path, const std::string& old_domain, 252 | const std::string& new_value, const std::vector& new_options) { 253 | // 1. Clear old cookie: 254 | clear_cookie(name, old_path, old_domain); 255 | // 2. Set new cookie: 256 | Cookie new_cookie{name, new_value, new_options}; 257 | cookie(new_cookie); 258 | } 259 | 260 | 261 | } // < mana 262 | 263 | #endif 264 | -------------------------------------------------------------------------------- /include/mana/router.hpp: -------------------------------------------------------------------------------- 1 | // This file is a part of the IncludeOS unikernel - www.includeos.org 2 | // 3 | // Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences 4 | // and Alfred Bratterud 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef MANA_ROUTER_HPP 19 | #define MANA_ROUTER_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "route.hpp" 26 | #include "params.hpp" 27 | 28 | namespace mana { 29 | 30 | //------------------------------- 31 | // This class is used to provide 32 | // route resolution 33 | //------------------------------- 34 | class Router { 35 | private: 36 | //------------------------------- 37 | // Internal class type aliases 38 | //using Span = gsl::span; 39 | using Route_table = std::unordered_map>; 40 | //------------------------------- 41 | public: 42 | 43 | /** 44 | * @brief Returned in match-method. 45 | * Contains both the End_point and the route parameters so that both can be returned. 46 | */ 47 | struct ParsedRoute { 48 | End_point job; 49 | Params parsed_values; 50 | }; 51 | 52 | //------------------------------- 53 | // Default constructor to set up 54 | // default routes 55 | //------------------------------- 56 | explicit Router() = default; 57 | 58 | //------------------------------- 59 | // Default destructor 60 | //------------------------------- 61 | ~Router() noexcept = default; 62 | 63 | //------------------------------- 64 | // Default move constructor 65 | //------------------------------- 66 | Router(Router&&) = default; 67 | 68 | //------------------------------- 69 | // Default move assignment operator 70 | //------------------------------- 71 | Router& operator = (Router&&) = default; 72 | 73 | //------------------------------- 74 | // Add a route mapping for route 75 | // resolution upon request 76 | // 77 | // @tparam (std::string) route - The route to map unto a 78 | // resulting destination 79 | // 80 | // @param result - The route mapping 81 | // 82 | // @return - The object that invoked this method 83 | //------------------------------- 84 | template 85 | Router& on_options(Routee&& route, End_point result); 86 | 87 | //------------------------------- 88 | // Add a route mapping for route 89 | // resolution upon request 90 | // 91 | // @tparam (std::string) route - The route to map unto a 92 | // resulting destination 93 | // 94 | // @param result - The route mapping 95 | // 96 | // @return - The object that invoked this method 97 | //------------------------------- 98 | template 99 | Router& on_get(Routee&& route, End_point result); 100 | 101 | //------------------------------- 102 | // Add a route mapping for route 103 | // resolution upon request 104 | // 105 | // @tparam (std::string) route - The route to map unto a 106 | // resulting destination 107 | // 108 | // @param result - The route mapping 109 | // 110 | // @return - The object that invoked this method 111 | //------------------------------- 112 | template 113 | Router& on_head(Routee&& route, End_point result); 114 | 115 | //------------------------------- 116 | // Add a route mapping for route 117 | // resolution upon request 118 | // 119 | // @tparam (std::string) route - The route to map unto a 120 | // resulting destination 121 | // 122 | // @param result - The route mapping 123 | // 124 | // @return - The object that invoked this method 125 | //------------------------------- 126 | template 127 | Router& on_post(Routee&& route, End_point result); 128 | 129 | //------------------------------- 130 | // Add a route mapping for route 131 | // resolution upon request 132 | // 133 | // @tparam (std::string) route - The route to map unto a 134 | // resulting destination 135 | // 136 | // @param result - The route mapping 137 | // 138 | // @return - The object that invoked this method 139 | //------------------------------- 140 | template 141 | Router& on_put(Routee&& route, End_point result); 142 | 143 | //------------------------------- 144 | // Add a route mapping for route 145 | // resolution upon request 146 | // 147 | // @tparam (std::string) route - The route to map unto a 148 | // resulting destination 149 | // 150 | // @param result - The route mapping 151 | // 152 | // @return - The object that invoked this method 153 | //------------------------------- 154 | template 155 | Router& on_delete(Routee&& route, End_point result); 156 | 157 | //------------------------------- 158 | // Add a route mapping for route 159 | // resolution upon request 160 | // 161 | // @tparam (std::string) route - The route to map unto a 162 | // resulting destination 163 | // 164 | // @param result - The route mapping 165 | // 166 | // @return - The object that invoked this method 167 | //------------------------------- 168 | template 169 | Router& on_trace(Routee&& route, End_point result); 170 | 171 | //------------------------------- 172 | // Add a route mapping for route 173 | // resolution upon request 174 | // 175 | // @tparam (std::string) route - The route to map unto a 176 | // resulting destination 177 | // 178 | // @param result - The route mapping 179 | // 180 | // @return - The object that invoked this method 181 | //------------------------------- 182 | template 183 | Router& on_connect(Routee&& route, End_point result); 184 | 185 | //------------------------------- 186 | // Add a route mapping for route 187 | // resolution upon request 188 | // 189 | // @tparam (std::string) route - The route to map unto a 190 | // resulting destination 191 | // 192 | // @param result - The route mapping 193 | // 194 | // @return - The object that invoked this method 195 | //------------------------------- 196 | template 197 | Router& on_patch(Routee&& route, End_point result); 198 | 199 | //------------------------------- 200 | // General way to add a route mapping for route 201 | // resolution upon request 202 | // 203 | // @param method - HTTP method 204 | // 205 | // @tparam (std::string) route - The route to map unto a 206 | // resulting destination 207 | // 208 | // @param result - The route mapping 209 | // 210 | // @return - The object that invoked this method 211 | //------------------------------- 212 | template 213 | Router& on(http::Method method, Routee&& route, End_point result); 214 | 215 | //------------------------------- 216 | // Install a new route table for 217 | // route resolutions 218 | // 219 | // @tparam (http::Router) new_routes - The new route table 220 | // to install 221 | // 222 | // @return - The object that invoked this method 223 | //------------------------------- 224 | template 225 | Router& install_new_configuration(Routee_Table&& new_routes); 226 | 227 | 228 | /** 229 | * Get the route callback where Route_expr matched a given path 230 | * 231 | * @param path : the route path 232 | * @note : not const becuase it uses index operator to a map 233 | **/ 234 | inline ParsedRoute match(http::Method, const std::string&); 235 | 236 | /** 237 | * @brief Make the router use another router on a given route 238 | * @details Currently only copies the content from the outside 239 | * Router and adds new Route in RouteTable by combining 240 | * root route and the route to the other Route. 241 | * 242 | * Maybe Router should be able to keep a collection of other routers. 243 | * 244 | * @param Routee Root path 245 | * @param Router another router with Routes 246 | * 247 | * @return this Router 248 | */ 249 | template 250 | Router& use(Routee&&, const Router&); 251 | 252 | /** 253 | * @brief Copies Routes from another Router object 254 | * 255 | * @param Router to be copied from 256 | * @return this Router 257 | */ 258 | Router& add(const Router&); 259 | 260 | /** 261 | * @brief Optimize route search for all routes by bringing 262 | * the most hitted route to the front of the search queue 263 | * 264 | * @return The object that invoked this method 265 | */ 266 | Router& optimize_route_search(); 267 | 268 | /** 269 | * @brief Optimize route search for the specified HTTP method 270 | * by bringing the most hitted route to the front of the 271 | * search queue 272 | * 273 | * @param method 274 | * The HTTP method to optimize search for 275 | * 276 | * @return The object that invoked this method 277 | */ 278 | Router& optimize_route_search(const http::Method method); 279 | 280 | Router& operator<<(const Router& obj) 281 | { return add(obj); } 282 | 283 | std::string to_string() const; 284 | 285 | private: 286 | 287 | Router(const Router&) = delete; 288 | Router& operator = (const Router&) = delete; 289 | 290 | Route_table route_table_; 291 | 292 | }; //< class Router 293 | 294 | class Router_error : public std::runtime_error { 295 | using runtime_error::runtime_error; 296 | }; 297 | 298 | /**--v----------- Implementation Details -----------v--**/ 299 | 300 | template 301 | inline Router& Router::on_options(Routee&& route, End_point result) { 302 | route_table_[http::OPTIONS].emplace_back(std::forward(route), result); 303 | return *this; 304 | } 305 | 306 | template 307 | inline Router& Router::on_get(Routee&& route, End_point result) { 308 | route_table_[http::GET].emplace_back(std::forward(route), result); 309 | return *this; 310 | } 311 | 312 | template 313 | inline Router& Router::on_head(Routee&& route, End_point result) { 314 | route_table_[http::HEAD].emplace_back(std::forward(route), result); 315 | return *this; 316 | } 317 | 318 | template 319 | inline Router& Router::on_post(Routee&& route, End_point result) { 320 | route_table_[http::POST].emplace_back(std::forward(route), result); 321 | return *this; 322 | } 323 | 324 | template 325 | inline Router& Router::on_put(Routee&& route, End_point result) { 326 | route_table_[http::PUT].emplace_back(std::forward(route), result); 327 | return *this; 328 | } 329 | 330 | template 331 | inline Router& Router::on_delete(Routee&& route, End_point result) { 332 | route_table_[http::DELETE].emplace_back(std::forward(route), result); 333 | return *this; 334 | } 335 | 336 | template 337 | inline Router& Router::on_trace(Routee&& route, End_point result) { 338 | route_table_[http::TRACE].emplace_back(std::forward(route), result); 339 | return *this; 340 | } 341 | 342 | template 343 | inline Router& Router::on_connect(Routee&& route, End_point result) { 344 | route_table_[http::CONNECT].emplace_back(std::forward(route), result); 345 | return *this; 346 | } 347 | 348 | template 349 | inline Router& Router::on_patch(Routee&& route, End_point result) { 350 | route_table_[http::PATCH].emplace_back(std::forward(route), result); 351 | return *this; 352 | } 353 | 354 | template 355 | inline Router& Router::on(http::Method method, Routee&& route, End_point result) { 356 | route_table_[method].emplace_back(std::forward(route), result); 357 | return *this; 358 | } 359 | 360 | template 361 | inline Router& Router::install_new_configuration(Routee_Table&& new_routes) { 362 | route_table_ = std::forward(new_routes).route_table_; 363 | return *this; 364 | } 365 | 366 | inline Router::ParsedRoute Router::match(http::Method method, const std::string& path) { 367 | auto routes = route_table_[method]; 368 | 369 | if (routes.empty()) { 370 | throw Router_error("No routes for method " + std::string(http::method::str(method))); 371 | } 372 | 373 | for (auto& route : routes) { 374 | if (std::regex_match(path, route.expr)) { 375 | ++route.hits; 376 | 377 | // Set the pairs in params: 378 | Params params; 379 | std::smatch res; 380 | 381 | for (std::sregex_iterator i = std::sregex_iterator{path.begin(), path.end(), route.expr}; 382 | i != std::sregex_iterator{}; ++i) { res = *i; } 383 | 384 | // First parameter/value is in res[1], second in res[2], and so on 385 | for (size_t i = 0; i < route.keys.size(); i++) 386 | params.insert(route.keys[i].name, res[i + 1]); 387 | 388 | ParsedRoute parsed_route; 389 | parsed_route.job = route.end_point; 390 | parsed_route.parsed_values = params; 391 | 392 | return parsed_route; 393 | } 394 | } 395 | 396 | throw Router_error("No matching route for " + std::string(http::method::str(method)) + " " + path); 397 | } 398 | 399 | template 400 | inline Router& Router::use(Routee&& root, const Router& router) { 401 | // pair> 402 | for(auto& method_routes : router.route_table_) 403 | { 404 | auto& method = method_routes.first; 405 | auto& routes = method_routes.second; 406 | // vector 407 | for(auto& route : routes) 408 | { 409 | std::string path = root + route.path; 410 | on(method, path, route.end_point); 411 | } 412 | } 413 | return *this; 414 | } 415 | 416 | inline Router& Router::add(const Router& router) { 417 | for (const auto& e : router.route_table_) { 418 | auto it = route_table_.find(e.first); 419 | if (it not_eq route_table_.end()) { 420 | it->second.insert(it->second.cend(), e.second.cbegin(), e.second.cend()); 421 | continue; 422 | } 423 | route_table_[e.first] = e.second; 424 | } 425 | return *this; 426 | } 427 | 428 | inline Router& Router::optimize_route_search() { 429 | auto it = route_table_.begin(); 430 | auto end = route_table_.end(); 431 | 432 | while (it not_eq end) { 433 | std::stable_sort(it->second.begin(), it->second.end()); 434 | ++it; 435 | } 436 | 437 | return *this; 438 | } 439 | 440 | inline Router& Router::optimize_route_search(const http::Method method) { 441 | auto it = route_table_.find(method); 442 | if (it not_eq route_table_.end()) { 443 | std::stable_sort(it->second.begin(), it->second.end()); 444 | } 445 | return *this; 446 | } 447 | 448 | inline std::string Router::to_string() const { 449 | std::ostringstream ss; 450 | 451 | for(const auto& method_routes : route_table_) { 452 | auto&& method = method_routes.first; 453 | auto&& routes = method_routes.second; 454 | for(auto&& route : routes) { 455 | ss << method << '\t' << route.path << '\n'; 456 | } 457 | } 458 | 459 | return ss.str(); 460 | } 461 | 462 | /**--^----------- Implementation Details -----------^--**/ 463 | 464 | } //< namespace mana 465 | 466 | 467 | #endif //< MANA_ROUTER_HPP 468 | --------------------------------------------------------------------------------