forked from mirror/openmw-tes3mp
Compare commits
314 Commits
0.7.0
...
sol2-serve
Author | SHA1 | Date |
---|---|---|
David Cernat | 9029533ec1 | 6 years ago |
Testman | 7fadc533c7 | 6 years ago |
David Cernat | cf33695447 | 7 years ago |
David Cernat | dbd6ccaf97 | 7 years ago |
Testman | 702d5b44c9 | 7 years ago |
David Cernat | 8b482e19ec | 7 years ago |
David Cernat | d5b3d08ec9 | 7 years ago |
David Cernat | 524363702c | 7 years ago |
David Cernat | b3c398605f | 7 years ago |
David Cernat | 176aa62b15 | 7 years ago |
David Cernat | 6bb2a7e24a | 7 years ago |
David Cernat | 058d67dbd0 | 7 years ago |
David Cernat | 49bf605e8e | 7 years ago |
David Cernat | 07b781cd32 | 7 years ago |
Stanislav Zhukov | 08915c44a0 | 7 years ago |
Kyle Cooley | e12330b038 | 7 years ago |
David Cernat | 5c41350532 | 7 years ago |
David Cernat | fc4d3fe3fa | 7 years ago |
David Cernat | 569243c987 | 7 years ago |
Koncord | 7180b009bd | 7 years ago |
David Cernat | 3f7e163c92 | 7 years ago |
David Cernat | 7083cb0359 | 7 years ago |
David Cernat | ad4214d3e2 | 7 years ago |
Kyle Cooley | 83fe29d10f | 7 years ago |
Kyle Cooley | 3a68f4bd8f | 7 years ago |
David Cernat | 8a393d2984 | 7 years ago |
David Cernat | b5922d18fd | 7 years ago |
David Cernat | 98eece808b | 7 years ago |
David Cernat | d4dbfdfdb6 | 7 years ago |
David Cernat | 672bb707a7 | 7 years ago |
David Cernat | 7c8dd7380f | 7 years ago |
David Cernat | c5f33e451f | 7 years ago |
David Cernat | 4846497078 | 7 years ago |
David Cernat | 502751cae0 | 7 years ago |
David Cernat | d58efde3f1 | 7 years ago |
David Cernat | e355dca4dd | 7 years ago |
David Cernat | ed15d9ebf5 | 7 years ago |
David Cernat | 2390744b45 | 7 years ago |
David Cernat | c3b44e11fb | 7 years ago |
David Cernat | f50637bdd4 | 7 years ago |
Koncord | 6131ada0ba | 7 years ago |
David Cernat | 5bb09d3bed | 7 years ago |
Koncord | 2aaf9105af | 7 years ago |
David Cernat | 3d5860d6f4 | 7 years ago |
David Cernat | 7ec08e125b | 7 years ago |
David Cernat | b2a3dd9d60 | 7 years ago |
Koncord | 2ac01dc02a | 7 years ago |
Koncord | 4aff1f1833 | 7 years ago |
Koncord | 017956366f | 7 years ago |
Koncord | afbafdf806 | 7 years ago |
Koncord | d0eef7c98e | 7 years ago |
Koncord | 7deff7a42a | 7 years ago |
Koncord | 23da0b16ea | 7 years ago |
Koncord | 6f7771d97e | 7 years ago |
Koncord | 24ba4ae404 | 7 years ago |
Koncord | 73d030b779 | 7 years ago |
Koncord | 4e869a2974 | 7 years ago |
Koncord | e85d0db771 | 7 years ago |
Koncord | 44c549211e | 7 years ago |
Koncord | 2bfd4627ed | 7 years ago |
Koncord | 9dae748a76 | 7 years ago |
Koncord | bb7c5ee34c | 7 years ago |
Koncord | 54945b537d | 7 years ago |
Koncord | 4bde7d80f5 | 7 years ago |
Koncord | f2a88e6a37 | 7 years ago |
Koncord | 410eb353e8 | 7 years ago |
Koncord | a9614ad28e | 7 years ago |
Koncord | 69436714f9 | 7 years ago |
David Cernat | 3b865244d0 | 7 years ago |
David Cernat | 4e9cac96c7 | 7 years ago |
David Cernat | ac374a8ef9 | 7 years ago |
David Cernat | bdafa8e9ab | 7 years ago |
David Cernat | f1ba9253b0 | 7 years ago |
David Cernat | 5858e05362 | 7 years ago |
David Cernat | 1a8a518897 | 7 years ago |
David Cernat | 569911121d | 7 years ago |
David Cernat | 5cf71a4e67 | 7 years ago |
David Cernat | d70b93e095 | 7 years ago |
Koncord | 6197636fac | 7 years ago |
Koncord | edd883853d | 7 years ago |
Koncord | 0e97b769f9 | 7 years ago |
Koncord | 4cfb04aa7f | 7 years ago |
Koncord | 7e5b929ea2 | 7 years ago |
Koncord | 29ba07fe8c | 7 years ago |
Koncord | de0bb3cdab | 7 years ago |
Koncord | cb86557aca | 7 years ago |
Koncord | 0780329a59 | 7 years ago |
Koncord | 1f1cbf53f9 | 7 years ago |
Koncord | b63bf258ff | 7 years ago |
Koncord | c2578918f2 | 7 years ago |
Koncord | e2e197d84a | 7 years ago |
Koncord | f1e2bc01f6 | 7 years ago |
Koncord | 1e25a122c9 | 7 years ago |
David Cernat | a037193e79 | 7 years ago |
Koncord | 05fac2f67d | 7 years ago |
Koncord | 043eb224e2 | 7 years ago |
Koncord | 35b771b19e | 7 years ago |
David Cernat | 92060bd6b6 | 7 years ago |
Koncord | 1de9f30449 | 7 years ago |
David Cernat | 45d3b24c17 | 7 years ago |
Kyle Cooley | 7248a5d037 | 7 years ago |
Kyle Cooley | ba4d2bd5fe | 7 years ago |
David Cernat | b6a7377692 | 7 years ago |
Kyle Cooley | fcd4d8b842 | 7 years ago |
Kyle Cooley | e2103d0bea | 7 years ago |
Koncord | 35922e4898 | 7 years ago |
Koncord | ef0384b296 | 7 years ago |
Koncord | 5b8f4f3e92 | 7 years ago |
Koncord | 647661daf9 | 7 years ago |
David Cernat | 2f6e3b4cda | 7 years ago |
Koncord | c4949ac5d9 | 7 years ago |
Koncord | 8f5d31cb03 | 7 years ago |
Koncord | 4ab338bbb1 | 7 years ago |
Koncord | 5777759aae | 7 years ago |
Koncord | 2019128d92 | 7 years ago |
David Cernat | 51a92bcf8f | 7 years ago |
David Cernat | 09958681cd | 7 years ago |
David Cernat | 8bb764c6d6 | 7 years ago |
David Cernat | dddd2f1cc7 | 7 years ago |
David Cernat | a84c4c7ecc | 7 years ago |
David Cernat | f2eca2566f | 7 years ago |
Koncord | 077a3d06b3 | 7 years ago |
Koncord | f9c4b847aa | 7 years ago |
Koncord | c5388e49f2 | 7 years ago |
Koncord | ba07d7820f | 7 years ago |
Koncord | 77d14211c9 | 7 years ago |
Koncord | ecbe0127b0 | 7 years ago |
Koncord | fd721143e2 | 7 years ago |
Koncord | 25b7095396 | 7 years ago |
Koncord | 44dc153ebe | 7 years ago |
Koncord | 1ef6ad6215 | 7 years ago |
Koncord | aff1859759 | 7 years ago |
Koncord | 122a30c183 | 7 years ago |
Koncord | 2726d94d10 | 7 years ago |
Koncord | d98bb74b80 | 7 years ago |
Koncord | bfdf348a6c | 7 years ago |
Koncord | f11473da87 | 7 years ago |
Koncord | 4d0072a74c | 7 years ago |
Koncord | cd620e17ec | 7 years ago |
Koncord | dd352f0a91 | 7 years ago |
Koncord | f35d35741e | 7 years ago |
Koncord | 585c24cee8 | 7 years ago |
Koncord | ff8b5061b4 | 7 years ago |
Koncord | e97dac7793 | 7 years ago |
Koncord | 7748e582a8 | 7 years ago |
Koncord | 2cb0ea20f0 | 7 years ago |
David Cernat | de77ee3126 | 7 years ago |
David Cernat | ad61d88cb1 | 7 years ago |
Koncord | 051f65a4d5 | 7 years ago |
Koncord | 7eecbfd08e | 7 years ago |
Koncord | 1c7330635b | 7 years ago |
Koncord | 392e645fe5 | 7 years ago |
Koncord | 91398c5dcc | 7 years ago |
Koncord | aa183e6844 | 7 years ago |
Koncord | c55f0f73b8 | 7 years ago |
David Cernat | 85a9181c12 | 7 years ago |
Koncord | 94f3eaa980 | 7 years ago |
David Cernat | 456bcee68a | 7 years ago |
David Cernat | cfb5835e17 | 7 years ago |
Koncord | cd03d59056 | 7 years ago |
David Cernat | 2bc79fcdf4 | 7 years ago |
David Cernat | c2f330d4f1 | 7 years ago |
David Cernat | fc5e883160 | 7 years ago |
David Cernat | 993cc3dfd6 | 7 years ago |
David Cernat | d1ad0c91f8 | 7 years ago |
David Cernat | 7e322f1f8b | 7 years ago |
David Cernat | bd9e8bd10f | 7 years ago |
David Cernat | 711bdf187a | 7 years ago |
David Cernat | 720ef5f6c5 | 7 years ago |
David Cernat | eff3504b05 | 7 years ago |
Koncord | b55bb445dd | 7 years ago |
Koncord | a546d99000 | 7 years ago |
Koncord | 1841562553 | 7 years ago |
Koncord | 744b8cf168 | 7 years ago |
Koncord | e44fcdc0b3 | 7 years ago |
David Cernat | 901fe72471 | 7 years ago |
David Cernat | a796f81444 | 7 years ago |
Koncord | 46d55816b8 | 7 years ago |
Koncord | 4ebfcc4a21 | 7 years ago |
Koncord | 729f6e745e | 7 years ago |
Koncord | fef3764eb1 | 7 years ago |
Koncord | 382f56178c | 7 years ago |
Koncord | e657934cef | 7 years ago |
Koncord | c276ff4bd9 | 7 years ago |
David Cernat | 0a35b897be | 7 years ago |
David Cernat | b0965f094a | 7 years ago |
David Cernat | ef79a98544 | 7 years ago |
David Cernat | 64b57983f0 | 7 years ago |
David Cernat | 606ddff813 | 7 years ago |
David Cernat | cac4684986 | 7 years ago |
David Cernat | 07d75abdf8 | 7 years ago |
David Cernat | 1ee460bba8 | 7 years ago |
David Cernat | d33254f287 | 7 years ago |
David Cernat | 010a80ceca | 7 years ago |
David Cernat | 947b3f76be | 7 years ago |
David Cernat | 6f822f54aa | 7 years ago |
David Cernat | a3e2ab4d4e | 7 years ago |
David Cernat | 4cc0216e0a | 7 years ago |
David Cernat | 80be664139 | 7 years ago |
David Cernat | 57a0415ba3 | 7 years ago |
David Cernat | 494b10b97e | 7 years ago |
David Cernat | ba161ddddd | 7 years ago |
David Cernat | 7788821a69 | 7 years ago |
David Cernat | 068f733d1e | 7 years ago |
David Cernat | 1272b03f25 | 7 years ago |
David Cernat | b4e8560698 | 7 years ago |
David Cernat | 926106cf8c | 7 years ago |
David Cernat | ac7a588632 | 7 years ago |
David Cernat | a21f5d18d6 | 7 years ago |
David Cernat | a8261bb385 | 7 years ago |
David Cernat | 700e4d032e | 7 years ago |
David Cernat | 4dbada69bf | 7 years ago |
Koncord | ca7f3f7450 | 7 years ago |
Koncord | 64b531aa3c | 7 years ago |
Koncord | f377164db9 | 7 years ago |
Koncord | 7ab01b66e4 | 7 years ago |
Koncord | d15c674584 | 7 years ago |
Koncord | 0da44f69ad | 7 years ago |
Koncord | 062d6a1824 | 7 years ago |
David Cernat | 29cb51cdce | 7 years ago |
Koncord | 1d111fdca8 | 7 years ago |
Koncord | bd7082f57e | 7 years ago |
David Cernat | 71c921faa7 | 7 years ago |
David Cernat | 5653d07c7b | 7 years ago |
Koncord | 948090676a | 7 years ago |
David Cernat | 61db22f5ae | 7 years ago |
David Cernat | b7e5e77166 | 7 years ago |
David Cernat | 378d30834b | 7 years ago |
Koncord | 14d47213ef | 7 years ago |
Koncord | 3495fd43f4 | 7 years ago |
Koncord | e7a5919477 | 7 years ago |
Koncord | 1aa630e4a9 | 7 years ago |
Koncord | e8915f8ec5 | 7 years ago |
Koncord | 14fdec2478 | 7 years ago |
David Cernat | b801cf2c9e | 7 years ago |
David Cernat | 878294e4fe | 7 years ago |
Koncord | d44848ecbb | 7 years ago |
Koncord | 05abb8ace3 | 7 years ago |
Koncord | 04a844a9c0 | 7 years ago |
Koncord | dad0b38f25 | 7 years ago |
Koncord | a3d5fbbdcd | 7 years ago |
Koncord | 916ada108f | 7 years ago |
David Cernat | bece095579 | 7 years ago |
David Cernat | d6dc75e94b | 7 years ago |
David Cernat | 76a4abd7c0 | 7 years ago |
David Cernat | 0e73571111 | 7 years ago |
David Cernat | 8a93631e08 | 7 years ago |
Koncord | ba8613a179 | 7 years ago |
David Cernat | fb67180809 | 7 years ago |
Koncord | 4530370e52 | 7 years ago |
Koncord | ce6a4e4032 | 7 years ago |
Koncord | fc3f2483ee | 7 years ago |
Koncord | dffd3bfa7d | 7 years ago |
Koncord | 7a0b45d456 | 7 years ago |
Koncord | 66283943c5 | 7 years ago |
Koncord | d702845026 | 7 years ago |
Koncord | 0a0c9893b1 | 7 years ago |
David Cernat | 6e7c033a5d | 7 years ago |
David Cernat | bbac26294f | 7 years ago |
David Cernat | b20df4b7cd | 7 years ago |
David Cernat | 17a8e32782 | 7 years ago |
Koncord | 1fd16ba69c | 7 years ago |
Koncord | 62588ce088 | 7 years ago |
Koncord | 6cb9c3c713 | 7 years ago |
Koncord | 3839a2dcfd | 7 years ago |
Koncord | ed75563a94 | 7 years ago |
Koncord | 01a5196a92 | 7 years ago |
Koncord | 15723adb9a | 7 years ago |
Koncord | 57353cdfff | 7 years ago |
David Cernat | fe9a3088bd | 7 years ago |
Koncord | 5c79e7106f | 7 years ago |
David Cernat | 4845599bda | 7 years ago |
David Cernat | 7a38a0b223 | 7 years ago |
Koncord | cbabc91b06 | 7 years ago |
Koncord | 846f83e3e4 | 7 years ago |
David Cernat | 841a4f90c1 | 7 years ago |
David Cernat | 3284769fef | 7 years ago |
David Cernat | b5ce3cebbc | 7 years ago |
Koncord | 73aa83aa03 | 7 years ago |
Koncord | 5fcdff843c | 7 years ago |
David Cernat | 76f1a61538 | 7 years ago |
David Cernat | d591180e99 | 7 years ago |
David Cernat | 381a5fabc4 | 7 years ago |
David Cernat | 9838cc680a | 7 years ago |
David Cernat | 98195b5e3c | 7 years ago |
Koncord | 3124e627cf | 7 years ago |
Koncord | c2286482ae | 7 years ago |
Koncord | f814ae795d | 7 years ago |
Koncord | 50ef9677fe | 7 years ago |
Koncord | 838e05521e | 7 years ago |
David Cernat | cccbe753d7 | 7 years ago |
Koncord | 6decd148e5 | 7 years ago |
Koncord | 045dc566ea | 7 years ago |
Koncord | bb183457a6 | 7 years ago |
Koncord | dc18916f46 | 7 years ago |
Koncord | f99dafbf51 | 7 years ago |
Koncord | aee6fb1265 | 7 years ago |
Koncord | d1388cdf84 | 7 years ago |
Koncord | b869fe0b76 | 7 years ago |
Koncord | 0e2817da88 | 7 years ago |
Koncord | fc8232f943 | 7 years ago |
Koncord | 26324c2578 | 7 years ago |
Koncord | 4e93905350 | 7 years ago |
Koncord | 510e657c93 | 7 years ago |
Koncord | 060ebe3d4a | 7 years ago |
Koncord | 1c0adc47ee | 7 years ago |
Koncord | 66fdba957b | 7 years ago |
Koncord | 1d16958910 | 7 years ago |
Koncord | b18c6dec9d | 7 years ago |
Koncord | 991a1fe8d8 | 7 years ago |
Koncord | 60fc0bedb8 | 7 years ago |
Koncord | 7717f9bece | 7 years ago |
Koncord | fe2dd1bad4 | 7 years ago |
Koncord | 2d0840cb3a | 7 years ago |
@ -1,69 +0,0 @@
|
|||||||
stages:
|
|
||||||
- build
|
|
||||||
|
|
||||||
Debian:
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
- linux
|
|
||||||
image: gcc
|
|
||||||
cache:
|
|
||||||
key: apt-cache
|
|
||||||
paths:
|
|
||||||
- apt-cache/
|
|
||||||
before_script:
|
|
||||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
|
||||||
- apt-get update -yq
|
|
||||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
|
|
||||||
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
|
||||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
|
|
||||||
- make -j$cores_to_use
|
|
||||||
- DESTDIR=artifacts make install
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build/artifacts/
|
|
||||||
MacOS:
|
|
||||||
tags:
|
|
||||||
- macos
|
|
||||||
- xcode
|
|
||||||
except:
|
|
||||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
|
||||||
stage: build
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- rm -fr build/* # remove anything in the build directory
|
|
||||||
- CI/before_install.osx.sh
|
|
||||||
- CI/before_script.osx.sh
|
|
||||||
- cd build; make -j2 package
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build/OpenMW-*.dmg
|
|
||||||
|
|
||||||
Windows:
|
|
||||||
tags:
|
|
||||||
- win10
|
|
||||||
- msvc2017
|
|
||||||
except:
|
|
||||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
|
||||||
stage: build
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
# - env # turn on for debugging
|
|
||||||
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
|
||||||
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
|
|
||||||
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
|
|
||||||
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- deps
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- "*.zip"
|
|
@ -1,3 +1,6 @@
|
|||||||
[submodule "extern/breakpad"]
|
[submodule "extern/breakpad"]
|
||||||
path = extern/breakpad
|
path = extern/breakpad
|
||||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
url = https://chromium.googlesource.com/breakpad/breakpad
|
||||||
|
[submodule "extern/sol"]
|
||||||
|
path = extern/sol
|
||||||
|
url = https://github.com/ThePhD/sol2
|
||||||
|
@ -0,0 +1,198 @@
|
|||||||
|
//
|
||||||
|
// Created by koncord on 31.03.18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Settings.hpp"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
#include <apps/browser/netutils/QueryClient.hpp>
|
||||||
|
#include "MainWindow.hpp"
|
||||||
|
|
||||||
|
std::string loadSettings (Settings::Manager & settings, const std::string &cfgName)
|
||||||
|
{
|
||||||
|
Files::ConfigurationManager mCfgMgr;
|
||||||
|
// Create the settings manager and load default settings file
|
||||||
|
const std::string localdefault = (mCfgMgr.getLocalPath() / (cfgName + "-default.cfg")).string();
|
||||||
|
const std::string globaldefault = (mCfgMgr.getGlobalPath() / (cfgName + "-default.cfg")).string();
|
||||||
|
|
||||||
|
// prefer local
|
||||||
|
if (boost::filesystem::exists(localdefault))
|
||||||
|
settings.loadDefault(localdefault);
|
||||||
|
else if (boost::filesystem::exists(globaldefault))
|
||||||
|
settings.loadDefault(globaldefault);
|
||||||
|
else
|
||||||
|
throw std::runtime_error ("No default settings file found! Make sure the file \"" + cfgName + "-default.cfg\" was properly installed.");
|
||||||
|
|
||||||
|
// load user settings if they exist
|
||||||
|
const std::string settingspath = (mCfgMgr.getUserConfigPath() / (cfgName + ".cfg")).string();
|
||||||
|
if (boost::filesystem::exists(settingspath))
|
||||||
|
settings.loadUser(settingspath);
|
||||||
|
|
||||||
|
return settingspath;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsMgr::SettingsMgr()
|
||||||
|
{
|
||||||
|
clientCfg = loadSettings(clientMgr, "tes3mp-client");
|
||||||
|
switchMgr();
|
||||||
|
serverCfg = loadSettings(serverMgr, "tes3mp-server");
|
||||||
|
switchMgr();
|
||||||
|
|
||||||
|
std::string addr = clientMgr.getString("address", "Master");
|
||||||
|
int port = clientMgr.getInt("port", "Master");
|
||||||
|
QueryClient::Get().SetServer(addr, port);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::loadBrowserSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
mw.comboLatency->setCurrentIndex(clientMgr.getInt("maxLatency", "Browser"));
|
||||||
|
mw.leGamemode->setText(QString::fromStdString(clientMgr.getString("gameMode", "Browser")));
|
||||||
|
mw.cBoxNotFull->setCheckState(clientMgr.getBool("notFull", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||||
|
mw.cBoxWithPlayers->setCheckState(clientMgr.getBool("withPlayers", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||||
|
mw.cBBoxWOPass->setCheckState(clientMgr.getBool("noPassword", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||||
|
|
||||||
|
mw.tblServerBrowser->sortByColumn(clientMgr.getInt("sortByCol", "Browser"),
|
||||||
|
clientMgr.getBool("sortByColAscending", "Browser") ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||||
|
|
||||||
|
loadClientSettings(mw);
|
||||||
|
switchMgr();
|
||||||
|
loadServerSettings(mw);
|
||||||
|
switchMgr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::saveBrowserSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
clientMgr.setInt("maxLatency", "Browser", mw.comboLatency->currentIndex());
|
||||||
|
clientMgr.setString("gameMode", "Browser", mw.leGamemode->text().toStdString());
|
||||||
|
clientMgr.setBool("notFull", "Browser", mw.cBoxNotFull->checkState() == Qt::Checked);
|
||||||
|
clientMgr.setBool("withPlayers", "Browser", mw.cBoxWithPlayers->checkState() == Qt::Checked);
|
||||||
|
clientMgr.setBool("noPassword", "Browser", mw.cBBoxWOPass->checkState() == Qt::Checked);
|
||||||
|
|
||||||
|
clientMgr.setInt("sortByCol", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorSection());
|
||||||
|
clientMgr.setBool("sortByColAscending", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder);
|
||||||
|
|
||||||
|
saveClientSettings(mw);
|
||||||
|
|
||||||
|
clientMgr.saveUser(clientCfg);
|
||||||
|
switchMgr();
|
||||||
|
saveServerSettings(mw);
|
||||||
|
switchMgr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::loadClientSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
mw.leClientAddress->setText(QString::fromStdString(clientMgr.getString("destinationAddress", "General")));
|
||||||
|
mw.leClientPort->setText(QString::fromStdString(clientMgr.getString("port", "General")));
|
||||||
|
mw.leClientPassword->setText(QString::fromStdString(clientMgr.getString("password", "General")));
|
||||||
|
mw.combLoglevel->setCurrentIndex(clientMgr.getInt("logLevel", "General"));
|
||||||
|
|
||||||
|
mw.leClientMAddress->setText(QString::fromStdString(clientMgr.getString("address", "Master")));
|
||||||
|
mw.leClientMPort->setText(QString::fromStdString(clientMgr.getString("port", "Master")));
|
||||||
|
|
||||||
|
mw.pbChatKey->setText(QString::fromStdString(clientMgr.getString("keySay", "Chat")));
|
||||||
|
mw.pbModeKey->setText(QString::fromStdString(clientMgr.getString("keyChatMode", "Chat")));
|
||||||
|
|
||||||
|
mw.sbPosX->setValue(clientMgr.getInt("x", "Chat"));
|
||||||
|
mw.sbPosY->setValue(clientMgr.getInt("y", "Chat"));
|
||||||
|
mw.sbPosW->setValue(clientMgr.getInt("w", "Chat"));
|
||||||
|
mw.sbPosH->setValue(clientMgr.getInt("h", "Chat"));
|
||||||
|
|
||||||
|
mw.sbDelay->setValue(clientMgr.getFloat("delay", "Chat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::saveClientSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
clientMgr.setString("destinationAddress", "General", mw.leClientAddress->text().toStdString());
|
||||||
|
clientMgr.setString("port", "General", mw.leClientPort->text().toStdString());
|
||||||
|
clientMgr.setString("password", "General", mw.leClientPassword->text().toStdString());
|
||||||
|
clientMgr.setInt("logLevel", "General", mw.combLoglevel->currentIndex());
|
||||||
|
|
||||||
|
clientMgr.setString("address", "Master", mw.leClientMAddress->text().toStdString());
|
||||||
|
clientMgr.setString("port", "Master", mw.leClientMPort->text().toStdString());
|
||||||
|
|
||||||
|
clientMgr.setString("keySay", "Chat", mw.pbChatKey->text().toStdString());
|
||||||
|
clientMgr.setString("keyChatMode", "Chat", mw.pbModeKey->text().toStdString());
|
||||||
|
|
||||||
|
clientMgr.setInt("x", "Chat", mw.sbPosX->value());
|
||||||
|
clientMgr.setInt("y", "Chat", mw.sbPosY->value());
|
||||||
|
clientMgr.setInt("w", "Chat", mw.sbPosW->value());
|
||||||
|
clientMgr.setInt("h", "Chat", mw.sbPosH->value());
|
||||||
|
|
||||||
|
clientMgr.setFloat("delay", "Chat", mw.sbDelay->value());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::loadServerSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
mw.leServerAddress->setText(QString::fromStdString(serverMgr.getString("localAddress", "General")));
|
||||||
|
mw.leServerPort->setText(QString::fromStdString(serverMgr.getString("port", "General")));
|
||||||
|
mw.sbMaxPlayers->setValue(serverMgr.getInt("maximumPlayers", "General"));
|
||||||
|
mw.leHostname->setText(QString::fromStdString(serverMgr.getString("hostname", "General")));
|
||||||
|
mw.combServerLoglevel->setCurrentIndex(serverMgr.getInt("logLevel", "General"));
|
||||||
|
mw.leServerPassword->setText(QString::fromStdString(serverMgr.getString("password", "General")));
|
||||||
|
|
||||||
|
mw.chbMSenabled->setCheckState(serverMgr.getBool("enabled", "MasterServer") ? Qt::Checked : Qt::Unchecked);
|
||||||
|
mw.leServerMaddress->setText(QString::fromStdString(serverMgr.getString("address", "MasterServer")));
|
||||||
|
mw.leServerMPort->setText(QString::fromStdString(serverMgr.getString("port", "MasterServer")));
|
||||||
|
mw.sbRate->setValue(serverMgr.getInt("rate", "MasterServer"));
|
||||||
|
|
||||||
|
#ifndef TES3MP_PRE07
|
||||||
|
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Modules")));
|
||||||
|
mw.chbAutosort->setCheckState(serverMgr.getBool("autoSort", "Modules") ? Qt::Checked : Qt::Unchecked);
|
||||||
|
#else
|
||||||
|
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Plugins")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::saveServerSettings(Ui::MainWindow &mw)
|
||||||
|
{
|
||||||
|
serverMgr.setString("localAddress", "General", mw.leServerAddress->text().toStdString());
|
||||||
|
serverMgr.setString("port", "General", mw.leServerPort->text().toStdString());
|
||||||
|
serverMgr.setInt("maximumPlayers", "General", mw.sbMaxPlayers->value());
|
||||||
|
serverMgr.setString("hostname", "General", mw.leHostname->text().toStdString());
|
||||||
|
serverMgr.setInt("logLevel", "General", mw.combServerLoglevel->currentIndex());
|
||||||
|
serverMgr.setString("password", "General", mw.leServerPassword->text().toStdString());
|
||||||
|
|
||||||
|
serverMgr.setBool("enabled", "MasterServer", mw.chbMSenabled->checkState() == Qt::Checked);
|
||||||
|
serverMgr.setString("address", "MasterServer", mw.leServerMaddress->text().toStdString());
|
||||||
|
serverMgr.setString("port", "MasterServer", mw.leServerMPort->text().toStdString());
|
||||||
|
serverMgr.setInt("rate", "MasterServer", mw.sbRate->value());
|
||||||
|
|
||||||
|
#ifndef TES3MP_PRE07
|
||||||
|
serverMgr.setString("home", "Modules", mw.leModulePath->text().toStdString());
|
||||||
|
serverMgr.setBool("autoSort", "Modules", mw.chbAutosort->checkState() == Qt::Checked);
|
||||||
|
#else
|
||||||
|
serverMgr.setString("home", "Plugins", mw.leModulePath->text().toStdString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
serverMgr.saveUser(serverCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsMgr::switchMgr()
|
||||||
|
{
|
||||||
|
static Settings::CategorySettingValueMap saveUserSettings;
|
||||||
|
static Settings::CategorySettingValueMap saveDefaultSettings;
|
||||||
|
static Settings::CategorySettingVector saveChangedSettings;
|
||||||
|
static bool currentMgrIsClient = true;
|
||||||
|
|
||||||
|
if(!currentMgrIsClient)
|
||||||
|
{
|
||||||
|
saveUserSettings.swap(clientMgr.mUserSettings);
|
||||||
|
saveDefaultSettings.swap(clientMgr.mDefaultSettings);
|
||||||
|
saveChangedSettings.swap(clientMgr.mChangedSettings);
|
||||||
|
|
||||||
|
qDebug() << "Manager switched to Client config";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveUserSettings.swap(serverMgr.mUserSettings);
|
||||||
|
saveDefaultSettings.swap(serverMgr.mDefaultSettings);
|
||||||
|
saveChangedSettings.swap(serverMgr.mChangedSettings);
|
||||||
|
|
||||||
|
qDebug() << "Manager switched to Server config";
|
||||||
|
}
|
||||||
|
currentMgrIsClient = !currentMgrIsClient;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// Created by koncord on 31.03.18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsMgr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SettingsMgr();
|
||||||
|
|
||||||
|
void loadBrowserSettings(Ui::MainWindow &mw);
|
||||||
|
void saveBrowserSettings(Ui::MainWindow &mw);
|
||||||
|
|
||||||
|
void loadClientSettings(Ui::MainWindow &mw);
|
||||||
|
void saveClientSettings(Ui::MainWindow &mw);
|
||||||
|
|
||||||
|
void loadServerSettings(Ui::MainWindow &mw);
|
||||||
|
void saveServerSettings(Ui::MainWindow &mw);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Settings::Manager serverMgr, clientMgr;
|
||||||
|
std::string serverCfg, clientCfg;
|
||||||
|
void switchMgr();
|
||||||
|
};
|
@ -1,48 +0,0 @@
|
|||||||
#include "cellnameloader.hpp"
|
|
||||||
|
|
||||||
#include <components/esm/loadcell.hpp>
|
|
||||||
#include <components/contentselector/view/contentselector.hpp>
|
|
||||||
|
|
||||||
QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
|
|
||||||
{
|
|
||||||
QSet<QString> cellNames;
|
|
||||||
ESM::ESMReader esmReader;
|
|
||||||
|
|
||||||
// Loop through all content files
|
|
||||||
for (auto &contentPath : contentPaths) {
|
|
||||||
esmReader.open(contentPath.toStdString());
|
|
||||||
|
|
||||||
// Loop through all records
|
|
||||||
while(esmReader.hasMoreRecs())
|
|
||||||
{
|
|
||||||
ESM::NAME recordName = esmReader.getRecName();
|
|
||||||
esmReader.getRecHeader();
|
|
||||||
|
|
||||||
if (isCellRecord(recordName)) {
|
|
||||||
QString cellName = getCellName(esmReader);
|
|
||||||
if (!cellName.isEmpty()) {
|
|
||||||
cellNames.insert(cellName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop loading content for this record and continue to the next
|
|
||||||
esmReader.skipRecord();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cellNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
|
|
||||||
{
|
|
||||||
return recordName.intval == ESM::REC_CELL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
|
|
||||||
{
|
|
||||||
ESM::Cell cell;
|
|
||||||
bool isDeleted = false;
|
|
||||||
cell.loadNameAndData(esmReader, isDeleted);
|
|
||||||
|
|
||||||
return QString::fromStdString(cell.mName);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
#ifndef OPENMW_CELLNAMELOADER_H
|
|
||||||
#define OPENMW_CELLNAMELOADER_H
|
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
|
||||||
|
|
||||||
namespace ESM {class ESMReader; struct Cell;}
|
|
||||||
namespace ContentSelectorView {class ContentSelector;}
|
|
||||||
|
|
||||||
class CellNameLoader {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the names of all cells contained within the given content files
|
|
||||||
* @param contentPaths the file paths of each content file to be examined
|
|
||||||
* @return the names of all cells
|
|
||||||
*/
|
|
||||||
QSet<QString> getCellNames(QStringList &contentPaths);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Returns whether or not the given record is of type "Cell"
|
|
||||||
* @param name The name associated with the record
|
|
||||||
* @return whether or not the given record is of type "Cell"
|
|
||||||
*/
|
|
||||||
bool isCellRecord(ESM::NAME &name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the cell
|
|
||||||
* @param esmReader the reader currently pointed to a loaded cell
|
|
||||||
* @return the name of the cell
|
|
||||||
*/
|
|
||||||
QString getCellName(ESM::ESMReader &esmReader);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_CELLNAMELOADER_H
|
|
@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// Created by koncord on 04.09.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AdminRest.hpp"
|
||||||
|
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
|
||||||
|
#include "RestUtils.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace chrono;
|
||||||
|
using namespace boost::property_tree;
|
||||||
|
|
||||||
|
|
||||||
|
AdminRest::AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile,
|
||||||
|
unsigned short port, std::shared_ptr<MasterServer> master) : httpServer(cert, key, verifyFile), master(master)
|
||||||
|
{
|
||||||
|
httpServer.config.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdminRest::start()
|
||||||
|
{
|
||||||
|
static const string AdminArea = "^/api/admin?";
|
||||||
|
|
||||||
|
httpServer.resource[AdminArea]["POST"] = [this](auto response, auto request) {
|
||||||
|
cout << request->method << endl;
|
||||||
|
cout << request->path << endl;
|
||||||
|
cout << request->http_version << endl;
|
||||||
|
|
||||||
|
for (auto &header : request->header)
|
||||||
|
cout << header.first << ": " << header.second << endl;
|
||||||
|
|
||||||
|
string resp;
|
||||||
|
master->luaStuff([&request, &response, &resp](sol::state &state) {
|
||||||
|
sol::protected_function func = state["OnAdminRequest"];
|
||||||
|
|
||||||
|
sol::protected_function_result result = func.call(request->remote_endpoint_address, request->content.string());
|
||||||
|
if (result.valid())
|
||||||
|
*response << result.get<string>();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "Error: " << result.get<string>() << endl;
|
||||||
|
*response << response500;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*httpServer.on_error = [](auto request, const boost::system::error_code& err)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << err.message() << " " << err.category().name() << std::endl;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
httpServer.default_resource["GET"] = [](auto response, auto /*request*/) {
|
||||||
|
cout << "Default request" << endl;
|
||||||
|
*response << response400;
|
||||||
|
};
|
||||||
|
|
||||||
|
thr = thread([this](){httpServer.start();});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdminRest::stop()
|
||||||
|
{
|
||||||
|
httpServer.stop();
|
||||||
|
if(thr.joinable())
|
||||||
|
thr.join();
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by koncord on 04.09.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SimpleWeb/https_server.hpp"
|
||||||
|
#include "MasterServer.hpp"
|
||||||
|
|
||||||
|
typedef SimpleWeb::Server<SimpleWeb::HTTPS> HttpsServer;
|
||||||
|
|
||||||
|
class AdminRest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile, unsigned short port, std::shared_ptr<MasterServer> master);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
HttpsServer httpServer;
|
||||||
|
std::shared_ptr<MasterServer> master;
|
||||||
|
std::thread thr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by koncord on 04.09.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "SimpleWeb/base_server.hpp"
|
||||||
|
|
||||||
|
static std::string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
|
||||||
|
static std::string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
|
||||||
|
static std::string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
|
||||||
|
|
||||||
|
static std::string response403 = "HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\n\r\nForbidden";
|
||||||
|
static std::string response500 = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\nInternal Server Error";
|
||||||
|
|
||||||
|
template <class Protocol>
|
||||||
|
inline void ResponseStr(std::shared_ptr<typename SimpleWeb::ServerBase<Protocol>::Response> response,
|
||||||
|
std::string content, std::string type = "", std::string code = "200 OK")
|
||||||
|
{
|
||||||
|
*response << "HTTP/1.1 " << code << "\r\n";
|
||||||
|
if (!type.empty())
|
||||||
|
*response << "Content-Type: " << type <<"\r\n";
|
||||||
|
*response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2016 Ole Christian Eidheim
|
||||||
|
|
||||||
|
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.
|
@ -1,55 +1,42 @@
|
|||||||
/*
|
#pragma once
|
||||||
* https://github.com/eidheim/Simple-Web-Server/
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
* Copyright (c) 2014-2016 Ole Christian Eidheim
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SERVER_HTTP_HPP
|
|
||||||
#define SERVER_HTTP_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
#include "base_server.hpp"
|
||||||
|
|
||||||
namespace SimpleWeb
|
namespace SimpleWeb {
|
||||||
{
|
|
||||||
|
|
||||||
template <class socket_type>
|
template <class socket_type>
|
||||||
class Server : public ServerBase<socket_type> {};
|
class Server : public ServerBase<socket_type> {};
|
||||||
|
|
||||||
typedef boost::asio::ip::tcp::socket HTTP;
|
using HTTP = asio::ip::tcp::socket;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Server<HTTP> : public ServerBase<HTTP>
|
class Server<HTTP> : public ServerBase<HTTP> {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Server() : ServerBase<HTTP>::ServerBase(80)
|
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void accept()
|
void accept() override {
|
||||||
{
|
auto session = std::make_shared<Session>(create_connection(*io_service));
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
|
||||||
auto socket = std::make_shared<HTTP>(*io_service);
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
|
if(!lock)
|
||||||
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
|
return;
|
||||||
{
|
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
// Immediately start accepting a new connection (unless io_service has been stopped)
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
if(ec != asio::error::operation_aborted)
|
||||||
accept();
|
this->accept();
|
||||||
|
|
||||||
if (!ec)
|
if(!ec) {
|
||||||
{
|
asio::ip::tcp::no_delay option(true);
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
error_code ec;
|
||||||
socket->set_option(option);
|
session->connection->socket->set_option(option, ec);
|
||||||
|
|
||||||
this->read_request_and_content(socket);
|
this->read_request_and_content(session);
|
||||||
}
|
}
|
||||||
else if (on_error)
|
else if(this->on_error)
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
#endif //SERVER_HTTP_HPP
|
|
||||||
|
@ -1,91 +1,82 @@
|
|||||||
#ifndef HTTPS_SERVER_HPP
|
#pragma once
|
||||||
#define HTTPS_SERVER_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
#include "base_server.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_STANDALONE_ASIO
|
||||||
|
#include <asio/ssl.hpp>
|
||||||
|
#else
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <openssl/ssl.h>
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
namespace SimpleWeb
|
namespace SimpleWeb {
|
||||||
{
|
using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>;
|
||||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Server<HTTPS> : public ServerBase<HTTPS>
|
class Server<HTTPS> : public ServerBase<HTTPS> {
|
||||||
{
|
|
||||||
std::string session_id_context;
|
std::string session_id_context;
|
||||||
bool set_session_id_context = false;
|
bool set_session_id_context = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server(const std::string &cert_file, const std::string &private_key_file,
|
Server(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string())
|
||||||
const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),
|
: ServerBase<HTTPS>::ServerBase(443), context(asio::ssl::context::tlsv12) {
|
||||||
context(boost::asio::ssl::context::tlsv12)
|
|
||||||
{
|
|
||||||
context.use_certificate_chain_file(cert_file);
|
context.use_certificate_chain_file(cert_file);
|
||||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
|
||||||
|
|
||||||
if (verify_file.size() > 0)
|
if(verify_file.size() > 0) {
|
||||||
{
|
|
||||||
context.load_verify_file(verify_file);
|
context.load_verify_file(verify_file);
|
||||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
|
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | asio::ssl::verify_client_once);
|
||||||
boost::asio::ssl::verify_client_once);
|
|
||||||
set_session_id_context = true;
|
set_session_id_context = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start()
|
void start() override {
|
||||||
{
|
if(set_session_id_context) {
|
||||||
if (set_session_id_context)
|
|
||||||
{
|
|
||||||
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
|
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
|
||||||
session_id_context = std::to_string(config.port) + ':';
|
session_id_context = std::to_string(config.port) + ':';
|
||||||
session_id_context.append(config.address.rbegin(), config.address.rend());
|
session_id_context.append(config.address.rbegin(), config.address.rend());
|
||||||
SSL_CTX_set_session_id_context(context.native_handle(),
|
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
||||||
reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
std::min<size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
|
||||||
std::min<size_t>(session_id_context.size(),
|
|
||||||
SSL_MAX_SSL_SESSION_ID_LENGTH));
|
|
||||||
}
|
}
|
||||||
ServerBase::start();
|
ServerBase::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
boost::asio::ssl::context context;
|
asio::ssl::context context;
|
||||||
|
|
||||||
virtual void accept()
|
void accept() override {
|
||||||
{
|
auto session = std::make_shared<Session>(create_connection(*io_service, context));
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
auto socket = std::make_shared<HTTPS>(*io_service, context);
|
|
||||||
|
|
||||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
|
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
|
||||||
{
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
if(!lock)
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
return;
|
||||||
accept();
|
|
||||||
|
|
||||||
|
if(ec != asio::error::operation_aborted)
|
||||||
|
this->accept();
|
||||||
|
|
||||||
if (!ec)
|
if(!ec) {
|
||||||
{
|
asio::ip::tcp::no_delay option(true);
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
error_code ec;
|
||||||
socket->lowest_layer().set_option(option);
|
session->connection->socket->lowest_layer().set_option(option, ec);
|
||||||
|
|
||||||
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
session->connection->set_timeout(config.timeout_request);
|
||||||
auto timer = get_timeout_timer(socket, config.timeout_request);
|
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
|
||||||
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
session->connection->cancel_timeout();
|
||||||
(const boost::system::error_code &ec)
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
{
|
if(!lock)
|
||||||
if (timer)
|
return;
|
||||||
timer->cancel();
|
|
||||||
if(!ec)
|
if(!ec)
|
||||||
read_request_and_content(socket);
|
this->read_request_and_content(session);
|
||||||
else if (on_error)
|
else if(this->on_error)
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (on_error)
|
else if(this->on_error)
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
#endif //HTTPS_SERVER_HPP
|
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace SimpleWeb {
|
||||||
|
enum class StatusCode {
|
||||||
|
unknown = 0,
|
||||||
|
information_continue = 100,
|
||||||
|
information_switching_protocols,
|
||||||
|
information_processing,
|
||||||
|
success_ok = 200,
|
||||||
|
success_created,
|
||||||
|
success_accepted,
|
||||||
|
success_non_authoritative_information,
|
||||||
|
success_no_content,
|
||||||
|
success_reset_content,
|
||||||
|
success_partial_content,
|
||||||
|
success_multi_status,
|
||||||
|
success_already_reported,
|
||||||
|
success_im_used = 226,
|
||||||
|
redirection_multiple_choices = 300,
|
||||||
|
redirection_moved_permanently,
|
||||||
|
redirection_found,
|
||||||
|
redirection_see_other,
|
||||||
|
redirection_not_modified,
|
||||||
|
redirection_use_proxy,
|
||||||
|
redirection_switch_proxy,
|
||||||
|
redirection_temporary_redirect,
|
||||||
|
redirection_permanent_redirect,
|
||||||
|
client_error_bad_request = 400,
|
||||||
|
client_error_unauthorized,
|
||||||
|
client_error_payment_required,
|
||||||
|
client_error_forbidden,
|
||||||
|
client_error_not_found,
|
||||||
|
client_error_method_not_allowed,
|
||||||
|
client_error_not_acceptable,
|
||||||
|
client_error_proxy_authentication_required,
|
||||||
|
client_error_request_timeout,
|
||||||
|
client_error_conflict,
|
||||||
|
client_error_gone,
|
||||||
|
client_error_length_required,
|
||||||
|
client_error_precondition_failed,
|
||||||
|
client_error_payload_too_large,
|
||||||
|
client_error_uri_too_long,
|
||||||
|
client_error_unsupported_media_type,
|
||||||
|
client_error_range_not_satisfiable,
|
||||||
|
client_error_expectation_failed,
|
||||||
|
client_error_im_a_teapot,
|
||||||
|
client_error_misdirection_required = 421,
|
||||||
|
client_error_unprocessable_entity,
|
||||||
|
client_error_locked,
|
||||||
|
client_error_failed_dependency,
|
||||||
|
client_error_upgrade_required = 426,
|
||||||
|
client_error_precondition_required = 428,
|
||||||
|
client_error_too_many_requests,
|
||||||
|
client_error_request_header_fields_too_large = 431,
|
||||||
|
client_error_unavailable_for_legal_reasons = 451,
|
||||||
|
server_error_internal_server_error = 500,
|
||||||
|
server_error_not_implemented,
|
||||||
|
server_error_bad_gateway,
|
||||||
|
server_error_service_unavailable,
|
||||||
|
server_error_gateway_timeout,
|
||||||
|
server_error_http_version_not_supported,
|
||||||
|
server_error_variant_also_negotiates,
|
||||||
|
server_error_insufficient_storage,
|
||||||
|
server_error_loop_detected,
|
||||||
|
server_error_not_extended = 510,
|
||||||
|
server_error_network_authentication_required
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::vector<std::pair<StatusCode, std::string>> &status_codes() noexcept {
|
||||||
|
const static std::vector<std::pair<StatusCode, std::string>> status_codes = {
|
||||||
|
{StatusCode::unknown, ""},
|
||||||
|
{StatusCode::information_continue, "100 Continue"},
|
||||||
|
{StatusCode::information_switching_protocols, "101 Switching Protocols"},
|
||||||
|
{StatusCode::information_processing, "102 Processing"},
|
||||||
|
{StatusCode::success_ok, "200 OK"},
|
||||||
|
{StatusCode::success_created, "201 Created"},
|
||||||
|
{StatusCode::success_accepted, "202 Accepted"},
|
||||||
|
{StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"},
|
||||||
|
{StatusCode::success_no_content, "204 No Content"},
|
||||||
|
{StatusCode::success_reset_content, "205 Reset Content"},
|
||||||
|
{StatusCode::success_partial_content, "206 Partial Content"},
|
||||||
|
{StatusCode::success_multi_status, "207 Multi-Status"},
|
||||||
|
{StatusCode::success_already_reported, "208 Already Reported"},
|
||||||
|
{StatusCode::success_im_used, "226 IM Used"},
|
||||||
|
{StatusCode::redirection_multiple_choices, "300 Multiple Choices"},
|
||||||
|
{StatusCode::redirection_moved_permanently, "301 Moved Permanently"},
|
||||||
|
{StatusCode::redirection_found, "302 Found"},
|
||||||
|
{StatusCode::redirection_see_other, "303 See Other"},
|
||||||
|
{StatusCode::redirection_not_modified, "304 Not Modified"},
|
||||||
|
{StatusCode::redirection_use_proxy, "305 Use Proxy"},
|
||||||
|
{StatusCode::redirection_switch_proxy, "306 Switch Proxy"},
|
||||||
|
{StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"},
|
||||||
|
{StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"},
|
||||||
|
{StatusCode::client_error_bad_request, "400 Bad Request"},
|
||||||
|
{StatusCode::client_error_unauthorized, "401 Unauthorized"},
|
||||||
|
{StatusCode::client_error_payment_required, "402 Payment Required"},
|
||||||
|
{StatusCode::client_error_forbidden, "403 Forbidden"},
|
||||||
|
{StatusCode::client_error_not_found, "404 Not Found"},
|
||||||
|
{StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"},
|
||||||
|
{StatusCode::client_error_not_acceptable, "406 Not Acceptable"},
|
||||||
|
{StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"},
|
||||||
|
{StatusCode::client_error_request_timeout, "408 Request Timeout"},
|
||||||
|
{StatusCode::client_error_conflict, "409 Conflict"},
|
||||||
|
{StatusCode::client_error_gone, "410 Gone"},
|
||||||
|
{StatusCode::client_error_length_required, "411 Length Required"},
|
||||||
|
{StatusCode::client_error_precondition_failed, "412 Precondition Failed"},
|
||||||
|
{StatusCode::client_error_payload_too_large, "413 Payload Too Large"},
|
||||||
|
{StatusCode::client_error_uri_too_long, "414 URI Too Long"},
|
||||||
|
{StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"},
|
||||||
|
{StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"},
|
||||||
|
{StatusCode::client_error_expectation_failed, "417 Expectation Failed"},
|
||||||
|
{StatusCode::client_error_im_a_teapot, "418 I'm a teapot"},
|
||||||
|
{StatusCode::client_error_misdirection_required, "421 Misdirected Request"},
|
||||||
|
{StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"},
|
||||||
|
{StatusCode::client_error_locked, "423 Locked"},
|
||||||
|
{StatusCode::client_error_failed_dependency, "424 Failed Dependency"},
|
||||||
|
{StatusCode::client_error_upgrade_required, "426 Upgrade Required"},
|
||||||
|
{StatusCode::client_error_precondition_required, "428 Precondition Required"},
|
||||||
|
{StatusCode::client_error_too_many_requests, "429 Too Many Requests"},
|
||||||
|
{StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"},
|
||||||
|
{StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"},
|
||||||
|
{StatusCode::server_error_internal_server_error, "500 Internal Server Error"},
|
||||||
|
{StatusCode::server_error_not_implemented, "501 Not Implemented"},
|
||||||
|
{StatusCode::server_error_bad_gateway, "502 Bad Gateway"},
|
||||||
|
{StatusCode::server_error_service_unavailable, "503 Service Unavailable"},
|
||||||
|
{StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"},
|
||||||
|
{StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"},
|
||||||
|
{StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"},
|
||||||
|
{StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"},
|
||||||
|
{StatusCode::server_error_loop_detected, "508 Loop Detected"},
|
||||||
|
{StatusCode::server_error_not_extended, "510 Not Extended"},
|
||||||
|
{StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}};
|
||||||
|
return status_codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline StatusCode status_code(const std::string &status_code_str) noexcept {
|
||||||
|
for(auto &status_code : status_codes()) {
|
||||||
|
if(status_code.second == status_code_str)
|
||||||
|
return status_code.first;
|
||||||
|
}
|
||||||
|
return StatusCode::unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::string &status_code(StatusCode status_code_enum) noexcept {
|
||||||
|
for(auto &status_code : status_codes()) {
|
||||||
|
if(status_code.first == status_code_enum)
|
||||||
|
return status_code.second;
|
||||||
|
}
|
||||||
|
return status_codes()[0].second;
|
||||||
|
}
|
||||||
|
} // namespace SimpleWeb
|
@ -0,0 +1,340 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "status_code.hpp"
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace SimpleWeb {
|
||||||
|
inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept {
|
||||||
|
return str1.size() == str2.size() &&
|
||||||
|
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
|
||||||
|
return tolower(a) == tolower(b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
class CaseInsensitiveEqual {
|
||||||
|
public:
|
||||||
|
bool operator()(const std::string &str1, const std::string &str2) const noexcept {
|
||||||
|
return case_insensitive_equal(str1, str2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
|
||||||
|
class CaseInsensitiveHash {
|
||||||
|
public:
|
||||||
|
size_t operator()(const std::string &str) const noexcept {
|
||||||
|
size_t h = 0;
|
||||||
|
std::hash<int> hash;
|
||||||
|
for(auto c : str)
|
||||||
|
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
|
||||||
|
|
||||||
|
/// Percent encoding and decoding
|
||||||
|
class Percent {
|
||||||
|
public:
|
||||||
|
/// Returns percent-encoded string
|
||||||
|
static std::string encode(const std::string &value) noexcept {
|
||||||
|
static auto hex_chars = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
result.reserve(value.size()); // Minimum size of result
|
||||||
|
|
||||||
|
for(auto &chr : value) {
|
||||||
|
if(chr == ' ')
|
||||||
|
result += '+';
|
||||||
|
else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']')
|
||||||
|
result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
|
||||||
|
else
|
||||||
|
result += chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns percent-decoded string
|
||||||
|
static std::string decode(const std::string &value) noexcept {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result
|
||||||
|
|
||||||
|
for(size_t i = 0; i < value.size(); ++i) {
|
||||||
|
auto &chr = value[i];
|
||||||
|
if(chr == '%' && i + 2 < value.size()) {
|
||||||
|
auto hex = value.substr(i + 1, 2);
|
||||||
|
auto decoded_chr = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
|
||||||
|
result += decoded_chr;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if(chr == '+')
|
||||||
|
result += ' ';
|
||||||
|
else
|
||||||
|
result += chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query string creation and parsing
|
||||||
|
class QueryString {
|
||||||
|
public:
|
||||||
|
/// Returns query string created from given field names and values
|
||||||
|
static std::string create(const CaseInsensitiveMultimap &fields) noexcept {
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
for(auto &field : fields) {
|
||||||
|
result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns query keys with percent-decoded values.
|
||||||
|
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept {
|
||||||
|
CaseInsensitiveMultimap result;
|
||||||
|
|
||||||
|
if(query_string.empty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
size_t name_pos = 0;
|
||||||
|
auto name_end_pos = std::string::npos;
|
||||||
|
auto value_pos = std::string::npos;
|
||||||
|
for(size_t c = 0; c < query_string.size(); ++c) {
|
||||||
|
if(query_string[c] == '&') {
|
||||||
|
auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
|
||||||
|
if(!name.empty()) {
|
||||||
|
auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
|
||||||
|
result.emplace(std::move(name), Percent::decode(value));
|
||||||
|
}
|
||||||
|
name_pos = c + 1;
|
||||||
|
name_end_pos = std::string::npos;
|
||||||
|
value_pos = std::string::npos;
|
||||||
|
}
|
||||||
|
else if(query_string[c] == '=') {
|
||||||
|
name_end_pos = c;
|
||||||
|
value_pos = c + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(name_pos < query_string.size()) {
|
||||||
|
auto name = query_string.substr(name_pos, name_end_pos - name_pos);
|
||||||
|
if(!name.empty()) {
|
||||||
|
auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
|
||||||
|
result.emplace(std::move(name), Percent::decode(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpHeader {
|
||||||
|
public:
|
||||||
|
/// Parse header fields
|
||||||
|
static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
|
||||||
|
CaseInsensitiveMultimap result;
|
||||||
|
std::string line;
|
||||||
|
getline(stream, line);
|
||||||
|
size_t param_end;
|
||||||
|
while((param_end = line.find(':')) != std::string::npos) {
|
||||||
|
size_t value_start = param_end + 1;
|
||||||
|
if(value_start < line.size()) {
|
||||||
|
if(line[value_start] == ' ')
|
||||||
|
value_start++;
|
||||||
|
if(value_start < line.size())
|
||||||
|
result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
getline(stream, line);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RequestMessage {
|
||||||
|
public:
|
||||||
|
/// Parse request line and header fields
|
||||||
|
static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
|
||||||
|
header.clear();
|
||||||
|
std::string line;
|
||||||
|
getline(stream, line);
|
||||||
|
size_t method_end;
|
||||||
|
if((method_end = line.find(' ')) != std::string::npos) {
|
||||||
|
method = line.substr(0, method_end);
|
||||||
|
|
||||||
|
size_t query_start = std::string::npos;
|
||||||
|
size_t path_and_query_string_end = std::string::npos;
|
||||||
|
for(size_t i = method_end + 1; i < line.size(); ++i) {
|
||||||
|
if(line[i] == '?' && (i + 1) < line.size())
|
||||||
|
query_start = i + 1;
|
||||||
|
else if(line[i] == ' ') {
|
||||||
|
path_and_query_string_end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(path_and_query_string_end != std::string::npos) {
|
||||||
|
if(query_start != std::string::npos) {
|
||||||
|
path = line.substr(method_end + 1, query_start - method_end - 2);
|
||||||
|
query_string = line.substr(query_start, path_and_query_string_end - query_start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
|
||||||
|
|
||||||
|
size_t protocol_end;
|
||||||
|
if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) {
|
||||||
|
if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0)
|
||||||
|
return false;
|
||||||
|
version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
header = HttpHeader::parse(stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResponseMessage {
|
||||||
|
public:
|
||||||
|
/// Parse status line and header fields
|
||||||
|
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
|
||||||
|
header.clear();
|
||||||
|
std::string line;
|
||||||
|
getline(stream, line);
|
||||||
|
size_t version_end = line.find(' ');
|
||||||
|
if(version_end != std::string::npos) {
|
||||||
|
if(5 < line.size())
|
||||||
|
version = line.substr(5, version_end - 5);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
if((version_end + 1) < line.size())
|
||||||
|
status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
header = HttpHeader::parse(stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ContentDisposition {
|
||||||
|
public:
|
||||||
|
/// Can be used to parse the Content-Disposition header field value when
|
||||||
|
/// clients are posting requests with enctype="multipart/form-data"
|
||||||
|
static CaseInsensitiveMultimap parse(const std::string &line) {
|
||||||
|
CaseInsensitiveMultimap result;
|
||||||
|
|
||||||
|
size_t para_start_pos = 0;
|
||||||
|
size_t para_end_pos = std::string::npos;
|
||||||
|
size_t value_start_pos = std::string::npos;
|
||||||
|
for(size_t c = 0; c < line.size(); ++c) {
|
||||||
|
if(para_start_pos != std::string::npos) {
|
||||||
|
if(para_end_pos == std::string::npos) {
|
||||||
|
if(line[c] == ';') {
|
||||||
|
result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
|
||||||
|
para_start_pos = std::string::npos;
|
||||||
|
}
|
||||||
|
else if(line[c] == '=')
|
||||||
|
para_end_pos = c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(value_start_pos == std::string::npos) {
|
||||||
|
if(line[c] == '"' && c + 1 < line.size())
|
||||||
|
value_start_pos = c + 1;
|
||||||
|
}
|
||||||
|
else if(line[c] == '"') {
|
||||||
|
result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
|
||||||
|
para_start_pos = std::string::npos;
|
||||||
|
para_end_pos = std::string::npos;
|
||||||
|
value_start_pos = std::string::npos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(line[c] != ' ' && line[c] != ';')
|
||||||
|
para_start_pos = c;
|
||||||
|
}
|
||||||
|
if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
|
||||||
|
result.emplace(line.substr(para_start_pos), std::string());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
|
#ifdef __SSE2__
|
||||||
|
#include <emmintrin.h>
|
||||||
|
namespace SimpleWeb {
|
||||||
|
inline void spin_loop_pause() noexcept { _mm_pause(); }
|
||||||
|
} // namespace SimpleWeb
|
||||||
|
// TODO: need verification that the following checks are correct:
|
||||||
|
#elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
|
||||||
|
#include <intrin.h>
|
||||||
|
namespace SimpleWeb {
|
||||||
|
inline void spin_loop_pause() noexcept { _mm_pause(); }
|
||||||
|
} // namespace SimpleWeb
|
||||||
|
#else
|
||||||
|
namespace SimpleWeb {
|
||||||
|
inline void spin_loop_pause() noexcept {}
|
||||||
|
} // namespace SimpleWeb
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace SimpleWeb {
|
||||||
|
/// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service
|
||||||
|
class ScopeRunner {
|
||||||
|
/// Scope count that is set to -1 if scopes are to be canceled
|
||||||
|
std::atomic<long> count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class SharedLock {
|
||||||
|
friend class ScopeRunner;
|
||||||
|
std::atomic<long> &count;
|
||||||
|
SharedLock(std::atomic<long> &count) noexcept : count(count) {}
|
||||||
|
SharedLock &operator=(const SharedLock &) = delete;
|
||||||
|
SharedLock(const SharedLock &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~SharedLock() noexcept {
|
||||||
|
count.fetch_sub(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeRunner() noexcept : count(0) {}
|
||||||
|
|
||||||
|
/// Returns nullptr if scope should be exited, or a shared lock otherwise
|
||||||
|
std::unique_ptr<SharedLock> continue_lock() noexcept {
|
||||||
|
long expected = count;
|
||||||
|
while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
|
||||||
|
spin_loop_pause();
|
||||||
|
|
||||||
|
if(expected < 0)
|
||||||
|
return nullptr;
|
||||||
|
else
|
||||||
|
return std::unique_ptr<SharedLock>(new SharedLock(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blocks until all shared locks are released, then prevents future shared locks
|
||||||
|
void stop() noexcept {
|
||||||
|
long expected = 0;
|
||||||
|
while(!count.compare_exchange_weak(expected, -1)) {
|
||||||
|
if(expected < 0)
|
||||||
|
return;
|
||||||
|
expected = 0;
|
||||||
|
spin_loop_pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace SimpleWeb
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue