add crow as local files
|
@ -0,0 +1,97 @@
|
|||
---
|
||||
BasedOnStyle: Mozilla
|
||||
AccessModifierOffset: '-4'
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: 'false'
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: 'true' #AlignAfterOperator
|
||||
AlignTrailingComments: 'true'
|
||||
AllowShortBlocksOnASingleLine: 'true'
|
||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: 'true'
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'false'
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'true'
|
||||
BinPackParameters: 'true'
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: 'true'
|
||||
AfterClass: 'true'
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: 'true'
|
||||
AfterFunction: 'true'
|
||||
AfterNamespace: 'true'
|
||||
AfterObjCDeclaration: 'true'
|
||||
AfterStruct: 'true'
|
||||
AfterUnion: 'true'
|
||||
AfterExternBlock: 'true'
|
||||
BeforeCatch: 'true'
|
||||
BeforeElse: 'true'
|
||||
# BeforeLambdaBody: 'false'
|
||||
# BeforeWhile: 'false'
|
||||
IndentBraces: 'false'
|
||||
SplitEmptyFunction: 'false'
|
||||
SplitEmptyRecord: 'false'
|
||||
SplitEmptyNamespace: 'false'
|
||||
BreakBeforeTernaryOperators: 'false'
|
||||
BreakBeforeBinaryOperators: 'false'
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: '0'
|
||||
CompactNamespaces: 'false'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
||||
ContinuationIndentWidth: '2'
|
||||
Cpp11BracedListStyle: 'true'
|
||||
FixNamespaceComments: 'true'
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 2
|
||||
# CaseSensitive: true
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '<[[:alnum:].]+>'
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
IndentWrappedFunctionNames: 'true'
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: '3'
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'false'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'false'
|
||||
SpaceAfterCStyleCast: 'false'
|
||||
SpaceAfterLogicalNot: 'false'
|
||||
SpaceAfterTemplateKeyword: 'false'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeCpp11BracedList: 'false'
|
||||
SpaceBeforeCtorInitializerColon: 'false'
|
||||
SpaceBeforeInheritanceColon: 'true'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: 'true'
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
SpacesBeforeTrailingComments: '1'
|
||||
SpacesInAngles: 'false'
|
||||
SpacesInCStyleCastParentheses: 'false'
|
||||
SpacesInContainerLiterals: 'false'
|
||||
SpacesInParentheses: 'false'
|
||||
SpacesInSquareBrackets: 'false'
|
||||
#SpacesInLineCommentPrefix:
|
||||
# Minimum: 1
|
||||
# Maximum: -1
|
||||
Standard: Cpp11
|
||||
TabWidth: '4'
|
||||
UseTab: Never
|
||||
|
||||
...
|
|
@ -0,0 +1,4 @@
|
|||
service_name: gh-actions
|
||||
gcov_options: \-lp
|
||||
include:
|
||||
- include
|
|
@ -0,0 +1,193 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: amd64-pull
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
image: ubuntu:focal
|
||||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
from_secret: coveralls_token
|
||||
APP_ID:
|
||||
from_secret: appid
|
||||
APP_INSTALLATION:
|
||||
from_secret: appins
|
||||
PK:
|
||||
from_secret: pk
|
||||
|
||||
commands:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- export TRAVIS_BRANCH=$DRONE_BRANCH
|
||||
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
|
||||
- export COVERALLS_PULL_REQUEST=$DRONE_PULL_REQUEST
|
||||
- apt-get -y update
|
||||
- apt-get -y install libasio-dev doxygen mkdocs graphviz zlib1g-dev gcc clang clang-format make cmake python3 python3-pip git openssl libssl-dev jq wget curl
|
||||
- git clone https://github.com/CrowCpp/cpp-coveralls.git
|
||||
- cd cpp-coveralls
|
||||
- pip3 install . --no-input
|
||||
- cd ..
|
||||
- export CC=/usr/bin/gcc
|
||||
- export CXX=/usr/bin/g++
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- coveralls --verbose --exclude-pattern .*/http_parser_merged.h --exclude-pattern .*/TinySHA1.hpp
|
||||
- git clone https://github.com/CrowCpp/crow-clang-format.git
|
||||
- cd crow-clang-format
|
||||
- chmod +x crow-clang-format.sh get-access-token.sh make-jwt.sh
|
||||
- ./crow-clang-format.sh
|
||||
- cd ..
|
||||
- export CC=/usr/bin/clang
|
||||
- export CXX=/usr/bin/clang++
|
||||
- mkdir build-clang
|
||||
- cd build-clang
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: arm64-pull
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
image: ubuntu:focal
|
||||
commands:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- apt-get -y update
|
||||
- apt-get -y install libasio-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake --version
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- export CC=/usr/bin/clang
|
||||
- export CXX=/usr/bin/clang++
|
||||
- mkdir build-clang
|
||||
- cd build-clang
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: amd64-master
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
image: ubuntu:focal
|
||||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
from_secret: coveralls_token
|
||||
GH_REPO_TOKEN:
|
||||
from_secret: repo_token
|
||||
GH_REPO_NAME: crow
|
||||
GH_REPO_REF: github.com/crowcpp/crow.git
|
||||
THEME_REPO_REF: github.com/crowcpp/darxygen.git
|
||||
|
||||
commands:
|
||||
- export DRONE_BUILD_DIR=$PWD
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- export DOXYFILE=$DRONE_BUILD_DIR/Doxyfile
|
||||
- export TRAVIS_BRANCH=$DRONE_REPO_BRANCH
|
||||
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
|
||||
- apt-get -y update
|
||||
- apt-get -y install libasio-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git openssl libssl-dev
|
||||
- pip3 install mkdocs-material mkdocs-redirects pyyaml mkdocs-meta-descriptions-plugin --no-input
|
||||
- git clone https://github.com/CrowCpp/cpp-coveralls.git
|
||||
- cd cpp-coveralls
|
||||
- pip3 install . --no-input
|
||||
- cd ..
|
||||
- export CC=/usr/bin/gcc
|
||||
- export CXX=/usr/bin/g++
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- coveralls --verbose --exclude-pattern .*/http_parser_merged.h --exclude-pattern .*/TinySHA1.hpp
|
||||
- export CC=/usr/bin/clang
|
||||
- export CXX=/usr/bin/clang++
|
||||
- mkdir build-clang
|
||||
- cd build-clang
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- chmod +x scripts/generateDocumentationAndDeploy.sh
|
||||
- ./scripts/generateDocumentationAndDeploy.sh
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: arm64-master
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
image: ubuntu:focal
|
||||
commands:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- apt-get -y update
|
||||
- apt-get -y install libasio-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake --version
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- export CC=/usr/bin/clang
|
||||
- export CXX=/usr/bin/clang++
|
||||
- mkdir build-clang
|
||||
- cd build-clang
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
- make -j4
|
||||
- ctest -V -j4
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
|
@ -0,0 +1,56 @@
|
|||
darxygen/**
|
||||
docs/reference/**
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
example
|
||||
unittest
|
||||
Testing/*
|
||||
include/crow/tags
|
||||
|
||||
*.swp
|
||||
*.gcov
|
||||
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
build
|
||||
|
||||
.directory
|
||||
crow_all.h
|
||||
|
||||
# conan.io
|
||||
build*/
|
||||
|
||||
#Doxygen
|
||||
html/
|
||||
|
||||
#QT-Creator
|
||||
*.config
|
||||
*.creator*
|
||||
*.cflags
|
||||
*.cxxflags
|
||||
*.files
|
||||
*.includes
|
||||
|
||||
#VS-Code
|
||||
.vscode
|
|
@ -0,0 +1,57 @@
|
|||
language: cpp
|
||||
|
||||
cache: ccache
|
||||
|
||||
dist: focal
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "CQRYHWlg/WDu5DBUeDwGo+rPeOofN08DhiLUNlLtZjMWaRyP0Cop1qVaFs8ESOkYiWek2MdpvjZud+7hL+yx2ogvNx4SfHpUMCDKYgcX+YQ9MmYwabvoKq8N6KVXE3lbPp549TonHdDuNCWNKRniNjYtrij5J+IiIiT8/6Txo2p9RWk6YSUTdXJ9YrfuWMtRuF5uo9SHGyujv8pOJKedrwWoSBbHT44jnwfHMVe/C8jgjwlrJ9N3iXOtsG6sj+UTS8vOpL+jpBONEbBfHgSFU57I7IFNdPQbSObpVwG9geOAHT7IQQyQ9hp2AJoFxxVURB5SzqztDDpQ0XIF76vuH9tA/fF2pwDsLRmcLR8JU1TCmQgvnlYD0+Or9S1Dq0tQME5AP+21Hk2zVcGdbgQP7XWix758F0vpOXa4PXw8TmAjP2jKyAMHlzR3icr3+OmKSK3uXMMt2HSMOJQ+JvFxr//DM493i/VGyeY25/zu3A9RstiE+1d82Fi9xKOmMf4smvSkjOgT0b727jqNbNe6CvEKQUmqHabzYRQzUVz6WPVDHBxZP7AiKmZIVQXYnDsVXywStkSoxxY5En6XKpq0GR3bIVtUMORgZPoZi7Jni+/4EckcYH8g9mpsQf9tPRcOZ2WIvt5gqp2MZuwBLBRcbxihuECfBscqdeA0oDU5AZw="
|
||||
- GH_REPO_NAME: crow
|
||||
- DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile
|
||||
- GH_REPO_REF: github.com/crowcpp/crow.git
|
||||
- THEME_REPO_REF: github.com/crowcpp/darxygen.git
|
||||
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libasio-dev
|
||||
- doxygen
|
||||
- mkdocs
|
||||
- graphviz
|
||||
- zlib1g-dev
|
||||
- libssl-dev
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_COMPILER" == "gcc" -a "$TRAVIS_CPU_ARCH" == "amd64" ]; then export PUSH_COVERAGE=ON; fi
|
||||
- if [ "$TRAVIS_BRANCH" == "master" -a "$TRAVIS_PULL_REQUEST" == "false" -a "$PUSH_COVERAGE" == "ON" ]; then export TRAVIS_BUILD_DOCS=ON; pip install mkdocs-material; fi
|
||||
|
||||
install:
|
||||
- if [ "$PUSH_COVERAGE" == "ON" ]; then pip install --user cpp-coveralls; fi
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake --version
|
||||
- cmake .. -DCROW_FEATURES="ssl;compression" -DCROW_AMALGAMATE=ON
|
||||
|
||||
script: make -j4 && ctest -V -j4
|
||||
|
||||
after_success:
|
||||
- cd ..
|
||||
- if [ "$PUSH_COVERAGE" == "ON" ]; then coveralls -i include --exclude-pattern .*/http_parser_merged.h --exclude-pattern .*/TinySHA1.hpp --gcov-options '\-lp'; fi
|
||||
- chmod +x scripts/generateDocumentationAndDeploy.sh
|
||||
- if [ "$TRAVIS_BUILD_DOCS" == "ON" ]; then ./scripts/generateDocumentationAndDeploy.sh; fi
|
|
@ -0,0 +1,169 @@
|
|||
#####################################
|
||||
# Define Project-Wide Settings
|
||||
#####################################
|
||||
cmake_minimum_required(VERSION 3.15.0 FATAL_ERROR)
|
||||
|
||||
# Define the project name and language
|
||||
project(Crow
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
# Make sure Findasio.cmake module is found
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
# Check if Crow is the main project
|
||||
set(CROW_IS_MAIN_PROJECT OFF)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CROW_IS_MAIN_PROJECT ON)
|
||||
endif()
|
||||
|
||||
# Set required C++ standard
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
|
||||
# Default to build type "Release" unless tests are being built
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
if (NOT CROW_BUILD_TESTS)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
else()
|
||||
message(STATUS "No build type selected but tests are being built, default to Debug")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#####################################
|
||||
# Define Options
|
||||
#####################################
|
||||
option(CROW_BUILD_EXAMPLES "Build the examples in the project" ${CROW_IS_MAIN_PROJECT})
|
||||
option(CROW_BUILD_TESTS "Build the tests in the project" ${CROW_IS_MAIN_PROJECT})
|
||||
option(CROW_AMALGAMATE "Combine all headers into one" OFF)
|
||||
option(CROW_INSTALL "Add install step for Crow" ON )
|
||||
|
||||
# Possible values: ssl, compression
|
||||
option(CROW_FEATURES "Enable features extending Crow's abilities" "")
|
||||
|
||||
#####################################
|
||||
# Define Targets
|
||||
#####################################
|
||||
add_library(Crow INTERFACE)
|
||||
add_library(Crow::Crow ALIAS Crow)
|
||||
|
||||
target_include_directories(Crow
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE Crow_Source_Files ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
|
||||
target_sources(Crow INTERFACE ${Crow_Source_Files})
|
||||
|
||||
find_package(asio REQUIRED)
|
||||
|
||||
target_link_libraries(Crow
|
||||
INTERFACE
|
||||
asio::asio
|
||||
)
|
||||
|
||||
target_compile_definitions(Crow INTERFACE "")
|
||||
|
||||
if("compression" IN_LIST CROW_FEATURES)
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_link_libraries(Crow INTERFACE ZLIB::ZLIB)
|
||||
target_compile_definitions(Crow INTERFACE CROW_ENABLE_COMPRESSION)
|
||||
endif()
|
||||
|
||||
if("ssl" IN_LIST CROW_FEATURES)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(Crow INTERFACE OpenSSL::SSL)
|
||||
target_compile_definitions(Crow INTERFACE CROW_ENABLE_SSL)
|
||||
endif()
|
||||
|
||||
if(CROW_AMALGAMATE)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
|
||||
COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/scripts/merge_all.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/include/crow/*.h ${CMAKE_CURRENT_SOURCE_DIR}/include/crow/middlewares/*.h
|
||||
)
|
||||
|
||||
add_custom_target(crow_amalgamated ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h)
|
||||
endif()
|
||||
|
||||
# Examples
|
||||
if(CROW_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
if(NOT MSVC AND CROW_BUILD_TESTS)
|
||||
if(NOT "compression" IN_LIST CROW_FEATURES)
|
||||
message(STATUS "Compression tests are omitted. (Configure with CROW_FEATURES containing 'compression' to enable them)")
|
||||
endif()
|
||||
if(NOT "ssl" IN_LIST CROW_FEATURES)
|
||||
message(STATUS "SSL tests are omitted. (Configure with CROW_FEATURES containing 'ssl' to enable them)")
|
||||
else()
|
||||
add_test(NAME ssl_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/ssl/ssltest)
|
||||
endif()
|
||||
|
||||
add_subdirectory(tests)
|
||||
enable_testing()
|
||||
add_test(NAME crow_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/unittest)
|
||||
add_test(NAME template_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/template/test.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/template)
|
||||
endif()
|
||||
|
||||
#####################################
|
||||
# Install Files
|
||||
#####################################
|
||||
if(CROW_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS Crow EXPORT CrowTargets)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
install(EXPORT CrowTargets
|
||||
FILE CrowTargets.cmake
|
||||
NAMESPACE Crow::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crow"
|
||||
)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/CrowConfig.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CrowConfig.cmake"
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crow"
|
||||
)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findasio.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CrowConfig.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crow"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
set(CPACK_PACKAGE_NAME "Crow")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "CrowCpp")
|
||||
set(CPACK_PACKAGE_VENDOR "CrowCpp")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "A Fast and Easy to use C++ microframework for the web.")
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://crowcpp.org")
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEBUG OFF)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libasio-dev")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "libdevel")
|
||||
|
||||
include(CPack)
|
||||
|
||||
#####################################
|
||||
# Uninstall Files
|
||||
#####################################
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
|
@ -0,0 +1,32 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2014-2017, ipkn
|
||||
2020-2022, CrowCpp
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The Crow logo and other graphic material (excluding third party logos) used are under exclusive Copyright (c) 2021-2022, Farook Al-Sammarraie (The-EDev), All rights reserved.
|
|
@ -0,0 +1,209 @@
|
|||
<p align="center"><img src="https://raw.githubusercontent.com/CrowCpp/Crow/master/docs/assets/crowlogo_dual_color.svg" width=600></p>
|
||||
|
||||
<h4 align="center">A Fast and Easy to use microframework for the web.</h4>
|
||||
<p align="center">
|
||||
<a href="https://github.com/CrowCpp/Crow/actions/workflows/build_and_test.yml"><img src="https://github.com/CrowCpp/Crow/actions/workflows/build_and_test.yml/badge.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://coveralls.io/github/CrowCpp/Crow?branch=master"><img src="https://coveralls.io/repos/github/CrowCpp/Crow/badge.svg?branch=master" alt="Coverage Status"></a>
|
||||
<a href="https://crowcpp.org"><img src="https://img.shields.io/badge/-Documentation-informational" alt="Documentation"></a>
|
||||
<a href="https://gitter.im/crowfork/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"><img src="https://img.shields.io/gitter/room/crowfork/community?color=8DDEC5&logo=gitter" alt="Gitter"></a>
|
||||
<a href="https://opencollective.com/crow"><img alt="Open Collective" src="https://img.shields.io/opencollective/all/crow?label=Support%20Crow&logo=opencollective"></a>
|
||||
</p>
|
||||
|
||||
## Description
|
||||
|
||||
Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.
|
||||
|
||||
### Features
|
||||
- Easy Routing (similar to flask).
|
||||
- Type-safe Handlers.
|
||||
- Blazingly fast (see [this benchmark](https://github.com/ipkn/crow-benchmark) and [this benchmark](https://github.com/guteksan/REST-CPP-benchmark)).
|
||||
- Built in JSON support.
|
||||
- [Mustache](http://mustache.github.io/) based templating library (`crow::mustache`).
|
||||
- Header only library (single header file available).
|
||||
- Middleware support for extensions.
|
||||
- HTTP/1.1 and Websocket support.
|
||||
- Multi-part request and response support.
|
||||
- Uses modern C++ (11/14)
|
||||
|
||||
### Still in development
|
||||
- [Async support](https://github.com/CrowCpp/Crow/issues/258)
|
||||
- [HTTP/2 support](https://github.com/crowcpp/crow/issues/8)
|
||||
|
||||
## Documentation
|
||||
Available [here](https://crowcpp.org).
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> If you are using Crow v0.3, then you have to put `#define CROW_MAIN` at the top of one and only one source file.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Hello World
|
||||
```cpp
|
||||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")([](){
|
||||
return "Hello world";
|
||||
});
|
||||
|
||||
app.port(18080).multithreaded().run();
|
||||
}
|
||||
```
|
||||
|
||||
#### JSON Response
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
crow::json::wvalue x({{"message", "Hello, World!"}});
|
||||
x["message2"] = "Hello, World.. Again!";
|
||||
return x;
|
||||
});
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
```cpp
|
||||
CROW_ROUTE(app,"/hello/<int>")
|
||||
([](int count){
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
```
|
||||
Handler arguments type check at compile time
|
||||
```cpp
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
CROW_ROUTE(app,"/another/<int>")
|
||||
([](int a, int b){
|
||||
return crow::response(500);
|
||||
});
|
||||
```
|
||||
|
||||
#### Handling JSON Requests
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods("POST"_method)
|
||||
([](const crow::request& req){
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(crow::status::BAD_REQUEST); // same as crow::response(400)
|
||||
int sum = x["a"].i()+x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
```
|
||||
|
||||
More examples can be found [here](https://github.com/crowcpp/crow/tree/master/examples).
|
||||
|
||||
## Setting Up / Building
|
||||
Available [here](https://crowcpp.org/master/getting_started/setup).
|
||||
|
||||
## Disclaimer
|
||||
CrowCpp/Crow is a project based on ipkn/crow. Neither CrowCpp, it's members, or this project have been associated with, or endorsed or supported by ipkn (Jaeseung Ha) in any way. We do use ipkn/crow's source code under the BSD-3 clause license and sometimes refer to the public comments available on the github repository. But we do not in any way claim to be associated with or in contact with ipkn (Jaeseung Ha) regarding CrowCpp or CrowCpp/Crow
|
||||
|
||||
## Attributions
|
||||
Crow has incorporated the following libraries into its source.
|
||||
|
||||
http-parser (used for converting http strings to crow::request objects)
|
||||
|
||||
https://github.com/nodejs/http-parser
|
||||
|
||||
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
|
||||
Igor Sysoev.
|
||||
|
||||
Additional changes are licensed under the same terms as NGINX and
|
||||
copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
|
||||
qs_parse (used for reading query string parameters)
|
||||
|
||||
https://github.com/bartgrantham/qs_parse
|
||||
|
||||
Copyright (c) 2010 Bart Grantham
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
|
||||
TinySHA1 (used during the websocket handshake, not for security)
|
||||
|
||||
https://github.com/mohaps/TinySHA1
|
||||
|
||||
TinySHA1 - a header only implementation of the SHA1 algorithm. Based on the
|
||||
implementation in boost::uuid::details
|
||||
|
||||
Copyright (c) 2012-22 SAURAV MOHAPATRA mohaps@gmail.com
|
||||
Permission to use, copy, modify, and distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright
|
||||
notice and this permission notice appear in all copies.
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
|
||||
Catch2 (used only in unit tests, not part of the actual framework)
|
||||
|
||||
https://github.com/catchorg/Catch2
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,47 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
get_filename_component(CROW_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CROW_CMAKE_DIR})
|
||||
find_dependency(asio)
|
||||
list(REMOVE_AT CMAKE_MODULE_PATH -1)
|
||||
|
||||
set(CROW_INSTALLED_FEATURES "@CROW_FEATURES@")
|
||||
|
||||
if(NOT DEFINED CROW_FEATURES)
|
||||
set(CROW_FEATURES ${CROW_INSTALLED_FEATURES})
|
||||
endif()
|
||||
|
||||
if("compression" IN_LIST CROW_FEATURES)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if("ssl" IN_LIST CROW_FEATURES)
|
||||
find_dependency(OpenSSL)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CrowTargets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
|
||||
get_target_property(_CROW_ILL Crow::Crow INTERFACE_LINK_LIBRARIES)
|
||||
get_target_property(_CROW_ICD Crow::Crow INTERFACE_COMPILE_DEFINITIONS)
|
||||
|
||||
list(REMOVE_ITEM _CROW_ILL "ZLIB::ZLIB" "OpenSSL::SSL")
|
||||
list(REMOVE_ITEM _CROW_ICD "CROW_ENABLE_SSL" "CROW_ENABLE_COMPRESSION")
|
||||
|
||||
if("compression" IN_LIST CROW_FEATURES)
|
||||
list(APPEND _CROW_ILL "ZLIB::ZLIB")
|
||||
list(APPEND _CROW_ICD "CROW_ENABLE_COMPRESSION")
|
||||
endif()
|
||||
|
||||
if("ssl" IN_LIST CROW_FEATURES)
|
||||
list(APPEND _CROW_ILL "OpenSSL::SSL")
|
||||
list(APPEND _CROW_ICD "CROW_ENABLE_SSL")
|
||||
endif()
|
||||
|
||||
set_target_properties(Crow::Crow PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "${_CROW_ICD}"
|
||||
INTERFACE_LINK_LIBRARIES "${_CROW_ILL}"
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
#Findasio.cmake
|
||||
#
|
||||
# Finds the asio library
|
||||
#
|
||||
# from https://think-async.com/Asio/
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# ASIO_FOUND
|
||||
# ASIO_INCLUDE_DIR
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# asio::asio
|
||||
#
|
||||
|
||||
find_package(Threads QUIET)
|
||||
if (Threads_FOUND)
|
||||
find_path(ASIO_INCLUDE_DIR asio.hpp)
|
||||
|
||||
mark_as_advanced(ASIO_FOUND ASIO_INCLUDE_DIR)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(asio
|
||||
FOUND_VAR ASIO_FOUND
|
||||
REQUIRED_VARS ASIO_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(ASIO_FOUND AND NOT TARGET asio::asio)
|
||||
add_library(asio::asio INTERFACE IMPORTED)
|
||||
target_include_directories(asio::asio
|
||||
INTERFACE
|
||||
${ASIO_INCLUDE_DIR}
|
||||
)
|
||||
target_compile_definitions(asio::asio
|
||||
INTERFACE
|
||||
"ASIO_STANDALONE"
|
||||
)
|
||||
target_link_libraries(asio::asio
|
||||
INTERFACE
|
||||
Threads::Threads
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
if(asio_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "asio requires Threads, which couldn't be found.")
|
||||
elseif(asio_FIND_QUIETLY)
|
||||
message(STATUS "asio requires Threads, which couldn't be found.")
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,22 @@
|
|||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Compiler options with hardening flags
|
||||
|
||||
function(add_warnings_optimizations target_name)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name}
|
||||
PRIVATE
|
||||
/W4
|
||||
/permissive-
|
||||
$<$<CONFIG:RELEASE>:/O2 /Ob2>
|
||||
$<$<CONFIG:MINSIZEREL>:/O1 /Ob1>
|
||||
$<$<CONFIG:RELWITHDEBINFO>:/Zi /O2 /Ob1>
|
||||
$<$<CONFIG:DEBUG>:/Zi /Ob0 /Od /RTC1>
|
||||
)
|
||||
else()
|
||||
target_compile_options(${target_name}
|
||||
PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
$<$<CONFIG:RELEASE>:-O2>
|
||||
$<$<CONFIG:DEBUG>:-O0 -g -p -pg>
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
|
@ -0,0 +1,127 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="900"
|
||||
height="400"
|
||||
viewBox="0 0 238.12499 105.83333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="crowlogo.svg"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
inkscape:export-filename="/home/farook/vaiossd/Pictures/CrowLogo.png"
|
||||
inkscape:export-xdpi="324.99619"
|
||||
inkscape:export-ydpi="324.99619"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.49497475"
|
||||
inkscape:cx="196.97975"
|
||||
inkscape:cy="329.30973"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
inkscape:pagecheckerboard="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(2.890729,-78.735456)">
|
||||
<path
|
||||
id="rect51232"
|
||||
style="stroke-width:1.621;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="m 86.263122,129.89227 c -1.679885,0.94306 -0.970661,50.071 -0.970661,50.071 h 8.67359 c 0,0 -1.146978,-23.39877 0.97066,-22.01299 5.738329,3.75514 18.678879,22.04105 18.678879,22.04105 l 10.82099,0.0233 c 0,0 -22.33706,-23.42311 -21.33927,-25.78171 0.99777,-2.35858 13.45284,-3.56535 14.28699,-5.61353 0.83414,-2.04817 0.74681,-7.53026 0.0119,-9.332 -0.73488,-1.80175 -17.951097,-8.50923 -22.459538,-9.39514 -4.508449,-0.88592 -6.875855,-1.00922 -8.67359,0 z m 8.673589,7.89629 c 0.435027,-0.21177 12.515509,4.68197 12.527659,5.49714 0.0113,0.76226 -12.065069,5.41359 -12.527659,5.04327 -0.327731,-0.26235 -0.43502,-10.32863 0,-10.54041 z"
|
||||
sodipodi:nodetypes="sccscczzzzszssz" />
|
||||
<path
|
||||
id="path55748"
|
||||
style="stroke-width:1.50035;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="m 214.40962,132.10711 c 0,0 -6.19646,30.36708 -7.43712,30.33831 -1.22676,-0.0284 -7.40908,-29.27248 -7.40908,-29.27248 l -7.84322,0.0207 c 0,0 -4.25108,28.63573 -6.44505,28.48646 -1.9928,-0.13558 -7.20545,-29.39322 -7.20545,-29.39322 l -7.79639,1.91192 11.03502,45.01483 7.88892,1.48481 c 0,0 5.4369,-26.82435 6.66418,-26.78989 1.22731,0.0345 7.42065,26.61016 7.42065,26.61016 l 7.88891,-1.4848 11.03501,-45.01483 z"
|
||||
sodipodi:nodetypes="csccscccczcccc" />
|
||||
<path
|
||||
id="rect53235"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.65014;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 128.1657,156.63351 c 0.0252,15.02476 7.0482,20.22602 17.35625,20.23066 10.30804,0.005 17.61679,-6.50768 17.8414,-20.03302 0.2246,-13.52536 -8.11076,-20.48288 -17.43254,-20.41622 -9.32178,0.0666 -17.79031,5.1938 -17.76511,20.21858 z"
|
||||
sodipodi:nodetypes="zzzzz" />
|
||||
<path
|
||||
style="color:#000000;fill:#000000;stroke-width:0.669208;-inkscape-stroke:none"
|
||||
d="m 75.361766,164.31848 c -1.522517,0.0929 -3.225106,0.23753 -5.059422,0.41007 -14.861309,1.39786 -38.370023,4.6261 -44.831721,-3.01508 -7.259264,-8.58431 -5.992766,-15.11802 -3.7379,-29.04552 2.254865,-13.92751 11.768175,-32.3183 23.453693,-33.61763 11.685519,-1.299327 24.289435,-0.14247 28.381026,1.21309 L 77.417321,85.791987 C 62.828148,82.27312 56.039475,80.867702 37.328615,83.440263 18.617756,86.012824 9.3053305,108.67011 6.7920712,129.55115 c -2.5132504,20.88098 -0.630659,37.59767 5.7039948,44.13904 6.334653,6.54136 43.941662,8.861 64.480651,5.0425 z"
|
||||
id="path830"
|
||||
sodipodi:nodetypes="csszzcczzzcc" />
|
||||
<g
|
||||
id="g10108"
|
||||
transform="matrix(0.45544811,0,0,0.45544811,-15.928506,65.153935)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.330875px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150.8378,207.05744 -2.91031,2.49455 c 0,0 3.0291,2.85092 4.09819,4.09818 1.0691,1.24727 6.71155,10.86915 7.1867,12.53216 0.47516,1.66304 2.49456,2.01942 4.0388,3.74182 1.54425,1.72244 4.63273,3.50426 2.37577,5.28609 -2.25698,1.78184 2.13819,0.77214 2.0194,-2.01939 -0.11881,-2.79153 4.15758,5.28609 1.12848,6.35518 -3.02909,1.06909 1.54424,1.12848 2.13819,0.11879 0.59393,-1.00969 0.89091,-2.85092 0.65334,-4.87032 -0.23758,-2.01942 -3.32608,-5.76124 -4.03881,-6.41455 -0.71272,-0.65336 3.38547,-0.41578 4.51395,1.06908 1.12851,1.48483 1.24728,1.95999 0.41576,3.20727 -0.83151,1.2473 3.26668,-1.00969 1.30669,-3.0291 -1.96002,-2.01938 -0.47517,-1.84122 -3.2073,-3.14786 -2.73213,-1.3067 -3.0291,-2.13822 -5.34546,-2.01941 -2.31637,0.1188 -14.37339,-17.40249 -14.37339,-17.40249 z"
|
||||
id="path15574" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.330875px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 169.84392,200.40528 -3.44486,2.13819 7.89943,8.84975 c 0,0 4.09819,2.96969 3.26668,6.05817 -0.83152,3.08849 -2.96971,4.21701 -2.96971,4.21701 0,0 0.29696,0.53452 1.24727,0.53452 0.95031,0 2.13819,-3.08847 3.86063,-2.13817 1.72242,0.95031 4.15758,3.32604 4.57334,4.81091 0.41576,1.48487 0.11881,1.84123 0.35636,2.13819 0.23758,0.29698 1.48486,0.29698 1.30667,-0.95031 -0.17817,-1.24727 0.83152,-0.0594 1.0691,1.00969 0.23759,1.0691 1.00969,2.96972 0.8909,4.81093 -0.11879,1.84121 1.0691,3.62305 1.00972,0.95031 -0.0594,-2.67272 0.53454,-5.58303 -0.83151,-8.0182 -1.36607,-2.43517 -1.96002,-5.70182 0.17816,-3.56365 2.1382,2.13817 3.80124,2.37578 4.39518,3.92 0.59394,1.54427 0.53455,1.90061 1.24727,1.90061 0.71273,0 0.29697,-2.13818 -1.0097,-3.62305 -1.30666,-1.48482 -1.36607,-2.96969 -5.10789,-4.8703 -3.74184,-1.90061 -5.76124,-3.80122 -9.74063,-7.42426 -3.97943,-3.62305 -8.19641,-10.75034 -8.19641,-10.75034 z"
|
||||
id="path15576" />
|
||||
<path
|
||||
style="fill:#fefefe;fill-opacity:1;stroke:none;stroke-width:0.330875px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 156.9048,221.62443 c 1.16656,2.08 2.12376,3.91453 2.30758,4.5579 0.47516,1.66304 2.49456,2.01942 4.0388,3.74182 1.54425,1.72244 4.63273,3.50426 2.37577,5.28609 -2.25698,1.78184 2.13819,0.77214 2.0194,-2.01939 -0.11881,-2.79153 4.15758,5.28609 1.12848,6.35518 -3.02909,1.06909 1.54424,1.12848 2.13819,0.11879 0.59393,-1.00969 0.89091,-2.85092 0.65334,-4.87032 -0.23758,-2.01942 -3.32608,-5.76124 -4.03881,-6.41455 -0.71272,-0.65336 3.38547,-0.41578 4.51395,1.06908 1.12851,1.48483 1.24728,1.95999 0.41576,3.20727 -0.83151,1.2473 3.26668,-1.00969 1.30669,-3.0291 -1.96002,-2.01938 -0.47517,-1.84122 -3.2073,-3.14786 -2.73213,-1.3067 -3.0291,-2.13821 -5.34546,-2.01941 -0.63844,0.0329 -2.01688,-1.2746 -3.67887,-3.1858 z"
|
||||
id="path16765"
|
||||
sodipodi:nodetypes="cssssssssssssscc" />
|
||||
<path
|
||||
style="fill:#fefefe;fill-opacity:1;stroke:none;stroke-width:0.330875px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 178.26789,219.71627 c 0.42673,8.5e-4 0.639,0.0327 1.06396,0.17299 1.2402,0.40914 4.6336,3.87481 4.94485,4.98647 0.41576,1.48486 0.11881,1.84122 0.35636,2.13817 0.23758,0.297 1.48486,0.297 1.30667,-0.9503 -0.17817,-1.24726 0.83152,-0.0594 1.0691,1.00969 0.23759,1.06909 1.00969,2.96971 0.8909,4.81094 -0.11879,1.8412 0.68891,3.56455 1.00972,0.9503 0.32561,-2.65347 0.53454,-5.58302 -0.83151,-8.01819 -1.36607,-2.43517 -1.96002,-5.70184 0.17816,-3.56366 2.1382,2.13818 3.80124,2.37578 4.39518,3.92 0.59394,1.54427 0.53455,1.90061 1.24727,1.90061 0.71273,0 0.29697,-2.13818 -1.0097,-3.62304 -1.30666,-1.48483 -1.03947,-2.80434 -4.78129,-4.70495 z"
|
||||
id="path16767"
|
||||
sodipodi:nodetypes="csssssssssssscc" />
|
||||
<path
|
||||
id="path7288"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.24079px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 540.33594,383.94922 c -14.94254,-0.20305 -43.68196,4.64832 -48.69336,13.83594 -5.34554,9.80012 -1.29297,9.71484 -1.29297,9.71484 0,0 0.8077,-0.28159 1.55469,-0.50977 0.19731,-0.21631 0.20586,-0.45983 0.28515,-0.63085 0.23859,-0.51464 0.80638,-1.58001 2.79297,-2.7754 2.5894,-1.55814 7.24439,-2.90119 13.8125,-2.49804 h 0.002 c 6.55107,0.40265 21.02682,2.55398 33.91601,4.69726 12.89038,2.14345 23.86133,3.91992 23.86133,3.91992 0.37304,0.0474 1.19328,0.89199 0.71094,2.23047 -0.0485,0.27141 -0.53901,-0.0127 -1.7793,-0.82421 -0.54883,-0.18974 -10.06922,-2.20171 -22.95117,-4.34376 -12.88196,-2.14208 -27.40482,-4.39181 -33.82227,-4.68359 -6.3998,-0.2911 -10.40495,0.97984 -12.66015,2.64453 -1.2761,0.94199 -1.89329,1.77426 -1.85938,2.09961 0.0339,0.32536 6.33984,6.9961 6.33984,6.9961 0,0 39.2489,7.22097 53.78711,13.31054 2.11733,0.88687 19.62584,25.33779 21.20508,40.50586 1.57693,15.14555 0.73641,25.47551 -5.5,34.38477 -6.23648,8.90914 -24.9456,32.96326 -41.87304,43.65429 -16.92741,10.69108 -63.25627,187.98549 -56.12891,202.24024 7.12736,14.25468 13.3636,0.88961 12.47265,9.79883 -0.89091,8.90914 -9.80021,42.76481 -11.58203,57.01953 -1.78185,14.25475 1.78215,49.89128 -0.89062,59.6914 -2.67273,9.80017 -11.58203,39.19922 -11.58203,39.19922 l 92.65625,4.45508 21.38086,-64.14648 c 0,0 22.27428,-61.47279 25.83789,-66.81836 3.56368,-5.34562 49.00102,-8.01824 69.49218,-17.81836 3.20474,-1.53271 18.64775,-17.60907 18.84961,-7.07031 0.17802,9.29552 -6.6987,34.15384 -4.14565,34.73511 2.20054,0.50101 16.46827,-42.73429 18.39175,-39.29371 3.05639,5.46716 -2.73735,30.6253 0.13898,29.41075 2.4247,-1.02385 19.58949,-49.49495 22.89227,-55.19981 2.2221,-3.83811 11.22532,27.39209 14.96875,19.81055 2.46693,-4.99624 -0.15857,-51.76775 1.44531,-48.14844 2.85449,6.44179 5.94883,0.0231 5.67969,-3.12891 -0.98918,-11.59328 4.17916,10.73996 5.24805,8.9375 1.29917,-2.19084 -0.53332,-17.18074 0.68554,-19.54101 0.80519,-1.55891 2.51297,0.22857 3.25391,-1.46485 0.6006,-1.37268 -1.91169,-20.09044 -1.33399,-21.40625 3.0333,-6.90856 9.07107,-8.76946 10.14454,-14.35156 2.29625,-11.94062 5.07988,-39.1127 -14.36133,-63.50781 -1.28504,-1.61242 -0.16578,-3.45865 -0.83789,-8.92188 -1.67762,-13.63237 -6.93933,-21.21375 -10.07422,-25.91601 -7.12736,-10.69108 -8.5824,-5.89177 -19.27344,-20.14649 -10.691,-14.25467 -17.81701,-8.0172 -16.03516,-21.38086 0.26079,-1.95428 2.37926,-10.46495 1.21875,-18.82617 -0.56277,-4.05418 -5.25363,-9.4157 -4.88086,-11.41015 0.64154,-3.43253 8.23586,8.56414 7.84766,5.25195 -0.66187,-5.64741 -6.76676,-9.43312 -7.61719,-13.65625 -0.55237,-2.74326 7.33545,1.66069 5.45313,-0.70703 -5.51165,-6.9329 -9.56828,-14.28856 -11.54883,-19.94727 -6.23649,-17.81835 -27.89239,-56.52498 -51.94727,-63.65234 -24.05476,-7.12736 -32.54194,-4.6186 -54.3457,-3.3418 -4.23152,0.24794 -11.8782,2.58634 -13.83203,2.52734 -5.34452,-0.16138 -20.6314,-4.07444 -32.68359,-4.89453 -0.86243,-0.0587 -1.7968,-0.0958 -2.79297,-0.10937 z"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-3.6166667e-4,0)"
|
||||
sodipodi:nodetypes="ssccssssscccssszcsssssssscccssssssssscsssssscsscssssssscssss" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.926042;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 158.69265,109.04723 c 0.35531,0.23898 0.7365,0.57494 1.29845,0.562 0.56203,-0.0124 2.14472,-0.58138 2.12536,-1.29846 -0.0198,-0.71706 -0.42633,-1.33717 -0.89791,-1.55039 -0.47157,-0.21319 -1.20917,-0.14781 -1.58972,-0.0767 -0.38061,0.0708 -1.00073,0.58738 -1.19454,0.87807 -0.19379,0.29064 -0.097,1.24674 0.25836,1.48577 z"
|
||||
id="path19231"
|
||||
sodipodi:nodetypes="scssccs" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.572904;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path871"
|
||||
cx="80.292877"
|
||||
cy="175.08554"
|
||||
rx="1.108071"
|
||||
ry="0.6248076"
|
||||
transform="rotate(-31.358267)" />
|
||||
<path
|
||||
style="fill:none;stroke:none;stroke-width:0.328292px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 132.43754,109.49047 c 0,0 10.60751,1.65006 13.43618,4.71448 2.82867,3.06438 6.12877,5.65732 5.65733,9.66459 -0.47145,4.00727 0.94289,6.60022 -0.70716,8.95747 -1.65006,2.3572 -6.60023,8.72171 -11.07895,11.55038 -4.47873,2.82866 -16.7363,49.73742 -14.85052,53.50898 1.88578,3.77155 3.53585,0.23571 3.30012,2.59295 -0.23572,2.3572 -2.59295,11.31467 -3.06439,15.08622 -0.47145,3.77156 0.47144,13.20044 -0.23573,15.79339 -0.70716,2.59296 -3.06438,10.37178 -3.06438,10.37178 l 24.51512,1.17861 5.65733,-16.972 c 0,0 5.89306,-16.26482 6.83595,-17.67917 0.94289,-1.41436 12.96474,-2.1215 18.38635,-4.71445 0.84791,-0.40553 4.93432,-4.65888 4.98774,-1.87049 0.0471,2.45943 -3.95449,11.96428 -3.15527,12.18359 0.82493,0.22636 5.15876,-10.59591 5.66768,-9.6856 0.80868,1.44652 -0.54345,8.24275 0.5449,7.44472 1.62116,-1.18872 5.93159,-16.46315 6.80546,-17.97256 0.58792,-1.0155 2.97009,7.24758 3.96054,5.24162 0.6527,-1.32192 -0.0419,-13.6966 0.38246,-12.739 0.75524,1.70439 1.57372,0.006 1.50251,-0.82797 -0.26172,-3.06738 1.10602,2.84123 1.38883,2.36433 0.34375,-0.57966 -0.14126,-4.54571 0.18124,-5.1702 0.21303,-0.41246 0.66469,0.0609 0.86074,-0.38715 0.15891,-0.36319 -0.50569,-5.31572 -0.35283,-5.66386 0.80255,-1.82789 2.4,-2.32012 2.68401,-3.79704 0.60756,-3.15929 1.34389,-10.34871 -3.79993,-16.80325 -0.34,-0.42662 0.79779,-1.37184 0.66735,-1.86932 -1.28394,-4.89681 -1.93105,-7.4271 -2.76048,-8.67124 -1.88577,-2.82867 -3.06439,-0.23572 -5.89306,-4.00727 -2.82866,-3.77156 -4.71445,-2.12153 -4.243,-5.65734 0.069,-0.51707 0.62947,-2.76876 0.32243,-4.98099 -0.1489,-1.07268 -1.38955,-2.49118 -1.29092,-3.01888 0.16974,-0.9082 2.17877,2.26606 2.07605,1.38971 -0.17511,-1.49421 -1.79038,-2.49582 -2.01539,-3.61319 -0.14615,-0.72582 1.94088,0.43924 1.44286,-0.18721 -1.45829,-1.83434 -2.53158,-3.78032 -3.0556,-5.27752 -1.65007,-4.71445 -7.37976,-14.95582 -13.74427,-16.8416 -6.3645,-1.88578 -8.61013,-1.22178 -14.37905,-0.88396 -3.8114,0.22318 -7.79232,-0.31957 -12.30742,-0.62679 -3.65095,-0.24842 -12.20772,1.03929 -13.62205,3.63224 -1.41434,2.59296 -0.34253,2.57021 -0.34253,2.57021 0,0 0.58139,-0.20655 0.70839,-0.20834 0.60542,-0.009 1.99136,1.88112 1.99136,1.88112 z"
|
||||
id="path3379"
|
||||
sodipodi:nodetypes="csssssssscccsssssssssssssssssssssssssssssscsc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="900"
|
||||
height="400"
|
||||
viewBox="0 0 238.12499 105.83333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="crowlogo_inverted.svg"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
inkscape:export-filename="/home/farook/vaiossd/Pictures/CrowLogo.png"
|
||||
inkscape:export-xdpi="324.99619"
|
||||
inkscape:export-ydpi="324.99619"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="281.42857"
|
||||
inkscape:cy="511.42857"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
inkscape:pagecheckerboard="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(2.890729,-78.735456)">
|
||||
<path
|
||||
id="rect51232"
|
||||
style="stroke-width:1.621;stroke-linecap:round;stroke-linejoin:round;fill:#ffffff;fill-opacity:1"
|
||||
d="m 86.263122,129.89227 c -1.679885,0.94306 -0.970661,50.071 -0.970661,50.071 h 8.67359 c 0,0 -1.146978,-23.39877 0.97066,-22.01299 5.738329,3.75514 18.678879,22.04105 18.678879,22.04105 l 10.82099,0.0233 c 0,0 -22.33706,-23.42311 -21.33927,-25.78171 0.99777,-2.35858 13.45284,-3.56535 14.28699,-5.61353 0.83414,-2.04817 0.74681,-7.53026 0.0119,-9.332 -0.73488,-1.80175 -17.951097,-8.50923 -22.459538,-9.39514 -4.508449,-0.88592 -6.875855,-1.00922 -8.67359,0 z m 8.673589,7.89629 c 0.435027,-0.21177 12.515509,4.68197 12.527659,5.49714 0.0113,0.76226 -12.065069,5.41359 -12.527659,5.04327 -0.327731,-0.26235 -0.43502,-10.32863 0,-10.54041 z"
|
||||
sodipodi:nodetypes="sccscczzzzszssz" />
|
||||
<path
|
||||
id="path55748"
|
||||
style="stroke-width:1.50035;stroke-linecap:round;stroke-linejoin:round;fill:#ffffff;fill-opacity:1"
|
||||
d="m 214.40962,132.10711 c 0,0 -6.19646,30.36708 -7.43712,30.33831 -1.22676,-0.0284 -7.40908,-29.27248 -7.40908,-29.27248 l -7.84322,0.0207 c 0,0 -4.25108,28.63573 -6.44505,28.48646 -1.9928,-0.13558 -7.20545,-29.39322 -7.20545,-29.39322 l -7.79639,1.91192 11.03502,45.01483 7.88892,1.48481 c 0,0 5.4369,-26.82435 6.66418,-26.78989 1.22731,0.0345 7.42065,26.61016 7.42065,26.61016 l 7.88891,-1.4848 11.03501,-45.01483 z"
|
||||
sodipodi:nodetypes="csccscccczcccc" />
|
||||
<path
|
||||
id="rect53235"
|
||||
style="fill:none;stroke:#fefefe;stroke-width:8.65014;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 128.1657,156.63351 c 0.0252,15.02476 7.0482,20.22602 17.35625,20.23066 10.30804,0.005 17.61679,-6.50768 17.8414,-20.03302 0.2246,-13.52536 -8.11076,-20.48288 -17.43254,-20.41622 -9.32178,0.0666 -17.79031,5.1938 -17.76511,20.21858 z"
|
||||
sodipodi:nodetypes="zzzzz" />
|
||||
<path
|
||||
style="color:#000000;fill:#ffffff;stroke-width:0.669208;-inkscape-stroke:none;fill-opacity:1"
|
||||
d="m 75.361766,164.31848 c -1.522517,0.0929 -3.225106,0.23753 -5.059422,0.41007 -14.861309,1.39786 -38.370023,4.6261 -44.831721,-3.01508 -7.259264,-8.58431 -5.992766,-15.11802 -3.7379,-29.04552 2.254865,-13.92751 11.768175,-32.3183 23.453693,-33.61763 11.685519,-1.299327 24.289435,-0.14247 28.381026,1.21309 L 77.417321,85.791987 C 62.828148,82.27312 56.039475,80.867702 37.328615,83.440263 18.617756,86.012824 9.3053305,108.67011 6.7920712,129.55115 c -2.5132504,20.88098 -0.630659,37.59767 5.7039948,44.13904 6.334653,6.54136 43.941662,8.861 64.480651,5.0425 z"
|
||||
id="path830"
|
||||
sodipodi:nodetypes="csszzcczzzcc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.150696px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 52.770285,159.45785 -1.325495,1.13614 c 0,0 1.379598,1.29845 1.866513,1.86651 0.486919,0.56807 3.056762,4.95033 3.273169,5.70775 0.21641,0.75743 1.136142,0.91974 1.839463,1.7042 0.703326,0.78449 2.109969,1.59601 1.08204,2.40754 -1.027937,0.81154 0.973835,0.35167 0.919732,-0.91972 -0.05411,-1.2714 1.893562,2.40754 0.513964,2.89445 -1.379593,0.48692 0.703322,0.51397 0.973835,0.0541 0.270504,-0.45986 0.405763,-1.29844 0.297562,-2.21817 -0.108205,-0.91974 -1.514856,-2.62395 -1.839468,-2.9215 -0.324607,-0.29757 1.541906,-0.18936 2.05587,0.48691 0.513978,0.67627 0.568071,0.89268 0.189357,1.46075 -0.37871,0.56808 1.487803,-0.45986 0.59513,-1.3796 -0.892688,-0.91972 -0.216416,-0.83858 -1.460759,-1.43369 -1.244344,-0.59513 -1.379598,-0.97385 -2.43458,-0.91973 -1.054986,0.0541 -6.546333,-7.92594 -6.546333,-7.92594 z"
|
||||
id="path15574" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.150696px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 61.426586,156.42814 -1.568955,0.97384 3.597781,4.0306 c 0,0 1.866513,1.35254 1.487803,2.75918 -0.378714,1.40665 -1.352549,1.92063 -1.352549,1.92063 0,0 0.13525,0.24344 0.568067,0.24344 0.432817,0 0.973835,-1.40663 1.758317,-0.97382 0.784473,0.43282 1.893562,1.51484 2.082919,2.19112 0.189357,0.67628 0.05411,0.83858 0.162303,0.97383 0.108206,0.13526 0.676277,0.13526 0.595121,-0.43281 -0.08115,-0.56807 0.378714,-0.0271 0.486919,0.45986 0.10821,0.48692 0.459862,1.35255 0.405759,2.19113 -0.0541,0.83857 0.486919,1.65011 0.459875,0.43281 -0.02705,-1.21728 0.243455,-2.54278 -0.37871,-3.65187 -0.622174,-1.10909 -0.892687,-2.59688 0.08114,-1.62306 0.973839,0.97383 1.731268,1.08205 2.001776,1.78536 0.270509,0.70333 0.24346,0.86563 0.568067,0.86563 0.324612,0 0.135255,-0.97383 -0.459866,-1.65011 -0.595116,-0.67626 -0.622174,-1.35254 -2.326379,-2.21817 -1.704214,-0.86563 -2.623946,-1.73126 -4.436351,-3.38137 -1.812424,-1.65011 -3.73304,-4.89622 -3.73304,-4.89622 z"
|
||||
id="path15576" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.150696px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 55.533489,166.09236 c 0.531307,0.94733 0.967262,1.78287 1.050983,2.07589 0.21641,0.75743 1.136142,0.91974 1.839463,1.7042 0.703326,0.78449 2.109969,1.59601 1.08204,2.40754 -1.027937,0.81154 0.973835,0.35167 0.919732,-0.91972 -0.05411,-1.2714 1.893562,2.40754 0.513964,2.89445 -1.379593,0.48692 0.703322,0.51397 0.973835,0.0541 0.270504,-0.45986 0.405763,-1.29844 0.297562,-2.21817 -0.108205,-0.91974 -1.514856,-2.62395 -1.839468,-2.9215 -0.324607,-0.29757 1.541906,-0.18936 2.05587,0.48691 0.513978,0.67627 0.568071,0.89268 0.189357,1.46075 -0.37871,0.56808 1.487803,-0.45986 0.59513,-1.3796 -0.892688,-0.91972 -0.216416,-0.83858 -1.460759,-1.43369 -1.244344,-0.59513 -1.379598,-0.97384 -2.43458,-0.91973 -0.290776,0.015 -0.918584,-0.58052 -1.675534,-1.45097 z"
|
||||
id="path16765"
|
||||
sodipodi:nodetypes="cssssssssssssscc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.150696px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 65.263268,165.22329 c 0.194353,3.9e-4 0.291031,0.0149 0.484578,0.0788 0.564847,0.18634 2.110365,1.76478 2.252123,2.27108 0.189357,0.67628 0.05411,0.83858 0.162303,0.97383 0.108206,0.13526 0.676277,0.13526 0.595121,-0.43282 -0.08115,-0.56806 0.378714,-0.0271 0.486919,0.45987 0.10821,0.48691 0.459862,1.35254 0.405759,2.19113 -0.0541,0.83857 0.313763,1.62347 0.459875,0.43281 0.148298,-1.20852 0.243455,-2.54277 -0.37871,-3.65187 -0.622174,-1.10909 -0.892687,-2.59689 0.08114,-1.62306 0.973839,0.97383 1.731268,1.08204 2.001776,1.78536 0.270509,0.70333 0.24346,0.86563 0.568067,0.86563 0.324612,0 0.135255,-0.97383 -0.459866,-1.65011 -0.595116,-0.67626 -0.473424,-1.27723 -2.177629,-2.14286 z"
|
||||
id="path16767"
|
||||
sodipodi:nodetypes="csssssssssssscc" />
|
||||
<path
|
||||
id="path7288"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.14952px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 49.18396,111.42134 c -1.800635,-0.0245 -5.26385,0.56015 -5.867743,1.66729 -0.644159,1.18095 -0.155808,1.17068 -0.155808,1.17068 0,0 0.09733,-0.0339 0.187346,-0.0614 0.02378,-0.0261 0.02481,-0.0554 0.03436,-0.076 0.02875,-0.062 0.09717,-0.1904 0.336564,-0.33445 0.312033,-0.18776 0.872977,-0.3496 1.664461,-0.30102 h 2.41e-4 c 0.78943,0.0485 2.533815,0.30776 4.087014,0.56604 1.553342,0.25829 2.875385,0.47236 2.875385,0.47236 0.04495,0.006 0.143795,0.10749 0.08567,0.26878 -0.0058,0.0327 -0.06495,-0.002 -0.214412,-0.0993 -0.06614,-0.0229 -1.213381,-0.26531 -2.765708,-0.52344 -1.552327,-0.25813 -3.30239,-0.52923 -4.075718,-0.56439 -0.771201,-0.0351 -1.253838,0.11807 -1.525598,0.31868 -0.153775,0.11351 -0.228149,0.2138 -0.224063,0.25301 0.0041,0.0392 0.763976,0.84306 0.763976,0.84306 0,0 4.729649,0.87015 6.481561,1.60397 0.255147,0.10687 2.364992,3.0533 2.555296,4.88112 0.190027,1.82509 0.08874,3.0699 -0.662772,4.1435 -0.75152,1.07358 -3.006044,3.9722 -5.045868,5.26051 -2.03982,1.28832 -7.622632,22.653 -6.763757,24.37076 0.858876,1.71774 1.610367,0.1072 1.503004,1.1808 -0.107358,1.07358 -1.180964,5.15332 -1.39568,6.87108 -0.21472,1.71775 0.214756,6.01209 -0.107324,7.19305 -0.322074,1.18096 -1.39568,4.72366 -1.39568,4.72366 l 11.165447,0.53685 2.576478,-7.7299 c 0,0 2.68414,-7.40772 3.113569,-8.05188 0.429438,-0.64417 5.904818,-0.96623 8.374084,-2.14718 0.386184,-0.1847 2.247128,-2.12197 2.271453,-0.852 0.02145,1.12014 -0.80722,4.11567 -0.499567,4.18571 0.265174,0.0604 1.984492,-5.14965 2.216279,-4.73504 0.368307,0.65881 -0.329862,3.69047 0.01675,3.54411 0.292186,-0.12338 2.360611,-5.96434 2.758609,-6.6518 0.267772,-0.46251 1.352696,3.30086 1.803794,2.38725 0.297275,-0.60207 -0.01911,-6.23822 0.174166,-5.80208 0.343977,0.77626 0.716858,0.003 0.684425,-0.37704 -0.1192,-1.39704 0.503605,1.2942 0.632411,1.077 0.156555,-0.264 -0.06427,-2.07035 0.08261,-2.35477 0.09703,-0.18785 0.302823,0.0275 0.392109,-0.17652 0.07237,-0.16541 -0.230366,-2.42098 -0.160751,-2.57954 0.365525,-0.83251 1.0931,-1.05675 1.222458,-1.72942 0.276707,-1.43889 0.612145,-4.71323 -1.730598,-7.65294 -0.154852,-0.1943 -0.01998,-0.41678 -0.100969,-1.07512 -0.20216,-1.64276 -0.836217,-2.55634 -1.213983,-3.12299 -0.858876,-1.28831 -1.034214,-0.70998 -2.322527,-2.42773 -1.288308,-1.71774 -2.14702,-0.9661 -1.9323,-2.57648 0.03143,-0.23549 0.28671,-1.26106 0.146864,-2.26862 -0.06782,-0.48855 -0.633083,-1.13463 -0.588163,-1.37497 0.07731,-0.41364 0.992454,1.03201 0.945674,0.63288 -0.07976,-0.68054 -0.815421,-1.13673 -0.917901,-1.64563 -0.06656,-0.33058 0.88395,0.20012 0.657123,-0.0852 -0.664175,-0.83545 -1.153015,-1.72183 -1.39168,-2.40373 -0.751521,-2.14718 -3.361144,-6.81149 -6.259852,-7.67036 -2.898695,-0.85888 -3.921434,-0.55656 -6.548874,-0.4027 -0.509915,0.0299 -1.43137,0.31166 -1.666814,0.30455 -0.644036,-0.0194 -2.486166,-0.49098 -3.938503,-0.58981 -0.103926,-0.007 -0.216521,-0.0115 -0.336564,-0.0132 z"
|
||||
sodipodi:nodetypes="ssccssssscccssszcsssssssscccssssssssscsssssscsscssssssscssss" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.421764;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 56.347762,114.81929 c 0.161825,0.10884 0.335437,0.26186 0.591376,0.25596 0.255976,-0.006 0.976809,-0.26479 0.967991,-0.59138 -0.009,-0.32658 -0.194171,-0.60901 -0.408951,-0.70612 -0.214776,-0.0971 -0.550714,-0.0673 -0.724035,-0.0349 -0.173348,0.0322 -0.455781,0.26752 -0.544051,0.39991 -0.08826,0.13237 -0.04418,0.56783 0.11767,0.67669 z"
|
||||
id="path19231"
|
||||
sodipodi:nodetypes="scssccs" />
|
||||
<ellipse
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.260928;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path871"
|
||||
cx="-10.937903"
|
||||
cy="127.09028"
|
||||
rx="0.50466883"
|
||||
ry="0.28456745"
|
||||
transform="rotate(-31.358267)" />
|
||||
<path
|
||||
style="fill:none;stroke:none;stroke-width:0.14952px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 44.389921,115.02116 c 0,0 4.831171,0.75152 6.119483,2.1472 1.288312,1.39567 2.791337,2.57662 2.57662,4.40172 -0.214721,1.82511 0.429438,3.00606 -0.322074,4.07967 -0.751517,1.07358 -3.006063,3.97228 -5.045887,5.26059 -2.039829,1.28831 -7.622516,22.65282 -6.763641,24.37057 0.858874,1.71774 1.610396,0.10735 1.503033,1.18095 -0.107358,1.07358 -1.180954,5.15325 -1.395671,6.87099 -0.214721,1.71775 0.214717,6.01212 -0.107362,7.19307 -0.322075,1.18096 -1.395667,4.72381 -1.395667,4.72381 l 11.165366,0.5368 2.57662,-7.72987 c 0,0 2.683983,-7.40778 3.11342,-8.05194 0.429438,-0.64417 5.904767,-0.96624 8.374029,-2.14719 0.386179,-0.1847 2.247326,-2.12188 2.271656,-0.85191 0.02145,1.12014 -1.801065,5.44911 -1.437061,5.54899 0.375712,0.1031 2.349547,-4.82589 2.581334,-4.41129 0.368312,0.65882 -0.247513,3.75415 0.248173,3.39069 0.738355,-0.5414 2.701532,-7.49811 3.099534,-8.18557 0.267767,-0.46251 1.352722,3.30089 1.803821,2.38728 0.297271,-0.60206 -0.01908,-6.23809 0.174191,-5.80195 0.343972,0.77626 0.716747,0.003 0.684315,-0.3771 -0.1192,-1.39703 0.503735,1.29404 0.63254,1.07683 0.15656,-0.264 -0.06434,-2.07033 0.08254,-2.35476 0.09702,-0.18785 0.302732,0.0277 0.392023,-0.17632 0.07237,-0.16542 -0.230316,-2.42104 -0.160696,-2.5796 0.36552,-0.83251 1.093075,-1.05669 1.222427,-1.72935 0.276712,-1.43889 0.612072,-4.7133 -1.730671,-7.65301 -0.154852,-0.1943 0.363352,-0.6248 0.303944,-0.85138 -0.584768,-2.23024 -0.879494,-3.38266 -1.257256,-3.9493 -0.85887,-1.28831 -1.39567,-0.10736 -2.683983,-1.8251 -1.288308,-1.71775 -2.147187,-0.96625 -1.932466,-2.57663 0.03143,-0.2355 0.286691,-1.26102 0.14685,-2.26858 -0.06782,-0.48855 -0.632868,-1.1346 -0.587947,-1.37494 0.07731,-0.41364 0.992317,1.03207 0.945533,0.63294 -0.07975,-0.68054 -0.815425,-1.13672 -0.917906,-1.64562 -0.06656,-0.33058 0.88397,0.20005 0.657148,-0.0853 -0.664175,-0.83544 -1.153003,-1.72174 -1.391667,-2.40363 -0.751521,-2.14719 -3.361098,-6.8116 -6.259802,-7.67048 -2.898699,-0.85887 -3.921467,-0.55646 -6.548911,-0.4026 -1.735895,0.10165 -3.548997,-0.14554 -5.605391,-0.28547 -1.662818,-0.11314 -5.559983,0.47335 -6.204137,1.6543 -0.644159,1.18096 -0.156005,1.1706 -0.156005,1.1706 0,0 0.264793,-0.0941 0.322635,-0.0949 0.275738,-0.004 0.906961,0.85675 0.906961,0.85675 z"
|
||||
id="path3379"
|
||||
sodipodi:nodetypes="csssssssscccsssssssssssssssssssssssssssssscsc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="150mm"
|
||||
height="150mm"
|
||||
viewBox="0 0 150 150"
|
||||
version="1.1"
|
||||
id="svg2098"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
sodipodi:docname="fast_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview2100"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673542"
|
||||
inkscape:cx="236.85572"
|
||||
inkscape:cy="257.89448"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="150mm" />
|
||||
<defs
|
||||
id="defs2095" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-31.274158,-78.140537)">
|
||||
<circle
|
||||
style="fill:none;fill-opacity:1;stroke:#122027;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7742"
|
||||
cx="106.62331"
|
||||
cy="153.48967"
|
||||
r="70" />
|
||||
<circle
|
||||
style="fill:#343434;fill-opacity:1;stroke:none;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7840"
|
||||
cx="106.80286"
|
||||
cy="151.15532"
|
||||
r="10" />
|
||||
<path
|
||||
id="rect7980"
|
||||
style="fill:#343434;fill-opacity:1;stroke-width:6.46499;stroke-linecap:round"
|
||||
d="m 156.9346,153.30987 -50.40574,1.47566 -0.0786,-6.1047 z"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:transform-center-x="-24.979325"
|
||||
inkscape:transform-center-y="-0.23024" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path9189"
|
||||
cx="56.373787"
|
||||
cy="185.78271"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9271"
|
||||
cx="46.658237"
|
||||
cy="154.20793"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9273"
|
||||
cx="53.132095"
|
||||
cy="125.30756"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9275"
|
||||
cx="73.13047"
|
||||
cy="102.3995"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9277"
|
||||
cx="106.98246"
|
||||
cy="92.060951"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#122027;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9279"
|
||||
cx="-73.287689"
|
||||
cy="-160.29289"
|
||||
r="3"
|
||||
transform="rotate(150.48856)" />
|
||||
<path
|
||||
id="circle12049"
|
||||
style="fill:none;stroke:#ff4141;stroke-width:5.66447;stroke-linecap:round;stroke-opacity:1"
|
||||
d="m 163.0663,127.86979 c 3.88122,8.04831 5.15863,16.53521 5.15863,26.06886 0,12.05511 -3.478,23.2979 -9.48531,32.77966"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="150mm"
|
||||
height="150mm"
|
||||
viewBox="0 0 150 150"
|
||||
version="1.1"
|
||||
id="svg2098"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="fast_light_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview2100"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673542"
|
||||
inkscape:cx="-200.88623"
|
||||
inkscape:cy="257.89448"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="150mm" />
|
||||
<defs
|
||||
id="defs2095" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-31.274158,-78.140537)">
|
||||
<circle
|
||||
style="fill:none;fill-opacity:1;stroke:#e5f2f8;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7742"
|
||||
cx="106.62331"
|
||||
cy="153.48967"
|
||||
r="70" />
|
||||
<circle
|
||||
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7840"
|
||||
cx="106.80286"
|
||||
cy="151.15532"
|
||||
r="10" />
|
||||
<path
|
||||
id="rect7980"
|
||||
style="fill:#515b60;fill-opacity:1;stroke-width:6.46499;stroke-linecap:round"
|
||||
d="m 156.9346,153.30987 -50.40574,1.47566 -0.0786,-6.1047 z"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:transform-center-x="-24.979325"
|
||||
inkscape:transform-center-y="-0.23024" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path9189"
|
||||
cx="56.373787"
|
||||
cy="185.78271"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9271"
|
||||
cx="46.658237"
|
||||
cy="154.20793"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9273"
|
||||
cx="53.132095"
|
||||
cy="125.30756"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9275"
|
||||
cx="73.13047"
|
||||
cy="102.3995"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9277"
|
||||
cx="106.98246"
|
||||
cy="92.060951"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9279"
|
||||
cx="-73.287689"
|
||||
cy="-160.29289"
|
||||
r="3"
|
||||
transform="rotate(150.48856)" />
|
||||
<path
|
||||
id="circle12049"
|
||||
style="fill:none;stroke:#ff4141;stroke-width:5.66447;stroke-linecap:round;stroke-opacity:1"
|
||||
d="m 163.0663,127.86979 c 3.88122,8.04831 5.15863,16.53521 5.15863,26.06886 0,12.05511 -3.478,23.2979 -9.48531,32.77966"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 6.5 KiB |
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="95mm"
|
||||
height="95mm"
|
||||
viewBox="0 0 95 95"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="148.74496"
|
||||
inkscape:cy="185.86807"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-52.906909,-78.735456)">
|
||||
<path
|
||||
style="color:#000000;fill:#ffffff;stroke-width:0.633931;-inkscape-stroke:none;fill-opacity:1"
|
||||
d="m 131.60351,157.53369 c -1.44225,0.088 -3.05509,0.225 -4.79271,0.38845 -14.0779,1.32417 -36.347368,4.38223 -42.468437,-2.85614 -6.876591,-8.13179 -5.676856,-14.32107 -3.540856,-27.51438 2.136,-13.19332 11.147817,-30.61464 22.217343,-31.845476 11.06952,-1.230833 23.00902,-0.134959 26.88493,1.149142 l 3.64693,-13.70856 c -13.8201,-3.333369 -20.25092,-4.6647 -37.975455,-2.227752 -17.724509,2.436948 -26.546029,23.899856 -28.926801,43.680146 -2.380764,19.78024 -0.597414,35.6157 5.403308,41.81224 6.000721,6.19654 41.625298,8.3939 61.081568,4.77669 z"
|
||||
id="path830"
|
||||
sodipodi:nodetypes="csszzcczzzcc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.142752px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 110.20292,152.92929 -1.25562,1.07624 c 0,0 1.30687,1.23 1.76812,1.76812 0.46125,0.53812 2.89562,4.68937 3.10062,5.40686 0.205,0.7175 1.07625,0.87126 1.7425,1.61437 0.66625,0.74313 1.99874,1.51187 1.025,2.28062 -0.97375,0.76876 0.9225,0.33314 0.87124,-0.87124 -0.0512,-1.20438 1.79375,2.28063 0.48688,2.74187 -1.30687,0.46125 0.66624,0.48687 0.92249,0.0512 0.25625,-0.43562 0.38438,-1.22999 0.28188,-2.10124 -0.1025,-0.87126 -1.435,-2.48563 -1.7425,-2.76749 -0.3075,-0.28189 1.46062,-0.17938 1.94749,0.46124 0.48689,0.64062 0.53813,0.84562 0.17938,1.38374 -0.35875,0.53814 1.40937,-0.43562 0.56376,-1.30687 -0.84563,-0.87124 -0.20501,-0.79437 -1.38376,-1.35811 -1.17875,-0.56376 -1.30687,-0.92251 -2.30624,-0.87125 -0.99937,0.0513 -6.20124,-7.50811 -6.20124,-7.50811 z"
|
||||
id="path15574" />
|
||||
<path
|
||||
style="fill:#fefefe;fill-opacity:1;stroke:none;stroke-width:0.142752px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 118.4029,150.05928 -1.48624,0.9225 3.40812,3.81813 c 0,0 1.76812,1.28124 1.40937,2.61373 -0.35875,1.3325 -1.28125,1.81938 -1.28125,1.81938 0,0 0.12812,0.23062 0.53812,0.23062 0.41,0 0.9225,-1.33249 1.66563,-0.92249 0.74312,0.41 1.79374,1.43498 1.97312,2.07561 0.17937,0.64063 0.0513,0.79438 0.15375,0.9225 0.1025,0.12813 0.64062,0.12813 0.56374,-0.41 -0.0769,-0.53812 0.35875,-0.0256 0.46126,0.43562 0.1025,0.46125 0.43562,1.28125 0.38436,2.07562 -0.0512,0.79437 0.46126,1.56313 0.43564,0.41 -0.0256,-1.15311 0.23062,-2.40873 -0.35875,-3.45936 -0.58937,-1.05063 -0.84563,-2.45999 0.0769,-1.5375 0.9225,0.92249 1.64,1.02501 1.89625,1.69124 0.25625,0.66626 0.23062,0.82 0.53812,0.82 0.3075,0 0.12812,-0.92249 -0.43562,-1.56313 -0.56375,-0.64061 -0.58938,-1.28124 -2.20375,-2.10123 -1.61437,-0.82 -2.48562,-1.64 -4.20249,-3.20312 -1.71688,-1.56312 -3.53625,-4.63812 -3.53625,-4.63812 z"
|
||||
id="path15576" />
|
||||
<path
|
||||
style="fill:#010101;fill-opacity:1;stroke:none;stroke-width:0.142752px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 112.82046,159.21405 c 0.5033,0.8974 0.91627,1.68889 0.99558,1.96646 0.205,0.7175 1.07625,0.87126 1.7425,1.61437 0.66625,0.74313 1.99874,1.51187 1.025,2.28062 -0.97375,0.76876 0.9225,0.33314 0.87124,-0.87124 -0.0512,-1.20438 1.79375,2.28063 0.48688,2.74187 -1.30687,0.46125 0.66624,0.48687 0.92249,0.0512 0.25625,-0.43562 0.38438,-1.22999 0.28188,-2.10124 -0.1025,-0.87126 -1.435,-2.48563 -1.7425,-2.76749 -0.3075,-0.28189 1.46062,-0.17938 1.94749,0.46124 0.48689,0.64062 0.53813,0.84562 0.17938,1.38374 -0.35875,0.53814 1.40937,-0.43562 0.56376,-1.30687 -0.84563,-0.87124 -0.20501,-0.79437 -1.38376,-1.35811 -1.17875,-0.56376 -1.30687,-0.92251 -2.30624,-0.87125 -0.27545,0.0142 -0.87016,-0.54991 -1.58721,-1.37448 z"
|
||||
id="path16765"
|
||||
sodipodi:nodetypes="cssssssssssssscc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.142752px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 122.03733,158.3908 c 0.18411,3.7e-4 0.27569,0.0141 0.45904,0.0746 0.53507,0.17652 1.99911,1.67175 2.1334,2.15136 0.17937,0.64063 0.0513,0.79438 0.15375,0.92249 0.1025,0.12814 0.64062,0.12814 0.56374,-0.40999 -0.0769,-0.53812 0.35875,-0.0256 0.46126,0.43562 0.1025,0.46124 0.43562,1.28124 0.38436,2.07562 -0.0512,0.79437 0.29723,1.53789 0.43564,0.41 0.14048,-1.14481 0.23062,-2.40873 -0.35875,-3.45936 -0.58937,-1.05063 -0.84563,-2.46 0.0769,-1.5375 0.9225,0.92249 1.64,1.025 1.89625,1.69124 0.25625,0.66626 0.23062,0.82 0.53812,0.82 0.3075,0 0.12812,-0.9225 -0.43562,-1.56313 -0.56375,-0.64061 -0.44847,-1.2099 -2.06284,-2.02989 z"
|
||||
id="path16767"
|
||||
sodipodi:nodetypes="csssssssssssscc" />
|
||||
<path
|
||||
id="path7288"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.141638px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 106.80565,107.42503 c -1.70572,-0.0232 -4.98637,0.53061 -5.55843,1.57939 -0.6102,1.1187 -0.14759,1.10896 -0.14759,1.10896 0,0 0.0922,-0.0321 0.17747,-0.0582 0.0225,-0.0247 0.0235,-0.0525 0.0326,-0.072 0.0272,-0.0588 0.0921,-0.18036 0.31882,-0.31681 0.29558,-0.17787 0.82696,-0.33118 1.57672,-0.28516 h 2.3e-4 c 0.74781,0.046 2.40024,0.29154 3.87156,0.5362 1.47146,0.24468 2.72381,0.44746 2.72381,0.44746 0.0426,0.005 0.13622,0.10183 0.0812,0.25462 -0.006,0.031 -0.0615,-0.001 -0.20311,-0.0941 -0.0626,-0.0217 -1.14942,-0.25133 -2.61991,-0.49585 -1.4705,-0.24452 -3.12831,-0.50133 -3.86087,-0.53463 -0.73055,-0.0332 -1.18774,0.11185 -1.44518,0.30187 -0.14567,0.10753 -0.21612,0.20254 -0.21225,0.23968 0.004,0.0371 0.7237,0.79861 0.7237,0.79861 0,0 4.48033,0.82429 6.13989,1.51942 0.2417,0.10124 2.24032,2.89235 2.42059,4.62381 0.18001,1.72889 0.0841,2.90807 -0.62783,3.92508 -0.71191,1.01699 -2.84758,3.7628 -4.77988,4.9832 -1.93229,1.22041 -7.220799,21.45885 -6.4072,23.08605 0.8136,1.62719 1.52548,0.10155 1.42377,1.11855 -0.1017,1.01699 -1.118707,4.88167 -1.322104,6.50887 -0.203401,1.6272 0.203435,5.69517 -0.101666,6.81387 -0.305096,1.1187 -1.322107,4.47465 -1.322107,4.47465 l 10.576857,0.50856 2.44066,-7.32243 c 0,0 2.54264,-7.01721 2.94944,-7.62742 0.4068,-0.61021 5.59354,-0.91529 7.93264,-2.03399 0.36583,-0.17496 2.12867,-2.01011 2.15171,-0.80709 0.0203,1.0611 -0.76466,3.89871 -0.47323,3.96507 0.2512,0.0572 1.87988,-4.87819 2.09945,-4.48544 0.34889,0.62408 -0.31248,3.49592 0.0159,3.35728 0.27679,-0.11687 2.23617,-5.64993 2.61319,-6.30114 0.25366,-0.43813 1.28139,3.12685 1.70871,2.2614 0.2816,-0.57033 -0.0181,-5.90937 0.16498,-5.49622 0.32585,0.73534 0.67907,0.003 0.64835,-0.35717 -0.11292,-1.32339 0.47706,1.22598 0.59907,1.02023 0.1483,-0.25009 -0.0609,-1.96121 0.0783,-2.23064 0.0919,-0.17795 0.28686,0.0261 0.37144,-0.16721 0.0686,-0.1567 -0.21823,-2.29336 -0.15228,-2.44356 0.34625,-0.78862 1.03548,-1.00105 1.15801,-1.63825 0.26212,-1.36304 0.57988,-4.46478 -1.63936,-7.24952 -0.14669,-0.18406 -0.0189,-0.39481 -0.0956,-1.01845 -0.1915,-1.55615 -0.79214,-2.42158 -1.14999,-2.95835 -0.8136,-1.2204 -0.97969,-0.67256 -2.20009,-2.29975 -1.2204,-1.6272 -2.03384,-0.91518 -1.83044,-2.44066 0.0298,-0.22309 0.27159,-1.19459 0.13912,-2.14904 -0.0642,-0.46279 -0.59971,-1.07482 -0.55716,-1.30249 0.0732,-0.39183 0.94014,0.97761 0.89582,0.59952 -0.0756,-0.64466 -0.77243,-1.07681 -0.86951,-1.55888 -0.0631,-0.31315 0.83735,0.18957 0.62248,-0.0807 -0.62916,-0.7914 -1.09223,-1.63106 -1.31831,-2.27701 -0.71191,-2.034 -3.18396,-6.45242 -5.92987,-7.26602 -2.74589,-0.8136 -3.71471,-0.52722 -6.20364,-0.38147 -0.48304,0.0283 -1.35592,0.29523 -1.57895,0.2885 -0.61009,-0.0184 -2.35511,-0.4651 -3.73089,-0.55872 -0.0984,-0.007 -0.2051,-0.0109 -0.31882,-0.0125 z"
|
||||
sodipodi:nodetypes="ssccssssscccssszcsssssssscccssssssssscsssssscsscssssssscssss" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.399531;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 113.59181,110.64385 c 0.15329,0.1031 0.31775,0.24805 0.5602,0.24247 0.24248,-0.005 0.92532,-0.25083 0.91696,-0.56021 -0.009,-0.30937 -0.18393,-0.57691 -0.38739,-0.6689 -0.20345,-0.092 -0.52168,-0.0638 -0.68587,-0.0331 -0.16421,0.0305 -0.43175,0.25342 -0.51537,0.37884 -0.0836,0.12539 -0.0418,0.53789 0.11147,0.64101 z"
|
||||
id="path19231"
|
||||
sodipodi:nodetypes="scssccs" />
|
||||
<ellipse
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.247173;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path871"
|
||||
cx="40.080688"
|
||||
cy="153.32861"
|
||||
rx="0.47806513"
|
||||
ry="0.26956642"
|
||||
transform="rotate(-31.358267)" />
|
||||
<path
|
||||
style="fill:none;stroke:none;stroke-width:0.141638px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 102.26433,110.83508 c 0,0 4.57649,0.7119 5.79689,2.03401 1.2204,1.3221 2.64419,2.44079 2.44079,4.16968 -0.2034,1.7289 0.4068,2.8476 -0.30509,3.86461 -0.7119,1.01698 -2.8476,3.76288 -4.77989,4.98328 -1.9323,1.2204 -7.220698,21.45867 -6.407099,23.08587 0.813599,1.62719 1.525499,0.10169 1.423799,1.1187 -0.1017,1.01699 -1.118698,4.88159 -1.322096,6.50878 -0.203402,1.6272 0.203398,5.69519 -0.101703,6.81389 -0.305097,1.1187 -1.322093,4.47479 -1.322093,4.47479 l 10.576782,0.5085 2.44079,-7.32239 c 0,0 2.5425,-7.01728 2.9493,-7.62748 0.4068,-0.61021 5.59349,-0.9153 7.93259,-2.034 0.36582,-0.17496 2.12886,-2.01002 2.1519,-0.807 0.0203,1.06109 -1.70612,5.16186 -1.3613,5.25648 0.3559,0.0977 2.22569,-4.57149 2.44526,-4.17875 0.34889,0.62408 -0.23447,3.55624 0.23509,3.21194 0.69943,-0.51286 2.55912,-7.10284 2.93614,-7.75406 0.25365,-0.43813 1.28141,3.12689 1.70873,2.26144 0.2816,-0.57033 -0.0181,-5.90925 0.16501,-5.49611 0.32584,0.73534 0.67896,0.003 0.64824,-0.35721 -0.11292,-1.32339 0.47718,1.22581 0.5992,1.02006 0.1483,-0.25009 -0.0609,-1.9612 0.0782,-2.23063 0.0919,-0.17795 0.28677,0.0263 0.37136,-0.16703 0.0686,-0.15669 -0.21818,-2.29341 -0.15223,-2.44361 0.34625,-0.78862 1.03546,-1.00099 1.15799,-1.63819 0.26212,-1.36304 0.57981,-4.46484 -1.63944,-7.24958 -0.14669,-0.18406 0.3442,-0.59186 0.28792,-0.8065 -0.55394,-2.11267 -0.83313,-3.20434 -1.19098,-3.74111 -0.81359,-1.2204 -1.3221,-0.1017 -2.54249,-1.72889 -1.2204,-1.6272 -2.034,-0.91531 -1.8306,-2.4408 0.0298,-0.22308 0.27158,-1.19455 0.13911,-2.14899 -0.0642,-0.4628 -0.59951,-1.0748 -0.55695,-1.30247 0.0732,-0.39183 0.94,0.97767 0.89568,0.59958 -0.0755,-0.64466 -0.77244,-1.07679 -0.86951,-1.55887 -0.0631,-0.31315 0.83737,0.1895 0.6225,-0.0808 -0.62916,-0.79141 -1.09222,-1.63098 -1.3183,-2.27693 -0.71191,-2.034 -3.18392,-6.45253 -5.92982,-7.26612 -2.74589,-0.8136 -3.71474,-0.52713 -6.20368,-0.38138 -1.64439,0.0963 -3.36191,-0.13787 -5.3099,-0.27042 -1.57517,-0.10718 -5.26689,0.44839 -5.87709,1.56709 -0.6102,1.1187 -0.14778,1.10889 -0.14778,1.10889 0,0 0.25083,-0.0891 0.30563,-0.0899 0.2612,-0.004 0.85915,0.81159 0.85915,0.81159 z"
|
||||
id="path3379"
|
||||
sodipodi:nodetypes="csssssssscccsssssssssssssssssssssssssssssscsc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="130mm"
|
||||
height="130mm"
|
||||
viewBox="0 0 130 130"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
sodipodi:docname="header_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="-10.180046"
|
||||
inkscape:cy="249.75045"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="130mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="286.39862"
|
||||
y="431.63394"
|
||||
width="213.10229"
|
||||
height="276.89725"
|
||||
id="rect12469" />
|
||||
<rect
|
||||
x="308.11603"
|
||||
y="465.56741"
|
||||
width="176.45412"
|
||||
height="267.39587"
|
||||
id="rect3773" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-55.42222,-82.637121)">
|
||||
<path
|
||||
id="path1009"
|
||||
style="fill:#122027;fill-opacity:1;stroke-width:8.47412"
|
||||
d="M 62 0 L 62 244.93359 L 62 489.86719 L 249.05273 489.86719 L 436.10547 489.86719 L 436.10547 302.15625 L 436.10547 114.44531 L 378.94141 57.222656 L 321.7793 0 L 191.88867 0 L 62 0 z M 228.81055 227.10352 L 246.87695 227.10352 L 246.87695 286.67383 C 251.17383 280.09831 256.2194 275.18294 262.01367 271.92773 C 267.87305 268.67253 274.61133 267.04492 282.22852 267.04492 C 294.79362 267.04492 304.29883 270.95117 310.74414 278.76367 C 317.18945 286.51107 320.41211 297.93685 320.41211 313.04102 L 320.41211 379.05664 L 302.44336 379.05664 L 302.44336 313.62695 C 302.44336 303.27539 300.42513 295.52799 296.38867 290.38477 C 292.35221 285.24154 286.29753 282.66992 278.22461 282.66992 C 268.52409 282.66992 260.87435 285.76237 255.27539 291.94727 C 249.67643 298.13216 246.87695 306.56315 246.87695 317.24023 L 246.87695 379.05664 L 228.81055 379.05664 L 228.81055 227.10352 z M 168.45898 354.25195 L 189.06445 354.25195 L 189.06445 379.05664 L 168.45898 379.05664 L 168.45898 354.25195 z "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,55.42222,82.637121)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="scale(0.26458333)"
|
||||
id="text3771"
|
||||
style="font-style:normal;font-weight:normal;font-size:200px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect3773);fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="130mm"
|
||||
height="130mm"
|
||||
viewBox="0 0 130 130"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="header_light_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="-122.16055"
|
||||
inkscape:cy="249.75045"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="130mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="286.39862"
|
||||
y="431.63394"
|
||||
width="213.10229"
|
||||
height="276.89725"
|
||||
id="rect12469" />
|
||||
<rect
|
||||
x="308.11603"
|
||||
y="465.56741"
|
||||
width="176.45412"
|
||||
height="267.39587"
|
||||
id="rect3773" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-55.42222,-82.637121)">
|
||||
<path
|
||||
id="path1009"
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke-width:8.47412"
|
||||
d="M 62 0 L 62 244.93359 L 62 489.86719 L 249.05273 489.86719 L 436.10547 489.86719 L 436.10547 302.15625 L 436.10547 114.44531 L 378.94141 57.222656 L 321.7793 0 L 191.88867 0 L 62 0 z M 228.81055 227.10352 L 246.87695 227.10352 L 246.87695 286.67383 C 251.17383 280.09831 256.2194 275.18294 262.01367 271.92773 C 267.87305 268.67253 274.61133 267.04492 282.22852 267.04492 C 294.79362 267.04492 304.29883 270.95117 310.74414 278.76367 C 317.18945 286.51107 320.41211 297.93685 320.41211 313.04102 L 320.41211 379.05664 L 302.44336 379.05664 L 302.44336 313.62695 C 302.44336 303.27539 300.42513 295.52799 296.38867 290.38477 C 292.35221 285.24154 286.29753 282.66992 278.22461 282.66992 C 268.52409 282.66992 260.87435 285.76237 255.27539 291.94727 C 249.67643 298.13216 246.87695 306.56315 246.87695 317.24023 L 246.87695 379.05664 L 228.81055 379.05664 L 228.81055 227.10352 z M 168.45898 354.25195 L 189.06445 354.25195 L 189.06445 379.05664 L 168.45898 379.05664 L 168.45898 354.25195 z "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,55.42222,82.637121)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="scale(0.26458333)"
|
||||
id="text3771"
|
||||
style="font-style:normal;font-weight:normal;font-size:200px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect3773);fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 216 KiB |
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128mm"
|
||||
height="128mm"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
sodipodi:docname="typesafe_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0419012"
|
||||
inkscape:cx="-37.911464"
|
||||
inkscape:cy="204.43397"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="128mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="mirror_symmetry"
|
||||
start_point="83.49758,103.2497"
|
||||
end_point="83.677144,216.55502"
|
||||
center_point="83.587362,159.90236"
|
||||
id="path-effect1148"
|
||||
is_visible="true"
|
||||
lpeversion="1.1"
|
||||
mode="free"
|
||||
discard_orig_path="false"
|
||||
fuse_paths="true"
|
||||
oposite_fuse="false"
|
||||
split_items="false"
|
||||
split_open="false" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-35.610134,-93.0589)">
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#122027;stroke-width:6.465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 83.489092,97.893689 C 61.657927,122.25262 42.116054,116.87646 39.272073,119.55834 c -2.61289,2.46396 6.066946,69.88504 44.404869,96.86893 38.252208,-27.10527 46.718298,-94.55352 44.097618,-97.00919 -2.85247,-2.67285 -22.37721,2.76522 -44.285468,-21.524391 z"
|
||||
id="path857"
|
||||
sodipodi:nodetypes="csc"
|
||||
inkscape:original-d="m 83.677144,97.68319 c -21.906891,24.60036 -41.552926,19.18557 -44.405071,21.87515 -2.615268,2.4662 6.082752,70.00779 44.5096,96.94249"
|
||||
inkscape:path-effect="#path-effect1148"
|
||||
transform="translate(16.150228,0.25394282)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128mm"
|
||||
height="128mm"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="typesafe_light_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0419012"
|
||||
inkscape:cx="114.21428"
|
||||
inkscape:cy="204.43397"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="128mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="mirror_symmetry"
|
||||
start_point="83.49758,103.2497"
|
||||
end_point="83.677144,216.55502"
|
||||
center_point="83.587362,159.90236"
|
||||
id="path-effect1148"
|
||||
is_visible="true"
|
||||
lpeversion="1.1"
|
||||
mode="free"
|
||||
discard_orig_path="false"
|
||||
fuse_paths="true"
|
||||
oposite_fuse="false"
|
||||
split_items="false"
|
||||
split_open="false" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-35.610134,-93.0589)">
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#e5f2f8;stroke-width:6.465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 83.489092,97.893689 C 61.657927,122.25262 42.116054,116.87646 39.272073,119.55834 c -2.61289,2.46396 6.066946,69.88504 44.404869,96.86893 38.252208,-27.10527 46.718298,-94.55352 44.097618,-97.00919 -2.85247,-2.67285 -22.37721,2.76522 -44.285468,-21.524391 z"
|
||||
id="path857"
|
||||
sodipodi:nodetypes="csc"
|
||||
inkscape:original-d="m 83.677144,97.68319 c -21.906891,24.60036 -41.552926,19.18557 -44.405071,21.87515 -2.615268,2.4662 6.082752,70.00779 44.5096,96.94249"
|
||||
inkscape:path-effect="#path-effect1148"
|
||||
transform="translate(16.150228,0.25394282)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="202mm"
|
||||
height="202mm"
|
||||
viewBox="0 0 202 202"
|
||||
version="1.1"
|
||||
id="svg13908"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
sodipodi:docname="websocket_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview13910"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="158.13004"
|
||||
inkscape:cy="343.40687"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1007"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="201.77914mm" />
|
||||
<defs
|
||||
id="defs13905" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-4.4986958,-69.044219)">
|
||||
<path
|
||||
style="fill:#122027;stroke-width:0.0841212;fill-opacity:1"
|
||||
d="m 69.265523,230.77706 -14.111288,-14.11148 8.832818,-8.83251 8.832808,-8.83251 L 51.116516,177.2971 29.41317,155.59363 v -12.26062 -12.26062 h 12.534076 12.534076 v 7.08736 7.08737 l 9.169279,9.16899 9.169279,9.16899 13.396176,-13.39618 13.396175,-13.39617 -9.168982,-9.16928 -9.169,-9.16932 H 55.360589 29.44692 L 16.972808,105.92001 4.4986958,93.385901 H 48.103109 91.707532 l 3.746995,3.764422 c 2.060862,2.070436 11.842143,11.897187 21.736193,21.837197 l 17.9892,18.0727 -4.40824,4.40884 -4.40823,4.40882 8.60118,8.60149 8.60117,8.60148 v 17.74937 17.74937 l -17.49722,-17.49708 -17.49722,-17.49705 -4.45581,4.45833 -4.455823,4.45834 17.460153,17.51827 17.46015,17.51826 H 116.84371 99.107375 l -8.622061,-8.62237 -8.622051,-8.62235 -4.43771,4.43711 -4.437711,4.43712 10.452037,10.45228 10.452028,10.45229 h 43.721543 43.72154 l 10.28448,10.23153 c 5.65645,5.62734 11.2686,11.2109 12.47142,12.4079 l 2.18695,2.17636 H 144.82733 83.376811 Z M 156.184,182.57579 v -24.87875 l -10.45194,-10.4522 -10.45193,-10.45218 8.83245,-8.83275 c 4.85784,-4.85802 8.87197,-8.83278 8.92029,-8.83278 0.0483,0 6.41741,6.33451 14.15356,14.07664 l 14.06572,14.07661 v 30.08708 30.08708 H 168.71808 156.184 Z"
|
||||
id="path13999" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="202mm"
|
||||
height="202mm"
|
||||
viewBox="0 0 202 202"
|
||||
version="1.1"
|
||||
id="svg13908"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="websocket_light_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview13910"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="160.16605"
|
||||
inkscape:cy="343.40687"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="201.77914mm" />
|
||||
<defs
|
||||
id="defs13905" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-4.4986958,-69.044219)">
|
||||
<path
|
||||
style="fill:#e5f2f8;stroke-width:0.0841212;fill-opacity:1"
|
||||
d="m 69.265523,230.77706 -14.111288,-14.11148 8.832818,-8.83251 8.832808,-8.83251 L 51.116516,177.2971 29.41317,155.59363 v -12.26062 -12.26062 h 12.534076 12.534076 v 7.08736 7.08737 l 9.169279,9.16899 9.169279,9.16899 13.396176,-13.39618 13.396175,-13.39617 -9.168982,-9.16928 -9.169,-9.16932 H 55.360589 29.44692 L 16.972808,105.92001 4.4986958,93.385901 H 48.103109 91.707532 l 3.746995,3.764422 c 2.060862,2.070436 11.842143,11.897187 21.736193,21.837197 l 17.9892,18.0727 -4.40824,4.40884 -4.40823,4.40882 8.60118,8.60149 8.60117,8.60148 v 17.74937 17.74937 l -17.49722,-17.49708 -17.49722,-17.49705 -4.45581,4.45833 -4.455823,4.45834 17.460153,17.51827 17.46015,17.51826 H 116.84371 99.107375 l -8.622061,-8.62237 -8.622051,-8.62235 -4.43771,4.43711 -4.437711,4.43712 10.452037,10.45228 10.452028,10.45229 h 43.721543 43.72154 l 10.28448,10.23153 c 5.65645,5.62734 11.2686,11.2109 12.47142,12.4079 l 2.18695,2.17636 H 144.82733 83.376811 Z M 156.184,182.57579 v -24.87875 l -10.45194,-10.4522 -10.45193,-10.45218 8.83245,-8.83275 c 4.85784,-4.85802 8.87197,-8.83278 8.92029,-8.83278 0.0483,0 6.41741,6.33451 14.15356,14.07664 l 14.06572,14.07661 v 30.08708 30.08708 H 168.71808 156.184 Z"
|
||||
id="path13999" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,129 @@
|
|||
Hello World is a good start, but what if you want something a bit more fancy.. Something like an HTML document saying "Hello World". If that's what you want, follow along:
|
||||
|
||||
## Basic Webpage
|
||||
Let's start our webpage with.. well.. a webpage. But before we create a webpage we need to place it somewhere Crow recognizes, for now this directory is going to be called `templates`, but we can [change it later](../../guides/templating/#page).
|
||||
|
||||
Once our `templates` folder is created, we can create our HTML document inside it, let's call it `fancypage.html`.
|
||||
|
||||
After that we can just place something simple inside it like:
|
||||
``` html title="templates/fancypage.html"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello World!</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
<br>
|
||||
Now that we have our HTML page ready, let's take our Hello World example from earlier:
|
||||
``` cpp linenums="1"
|
||||
#include "crow.h"
|
||||
//#include "crow_all.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app; //define your crow application
|
||||
|
||||
//define your endpoint at the root directory
|
||||
CROW_ROUTE(app, "/")([](){
|
||||
return "Hello world";
|
||||
});
|
||||
|
||||
//set the port, set the app to run on multiple threads, and run the app
|
||||
app.port(18080).multithreaded().run();
|
||||
}
|
||||
```
|
||||
<br>
|
||||
|
||||
And now let's modify it so that it returns our cool page:
|
||||
``` cpp title="/main.cpp" linenums="1" hl_lines="10 11"
|
||||
#include "crow.h"
|
||||
//#include "crow_all.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
//define your endpoint at the root directory
|
||||
CROW_ROUTE(app, "/")([](){
|
||||
auto page = crow::mustache::load_text("fancypage.html");
|
||||
return page;
|
||||
});
|
||||
|
||||
app.port(18080).multithreaded().run();
|
||||
}
|
||||
```
|
||||
|
||||
Your project should look something something like:
|
||||
```
|
||||
./
|
||||
|-templates/
|
||||
| |-fancypage.html
|
||||
|
|
||||
|-main.cpp
|
||||
|-crow_all.h
|
||||
```
|
||||
or
|
||||
```
|
||||
./
|
||||
|-templates/
|
||||
| |-fancypage.html
|
||||
|
|
||||
|-crow/
|
||||
| |-include/...
|
||||
| |-crow.h
|
||||
|-main.cpp
|
||||
```
|
||||
|
||||
|
||||
Once the code is done compiling, if we call `http://localhost:18080/` we get our Hello World in an HTML document rather than just plain text.
|
||||
|
||||
!!! note
|
||||
|
||||
Compilation instructions are available for [Linux](../setup/linux#compiling-your-project), [MacOS](../setup/macos#compiling-using-a-compiler-directly), and [Windows](../setup/windows#getting-and-compiling-crow)
|
||||
|
||||
|
||||
## Template Webpage with a variable
|
||||
But we can make things even more exciting, we can greet a user by their name instead!!
|
||||
|
||||
Let's start with our webpage, and modify it with a little bit of [mustache](../../guides/templating) syntax:
|
||||
``` html title="templates/fancypage.html" hl_lines="4"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello {{person}}!</p> <!--(1)-->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
1. `{{}}` in mustache define a simple variable
|
||||
|
||||
<br>
|
||||
Now let's modify our C++ code to use the variable we just added to our webpage (or template):
|
||||
``` cpp title="/main.cpp" linenums="1" hl_lines="9-12"
|
||||
#include "crow.h"
|
||||
//#include "crow_all.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
//define your endpoint at the root directory
|
||||
CROW_ROUTE(app, "/<string>")([](std::string name){ // (1)
|
||||
auto page = crow::mustache::load("fancypage.html"); // (2)
|
||||
crow::mustache::context ctx ({{"person", name}}); // (3)
|
||||
return page.render(ctx); //(4)
|
||||
});
|
||||
|
||||
app.port(18080).multithreaded().run();
|
||||
}
|
||||
```
|
||||
|
||||
1. We are adding a `string` variable to the URL and a counterpart (`std::string name`) to our route - this can be anything the user wants.
|
||||
2. We are using `load()` instead of `load_text()` since we have an actual variable now.
|
||||
3. We are creating a new [context](../../guides/templating/#context) containing the `person` variable from our template and the `name` we got from the URL.
|
||||
4. We are using `render(ctx)` to apply our context to the template.
|
||||
|
||||
Now (after compiling the code and running the executable a second time) calling `http://localhost:18080/Bob` should return a webpage containing "Hello Bob!". **We did it!**
|
||||
|
||||
For more details on templates and HTML pages in Crow please go [here](../../guides/templating/)
|
|
@ -0,0 +1,93 @@
|
|||
Here's how you can install Crow on your favorite GNU/Linux distro.
|
||||
## Getting Crow
|
||||
|
||||
### Requirements
|
||||
- C++ compiler with at least C++11 support.
|
||||
- Asio development headers (1.10.9 or later).
|
||||
- **(optional)** ZLib for HTTP Compression.
|
||||
- **(optional)** OpenSSL for HTTPS support.
|
||||
- **(optional)** CMake for building tests, examples, and/or installing Crow.
|
||||
- **(optional)** Python3 to build tests and/or examples.
|
||||
!!! note
|
||||
|
||||
Crow's CI uses `g++-9.4` and `clang-10.0` running on AMD64 (x86_64) and ARM64v8 architectures.
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
### Using a package Manager
|
||||
You can install Crow on GNU/Linux as a pre-made package
|
||||
=== "Debian/Ubuntu"
|
||||
|
||||
Simply download Crow's `.deb` file from the [release section](https://github.com/CrowCpp/Crow/releases/latest) and Install it.
|
||||
|
||||
=== "Arch"
|
||||
|
||||
Crow is available for Arch based distros through the AUR package `crow`.
|
||||
|
||||
|
||||
<br><br>
|
||||
### Release package
|
||||
Crow provides an archive containing the framework and CMake files, just copy the `include` folder to `/usr/local/include` and `lib` folder to `/usr/local/lib`.<br><br>
|
||||
You can also download the `crow_all.h` file and simply include that into your project.
|
||||
<br><br>
|
||||
### Installing from source
|
||||
#### Using CMake
|
||||
1. Download Crow's source code (Either through Github's UI or by using<br> `git clone https://github.com/CrowCpp/Crow.git`).
|
||||
2. Run `mkdir build` inside of crow's source directory.
|
||||
3. Navigate to the new "build" directory and run the following:<br>
|
||||
`cmake .. -DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF`
|
||||
4. Run `make install`.
|
||||
|
||||
!!! note
|
||||
|
||||
You can ignore `-DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF` if you want to build the Examples and Unit Tests.
|
||||
|
||||
!!! note
|
||||
|
||||
While building you can set the `CROW_FEATURES` variable (as a `;` separated list). You can use an argument such as `-DCROW_FEATURES="ssl;compression"`.
|
||||
|
||||
!!! note
|
||||
|
||||
You can uninstall Crow at a later time using `make uninstall`.
|
||||
|
||||
<br>
|
||||
#### Manually
|
||||
Crow can be installed manually on your Linux computer.
|
||||
##### Multiple header files
|
||||
=== "Project Only"
|
||||
|
||||
Copy Crow's `include` directory to your project's `include` directory.
|
||||
|
||||
=== "System wide"
|
||||
|
||||
Copy Crow's `include` directory to the `/usr/local/include` directory.
|
||||
|
||||
##### Single header (crow_all.h)
|
||||
!!! warning
|
||||
|
||||
`crow_all.h` is recommended only for small, possibly single source file projects, and ideally should not be installed on your system.
|
||||
|
||||
navigate to the `scripts` directory and run `./merge_all.py ../include crow_all.h`. This will generate a `crow_all.h` file that you can use in your projects.
|
||||
!!! note
|
||||
|
||||
You can also include or exclude middlewares from your `crow_all.h` by using `-i` or `-e` followed by the middleware header file names separated by a comma (e.g. `merge_all.py ../include crow_all.h -e cookie_parser` to exclude the cookie parser middleware).
|
||||
|
||||
## Compiling your project
|
||||
### Using CMake
|
||||
In order to get your CMake project to work with Crow, all you need are the following lines in your CMakeLists.txt:
|
||||
```
|
||||
find_package(Crow)
|
||||
target_link_libraries(your_project PUBLIC Crow::Crow)
|
||||
```
|
||||
From there CMake should handle compiling and linking your project.
|
||||
!!! note
|
||||
|
||||
For optional features like HTTP Compression or HTTPS you can set the `CROW_FEATURES` variable using lines such as `set(CROW_FEATURES "ssl;compression")`, `set(CROW_FEATURES ssl compression)`, or `set(CROW_FEATURES ssl)`.
|
||||
|
||||
### Directly using a compiler
|
||||
All you need to do is run the following command:
|
||||
```
|
||||
g++ main.cpp -lpthread
|
||||
```
|
||||
You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++.
|
|
@ -0,0 +1,77 @@
|
|||
Here's how you can install Crow on your Mac.
|
||||
## Getting Crow
|
||||
### From a [release](https://github.com/CrowCpp/Crow/releases)
|
||||
#### Archive
|
||||
Crow provides an archive containing the framework and CMake files, You will only need the `include` folder inside that archive.
|
||||
#### Single header file
|
||||
You can also download the `crow_all.h` file which replaces the `include` folder.
|
||||
|
||||
### From Source
|
||||
To get Crow from source, you only need to download the repository (as a `.zip` or through `git clone https://github.com/CrowCpp/Crow.git`).
|
||||
#### include folder
|
||||
Once you've downloaded Crow's source code, you only need to take the `include` folder.
|
||||
#### Single header file
|
||||
You can generate your own single header file by navigating to the `scripts` folder with your terminal and running the following command:
|
||||
```
|
||||
python3 merge_all.py ../include crow_all.h
|
||||
```
|
||||
This will generate a `crow_all.h` file which you can use in the following steps
|
||||
!!! warning
|
||||
|
||||
`crow_all.h` is recommended only for small, possibly single source file projects. For larger projects, it is advised to use the multi-header version.
|
||||
|
||||
|
||||
## Setting up your Crow project
|
||||
### Using XCode
|
||||
1. Download and install [Homebrew](https://brew.sh).
|
||||
2. Run `brew install asio` in your terminal.
|
||||
3. Create a new XCode project (macOS -> Command Line Tool).
|
||||
4. Change the following project settings:
|
||||
|
||||
=== "Multiple Headers"
|
||||
|
||||
1. Add header search paths for crow's include folder and asio's folder (`/usr/local/include`, `/usr/local/Cellar/asio/include`, and where you placed Crow's `include` folder)
|
||||
2. Add linker flags (`-lpthread`)
|
||||
|
||||
=== "Single Header"
|
||||
|
||||
1. Place `crow_all.h` inside your project folder and add it to the project in XCode (you need to use File -> Add files to "project_name")
|
||||
2. Add header search paths for asio's folder:
|
||||
1. `/usr/local/include`, and
|
||||
2. **Silicon**: `/opt/homebrew/Cellar/asio/<asio_version>/include`
|
||||
3. **Intel**: `/usr/local/Cellar/asio/<asio_version>/include`
|
||||
3. Add linker flags (`-lpthread` for g++, `-pthread` for clang++)
|
||||
|
||||
5. Write your Crow application in `main.cpp` (something like the Hello World example will work).
|
||||
6. Press `▶` to compile and run your Crow application.
|
||||
|
||||
|
||||
## Building Crow's tests/examples
|
||||
!!! note
|
||||
|
||||
This tutorial can be used for Crow projects built with CMake as well
|
||||
|
||||
1. Download and install [Homebrew](https://brew.sh).
|
||||
2. Run `brew install cmake asio` in your terminal.
|
||||
3. Get Crow's source code (the entire source code).
|
||||
3. Run the following Commands:
|
||||
1. `mkdir build`
|
||||
2. `cd build`
|
||||
3. `cmake ..`
|
||||
4. `make -j12`
|
||||
!!! note
|
||||
|
||||
You can add options like `-DCROW_FEATURES="ssl;compression"` or `-DCROW_AMALGAMATE` to `cmake ..` to build optional tests/examples for HTTP Compression or HTTPS.
|
||||
|
||||
## Compiling using a compiler directly
|
||||
All you need to do is run the following command:
|
||||
```
|
||||
g++ main.cpp -lpthread
|
||||
```
|
||||
!!! note
|
||||
|
||||
You'll need to install GCC via `brew install gcc`. the Clang compiler should be part of XCode or XCode command line tools.
|
||||
|
||||
You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++.
|
||||
|
||||
If GCC throws errors and your program does not compile, you may be using C++03 instead of ≥C++11. Use the flag `-std=c++11`.
|
|
@ -0,0 +1,22 @@
|
|||
Here's how you can install Crow on your Windows machine.
|
||||
## Getting and Compiling Crow
|
||||
### Using A package manager
|
||||
#### VCPKG
|
||||
Crow can be simply installed through VCPKG using the command `vcpkg install crow`
|
||||
|
||||
### Manually (source or release)
|
||||
#### Microsoft Visual Studio and VCPKG
|
||||
The following guide will use `example_with_all.cpp` as the Crow application for demonstration purposes. VCPKG will be used only to install Crow's dependencies.
|
||||
|
||||
1. Generate `crow_all.h` by navigating to the `scripts` folder and running `python3 merge_all.py ..\include crow_all.h`.
|
||||
2. `git clone https://github.com/microsoft/vcpkg.git`
|
||||
3. `.\vcpkg\bootstrap-vcpkg.bat`
|
||||
4. `.\vcpkg\vcpkg integrate install`
|
||||
5. Create empty Visual Studio project.
|
||||
6. In solution explorer, right click the name of your project then click `Open Folder in File Explorer`.
|
||||
7. Copy `crow_all.h`, `example_with_all.cpp`, `vcpkg.json` to opened folder.
|
||||
8. Add `crow_all.h` to `Header Files` and `example_with_all.cpp` to `Source Files`.
|
||||
9. In solution explorer, right click the name of your project then click `Properties`.
|
||||
10. Under `vcpkg`, set `Use Vcpkg Manifest` to `Yes` and `Additional Options` to `--feature-flags="versions"`.
|
||||
11. Set `Debug/Release` and `x64/x86`.
|
||||
12. Run.
|
|
@ -0,0 +1,61 @@
|
|||
This page shows how you can get started with a simple hello world application.
|
||||
|
||||
## 1. Include
|
||||
Starting with an empty `main.cpp` file, first add `#!cpp #include "crow.h"` or `#!cpp #include "crow_all.h"` if you're using the single header file.
|
||||
|
||||
!!! note
|
||||
|
||||
If you are using version v0.3, then you have to put `#!cpp #define CROW_MAIN` at the top of one and only one source file.
|
||||
|
||||
## 2. App declaration
|
||||
Next Create a `main()` and declare a `#!cpp crow::SimpleApp` inside, your code should look like this
|
||||
``` cpp
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
}
|
||||
```
|
||||
The App (or SimpleApp) class organizes all the different parts of Crow and provides the developer (you) a simple interface to interact with these parts.
|
||||
For more information, please go [here](../../guides/app).
|
||||
|
||||
## 3. Adding routes
|
||||
Once you have your app, the next step is to add routes (or endpoints). You can do so with the `CROW_ROUTE` macro.
|
||||
``` cpp
|
||||
CROW_ROUTE(app, "/")([](){
|
||||
return "Hello world";
|
||||
});
|
||||
```
|
||||
For more details on routes, please go [here](../../guides/routes).
|
||||
|
||||
## 4. Running the app
|
||||
Once you're happy with how you defined all your routes, you're going to want to instruct Crow to run your app. This is done using the `run()` method.
|
||||
``` cpp
|
||||
app.port(18080).multithreaded().run();
|
||||
```
|
||||
Please note that the `port()` and `multithreaded()` methods aren't needed, though not using `port()` will cause the default port (`80`) to be used.<br>
|
||||
|
||||
## Putting it all together
|
||||
|
||||
Once you've followed all the steps above, your code should look similar to this
|
||||
|
||||
``` cpp title="main.cpp" linenums="1"
|
||||
#include "crow.h"
|
||||
//#include "crow_all.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app; //define your crow application
|
||||
|
||||
//define your endpoint at the root directory
|
||||
CROW_ROUTE(app, "/")([](){
|
||||
return "Hello world";
|
||||
});
|
||||
|
||||
//set the port, set the app to run on multiple threads, and run the app
|
||||
app.port(18080).multithreaded().run();
|
||||
}
|
||||
```
|
||||
|
||||
You then need to compile your code on your [Linux](../setup/linux#compiling-your-project), [MacOS](../setup/macos#compiling-using-a-compiler-directly), or [Windows](../setup/windows#getting-and-compiling-crow) machine
|
||||
|
||||
After building your `.cpp` file and running the resulting executable, you should be able to access your endpoint at [http://localhost:18080](http://localhost:18080). Opening this URL in your browser will show a white screen with "Hello world" typed on it.
|
|
@ -0,0 +1,38 @@
|
|||
A Crow app defines an interface to allow the developer access to all the different parts of the framework, without having to manually deal with each one.<br><br>
|
||||
An app allows access to the HTTP server (for handling connections), router (for handling URLs and requests), Middlewares (for extending Crow), among many others.<br><br>
|
||||
|
||||
Crow has 2 different app types:
|
||||
|
||||
## SimpleApp
|
||||
Has no middlewares.
|
||||
|
||||
## App<m1, m2, ...>
|
||||
Has middlewares.
|
||||
|
||||
## Using the app
|
||||
To use a Crow app, simply define `#!cpp crow::SimpleApp` or `#!cpp crow::App<m1, m2 ...>` if you're using middlewares.<br>
|
||||
The methods of an app can be chained. That means that you can configure and run your app in the same code line.
|
||||
``` cpp
|
||||
app.bindaddr("192.168.1.2").port(443).ssl_file("certfile.crt","keyfile.key").multithreaded().run();
|
||||
```
|
||||
Or if you like your code neat
|
||||
``` cpp
|
||||
app.bindaddr("192.168.1.2")
|
||||
.port(443)
|
||||
.ssl_file("certfile.crt","keyfile.key")
|
||||
.multithreaded()
|
||||
.run();
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
The `run()` method is blocking. To run a Crow app asynchronously `run_async()` should be used instead.
|
||||
|
||||
!!! warning
|
||||
|
||||
When using `run_async()`, make sure to use a variable to save the function's output (such as `#!cpp auto _a = app.run_async()`). Otherwise the app will run synchronously.
|
||||
|
||||
<br><br>
|
||||
|
||||
For more info on middlewares, check out [this page](../middleware).<br><br>
|
||||
For more info on what functions are available to a Crow app, go [here](../reference/classcrow_1_1_crow.html).
|
|
@ -0,0 +1,71 @@
|
|||
While Crow doesn't directly support HTTP authentication, it does provide all the tools you need to build your own. This tutorial will show you how to setup basic and token authentication using Crow.
|
||||
|
||||
## Shared information
|
||||
Every way boils down to the same basic flow:
|
||||
- The handler calls a verification function.
|
||||
- The handler provides a `request` and \<optionally\> a `response`.
|
||||
- The function returns a `bool` or `enum` status.
|
||||
- Handler either continues or stops executing based on the returned status.
|
||||
- Either the function or handler modify and `end()` the `response` in case of failure.
|
||||
|
||||
For the purposes of this tutorial, we will assume that the verification function is defined as `#!cpp bool verify(crow::request req, crow::response res)`
|
||||
|
||||
## Basic Auth
|
||||
Basic HTTP authentication requires the client to send the Username and Password as a single string, separated by a colon (':') and then encoded as Base64. This data needs to be placed in the `Authorization` header of the request. A sample header using the credentials "Username" and "Password" would look like this: `Authorization: Basic VXNlcm5hbWU6UGFzc3dvcmQ=`.<br><br>
|
||||
|
||||
We don't need to worry about creating the request, we only need to extract the credentials from the `Authorization` header and verify them.
|
||||
!!! note
|
||||
|
||||
There are multiple ways to verify the credentials. Most involve checking the username in a database, then checking a hash of the password against the stored password hash for that username. This tutorial will not go over them
|
||||
|
||||
<br>
|
||||
|
||||
To do this we first need to get the `Authorization` header as a string by using the following code:
|
||||
```cpp
|
||||
std::string myauth = req.get_header_value("Authorization");
|
||||
```
|
||||
<br>
|
||||
|
||||
Next we need to isolate our encoded credentials and decode them as follows:
|
||||
```cpp
|
||||
std::string mycreds = myauth.substr(6);
|
||||
std::string d_mycreds = crow::utility::base64decode(mycreds, mycreds.size());
|
||||
```
|
||||
<br>
|
||||
|
||||
Now that we have our `username:password` string, we only need to separate it into 2 different strings and verify their validity:
|
||||
```cpp
|
||||
size_t found = d_mycreds.find(':');
|
||||
std::string username = d_mycreds.substr(0, found);
|
||||
std::string password = d_mycreds.substr(found+1);
|
||||
|
||||
/*Verify validity of username and password here*/
|
||||
return true; //or false if the username/password are invalid
|
||||
```
|
||||
|
||||
## Token Auth
|
||||
Tokens are some form of unique data that a server can provide to a client in order to verify the client's identity later. While on the surface level they don't provide more security than a strong password, they are designed to be less valuable by being *temporary* and providing *limited access*. Variables like expiration time and access scopes are heavily reliant on the implementation however.<br><br>
|
||||
|
||||
### Access Tokens
|
||||
The kind of the token itself can vary depending on the implementation and project requirements: Many services use randomly generated strings as tokens. Then compare them against a database to retrieve the associated user data. Some services however prefer using data bearing tokens. One example of the latter kind is JWT, which uses JSON strings encoded in Base64 and signed using a private key or an agreed upon secret. While this has the added hassle of signing the token to ensure that it's not been tampered with. It does allow for the client to issue tokens without ever needing to present a password or contact a server. The server would simply be able to verify the signature using the client's public key or secret.<br><br>
|
||||
|
||||
### Using an Access Token
|
||||
Authenticating with an access token usually involves 2 stages: The first being acquiring the access token from an authority (either by providing credentials such as a username and a password to a server or generating a signed token). The scope of the token (what kind of information it can read or change) is usually defined in this step.<br><br>
|
||||
|
||||
The second stage is simply presenting the Token to the server when requesting a resource. This is even simpler than using basic authentication. All the client needs to do is provide the `Authorization` header with a keyword (usually `Bearer`) followed by the token itself (for example: `Authorization: Bearer ABC123`). Once the client has done that the server will need to acquire this token, which can easily be done as follows:<br>
|
||||
|
||||
```cpp
|
||||
std::string myauth = req.get_header_value("Authorization");
|
||||
std::string mycreds = myauth.substr(7); // The length can change based on the keyword used
|
||||
|
||||
/*Verify validity of the token here*/
|
||||
return true; //or false if the token is invalid
|
||||
```
|
||||
<br>
|
||||
The way of verifying the token is largely up to the implementation, and involves either Bearer token decoding and verification, or database access, neither of which is in this tutorial's scope.<br><br>
|
||||
|
||||
### Refresh Tokens
|
||||
Some services may choose to provide a refresh token alongside the access token. This token can be used to request a new access token if the existing one has expired. It provides convenience and security in that it makes it possible to acquire new access tokens without the need to expose a password. The downside however is that it can allow a malicious entity to keep its access to a compromised account. As such refresh tokens need to be handled with care, kept secure, and always invalidated as soon as a client logs out or requests a new access token.
|
||||
|
||||
## Sessions
|
||||
While Crow does not provide built in support for user sessions, a community member was kind enough to provide their own implementation on one of the related issue, their comment along with the code is available [here](https://github.com/CrowCpp/Crow/issues/144#issuecomment-860384771) (Please keep in mind that while we appreciate all efforts to push Crow forward, we cannot provide support for this implementation unless it becomes part of the core project).
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
## Encoding
|
||||
Using `#!cpp crow::utility::base64encode(mystring, mystring.size())` will return a Base64 encoded string. For URL safe Base64 `#!cpp crow::utility::base64encode_urlsafe(mystring, mystring.size())` can be used. The key used in the encoding process can be changed, it is a string containing all 64 characters to be used.
|
||||
|
||||
## Decoding
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Using `#!cpp crow::utility::base64decode(mystring, mystring.size())` with `mystring` being a Base64 encoded string will return a plain-text string. The function works with both normal and URL safe Base64. However it cannot decode a Base64 string encoded with a custom key.
|
|
@ -0,0 +1,42 @@
|
|||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Crow supports Flask-style blueprints.<br>
|
||||
A blueprint is a limited app. It cannot handle networking, but it can handle routes.<br>
|
||||
Blueprints allow developers to compartmentalize their Crow applications, making them much more modular.<br><br>
|
||||
|
||||
In order for a blueprint to work, it has to be registered with a Crow app before the app is run. This can be done using `#!cpp app.register_blueprint(blueprint);`.<br><br>
|
||||
|
||||
Blueprints let you do the following:<br><br>
|
||||
|
||||
### Define Routes
|
||||
You can define routes in a blueprint, similarly to how `#!cpp CROW_ROUTE(app, "/xyz")` works, you can use `#!cpp CROW_BP_ROUTE(blueprint, "/xyz")` to define a blueprint route.
|
||||
|
||||
### Define a Prefix
|
||||
Blueprints can have a prefix assigned to them. This can be done when creating a new blueprint as in `#!cpp crow::blueprint bp("prefix");`. This prefix will be applied to all routes belonging to the blueprint, turning a route such as `/crow/rocks` into `/prefix/crow/rocks`.
|
||||
|
||||
!!! Warning
|
||||
|
||||
Unlike routes, blueprint prefixes should contain no slashes.
|
||||
|
||||
|
||||
### Use a custom Static directory
|
||||
Blueprints let you define a custom static directory (relative to your working directory). This can be done by initializing a blueprint as `#!cpp crow::blueprint bp("prefix", "custom_static");`. This does not have an effect on `#!cpp set_static_file_info()`, it's only for when you want direct access to a file.
|
||||
|
||||
!!! note
|
||||
|
||||
Currently changing which endpoint the blueprint uses isn't possible, so whatever you've set in `CROW_STATIC_ENDPOINT` (default is "static") will be used. Making your final route `/prefix/static/filename`.
|
||||
|
||||
|
||||
### Use a custom Templates directory
|
||||
Similar to static directories, You can set a custom templates directory (relative to your working directory). To do this you initialize the blueprint as `#!cpp crow::blueprint bp("prefix", "custom_static", "custom_templates");`. Any routes defined for the blueprint will use that directory when calling `#!cpp crow::mustache::load("filename.html")`.
|
||||
|
||||
!!! note
|
||||
|
||||
If you want to define a custom templates directory without defining a custom static directory, you can pass the static directory as an empty string. Making your constructor `#!cpp crow::blueprint bp("prefix", "", "custom_templates");`.
|
||||
|
||||
### Define a custom Catchall route
|
||||
You can define a custom catchall route for a blueprint by calling `#!cpp CROW_BP_CATCHALL_ROUTE(blueprint)`. This causes any requests with a URL starting with `/prefix` and no route found to call the blueprint's catchall route. If no catchall route is defined, Crow will default to either the parent blueprint or the app's catchall route.
|
||||
|
||||
### Register other Blueprints
|
||||
Blueprints can also register other blueprints. This is done through `#!cpp blueprint.register_blueprint(blueprint_2);`. The child blueprint's routes become `/prefix/prefix_2/abc/xyz`.
|
|
@ -0,0 +1,21 @@
|
|||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Crow supports Zlib compression using Gzip or Deflate algorithms.
|
||||
|
||||
## HTTP Compression
|
||||
HTTP compression is by default disabled in crow. Do the following to enable it: <br>
|
||||
- Define `CROW_ENABLE_COMPRESSION` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_COMPRESSION` for example) or `set(CROW_FEATURES compression)` in `CMakeLists.txt`.
|
||||
- Call `#!cpp use_compression(crow::compression::algorithm)` on your Crow app.
|
||||
- When compiling your application, make sure that ZLIB is included as a dependency. Either through `-lz` compiler argument or `find_package(ZLIB)` in CMake.
|
||||
|
||||
!!! note
|
||||
|
||||
3<sup>rd</sup> point is not needed for MSVC or CMake projects using `Crow::Crow` since `vcpkg.json` and Crow's target already include zlib as a dependency.
|
||||
|
||||
For the compression algorithm you can use `crow::compression::algorithm::DEFLATE` or `crow::compression::algorithm::GZIP`.<br>
|
||||
And now your HTTP responses will be compressed.
|
||||
|
||||
## Websocket Compression
|
||||
Crow currently does not support Websocket compression.<br>
|
||||
Feel free to discuss the subject with us on GitHub if you're feeling adventurous and want to try to implement it. We appreciate all the help.
|
|
@ -0,0 +1,103 @@
|
|||
Crow contains some middlewares that are ready to be used in your application.
|
||||
<br>
|
||||
Make sure you understand how to enable and use [middleware](../middleware/).
|
||||
|
||||
## Sessions
|
||||
Include: `crow/middlewares/session.h` <br>
|
||||
Examples: `examples/middlewares/session.cpp`
|
||||
|
||||
This middleware can be used for managing sessions - small packets of data associated with a single client that persist across multiple requests. Sessions shouldn't store anything permanent, but only context that is required to easily work with the current client (is the user authenticated, what page did he visit last, etc.).
|
||||
|
||||
### Setup
|
||||
|
||||
Session data can be stored in multiple ways:
|
||||
|
||||
* `crow::InMemoryStore` - stores all data in memory
|
||||
* `crow::FileStore` - stores all all data in json files
|
||||
* A custom store
|
||||
|
||||
__Always list the CookieParser before the Session__
|
||||
```cpp
|
||||
using Session = crow::SessionMiddleware<crow::FileStore>;
|
||||
crow::App<crow::CookieParser, Session> app{Session{
|
||||
crow::FileStore{"/tmp/sessiondata"}
|
||||
}};
|
||||
```
|
||||
|
||||
Session ids are represented as random alphanumeric strings and are stored in cookies. See the examples for more customization options.
|
||||
|
||||
### Usage
|
||||
|
||||
A session is basically a key-value map with support for multiple types: strings, integers, booleans and doubles. The map is created and persisted automatically as soon it is first written to.
|
||||
|
||||
```cpp
|
||||
auto& session = app.get_context<Session>(request);
|
||||
|
||||
session.get("key", "not-found"); // get string by key and return "not-found" if not found
|
||||
session.get("int", -1);
|
||||
session.get<bool>("flag"); // returns default value(false) if not found
|
||||
|
||||
session.set("key", "new value");
|
||||
session.string("any-type"); // return any type as string representation
|
||||
session.remove("key");
|
||||
session.keys(); // return list of keys
|
||||
```
|
||||
|
||||
Session objects are shared between concurrent requests,
|
||||
this means we can perform atomic operations and even lock the object.
|
||||
```cpp
|
||||
session.apply("views", [](int v){return v + 1;}); // this operation is always atomic, no way to get a data race
|
||||
session.mutex().lock(); // manually lock session
|
||||
```
|
||||
|
||||
### Expiration
|
||||
|
||||
Expiration can happen either by the cookie expiring or the store deleting "old" data.
|
||||
|
||||
* By default, cookies expire after 30 days. This can be changed with the cookie option in the Session constructor.
|
||||
* `crow::FileStore` automatically supports deleting files that are expired (older than 30 days). The expiration age can also be changed in the constructor.
|
||||
|
||||
The session expiration can be postponed. This will make the Session issue a new cookie and make the store acknowledge the new expiration time.
|
||||
```cpp
|
||||
session.refresh_expiration()
|
||||
```
|
||||
|
||||
## Cookies
|
||||
Include: `crow/middlewares/cookie_parser.h` <br>
|
||||
Examples: `examples/middlewares/example_cookies.cpp`
|
||||
|
||||
This middleware allows to read and write cookies by using `CookieParser`. Once enabled, it parses all incoming cookies.
|
||||
|
||||
Cookies can be read and written with the middleware context. All cookie attributes can be changed as well.
|
||||
|
||||
```cpp
|
||||
auto& ctx = app.get_context<crow::CookieParser>(request);
|
||||
std::string value = ctx.get_cookie("key");
|
||||
ctx.set_cookie("key", "value")
|
||||
.path("/")
|
||||
.max_age(120);
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
Make sure `CookieParser` is listed before any other middleware that relies on it.
|
||||
|
||||
## CORS
|
||||
Include: `crow/middlewares/cors.h` <br>
|
||||
Examples: `examples/middlewares/example_cors.cpp`
|
||||
|
||||
This middleware allows to set CORS policies by using `CORSHandler`. Once enabled, it will apply the default CORS rules globally.
|
||||
|
||||
The CORS rules can be modified by first getting the middleware via `#!cpp auto& cors = app.get_middleware<crow::CORSHandler>();`. The rules can be set per URL prefix using `prefix()`, per blueprint using `blueprint()`, or globally via `global()`. These will return a `CORSRules` object which contains the actual rules for the prefix, blueprint, or application. For more details go [here](../reference/structcrow_1_1_c_o_r_s_handler.html).
|
||||
|
||||
`CORSRules` can be modified using the methods `origin()`, `methods()`, `headers()`, `max_age()`, `allow_credentials()`, or `ignore()`. For more details on these methods and what default values they take go [here](../reference/structcrow_1_1_c_o_r_s_rules.html).
|
||||
|
||||
```cpp
|
||||
auto& cors = app.get_middleware<crow::CORSHandler>();
|
||||
cors
|
||||
.global()
|
||||
.headers("X-Custom-Header", "Upgrade-Insecure-Requests")
|
||||
.methods("POST"_method, "GET"_method)
|
||||
.prefix("/cors")
|
||||
.origin("example.com");
|
||||
```
|
|
@ -0,0 +1,50 @@
|
|||
Crow has built in support for JSON data.<br><br>
|
||||
|
||||
## type
|
||||
The types of values that `rvalue and wvalue` can take are as follows:<br>
|
||||
|
||||
- `False`: from type `bool`.
|
||||
- `True`: from type `bool`.
|
||||
- `Number`
|
||||
- `Floating_point`: from type `double`.
|
||||
- `Signed_integer`: from type `int`.
|
||||
- `Unsigned_integer`: from type `unsigned int`.
|
||||
- `String`: from type `std::string`.
|
||||
- `List`: from type `std::vector`.
|
||||
- `Object`: from type `crow::json::wvalue or crow::json::rvalue`.<br>
|
||||
This last type means that `rvalue or wvalue` can have keys.
|
||||
|
||||
## rvalue
|
||||
JSON read value, used for taking a JSON string and parsing it into `crow::json`.<br><br>
|
||||
|
||||
You can read individual items of the rvalue, but you cannot add items to it.<br>
|
||||
To do that, you need to convert it to a `wvalue`, which can be done by simply writing `#!cpp crow::json::wvalue wval (rval);` (assuming `rval` is your `rvalue`).<br><br>
|
||||
|
||||
For more info on read values go [here](../reference/classcrow_1_1json_1_1rvalue.html).<br><br>
|
||||
|
||||
## wvalue
|
||||
JSON write value, used for creating, editing and converting JSON to a string.<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
setting a `wvalue` to object type can be done by simply assigning a value to whatever string key you like, something like `#!cpp wval["key1"] = val1;`. Keep in mind that val1 can be any of the above types.
|
||||
|
||||
A `wvalue` can be treated as an object or even a list (setting a value by using `json[3] = 32` for example). Please note that this will remove the data in the value if it isn't of List type.
|
||||
|
||||
!!! warning
|
||||
|
||||
JSON does not allow floating point values like `NaN` or `INF`, Crow will output `null` instead of `NaN` or `INF` when converting `wvalue` to a string. (`{"Key": NaN}` becomes `{"Key": null}`)
|
||||
|
||||
<br><br>
|
||||
|
||||
Additionally, a `wvalue` can be initialized as an object using an initializer list, an example object would be `wvalue x = {{"a", 1}, {"b", 2}}`. Or as a list using `wvalue x = json::wvalue::list({1, 2, 3})`, lists can include any type that `wvalue` supports.<br><br>
|
||||
|
||||
An object type `wvalue` uses `std::unordered_map` by default, if you want to have your returned `wvalue` key value pairs be sorted (using `std::map`) you can add `#!cpp #define CROW_JSON_USE_MAP` to the top of your program.<br><br>
|
||||
|
||||
A JSON `wvalue` can be returned directly inside a route handler, this will cause the `content-type` header to automatically be set to `Application/json` and the JSON value will be converted to string and placed in the response body. For more information go to [Routes](../routes).<br><br>
|
||||
|
||||
For more info on write values go [here](../reference/classcrow_1_1json_1_1wvalue.html).
|
||||
|
||||
!!! note
|
||||
|
||||
Crow's json exceptions can be disabled by using the `#!cpp #define CROW_JSON_NO_ERROR_CHECK` macro. This should increase the program speed with the drawback of having unexpected behavious when used incorrectly (e.g. by attempting to parse an invalid json object).
|
|
@ -0,0 +1,60 @@
|
|||
Crow comes with a simple and easy to use logging system.<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
Currently Crow's Logger is not linked to the Crow application, meaning if an executable has more than one Crow application they'll be sharing any variables or classes relating to Logging.
|
||||
|
||||
## Setting up logging level
|
||||
You can set up the level at which crow displays logs by using the app's `loglevel(crow::LogLevel)` method.<br><br>
|
||||
|
||||
The available log levels are as follows (please note that setting a level will also display all logs below this level):
|
||||
|
||||
- Debug
|
||||
- Info
|
||||
- Warning
|
||||
- Error
|
||||
- Critical
|
||||
<br><br>
|
||||
|
||||
To set a logLevel, just use `#!cpp app.loglevel(crow::LogLevel::Warning)`, This will not show any debug or info logs. It will however still show error and critical logs.<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
Setting the Macro `CROW_ENABLE_DEBUG` during compilation will also set the log level to `Debug` (unless otherwise set using `loglevel()`).
|
||||
|
||||
|
||||
## Writing a log
|
||||
Writing a log is as simple as `#!cpp CROW_LOG_<LOG LEVEL> << "Hello";` (replace<LOG LEVEL> with the actual level in all caps, so you have `CROW_LOG_WARNING`).
|
||||
|
||||
!!! note
|
||||
|
||||
Log times are reported in GMT timezone by default. This is because HTTP requires all reported times for requests and responses to be in GMT. This can be changed by using the macro `CROW_USE_LOCALTIMEZONE` which will set **only the log timezone** to the server's local timezone.
|
||||
|
||||
## Creating A custom logger
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Assuming you have an existing logger or Crow's default format just doesn't work for you. Crow allows you to use a custom logger for any log made using the `CROW_LOG_<LOG LEVEL>` macro.<br>
|
||||
All you need is a class extending `#!cpp crow::ILogHandler` containing the method `#!cpp void log(std::string, crow::LogLevel)`.<br>
|
||||
Once you have your custom logger, you need to set it via `#!cpp crow::logger::setHandler(&MyLogger);`. Here's a full example:<br>
|
||||
```cpp
|
||||
class CustomLogger : public crow::ILogHandler {
|
||||
public:
|
||||
CustomLogger() {}
|
||||
void log(std::string message, crow::LogLevel /*level*/) {
|
||||
// "message" doesn't contain the timestamp and loglevel
|
||||
// prefix the default logger does and it doesn't end
|
||||
// in a newline.
|
||||
std::cerr << message << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CustomLogger logger;
|
||||
crow::logger::setHandler(&logger);
|
||||
|
||||
crow::SimpleApp app;
|
||||
CROW_ROUTE(app, "/")([]() { return "Hello"; });
|
||||
app.run();
|
||||
}
|
||||
```
|
|
@ -0,0 +1,95 @@
|
|||
Middleware is used for altering and inspecting requests before and after calling the handler. In Crow it's very similar to middleware in other web frameworks.
|
||||
|
||||
All middleware is registered in the Crow application
|
||||
|
||||
```cpp
|
||||
crow::App<FirstMW, SecondMW, ThirdMW> app;
|
||||
```
|
||||
|
||||
and is called in this specified order.
|
||||
|
||||
Any middleware requires the following 3 members:
|
||||
|
||||
* A context struct for storing request local data.
|
||||
* A `before_handle` method, which is called before the handler.
|
||||
* A `after_handle` method, which is called after the handler.
|
||||
|
||||
!!! warning
|
||||
|
||||
As soon as `response.end()` is called, no other handlers and middleware is run, except for after_handlers of already visited middleware.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
A middleware that can be used to guard admin handlers
|
||||
|
||||
```cpp
|
||||
struct AdminAreaGuard
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{
|
||||
if (req.remote_ip_address != ADMIN_IP)
|
||||
{
|
||||
res.code = 403;
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
void after_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### before_handle and after_handle
|
||||
There are two possible signatures for before_handle and after_handle
|
||||
|
||||
1. if you only need to access this middleware's context.
|
||||
|
||||
```cpp
|
||||
void before_handle(request& req, response& res, context& ctx)
|
||||
```
|
||||
|
||||
2. To get access to other middlewares context
|
||||
|
||||
``` cpp
|
||||
template <typename AllContext>
|
||||
void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
|
||||
{
|
||||
auto other_ctx = all_ctx.template get<OtherMiddleware>();
|
||||
}
|
||||
```
|
||||
|
||||
## Local middleware
|
||||
|
||||
By default, every middleware is called for each request. If you want to enable middleware for specific handlers or blueprints, you have to extend it from `crow::ILocalMiddleware`
|
||||
|
||||
```cpp
|
||||
struct LocalMiddleware : crow::ILocalMiddleware
|
||||
{
|
||||
```
|
||||
|
||||
After this, you can enable it for specific handlers
|
||||
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/with_middleware")
|
||||
.CROW_MIDDLEWARES(app, LocalMiddleware)
|
||||
([]() {
|
||||
return "Hello world!";
|
||||
});
|
||||
```
|
||||
|
||||
or blueprints
|
||||
|
||||
```cpp
|
||||
Blueprint bp("with_middleware");
|
||||
bp.CROW_MIDDLEWARES(app, FistLocalMiddleware, SecondLocalMiddleware);
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
Local and global middleware are called separately. First all global middleware is run, then all enabled local middleware for the current handler is run. In both cases middleware is called strongly
|
||||
in the order listed in the Crow application.
|
|
@ -0,0 +1,32 @@
|
|||
<span class="tag">[:octicons-feed-tag-16: v0.2](https://github.com/CrowCpp/Crow/releases/0.2)</span>
|
||||
|
||||
|
||||
Multipart is a way of forming HTTP requests or responses to contain multiple distinct parts.<br>
|
||||
|
||||
Such an approach allows a request to contain multiple different pieces of data with potentially conflicting data types in a single response payload.<br>
|
||||
It is typically used either in HTML forms, or when uploading multiple files.<br><br>
|
||||
|
||||
## How multipart messages work
|
||||
The structure of a multipart request is typically consistent of:<br>
|
||||
|
||||
- A Header: Typically `multipart/form-data;boundary=<boundary>`, This defines the HTTP message as being multipart, as well as defining the separator used to distinguish the different parts.<br>
|
||||
- 1 or more parts:
|
||||
- `--<boundary>`
|
||||
- Part header: typically `content-disposition: mime/type; name="<fieldname>"` (`mime/type` should be replaced with the actual mime-type), can also contain a `filename` property (separated from the rest by a `;` and structured similarly to the `name` property)
|
||||
- Value
|
||||
- `--<boundary>--`<br><br>
|
||||
|
||||
## Multipart messages in Crow
|
||||
Crow supports multipart requests and responses though `crow::multipart::message`.<br>
|
||||
A message can be created either by defining the headers, boundary, and individual parts and using them to create the message. or simply by reading a `crow::request`.<br><br>
|
||||
|
||||
Once a multipart message has been made, the individual parts can be accessed throughout `msg.parts`, `parts` is an `std::vector`.<br><br>
|
||||
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Part headers are organized in a similar way to request and response headers, and can be retrieved via `crow::multipart::get_header_object("header-key")`. This function returns a `crow::multipart::header` object.<br><br>
|
||||
|
||||
The message's individual body parts can be accessed by name using `msg.get_part_by_name("part-name")`.<br><br>
|
||||
|
||||
For more info on Multipart messages, go [here](../reference/namespacecrow_1_1multipart.html)
|
|
@ -0,0 +1,61 @@
|
|||
You can set Crow up behind any HTTP proxy of your liking, but we will be focusing specifically on 2 of the most popular web server software solutions, Apache2 and Nginx.<br><br>
|
||||
|
||||
A reverse proxy allows you to use Crow without exposing it directly to the internet. It also allows you to, for example, have crow run on a certain specific domain name, subdomain, or even a path, such as `domain.abc/crow`.<br><br>
|
||||
|
||||
We advise that you set crow up behind some form of reverse proxy if you plan on running a production Crow server that isn't local.<br>
|
||||
|
||||
!!! warning "SSL"
|
||||
|
||||
When using a proxy, make sure that you **do not** compile Crow with SSL enabled. SSL should be handled by the proxy.
|
||||
|
||||
## Apache2
|
||||
|
||||
Assuming you have both Apache2 and the modules [proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html), [proxy_http](https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html), [proxy_html](https://httpd.apache.org/docs/2.4/mod/mod_proxy_html.html) (if you plan on serving HTML pages), and [proxy_wstunnel](https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html) (if you plan on using websockets). You will need to enable those modules, which you can do using the following commands:
|
||||
|
||||
```sh
|
||||
a2enmod proxy
|
||||
a2enmod proxy_http
|
||||
a2enmod proxy_html
|
||||
a2enmod proxy_wstunnel
|
||||
```
|
||||
|
||||
Next up you'll need to change your configuration (default is `/etc/apache2/sites-enabled/000-default.conf`) and add the following lines (replace `localhost` and `40080` with the address and port you defined for your Crow App):
|
||||
```
|
||||
ProxyPass / http://localhost:40080
|
||||
ProxyPassReverse / http://localhost:40080
|
||||
```
|
||||
If you want crow to run in a subdirectory (such as `domain.abc/crow`) you can use the `location` tag:
|
||||
```
|
||||
<Location "/crow">
|
||||
|
||||
ProxyPass http://localhost:40080
|
||||
|
||||
ProxyPassReverse http://localhost:40080
|
||||
|
||||
</Location>
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
If you're using an Arch Linux based OS. You will have to access `/etc/httpd/conf/httpd.conf` to enable modules and change configuration.
|
||||
|
||||
## Nginx
|
||||
|
||||
Setting Nginx up is slightly simpler than Apache, all you need is the Nginx package itself. Once you've installed it, go to the configuration file (usually a `.conf` file located in `/etc/nginx`) and add the following lines to your server section (replace `localhost` and `40080` with the address and port you defined for your Crow App):
|
||||
|
||||
```
|
||||
location / {
|
||||
proxy_pass http://localhost:40080/;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
```
|
||||
Remember to remove or comment out any existing `location /` section.<br><br>
|
||||
|
||||
Alternatively, if you want to use a subdirectory, you can simply change the location parameter as such:
|
||||
|
||||
```
|
||||
location /crow/ {
|
||||
proxy_pass http://localhost:40080/;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
```
|
|
@ -0,0 +1,39 @@
|
|||
A query string is the part of the URL that comes after a `?` character, it is usually formatted as `key=value&otherkey=othervalue`.
|
||||
<br><br>
|
||||
|
||||
Crow supports query strings through `crow::request::url_params`. The object is of type `crow::query_string` and can has the following functions:<br>
|
||||
|
||||
## get(name)
|
||||
Returns the value (as char*) based on the given key (or name). Returns `nullptr` if the key is not found.
|
||||
## pop(name)
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Works the same as `get`, but removes the returned value.
|
||||
!!! note
|
||||
|
||||
`crow::request::url_params` is a const value, therefore for pop (also pop_list and pop_dict) to work, a copy needs to be made.
|
||||
|
||||
## get_list(name)
|
||||
A URL can be `http://example.com?key[]=value1&key[]=value2&key[]=value3`. Using `get_list("key")` on such a URL returns an `std::vector<std::string>` containing `[value1, value2, value3]`.<br><br>
|
||||
|
||||
`#!cpp get_list("key", false)` can be used to parse `http://example.com?key=value1&key=value2&key=value3`
|
||||
## pop_list(name)
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Works the same as `get_list` but removes all instances of values having the given key (`use_brackets` is also available here).
|
||||
## get_dict(name)
|
||||
Returns an `std::unordered_map<std::string, std::string>` from a query string such as `?key[sub_key1]=value1&key[sub_key2]=value2&key[sub_key3]=value3`.<br>
|
||||
The key in the map is what's in the brackets (`sub_key1` for example), and the value being what's after the `=` sign (`value1`). The name passed to the function is not part of the returned value.
|
||||
## pop_dict(name)
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Works the same as `get_dict` but removing the values from the query string.
|
||||
!!! warning
|
||||
|
||||
if your query string contains both a list and dictionary with the same key, it is best to use `pop_list` before either `get_dict` or `pop_dict`, since a map cannot contain more than one value per key, each item in the list will override the previous and only the last will remain with an empty key.
|
||||
|
||||
<br><br>
|
||||
For more information take a look [here](../reference/classcrow_1_1query__string.html).
|
|
@ -0,0 +1,195 @@
|
|||
Routes define what happens when your client connects to a certain URL.<br>
|
||||
|
||||
## Macro
|
||||
`CROW_ROUTE(app, url)`<br>
|
||||
Can be replaced with `#!cpp app.route<crow::black_magick::get_parameter_tag(url)>(url)` or `#!cpp app.route_dynamic(url)` if you're using VS2013 or want runtime URL evaluation. Although this usage is **NOT** recommended.
|
||||
## App
|
||||
Which app class to assign the route to.
|
||||
## Path (URL)
|
||||
Which relative path is assigned to the route.<br>
|
||||
Using `/hello` means the client will need to access `http://example.com/hello` in order to access the route.<br>
|
||||
A path can have parameters, for example `/hello/<int>` will allow a client to input an int into the url which will be in the handler (something like `http://example.com/hello/42`).<br>
|
||||
Parameters can be `<int>`, `<uint>`, `<double>`, `<string>`, or `<path>`.<br>
|
||||
It's worth noting that the parameters also need to be defined in the handler, an example of using parameters would be to add 2 numbers based on input:
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](int a, int b)
|
||||
{
|
||||
return std::to_string(a+b);
|
||||
});
|
||||
```
|
||||
you can see the first `<int>` is defined as `a` and the second as `b`. If you were to run this and call `http://example.com/add/1/2`, the result would be a page with `3`. Exciting!
|
||||
|
||||
## Methods
|
||||
You can change the HTTP methods the route uses from just the default `GET` by using `method()`, your route macro should look like `CROW_ROUTE(app, "/add/<int>/<int>").methods(crow::HTTPMethod::GET, crow::HTTPMethod::PATCH)` or `CROW_ROUTE(app, "/add/<int>/<int>").methods("GET"_method, "PATCH"_method)`.
|
||||
|
||||
!!! note
|
||||
|
||||
Crow handles `OPTIONS` method automatically. The `HEAD` method is handled automatically unless defined in a route.
|
||||
Adding `OPTIONS` to a route's methods has no effect.
|
||||
|
||||
Crow defines the following methods:
|
||||
```
|
||||
DELETE
|
||||
GET
|
||||
HEAD
|
||||
POST
|
||||
PUT
|
||||
|
||||
CONNECT
|
||||
OPTIONS
|
||||
TRACE
|
||||
|
||||
PATCH
|
||||
PURGE
|
||||
|
||||
COPY
|
||||
LOCK
|
||||
MKCOL
|
||||
MOVE
|
||||
PROPFIND
|
||||
PROPPATCH
|
||||
SEARCH
|
||||
UNLOCK
|
||||
BIND
|
||||
REBIND
|
||||
UNBIND
|
||||
ACL
|
||||
|
||||
REPORT
|
||||
MKACTIVITY
|
||||
CHECKOUT
|
||||
MERGE
|
||||
|
||||
SEARCH
|
||||
NOTIFY
|
||||
SUBSCRIBE
|
||||
UNSUBSCRIBE
|
||||
|
||||
MKCALENDAR
|
||||
|
||||
LINK
|
||||
UNLINK
|
||||
|
||||
SOURCE
|
||||
```
|
||||
|
||||
## Handler
|
||||
Basically a piece of code that gets executed whenever the client calls the associated route, usually in the form of a [lambda expression](https://en.cppreference.com/w/cpp/language/lambda). It can be as simple as `#!cpp ([](){return "Hello World"})`.<br><br>
|
||||
|
||||
### Request
|
||||
Handlers can also use information from the request by adding it as a parameter `#!cpp ([](const crow::request& req){...})`.<br><br>
|
||||
|
||||
You can also access the URL parameters in the handler using `#!cpp req.url_params.get("param_name");`. If the parameter doesn't exist, `nullptr` is returned.<br><br>
|
||||
|
||||
|
||||
!!! note "Note <span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>"
|
||||
|
||||
parameters inside the body can be parsed using `#!cpp req.get_body_params();`. which is useful for requests of type `application/x-www-form-urlencoded`. Its format is similar to `url_params`.
|
||||
|
||||
|
||||
For more information on `crow::request` go [here](../reference/structcrow_1_1request.html).<br><br>
|
||||
|
||||
### Response
|
||||
Crow also provides the ability to define a response in the parameters by using `#!cpp ([](crow::response& res){...})`.<br><br>
|
||||
|
||||
Please note that in order to return a response defined as a parameter you'll need to use `res.end();`.<br><br>
|
||||
|
||||
Alternatively, you can define the response in the body and return it (`#!cpp ([](){return crow::response()})`).<br>
|
||||
|
||||
For more information on `crow::response` go [here](../reference/structcrow_1_1response.html).<br><br>
|
||||
|
||||
Crow defines the following status codes:
|
||||
```
|
||||
100 Continue
|
||||
101 Switching Protocols
|
||||
|
||||
200 OK
|
||||
201 Created
|
||||
202 Accepted
|
||||
203 Non-Authoritative Information
|
||||
204 No Content
|
||||
205 Reset Content
|
||||
206 Partial Content
|
||||
|
||||
300 Multiple Choices
|
||||
301 Moved Permanently
|
||||
302 Found
|
||||
303 See Other
|
||||
304 Not Modified
|
||||
307 Temporary Redirect
|
||||
308 Permanent Redirect
|
||||
|
||||
400 Bad Request
|
||||
401 Unauthorized
|
||||
403 Forbidden
|
||||
404 Not Found
|
||||
405 Method Not Allowed
|
||||
407 Proxy Authentication Required
|
||||
409 Conflict
|
||||
410 Gone
|
||||
413 Payload Too Large
|
||||
415 Unsupported Media Type
|
||||
416 Range Not Satisfiable
|
||||
417 Expectation Failed
|
||||
428 Precondition Required
|
||||
429 Too Many Requests
|
||||
451 Unavailable For Legal Reasons
|
||||
|
||||
500 Internal Server Error
|
||||
501 Not Implemented
|
||||
502 Bad Gateway
|
||||
503 Service Unavailable
|
||||
504 Gateway Timeout
|
||||
506 Variant Also Negotiates
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
If your status code is not defined in the list above (e.g. `crow::response(123)`) Crow will return `500 Internal Server Error` instead.
|
||||
|
||||
|
||||
### Return statement
|
||||
A `crow::response` is very strictly tied to a route. If you can have something in a response constructor, you can return it in a handler.<br><br>
|
||||
The main return type is `std::string`, although you could also return a `crow::json::wvalue` or `crow::multipart::message` directly.<br><br>
|
||||
For more information on the specific constructors for a `crow::response` go [here](../reference/structcrow_1_1response.html).
|
||||
|
||||
## Returning custom classes
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
If you have your own class you want to return (without converting it to string and returning that), you can use the `crow::returnable` class.<br>
|
||||
to use the returnable class, you only need your class to publicly extend `crow::returnable`, add a `dump()` method that returns your class as an `std::string`, and add a constructor that has a `Content-Type` header as a string argument.<br><br>
|
||||
|
||||
Your class should look like the following:
|
||||
```cpp
|
||||
class a : public crow::returnable
|
||||
{
|
||||
a() : returnable("text/plain"){};
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
std::string dump() const override
|
||||
{
|
||||
return this.as_string();
|
||||
}
|
||||
}
|
||||
```
|
||||
<br><br>
|
||||
|
||||
## Response codes
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Instead of assigning a response code, you can use the `crow::status` enum, for example you can replace `crow::response(200)` with `crow::response(crow::status::OK)`
|
||||
|
||||
## Catchall routes
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
By default, any request that Crow can't find a route for will return a simple 404 response. You can change that to return a default route using the `CROW_CATCHALL_ROUTE(app)` macro. Defining it is identical to a normal route, even when it comes to the `const crow::request&` and `crow::response&` parameters being optional.
|
||||
!!! note
|
||||
|
||||
For versions higher than 0.3 (excluding patches), Catchall routes handle 404 and 405 responses. The default response will contain the code 404 or 405.
|
|
@ -0,0 +1,17 @@
|
|||
Crow supports HTTPS though SSL or TLS.<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
When mentioning SSL in this documentation, it is often a reference to openSSL, which includes TLS.<br><br>
|
||||
|
||||
|
||||
To enable SSL, first your application needs to define either a `.crt` and `.key` files, or a `.pem` file. Once you have your files, you can add them to your app like this:<br>
|
||||
`#!cpp app.ssl_file("/path/to/cert.crt", "/path/to/keyfile.key")` or `#!cpp app.ssl_file("/path/to/pem_file.pem")`. Please note that this method can be part of the app method chain, which means it can be followed by `.run()` or any other method.<br><br>
|
||||
|
||||
You also need to define `CROW_ENABLE_SSL` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_SSL` for example) or `set(CROW_FEATURES ssl)` in `CMakeLists.txt`.
|
||||
|
||||
You can also set your own SSL context (by using `asio::ssl::context ctx`) and then applying it via the `#!cpp app.ssl(ctx)` method.<br><br>
|
||||
|
||||
!!! warning
|
||||
|
||||
If you plan on using a proxy like Nginx or Apache2, **DO NOT** use SSL in crow, instead define it in your proxy and keep the connection between the proxy and Crow non-SSL.
|
|
@ -0,0 +1,26 @@
|
|||
<span class="tag">[:octicons-feed-tag-16: v0.2](https://github.com/CrowCpp/Crow/releases/0.2)</span>
|
||||
|
||||
|
||||
A static file is any file that resides in the server's storage.
|
||||
|
||||
Crow supports returning static files as responses either implicitly or explicitly.
|
||||
|
||||
## Implicit
|
||||
Crow implicitly returns any static files placed in a `static` directory and any subdirectories, as long as the user calls the endpoint `/static/path/to/file`.<br><br>
|
||||
The static folder or endpoint can be changed by defining the macros `CROW_STATIC_DIRECTORY "alternative_directory/"` and `CROW_STATIC_ENDPOINT "/alternative_endpoint/<path>"`.<br>
|
||||
static directory changes the directory in the server's file system, while the endpoint changes the URL that the client needs to access.
|
||||
|
||||
## Explicit
|
||||
You can directly return a static file by using the `crow::response` method `#!cpp response.set_static_file_info("path/to/file");`. The path is relative to the working directory.
|
||||
|
||||
!!! Warning
|
||||
|
||||
The path to the file is sanitized by default. it should be fine for most circumstances but if you know what you're doing and need the sanitizer off you can use `#!cpp response.set_static_file_info_unsafe("path/to/file")` instead.
|
||||
|
||||
!!! note
|
||||
|
||||
Crow sets the `content-type` header automatically based on the file's extension, if an extension is unavailable or undefined, Crow uses `text/plain`, if you'd like to explicitly set a `content-type`, use `#!cpp response.set_header("content-type", "mime/type");` **AFTER** calling `set_static_file_info`.
|
||||
|
||||
!!! note
|
||||
|
||||
Please keep in mind that using the `set_static_file_info` method means any data already in your response body is ignored and not sent to the client.
|
|
@ -0,0 +1,36 @@
|
|||
Using Systemd allows you to run any executable or script when the system starts. This can be useful when you don't want to re-run your Crow application every single time you restart your server.<br><br>
|
||||
|
||||
## Writing the Service Unit File
|
||||
In order to have Systemd recognize your application, you need to create a `.service` file that explains how Systemd should handle your program.<br><br>
|
||||
|
||||
To create a service file, you need to go to `/etc/systemd/system` and create an empty text file with the extension `.service`, the file name can be anything.<br><br>
|
||||
|
||||
Once the file is created, open it using your favorite text editor and add the following:
|
||||
|
||||
```sh
|
||||
[Unit]
|
||||
Description=My revolutionary Crow application
|
||||
|
||||
Wants=network.target
|
||||
After=syslog.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/absolute/path/to/your/executable
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
KillMode=process
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
You will then need to give the correct permission, this can be done by using the following command (a `sudo` maybe required):
|
||||
|
||||
```sh
|
||||
chmod 640 /etc/systemd/system/crowthing.service
|
||||
```
|
||||
|
||||
And that's it! You can now use your `systemctl` controls to `enable`, `start`, `stop`, or `disable` your Crow application.<br><br>
|
||||
|
||||
If you're not familiar with Systemd, `systemctl enable crowthing.service` will allow your Crow application to run at startup, `start` will start it, and the rest is simple.
|
|
@ -0,0 +1,52 @@
|
|||
Templating is when you return an HTML page with custom data. You can probably tell why that's useful.<br><br>
|
||||
|
||||
Crow supports [mustache](http://mustache.github.io) for templates through its own implementation `crow::mustache`.<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
Currently Crow's Mustache implementation is not linked to the Crow application, meaning if an executable has more than one Crow application they'll be sharing any variables or classes relating to template loading and compiling.
|
||||
|
||||
## Components of mustache
|
||||
|
||||
There are 2 components of a mustache template implementation:
|
||||
|
||||
- Page
|
||||
- Context
|
||||
|
||||
### Page
|
||||
The HTML page (including the mustache tags). It is usually loaded into `crow::mustache::template_t`. It needs to be placed in the *templates directory* which should be directly inside the current working directory of the crow executable.<br><br>
|
||||
|
||||
The templates directory is usually called `templates`, but can be adjusted per Route (via `crow::mustache::set_base("new_templates_directory")`), per [Blueprint](../blueprints), or globally (via `crow::mustache::set_global_base("new_templates_directory"")`).<br><br>
|
||||
|
||||
For more information on how to formulate a template, see [this mustache manual](http://mustache.github.io/mustache.5.html).
|
||||
|
||||
### Context
|
||||
A JSON object containing the tags as keys and their values. `crow::mustache::context` is actually a [crow::json::wvalue](../json#wvalue).<br><br>
|
||||
|
||||
!!! note
|
||||
|
||||
`crow::mustache::context` can take a C++ lambda as a value. The lambda needs to take a string as an argument and return a string, such as `#!cpp ctx[lmd] = [&](std::string){return "Hello World";};`.
|
||||
|
||||
!!! note
|
||||
|
||||
The string returned by the lamdba can contain mustache tags, Crow will parse it as any normal template string.
|
||||
|
||||
## Returning a template
|
||||
To return a mustache template, you need to load a page using `#!cpp auto page = crow::mustache::load("path/to/template.html");`. Or just simply load a string using `#!cpp auto page = crow::mustache::compile("my mustache {{value}}");`. Keep in mind that the path is relative to the templates directory.
|
||||
|
||||
!!! note
|
||||
|
||||
You can also use `#!cpp auto page = crow::mustache::load_text("path/to/template.html");` if you want to load a template without mustache processing.
|
||||
|
||||
!!! Warning
|
||||
|
||||
The path to the template is sanitized by default. it should be fine for most circumstances but if you know what you're doing and need the sanitizer off you can use `#!cpp crow::mustache::load_unsafe()` instead.
|
||||
|
||||
<br>
|
||||
You also need to set up the context by using `#!cpp crow::mustache::context ctx;`. Then you need to assign the keys and values, this can be done the same way you assign values to a JSON write value (`ctx["key"] = value;`).<br>
|
||||
With your context and page ready, just `#!cpp return page.render(ctx);`. This will use the context data to return a filled template.<br>
|
||||
Alternatively you could just render the page without a context using `#!cpp return page.render();`.
|
||||
|
||||
!!! note
|
||||
|
||||
`#!cpp page.render();` returns a crow::returnable class in order to set the `Content-Type` header. to get a simple string, use `#!cpp page.render_string()` instead.
|
|
@ -0,0 +1,77 @@
|
|||
Unit tests can be written in 2 ways for a Crow application.<br><br>
|
||||
|
||||
## The handler method
|
||||
Crow allows users to handle requests that may not come from the network. This is done by calling the `handle(req, res)` method and providing a request and response objects. Which causes crow to identify and run the appropriate handler, returning the resulting response.
|
||||
|
||||
```cpp linenums="1"
|
||||
CROW_ROUTE(app, "/place")
|
||||
([] { return "hi"; });
|
||||
|
||||
app.validate(); //Used to make sure all the route handlers are in order.
|
||||
|
||||
{
|
||||
request req;
|
||||
response res;
|
||||
|
||||
req.url = "/place";
|
||||
|
||||
app.handle(req, res); //res will contain a code of 200, and a response body of "hi"
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This method is the simpler of the two and is usually all you really need to test your routes.
|
||||
|
||||
|
||||
!!! warning
|
||||
|
||||
This method does not send any data, nor does it run any post handle code, so things like static file serving (as far as sending the actual data) or compression cannot be tested using this method.
|
||||
|
||||
|
||||
## The client method
|
||||
This method involves creating a simple [ASIO](https://think-async.com/Asio/) client that sends the request and receives the response. It is considerably more complex than the earlier method, but it is both more realistic and includes post handle operations.
|
||||
|
||||
```cpp linenums="1"
|
||||
static char buf[2048];
|
||||
SimpleApp app;
|
||||
CROW_ROUTE(app, "/")([] { return "A"; });
|
||||
|
||||
auto _ = async(launch::async,[&] { app1.bindaddr("127.0.0.1").port(45451).run(); });
|
||||
app.wait_for_server_start();
|
||||
|
||||
std::string sendmsg = "GET /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n";
|
||||
asio::io_service is;
|
||||
{
|
||||
asio::ip::tcp::socket c(is);
|
||||
c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 45451));
|
||||
|
||||
c.send(asio::buffer(sendmsg));
|
||||
|
||||
size_t recved = c.receive(asio::buffer(buf, 2048));
|
||||
CHECK('A' == buf[recved - 1]); //This is specific to catch2 testing library, but it should give a general idea of how to read the response.
|
||||
}
|
||||
|
||||
app.stop(); //THIS MUST RUN
|
||||
}
|
||||
|
||||
```
|
||||
The first part is straightforward, create an app and add a route.<br>
|
||||
The second part is launching the app asynchronously and waiting until it starts.<br>
|
||||
The third is formulating our HTTP request string, the format is:
|
||||
```
|
||||
METHOD /
|
||||
Content-Length:123
|
||||
header1:value1
|
||||
header2:value2
|
||||
|
||||
BODY
|
||||
|
||||
```
|
||||
Next an `io_service` is created, then a TCP socket is created with the `io_service` and is connected to the application.<br>
|
||||
Then send the HTTP request string through the socket inside a buffer, and read the result into the buffer in `line 1`.<br>
|
||||
Finally check the result against the expected one.
|
||||
|
||||
!!! warning
|
||||
|
||||
Be absolutely sure that the line `app.stop()` runs, whether the test fails or succeeds. Not running it WILL CAUSE OTHER TESTS TO FAIL AND THE TEST TO HANG UNTIL THE PROCESS IS TERMINATED.
|
|
@ -0,0 +1,51 @@
|
|||
Websockets are a way of connecting a client and a server without the request response nature of HTTP.<br><br>
|
||||
|
||||
## Routes
|
||||
To create a websocket in Crow, you need a websocket route.<br>
|
||||
A websocket route differs from a normal route quite a bit. It uses a slightly altered `CROW_WEBSOCKET_ROUTE(app, "/url")` macro, which is then followed by a series of methods (with handlers inside) for each event. These are (sorted by order of execution):
|
||||
|
||||
|
||||
- `#!cpp onaccept([&](const crow::request& req, void** userdata){handler code goes here})`
|
||||
- `#!cpp onopen([&](crow::websocket::connection& conn){handler code goes here})`
|
||||
- `#!cpp onmessage([&](crow::websocket::connection& conn, const std::string& message, bool is_binary){handler code goes here})`
|
||||
- `#!cpp onerror([&](crow::websocket::connection& conn, const std::string& error_message){handler code goes here})`
|
||||
- `#!cpp onclose([&](crow::websocket::connection& conn, const std::string& reason){handler code goes here})`
|
||||
|
||||
!!! note
|
||||
|
||||
`onaccept` must return a boolean. In case `false` is returned, the connection is shut down, deleted, and no further communication is done.
|
||||
|
||||
!!! Warning
|
||||
|
||||
By default, Crow allows clients to send unmasked websocket messages. This is useful for debugging, but goes against the protocol specifications. Production Crow applications should enforce the protocol by adding `#!cpp #define CROW_ENFORCE_WS_SPEC` to their source code.
|
||||
|
||||
These event methods and their handlers can be chained. The full route should look similar to this:
|
||||
```cpp
|
||||
CROW_WEBSOCKET_ROUTE(app, "/ws")
|
||||
.onopen([&](crow::websocket::connection& conn){
|
||||
do_something();
|
||||
})
|
||||
.onclose([&](crow::websocket::connection& conn, const std::string& reason){
|
||||
do_something();
|
||||
})
|
||||
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary){
|
||||
if (is_binary)
|
||||
do_something(data);
|
||||
else
|
||||
do_something_else(data);
|
||||
});
|
||||
```
|
||||
|
||||
## Maximum payload size
|
||||
<span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>
|
||||
|
||||
The maximum payload size that a connection accepts can be adjusted either globally by using `#!cpp app.websocket_max_payload(<value in bytes>)` or per route by using `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").max_payload(<value in bytes>)`. In case a message was sent that exceeded the limit. The connection would be shut down and `onerror` would be triggered.
|
||||
|
||||
!!! note
|
||||
|
||||
By default, this limit is disabled. To disable the global setting in specific routes, you only need to call `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").max_payload(UINT64_MAX)`.
|
||||
|
||||
|
||||
For more info about websocket routes go [here](../reference/classcrow_1_1_web_socket_rule.html).
|
||||
|
||||
For more info about websocket connections go [here](../reference/classcrow_1_1websocket_1_1_connection.html).
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
template: home.html
|
||||
---
|
|
@ -0,0 +1,536 @@
|
|||
{% extends "main.html" %}
|
||||
|
||||
<!-- Render hero under tabs -->
|
||||
|
||||
{% block extrahead %}
|
||||
<meta property="og:title" content="CrowCpp"/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:description" content="A Fast and Easy to use microframework for the web."/>
|
||||
<meta name="description" content="Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.">
|
||||
<meta property="og:image" content="assets/og_img.png" />
|
||||
<meta property="og:url" content="https://crowcpp.org">
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:image" content="assets/og_img.png">
|
||||
{% endblock %}
|
||||
|
||||
<!-- Content -->
|
||||
{% block content %}
|
||||
<style>
|
||||
.clogo{
|
||||
width: 50%;
|
||||
margin-inline: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ccard{
|
||||
border-style: solid;
|
||||
border-width: .1rem;
|
||||
border-color: var(--home-border-color);
|
||||
border-radius: 0.5rem;
|
||||
width: 10rem;
|
||||
height: 12rem;
|
||||
box-shadow: 2px 5px 5px var(--home-shadow-color);
|
||||
margin-inline: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: inline-block;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.ccard:hover{
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.ccard__image{
|
||||
max-width: 7.5rem;
|
||||
max-height: 7.5rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: solid;
|
||||
border-width: 0.1rem;
|
||||
border-image-slice: 1;
|
||||
border-image-source: var(--home-image-border);
|
||||
}
|
||||
|
||||
.ccard__text{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.csection{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ssection{
|
||||
display:flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sdescription{
|
||||
display:flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
height: 14rem;
|
||||
}
|
||||
|
||||
.scontent{
|
||||
display:flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
height: 14rem;
|
||||
}
|
||||
.highlight{
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.sbuttons{
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.crow-button{
|
||||
margin: 1rem 1rem;
|
||||
}
|
||||
|
||||
code{
|
||||
border-radius: 0.3rem !important;
|
||||
}
|
||||
|
||||
.dcard{
|
||||
width: 10rem;
|
||||
height: 14rem;
|
||||
margin-inline: 0.5rem;
|
||||
display: inline-block;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.dcard:hover{
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dcard__image{
|
||||
max-width: 7.5rem;
|
||||
max-height: 7.5rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.dcard__title{
|
||||
font-size: 1.25rem;
|
||||
margin: 0px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dcard__description{
|
||||
font-size: 0.75rem;
|
||||
margin: 0.1rem 0px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
min-height: 50px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md-footer-copyright {
|
||||
color: var(--md-footer-fg-color--light);
|
||||
font-size: .64rem;
|
||||
margin: auto .6rem;
|
||||
padding: .4rem 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
<img class="clogo" alt="logo" src="assets/crowlogo_main_color.svg#only-light">
|
||||
<img class="clogo" alt="logo" src="assets/crowlogo_main_light_color.svg#only-dark">
|
||||
|
||||
<h1 style="text-align:center;">A Fast and Easy to use microframework for the web.</h1>
|
||||
|
||||
<hr>
|
||||
<p style="text-align:center;">Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.</p>
|
||||
<hr>
|
||||
|
||||
<section class="csection">
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= assets/fast_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/fast_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Blazingly Fast</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= assets/header_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/header_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Header Only</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= assets/typesafe_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/typesafe_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Typesafe handlers</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= assets/websocket_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/websocket_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Websocket Support</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<section class="ssection">
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">Easy to get started</h2>
|
||||
</div>
|
||||
<div class="scontent">
|
||||
<div class="highlight"><pre id="__code_0"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_0 > code"></button><code><span class="cp">#include</span> <span class="cpf">"crow.h"</span><span class="cp"></span>
|
||||
|
||||
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
|
||||
<span class="p">{</span>
|
||||
<span class="n">crow</span><span class="o">::</span><span class="n">SimpleApp</span> <span class="n">app</span><span class="p">;</span>
|
||||
|
||||
<span class="cp">CROW_ROUTE</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="s">"/"</span><span class="p">)([](){</span>
|
||||
<span class="k">return</span> <span class="s">"Hello world"</span><span class="p">;</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="n">app</span><span class="p">.</span><span class="n">port</span><span class="p">(</span><span class="mi">18080</span><span class="p">).</span><span class="n">run</span><span class="p">();</span>
|
||||
<span class="p">}</span>
|
||||
</code></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="ssection">
|
||||
<div class="scontent">
|
||||
<div class="highlight"><pre id="__code_1"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_1 > code"></button><code><span class="cp">CROW_ROUTE</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="s">"/json"</span><span class="p">)</span>
|
||||
<span class="p">([]{</span>
|
||||
<span class="n">crow</span><span class="o">::</span><span class="n">json</span><span class="o">::</span><span class="n">wvalue</span> <span class="n">x</span><span class="p">{{"({{"}}</span><span class="s">"message"</span><span class="p">,</span> <span class="s">"Hello, World!"</span><span class="p">{{"}});"}}</span>
|
||||
<span class="n">x</span><span class="p">[</span><span class="s">"message2"</span><span class="p">]</span> <span class="o">=</span> <span class="s">"Hello, World.. Again!"</span><span class="p">;</span>
|
||||
<span class="k">return</span> <span class="n">x</span><span class="p">;</span>
|
||||
<span class="p">});</span>
|
||||
</code></pre></div>
|
||||
</div>
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">JSON Support Built-in</h2>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="ssection">
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">URL parameter support as well!</h2>
|
||||
</div>
|
||||
<div class="scontent">
|
||||
<div class="highlight"><pre id="__code_2"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_2 > code"></button><code><span class="cp">CROW_ROUTE</span><span class="p">(</span><span class="n">app</span><span class="p">,</span><span class="s">"/hello/<int>"</span><span class="p">)</span>
|
||||
<span class="p">([](</span><span class="kt">int</span> <span class="n">count</span><span class="p">){</span>
|
||||
<span class="k">return</span> <span class="n">crow</span><span class="o">::</span><span class="n">response</span><span class="p">(std::to_string(count));</span></span>
|
||||
<span class="p">});</span>
|
||||
</code></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1 style="text-align:center;">Support Crow</h1>
|
||||
<h3 style="text-align:center;">Crow is provided free of charge courtesy of everyone who is donating their money, time, and expertise to keep it going.<h3>
|
||||
<h3 style="text-align:center;">Help us make something great!</h3>
|
||||
|
||||
<dev class="sbuttons" id="contributors">
|
||||
</dev>
|
||||
<dev class="sbuttons">
|
||||
<a href="https://opencollective.com/crow" title="Crow - OpenCollective" class="md-button crow-button">Fund Crow</a>
|
||||
<a href="https://github.com/CrowCpp/Crow" title="Crow - OpenCollective" class="md-button crow-button">Develop Crow</a>
|
||||
<a href="https://gitter.im/crowfork/community" title="Crow - OpenCollective" class="md-button crow-button">Chat with us</a>
|
||||
</dev>
|
||||
|
||||
<hr>
|
||||
<h1 style="text-align:center;">Get Crow</h1>
|
||||
<h3 style="text-align:center;">Crow is everywhere, you just need to grab it!<h3>
|
||||
|
||||
<section class="csection">
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://github.com/CrowCpp/Crow/releases/latest">
|
||||
<img class="dcard__image" src="assets/pkg_logos/ubuntu.png">
|
||||
<p class="dcard__title">.deb file</p>
|
||||
<p class="dcard__description">for Ubuntu/Debian based systems</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://aur.archlinux.org/packages/crow">
|
||||
<img class="dcard__image" src="assets/pkg_logos/arch.png">
|
||||
<p class="dcard__title">AUR</p>
|
||||
<p class="dcard__description">for Arch Linux based systems</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://vcpkg.io">
|
||||
<img class="dcard__image" src="assets/pkg_logos/vcpkg.png">
|
||||
<p class="dcard__title">VCPKG</p>
|
||||
<p class="dcard__description">for Windows systems</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://conan.io/center/crowcpp-crow">
|
||||
<img class="dcard__image" src="assets/pkg_logos/conan.png">
|
||||
<p class="dcard__title">Conan Center</p>
|
||||
<p class="dcard__description">for developers using the conan package manager</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://github.com/CrowCpp/Crow/releases/latest">
|
||||
<img class="dcard__image" src="assets/pkg_logos/github.png">
|
||||
<p class="dcard__title">Header File</p>
|
||||
<p class="dcard__description">Download Crow directly from github</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
<h1 style="text-align:center;">Learn Crow</h1>
|
||||
<h3 style="text-align:center;">The 1000 mile journey begins with a single step. Get started by installing Crow and building you first application. Or go through the guides if you're stuck somewhere.<h3>
|
||||
|
||||
<dev class="sbuttons">
|
||||
<a href="getting_started/setup/" title="Get Started" class="md-button crow-button">Get Started</a>
|
||||
<a href="guides/app/" title="Guides" class="md-button crow-button">Guides</a>
|
||||
<a href="reference/index.html" title="API Reference" class="md-button crow-button">API Reference</a>
|
||||
</dev>
|
||||
|
||||
|
||||
<script>
|
||||
let text = "";
|
||||
|
||||
function convertRemToPixels(rem) {
|
||||
return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
}
|
||||
|
||||
function makeCard(name, img_url, ref, size = 6)
|
||||
{
|
||||
let finalName = fixLong(name);
|
||||
if (img_url == null)
|
||||
{
|
||||
img_url = AvatarImage(name);
|
||||
}
|
||||
return `<a title="${name}" href="${ref}" style=\"border-style: solid;border-width: .1rem;border-color: var(--home-border-color);border-radius: ${size/12}rem;width: ${size}rem;height: ${size}rem;box-shadow: 2px 5px 5px var(--home-shadow-color);margin-inline: ${size/12}rem;margin-bottom: ${size/12}rem;margin-top: ${size/12}rem;display: inline-block;\"><img style=\"width: ${size/2}rem;height: ${size/2}rem; border-radius: ${size/10}rem;margin-left: auto;margin-right: auto;display: block;margin-top: ${size/7.7}rem;\" src=\"${img_url}\"><p style=\"text-align: center;font-size: ${size/7.7}rem;\">${finalName}</p></a>`;
|
||||
}
|
||||
|
||||
function fixLong(name)
|
||||
{
|
||||
if (name.length > 12)
|
||||
{
|
||||
name = name.slice(0,9);
|
||||
name += "...";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function GetColor(name)
|
||||
{
|
||||
var r = 0;
|
||||
var g = 0;
|
||||
var b = 0;
|
||||
if (name.length >= 3)
|
||||
{
|
||||
var i = 0;
|
||||
for (i; i < name.length/3; i++)
|
||||
{
|
||||
r += name[i].charCodeAt();
|
||||
}
|
||||
r %= 256;
|
||||
for (i; i < name.length*2/3; i++)
|
||||
{
|
||||
g += name[i].charCodeAt();
|
||||
}
|
||||
g %= 256;
|
||||
for (i; i < name.length; i++)
|
||||
{
|
||||
b += name[i].charCodeAt();
|
||||
}
|
||||
b %= 256;
|
||||
console.log(`rgb(${r},${g},${b})`);
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
return "rgb(0,0,0)";
|
||||
}
|
||||
|
||||
function AvatarImage(name)
|
||||
{
|
||||
var letters = name.includes(' ') ? name[0] + name.split(' ').slice(-1)[0][0] : name.slice(0,2);
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext("2d");
|
||||
var size = convertRemToPixels(3);
|
||||
var color = GetColor(name);
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
context.font = Math.round(canvas.width / 2) + "px Arial";
|
||||
context.textAlign = "center";
|
||||
// Setup background and front color
|
||||
context.fillStyle = color;
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Check the color brightness (0-255)
|
||||
color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
|
||||
r = color[1];
|
||||
g = color[2];
|
||||
b = color[3];
|
||||
hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
|
||||
|
||||
context.fillStyle = hsp > 150 ? "#333" : "#EEE"; // 150 was reached by trial and error
|
||||
context.fillText(letters, size / 2, size / 1.5);
|
||||
// Set image representation in default format (png)
|
||||
dataURI = canvas.toDataURL();
|
||||
// Dispose canvas element
|
||||
canvas = null;
|
||||
return dataURI;
|
||||
}
|
||||
|
||||
async function getGHData()
|
||||
{
|
||||
let x = await fetch("https://api.github.com/repos/crowcpp/crow/contributors?per_page=100");
|
||||
let y = await x.json();
|
||||
text += "<a href=\"https://github.com/CrowCpp/Crow\"><h2>Code Contributors</h2></a><section> ";
|
||||
if (y.length != null)
|
||||
{
|
||||
for(var i = 0; i < y.length; i++){let item = y[i]; text += makeCard(item.login, item.avatar_url, item.html_url, 3.5);}
|
||||
}
|
||||
else
|
||||
{
|
||||
text += "<h3>GitHub won't let us show our awesome Code Contributors at the moment.</h3>"
|
||||
}
|
||||
text += "</section><br><br>";
|
||||
}
|
||||
|
||||
async function getData()
|
||||
{
|
||||
let backers = [];
|
||||
let boosters = [];
|
||||
let sponsors = [];
|
||||
let donations = [];
|
||||
|
||||
let x = await fetch("https://opencollective.com/crow/members/all.json");
|
||||
let y = await x.json();
|
||||
|
||||
for (var i = 0; i < y.length; i++)
|
||||
{
|
||||
let item = y[i];
|
||||
if (item.role === "BACKER") {
|
||||
if(item.tier === "Backer"){
|
||||
backers.push(item);
|
||||
}
|
||||
else if(item.tier === "Booster"){
|
||||
boosters.push(item);
|
||||
}
|
||||
else if(item.tier === "Sponsor"){
|
||||
sponsors.push(item);
|
||||
}
|
||||
else if(item.tier === "Donation"){
|
||||
donations.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ghx = await fetch("https://gh-sponsors-dsdpxxx9d-filiptronicek.vercel.app/sponsors/CrowCpp");
|
||||
let ghy = await fetch("https://gh-sponsors-dsdpxxx9d-filiptronicek.vercel.app/count/CrowCpp");
|
||||
let ghsponsors = await ghx.json();
|
||||
ghsponsors = ghsponsors.sponsors;
|
||||
let ghx_count = await ghy.json();
|
||||
ghx_count = ghx_count.sponsors.count;
|
||||
|
||||
if (ghx_count > ghsponsors.length)
|
||||
{
|
||||
let priv_count = ghx_count - ghsponsors.length;
|
||||
ghsponsors.push({handle: priv_count = 1 ? "Private sponsor" : `${priv_count} Private sponsors`, avatar:null, profile:"https://github.com/CrowCpp"});
|
||||
}
|
||||
|
||||
|
||||
text += "<section style=\"text-align: center;\"><a href=\"https://opencollective.com/crow/contribute/sponsor-30717/checkout\"><h2>Sponsors</h2></a>";
|
||||
for(var i = 0; i < sponsors.length; i++){let item = sponsors[i]; text += makeCard(item.name, item.image, item.profile, 10);}
|
||||
|
||||
text += "</section><br><section style=\"text-align: center;\"><a href=\"https://opencollective.com/crow/contribute/booster-30767/checkout\"><h2>Boosters</h2></a>";
|
||||
for(var i = 0; i < boosters.length; i++){let item = boosters[i]; text += makeCard(item.name, item.image, item.profile, 5);}
|
||||
|
||||
text += "</section><br><section style=\"text-align: center;\"><a href=\"https://opencollective.com/crow/contribute/backer-30716/checkout\"><h2>Backers</h2></a>";
|
||||
for(var i = 0; i < backers.length; i++){let item = backers[i]; text += makeCard(item.name, item.image, item.profile, 3.5);}
|
||||
|
||||
text += "</section><br><section style=\"text-align: center;\"><a href=\"https://opencollective.com/crow/contribute/donation-30769/checkout\"><h2>Donations</h2></a>";
|
||||
for(var i = 0; i < donations.length; i++){let item = donations[i]; text += makeCard(item.name, item.image, item.profile, 3.5);}
|
||||
|
||||
text += "</section><br><section style=\"text-align: center;\"><a href=\"https://github.com/sponsors/CrowCpp\"><h2>GitHub Sponsors</h2></a>";
|
||||
for(var i = 0; i < ghsponsors.length; i++){let item = ghsponsors[i]; text += makeCard(item.handle, item.avatar, item.profile, 5);}
|
||||
|
||||
text += "</section><section style=\"text-align: center;\">";
|
||||
await getGHData();
|
||||
text += "</section>";
|
||||
document.getElementById("contributors").innerHTML = text;
|
||||
}
|
||||
|
||||
getData();
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
<!-- Navigation -->
|
||||
{% block site_nav %}
|
||||
|
||||
<!-- Main navigation -->
|
||||
{% if nav %}
|
||||
<div
|
||||
class="md-sidebar md-sidebar--primary"
|
||||
data-md-component="sidebar"
|
||||
data-md-type="navigation"
|
||||
hidden
|
||||
>
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
{% include "partials/nav.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Table of contents -->
|
||||
{% if page.toc and not "toc.integrate" in features %}
|
||||
<div
|
||||
class="md-sidebar md-sidebar--secondary"
|
||||
data-md-component="sidebar"
|
||||
data-md-type="toc"
|
||||
hidden
|
||||
>
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
{% include "partials/toc.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
|
||||
<footer class="md-footer">
|
||||
<!-- Further information -->
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
|
||||
<!-- Copyright and theme information -->
|
||||
<div class="md-footer-copyright" style="flex: 1;display: flex;justify-content: left;">
|
||||
{% if config.copyright %}
|
||||
<div class="md-footer-copyright__highlight">
|
||||
{{ config.copyright }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ extracopyright }}
|
||||
</div>
|
||||
|
||||
<a style="margin: auto .6rem; font-size: .64rem;text-align: center;flex: 1;display: flex;justify-content: center;" href="privacy_policy.html">Privacy Policy</a>
|
||||
|
||||
<!-- Social links -->
|
||||
{% include "partials/social.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
<meta property="og:title" content="CrowCpp"/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:description" content="A Fast and Easy to use microframework for the web."/>
|
||||
<meta property="og:image" content="/assets/og_img.png" />
|
||||
<meta property="og:url" content="https://crowcpp.org">
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:image" content="/assets/og_img.png">
|
||||
{% endblock %}
|
|
@ -0,0 +1,112 @@
|
|||
<!--
|
||||
Copyright (c) 2016-2021 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
{% import "partials/language.html" as lang with context %}
|
||||
|
||||
<style>
|
||||
.md-footer-copyright {
|
||||
color: var(--md-footer-fg-color--light);
|
||||
font-size: .64rem;
|
||||
margin: auto .6rem;
|
||||
padding: .4rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="md-footer">
|
||||
|
||||
<!-- Link to previous and/or next page -->
|
||||
{% if page.previous_page or page.next_page %}
|
||||
<nav
|
||||
class="md-footer__inner md-grid"
|
||||
aria-label="{{ lang.t('footer.title') }}"
|
||||
>
|
||||
|
||||
<!-- Link to previous page -->
|
||||
{% if page.previous_page %}
|
||||
{% set direction = lang.t("footer.previous") %}
|
||||
<a
|
||||
href="{{ page.previous_page.url | url }}"
|
||||
class="md-footer__link md-footer__link--prev"
|
||||
aria-label="{{ direction }}: {{ page.previous_page.title | e }}"
|
||||
rel="prev"
|
||||
>
|
||||
<div class="md-footer__button md-icon">
|
||||
{% include ".icons/material/arrow-left.svg" %}
|
||||
</div>
|
||||
<div class="md-footer__title">
|
||||
<div class="md-ellipsis">
|
||||
<span class="md-footer__direction">
|
||||
{{ direction }}
|
||||
</span>
|
||||
{{ page.previous_page.title }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<!-- Link to next page -->
|
||||
{% if page.next_page %}
|
||||
{% set direction = lang.t("footer.next") %}
|
||||
<a
|
||||
href="{{ page.next_page.url | url }}"
|
||||
class="md-footer__link md-footer__link--next"
|
||||
aria-label="{{ direction }}: {{ page.next_page.title | e }}"
|
||||
rel="next"
|
||||
>
|
||||
<div class="md-footer__title">
|
||||
<div class="md-ellipsis">
|
||||
<span class="md-footer__direction">
|
||||
{{ direction }}
|
||||
</span>
|
||||
{{ page.next_page.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-footer__button md-icon">
|
||||
{% include ".icons/material/arrow-right.svg" %}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<!-- Further information -->
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
|
||||
<!-- Copyright and theme information -->
|
||||
<div class="md-footer-copyright" style="flex: 1;display: flex;justify-content: left;">
|
||||
{% if config.copyright %}
|
||||
<div class="md-footer-copyright__highlight">
|
||||
{{ config.copyright }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ extracopyright }}
|
||||
</div>
|
||||
|
||||
<a style="margin: auto .6rem; font-size: .64rem;text-align: center;flex: 1;display: flex;justify-content: center;" href="/privacy_policy.html">Privacy Policy</a>
|
||||
|
||||
<!-- Social links -->
|
||||
{% include "partials/social.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,150 @@
|
|||
<!--
|
||||
Copyright (c) 2016-2021 Martin Donath <martin.donath@squidfunk.com>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!-- Header -->
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav
|
||||
class="md-header__inner md-grid"
|
||||
aria-label="{{ lang.t('header.title') }}"
|
||||
>
|
||||
|
||||
<!-- Link to home -->
|
||||
<a
|
||||
href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
|
||||
title="{{ config.site_name | e }}"
|
||||
class="md-header__button md-logo"
|
||||
aria-label="{{ config.site_name }}"
|
||||
data-md-component="logo"
|
||||
>
|
||||
{% include "partials/logo.html" %}
|
||||
</a>
|
||||
|
||||
<!-- Button to open drawer -->
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
{% include ".icons/material/menu" ~ ".svg" %}
|
||||
</label>
|
||||
|
||||
<!-- Header title -->
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
{{ config.site_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
{% if page and page.meta and page.meta.title %}
|
||||
{{ page.meta.title }}
|
||||
{% else %}
|
||||
{{ page.title }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color palette -->
|
||||
{% if not config.theme.palette is mapping %}
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
{% for option in config.theme.palette %}
|
||||
{% set primary = option.primary | replace(" ", "-") | lower %}
|
||||
{% set accent = option.accent | replace(" ", "-") | lower %}
|
||||
<input
|
||||
class="md-option"
|
||||
data-md-color-media="{{ option.media }}"
|
||||
data-md-color-scheme="{{ option.scheme }}"
|
||||
data-md-color-primary="{{ primary }}"
|
||||
data-md-color-accent="{{ accent }}"
|
||||
{% if option.toggle %}
|
||||
aria-label="{{ option.toggle.name }}"
|
||||
{% else %}
|
||||
aria-hidden="true"
|
||||
{% endif %}
|
||||
type="radio"
|
||||
name="__palette"
|
||||
id="__palette_{{ loop.index }}"
|
||||
/>
|
||||
{% if option.toggle %}
|
||||
<label
|
||||
class="md-header__button md-icon"
|
||||
title="{{ option.toggle.name }}"
|
||||
for="__palette_{{ loop.index0 or loop.length }}"
|
||||
hidden
|
||||
>
|
||||
{% include ".icons/" ~ option.toggle.icon ~ ".svg" %}
|
||||
</label>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<!-- Site language selector -->
|
||||
{% if config.extra.alternate %}
|
||||
<div class="md-header__option"></form>
|
||||
<div class="md-select">
|
||||
{% set icon = config.theme.icon.alternate or "material/translate" %}
|
||||
<button
|
||||
class="md-header__button md-icon"
|
||||
aria-label="{{ lang.t('select.language.title') }}"
|
||||
>
|
||||
{% include ".icons/" ~ icon ~ ".svg" %}
|
||||
</button>
|
||||
<div class="md-select__inner">
|
||||
<ul class="md-select__list">
|
||||
{% for alt in config.extra.alternate %}
|
||||
<li class="md-select__item">
|
||||
<a
|
||||
href="{{ alt.link | url }}"
|
||||
hreflang="{{ alt.lang }}"
|
||||
class="md-select__link"
|
||||
>
|
||||
{{ alt.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Button to open search modal -->
|
||||
{% if "search" in config["plugins"] %}
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
{% include ".icons/material/magnify.svg" %}
|
||||
</label>
|
||||
|
||||
<!-- Search interface -->
|
||||
{% include "partials/search.html" %}
|
||||
{% endif %}
|
||||
|
||||
<div class="md-header__source">
|
||||
<a class="md-source" href="https://opencollective.com/crow" style="padding-left:2.4rem;"><img style="border-radius: 5px" alt="Open Collective" src="https://img.shields.io/opencollective/all/crow?label=Support%20Crow&color=important&labelColor=122027&logo=opencollective&style=for-the-badge"></a>
|
||||
</div>
|
||||
|
||||
<!-- Repository information -->
|
||||
{% if config.repo_url %}
|
||||
<div class="md-header__source">
|
||||
{% include "partials/source.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</nav>
|
||||
</header>
|
|
@ -0,0 +1,19 @@
|
|||
<!-- Matomo -->
|
||||
<script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="{{config.extra.analytics.link}}";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '{{config.extra.analytics.id}}']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<img referrerpolicy="no-referrer-when-downgrade" src="https://thee.dev/matomo/matomo.php?idsite=1&rec=1&action_name={{ page.title }}" style="border:0" alt="" />
|
||||
</noscript>
|
||||
<!-- End Matomo Code -->
|
|
@ -0,0 +1,37 @@
|
|||
<!--
|
||||
Copyright (c) 2016-2022 Martin Donath <martin.donath@squidfunk.com>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!-- Social links -->
|
||||
<div class="md-social" style="flex: 1;display: flex;justify-content: right;">
|
||||
{% for social in config.extra.social %}
|
||||
{% set title = social.name %}
|
||||
{% if not title and "//" in social.link %}
|
||||
{% set _, url = social.link.split("//") %}
|
||||
{% set title = url.split("/")[0] %}
|
||||
{% endif %}
|
||||
<a
|
||||
href="{{ social.link }}"
|
||||
target="_blank" rel="noopener"
|
||||
title="{{ title | e }}"
|
||||
class="md-social__link"
|
||||
>
|
||||
{% include ".icons/" ~ social.icon ~ ".svg" %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
{% extends "main.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Privacy Policy for CrowCpp</h1>
|
||||
|
||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.</p>
|
||||
|
||||
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards to the information that they shared and/or collect in CrowCpp. This policy is not applicable to any information collected offline or via channels other than this website.</p>
|
||||
|
||||
<h2>Consent</h2>
|
||||
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms.</p>
|
||||
|
||||
<h2>Log Files</h2>
|
||||
|
||||
<p>CrowCpp follows a standard procedure of using log files. These files log visitors when they visit websites. All hosting companies do this and a part of hosting services' analytics. The information collected by log files include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp, referring/exit pages, and possibly the number of clicks. These are not linked to any information that is personally identifiable. The purpose of the information is for analyzing trends, administering the site, tracking users' movement on the website.</p>
|
||||
|
||||
<h2>Other information we collect</h2>
|
||||
|
||||
<p>If you contact us directly, we may receive information about you such as your name, email address, phone number, the contents of the message and/or attachments you may send us, and any other information you may choose to provide.</p>
|
||||
|
||||
<h2>How we use your information</h2>
|
||||
|
||||
<p>We use the information we collect in various ways, including to:</p>
|
||||
|
||||
<ul>
|
||||
<li>Provide, operate, and maintain our website</li>
|
||||
<li>Improve or expand our website</li>
|
||||
<li>Understand and analyze how you use our website</li>
|
||||
<li>Find and prevent fraud</li>
|
||||
</ul>
|
||||
|
||||
<h2>Children's Information</h2>
|
||||
|
||||
<p>CrowCpp does not knowingly collect any Personal Identifiable Information from children under the age of 13. If you think that your child provided this kind of information on our website, we strongly encourage you to contact us immediately and we will do our best efforts to promptly remove such information from our records.</p>
|
||||
{% endblock %}
|
||||
{% block site_nav %}{% endblock %}
|
|
@ -0,0 +1,92 @@
|
|||
[data-md-color-scheme="crow-light"]{
|
||||
|
||||
--md-primary-fg-color: #24404f;
|
||||
--md-accent-fg-color: #122027;
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
--md-default-bg-color: #e5f2f8;
|
||||
--md-code-bg-color: #cfcfcf !important;
|
||||
--md-code-hl-comment-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-generic-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-variable-color: var(--md-code-fg-color) !important;
|
||||
--md-code-fg-color: #1d1d1d !important;
|
||||
--md-code-hl-punctuation-color: #1d1d1d !important;
|
||||
--home-border-color: #00000080;
|
||||
--home-shadow-color: #00000040;
|
||||
--home-image-border: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0, 0, 0) 50%, rgba(0,0,0,0) 100%);
|
||||
--md-default-tag-bg-color: var(--md-default-bg-color);
|
||||
|
||||
}
|
||||
|
||||
[data-md-color-scheme="crow-dark"]{
|
||||
|
||||
--md-primary-fg-color: #24404f;
|
||||
--md-accent-fg-color: #445e6d;
|
||||
--md-default-fg-color: rgba(255, 255, 255, 0.87);
|
||||
--md-typeset-color: rgba(255, 255, 255, 0.80);
|
||||
--md-admonition-fg-color: rgba(255, 255, 255, 0.80);
|
||||
--md-default-fg-color--light: rgba(255, 255, 255, 0.54);
|
||||
--md-default-fg-color--lighter: rgba(255, 255, 255, 0.32);
|
||||
--md-default-fg-color--lightest: rgba(255, 255, 255, 0.07);
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
--md-default-bg-color: #1a2124;
|
||||
--md-code-bg-color: #101010 !important;
|
||||
--md-code-hl-comment-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-generic-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-variable-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-operator-color: var(--md-code-fg-color) !important;
|
||||
--md-code-fg-color: #cfcfcf !important;
|
||||
--md-code-hl-punctuation-color: #adadad !important;
|
||||
--home-border-color: #ffffff20;
|
||||
--home-shadow-color: #00000040;
|
||||
--home-image-border: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%);
|
||||
--md-admonition-bg-color: #272a2b;
|
||||
--md-code-hl-color: rgba(255, 255, 0, 0.18);
|
||||
--md-default-tag-bg-color: var(--md-default-fg-color);
|
||||
|
||||
}
|
||||
|
||||
.md-typeset .md-button {
|
||||
color: var(--md-default-fg-color--light);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
:root{
|
||||
--md-default-fg-color: rgba(255, 255, 255, 0.87);
|
||||
--md-default-fg-color--light: rgba(255, 255, 255, 0.54);
|
||||
--md-default-fg-color--lighter: rgba(255, 255, 255, 0.32);
|
||||
--md-default-fg-color--lightest: rgba(255, 255, 255, 0.07);
|
||||
--md-default-bg-color: #1a2124;
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
}
|
||||
|
||||
.md-typeset code {
|
||||
background-color: #2f2f2f;
|
||||
}
|
||||
|
||||
|
||||
.highlight .c, .highlight .c1, .highlight .ch, .highlight .cm, .highlight .cs, .highlight .sd {
|
||||
color: #606060 !important;
|
||||
}
|
||||
|
||||
.linenos {
|
||||
background-color: #cfcfcf !important;
|
||||
color: #1d1d1d !important;
|
||||
}
|
||||
|
||||
.highlighttable .linenodiv pre {
|
||||
background-color: #cfcfcf !important;
|
||||
color: #1d1d1d !important;
|
||||
}
|
||||
|
||||
.highlight .p {
|
||||
color: #1d1d1d;
|
||||
}
|
||||
|
||||
.highlight .o {
|
||||
color: #1d1d1d;
|
||||
}
|
||||
|
||||
.highlight .n {
|
||||
color: #1d1d1d;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
.md-header__source {
|
||||
margin-left: 0px;
|
||||
width: autho;
|
||||
min-width: 9.6rem;
|
||||
}
|
||||
|
||||
.md-typeset code {
|
||||
border-radius: .25rem !important;
|
||||
}
|
||||
|
||||
.code {
|
||||
border-radius: .25rem !important;
|
||||
}
|
||||
|
||||
.tag
|
||||
{
|
||||
background-color: var(--md-primary-fg-color);
|
||||
color: var(--md-default-tag-bg-color);
|
||||
border-radius: 50px;
|
||||
padding-left: 0.15em;
|
||||
padding-right: 0.35em;
|
||||
padding-top: 0.45em;
|
||||
padding-bottom: 0.35em;
|
||||
}
|
||||
|
||||
.tag a
|
||||
{
|
||||
color: var(--md-default-tag-bg-color);
|
||||
}
|
||||
|
||||
.tag a:hover
|
||||
{
|
||||
color: var(--md-default-tag-bg-color);
|
||||
}
|
||||
|
||||
.md-typeset :is(.emojione, .twemoji, .gemoji)
|
||||
{
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.md-typeset :is(.emojione, .twemoji, .gemoji) svg
|
||||
{
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
[data-md-color-scheme="crow-dark"] img[src$="#only-dark"]
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
[data-md-color-scheme="crow-light"] img[src$="#only-light"]
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
[data-md-color-scheme="crow-light"] img[src$="#only-dark"]
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
[data-md-color-scheme="crow-dark"] img[src$="#only-light"]
|
||||
{
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/* Webfont: Lato-Medium */@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('Lato-Medium.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
body, input {
|
||||
font-family: "Lato", -apple-system, Helvetica, Arial, sans-serif;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[
|
||||
{"version": "master", "title": "master", "aliases": []},
|
||||
{"version": "1.0", "title": "1.0+3", "aliases": []},
|
||||
{"version": "0.3", "title": "0.3+4", "aliases": []}
|
||||
]
|
|
@ -0,0 +1,100 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(crow_examples)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler_options.cmake)
|
||||
|
||||
add_executable(helloworld helloworld.cpp)
|
||||
add_warnings_optimizations(helloworld)
|
||||
target_link_libraries(helloworld PUBLIC Crow::Crow)
|
||||
|
||||
# If compression is enabled, the example will be built
|
||||
if("compression" IN_LIST CROW_FEATURES)
|
||||
add_executable(example_compression example_compression.cpp)
|
||||
add_warnings_optimizations(example_compression)
|
||||
target_link_libraries(example_compression Crow::Crow)
|
||||
else()
|
||||
message(STATUS "example_compression example deactivated")
|
||||
endif()
|
||||
|
||||
# If SSL is enabled, the example will be built
|
||||
if("ssl" IN_LIST CROW_FEATURES)
|
||||
add_executable(example_ssl ssl/example_ssl.cpp)
|
||||
add_warnings_optimizations(example_ssl)
|
||||
target_link_libraries(example_ssl PUBLIC Crow::Crow)
|
||||
else()
|
||||
message(STATUS "example_ssl example deactivated")
|
||||
endif()
|
||||
|
||||
add_executable(example_websocket websocket/example_ws.cpp)
|
||||
add_warnings_optimizations(example_websocket)
|
||||
target_link_libraries(example_websocket PUBLIC Crow::Crow)
|
||||
add_custom_command(OUTPUT ws.html
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
copy ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html ${CMAKE_CURRENT_BINARY_DIR}/templates/ws.html
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html
|
||||
)
|
||||
add_custom_target(example_ws_copy ALL DEPENDS ws.html)
|
||||
|
||||
add_executable(basic_example example.cpp)
|
||||
add_warnings_optimizations(basic_example)
|
||||
target_link_libraries(basic_example PUBLIC Crow::Crow)
|
||||
|
||||
if(CROW_AMALGAMATE)
|
||||
add_executable(example_with_all example_with_all.cpp)
|
||||
add_dependencies(example_with_all crow_amalgamated)
|
||||
add_warnings_optimizations(example_with_all)
|
||||
target_link_libraries(example_with_all PUBLIC Crow::Crow)
|
||||
target_include_directories(example_with_all PUBLIC ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
add_executable(example_chat example_chat.cpp)
|
||||
add_warnings_optimizations(example_chat)
|
||||
target_link_libraries(example_chat PUBLIC Crow::Crow)
|
||||
add_custom_command(OUTPUT example_chat.html
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
copy ${PROJECT_SOURCE_DIR}/example_chat.html ${CMAKE_CURRENT_BINARY_DIR}/templates/example_chat.html
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/example_chat.html
|
||||
)
|
||||
add_custom_target(example_chat_copy ALL DEPENDS example_chat.html)
|
||||
|
||||
add_executable(example_static_file example_static_file.cpp)
|
||||
add_warnings_optimizations(example_static_file)
|
||||
target_link_libraries(example_static_file PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_catchall example_catchall.cpp)
|
||||
add_warnings_optimizations(example_catchall)
|
||||
target_link_libraries(example_catchall PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_json_map example_json_map.cpp)
|
||||
add_warnings_optimizations(example_json_map)
|
||||
target_link_libraries(example_json_map PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_blueprint example_blueprint.cpp)
|
||||
add_warnings_optimizations(example_blueprint)
|
||||
target_link_libraries(example_blueprint PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_middleware example_middleware.cpp)
|
||||
add_warnings_optimizations(example_middleware)
|
||||
target_link_libraries(example_middleware PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_cors middlewares/example_cors.cpp)
|
||||
add_warnings_optimizations(example_cors)
|
||||
target_link_libraries(example_cors PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_cookies middlewares/example_cookies.cpp)
|
||||
add_warnings_optimizations(example_cookies)
|
||||
target_link_libraries(example_cookies PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_session middlewares/example_session.cpp)
|
||||
add_warnings_optimizations(example_session)
|
||||
target_link_libraries(example_session PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_file_upload example_file_upload.cpp)
|
||||
add_warnings_optimizations(example_file_upload)
|
||||
target_link_libraries(example_file_upload PUBLIC Crow::Crow)
|
||||
|
||||
if(MSVC)
|
||||
add_executable(example_vs example_vs.cpp)
|
||||
add_warnings_optimizations(example_vs)
|
||||
target_link_libraries(example_vs Crow::Crow)
|
||||
endif()
|
|
@ -0,0 +1,225 @@
|
|||
#include "crow.h"
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExampleMiddleware
|
||||
{
|
||||
std::string message;
|
||||
|
||||
ExampleMiddleware():
|
||||
message("foo")
|
||||
{
|
||||
}
|
||||
|
||||
void setMessage(const std::string& newMsg)
|
||||
{
|
||||
message = newMsg;
|
||||
}
|
||||
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{
|
||||
CROW_LOG_DEBUG << " - MESSAGE: " << message;
|
||||
}
|
||||
|
||||
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::App<ExampleMiddleware> app;
|
||||
|
||||
app.get_middleware<ExampleMiddleware>().setMessage("hello");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")([] {
|
||||
return "Hello World!";
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([]() {
|
||||
return "About Crow example.";
|
||||
});
|
||||
|
||||
// a request to /path should be forwarded to /path/
|
||||
CROW_ROUTE(app, "/path/")
|
||||
([]() {
|
||||
return "Trailing slash test case..";
|
||||
});
|
||||
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([] {
|
||||
crow::json::wvalue x({{"message", "Hello, World!"}});
|
||||
x["message2"] = "Hello, World.. Again!";
|
||||
return x;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/json-initializer-list-constructor")
|
||||
([] {
|
||||
return crow::json::wvalue({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", std::int64_t(54)}, /* stores a 64-bit int hence a std::int64_t. */
|
||||
{"fourth", std::uint64_t(54)}, /* stores a 64-bit unsigned int hence a std::uint64_t. */
|
||||
{"fifth", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"sixth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
});
|
||||
});
|
||||
|
||||
// json list response
|
||||
CROW_ROUTE(app, "/json_list")
|
||||
([] {
|
||||
crow::json::wvalue x(crow::json::wvalue::list({1, 2, 3}));
|
||||
return x;
|
||||
});
|
||||
|
||||
// To see it in action enter {ip}:18080/hello/{integer_between -2^32 and 100} and you should receive
|
||||
// {integer_between -2^31 and 100} bottles of beer!
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count) {
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
|
||||
// Same as above, but using crow::status
|
||||
CROW_ROUTE(app, "/hello_status/<int>")
|
||||
([](int count) {
|
||||
if (count > 100)
|
||||
return crow::response(crow::status::BAD_REQUEST);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
|
||||
// To see it in action submit {ip}:18080/add/1/2 and you should receive 3 (exciting, isn't it)
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b) {
|
||||
std::ostringstream os;
|
||||
os << a + b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
|
||||
//same as the example above but uses mustache instead of stringstream
|
||||
CROW_ROUTE(app, "/add_mustache/<int>/<int>")
|
||||
([](int a, int b) {
|
||||
auto t = crow::mustache::compile("Value is {{func}}");
|
||||
crow::mustache::context ctx;
|
||||
ctx["func"] = [&](std::string) {
|
||||
return std::to_string(a + b);
|
||||
};
|
||||
auto result = t.render(ctx);
|
||||
return result;
|
||||
});
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
|
||||
// To see it in action, I recommend to use the Postman Chrome extension:
|
||||
// * Set the address to {ip}:18080/add_json
|
||||
// * Set the method to post
|
||||
// * Select 'raw' and then JSON
|
||||
// * Add {"a": 1, "b": 1}
|
||||
// * Send and you should receive 2
|
||||
|
||||
// A simpler way for json example:
|
||||
// * curl -d '{"a":1,"b":2}' {ip}:18080/add_json
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods("POST"_method)([](const crow::request& req) {
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
int sum = x["a"].i() + x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
// Example of a request taking URL parameters
|
||||
// If you want to activate all the functions just query
|
||||
// {ip}:18080/params?foo='blabla'&pew=32&count[]=a&count[]=b
|
||||
CROW_ROUTE(app, "/params")
|
||||
([](const crow::request& req) {
|
||||
std::ostringstream os;
|
||||
|
||||
// To get a simple string from the url params
|
||||
// To see it in action /params?foo='blabla'
|
||||
os << "Params: " << req.url_params << "\n\n";
|
||||
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
|
||||
|
||||
// To get a double from the request
|
||||
// To see in action submit something like '/params?pew=42'
|
||||
if (req.url_params.get("pew") != nullptr)
|
||||
{
|
||||
double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
|
||||
os << "The value of 'pew' is " << countD << '\n';
|
||||
}
|
||||
|
||||
// To get a list from the request
|
||||
// You have to submit something like '/params?count[]=a&count[]=b' to have a list with two values (a and b)
|
||||
auto count = req.url_params.get_list("count");
|
||||
os << "The key 'count' contains " << count.size() << " value(s).\n";
|
||||
for (const auto& countVal : count)
|
||||
{
|
||||
os << " - " << countVal << '\n';
|
||||
}
|
||||
|
||||
// To get a dictionary from the request
|
||||
// You have to submit something like '/params?mydict[a]=b&mydict[abcd]=42' to have a list of pairs ((a, b) and (abcd, 42))
|
||||
auto mydict = req.url_params.get_dict("mydict");
|
||||
os << "The key 'dict' contains " << mydict.size() << " value(s).\n";
|
||||
for (const auto& mydictVal : mydict)
|
||||
{
|
||||
os << " - " << mydictVal.first << " -> " << mydictVal.second << '\n';
|
||||
}
|
||||
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/large")
|
||||
([] {
|
||||
return std::string(512 * 1024, ' ');
|
||||
});
|
||||
|
||||
// Take a multipart/form-data request and print out its body
|
||||
CROW_ROUTE(app, "/multipart")
|
||||
([](const crow::request& req) {
|
||||
crow::multipart::message msg(req);
|
||||
CROW_LOG_INFO << "body of the first part " << msg.parts[0].body;
|
||||
return "it works!";
|
||||
});
|
||||
|
||||
// enables all log
|
||||
app.loglevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
crow::Blueprint bp("bp_prefix", "cstat", "ctemplate");
|
||||
|
||||
|
||||
crow::Blueprint sub_bp("bp2", "csstat", "cstemplate");
|
||||
|
||||
CROW_BP_ROUTE(sub_bp, "/")
|
||||
([]() {
|
||||
return "Hello world!";
|
||||
});
|
||||
|
||||
/* CROW_BP_ROUTE(bp, "/templatt")
|
||||
([]() {
|
||||
crow::mustache::context ctxdat;
|
||||
ctxdat["messg"] = "fifty five!!";
|
||||
|
||||
auto page = crow::mustache::load("indks.html");
|
||||
|
||||
return page.render(ctxdat);
|
||||
});
|
||||
*/
|
||||
CROW_BP_CATCHALL_ROUTE(sub_bp)
|
||||
([]() {
|
||||
return "WRONG!!";
|
||||
});
|
||||
|
||||
|
||||
bp.register_blueprint(sub_bp);
|
||||
app.register_blueprint(bp);
|
||||
|
||||
app.loglevel(crow::LogLevel::Debug).port(18080).run();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include <crow.h>
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello";
|
||||
});
|
||||
|
||||
//Setting a custom route for any URL that isn't defined, instead of a simple 404.
|
||||
CROW_CATCHALL_ROUTE(app)
|
||||
([](crow::response& res) {
|
||||
if (res.code == 404)
|
||||
{
|
||||
res.body = "The URL does not seem to be correct.";
|
||||
}
|
||||
else if (res.code == 405)
|
||||
{
|
||||
res.body = "The HTTP method does not seem to be correct.";
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#include "crow.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std;
|
||||
|
||||
vector<string> msgs;
|
||||
vector<pair<crow::response*, decltype(chrono::steady_clock::now())>> ress;
|
||||
|
||||
void broadcast(const string& msg)
|
||||
{
|
||||
msgs.push_back(msg);
|
||||
crow::json::wvalue x;
|
||||
x["msgs"][0] = msgs.back();
|
||||
x["last"] = msgs.size();
|
||||
string body = x.dump();
|
||||
for (auto p : ress)
|
||||
{
|
||||
auto* res = p.first;
|
||||
CROW_LOG_DEBUG << res << " replied: " << body;
|
||||
res->end(body);
|
||||
}
|
||||
ress.clear();
|
||||
}
|
||||
// To see how it works go on {ip}:40080 but I just got it working with external build (not directly in IDE, I guess a problem with dependency)
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
crow::mustache::set_base(".");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([] {
|
||||
crow::mustache::context ctx;
|
||||
return crow::mustache::load("example_chat.html").render();
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/logs")
|
||||
([] {
|
||||
CROW_LOG_INFO << "logs requested";
|
||||
crow::json::wvalue x;
|
||||
int start = max(0, (int)msgs.size() - 100);
|
||||
for (int i = start; i < (int)msgs.size(); i++)
|
||||
x["msgs"][i - start] = msgs[i];
|
||||
x["last"] = msgs.size();
|
||||
CROW_LOG_INFO << "logs completed";
|
||||
return x;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/logs/<int>")
|
||||
([](const crow::request& /*req*/, crow::response& res, int after) {
|
||||
CROW_LOG_INFO << "logs with last " << after;
|
||||
if (after < (int)msgs.size())
|
||||
{
|
||||
crow::json::wvalue x;
|
||||
for (int i = after; i < (int)msgs.size(); i++)
|
||||
x["msgs"][i - after] = msgs[i];
|
||||
x["last"] = msgs.size();
|
||||
|
||||
res.write(x.dump());
|
||||
res.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<pair<crow::response*, decltype(chrono::steady_clock::now())>> filtered;
|
||||
for (auto p : ress)
|
||||
{
|
||||
if (p.first->is_alive() && chrono::steady_clock::now() - p.second < chrono::seconds(30))
|
||||
filtered.push_back(p);
|
||||
else
|
||||
p.first->end();
|
||||
}
|
||||
ress.swap(filtered);
|
||||
ress.push_back({&res, chrono::steady_clock::now()});
|
||||
CROW_LOG_DEBUG << &res << " stored " << ress.size();
|
||||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/send")
|
||||
.methods("GET"_method, "POST"_method)([](const crow::request& req) {
|
||||
CROW_LOG_INFO << "msg from client: " << req.body;
|
||||
broadcast(req.body);
|
||||
return "";
|
||||
});
|
||||
|
||||
app.port(40080)
|
||||
//.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="msg" type="text">
|
||||
<button id="send">Send</button>
|
||||
<div id="logs">
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$("#send").click(function(){
|
||||
var msg = $("#msg").val();
|
||||
console.log(msg);
|
||||
if (msg.length > 0)
|
||||
$.post("/send", msg);
|
||||
$("#msg").val("");
|
||||
});
|
||||
$("#msg").keyup(function(event){
|
||||
if(event.keyCode == 13){
|
||||
$("#send").click();
|
||||
}
|
||||
});
|
||||
var lastLog = 0;
|
||||
var updateLog;
|
||||
updateLog = function(data)
|
||||
{
|
||||
console.log("recv ");
|
||||
console.log(data);
|
||||
var lastLog = data.last*1;
|
||||
console.log("lastLog: " + lastLog);
|
||||
var s = "";
|
||||
function htmlEncode(s)
|
||||
{
|
||||
return s.replace(/&(?!\w+([;\s]|$))/g, "&")
|
||||
.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
for(var x in data.msgs)
|
||||
{
|
||||
|
||||
s = htmlEncode(data.msgs[x]) + "<BR>" + s;
|
||||
}
|
||||
$("#logs").html(s+$("#logs").html());
|
||||
var failFunction;
|
||||
failFunction = function(){
|
||||
$.getJSON("/logs/"+lastLog, updateLog).fail(failFunction);
|
||||
};
|
||||
$.getJSON("/logs/"+lastLog, updateLog).fail(failFunction);
|
||||
}
|
||||
$.getJSON("/logs", updateLog);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
#include "crow.h"
|
||||
#include "crow/compression.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
//crow::App<crow::CompressionGzip> app;
|
||||
|
||||
CROW_ROUTE(app, "/hello")
|
||||
([&](const crow::request&, crow::response& res) {
|
||||
res.compressed = false;
|
||||
|
||||
res.body = "Hello World! This is uncompressed!";
|
||||
res.end();
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/hello_compressed")
|
||||
([]() {
|
||||
return "Hello World! This is compressed by default!";
|
||||
});
|
||||
|
||||
|
||||
app.port(18080)
|
||||
.use_compression(crow::compression::algorithm::DEFLATE)
|
||||
//.use_compression(crow::compression::algorithm::GZIP)
|
||||
.loglevel(crow::LogLevel::Debug)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/uploadfile")
|
||||
.methods(crow::HTTPMethod::Post)([](const crow::request& req) {
|
||||
crow::multipart::message file_message(req);
|
||||
for (const auto& part : file_message.part_map)
|
||||
{
|
||||
const auto& part_name = part.first;
|
||||
const auto& part_value = part.second;
|
||||
CROW_LOG_DEBUG << "Part: " << part_name;
|
||||
if ("InputFile" == part_name)
|
||||
{
|
||||
// Extract the file name
|
||||
auto headers_it = part_value.headers.find("Content-Disposition");
|
||||
if (headers_it == part_value.headers.end())
|
||||
{
|
||||
CROW_LOG_ERROR << "No Content-Disposition found";
|
||||
return crow::response(400);
|
||||
}
|
||||
auto params_it = headers_it->second.params.find("filename");
|
||||
if (params_it == headers_it->second.params.end())
|
||||
{
|
||||
CROW_LOG_ERROR << "Part with name \"InputFile\" should have a file";
|
||||
return crow::response(400);
|
||||
}
|
||||
const std::string outfile_name = params_it->second;
|
||||
|
||||
for (const auto& part_header : part_value.headers)
|
||||
{
|
||||
const auto& part_header_name = part_header.first;
|
||||
const auto& part_header_val = part_header.second;
|
||||
CROW_LOG_DEBUG << "Header: " << part_header_name << '=' << part_header_val.value;
|
||||
for (const auto& param : part_header_val.params)
|
||||
{
|
||||
const auto& param_key = param.first;
|
||||
const auto& param_val = param.second;
|
||||
CROW_LOG_DEBUG << " Param: " << param_key << ',' << param_val;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new file with the extracted file name and write file contents to it
|
||||
std::ofstream out_file(outfile_name);
|
||||
if (!out_file)
|
||||
{
|
||||
CROW_LOG_ERROR << " Write to file failed\n";
|
||||
continue;
|
||||
}
|
||||
out_file << part_value.body;
|
||||
out_file.close();
|
||||
CROW_LOG_INFO << " Contents written to " << outfile_name << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_DEBUG << " Value: " << part_value.body << '\n';
|
||||
}
|
||||
}
|
||||
return crow::response(200);
|
||||
});
|
||||
|
||||
// enables all log
|
||||
app.loglevel(crow::LogLevel::Debug);
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#define CROW_JSON_USE_MAP
|
||||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
// simple json response using a map
|
||||
// To see it in action enter {ip}:18080/json
|
||||
// it shoud show amessage before zmessage despite adding zmessage first.
|
||||
CROW_ROUTE(app, "/json")
|
||||
([] {
|
||||
crow::json::wvalue x({{"zmessage", "Hello, World!"},
|
||||
{"amessage", "Hello, World2!"}});
|
||||
return x;
|
||||
});
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#include "crow.h"
|
||||
|
||||
struct RequestLogger
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
|
||||
// This method is run before handling the request
|
||||
void before_handle(crow::request& req, crow::response& /*res*/, context& /*ctx*/)
|
||||
{
|
||||
CROW_LOG_INFO << "Request to:" + req.url;
|
||||
}
|
||||
|
||||
// This method is run after handling the request
|
||||
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{}
|
||||
};
|
||||
|
||||
// Per handler middleware has to extend ILocalMiddleware
|
||||
// It is called only if enabled
|
||||
struct SecretContentGuard : crow::ILocalMiddleware
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& /*req*/, crow::response& res, context& /*ctx*/)
|
||||
{
|
||||
// A request can be aborted prematurely
|
||||
res.write("SECRET!");
|
||||
res.code = 403;
|
||||
res.end();
|
||||
}
|
||||
|
||||
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{}
|
||||
};
|
||||
|
||||
struct RequestAppend : crow::ILocalMiddleware
|
||||
{
|
||||
// Values from this context can be accessed from handlers
|
||||
struct context
|
||||
{
|
||||
std::string message;
|
||||
};
|
||||
|
||||
void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{}
|
||||
|
||||
void after_handle(crow::request& /*req*/, crow::response& res, context& ctx)
|
||||
{
|
||||
// The response can be modified
|
||||
res.write(" + (" + ctx.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// ALL middleware (including per handler) is listed
|
||||
crow::App<RequestLogger, SecretContentGuard, RequestAppend> app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello, world!";
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/secret")
|
||||
// Enable SecretContentGuard for this handler
|
||||
.CROW_MIDDLEWARES(app, SecretContentGuard)([]() {
|
||||
return "";
|
||||
});
|
||||
|
||||
crow::Blueprint bp("bp", "c", "c");
|
||||
// Register middleware on all routes on a specific blueprint
|
||||
// This also applies to sub blueprints
|
||||
bp.CROW_MIDDLEWARES(app, RequestAppend);
|
||||
|
||||
CROW_BP_ROUTE(bp, "/")
|
||||
([&](const crow::request& req) {
|
||||
// Get RequestAppends context
|
||||
auto& ctx = app.get_context<RequestAppend>(req);
|
||||
ctx.message = "World";
|
||||
return "Hello:";
|
||||
});
|
||||
app.register_blueprint(bp);
|
||||
|
||||
app.port(18080).run();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
//#define CROW_STATIC_DIRECTORY "alternative_directory/"
|
||||
//#define CROW_STATIC_ENDPOINT "/alternative_endpoint/<path>"
|
||||
//#define CROW_DISABLE_STATIC_DIR
|
||||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
//Crow app initialization
|
||||
crow::SimpleApp app;
|
||||
|
||||
//
|
||||
CROW_ROUTE(app, "/")
|
||||
([](const crow::request&, crow::response& res) {
|
||||
//replace cat.jpg with your file path
|
||||
res.set_static_file_info("cat.jpg");
|
||||
res.end();
|
||||
});
|
||||
|
||||
|
||||
app.port(18080).run();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// You can also use the `/static` directory and endpoint (the directory needs to have the same path as your executable).
|
||||
/// Any file inside the static directory will have the same path it would in your filesystem.
|
||||
|
||||
/// TO change the static directory or endpoint, use the macros above (replace `alternative_directory` and/or `alternative_endpoint` with your own)
|
|
@ -0,0 +1,130 @@
|
|||
#include "crow.h"
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string message, crow::LogLevel level) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExampleMiddleware
|
||||
{
|
||||
std::string message;
|
||||
|
||||
ExampleMiddleware():
|
||||
message("foo")
|
||||
{
|
||||
}
|
||||
|
||||
void setMessage(const std::string& newMsg)
|
||||
{
|
||||
message = newMsg;
|
||||
}
|
||||
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{
|
||||
CROW_LOG_DEBUG << " - MESSAGE: " << message;
|
||||
}
|
||||
|
||||
void after_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::App<ExampleMiddleware> app;
|
||||
|
||||
app.get_middleware<ExampleMiddleware>().setMessage("hello");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")([] {
|
||||
return "Hello World!";
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([]() {
|
||||
return "About Crow example.";
|
||||
});
|
||||
|
||||
// a request to /path should be forwarded to /path/
|
||||
CROW_ROUTE(app, "/path/")
|
||||
([]() {
|
||||
return "Trailing slash test case..";
|
||||
});
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([] {
|
||||
crow::json::wvalue x;
|
||||
x["message"] = "Hello, World!";
|
||||
return x;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count) {
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b) {
|
||||
std::ostringstream os;
|
||||
os << a + b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods(crow::HTTPMethod::Post)([](const crow::request& req) {
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
auto sum = x["a"].i() + x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
app.route_dynamic("/params")([](const crow::request& req) {
|
||||
std::ostringstream os;
|
||||
os << "Params: " << req.url_params << "\n\n";
|
||||
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
|
||||
if (req.url_params.get("pew") != nullptr)
|
||||
{
|
||||
double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
|
||||
os << "The value of 'pew' is " << countD << '\n';
|
||||
}
|
||||
auto count = req.url_params.get_list("count");
|
||||
os << "The key 'count' contains " << count.size() << " value(s).\n";
|
||||
for (const auto& countVal : count)
|
||||
{
|
||||
os << " - " << countVal << '\n';
|
||||
}
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
// ignore all log
|
||||
crow::logger::setLogLevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#include "crow_all.h"
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")([] {
|
||||
return "Hello World!";
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([]() {
|
||||
return "About Crow example.";
|
||||
});
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([] {
|
||||
crow::json::wvalue x({{"message", "Hello, World!"}});
|
||||
x["message2"] = "Hello, World.. Again!";
|
||||
return x;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/json-initializer-list-constructor")
|
||||
([] {
|
||||
crow::json::wvalue r({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"fourth", (int64_t)54l}, /* stores a long (as 54l is a long literal) hence a std::int64_t. */
|
||||
{"fifth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"sixth", (uint64_t)54ul}, /* stores an unsigned long (as 54ul is an unsigned long literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
});
|
||||
return r;
|
||||
});
|
||||
|
||||
// json list response
|
||||
CROW_ROUTE(app, "/json_list")
|
||||
([] {
|
||||
crow::json::wvalue x(crow::json::wvalue::list({1, 2, 3}));
|
||||
return x;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count) {
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
|
||||
// example which uses only response as a paramter without
|
||||
// request being a parameter.
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b) {
|
||||
std::ostringstream os;
|
||||
os << a + b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods("POST"_method)([](const crow::request& req) {
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
int sum = x["a"].i() + x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/params")
|
||||
([](const crow::request& req) {
|
||||
std::ostringstream os;
|
||||
os << "Params: " << req.url_params << "\n\n";
|
||||
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
|
||||
if (req.url_params.get("pew") != nullptr)
|
||||
{
|
||||
double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
|
||||
os << "The value of 'pew' is " << countD << '\n';
|
||||
}
|
||||
auto count = req.url_params.get_list("count");
|
||||
os << "The key 'count' contains " << count.size() << " value(s).\n";
|
||||
for (const auto& countVal : count)
|
||||
{
|
||||
os << " - " << countVal << '\n';
|
||||
}
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
|
||||
// ignore all log
|
||||
crow::logger::setLogLevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.server_name("CrowCpp")
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello, world!";
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#include "crow.h"
|
||||
#include "crow/middlewares/cookie_parser.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// Include CookieParser middleware
|
||||
crow::App<crow::CookieParser> app;
|
||||
|
||||
CROW_ROUTE(app, "/read")
|
||||
([&](const crow::request& req) {
|
||||
auto& ctx = app.get_context<crow::CookieParser>(req);
|
||||
// Read cookies with get_cookie
|
||||
auto value = ctx.get_cookie("key");
|
||||
return "value: " + value;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/write")
|
||||
([&](const crow::request& req) {
|
||||
auto& ctx = app.get_context<crow::CookieParser>(req);
|
||||
// Store cookies with set_cookie
|
||||
ctx.set_cookie("key", "word")
|
||||
// configure additional parameters
|
||||
.path("/")
|
||||
.max_age(120)
|
||||
.httponly();
|
||||
return "ok!";
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#include "crow.h"
|
||||
#include "crow/middlewares/cors.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// Enable CORS
|
||||
crow::App<crow::CORSHandler> app;
|
||||
|
||||
// Customize CORS
|
||||
auto& cors = app.get_middleware<crow::CORSHandler>();
|
||||
|
||||
// clang-format off
|
||||
cors
|
||||
.global()
|
||||
.headers("X-Custom-Header", "Upgrade-Insecure-Requests")
|
||||
.methods("POST"_method, "GET"_method)
|
||||
.prefix("/cors")
|
||||
.origin("example.com")
|
||||
.prefix("/nocors")
|
||||
.ignore();
|
||||
// clang-format on
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Check Access-Control-Allow-Methods header";
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/cors")
|
||||
([]() {
|
||||
return "Check Access-Control-Allow-Origin header";
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
#include "crow.h"
|
||||
#include "crow/middlewares/session.h"
|
||||
|
||||
crow::response redirect()
|
||||
{
|
||||
crow::response res;
|
||||
res.redirect("/");
|
||||
return res;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Choose a storage kind for:
|
||||
// - InMemoryStore stores all entries in memory
|
||||
// - FileStore stores all entries in json files
|
||||
using Session = crow::SessionMiddleware<crow::InMemoryStore>;
|
||||
|
||||
// Writing your own store is easy
|
||||
// Check out the existing ones for guidelines
|
||||
|
||||
// Make sure the CookieParser is registered before the Session
|
||||
crow::App<crow::CookieParser, Session> app{Session{
|
||||
// customize cookies
|
||||
crow::CookieParser::Cookie("session").max_age(/*one day*/ 24 * 60 * 60).path("/"),
|
||||
// set session id length (small value only for demonstration purposes)
|
||||
4,
|
||||
// init the store
|
||||
crow::InMemoryStore{}}};
|
||||
|
||||
// List all values
|
||||
CROW_ROUTE(app, "/")
|
||||
([&](const crow::request& req) {
|
||||
// get session as middleware context
|
||||
auto& session = app.get_context<Session>(req);
|
||||
// the session acts as a multi-type map
|
||||
// that can store string, integers, doubles and bools
|
||||
// besides get/set/remove it also supports more advanced locking operations
|
||||
|
||||
// Atomically increase number of views
|
||||
// This will not skip a view even on multithreaded applications
|
||||
// with multiple concurrent requests from a client
|
||||
// if "views" doesn't exist, it'll be default initialized
|
||||
session.apply("views", [](int v) {
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
// get all currently present keys
|
||||
auto keys = session.keys();
|
||||
|
||||
std::string out;
|
||||
for (const auto& key : keys)
|
||||
// .string(key) converts a value of any type to a string
|
||||
out += "<p> " + key + " = " + session.string(key) + "</p>";
|
||||
return out;
|
||||
});
|
||||
|
||||
// Get a key
|
||||
CROW_ROUTE(app, "/get")
|
||||
([&](const crow::request& req) {
|
||||
auto& session = app.get_context<Session>(req);
|
||||
auto key = req.url_params.get("key");
|
||||
|
||||
// get a string
|
||||
// return "_NOT_FOUND_" if value is not found or of another type
|
||||
std::string string_v = session.get(key, "_NOT_FOUND_");
|
||||
// alternatively one can use
|
||||
// session.get<std::string>(key)
|
||||
// where the fallback is an empty value ""
|
||||
(void)string_v;
|
||||
|
||||
// get int
|
||||
// because supporting multiple integer types in a type bound map would be cumbersome,
|
||||
// all integral values (except uint64_t) are promoted to int64_t
|
||||
// that is why get<int>, get<uint32_t>, get<int64_t> are all accessing the same type
|
||||
int int_v = session.get(key, -1);
|
||||
(void)int_v;
|
||||
|
||||
return session.string(key);
|
||||
});
|
||||
|
||||
// Set a key
|
||||
// A session is stored as soon as it becomes non empty
|
||||
CROW_ROUTE(app, "/set")
|
||||
([&](const crow::request& req) {
|
||||
auto& session = app.get_context<Session>(req);
|
||||
|
||||
auto key = req.url_params.get("key");
|
||||
auto value = req.url_params.get("value");
|
||||
|
||||
session.set(key, value);
|
||||
|
||||
return redirect();
|
||||
});
|
||||
|
||||
// Remove a key
|
||||
CROW_ROUTE(app, "/remove")
|
||||
([&](const crow::request& req) {
|
||||
auto& session = app.get_context<Session>(req);
|
||||
auto key = req.url_params.get("key");
|
||||
session.remove(key);
|
||||
|
||||
return redirect();
|
||||
});
|
||||
|
||||
// Manually lock a session for synchronization in parallel requests
|
||||
CROW_ROUTE(app, "/lock")
|
||||
([&](const crow::request& req) {
|
||||
auto& session = app.get_context<Session>(req);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> l(session.mutex());
|
||||
|
||||
if (session.get("views", 0) % 2 == 0)
|
||||
{
|
||||
session.set("even", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
session.remove("even");
|
||||
}
|
||||
|
||||
return redirect();
|
||||
});
|
||||
|
||||
app.port(18080)
|
||||
//.multithreaded()
|
||||
.run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include "crow.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello world!";
|
||||
});
|
||||
|
||||
app.port(18080).ssl_file("test.crt", "test.key").run();
|
||||
|
||||
// Use .pem file
|
||||
//app.port(18080).ssl_file("test.pem").run();
|
||||
|
||||
// Use custom context; see asio::ssl::context
|
||||
/*
|
||||
* crow::ssl_context_t ctx;
|
||||
* ctx.set_verify_mode(...)
|
||||
*
|
||||
* ... configuring ctx
|
||||
*
|
||||
* app.port(18080).ssl(ctx).run();
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#include "crow.h"
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
std::mutex mtx;
|
||||
std::unordered_set<crow::websocket::connection*> users;
|
||||
|
||||
CROW_WEBSOCKET_ROUTE(app, "/ws")
|
||||
.onopen([&](crow::websocket::connection& conn) {
|
||||
CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip();
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
users.insert(&conn);
|
||||
})
|
||||
.onclose([&](crow::websocket::connection& conn, const std::string& reason) {
|
||||
CROW_LOG_INFO << "websocket connection closed: " << reason;
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
users.erase(&conn);
|
||||
})
|
||||
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary) {
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
for (auto u : users)
|
||||
if (is_binary)
|
||||
u->send_binary(data);
|
||||
else
|
||||
u->send_text(data);
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([] {
|
||||
char name[256];
|
||||
gethostname(name, 256);
|
||||
crow::mustache::context x;
|
||||
x["servername"] = name;
|
||||
|
||||
auto page = crow::mustache::load("ws.html");
|
||||
return page.render(x);
|
||||
});
|
||||
|
||||
app.port(40080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="msg" type="text"></input>
|
||||
<button id="send">
|
||||
Send
|
||||
</button><BR>
|
||||
<textarea id="log" cols=100 rows=50>
|
||||
</textarea>
|
||||
<script>
|
||||
var sock = new WebSocket("ws://{{servername}}:40080/ws");
|
||||
|
||||
sock.onopen = ()=>{
|
||||
console.log('open')
|
||||
}
|
||||
sock.onerror = (e)=>{
|
||||
console.log('error',e)
|
||||
}
|
||||
sock.onclose = (e)=>{
|
||||
console.log('close', e)
|
||||
}
|
||||
sock.onmessage = (e)=>{
|
||||
$("#log").val(
|
||||
e.data +"\n" + $("#log").val());
|
||||
}
|
||||
$("#msg").keypress(function(e){
|
||||
if (e.which == 13)
|
||||
{
|
||||
sock.send($("#msg").val());
|
||||
$("#msg").val("");
|
||||
}
|
||||
});
|
||||
$("#send").click(()=>{
|
||||
sock.send($("#msg").val());
|
||||
$("#msg").val("");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include "crow/query_string.h"
|
||||
#include "crow/http_parser_merged.h"
|
||||
#include "crow/ci_map.h"
|
||||
#include "crow/TinySHA1.hpp"
|
||||
#include "crow/settings.h"
|
||||
#include "crow/socket_adaptors.h"
|
||||
#include "crow/json.h"
|
||||
#include "crow/mustache.h"
|
||||
#include "crow/logging.h"
|
||||
#include "crow/task_timer.h"
|
||||
#include "crow/utility.h"
|
||||
#include "crow/common.h"
|
||||
#include "crow/http_request.h"
|
||||
#include "crow/websocket.h"
|
||||
#include "crow/parser.h"
|
||||
#include "crow/http_response.h"
|
||||
#include "crow/multipart.h"
|
||||
#include "crow/routing.h"
|
||||
#include "crow/middleware.h"
|
||||
#include "crow/middleware_context.h"
|
||||
#include "crow/compression.h"
|
||||
#include "crow/http_connection.h"
|
||||
#include "crow/http_server.h"
|
||||
#include "crow/app.h"
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
*
|
||||
* TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based
|
||||
* on the implementation in boost::uuid::details.
|
||||
*
|
||||
* SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
|
||||
*
|
||||
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef _TINY_SHA1_HPP_
|
||||
#define _TINY_SHA1_HPP_
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
namespace sha1
|
||||
{
|
||||
class SHA1
|
||||
{
|
||||
public:
|
||||
typedef uint32_t digest32_t[5];
|
||||
typedef uint8_t digest8_t[20];
|
||||
inline static uint32_t LeftRotate(uint32_t value, size_t count) {
|
||||
return (value << count) ^ (value >> (32-count));
|
||||
}
|
||||
SHA1(){ reset(); }
|
||||
virtual ~SHA1() {}
|
||||
SHA1(const SHA1& s) { *this = s; }
|
||||
const SHA1& operator = (const SHA1& s) {
|
||||
memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
|
||||
memcpy(m_block, s.m_block, 64);
|
||||
m_blockByteIndex = s.m_blockByteIndex;
|
||||
m_byteCount = s.m_byteCount;
|
||||
return *this;
|
||||
}
|
||||
SHA1& reset() {
|
||||
m_digest[0] = 0x67452301;
|
||||
m_digest[1] = 0xEFCDAB89;
|
||||
m_digest[2] = 0x98BADCFE;
|
||||
m_digest[3] = 0x10325476;
|
||||
m_digest[4] = 0xC3D2E1F0;
|
||||
m_blockByteIndex = 0;
|
||||
m_byteCount = 0;
|
||||
return *this;
|
||||
}
|
||||
SHA1& processByte(uint8_t octet) {
|
||||
this->m_block[this->m_blockByteIndex++] = octet;
|
||||
++this->m_byteCount;
|
||||
if(m_blockByteIndex == 64) {
|
||||
this->m_blockByteIndex = 0;
|
||||
processBlock();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
SHA1& processBlock(const void* const start, const void* const end) {
|
||||
const uint8_t* begin = static_cast<const uint8_t*>(start);
|
||||
const uint8_t* finish = static_cast<const uint8_t*>(end);
|
||||
while(begin != finish) {
|
||||
processByte(*begin);
|
||||
begin++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
SHA1& processBytes(const void* const data, size_t len) {
|
||||
const uint8_t* block = static_cast<const uint8_t*>(data);
|
||||
processBlock(block, block + len);
|
||||
return *this;
|
||||
}
|
||||
const uint32_t* getDigest(digest32_t digest) {
|
||||
size_t bitCount = this->m_byteCount * 8;
|
||||
processByte(0x80);
|
||||
if (this->m_blockByteIndex > 56) {
|
||||
while (m_blockByteIndex != 0) {
|
||||
processByte(0);
|
||||
}
|
||||
while (m_blockByteIndex < 56) {
|
||||
processByte(0);
|
||||
}
|
||||
} else {
|
||||
while (m_blockByteIndex < 56) {
|
||||
processByte(0);
|
||||
}
|
||||
}
|
||||
processByte(0);
|
||||
processByte(0);
|
||||
processByte(0);
|
||||
processByte(0);
|
||||
processByte( static_cast<unsigned char>((bitCount>>24) & 0xFF));
|
||||
processByte( static_cast<unsigned char>((bitCount>>16) & 0xFF));
|
||||
processByte( static_cast<unsigned char>((bitCount>>8 ) & 0xFF));
|
||||
processByte( static_cast<unsigned char>((bitCount) & 0xFF));
|
||||
|
||||
memcpy(digest, m_digest, 5 * sizeof(uint32_t));
|
||||
return digest;
|
||||
}
|
||||
const uint8_t* getDigestBytes(digest8_t digest) {
|
||||
digest32_t d32;
|
||||
getDigest(d32);
|
||||
size_t di = 0;
|
||||
digest[di++] = ((d32[0] >> 24) & 0xFF);
|
||||
digest[di++] = ((d32[0] >> 16) & 0xFF);
|
||||
digest[di++] = ((d32[0] >> 8) & 0xFF);
|
||||
digest[di++] = ((d32[0]) & 0xFF);
|
||||
|
||||
digest[di++] = ((d32[1] >> 24) & 0xFF);
|
||||
digest[di++] = ((d32[1] >> 16) & 0xFF);
|
||||
digest[di++] = ((d32[1] >> 8) & 0xFF);
|
||||
digest[di++] = ((d32[1]) & 0xFF);
|
||||
|
||||
digest[di++] = ((d32[2] >> 24) & 0xFF);
|
||||
digest[di++] = ((d32[2] >> 16) & 0xFF);
|
||||
digest[di++] = ((d32[2] >> 8) & 0xFF);
|
||||
digest[di++] = ((d32[2]) & 0xFF);
|
||||
|
||||
digest[di++] = ((d32[3] >> 24) & 0xFF);
|
||||
digest[di++] = ((d32[3] >> 16) & 0xFF);
|
||||
digest[di++] = ((d32[3] >> 8) & 0xFF);
|
||||
digest[di++] = ((d32[3]) & 0xFF);
|
||||
|
||||
digest[di++] = ((d32[4] >> 24) & 0xFF);
|
||||
digest[di++] = ((d32[4] >> 16) & 0xFF);
|
||||
digest[di++] = ((d32[4] >> 8) & 0xFF);
|
||||
digest[di++] = ((d32[4]) & 0xFF);
|
||||
return digest;
|
||||
}
|
||||
|
||||
protected:
|
||||
void processBlock() {
|
||||
uint32_t w[80];
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
w[i] = (m_block[i*4 + 0] << 24);
|
||||
w[i] |= (m_block[i*4 + 1] << 16);
|
||||
w[i] |= (m_block[i*4 + 2] << 8);
|
||||
w[i] |= (m_block[i*4 + 3]);
|
||||
}
|
||||
for (size_t i = 16; i < 80; i++) {
|
||||
w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1);
|
||||
}
|
||||
|
||||
uint32_t a = m_digest[0];
|
||||
uint32_t b = m_digest[1];
|
||||
uint32_t c = m_digest[2];
|
||||
uint32_t d = m_digest[3];
|
||||
uint32_t e = m_digest[4];
|
||||
|
||||
for (std::size_t i=0; i<80; ++i) {
|
||||
uint32_t f = 0;
|
||||
uint32_t k = 0;
|
||||
|
||||
if (i<20) {
|
||||
f = (b & c) | (~b & d);
|
||||
k = 0x5A827999;
|
||||
} else if (i<40) {
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ED9EBA1;
|
||||
} else if (i<60) {
|
||||
f = (b & c) | (b & d) | (c & d);
|
||||
k = 0x8F1BBCDC;
|
||||
} else {
|
||||
f = b ^ c ^ d;
|
||||
k = 0xCA62C1D6;
|
||||
}
|
||||
uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
|
||||
e = d;
|
||||
d = c;
|
||||
c = LeftRotate(b, 30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
|
||||
m_digest[0] += a;
|
||||
m_digest[1] += b;
|
||||
m_digest[2] += c;
|
||||
m_digest[3] += d;
|
||||
m_digest[4] += e;
|
||||
}
|
||||
private:
|
||||
digest32_t m_digest;
|
||||
uint8_t m_block[64];
|
||||
size_t m_blockByteIndex;
|
||||
size_t m_byteCount;
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,600 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "crow/version.h"
|
||||
#include "crow/settings.h"
|
||||
#include "crow/logging.h"
|
||||
#include "crow/utility.h"
|
||||
#include "crow/routing.h"
|
||||
#include "crow/middleware_context.h"
|
||||
#include "crow/http_request.h"
|
||||
#include "crow/http_server.h"
|
||||
#include "crow/task_timer.h"
|
||||
#include "crow/websocket.h"
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
#include "crow/compression.h"
|
||||
#endif
|
||||
|
||||
#ifdef CROW_MSVC_WORKAROUND
|
||||
#define CROW_ROUTE(app, url) app.route_dynamic(url)
|
||||
#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_dynamic(url)
|
||||
#else
|
||||
#define CROW_ROUTE(app, url) app.template route<crow::black_magic::get_parameter_tag(url)>(url)
|
||||
#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_tagged<crow::black_magic::get_parameter_tag(url)>(url)
|
||||
#define CROW_WEBSOCKET_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url).websocket<std::remove_reference<decltype(app)>::type>(&app)
|
||||
#define CROW_MIDDLEWARES(app, ...) template middlewares<typename std::remove_reference<decltype(app)>::type, __VA_ARGS__>()
|
||||
#endif
|
||||
#define CROW_CATCHALL_ROUTE(app) app.catchall_route()
|
||||
#define CROW_BP_CATCHALL_ROUTE(blueprint) blueprint.catchall_rule()
|
||||
|
||||
namespace crow
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
using ssl_context_t = asio::ssl::context;
|
||||
#endif
|
||||
/// The main server application
|
||||
|
||||
///
|
||||
/// Use `SimpleApp` or `App<Middleware1, Middleware2, etc...>`
|
||||
template<typename... Middlewares>
|
||||
class Crow
|
||||
{
|
||||
public:
|
||||
/// This crow application
|
||||
using self_t = Crow;
|
||||
/// The HTTP server
|
||||
using server_t = Server<Crow, SocketAdaptor, Middlewares...>;
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
/// An HTTP server that runs on SSL with an SSLAdaptor
|
||||
using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>;
|
||||
#endif
|
||||
Crow()
|
||||
{}
|
||||
|
||||
/// Construct Crow with a subset of middleware
|
||||
template<typename... Ts>
|
||||
Crow(Ts&&... ts):
|
||||
middlewares_(make_middleware_tuple(std::forward<Ts>(ts)...))
|
||||
{}
|
||||
|
||||
/// Process an Upgrade request
|
||||
|
||||
///
|
||||
/// Currently used to upgrade an HTTP connection to a WebSocket connection
|
||||
template<typename Adaptor>
|
||||
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
|
||||
{
|
||||
router_.handle_upgrade(req, res, adaptor);
|
||||
}
|
||||
|
||||
/// Process only the method and URL of a request and provide a route (or an error response)
|
||||
std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
|
||||
{
|
||||
return router_.handle_initial(req, res);
|
||||
}
|
||||
|
||||
/// Process the fully parsed request and generate a response for it
|
||||
void handle(request& req, response& res, std::unique_ptr<routing_handle_result>& found)
|
||||
{
|
||||
router_.handle<self_t>(req, res, *found);
|
||||
}
|
||||
|
||||
/// Process a fully parsed request from start to finish (primarily used for debugging)
|
||||
void handle_full(request& req, response& res)
|
||||
{
|
||||
auto found = handle_initial(req, res);
|
||||
if (found->rule_index)
|
||||
handle(req, res, found);
|
||||
}
|
||||
|
||||
/// Create a dynamic route using a rule (**Use CROW_ROUTE instead**)
|
||||
DynamicRule& route_dynamic(std::string&& rule)
|
||||
{
|
||||
return router_.new_rule_dynamic(std::move(rule));
|
||||
}
|
||||
|
||||
///Create a route using a rule (**Use CROW_ROUTE instead**)
|
||||
template<uint64_t Tag>
|
||||
#ifdef CROW_GCC83_WORKAROUND
|
||||
auto& route(std::string&& rule)
|
||||
#else
|
||||
auto route(std::string&& rule)
|
||||
#endif
|
||||
#if defined CROW_CAN_USE_CPP17 && !defined CROW_GCC83_WORKAROUND
|
||||
-> typename std::invoke_result<decltype(&Router::new_rule_tagged<Tag>), Router, std::string&&>::type
|
||||
#elif !defined CROW_GCC83_WORKAROUND
|
||||
-> typename std::result_of<decltype (&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type
|
||||
#endif
|
||||
{
|
||||
return router_.new_rule_tagged<Tag>(std::move(rule));
|
||||
}
|
||||
|
||||
/// Create a route for any requests without a proper route (**Use CROW_CATCHALL_ROUTE instead**)
|
||||
CatchallRule& catchall_route()
|
||||
{
|
||||
return router_.catchall_rule();
|
||||
}
|
||||
|
||||
/// Set the default max payload size for websockets
|
||||
self_t& websocket_max_payload(uint64_t max_payload)
|
||||
{
|
||||
max_payload_ = max_payload;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the default max payload size for websockets
|
||||
uint64_t websocket_max_payload()
|
||||
{
|
||||
return max_payload_;
|
||||
}
|
||||
|
||||
self_t& signal_clear()
|
||||
{
|
||||
signals_.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_t& signal_add(int signal_number)
|
||||
{
|
||||
signals_.push_back(signal_number);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<int> signals()
|
||||
{
|
||||
return signals_;
|
||||
}
|
||||
|
||||
/// Set the port that Crow will handle requests on
|
||||
self_t& port(std::uint16_t port)
|
||||
{
|
||||
port_ = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the port that Crow will handle requests on
|
||||
std::uint16_t port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
/// Set the connection timeout in seconds (default is 5)
|
||||
self_t& timeout(std::uint8_t timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set the server name
|
||||
self_t& server_name(std::string server_name)
|
||||
{
|
||||
server_name_ = server_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// The IP address that Crow will handle requests on (default is 0.0.0.0)
|
||||
self_t& bindaddr(std::string bindaddr)
|
||||
{
|
||||
bindaddr_ = bindaddr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the address that Crow will handle requests on
|
||||
std::string bindaddr()
|
||||
{
|
||||
return bindaddr_;
|
||||
}
|
||||
|
||||
/// Run the server on multiple threads using all available threads
|
||||
self_t& multithreaded()
|
||||
{
|
||||
return concurrency(std::thread::hardware_concurrency());
|
||||
}
|
||||
|
||||
/// Run the server on multiple threads using a specific number
|
||||
self_t& concurrency(std::uint16_t concurrency)
|
||||
{
|
||||
if (concurrency < 2) // Crow can have a minimum of 2 threads running
|
||||
concurrency = 2;
|
||||
concurrency_ = concurrency;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the number of threads that server is using
|
||||
std::uint16_t concurrency()
|
||||
{
|
||||
return concurrency_;
|
||||
}
|
||||
|
||||
/// Set the server's log level
|
||||
|
||||
///
|
||||
/// Possible values are:<br>
|
||||
/// crow::LogLevel::Debug (0)<br>
|
||||
/// crow::LogLevel::Info (1)<br>
|
||||
/// crow::LogLevel::Warning (2)<br>
|
||||
/// crow::LogLevel::Error (3)<br>
|
||||
/// crow::LogLevel::Critical (4)<br>
|
||||
self_t& loglevel(LogLevel level)
|
||||
{
|
||||
crow::logger::setLogLevel(level);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set the response body size (in bytes) beyond which Crow automatically streams responses (Default is 1MiB)
|
||||
|
||||
///
|
||||
/// Any streamed response is unaffected by Crow's timer, and therefore won't timeout before a response is fully sent.
|
||||
self_t& stream_threshold(size_t threshold)
|
||||
{
|
||||
res_stream_threshold_ = threshold;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the response body size (in bytes) beyond which Crow automatically streams responses
|
||||
size_t& stream_threshold()
|
||||
{
|
||||
return res_stream_threshold_;
|
||||
}
|
||||
|
||||
self_t& register_blueprint(Blueprint& blueprint)
|
||||
{
|
||||
router_.register_blueprint(blueprint);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set a custom duration and function to run on every tick
|
||||
template<typename Duration, typename Func>
|
||||
self_t& tick(Duration d, Func f)
|
||||
{
|
||||
tick_interval_ = std::chrono::duration_cast<std::chrono::milliseconds>(d);
|
||||
tick_function_ = f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
self_t& use_compression(compression::algorithm algorithm)
|
||||
{
|
||||
comp_algorithm_ = algorithm;
|
||||
compression_used_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
compression::algorithm compression_algorithm()
|
||||
{
|
||||
return comp_algorithm_;
|
||||
}
|
||||
|
||||
bool compression_used() const
|
||||
{
|
||||
return compression_used_;
|
||||
}
|
||||
#endif
|
||||
/// A wrapper for `validate()` in the router
|
||||
|
||||
///
|
||||
/// Go through the rules, upgrade them if possible, and add them to the list of rules
|
||||
void validate()
|
||||
{
|
||||
if (!validated_)
|
||||
{
|
||||
|
||||
#ifndef CROW_DISABLE_STATIC_DIR
|
||||
|
||||
// stat on windows doesn't care whether '/' or '\' is being used. on Linux however, using '\' doesn't work. therefore every instance of '\' gets replaced with '/' then a check is done to make sure the directory ends with '/'.
|
||||
std::string static_dir_(CROW_STATIC_DIRECTORY);
|
||||
std::replace(static_dir_.begin(), static_dir_.end(), '\\', '/');
|
||||
if (static_dir_[static_dir_.length() - 1] != '/')
|
||||
static_dir_ += '/';
|
||||
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([static_dir_](crow::response& res, std::string file_path_partial) {
|
||||
utility::sanitize_filename(file_path_partial);
|
||||
res.set_static_file_info_unsafe(static_dir_ + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
if (!router_.blueprints().empty())
|
||||
#endif
|
||||
{
|
||||
for (Blueprint* bp : router_.blueprints())
|
||||
{
|
||||
if (!bp->static_dir().empty())
|
||||
{
|
||||
// stat on windows doesn't care whether '/' or '\' is being used. on Linux however, using '\' doesn't work. therefore every instance of '\' gets replaced with '/' then a check is done to make sure the directory ends with '/'.
|
||||
std::string static_dir_(bp->static_dir());
|
||||
std::replace(static_dir_.begin(), static_dir_.end(), '\\', '/');
|
||||
if (static_dir_[static_dir_.length() - 1] != '/')
|
||||
static_dir_ += '/';
|
||||
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([static_dir_](crow::response& res, std::string file_path_partial) {
|
||||
utility::sanitize_filename(file_path_partial);
|
||||
res.set_static_file_info_unsafe(static_dir_ + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
router_.validate();
|
||||
validated_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the server
|
||||
void run()
|
||||
{
|
||||
|
||||
validate();
|
||||
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (ssl_used_)
|
||||
{
|
||||
ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, &ssl_context_)));
|
||||
ssl_server_->set_tick_function(tick_interval_, tick_function_);
|
||||
ssl_server_->signal_clear();
|
||||
for (auto snum : signals_)
|
||||
{
|
||||
ssl_server_->signal_add(snum);
|
||||
}
|
||||
notify_server_start();
|
||||
ssl_server_->run();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
|
||||
server_->set_tick_function(tick_interval_, tick_function_);
|
||||
for (auto snum : signals_)
|
||||
{
|
||||
server_->signal_add(snum);
|
||||
}
|
||||
notify_server_start();
|
||||
server_->run();
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-blocking version of \ref run()
|
||||
///
|
||||
/// The output from this method needs to be saved into a variable!
|
||||
/// Otherwise the call will be made on the same thread.
|
||||
std::future<void> run_async()
|
||||
{
|
||||
return std::async(std::launch::async, [&] {
|
||||
this->run();
|
||||
});
|
||||
}
|
||||
|
||||
/// Stop the server
|
||||
void stop()
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (ssl_used_)
|
||||
{
|
||||
if (ssl_server_) { ssl_server_->stop(); }
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// TODO(EDev): Move these 6 lines to a method in http_server.
|
||||
std::vector<crow::websocket::connection*> websockets_to_close = websockets_;
|
||||
for (auto websocket : websockets_to_close)
|
||||
{
|
||||
CROW_LOG_INFO << "Quitting Websocket: " << websocket;
|
||||
websocket->close("Server Application Terminated");
|
||||
}
|
||||
if (server_) { server_->stop(); }
|
||||
}
|
||||
}
|
||||
|
||||
void add_websocket(crow::websocket::connection* conn)
|
||||
{
|
||||
websockets_.push_back(conn);
|
||||
}
|
||||
|
||||
void remove_websocket(crow::websocket::connection* conn)
|
||||
{
|
||||
websockets_.erase(std::remove(websockets_.begin(), websockets_.end(), conn), websockets_.end());
|
||||
}
|
||||
|
||||
/// Print the routing paths defined for each HTTP method
|
||||
void debug_print()
|
||||
{
|
||||
CROW_LOG_DEBUG << "Routing:";
|
||||
router_.debug_print();
|
||||
}
|
||||
|
||||
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
|
||||
/// Use certificate and key files for SSL
|
||||
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_peer);
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
|
||||
ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
|
||||
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
|
||||
ssl_context_.set_options(
|
||||
asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Use .pem file for SSL
|
||||
self_t& ssl_file(const std::string& pem_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_peer);
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
|
||||
ssl_context_.load_verify_file(pem_filename);
|
||||
ssl_context_.set_options(
|
||||
asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Use certificate chain and key files for SSL
|
||||
self_t& ssl_chainfile(const std::string& crt_filename, const std::string& key_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_peer);
|
||||
ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
|
||||
ssl_context_.use_certificate_chain_file(crt_filename);
|
||||
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
|
||||
ssl_context_.set_options(
|
||||
asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_t& ssl(asio::ssl::context&& ctx)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
ssl_context_ = std::move(ctx);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ssl_used() const
|
||||
{
|
||||
return ssl_used_;
|
||||
}
|
||||
#else
|
||||
template<typename T, typename... Remain>
|
||||
self_t& ssl_file(T&&, Remain&&...)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename... Remain>
|
||||
self_t& ssl_chainfile(T&&, Remain&&...)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t& ssl(T&&)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ssl_used() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// middleware
|
||||
using context_t = detail::context<Middlewares...>;
|
||||
using mw_container_t = std::tuple<Middlewares...>;
|
||||
template<typename T>
|
||||
typename T::context& get_context(const request& req)
|
||||
{
|
||||
static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
|
||||
auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context);
|
||||
return ctx.template get<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& get_middleware()
|
||||
{
|
||||
return utility::get_element_by_type<T, Middlewares...>(middlewares_);
|
||||
}
|
||||
|
||||
/// Wait until the server has properly started
|
||||
void wait_for_server_start()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(start_mutex_);
|
||||
if (!server_started_)
|
||||
cv_started_.wait(lock);
|
||||
}
|
||||
if (server_)
|
||||
server_->wait_for_start();
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
else if (ssl_server_)
|
||||
ssl_server_->wait_for_start();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename... Ts>
|
||||
std::tuple<Middlewares...> make_middleware_tuple(Ts&&... ts)
|
||||
{
|
||||
auto fwd = std::forward_as_tuple((ts)...);
|
||||
return std::make_tuple(
|
||||
std::forward<Middlewares>(
|
||||
black_magic::tuple_extract<Middlewares, decltype(fwd)>(fwd))...);
|
||||
}
|
||||
|
||||
/// Notify anything using `wait_for_server_start()` to proceed
|
||||
void notify_server_start()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(start_mutex_);
|
||||
server_started_ = true;
|
||||
cv_started_.notify_all();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::uint8_t timeout_{5};
|
||||
uint16_t port_ = 80;
|
||||
uint16_t concurrency_ = 2;
|
||||
uint64_t max_payload_{UINT64_MAX};
|
||||
bool validated_ = false;
|
||||
std::string server_name_ = std::string("Crow/") + VERSION;
|
||||
std::string bindaddr_ = "0.0.0.0";
|
||||
size_t res_stream_threshold_ = 1048576;
|
||||
Router router_;
|
||||
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
compression::algorithm comp_algorithm_;
|
||||
bool compression_used_{false};
|
||||
#endif
|
||||
|
||||
std::chrono::milliseconds tick_interval_;
|
||||
std::function<void()> tick_function_;
|
||||
|
||||
std::tuple<Middlewares...> middlewares_;
|
||||
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
std::unique_ptr<ssl_server_t> ssl_server_;
|
||||
bool ssl_used_{false};
|
||||
ssl_context_t ssl_context_{asio::ssl::context::sslv23};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<server_t> server_;
|
||||
|
||||
std::vector<int> signals_{SIGINT, SIGTERM};
|
||||
|
||||
bool server_started_{false};
|
||||
std::condition_variable cv_started_;
|
||||
std::mutex start_mutex_;
|
||||
std::vector<crow::websocket::connection*> websockets_;
|
||||
};
|
||||
template<typename... Middlewares>
|
||||
using App = Crow<Middlewares...>;
|
||||
using SimpleApp = Crow<>;
|
||||
} // namespace crow
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <locale>
|
||||
#include <unordered_map>
|
||||
#include "crow/utility.h"
|
||||
|
||||
namespace crow
|
||||
{
|
||||
/// Hashing function for ci_map (unordered_multimap).
|
||||
struct ci_hash
|
||||
{
|
||||
size_t operator()(const std::string& key) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for (auto c : key)
|
||||
hash_combine(seed, std::toupper(c, locale));
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
private:
|
||||
static inline void hash_combine(std::size_t& seed, char v)
|
||||
{
|
||||
std::hash<char> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
};
|
||||
|
||||
/// Equals function for ci_map (unordered_multimap).
|
||||
struct ci_key_eq
|
||||
{
|
||||
bool operator()(const std::string& l, const std::string& r) const
|
||||
{
|
||||
return utility::string_equals(l, r);
|
||||
}
|
||||
};
|
||||
|
||||
using ci_map = std::unordered_multimap<std::string, std::string, ci_hash, ci_key_eq>;
|
||||
} // namespace crow
|
|
@ -0,0 +1,354 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include "crow/utility.h"
|
||||
|
||||
namespace crow
|
||||
{
|
||||
const char cr = '\r';
|
||||
const char lf = '\n';
|
||||
const std::string crlf("\r\n");
|
||||
|
||||
enum class HTTPMethod : char
|
||||
{
|
||||
#ifndef DELETE
|
||||
DELETE = 0,
|
||||
GET,
|
||||
HEAD,
|
||||
POST,
|
||||
PUT,
|
||||
|
||||
CONNECT,
|
||||
OPTIONS,
|
||||
TRACE,
|
||||
|
||||
PATCH,
|
||||
PURGE,
|
||||
|
||||
COPY,
|
||||
LOCK,
|
||||
MKCOL,
|
||||
MOVE,
|
||||
PROPFIND,
|
||||
PROPPATCH,
|
||||
SEARCH,
|
||||
UNLOCK,
|
||||
BIND,
|
||||
REBIND,
|
||||
UNBIND,
|
||||
ACL,
|
||||
|
||||
REPORT,
|
||||
MKACTIVITY,
|
||||
CHECKOUT,
|
||||
MERGE,
|
||||
|
||||
MSEARCH,
|
||||
NOTIFY,
|
||||
SUBSCRIBE,
|
||||
UNSUBSCRIBE,
|
||||
|
||||
MKCALENDAR,
|
||||
|
||||
LINK,
|
||||
UNLINK,
|
||||
|
||||
SOURCE,
|
||||
#endif
|
||||
|
||||
Delete = 0,
|
||||
Get,
|
||||
Head,
|
||||
Post,
|
||||
Put,
|
||||
|
||||
Connect,
|
||||
Options,
|
||||
Trace,
|
||||
|
||||
Patch,
|
||||
Purge,
|
||||
|
||||
Copy,
|
||||
Lock,
|
||||
MkCol,
|
||||
Move,
|
||||
Propfind,
|
||||
Proppatch,
|
||||
Search,
|
||||
Unlock,
|
||||
Bind,
|
||||
Rebind,
|
||||
Unbind,
|
||||
Acl,
|
||||
|
||||
Report,
|
||||
MkActivity,
|
||||
Checkout,
|
||||
Merge,
|
||||
|
||||
MSearch,
|
||||
Notify,
|
||||
Subscribe,
|
||||
Unsubscribe,
|
||||
|
||||
MkCalendar,
|
||||
|
||||
Link,
|
||||
Unlink,
|
||||
|
||||
Source,
|
||||
|
||||
|
||||
InternalMethodCount,
|
||||
// should not add an item below this line: used for array count
|
||||
};
|
||||
|
||||
constexpr const char* method_strings[] =
|
||||
{
|
||||
"DELETE",
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST",
|
||||
"PUT",
|
||||
|
||||
"CONNECT",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
|
||||
"PATCH",
|
||||
"PURGE",
|
||||
|
||||
"COPY",
|
||||
"LOCK",
|
||||
"MKCOL",
|
||||
"MOVE",
|
||||
"PROPFIND",
|
||||
"PROPPATCH",
|
||||
"SEARCH",
|
||||
"UNLOCK",
|
||||
"BIND",
|
||||
"REBIND",
|
||||
"UNBIND",
|
||||
"ACL",
|
||||
|
||||
"REPORT",
|
||||
"MKACTIVITY",
|
||||
"CHECKOUT",
|
||||
"MERGE",
|
||||
|
||||
"M-SEARCH",
|
||||
"NOTIFY",
|
||||
"SUBSCRIBE",
|
||||
"UNSUBSCRIBE",
|
||||
|
||||
"MKCALENDAR",
|
||||
|
||||
"LINK",
|
||||
"UNLINK",
|
||||
|
||||
"SOURCE"};
|
||||
|
||||
|
||||
inline std::string method_name(HTTPMethod method)
|
||||
{
|
||||
if (CROW_LIKELY(method < HTTPMethod::InternalMethodCount))
|
||||
{
|
||||
return method_strings[(unsigned char)method];
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
enum status
|
||||
{
|
||||
CONTINUE = 100,
|
||||
SWITCHING_PROTOCOLS = 101,
|
||||
|
||||
OK = 200,
|
||||
CREATED = 201,
|
||||
ACCEPTED = 202,
|
||||
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
NO_CONTENT = 204,
|
||||
RESET_CONTENT = 205,
|
||||
PARTIAL_CONTENT = 206,
|
||||
|
||||
MULTIPLE_CHOICES = 300,
|
||||
MOVED_PERMANENTLY = 301,
|
||||
FOUND = 302,
|
||||
SEE_OTHER = 303,
|
||||
NOT_MODIFIED = 304,
|
||||
TEMPORARY_REDIRECT = 307,
|
||||
PERMANENT_REDIRECT = 308,
|
||||
|
||||
BAD_REQUEST = 400,
|
||||
UNAUTHORIZED = 401,
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
METHOD_NOT_ALLOWED = 405,
|
||||
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
CONFLICT = 409,
|
||||
GONE = 410,
|
||||
PAYLOAD_TOO_LARGE = 413,
|
||||
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
RANGE_NOT_SATISFIABLE = 416,
|
||||
EXPECTATION_FAILED = 417,
|
||||
PRECONDITION_REQUIRED = 428,
|
||||
TOO_MANY_REQUESTS = 429,
|
||||
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||
|
||||
INTERNAL_SERVER_ERROR = 500,
|
||||
NOT_IMPLEMENTED = 501,
|
||||
BAD_GATEWAY = 502,
|
||||
SERVICE_UNAVAILABLE = 503,
|
||||
GATEWAY_TIMEOUT = 504,
|
||||
VARIANT_ALSO_NEGOTIATES = 506
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum class ParamType : char
|
||||
{
|
||||
INT,
|
||||
UINT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
PATH,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
/// @cond SKIP
|
||||
struct routing_params
|
||||
{
|
||||
std::vector<int64_t> int_params;
|
||||
std::vector<uint64_t> uint_params;
|
||||
std::vector<double> double_params;
|
||||
std::vector<std::string> string_params;
|
||||
|
||||
void debug_print() const
|
||||
{
|
||||
std::cerr << "routing_params" << std::endl;
|
||||
for (auto i : int_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : uint_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : double_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto& i : string_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get(unsigned) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline int64_t routing_params::get<int64_t>(unsigned index) const
|
||||
{
|
||||
return int_params[index];
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t routing_params::get<uint64_t>(unsigned index) const
|
||||
{
|
||||
return uint_params[index];
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double routing_params::get<double>(unsigned index) const
|
||||
{
|
||||
return double_params[index];
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string routing_params::get<std::string>(unsigned index) const
|
||||
{
|
||||
return string_params[index];
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
struct routing_handle_result
|
||||
{
|
||||
uint16_t rule_index;
|
||||
std::vector<uint16_t> blueprint_indices;
|
||||
routing_params r_params;
|
||||
HTTPMethod method;
|
||||
|
||||
routing_handle_result() {}
|
||||
|
||||
routing_handle_result(uint16_t rule_index_, std::vector<uint16_t> blueprint_indices_, routing_params r_params_):
|
||||
rule_index(rule_index_),
|
||||
blueprint_indices(blueprint_indices_),
|
||||
r_params(r_params_) {}
|
||||
|
||||
routing_handle_result(uint16_t rule_index_, std::vector<uint16_t> blueprint_indices_, routing_params r_params_, HTTPMethod method_):
|
||||
rule_index(rule_index_),
|
||||
blueprint_indices(blueprint_indices_),
|
||||
r_params(r_params_),
|
||||
method(method_) {}
|
||||
};
|
||||
} // namespace crow
|
||||
|
||||
// clang-format off
|
||||
#ifndef CROW_MSVC_WORKAROUND
|
||||
constexpr crow::HTTPMethod method_from_string(const char* str)
|
||||
{
|
||||
return crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get :
|
||||
crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete :
|
||||
crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head :
|
||||
crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post :
|
||||
crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options :
|
||||
crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect :
|
||||
crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
|
||||
crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge :
|
||||
crow::black_magic::is_equ_p(str, "COPY", 4) ? crow::HTTPMethod::Copy :
|
||||
crow::black_magic::is_equ_p(str, "LOCK", 4) ? crow::HTTPMethod::Lock :
|
||||
crow::black_magic::is_equ_p(str, "MKCOL", 5) ? crow::HTTPMethod::MkCol :
|
||||
crow::black_magic::is_equ_p(str, "MOVE", 4) ? crow::HTTPMethod::Move :
|
||||
crow::black_magic::is_equ_p(str, "PROPFIND", 8) ? crow::HTTPMethod::Propfind :
|
||||
crow::black_magic::is_equ_p(str, "PROPPATCH", 9) ? crow::HTTPMethod::Proppatch :
|
||||
crow::black_magic::is_equ_p(str, "SEARCH", 6) ? crow::HTTPMethod::Search :
|
||||
crow::black_magic::is_equ_p(str, "UNLOCK", 6) ? crow::HTTPMethod::Unlock :
|
||||
crow::black_magic::is_equ_p(str, "BIND", 4) ? crow::HTTPMethod::Bind :
|
||||
crow::black_magic::is_equ_p(str, "REBIND", 6) ? crow::HTTPMethod::Rebind :
|
||||
crow::black_magic::is_equ_p(str, "UNBIND", 6) ? crow::HTTPMethod::Unbind :
|
||||
crow::black_magic::is_equ_p(str, "ACL", 3) ? crow::HTTPMethod::Acl :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "REPORT", 6) ? crow::HTTPMethod::Report :
|
||||
crow::black_magic::is_equ_p(str, "MKACTIVITY", 10) ? crow::HTTPMethod::MkActivity :
|
||||
crow::black_magic::is_equ_p(str, "CHECKOUT", 8) ? crow::HTTPMethod::Checkout :
|
||||
crow::black_magic::is_equ_p(str, "MERGE", 5) ? crow::HTTPMethod::Merge :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "MSEARCH", 7) ? crow::HTTPMethod::MSearch :
|
||||
crow::black_magic::is_equ_p(str, "NOTIFY", 6) ? crow::HTTPMethod::Notify :
|
||||
crow::black_magic::is_equ_p(str, "SUBSCRIBE", 9) ? crow::HTTPMethod::Subscribe :
|
||||
crow::black_magic::is_equ_p(str, "UNSUBSCRIBE", 11) ? crow::HTTPMethod::Unsubscribe :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "MKCALENDAR", 10) ? crow::HTTPMethod::MkCalendar :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "LINK", 4) ? crow::HTTPMethod::Link :
|
||||
crow::black_magic::is_equ_p(str, "UNLINK", 6) ? crow::HTTPMethod::Unlink :
|
||||
|
||||
crow::black_magic::is_equ_p(str, "SOURCE", 6) ? crow::HTTPMethod::Source :
|
||||
throw std::runtime_error("invalid http method");
|
||||
}
|
||||
|
||||
constexpr crow::HTTPMethod operator"" _method(const char* str, size_t /*len*/)
|
||||
{
|
||||
return method_from_string( str );
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
|
@ -0,0 +1,99 @@
|
|||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <zlib.h>
|
||||
|
||||
// http://zlib.net/manual.html
|
||||
namespace crow
|
||||
{
|
||||
namespace compression
|
||||
{
|
||||
// Values used in the 'windowBits' parameter for deflateInit2.
|
||||
enum algorithm
|
||||
{
|
||||
// 15 is the default value for deflate
|
||||
DEFLATE = 15,
|
||||
// windowBits can also be greater than 15 for optional gzip encoding.
|
||||
// Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
|
||||
GZIP = 15 | 16,
|
||||
};
|
||||
|
||||
inline std::string compress_string(std::string const& str, algorithm algo)
|
||||
{
|
||||
std::string compressed_str;
|
||||
z_stream stream{};
|
||||
// Initialize with the default values
|
||||
if (::deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, algo, 8, Z_DEFAULT_STRATEGY) == Z_OK)
|
||||
{
|
||||
char buffer[8192];
|
||||
|
||||
stream.avail_in = str.size();
|
||||
// zlib does not take a const pointer. The data is not altered.
|
||||
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(str.c_str()));
|
||||
|
||||
int code = Z_OK;
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
|
||||
|
||||
code = ::deflate(&stream, Z_FINISH);
|
||||
// Successful and non-fatal error code returned by deflate when used with Z_FINISH flush
|
||||
if (code == Z_OK || code == Z_STREAM_END)
|
||||
{
|
||||
std::copy(&buffer[0], &buffer[sizeof(buffer) - stream.avail_out], std::back_inserter(compressed_str));
|
||||
}
|
||||
|
||||
} while (code == Z_OK);
|
||||
|
||||
if (code != Z_STREAM_END)
|
||||
compressed_str.clear();
|
||||
|
||||
::deflateEnd(&stream);
|
||||
}
|
||||
|
||||
return compressed_str;
|
||||
}
|
||||
|
||||
inline std::string decompress_string(std::string const& deflated_string)
|
||||
{
|
||||
std::string inflated_string;
|
||||
Bytef tmp[8192];
|
||||
|
||||
z_stream zstream{};
|
||||
zstream.avail_in = deflated_string.size();
|
||||
// Nasty const_cast but zlib won't alter its contents
|
||||
zstream.next_in = const_cast<Bytef*>(reinterpret_cast<Bytef const*>(deflated_string.c_str()));
|
||||
// Initialize with automatic header detection, for gzip support
|
||||
if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK)
|
||||
{
|
||||
do
|
||||
{
|
||||
zstream.avail_out = sizeof(tmp);
|
||||
zstream.next_out = &tmp[0];
|
||||
|
||||
auto ret = ::inflate(&zstream, Z_NO_FLUSH);
|
||||
if (ret == Z_OK || ret == Z_STREAM_END)
|
||||
{
|
||||
std::copy(&tmp[0], &tmp[sizeof(tmp) - zstream.avail_out], std::back_inserter(inflated_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Something went wrong with inflate; make sure we return an empty string
|
||||
inflated_string.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
} while (zstream.avail_out == 0);
|
||||
|
||||
// Free zlib's internal memory
|
||||
::inflateEnd(&zstream);
|
||||
}
|
||||
|
||||
return inflated_string;
|
||||
}
|
||||
} // namespace compression
|
||||
} // namespace crow
|
||||
|
||||
#endif
|
|
@ -0,0 +1,629 @@
|
|||
#pragma once
|
||||
#ifndef ASIO_STANDALONE
|
||||
#define ASIO_STANDALONE
|
||||
#endif
|
||||
#include <asio.hpp>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "crow/http_parser_merged.h"
|
||||
#include "crow/common.h"
|
||||
#include "crow/parser.h"
|
||||
#include "crow/http_response.h"
|
||||
#include "crow/logging.h"
|
||||
#include "crow/settings.h"
|
||||
#include "crow/task_timer.h"
|
||||
#include "crow/middleware_context.h"
|
||||
#include "crow/middleware.h"
|
||||
#include "crow/socket_adaptors.h"
|
||||
#include "crow/compression.h"
|
||||
#include "crow/utility.h"
|
||||
|
||||
namespace crow
|
||||
{
|
||||
using tcp = asio::ip::tcp;
|
||||
|
||||
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
static std::atomic<int> connectionCount;
|
||||
#endif
|
||||
|
||||
/// An HTTP connection.
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
class Connection: public std::enable_shared_from_this<Connection<Adaptor, Handler, Middlewares...>>
|
||||
{
|
||||
friend struct crow::response;
|
||||
|
||||
public:
|
||||
Connection(
|
||||
asio::io_service& io_service,
|
||||
Handler* handler,
|
||||
const std::string& server_name,
|
||||
std::tuple<Middlewares...>* middlewares,
|
||||
std::function<std::string()>& get_cached_date_str_f,
|
||||
detail::task_timer& task_timer,
|
||||
typename Adaptor::context* adaptor_ctx_,
|
||||
std::atomic<unsigned int>& queue_length):
|
||||
adaptor_(io_service, adaptor_ctx_),
|
||||
handler_(handler),
|
||||
parser_(this),
|
||||
req_(parser_.req),
|
||||
server_name_(server_name),
|
||||
middlewares_(middlewares),
|
||||
get_cached_date_str(get_cached_date_str_f),
|
||||
task_timer_(task_timer),
|
||||
res_stream_threshold_(handler->stream_threshold()),
|
||||
queue_length_(queue_length)
|
||||
{
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
connectionCount++;
|
||||
CROW_LOG_DEBUG << "Connection (" << this << ") allocated, total: " << connectionCount;
|
||||
#endif
|
||||
}
|
||||
|
||||
~Connection()
|
||||
{
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
connectionCount--;
|
||||
CROW_LOG_DEBUG << "Connection (" << this << ") freed, total: " << connectionCount;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The TCP socket on top of which the connection is established.
|
||||
decltype(std::declval<Adaptor>().raw_socket())& socket()
|
||||
{
|
||||
return adaptor_.raw_socket();
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
auto self = this->shared_from_this();
|
||||
adaptor_.start([self](const asio::error_code& ec) {
|
||||
if (!ec)
|
||||
{
|
||||
self->start_deadline();
|
||||
self->parser_.clear();
|
||||
|
||||
self->do_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_ERROR << "Could not start adaptor: " << ec.message();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handle_url()
|
||||
{
|
||||
routing_handle_result_ = handler_->handle_initial(req_, res);
|
||||
// if no route is found for the request method, return the response without parsing or processing anything further.
|
||||
if (!routing_handle_result_->rule_index)
|
||||
{
|
||||
parser_.done();
|
||||
complete_request();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_header()
|
||||
{
|
||||
// HTTP 1.1 Expect: 100-continue
|
||||
if (req_.http_ver_major == 1 && req_.http_ver_minor == 1 && get_header_value(req_.headers, "expect") == "100-continue")
|
||||
{
|
||||
buffers_.clear();
|
||||
static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
buffers_.emplace_back(expect_100_continue.data(), expect_100_continue.size());
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
void handle()
|
||||
{
|
||||
// TODO(EDev): cancel_deadline_timer should be looked into, it might be a good idea to add it to handle_url() and then restart the timer once everything passes
|
||||
cancel_deadline_timer();
|
||||
bool is_invalid_request = false;
|
||||
add_keep_alive_ = false;
|
||||
|
||||
req_.remote_ip_address = adaptor_.remote_endpoint().address().to_string();
|
||||
|
||||
add_keep_alive_ = req_.keep_alive;
|
||||
close_connection_ = req_.close_connection;
|
||||
|
||||
if (req_.check_version(1, 1)) // HTTP/1.1
|
||||
{
|
||||
if (!req_.headers.count("host"))
|
||||
{
|
||||
is_invalid_request = true;
|
||||
res = response(400);
|
||||
}
|
||||
else if (req_.upgrade)
|
||||
{
|
||||
// h2 or h2c headers
|
||||
if (req_.get_header_value("upgrade").substr(0, 2) == "h2")
|
||||
{
|
||||
// TODO(ipkn): HTTP/2
|
||||
// currently, ignore upgrade header
|
||||
}
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
handler_->handle_upgrade(req_, res, std::move(adaptor_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CROW_LOG_INFO << "Request: " << utility::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req_.http_ver_major + '0') << "." << (char)(req_.http_ver_minor + '0') << ' ' << method_name(req_.method) << " " << req_.url;
|
||||
|
||||
|
||||
need_to_call_after_handlers_ = false;
|
||||
if (!is_invalid_request)
|
||||
{
|
||||
res.complete_request_handler_ = nullptr;
|
||||
auto self = this->shared_from_this();
|
||||
res.is_alive_helper_ = [self]() -> bool {
|
||||
return self->adaptor_.is_open();
|
||||
};
|
||||
|
||||
ctx_ = detail::context<Middlewares...>();
|
||||
req_.middleware_context = static_cast<void*>(&ctx_);
|
||||
req_.middleware_container = static_cast<void*>(middlewares_);
|
||||
req_.io_service = &adaptor_.get_io_service();
|
||||
|
||||
detail::middleware_call_helper<detail::middleware_call_criteria_only_global,
|
||||
0, decltype(ctx_), decltype(*middlewares_)>({}, *middlewares_, req_, res, ctx_);
|
||||
|
||||
if (!res.completed_)
|
||||
{
|
||||
auto self = this->shared_from_this();
|
||||
res.complete_request_handler_ = [self] {
|
||||
self->complete_request();
|
||||
};
|
||||
need_to_call_after_handlers_ = true;
|
||||
handler_->handle(req_, res, routing_handle_result_);
|
||||
if (add_keep_alive_)
|
||||
res.set_header("connection", "Keep-Alive");
|
||||
}
|
||||
else
|
||||
{
|
||||
complete_request();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
complete_request();
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the after handle middleware and send the write the response to the connection.
|
||||
void complete_request()
|
||||
{
|
||||
CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_;
|
||||
res.is_alive_helper_ = nullptr;
|
||||
|
||||
if (need_to_call_after_handlers_)
|
||||
{
|
||||
need_to_call_after_handlers_ = false;
|
||||
|
||||
// call all after_handler of middlewares
|
||||
detail::after_handlers_call_helper<
|
||||
detail::middleware_call_criteria_only_global,
|
||||
(static_cast<int>(sizeof...(Middlewares)) - 1),
|
||||
decltype(ctx_),
|
||||
decltype(*middlewares_)>({}, *middlewares_, ctx_, req_, res);
|
||||
}
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
if (handler_->compression_used())
|
||||
{
|
||||
std::string accept_encoding = req_.get_header_value("Accept-Encoding");
|
||||
if (!accept_encoding.empty() && res.compressed)
|
||||
{
|
||||
switch (handler_->compression_algorithm())
|
||||
{
|
||||
case compression::DEFLATE:
|
||||
if (accept_encoding.find("deflate") != std::string::npos)
|
||||
{
|
||||
res.body = compression::compress_string(res.body, compression::algorithm::DEFLATE);
|
||||
res.set_header("Content-Encoding", "deflate");
|
||||
}
|
||||
break;
|
||||
case compression::GZIP:
|
||||
if (accept_encoding.find("gzip") != std::string::npos)
|
||||
{
|
||||
res.body = compression::compress_string(res.body, compression::algorithm::GZIP);
|
||||
res.set_header("Content-Encoding", "gzip");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//if there is a redirection with a partial URL, treat the URL as a route.
|
||||
std::string location = res.get_header_value("Location");
|
||||
if (!location.empty() && location.find("://", 0) == std::string::npos)
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (handler_->ssl_used())
|
||||
location.insert(0, "https://" + req_.get_header_value("Host"));
|
||||
else
|
||||
#endif
|
||||
location.insert(0, "http://" + req_.get_header_value("Host"));
|
||||
res.set_header("location", location);
|
||||
}
|
||||
|
||||
prepare_buffers();
|
||||
|
||||
if (res.is_static_type())
|
||||
{
|
||||
do_write_static();
|
||||
}
|
||||
else
|
||||
{
|
||||
do_write_general();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void prepare_buffers()
|
||||
{
|
||||
res.complete_request_handler_ = nullptr;
|
||||
res.is_alive_helper_ = nullptr;
|
||||
|
||||
if (!adaptor_.is_open())
|
||||
{
|
||||
//CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing;
|
||||
//delete this;
|
||||
return;
|
||||
}
|
||||
// TODO(EDev): HTTP version in status codes should be dynamic
|
||||
// Keep in sync with common.h/status
|
||||
static std::unordered_map<int, std::string> statusCodes = {
|
||||
{status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
|
||||
{status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
|
||||
|
||||
{status::OK, "HTTP/1.1 200 OK\r\n"},
|
||||
{status::CREATED, "HTTP/1.1 201 Created\r\n"},
|
||||
{status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
|
||||
{status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
|
||||
{status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
|
||||
{status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
|
||||
{status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
|
||||
|
||||
{status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
|
||||
{status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
|
||||
{status::FOUND, "HTTP/1.1 302 Found\r\n"},
|
||||
{status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
|
||||
{status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
|
||||
{status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
|
||||
{status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
|
||||
|
||||
{status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
|
||||
{status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
|
||||
{status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
|
||||
{status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
|
||||
{status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
|
||||
{status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
|
||||
{status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
|
||||
{status::GONE, "HTTP/1.1 410 Gone\r\n"},
|
||||
{status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
|
||||
{status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
|
||||
{status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
|
||||
{status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
|
||||
{status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
|
||||
{status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
|
||||
{status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
|
||||
|
||||
{status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
|
||||
{status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
|
||||
{status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
|
||||
{status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
|
||||
{status::GATEWAY_TIMEOUT, "HTTP/1.1 504 Gateway Timeout\r\n"},
|
||||
{status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
|
||||
};
|
||||
|
||||
static const std::string seperator = ": ";
|
||||
|
||||
buffers_.clear();
|
||||
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
|
||||
|
||||
if (!statusCodes.count(res.code))
|
||||
{
|
||||
CROW_LOG_WARNING << this << " status code "
|
||||
<< "(" << res.code << ")"
|
||||
<< " not defined, returning 500 instead";
|
||||
res.code = 500;
|
||||
}
|
||||
|
||||
auto& status = statusCodes.find(res.code)->second;
|
||||
buffers_.emplace_back(status.data(), status.size());
|
||||
|
||||
if (res.code >= 400 && res.body.empty())
|
||||
res.body = statusCodes[res.code].substr(9);
|
||||
|
||||
for (auto& kv : res.headers)
|
||||
{
|
||||
buffers_.emplace_back(kv.first.data(), kv.first.size());
|
||||
buffers_.emplace_back(seperator.data(), seperator.size());
|
||||
buffers_.emplace_back(kv.second.data(), kv.second.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
|
||||
if (!res.manual_length_header && !res.headers.count("content-length"))
|
||||
{
|
||||
content_length_ = std::to_string(res.body.size());
|
||||
static std::string content_length_tag = "Content-Length: ";
|
||||
buffers_.emplace_back(content_length_tag.data(), content_length_tag.size());
|
||||
buffers_.emplace_back(content_length_.data(), content_length_.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
if (!res.headers.count("server"))
|
||||
{
|
||||
static std::string server_tag = "Server: ";
|
||||
buffers_.emplace_back(server_tag.data(), server_tag.size());
|
||||
buffers_.emplace_back(server_name_.data(), server_name_.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
if (!res.headers.count("date"))
|
||||
{
|
||||
static std::string date_tag = "Date: ";
|
||||
date_str_ = get_cached_date_str();
|
||||
buffers_.emplace_back(date_tag.data(), date_tag.size());
|
||||
buffers_.emplace_back(date_str_.data(), date_str_.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
if (add_keep_alive_)
|
||||
{
|
||||
static std::string keep_alive_tag = "Connection: Keep-Alive";
|
||||
buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
}
|
||||
|
||||
void do_write_static()
|
||||
{
|
||||
asio::write(adaptor_.socket(), buffers_);
|
||||
|
||||
if (res.file_info.statResult == 0 && res.file_info.reader == nullptr)
|
||||
{
|
||||
std::ifstream is(res.file_info.path.c_str(), std::ios::in | std::ios::binary);
|
||||
std::vector<asio::const_buffer> buffers{1};
|
||||
char buf[16384];
|
||||
is.read(buf, sizeof(buf));
|
||||
while (is.gcount() > 0)
|
||||
{
|
||||
buffers[0] = asio::buffer(buf, is.gcount());
|
||||
do_write_sync(buffers);
|
||||
is.read(buf, sizeof(buf));
|
||||
}
|
||||
} else if (res.file_info.reader != nullptr)
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers{1};
|
||||
char buf[16384];
|
||||
res.file_info.reader->read(buf, sizeof(buf));
|
||||
while (res.file_info.reader->gcount() > 0)
|
||||
{
|
||||
buffers[0] = asio::buffer(buf, res.file_info.reader->gcount());
|
||||
do_write_sync(buffers);
|
||||
res.file_info.reader->read(buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
if (close_connection_)
|
||||
{
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
CROW_LOG_DEBUG << this << " from write (static)";
|
||||
}
|
||||
|
||||
res.end();
|
||||
res.clear();
|
||||
buffers_.clear();
|
||||
parser_.clear();
|
||||
}
|
||||
|
||||
void do_write_general()
|
||||
{
|
||||
if (res.body.length() < res_stream_threshold_)
|
||||
{
|
||||
res_body_copy_.swap(res.body);
|
||||
buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size());
|
||||
|
||||
do_write();
|
||||
|
||||
if (need_to_start_read_after_complete_)
|
||||
{
|
||||
need_to_start_read_after_complete_ = false;
|
||||
start_deadline();
|
||||
do_read();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::write(adaptor_.socket(), buffers_); // Write the response start / headers
|
||||
cancel_deadline_timer();
|
||||
if (res.body.length() > 0)
|
||||
{
|
||||
std::string buf;
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
|
||||
while (res.body.length() > 16384)
|
||||
{
|
||||
//buf.reserve(16385);
|
||||
buf = res.body.substr(0, 16384);
|
||||
res.body = res.body.substr(16384);
|
||||
buffers.clear();
|
||||
buffers.push_back(asio::buffer(buf));
|
||||
do_write_sync(buffers);
|
||||
}
|
||||
// Collect whatever is left (less than 16KB) and send it down the socket
|
||||
// buf.reserve(is.length());
|
||||
buf = res.body;
|
||||
res.body.clear();
|
||||
|
||||
buffers.clear();
|
||||
buffers.push_back(asio::buffer(buf));
|
||||
do_write_sync(buffers);
|
||||
}
|
||||
if (close_connection_)
|
||||
{
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
CROW_LOG_DEBUG << this << " from write (res_stream)";
|
||||
}
|
||||
|
||||
res.end();
|
||||
res.clear();
|
||||
buffers_.clear();
|
||||
parser_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void do_read()
|
||||
{
|
||||
auto self = this->shared_from_this();
|
||||
adaptor_.socket().async_read_some(
|
||||
asio::buffer(buffer_),
|
||||
[self](const asio::error_code& ec, std::size_t bytes_transferred) {
|
||||
bool error_while_reading = true;
|
||||
if (!ec)
|
||||
{
|
||||
bool ret = self->parser_.feed(self->buffer_.data(), bytes_transferred);
|
||||
if (ret && self->adaptor_.is_open())
|
||||
{
|
||||
error_while_reading = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_while_reading)
|
||||
{
|
||||
self->cancel_deadline_timer();
|
||||
self->parser_.done();
|
||||
self->adaptor_.shutdown_read();
|
||||
self->adaptor_.close();
|
||||
CROW_LOG_DEBUG << self << " from read(1) with description: \"" << http_errno_description(static_cast<http_errno>(self->parser_.http_errno)) << '\"';
|
||||
}
|
||||
else if (self->close_connection_)
|
||||
{
|
||||
self->cancel_deadline_timer();
|
||||
self->parser_.done();
|
||||
// adaptor will close after write
|
||||
}
|
||||
else if (!self->need_to_call_after_handlers_)
|
||||
{
|
||||
self->start_deadline();
|
||||
self->do_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// res will be completed later by user
|
||||
self->need_to_start_read_after_complete_ = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
auto self = this->shared_from_this();
|
||||
asio::async_write(
|
||||
adaptor_.socket(), buffers_,
|
||||
[self](const asio::error_code& ec, std::size_t /*bytes_transferred*/) {
|
||||
self->res.clear();
|
||||
self->res_body_copy_.clear();
|
||||
self->parser_.clear();
|
||||
if (!ec)
|
||||
{
|
||||
if (self->close_connection_)
|
||||
{
|
||||
self->adaptor_.shutdown_write();
|
||||
self->adaptor_.close();
|
||||
CROW_LOG_DEBUG << self << " from write(1)";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_DEBUG << self << " from write(2)";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
inline void do_write_sync(std::vector<asio::const_buffer>& buffers)
|
||||
{
|
||||
|
||||
asio::write(adaptor_.socket(), buffers, [&](asio::error_code ec, std::size_t) {
|
||||
if (!ec)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_ERROR << ec << " - happened while sending buffers";
|
||||
CROW_LOG_DEBUG << this << " from write (sync)(2)";
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void cancel_deadline_timer()
|
||||
{
|
||||
CROW_LOG_DEBUG << this << " timer cancelled: " << &task_timer_ << ' ' << task_id_;
|
||||
task_timer_.cancel(task_id_);
|
||||
}
|
||||
|
||||
void start_deadline(/*int timeout = 5*/)
|
||||
{
|
||||
cancel_deadline_timer();
|
||||
|
||||
auto self = this->shared_from_this();
|
||||
task_id_ = task_timer_.schedule([self] {
|
||||
if (!self->adaptor_.is_open())
|
||||
{
|
||||
return;
|
||||
}
|
||||
self->adaptor_.shutdown_readwrite();
|
||||
self->adaptor_.close();
|
||||
});
|
||||
CROW_LOG_DEBUG << this << " timer added: " << &task_timer_ << ' ' << task_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
Adaptor adaptor_;
|
||||
Handler* handler_;
|
||||
|
||||
std::array<char, 4096> buffer_;
|
||||
|
||||
HTTPParser<Connection> parser_;
|
||||
std::unique_ptr<routing_handle_result> routing_handle_result_;
|
||||
request& req_;
|
||||
response res;
|
||||
|
||||
bool close_connection_ = false;
|
||||
|
||||
const std::string& server_name_;
|
||||
std::vector<asio::const_buffer> buffers_;
|
||||
|
||||
std::string content_length_;
|
||||
std::string date_str_;
|
||||
std::string res_body_copy_;
|
||||
|
||||
detail::task_timer::identifier_type task_id_{};
|
||||
|
||||
bool need_to_call_after_handlers_{};
|
||||
bool need_to_start_read_after_complete_{};
|
||||
bool add_keep_alive_{};
|
||||
|
||||
std::tuple<Middlewares...>* middlewares_;
|
||||
detail::context<Middlewares...> ctx_;
|
||||
|
||||
std::function<std::string()>& get_cached_date_str;
|
||||
detail::task_timer& task_timer_;
|
||||
|
||||
size_t res_stream_threshold_;
|
||||
|
||||
std::atomic<unsigned int>& queue_length_;
|
||||
};
|
||||
|
||||
} // namespace crow
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef ASIO_STANDALONE
|
||||
#define ASIO_STANDALONE
|
||||
#endif
|
||||
#include <asio.hpp>
|
||||
|
||||
#include "crow/common.h"
|
||||
#include "crow/ci_map.h"
|
||||
#include "crow/query_string.h"
|
||||
|
||||
namespace crow
|
||||
{
|
||||
/// Find and return the value associated with the key. (returns an empty string if nothing is found)
|
||||
template<typename T>
|
||||
inline const std::string& get_header_value(const T& headers, const std::string& key)
|
||||
{
|
||||
if (headers.count(key))
|
||||
{
|
||||
return headers.find(key)->second;
|
||||
}
|
||||
static std::string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
/// An HTTP request.
|
||||
struct request
|
||||
{
|
||||
HTTPMethod method;
|
||||
std::string raw_url; ///< The full URL containing the `?` and URL parameters.
|
||||
std::string url; ///< The endpoint without any parameters.
|
||||
query_string url_params; ///< The parameters associated with the request. (everything after the `?` in the URL)
|
||||
ci_map headers;
|
||||
std::string body;
|
||||
std::string remote_ip_address; ///< The IP address from which the request was sent.
|
||||
unsigned char http_ver_major, http_ver_minor;
|
||||
bool keep_alive, ///< Whether or not the server should send a `connection: Keep-Alive` header to the client.
|
||||
close_connection, ///< Whether or not the server should shut down the TCP connection once a response is sent.
|
||||
upgrade; ///< Whether or noth the server should change the HTTP connection to a different connection.
|
||||
|
||||
void* middleware_context{};
|
||||
void* middleware_container{};
|
||||
asio::io_service* io_service{};
|
||||
|
||||
/// Construct an empty request. (sets the method to `GET`)
|
||||
request():
|
||||
method(HTTPMethod::Get)
|
||||
{}
|
||||
|
||||
/// Construct a request with all values assigned.
|
||||
request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body, unsigned char http_major, unsigned char http_minor, bool has_keep_alive, bool has_close_connection, bool is_upgrade):
|
||||
method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)), http_ver_major(http_major), http_ver_minor(http_minor), keep_alive(has_keep_alive), close_connection(has_close_connection), upgrade(is_upgrade)
|
||||
{}
|
||||
|
||||
void add_header(std::string key, std::string value)
|
||||
{
|
||||
headers.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
const std::string& get_header_value(const std::string& key) const
|
||||
{
|
||||
return crow::get_header_value(headers, key);
|
||||
}
|
||||
|
||||
bool check_version(unsigned char major, unsigned char minor) const
|
||||
{
|
||||
return http_ver_major == major && http_ver_minor == minor;
|
||||
}
|
||||
|
||||
/// Get the body as parameters in QS format.
|
||||
|
||||
///
|
||||
/// This is meant to be used with requests of type "application/x-www-form-urlencoded"
|
||||
const query_string get_body_params() const
|
||||
{
|
||||
return query_string(body, false);
|
||||
}
|
||||
|
||||
/// Send data to whoever made this request with a completion handler and return immediately.
|
||||
template<typename CompletionHandler>
|
||||
void post(CompletionHandler handler)
|
||||
{
|
||||
io_service->post(handler);
|
||||
}
|
||||
|
||||
/// Send data to whoever made this request with a completion handler.
|
||||
template<typename CompletionHandler>
|
||||
void dispatch(CompletionHandler handler)
|
||||
{
|
||||
io_service->dispatch(handler);
|
||||
}
|
||||
};
|
||||
} // namespace crow
|