├── .clang-format ├── .gitattributes ├── .github └── CODEOWNERS ├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CPPLINT.cfg ├── LICENSE ├── NOTICE.md ├── README.md ├── SECURITY.md ├── azure-pipelines ├── build │ ├── linux │ │ └── du │ │ │ ├── doclient-lite-docker.yml │ │ │ ├── dopapt-docker.yml │ │ │ ├── dosdkcpp-docker.yml │ │ │ └── templates │ │ │ ├── doclient-lite-docker-steps.yml │ │ │ ├── dopapt-docker-steps.yml │ │ │ └── dosdkcpp-docker-steps.yml │ ├── mac │ │ ├── dosdkcpp.yml │ │ └── templates │ │ │ └── dosdkcpp-steps.yml │ └── windows │ │ ├── dosdkcpp.yml │ │ └── templates │ │ └── dosdkcpp-steps.yml └── publishing │ └── github-release.yml ├── build ├── build-snaps.sh ├── build.py ├── docker │ ├── debian10 │ │ ├── amd64 │ │ │ └── Dockerfile │ │ ├── arm32 │ │ │ └── Dockerfile │ │ └── arm64 │ │ │ └── Dockerfile │ ├── debian11 │ │ ├── amd64 │ │ │ └── Dockerfile │ │ ├── arm32 │ │ │ └── Dockerfile │ │ └── arm64 │ │ │ └── Dockerfile │ ├── docker-build-plugin.sh │ ├── ubuntu1804 │ │ ├── amd64 │ │ │ └── Dockerfile │ │ └── arm64 │ │ │ └── Dockerfile │ ├── ubuntu2004 │ │ ├── amd64 │ │ │ └── Dockerfile │ │ └── arm64 │ │ │ └── Dockerfile │ └── ubuntu2204 │ │ ├── amd64 │ │ └── Dockerfile │ │ └── arm64 │ │ └── Dockerfile └── scripts │ ├── bootstrap-windows.ps1 │ ├── bootstrap.sh │ ├── check_binary_size.sh │ ├── install-vcpkg-deps.ps1 │ └── install-vcpkg-deps.sh ├── client-lite ├── CMakeLists.txt ├── build │ ├── postinst.in.sh │ ├── postrm.in.sh │ └── prerm.in.sh ├── src │ ├── config │ │ ├── config_defaults.h │ │ ├── config_manager.cpp │ │ ├── config_manager.h │ │ ├── mcc_manager.cpp │ │ ├── mcc_manager.h │ │ ├── network_monitor.cpp │ │ └── network_monitor.h │ ├── download │ │ ├── download.cpp │ │ ├── download.h │ │ ├── download_manager.cpp │ │ ├── download_manager.h │ │ ├── download_progress_tracker.h │ │ └── download_status.h │ ├── exe │ │ └── docs.cpp │ ├── include │ │ ├── basic_types.h │ │ ├── do_assert.h │ │ ├── do_common.h │ │ ├── do_error.h │ │ ├── do_filesystem.h │ │ ├── hresult_helpers.h │ │ └── sal_undef.h │ ├── ipc │ │ ├── rest_api_params.cpp │ │ ├── rest_api_params.h │ │ ├── rest_api_parser.cpp │ │ ├── rest_api_parser.h │ │ ├── rest_api_request.cpp │ │ ├── rest_api_request.h │ │ ├── rest_http_controller.cpp │ │ ├── rest_http_controller.h │ │ ├── rest_http_listener.cpp │ │ ├── rest_http_listener.h │ │ ├── rest_http_listener_conn.cpp │ │ ├── rest_http_listener_conn.h │ │ └── rest_port_advertiser.h │ ├── threading │ │ ├── do_event.cpp │ │ ├── do_event.h │ │ ├── task_queue.cpp │ │ ├── task_queue.h │ │ ├── task_thread.cpp │ │ ├── task_thread.h │ │ └── waitable_counter.h │ ├── trace │ │ ├── do_log.cpp │ │ ├── do_log.h │ │ ├── event_data.cpp │ │ ├── event_data.h │ │ ├── telemetry_logger.cpp │ │ └── telemetry_logger.h │ └── util │ │ ├── do_curl_wrappers.cpp │ │ ├── do_curl_wrappers.h │ │ ├── do_date_time.h │ │ ├── do_file.cpp │ │ ├── do_file.h │ │ ├── do_guid.cpp │ │ ├── do_guid.h │ │ ├── do_json_parser.cpp │ │ ├── do_json_parser.h │ │ ├── do_noncopyable.h │ │ ├── do_persistence.cpp │ │ ├── do_persistence.h │ │ ├── error_macros.cpp │ │ ├── error_macros.h │ │ ├── http_agent.cpp │ │ ├── http_agent.h │ │ ├── http_agent_interface.h │ │ ├── proc_launch_helper.h │ │ ├── proxy_finder.cpp │ │ ├── proxy_finder.h │ │ ├── safe_int.h │ │ ├── stop_watch.cpp │ │ ├── stop_watch.h │ │ ├── string_ops.cpp │ │ └── string_ops.h └── test │ ├── CMakeLists.txt │ ├── CPPLINT.cfg │ ├── do_log_tests.cpp │ ├── docs_tests.cpp │ ├── download_manager_tests.cpp │ ├── json_parser_tests.cpp │ ├── mcc_manager.tests.cpp │ ├── network_monitor_tests.cpp │ ├── rest_http_listener_tests.cpp │ ├── string_ops_tests.cpp │ ├── test_common.h │ ├── test_data.cpp │ ├── test_data.h │ ├── test_helpers.cpp │ ├── test_helpers.h │ └── test_verifiers.h ├── common ├── CMakeLists.txt ├── cmake │ ├── FindUUID.cmake │ ├── Findlibproxy.cmake │ ├── do-build-helpers.cmake │ └── do-packaging-helpers.cmake ├── lib-dohttp │ ├── CMakeLists.txt │ ├── do_cpprest_base_uri.h │ ├── do_cpprest_uri.cpp │ ├── do_cpprest_uri.h │ ├── do_cpprest_uri_builder.cpp │ ├── do_cpprest_uri_builder.h │ ├── do_cpprest_utils.cpp │ ├── do_cpprest_utils.h │ ├── do_http_defines.cpp │ ├── do_http_defines.h │ ├── do_http_packet.h │ ├── do_http_parser.cpp │ └── do_http_parser.h ├── lib-dotestutil │ ├── CMakeLists.txt │ ├── do_test_helpers.cpp │ └── do_test_helpers.h └── lib-doversion │ ├── CMakeLists.txt │ ├── do_version.cpp │ └── do_version.h ├── guardian └── SDL │ └── .gdnsuppress ├── licenses └── cgmanifest.json ├── plugins ├── CMakeLists.txt └── linux-apt │ ├── CMakeLists.txt │ ├── do_date_time.h │ ├── do_hash.cpp │ ├── do_hash.h │ ├── do_log.cpp │ ├── do_log.h │ ├── do_plugin_exception.h │ ├── do_string_util.cpp │ ├── do_string_util.h │ ├── main.cpp │ └── scripts │ ├── configure-apt-method.sh │ ├── postinst.in.sh │ ├── postrm.in.sh │ └── prerm.in.sh ├── sdk-cpp ├── CMakeLists.txt ├── build │ ├── cleanup-install.sh │ └── cmake │ │ └── deliveryoptimization_sdk-config.cmake ├── include │ ├── do_config.h │ ├── do_download.h │ ├── do_download_property.h │ ├── do_download_status.h │ └── do_errors.h ├── src │ ├── do_config.cpp │ ├── do_download.cpp │ ├── do_download_property.cpp │ ├── do_download_status.cpp │ └── internal │ │ ├── com │ │ ├── deliveryoptimization │ │ │ └── deliveryoptimization.h │ │ ├── do_config_internal.cpp │ │ ├── do_download_property_internal.cpp │ │ └── download_impl.cpp │ │ ├── do_config_internal.h │ │ ├── do_download_property_internal.h │ │ ├── do_error_helpers.cpp │ │ ├── do_error_helpers.h │ │ ├── do_filesystem.h │ │ ├── do_noncopyable.h │ │ ├── download_impl.h │ │ ├── download_interface.h │ │ └── rest │ │ ├── do_config_internal.cpp │ │ ├── do_download_property_internal.cpp │ │ ├── download_impl.cpp │ │ └── util │ │ ├── do_http_client.cpp │ │ ├── do_http_client.h │ │ ├── do_http_message.cpp │ │ ├── do_http_message.h │ │ ├── do_persistence.cpp │ │ ├── do_persistence.h │ │ ├── do_port_finder.cpp │ │ └── do_port_finder.h └── tests │ ├── CMakeLists.txt │ ├── CPPLINT.cfg │ ├── dosdkcpp_testrunner.cpp │ ├── download_properties_tests.cpp │ ├── download_status_tests.cpp │ ├── download_tests_common.cpp │ ├── rest │ ├── config_tests.cpp │ ├── mcc_download_tests.cpp │ ├── network_connectivity_tests.cpp │ ├── port_discovery_tests.cpp │ ├── rest_interface_tests.cpp │ └── test_helpers.cpp │ ├── test_data.cpp │ ├── test_data.h │ ├── test_helpers.h │ └── tests_common.h └── snapcraft-options ├── connect-snaps.sh ├── snapcraft-agent.yaml ├── snapcraft-sdk.yaml └── ubuntu-snap.md /.clang-format: -------------------------------------------------------------------------------- 1 | # $ sudo apt-get install clang-format-6.0 2 | # Integration with tools like VSCode: https://clang.llvm.org/docs/ClangFormat.html 3 | # Online configuration tool: 4 | # https://zed0.co.uk/clang-format-configurator/ 5 | 6 | # This is currently disabled (via DisableFormat) because some of our conventions deviate from msft standard 7 | # and clang-format-6.0 does not have customizations for these. The latest release (11.0) 8 | # adds some, like BeforeLambdaBody, but not all and it is not available in a debian release yet. 9 | # Some deviations: 10 | # 1) Lambda header is on the same line but the body+braces are on a new line, not indented. 11 | # 2) Header includes follow the order of do_common.h, .h, system/oss, private headers. 12 | # This can and should be solved by leaving a space between do_common.h and .h. 13 | # 3) A single space after class data member and before initializer open brace but not in other places. 14 | # Revisit when there is time and see if we can either customize to our convention or get rid of the deviations. 15 | # NOTE: Remove SortIncludes when removing DisableFormat. It is required because includes get sorted even with DisableFormat. 16 | 17 | --- 18 | BasedOnStyle: Microsoft 19 | BreakConstructorInitializers: AfterColon 20 | AccessModifierOffset: '-4' 21 | AlignAfterOpenBracket: DontAlign 22 | AlignConsecutiveAssignments: 'false' 23 | AlignConsecutiveDeclarations: 'false' 24 | AlignEscapedNewlines: Left 25 | AllowAllConstructorInitializersOnNextLine: 'false' 26 | AllowShortBlocksOnASingleLine: 'false' 27 | AllowShortFunctionsOnASingleLine: InlineOnly 28 | BraceWrapping: 29 | AfterCaseLabel: true 30 | AfterClass: true 31 | AfterControlStatement: true 32 | AfterEnum: true 33 | AfterFunction: true 34 | AfterNamespace: true 35 | AfterObjCDeclaration: true 36 | AfterStruct: true 37 | AfterUnion: false 38 | AfterExternBlock: true 39 | BeforeCatch: true 40 | BeforeElse: true 41 | IndentBraces: false 42 | SplitEmptyFunction: true 43 | SplitEmptyRecord: true 44 | SplitEmptyNamespace: true 45 | BreakBeforeBraces: Custom 46 | ColumnLimit: '140' 47 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 48 | Cpp11BracedListStyle: 'false' 49 | DisableFormat: 'true' 50 | FixNamespaceComments: 'true' 51 | IncludeBlocks: Preserve 52 | IndentPPDirectives: None 53 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 54 | Language: Cpp 55 | PointerAlignment: Left 56 | ReflowComments: 'true' 57 | SortIncludes: 'false' 58 | SpaceAfterLogicalNot: 'false' 59 | SpaceBeforeAssignmentOperators: 'true' 60 | SpaceBeforeCpp11BracedList: 'true' 61 | Standard: Cpp11 62 | TabWidth: '4' 63 | UseTab: Never 64 | 65 | ... 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | # Avoid Git line ending issues by forcing everything to use LF 3 | # except Windows batch files which require CRLF. 4 | * text=auto eol=lf 5 | 6 | # Explicitly declare text files you want to always be normalized and converted 7 | # to native line endings on checkout. 8 | *.c text 9 | *.cpp text 10 | *.h text 11 | *.cmake text 12 | *.sh text 13 | *.{cmd,[cC][mM][dD]} text eol=crlf 14 | *.{bat,[bB][aA][tT]} text eol=crlf 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # The DO team will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # DO team members will be requested for review when someone 4 | # opens a pull request. Additional control can be had via 5 | # Code Review Assignment functionality on github. 6 | * @microsoft/deliveryoptimization -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # VSCode 35 | *.vscode 36 | 37 | # Visual Studio 38 | *vs/ 39 | *.vcxproj* 40 | *.sln 41 | 42 | # CMake, CPack 43 | *CMakeFiles/ 44 | *CMakeCache.txt 45 | *MakeFile* 46 | CMakeLists.txt.user 47 | CMakeCache.txt 48 | CMakeScripts 49 | Testing 50 | Makefile 51 | cmake_install.cmake 52 | install_manifest.txt 53 | compile_commands.json 54 | CTestTestfile.cmake 55 | _deps 56 | *_CPack_Packages/ 57 | *.deb 58 | out/ 59 | 60 | # Ubuntu Snap files 61 | *.snap 62 | snap/ 63 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build\\${name}", 9 | "installRoot": "${projectDir}\\out\\install\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "" 13 | }, 14 | { 15 | "name": "x64-Clang-Debug-SDK", 16 | "generator": "Ninja", 17 | "configurationType": "Debug", 18 | "buildRoot": "${projectDir}\\out\\build\\${name}", 19 | "installRoot": "${projectDir}\\out\\install\\${name}", 20 | "cmakeCommandArgs": "", 21 | "buildCommandArgs": "", 22 | "ctestCommandArgs": "", 23 | "inheritEnvironments": [ "clang_cl_x64_x64" ], 24 | "variables": [ 25 | { 26 | "name": "DO_INCLUDE_SDK", 27 | "value": "True", 28 | "type": "BOOL" 29 | }, 30 | { 31 | "name": "DO_BUILD_TESTS", 32 | "value": "False", 33 | "type": "BOOL" 34 | } 35 | ] 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 10 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | set noparent # mark this file as the root 2 | filter=-whitespace,-legal/copyright,-build/c++11,-build/include,-build/include_subdir,-runtime/references 3 | linelength=140 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /azure-pipelines/build/mac/dosdkcpp.yml: -------------------------------------------------------------------------------- 1 | # Pipeline to build the DO CPP SDK on MacOS X 2 | # Runs tests after build and publishes the binaries as artifacts 3 | 4 | trigger: 5 | branches: 6 | include: 7 | - develop 8 | paths: 9 | include: 10 | - azure-pipelines/build/mac/* 11 | - common/* 12 | - sdk-cpp/* 13 | - build/build.py 14 | - CMakeLists.txt 15 | exclude: 16 | - azure-pipelines/* 17 | - sdk-cpp/build/cleanup-install.sh 18 | 19 | pr: 20 | branches: 21 | include: 22 | - develop 23 | paths: 24 | include: 25 | - azure-pipelines/build/mac/* 26 | - common/* 27 | - sdk-cpp/* 28 | - build/build.py 29 | - CMakeLists.txt 30 | exclude: 31 | - azure-pipelines/* 32 | - sdk-cpp/build/cleanup-install.sh 33 | 34 | pool: 35 | vmImage: "macOS-latest" 36 | 37 | jobs: 38 | - job: Debug 39 | steps: 40 | - template: templates/dosdkcpp-steps.yml 41 | parameters: 42 | targetOs: osx 43 | config: debug 44 | 45 | # TODO(jimson): Support minsizerel on mac 46 | - job: Release 47 | steps: 48 | - template: templates/dosdkcpp-steps.yml 49 | parameters: 50 | targetOs: osx 51 | config: release 52 | skipTests: true 53 | -------------------------------------------------------------------------------- /azure-pipelines/build/windows/dosdkcpp.yml: -------------------------------------------------------------------------------- 1 | # Pipeline to build the DO CPP SDK on Windows 10 2 | # Runs tests after build and publishes the binaries as artifacts 3 | 4 | trigger: 5 | branches: 6 | include: 7 | - develop 8 | paths: 9 | include: 10 | - azure-pipelines/build/windows/* 11 | - common/* 12 | - sdk-cpp/* 13 | - build/build.py 14 | - CMakeLists.txt 15 | exclude: 16 | - azure-pipelines/* 17 | - sdk-cpp/build/cleanup-install.sh 18 | 19 | pr: 20 | branches: 21 | include: 22 | - develop 23 | paths: 24 | include: 25 | - azure-pipelines/build/windows/* 26 | - common/* 27 | - sdk-cpp/* 28 | - build/build.py 29 | - CMakeLists.txt 30 | exclude: 31 | - azure-pipelines/* 32 | - sdk-cpp/build/cleanup-install.sh 33 | 34 | pool: 35 | vmImage: "windows-2022" 36 | 37 | jobs: 38 | - job: Debug 39 | steps: 40 | - checkout: self 41 | submodules: true 42 | - template: templates/dosdkcpp-steps.yml 43 | parameters: 44 | targetOsArch: 'windows10-x64' 45 | config: debug 46 | 47 | - job: Release 48 | steps: 49 | - checkout: self 50 | submodules: true 51 | - template: templates/dosdkcpp-steps.yml 52 | parameters: 53 | targetOsArch: 'windows10-x64' 54 | config: release 55 | skipTests: true 56 | -------------------------------------------------------------------------------- /build/build-snaps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | set -e 7 | 8 | if [ $1 == "agent" -o $1 == "sdk-tests" ] 9 | then 10 | echo "Building $1 snap..." 11 | 12 | if [ ! -d "./snap" ] 13 | then 14 | mkdir ./snap 15 | fi 16 | 17 | if [ $1 == "agent" ] 18 | then 19 | cp ./snapcraft-options/snapcraft-agent.yaml ./snap/snapcraft.yaml 20 | 21 | else 22 | cp ./snapcraft-options/snapcraft-sdk.yaml ./snap/snapcraft.yaml 23 | fi 24 | 25 | if [ $2 -a $2 == "arm64" ] 26 | then 27 | snapcraft --target-arch=arm64 --destructive-mode --enable-experimental-target-arch 28 | else 29 | snapcraft 30 | fi 31 | 32 | else 33 | echo "$1 is not a valid snap option, valid options are 'agent' or 'sdk-tests'" 34 | fi -------------------------------------------------------------------------------- /build/docker/debian10/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 10 amd64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Alternatively, use the command line: 6 | # Copy the build script to the build directory: 7 | # cp /build/scripts/bootstrap.sh /build/docker/debian10/amd64 8 | # 9 | # After running the above, you can build the image by running in the current dockerfile directory: 10 | # sudo docker build -t debian10_amd64 . --no-cache --network=host 11 | # 12 | # Open interactive terminal into the image in a container: 13 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 14 | # Example: 15 | # sudo docker run -ti --rm --entrypoint=/bin/bash -v ~/code/do-client/:/code debian10_amd64 16 | 17 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:0e328f15f5cb0f93bff52a37d98f0bbfc40f97fcaf250096fd2b2c6871a497eb 18 | 19 | SHELL [ "/bin/bash", "-c"] 20 | 21 | COPY bootstrap.sh /tmp/bootstrap.sh 22 | 23 | WORKDIR /tmp/ 24 | 25 | RUN chmod +x bootstrap.sh 26 | RUN ./bootstrap.sh --install build 27 | 28 | VOLUME /code 29 | WORKDIR /code 30 | 31 | ENTRYPOINT [ "/bin/bash", "-c" ] 32 | 33 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 34 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 35 | # then CMD becomes the default options passed to ENTRYPOINT. 36 | # In this case we don't have any desired default arguments. 37 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 38 | CMD [ ] 39 | -------------------------------------------------------------------------------- /build/docker/debian10/arm32/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 10 arm32 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-debian10-arm32 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:27ab779c4d59ead815ff584f2e41b34a6b261db358b4cdfe664b8bfa1f494ae8 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-arm-static /build/docker/debian10/arm32 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/scripts/bootstrap.sh /build/docker/debian10/arm32 24 | # 25 | # After running the above, you can build the image by running in the current dockerfile directory 26 | # sudo docker build -t . --no-cache --network=host 27 | 28 | COPY qemu-arm-static /usr/bin/qemu-arm-static 29 | COPY bootstrap.sh /tmp/bootstrap.sh 30 | 31 | WORKDIR /tmp/ 32 | RUN chmod +x bootstrap.sh 33 | RUN ./bootstrap.sh --install build 34 | 35 | VOLUME /code 36 | WORKDIR /code 37 | 38 | ENTRYPOINT [ "/bin/bash", "-c" ] 39 | 40 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 41 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 42 | # then CMD becomes the default options passed to ENTRYPOINT. 43 | # In this case we don't have any desired default arguments. 44 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 45 | CMD [ ] 46 | -------------------------------------------------------------------------------- /build/docker/debian10/arm64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 10 arm64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-debian10-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:e774922f3043b6564c6719a7ec32f25d4b70304eff9dbd725161d4033cf38038 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-arm-static /build/docker/debian10/arm64 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/scripts/bootstrap.sh /build/docker/debian10/arm64 24 | # 25 | # open bootstrap.sh and change the function installQemu to the following: 26 | # 27 | # function installQemu 28 | # { 29 | # echo "[INFO] Installing Qemu for cross-arch support" 30 | # # Install qemu for cross-arch support 31 | # apt-get -y install qemu binfmt-support qemu-user-static 32 | # 33 | # # Register qemu with docker to more easily run cross-arch containers 34 | # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 35 | # } 36 | # After building the image yoou can return the bootstrap.sh file to the original state 37 | 38 | # After running the above, you can build the image by running in the current dockerfile directory 39 | # sudo docker build -t debian10_arm64 . --no-cache --network=host 40 | 41 | COPY qemu-arm-static /usr/bin/qemu-arm-static 42 | COPY bootstrap.sh /tmp/bootstrap.sh 43 | 44 | WORKDIR /tmp/ 45 | RUN chmod +x bootstrap.sh 46 | RUN ./bootstrap.sh --install build 47 | 48 | VOLUME /code 49 | WORKDIR /code 50 | 51 | ENTRYPOINT [ "/bin/bash", "-c" ] 52 | 53 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 54 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 55 | # then CMD becomes the default options passed to ENTRYPOINT. 56 | # In this case we don't have any desired default arguments. 57 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 58 | CMD [ ] 59 | -------------------------------------------------------------------------------- /build/docker/debian11/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 11 amd64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Alternatively, use the command line: 6 | # Copy the build script to the build directory: 7 | # cp /build/scripts/bootstrap.sh /build/docker/debian11/amd64 8 | # 9 | # After running the above, you can build the image by running in the current dockerfile directory: 10 | # sudo docker build -t debian11_amd64 . --no-cache --network=host 11 | # 12 | # Open interactive terminal into the image in a container: 13 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 14 | # Example: 15 | # sudo docker run -ti --rm --entrypoint=/bin/bash -v ~/code/do-client/:/code debian11_amd64 16 | 17 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:3b6053ca925336c804e2d3f080af177efcdc9f51198a627569bfc7c7e730ef7e 18 | 19 | SHELL [ "/bin/bash", "-c"] 20 | 21 | COPY bootstrap.sh /tmp/bootstrap.sh 22 | 23 | WORKDIR /tmp/ 24 | 25 | RUN chmod +x bootstrap.sh 26 | RUN ./bootstrap.sh --install build 27 | 28 | 29 | VOLUME /code 30 | WORKDIR /code 31 | 32 | ENTRYPOINT [ "/bin/bash", "-c" ] 33 | 34 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 35 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 36 | # then CMD becomes the default options passed to ENTRYPOINT. 37 | # In this case we don't have any desired default arguments. 38 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 39 | CMD [ ] 40 | -------------------------------------------------------------------------------- /build/docker/debian11/arm32/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 11 arm32 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-debian11-arm32 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:819b11bd0ade30fbc72f4b83593d0d126b2f3329b053495833219213fe37714d 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-arm-static /build/docker/debian11/arm32 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/scripts/bootstrap.sh /build/docker/debian11/arm32 24 | # 25 | # After running the above, you can build the image by running in the current dockerfile directory 26 | # sudo docker build -t . --no-cache --network=host 27 | 28 | COPY qemu-arm-static /usr/bin/qemu-arm-static 29 | COPY bootstrap.sh /tmp/bootstrap.sh 30 | 31 | WORKDIR /tmp/ 32 | RUN chmod +x bootstrap.sh 33 | RUN ./bootstrap.sh --install build 34 | 35 | VOLUME /code 36 | WORKDIR /code 37 | 38 | ENTRYPOINT [ "/bin/bash", "-c" ] 39 | 40 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 41 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 42 | # then CMD becomes the default options passed to ENTRYPOINT. 43 | # In this case we don't have any desired default arguments. 44 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 45 | CMD [ ] 46 | -------------------------------------------------------------------------------- /build/docker/debian11/arm64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Debian 11 arm64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-debian11-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/debian:buster@sha256:de3c0d12dd75f1a47595ff0ce78f2d30d6ca95c3ad66af06c8815d1f9b8e208d 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-arm-static /build/docker/debian11/arm64 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/scripts/bootstrap.sh /build/docker/debian11/arm64 24 | # After running the above, you can build the image by running in the current dockerfile directory 25 | # sudo docker build -t debian11_arm64 . --no-cache --network=host 26 | 27 | COPY qemu-arm-static /usr/bin/qemu-arm-static 28 | COPY bootstrap.sh /tmp/bootstrap.sh 29 | 30 | WORKDIR /tmp/ 31 | RUN chmod +x bootstrap.sh 32 | RUN ./bootstrap.sh --install build 33 | 34 | VOLUME /code 35 | WORKDIR /code 36 | 37 | ENTRYPOINT [ "/bin/bash", "-c" ] 38 | 39 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 40 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 41 | # then CMD becomes the default options passed to ENTRYPOINT. 42 | # In this case we don't have any desired default arguments. 43 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 44 | CMD [ ] -------------------------------------------------------------------------------- /build/docker/docker-build-plugin.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # $1 = path to source code 7 | # $2 = debug or release 8 | 9 | set -e 10 | 11 | echo "Building apt plugin within Docker on Linux container" 12 | 13 | echo "***********************************************" 14 | echo "**** Building & Installing sdk from source ****" 15 | echo "***********************************************" 16 | cd $1 17 | python3 build/build.py --project sdk --cmaketarget deliveryoptimization --config $2 --package-for DEB --clean 18 | pushd "/tmp/build-deliveryoptimization-sdk/linux-$2" 19 | echo "**** Installing sdk package ****" 20 | dpkg --ignore-depends=deliveryoptimization-agent -i libdeliveryoptimization*.deb 21 | popd 22 | 23 | echo "***********************************************" 24 | echo "**** Building linux-apt plugin from source ****" 25 | echo "***********************************************" 26 | python3 build/build.py --project plugin-apt --config $2 --package-for debian --clean 27 | 28 | echo "***************************************" 29 | echo "**** Installing the plugin package ****" 30 | echo "***************************************" 31 | pushd "/tmp/build-deliveryoptimization-plugin-apt/linux-$2/" 32 | dpkg -i deliveryoptimization-plugin-apt*.deb 33 | popd 34 | 35 | echo "**** Removing packages ****" 36 | dpkg -r libdeliveryoptimization-dev deliveryoptimization-plugin-apt libdeliveryoptimization 37 | -------------------------------------------------------------------------------- /build/docker/ubuntu1804/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Ubuntu 18.04 x64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\linux-debug:/build ubuntu1804-x64 9 | 10 | # The hash is the SHA256 digest of the image in the docker repository. 11 | # Login to mcr.microsoft.com and search for 'ubuntu' to see the available tags. 12 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:18.04@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98 13 | 14 | SHELL [ "/bin/bash", "-c"] 15 | 16 | # You can build the image by running in the current dockerfile directory. 17 | # Copy bootstrap.sh to the current dockerfile directory first. 18 | # sudo docker build -t . --no-cache --network=host 19 | 20 | COPY bootstrap.sh /tmp/bootstrap.sh 21 | 22 | WORKDIR /tmp/ 23 | RUN chmod +x bootstrap.sh 24 | RUN ./bootstrap.sh --install build 25 | 26 | VOLUME /code 27 | WORKDIR /code 28 | 29 | ENTRYPOINT [ "/bin/bash", "-c"] 30 | 31 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 32 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 33 | # then CMD becomes the default options passed to ENTRYPOINT. 34 | # In this case we don't have any desired default arguments. 35 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 36 | CMD [ ] 37 | -------------------------------------------------------------------------------- /build/docker/ubuntu1804/arm64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO apt plugin for linux-arm (64bit). 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-ubuntu1804-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:18.04@sha256:6a4619c02fbaf80504f316f42bd4b732831d9590e9c1c0df2b6f294ffbee86c9 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-aarch64-static /build/docker/ubuntu1804/arm64 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/scripts/bootstrap.sh build/docker/ubuntu1804/arm64 24 | # 25 | # After running the above, you can build the image by running in the current dockerfile directory 26 | # sudo docker build -t . --no-cache --network=host 27 | 28 | COPY qemu-aarch64-static /usr/bin/qemu-aarch64-static 29 | COPY bootstrap.sh /tmp/bootstrap.sh 30 | 31 | WORKDIR /tmp/ 32 | RUN chmod +x bootstrap.sh 33 | RUN ./bootstrap.sh --install build 34 | 35 | VOLUME /code 36 | WORKDIR /code 37 | 38 | ENTRYPOINT [ "/bin/bash", "-c"] 39 | 40 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 41 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 42 | # then CMD becomes the default options passed to ENTRYPOINT. 43 | # In this case we don't have any desired default arguments. 44 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 45 | CMD [ ] 46 | -------------------------------------------------------------------------------- /build/docker/ubuntu2004/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Ubuntu 20.04 amd64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-ubuntu2004-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:20.04@sha256:b2339eee806d44d6a8adc0a790f824fb71f03366dd754d400316ae5a7e3ece3e 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # You can build the image by running in the current dockerfile directory 15 | # sudo docker build -t . --no-cache --network=host 16 | 17 | # Ubuntu 20.04 requires user prompt for apt-get update command, docker has issues handling this input 18 | ENV DEBIAN_FRONTEND=noninteractive 19 | 20 | COPY bootstrap.sh /tmp/bootstrap.sh 21 | 22 | WORKDIR /tmp/ 23 | RUN chmod +x bootstrap.sh 24 | RUN ./bootstrap.sh --install build 25 | 26 | VOLUME /code 27 | WORKDIR /code 28 | 29 | ENTRYPOINT [ "/bin/bash", "-c"] 30 | 31 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 32 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 33 | # then CMD becomes the default options passed to ENTRYPOINT. 34 | # In this case we don't have any desired default arguments. 35 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 36 | CMD [ ] 37 | -------------------------------------------------------------------------------- /build/docker/ubuntu2004/arm64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Ubuntu 20.04 arm64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-ubuntu2004-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:20.04@sha256:c01a10535d9ea3e4065dee4abdb288821b2918929f1e1584287a03e731b9176c 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # QEMU is a Linux emulator which enables cross-arch support in docker 15 | # In order to build this image on a Linux host, need to install QEMU: 16 | # 17 | # sudo apt-get install qemu-user 18 | # update-binfmts --display 19 | # sudo apt install qemu binfmt-support qemu-user-static 20 | # cp /usr/bin/qemu-aarch64-static /build/docker/ubuntu2004/arm64 21 | # 22 | # Then copy the build script to the build directory 23 | # cp /build/bootstrap.sh build/docker/ubuntu2004/arm64 24 | # 25 | # After running the above, you can build the image by running in the current dockerfile directory 26 | # sudo docker build -t . --no-cache --network=host 27 | 28 | # Ubuntu 20.04 requires user prompt for apt-get update command, docker has issues handling this input 29 | ENV DEBIAN_FRONTEND=noninteractive 30 | 31 | COPY qemu-aarch64-static /usr/bin/qemu-aarch64-static 32 | COPY bootstrap.sh /tmp/bootstrap.sh 33 | 34 | WORKDIR /tmp/ 35 | RUN chmod +x bootstrap.sh 36 | RUN ./bootstrap.sh --install build 37 | 38 | VOLUME /code 39 | WORKDIR /code 40 | 41 | ENTRYPOINT [ "/bin/bash", "-c"] 42 | 43 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 44 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 45 | # then CMD becomes the default options passed to ENTRYPOINT. 46 | # In this case we don't have any desired default arguments. 47 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 48 | CMD [ ] 49 | -------------------------------------------------------------------------------- /build/docker/ubuntu2204/amd64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Ubuntu 22.04 amd64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-ubuntu2204-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:22.04@sha256:2fdb1cf4995abb74c035e5f520c0f3a46f12b3377a59e86ecca66d8606ad64f9 11 | 12 | SHELL [ "/bin/bash", "-c"] 13 | 14 | # You can build the image by running in the current dockerfile directory 15 | # sudo docker build -t . --no-cache --network=host 16 | 17 | # Ubuntu 22.04 requires user prompt for apt-get update command, docker has issues handling this input 18 | # ENV DEBIAN_FRONTEND=noninteractive 19 | 20 | COPY bootstrap.sh /tmp/bootstrap.sh 21 | 22 | WORKDIR /tmp/ 23 | RUN chmod +x bootstrap.sh 24 | RUN ./bootstrap.sh --install build 25 | 26 | VOLUME /code 27 | WORKDIR /code 28 | 29 | ENTRYPOINT [ "/bin/bash", "-c"] 30 | 31 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 32 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 33 | # then CMD becomes the default options passed to ENTRYPOINT. 34 | # In this case we don't have any desired default arguments. 35 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 36 | CMD [ ] 37 | -------------------------------------------------------------------------------- /build/docker/ubuntu2204/arm64/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building DO client components for Ubuntu 22.04 arm64 2 | # First, install the docker extension for VSCode. Then you can right-click on this file 3 | # and choose Build Image. Give it a name and it will build the image. 4 | # 5 | # Open interactive terminal into the image in a container: 6 | # docker run -ti --rm --entrypoint=/bin/bash -v :/code -v :/build 7 | # Example: 8 | # docker run -ti --rm --entrypoint=/bin/bash -v D:\do-client-lite:/code -v D:\temp\build_client_lite\arm-linux-debug:/build custom-ubuntu2204-arm64 9 | 10 | FROM mcr.microsoft.com/mirror/docker/library/ubuntu:22.04@sha256:77bdd217935d10f0e753ed84118e9b11d3ab0a66a82bdf322087354ccd833733 11 | SHELL [ "/bin/bash", "-c"] 12 | 13 | # QEMU is a Linux emulator which enables cross-arch support in docker 14 | # In order to build this image on a Linux host, need to install QEMU: 15 | # 16 | # sudo apt-get install qemu-user 17 | # update-binfmts --display 18 | # sudo apt install qemu binfmt-support qemu-user-static 19 | # cp /usr/bin/qemu-aarch64-static /build/docker/ubuntu2204/arm64 20 | # 21 | # Then copy the build script to the build directory 22 | # cp /build/bootstrap.sh build/docker/ubuntu2204/arm64 23 | # 24 | # After running the above, you can build the image by running in the current dockerfile directory 25 | # sudo docker build -t . --no-cache --network=host 26 | 27 | # Ubuntu 22.04 requires user prompt for apt-get update command, docker has issues handling this input 28 | ENV DEBIAN_FRONTEND=noninteractive 29 | 30 | COPY qemu-aarch64-static /usr/bin/qemu-aarch64-static 31 | COPY bootstrap.sh /tmp/bootstrap.sh 32 | 33 | WORKDIR /tmp/ 34 | RUN chmod +x bootstrap.sh 35 | RUN ./bootstrap.sh --install build 36 | 37 | VOLUME /code 38 | WORKDIR /code 39 | 40 | ENTRYPOINT [ "/bin/bash", "-c"] 41 | 42 | # We specify an empty command so that we can pass options to the ENTRYPOINT command. 43 | # This is a bit of a Dockerfile quirk where if the ENTRYPOINT value is defined, 44 | # then CMD becomes the default options passed to ENTRYPOINT. 45 | # In this case we don't have any desired default arguments. 46 | # However, we have to specify CMD to enable passing of command line parameters to ENTRYPOINT in the first place. 47 | CMD [ ] 48 | -------------------------------------------------------------------------------- /build/scripts/bootstrap-windows.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # NOTE: This script is a sample, partly created to detail all the dependencies required for building do-client components on windows 5 | 6 | .\install-vcpkg-deps.ps1 $env:temp\deliveryoptimization_tools 7 | 8 | # This step will require manual installation via .msi GUI 9 | $ProgressPreference = 'SilentlyContinue' 10 | Invoke-WebRequest -Uri https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-windows-x86_64.msi -OutFile "cmake-3.20.0-windows-x86_64.msi" 11 | .\cmake-3.20.0-windows-x86_64.msi 12 | 13 | $ProgressPreference = 'SilentlyContinue' 14 | Invoke-WebRequest -Uri "https://www.python.org/ftp/python/3.7.0/python-3.7.0.exe" -OutFile "python-3.7.0.exe" 15 | .\python-3.7.0.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 16 | 17 | Write-Host "Finished bootstrapping" 18 | -------------------------------------------------------------------------------- /build/scripts/check_binary_size.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | if [ $# -lt 2 ]; then 7 | echo "Usage: " 8 | exit 1 9 | fi 10 | 11 | expected_size=$1 12 | shift 13 | 14 | for p in "$@" 15 | do 16 | echo "Processing file: $p" 17 | if [ ! -f $p ]; then 18 | echo "File not found" 19 | exit 2 20 | fi 21 | actual_size=`stat -c%s $p` 22 | change_pct=`echo "scale=2; ($actual_size - $expected_size) / $expected_size * 100" | bc` 23 | echo Size check, expected = $expected_size, actual = $actual_size, change pct = $change_pct% 24 | change_pct=$( printf "%.0f" $change_pct ) 25 | if [ $change_pct -ge 2 ]; then 26 | echo "[FAIL] Size increased beyond threshold. Adjust expected_size if this is expected." 27 | exit 3 28 | elif [ $change_pct -le -5 ]; then 29 | echo "[WARNING] Size reduced beyond threshold. Adjust expected_size if this is expected." 30 | exit 4 31 | fi 32 | done 33 | -------------------------------------------------------------------------------- /build/scripts/install-vcpkg-deps.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | if (-not (Test-Path "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe")) 5 | { 6 | Write-Host "vcpkg not found in $env:VCPKG_INSTALLATION_ROOT" 7 | exit 1 8 | } 9 | 10 | cd $env:VCPKG_INSTALLATION_ROOT 11 | git checkout 2021.05.12 12 | .\bootstrap-vcpkg.bat 13 | .\vcpkg integrate install 14 | .\vcpkg update 15 | .\vcpkg install gtest:x64-windows 16 | .\vcpkg install boost-program-options:x64-windows 17 | -------------------------------------------------------------------------------- /build/scripts/install-vcpkg-deps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # $1 = path to vcpkg installation directory 7 | 8 | set -e 9 | 10 | rm -rf $1 11 | mkdir $1 12 | cd $1 13 | git clone https://github.com/microsoft/vcpkg 14 | cd vcpkg 15 | git checkout 2021.05.12 16 | ./bootstrap-vcpkg.sh 17 | ./vcpkg integrate install 18 | 19 | ./vcpkg install ms-gsl 20 | ./vcpkg install boost-uuid 21 | ./vcpkg install boost-program-options 22 | ./vcpkg install gtest 23 | 24 | -------------------------------------------------------------------------------- /client-lite/build/postinst.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | do_group_name=@do_group@ 7 | do_user_name=@do_user@ 8 | config_path=@docs_svc_config_dir_path@ 9 | log_path=@docs_svc_log_dir_path@ 10 | svc_name=@docs_svc_name@ 11 | svc_config_path=@docs_systemd_cfg_path@ 12 | svc_bin_path=@docs_svc_bin_path@ 13 | 14 | # Exit early to fail the install if any command here fails 15 | set -e 16 | 17 | echo "**** Running post-install script for $svc_name, args: $1 $2 ****" 18 | 19 | if [ ! -f $svc_bin_path ]; then echo "Agent binary cannot be found at $svc_bin_path"; exit 1; fi 20 | 21 | if ! getent group $do_group_name > /dev/null; then 22 | echo "Creating group '$do_group_name'" 23 | addgroup --system $do_group_name 24 | fi 25 | 26 | if ! getent passwd $do_user_name > /dev/null; then 27 | echo "Creating user '$do_user_name'" 28 | adduser --system $do_user_name --ingroup $do_group_name --shell /bin/false 29 | fi 30 | 31 | # Add each admin user to the do group - for systems installed before Ubuntu 12.04 LTS (Precise Pangolin) 32 | # Use sed to parse the user name from a string in the form group:passwd:gid:member 33 | for u in $(getent group admin | sed -e "s/^.*://" -e "s/,/ /g"); do 34 | adduser "$u" $do_group_name > /dev/null || true 35 | done 36 | 37 | # Add each sudo user to the do group 38 | # Use sed to parse the user name from a string in the form group:passwd:gid:member 39 | for u in $(getent group sudo | sed -e "s/^.*://" -e "s/,/ /g"); do 40 | adduser "$u" $do_group_name > /dev/null || true 41 | done 42 | 43 | configure_dir() { 44 | local dir_path="$1" 45 | echo "Configuring dir: $dir_path" 46 | if [ ! -d "$dir_path" ]; then 47 | mkdir "$dir_path" 48 | fi 49 | chgrp -R $do_group_name "$dir_path" 50 | chown $do_user_name "$dir_path" 51 | } 52 | 53 | configure_dir "$config_path" 54 | # group needs write permission to config path (SDK writing configs) 55 | chmod g+w $config_path 56 | configure_dir "$log_path" 57 | 58 | # See https://www.freedesktop.org/software/systemd/man/systemd.directives.html 59 | echo "Installing $svc_name" 60 | cat > ${svc_config_path} << EOF 61 | [Unit] 62 | Description=$svc_name: Performs content delivery optimization tasks 63 | 64 | [Service] 65 | ExecStart=$svc_bin_path 66 | Restart=on-failure 67 | 68 | [Install] 69 | WantedBy=multi-user.target 70 | EOF 71 | 72 | echo "Service conf stored at: $svc_config_path" 73 | echo "Service bin located at: $svc_bin_path" 74 | echo "Reloading systemd daemon list and enabling $svc_name" 75 | systemctl daemon-reload 76 | systemctl enable $svc_name 77 | # Installed/upgraded daemon; remove from the failed services list. No-op if never failed earlier. 78 | systemctl reset-failed $svc_name 79 | systemctl stop $svc_name > /dev/null # stop if already running 80 | systemctl start $svc_name 81 | echo "**** Done! ****" 82 | -------------------------------------------------------------------------------- /client-lite/build/postrm.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | do_group_name=@do_group@ 7 | do_user_name=@do_user@ 8 | config_path=@docs_svc_config_dir_path@ 9 | log_path=@docs_svc_log_dir_path@ 10 | run_path=@docs_svc_run_dir_path@ 11 | svc_name=@docs_svc_name@ 12 | svc_config_path=@docs_systemd_cfg_path@ 13 | 14 | echo "**** Running post-removal script for $svc_name, args: $1 $2 ****" 15 | 16 | do_remove_daemon() { 17 | echo "Removing systemd unit file: $svc_config_path" 18 | rm $svc_config_path 19 | 20 | echo "Reloading daemons" 21 | systemctl daemon-reload 22 | } 23 | 24 | do_remove_dirs() { 25 | echo "Removing log directory: $log_path" 26 | rm -rf $log_path 27 | 28 | echo "Removing run directory: $run_path" 29 | rm -rf $run_path 30 | } 31 | 32 | do_remove_user_and_group() { 33 | echo "Removing user: ${do_user_name}" 34 | userdel ${do_user_name} 35 | 36 | echo "Removing group: ${do_group_name}" 37 | groupdel ${do_group_name} 38 | } 39 | 40 | do_remove_tasks() { 41 | do_remove_daemon 42 | do_remove_dirs 43 | do_remove_user_and_group 44 | } 45 | 46 | case "$1" in 47 | remove) 48 | do_remove_tasks 49 | ;; 50 | 51 | purge) 52 | do_remove_tasks 53 | 54 | echo "Removing config directory: $config_path" 55 | rm -rf $config_path 56 | ;; 57 | 58 | # upgrade: Removing user/group/directories during an upgrade is a mistake that can impact group membership, historical logs, etc. 59 | # abort-install, abort-upgrade: This project does not use any package install path that requires handling these cases. 60 | upgrade|abort-install|abort-upgrade) 61 | ;; 62 | esac 63 | 64 | echo "**** Done! ****" 65 | -------------------------------------------------------------------------------- /client-lite/build/prerm.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | svc_name=@docs_svc_name@ 7 | 8 | echo "**** Running pre-removal script for $svc_name, args: $1 $2 ****" 9 | 10 | echo "Stopping and disabling service $svc_name" 11 | systemctl stop $svc_name 12 | systemctl disable $svc_name 13 | 14 | echo "**** Done! ****" 15 | -------------------------------------------------------------------------------- /client-lite/src/config/config_defaults.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | constexpr auto g_mccHostBanInterval = std::chrono::minutes(15); 9 | constexpr auto g_progressTrackerCheckInterval = std::chrono::seconds(10); 10 | constexpr UINT g_progressTrackerMaxNoProgressIntervals = 30; 11 | constexpr auto g_progressTrackerMaxRetryDelay = std::chrono::seconds(30); 12 | 13 | // Provides a wait time of ~49 days which should be sufficient for uses 14 | // of steady_clock (timing, event waits, condition_variable waits). 15 | constexpr auto g_steadyClockInfiniteWaitTime = std::chrono::milliseconds(MAXUINT); 16 | 17 | const char* const ConfigName_AduIoTConnectionString = "ADUC_IoTConnectionString"; 18 | 19 | const char* const ConfigName_CacheHostFallbackDelayBgSecs = "DODelayCacheServerFallbackBackground"; 20 | const char* const ConfigName_CacheHostFallbackDelayFgSecs = "DODelayCacheServerFallbackForeground"; 21 | constexpr auto g_cacheHostFallbackDelayNoFallback = std::chrono::seconds(-1); // fallback to CDN not allowed 22 | 23 | const char* const ConfigName_CacheHostServer = "DOCacheHost"; 24 | 25 | const char* const ConfigName_RestControllerValidateRemoteAddr = "RestControllerValidateRemoteAddr"; 26 | constexpr auto g_RestControllerValidateRemoteAddrDefault = true; // default: enabled 27 | -------------------------------------------------------------------------------- /client-lite/src/config/config_manager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "config_manager.h" 6 | 7 | #include "config_defaults.h" 8 | #include "do_persistence.h" 9 | #include "string_ops.h" 10 | 11 | ConfigManager::ConfigManager() : 12 | ConfigManager(docli::GetAdminConfigFilePath(), docli::GetSDKConfigFilePath()) 13 | { 14 | } 15 | 16 | // Used by unit tests to override config paths 17 | ConfigManager::ConfigManager(const std::string& adminConfigPath, const std::string& sdkConfigPath) : 18 | _adminConfigs(adminConfigPath, true), 19 | _sdkConfigs(sdkConfigPath) 20 | { 21 | } 22 | 23 | void ConfigManager::RefreshAdminConfigs() 24 | { 25 | _adminConfigs.Refresh(); 26 | } 27 | 28 | boost::optional ConfigManager::CacheHostFallbackDelay() 29 | { 30 | boost::optional returnValue; 31 | 32 | // We don't yet differentiate between background and foreground downloads, so check both configs 33 | boost::optional delay = _adminConfigs.Get(ConfigName_CacheHostFallbackDelayBgSecs); 34 | if (!delay) 35 | { 36 | delay = _adminConfigs.Get(ConfigName_CacheHostFallbackDelayFgSecs); 37 | } 38 | 39 | if (delay) 40 | { 41 | returnValue = std::chrono::seconds(delay.get()); 42 | } 43 | return returnValue; 44 | } 45 | 46 | boost::optional ConfigManager::CacheHostServer() 47 | { 48 | boost::optional cacheHostServer = _adminConfigs.Get(ConfigName_CacheHostServer); 49 | return cacheHostServer; 50 | } 51 | 52 | std::string ConfigManager::IoTConnectionString() 53 | { 54 | boost::optional connectionString = _sdkConfigs.Get(ConfigName_AduIoTConnectionString); 55 | return boost::get_optional_value_or(connectionString, std::string{}); 56 | } 57 | 58 | bool ConfigManager::RestControllerValidateRemoteAddr() 59 | { 60 | boost::optional validateRemoteAddr = _adminConfigs.Get(ConfigName_RestControllerValidateRemoteAddr); 61 | return boost::get_optional_value_or(validateRemoteAddr, g_RestControllerValidateRemoteAddrDefault); 62 | } 63 | -------------------------------------------------------------------------------- /client-lite/src/config/config_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "do_json_parser.h" 8 | 9 | class ConfigManager 10 | { 11 | public: 12 | ConfigManager(); 13 | ConfigManager(const std::string& adminConfigPath, const std::string& sdkConfigPath); 14 | 15 | void RefreshAdminConfigs(); 16 | 17 | boost::optional CacheHostFallbackDelay(); 18 | boost::optional CacheHostServer(); 19 | std::string IoTConnectionString(); 20 | bool RestControllerValidateRemoteAddr(); 21 | 22 | private: 23 | JsonParser _adminConfigs; 24 | JsonParser _sdkConfigs; 25 | }; 26 | -------------------------------------------------------------------------------- /client-lite/src/config/mcc_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class ConfigManager; 11 | 12 | class MCCManager 13 | { 14 | public: 15 | MCCManager(ConfigManager& configManager); 16 | 17 | boost::optional FallbackDelay(); 18 | std::string GetHost(); 19 | void ReportHostError(HRESULT hr, UINT httpStatusCode, const std::string& mccHost, const std::string& originalUrl); 20 | bool IsBanned(const std::string& mccHost, const std::string& originalUrl) const; 21 | 22 | private: 23 | // MCC host can be banned for all original hosts (connection failures) or per host (not part of MCC allow list). 24 | // MccHost class is responsible for keeping track of this. 25 | class MccHost 26 | { 27 | public: 28 | MccHost(const std::string& address); 29 | 30 | void Ban(std::chrono::seconds banInterval); 31 | void BanForOriginalHost(const std::string& originalHost, std::chrono::seconds banInterval); 32 | 33 | const std::string& Address() const noexcept { return _address; } 34 | bool IsBanned(const std::string& originalHost) const; 35 | bool operator==(const std::string& otherMccAddress) const noexcept { return (_address == otherMccAddress); } 36 | 37 | private: 38 | struct OriginalHostStatus 39 | { 40 | std::string host; 41 | std::chrono::steady_clock::time_point timeOfUnban; 42 | 43 | OriginalHostStatus(const std::string& host, std::chrono::steady_clock::time_point timeOfUnban) : 44 | host(host), 45 | timeOfUnban(timeOfUnban) 46 | { 47 | } 48 | 49 | bool operator==(const std::string& otherHost) const noexcept { return (host == otherHost); } 50 | }; 51 | 52 | std::string _address; 53 | std::chrono::steady_clock::time_point _timeOfUnban; 54 | std::vector _timeOfUnbanForOriginalHosts; 55 | }; 56 | 57 | ConfigManager& _configManager; 58 | std::vector _mccHosts; 59 | }; 60 | -------------------------------------------------------------------------------- /client-lite/src/config/network_monitor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "network_monitor.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | bool NetworkMonitor::HasViableInterface() 12 | { 13 | struct ifaddrs* ifaddr; 14 | if (getifaddrs(&ifaddr) == -1) 15 | { 16 | DoLogError("getifaddrs() failed, errno: %d", errno); 17 | return true; 18 | } 19 | 20 | // Assume network connectivity is available if there is at least one network interface 21 | // that has an IPv4/IPv6 address, is running and not a loopback interface. 22 | // TODO(shishirb): Look into NetworkManager dbus API if needed (ability to distinguish among 23 | // local/portal/internet connectivity, or in case of false detections with current logic). 24 | for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) 25 | { 26 | if (ifa->ifa_addr == nullptr) 27 | { 28 | continue; 29 | } 30 | 31 | int family = ifa->ifa_addr->sa_family; 32 | if ((family != AF_INET) && (family != AF_INET6)) 33 | { 34 | continue; 35 | } 36 | 37 | if ((ifa->ifa_flags & IFF_RUNNING) 38 | && !(ifa->ifa_flags & IFF_LOOPBACK)) 39 | { 40 | DoLogInfo("Viable network interface detected: %s, family: %d%s, flags: 0x%x.", 41 | ifa->ifa_name, family, 42 | (family == AF_INET) ? " (AF_INET)" : 43 | (family == AF_INET6) ? " (AF_INET6)" : "", 44 | ifa->ifa_flags); 45 | freeifaddrs(ifaddr); 46 | return true; 47 | } 48 | } 49 | 50 | DoLogWarning("No viable network interface"); 51 | freeifaddrs(ifaddr); 52 | return false; 53 | } 54 | -------------------------------------------------------------------------------- /client-lite/src/config/network_monitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | class NetworkMonitor 7 | { 8 | public: 9 | static bool HasViableInterface(); 10 | }; 11 | -------------------------------------------------------------------------------- /client-lite/src/download/download_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include "do_curl_wrappers.h" 9 | #include "mcc_manager.h" 10 | #include "task_thread.h" 11 | 12 | enum class DownloadProperty; 13 | class Download; 14 | struct DownloadStatus; 15 | 16 | class DownloadManager 17 | { 18 | friend std::shared_ptr DownloadForId(const DownloadManager& manager, const std::string& id); 19 | 20 | public: 21 | DownloadManager(ConfigManager& config); 22 | 23 | std::string CreateDownload(std::string url = {}, std::string destFilePath = {}); 24 | 25 | void StartDownload(const std::string& downloadId) const; 26 | void PauseDownload(const std::string& downloadId) const; 27 | void FinalizeDownload(const std::string& downloadId); 28 | void AbortDownload(const std::string& downloadId); 29 | void SetDownloadProperty(const std::string& downloadId, DownloadProperty key, const std::string& value); 30 | std::string GetDownloadProperty(const std::string& downloadId, DownloadProperty key) const; 31 | DownloadStatus GetDownloadStatus(const std::string& downloadId) const; 32 | 33 | bool IsIdle() const; 34 | void RefreshAdminConfigs() const; 35 | 36 | private: 37 | mutable TaskThread _taskThread; 38 | std::unordered_map> _downloads; 39 | mutable bool _fRunning { true }; 40 | mutable std::shared_timed_mutex _downloadsMtx; 41 | 42 | ConfigManager& _config; 43 | CurlRequests _curlOps; 44 | MCCManager _mccManager; 45 | 46 | private: 47 | std::shared_ptr _GetDownload(const std::string& downloadId) const; 48 | }; 49 | -------------------------------------------------------------------------------- /client-lite/src/download/download_progress_tracker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "config_defaults.h" 8 | 9 | class DownloadProgressTracker 10 | { 11 | public: 12 | bool CheckProgress(UINT64 newBytesTransferred, UINT maxNoProgressIntervals = g_progressTrackerMaxNoProgressIntervals) 13 | { 14 | DO_ASSERT(newBytesTransferred >= _lastSeenBytesTransferred); 15 | 16 | if (newBytesTransferred > _lastSeenBytesTransferred) 17 | { 18 | _numNoProgressIntervals = 0; 19 | _lastSeenBytesTransferred = newBytesTransferred; 20 | } 21 | else 22 | { 23 | _numNoProgressIntervals++; 24 | } 25 | DoLogInfo("Bytes transferred so far: %llu, no-progress intervals: [cur %u, max %u]", 26 | _lastSeenBytesTransferred, _numNoProgressIntervals, maxNoProgressIntervals); 27 | return (_numNoProgressIntervals >= maxNoProgressIntervals); 28 | } 29 | 30 | void OnDownloadFailure() 31 | { 32 | _nextRetryDelay = std::min(g_progressTrackerMaxRetryDelay, _nextRetryDelay * 2); 33 | } 34 | 35 | // Forget the count of no-progress until now, retry delay will start from minimum next time 36 | void Reset() 37 | { 38 | ResetRetryDelay(); 39 | _numNoProgressIntervals = 0; 40 | } 41 | 42 | void ResetRetryDelay() 43 | { 44 | _nextRetryDelay = std::chrono::seconds(1); 45 | } 46 | 47 | auto NextRetryDelay() const 48 | { 49 | return _nextRetryDelay; 50 | } 51 | 52 | private: 53 | std::chrono::seconds _nextRetryDelay { 1 }; 54 | UINT _numNoProgressIntervals { 0 }; 55 | UINT64 _lastSeenBytesTransferred { 0 }; 56 | }; 57 | -------------------------------------------------------------------------------- /client-lite/src/download/download_status.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | enum class DownloadState 7 | { 8 | Created, 9 | Transferring, 10 | Transferred, 11 | Finalized, 12 | Aborted, 13 | Paused, 14 | }; 15 | 16 | struct DownloadStatus 17 | { 18 | friend class Download; 19 | 20 | UINT64 BytesTotal { 0 }; 21 | UINT64 BytesTransferred { 0 }; 22 | DownloadState State { DownloadState::Created }; 23 | HRESULT Error { S_OK }; 24 | HRESULT ExtendedError { S_OK }; 25 | 26 | bool IsTransientError() const noexcept 27 | { 28 | return (State == DownloadState::Paused) && (Error == S_OK) && FAILED(ExtendedError); 29 | } 30 | 31 | private: 32 | void _Transferring() 33 | { 34 | State = DownloadState::Transferring; 35 | Error = S_OK; 36 | ExtendedError = S_OK; 37 | } 38 | void _Paused(HRESULT hrError = S_OK, HRESULT hrExtendedError = S_OK) 39 | { 40 | State = DownloadState::Paused; 41 | Error = hrError; 42 | ExtendedError = hrExtendedError; 43 | } 44 | void _Transferred() 45 | { 46 | State = DownloadState::Transferred; 47 | } 48 | void _Finalized() 49 | { 50 | State = DownloadState::Finalized; 51 | } 52 | void _Aborted() 53 | { 54 | State = DownloadState::Aborted; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /client-lite/src/include/basic_types.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #if defined(__x86_64__) || defined(_M_X64) || defined(_M_ARM64) || defined(__aarch64__) 7 | #define DO_ENV_64BIT 8 | #endif 9 | 10 | using INT8 = signed char; 11 | using UINT8 = unsigned char; 12 | using INT16 = signed short; 13 | using UINT16 = unsigned short; 14 | using INT32 = signed int; 15 | using UINT32 = unsigned int; 16 | using INT64 = long long signed int; 17 | using UINT64 = long long unsigned int; 18 | 19 | using BYTE = UINT8; 20 | 21 | using INT = INT32; 22 | using UINT = UINT32; 23 | 24 | // We define HRESULT to be a signed 32bit integer to match Windows. 25 | // Note: Can't use long because hexadecimal literals are forced to be unsigned on GCC, per the standard. 26 | // That is, (long)0x80070490L != -2147023728 and ((long)0x80070490L < 0) evaluates to false. 27 | using HRESULT = INT32; 28 | 29 | #ifdef DO_ENV_64BIT 30 | typedef INT64 INT_PTR; 31 | typedef UINT64 UINT_PTR; 32 | #else 33 | typedef int INT_PTR; 34 | typedef unsigned int UINT_PTR; 35 | #endif 36 | 37 | typedef int BOOL; 38 | 39 | typedef char CHAR, *PSTR; 40 | typedef const char *PCSTR; 41 | 42 | #ifndef FALSE 43 | #define FALSE 0 44 | #endif 45 | 46 | #ifndef TRUE 47 | #define TRUE 1 48 | #endif 49 | 50 | #ifndef MAXUINT32 51 | #define MAXUINT32 ((UINT32)~((UINT32)0)) 52 | #define MAXUINT64 ((UINT64)~((UINT64)0)) 53 | #define MAXUINT ((UINT)~((UINT)0)) 54 | #endif 55 | -------------------------------------------------------------------------------- /client-lite/src/include/do_assert.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "do_log.h" 8 | 9 | #ifdef DEBUG 10 | 11 | #define DO_ASSERTMSG(_msg, _exp) \ 12 | ((!(_exp)) ? \ 13 | DOLog::Write(EVENT_LEVEL_ERROR, __FUNCTION__, __LINE__, "Assert (%s): %s", #_exp, _msg), \ 14 | assert(_exp), TRUE \ 15 | : TRUE) 16 | 17 | #else // !DEBUG 18 | 19 | #define DO_ASSERTMSG(_msg, _exp) 20 | 21 | #endif // DEBUG 22 | 23 | #define DO_ASSERT(_exp) DO_ASSERTMSG("Failed", _exp) 24 | -------------------------------------------------------------------------------- /client-lite/src/include/do_common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | // Note: Secure C string functions like wcscat_s, swscanf_s require __STDC_WANT_LIB_EXT1__ 7 | // to be defined prior to including stdio.h. However GCC hasn't implemented these functions 8 | // yet so we conditionally compile with __STDC_LIB_EXT1__ currently. 9 | // On the windows side, these are always available and we use __STDC_SECURE_LIB__ to test 10 | // for presence of these functions. 11 | #ifndef __STDC_WANT_LIB_EXT1__ 12 | #define __STDC_WANT_LIB_EXT1__ 1 13 | #endif 14 | 15 | #include // size_t 16 | #include 17 | #include 18 | 19 | #if !defined(DEBUG) && !defined(NDEBUG) 20 | #define DEBUG 21 | #endif 22 | 23 | #ifndef ARRAYSIZE 24 | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) 25 | #endif 26 | 27 | #ifndef INTERNET_DEFAULT_PORT 28 | #define INTERNET_DEFAULT_PORT 0 // use the protocol-specific default 29 | #endif 30 | 31 | #include "sal_undef.h" 32 | 33 | // Assign the given value to an optional output parameter. 34 | // Makes code more concise by removing trivial if (outParam) blocks. 35 | template 36 | inline void assign_to_opt_param(_Out_opt_ T* outParam, T val) 37 | { 38 | if (outParam != nullptr) 39 | { 40 | *outParam = val; 41 | } 42 | } 43 | 44 | #include "basic_types.h" 45 | #include "error_macros.h" // required by headers below 46 | #include "do_assert.h" 47 | #include "hresult_helpers.h" 48 | #include "do_log.h" 49 | -------------------------------------------------------------------------------- /client-lite/src/include/do_error.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef __DELIVERYOPTIMIZATION_ERROR_H__ 5 | #define __DELIVERYOPTIMIZATION_ERROR_H__ 6 | 7 | // Definitions of DeliveryOptimization error codes 8 | // 9 | // DO error codes can be identified by the 0x80D0 prefix. 10 | // 11 | // Error codes used to be separated into zones with a macro identifying each zone. 12 | // Currently, we only care about the transient error zone so only this macro is defined. 13 | // The error codes are still separated with sufficient buffer left to add errors in each zone. 14 | 15 | #define DO_ZONE_MASK 0xF800 16 | #define DO_TRANSIENT_ZONE 0x3800 17 | 18 | #define DO_E_NO_SERVICE HRESULT(0x80D01001L) // Delivery Optimization was unable to provide the service 19 | 20 | // Download job codes 21 | 22 | #define DO_E_DOWNLOAD_NO_PROGRESS HRESULT(0x80D02002L) // Download of a file saw no progress within the defined period 23 | #define DO_E_UNKNOWN_PROPERTY_ID HRESULT(0x80D02011L) // SetProperty() or GetProperty() called with an unknown property ID 24 | #define DO_E_READ_ONLY_PROPERTY HRESULT(0x80D02012L) // Unable to call SetProperty() on a read-only property 25 | #define DO_E_INVALID_STATE HRESULT(0x80D02013L) // The requested action is not allowed in the current job state. The job might have been canceled or completed transferring. It is in a read-only state now. 26 | #define DO_E_FILE_DOWNLOADSINK_UNSPECIFIED HRESULT(0x80D02018L) // Unable to start a download because no download sink (either local file or stream interface) was specified 27 | #define DO_E_INSUFFICIENT_RANGE_SUPPORT HRESULT(0x80D05011L) // The server does not support the necessary HTTP Range protocol header. 28 | 29 | // IDODownload interface 30 | 31 | #define DO_E_DOWNLOAD_NO_URI HRESULT(0x80D02200L) // The download was started without providing a URI 32 | 33 | // Transient conditions 34 | 35 | #define DO_E_BLOCKED_BY_NO_NETWORK HRESULT(0x80D03805L) // Download paused due to loss of network connectivity 36 | 37 | #endif // __DELIVERYOPTIMIZATION_ERROR_H__ 38 | -------------------------------------------------------------------------------- /client-lite/src/include/do_filesystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_FILESYSTEM_H 5 | #define _DELIVERY_OPTIMIZATION_DO_FILESYSTEM_H 6 | 7 | #if defined(__cpp_lib_filesystem) 8 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 9 | #elif defined(__cpp_lib_experimental_filesystem) 10 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 11 | #elif !defined(__has_include) 12 | // Cannot check if headers exist, assume experimental 13 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 14 | #elif __has_include() 15 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 16 | #elif __has_include() 17 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 18 | #else 19 | #error Could not find system header "" or "" 20 | #endif 21 | 22 | #if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 23 | #include 24 | namespace fs = std::experimental::filesystem; 25 | #else 26 | #include 27 | namespace fs = std::filesystem; 28 | #endif 29 | 30 | #endif // _DELIVERY_OPTIMIZATION_DO_FILESYSTEM_H 31 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_api_params.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "rest_api_params.h" 6 | 7 | #include "string_ops.h" 8 | 9 | #define INSERT_REST_API_PARAM(_p) RestApiParameters::_p, #_p 10 | 11 | // Maintain the same order as in RestApiParameters 12 | const RestApiParam RestApiParam::_knownParams[] = 13 | { 14 | { INSERT_REST_API_PARAM(Id), DownloadProperty::Id, RestApiParamTypes::String }, 15 | { INSERT_REST_API_PARAM(Uri), DownloadProperty::Uri, RestApiParamTypes::String }, 16 | { INSERT_REST_API_PARAM(DownloadFilePath), DownloadProperty::LocalPath, RestApiParamTypes::String }, 17 | { INSERT_REST_API_PARAM(NoProgressTimeoutSeconds), DownloadProperty::NoProgressTimeoutSeconds, RestApiParamTypes::UInt }, 18 | { INSERT_REST_API_PARAM(PropertyKey), DownloadProperty::Invalid, RestApiParamTypes::String }, 19 | }; 20 | 21 | const RestApiParam& RestApiParam::Lookup(RestApiParameters paramId) noexcept 22 | { 23 | return _knownParams[static_cast(paramId)]; 24 | } 25 | 26 | const RestApiParam* RestApiParam::Lookup(const char* stringId) noexcept 27 | { 28 | for (const auto& param : _knownParams) 29 | { 30 | if (StringCompareCaseInsensitive(param.stringId, stringId) == 0) 31 | { 32 | return ¶m; 33 | } 34 | } 35 | DoLogWarning("%s is not a known REST API param", stringId); 36 | return nullptr; 37 | } 38 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_api_params.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "download.h" 7 | 8 | enum class RestApiParameters 9 | { 10 | Id, 11 | Uri, 12 | DownloadFilePath, 13 | NoProgressTimeoutSeconds, 14 | PropertyKey, 15 | }; 16 | 17 | enum class RestApiParamTypes 18 | { 19 | UInt, 20 | String, 21 | }; 22 | 23 | struct RestApiParam 24 | { 25 | RestApiParameters paramId; 26 | const char* stringId; 27 | DownloadProperty downloadPropertyId; 28 | RestApiParamTypes type; 29 | 30 | static const RestApiParam& Lookup(RestApiParameters paramId) noexcept; 31 | static const RestApiParam* Lookup(const char* stringId) noexcept; 32 | 33 | bool IsUnknownDownloadPropertyId() const { return (downloadPropertyId == DownloadProperty::Invalid); } 34 | 35 | private: 36 | static const RestApiParam _knownParams[]; 37 | }; 38 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_api_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "do_http_packet.h" 8 | #include "rest_api_params.h" 9 | 10 | enum class RestApiMethods 11 | { 12 | Create, 13 | Enumerate, 14 | Start, 15 | Pause, 16 | Finalize, 17 | Abort, 18 | GetStatus, 19 | GetProperty, 20 | SetProperty, 21 | }; 22 | 23 | class RestApiParser 24 | { 25 | public: 26 | using query_data_t = std::map; 27 | 28 | static std::string ParamToString(RestApiParameters param); 29 | 30 | RestApiParser(const std::shared_ptr& request); 31 | 32 | RestApiMethods Method(); 33 | const std::string* QueryStringParam(RestApiParameters param); 34 | const query_data_t& Query() { return _QueryParams(); } 35 | 36 | std::string GetStringParam(RestApiParameters param); 37 | 38 | private: 39 | void _ParseQueryString(); 40 | const query_data_t& _QueryParams(); 41 | 42 | private: 43 | std::shared_ptr _request; 44 | 45 | // All further data members are lazy-init 46 | RestApiMethods _method; 47 | query_data_t _queryData; 48 | bool _methodInitialized { false }; 49 | bool _queryDataInitialized { false }; 50 | }; 51 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_api_request.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include "do_http_packet.h" 9 | #include "rest_api_parser.h" 10 | 11 | class DownloadManager; 12 | 13 | class IRestApiRequest 14 | { 15 | public: 16 | virtual ~IRestApiRequest() = default; 17 | virtual HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) = 0; 18 | }; 19 | 20 | class RestApiRequestBase 21 | { 22 | public: 23 | RestApiRequestBase(const std::shared_ptr& clientRequest); 24 | HRESULT Process(DownloadManager& downloadManager, boost::property_tree::ptree& responseBody); 25 | 26 | private: 27 | std::unique_ptr _apiRequest; 28 | RestApiParser _parser; 29 | }; 30 | 31 | class RestApiCreateRequest : public IRestApiRequest 32 | { 33 | private: 34 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 35 | }; 36 | 37 | class RestApiEnumerateRequest : public IRestApiRequest 38 | { 39 | private: 40 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 41 | }; 42 | 43 | class RestApiDownloadStateChangeRequest : public IRestApiRequest 44 | { 45 | private: 46 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 47 | }; 48 | 49 | class RestApiGetStatusRequest : public IRestApiRequest 50 | { 51 | private: 52 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 53 | }; 54 | 55 | class RestApiGetPropertyRequest : public IRestApiRequest 56 | { 57 | private: 58 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 59 | }; 60 | 61 | class RestApiSetPropertyRequest : public IRestApiRequest 62 | { 63 | private: 64 | HRESULT ParseAndProcess(DownloadManager& downloadManager, RestApiParser& parser, boost::property_tree::ptree& responseBody) override; 65 | }; 66 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_http_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "rest_http_listener.h" 7 | #include "waitable_counter.h" 8 | 9 | class ConfigManager; 10 | class DownloadManager; 11 | class RestApiRequestBase; 12 | 13 | // Controller for the REST-over-HTTP interface in DO client. 14 | // This interface is used as the inter-process communication 15 | // mechanism for our clients to create and manage download requests. 16 | class RestHttpController 17 | { 18 | public: 19 | RestHttpController(ConfigManager& config, std::shared_ptr downloadManager); 20 | ~RestHttpController(); 21 | 22 | void Start(boost::asio::io_service& ioService); 23 | std::string ServerEndpoint() const; 24 | uint16_t Port() const; 25 | 26 | private: 27 | void _HttpListenerCallback(const std::shared_ptr& packet, 28 | HttpListenerConnection& conn); 29 | static void _OnFailure(HttpListenerConnection& conn, HRESULT hr); 30 | static UINT _HttpStatusFromHRESULT(HRESULT hr); 31 | 32 | private: 33 | ConfigManager& _config; 34 | std::shared_ptr _downloadManager; 35 | RestHttpListener _listener; 36 | WaitableCounter _callTracker; 37 | }; 38 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_http_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include "rest_http_listener_conn.h" 9 | 10 | class RestHttpListener 11 | { 12 | public: 13 | void Start(boost::asio::io_service& ioService, const http_listener_callback_t& requestHandler); 14 | void Stop(); 15 | std::string Endpoint() const; 16 | uint16_t Port() const; 17 | 18 | private: 19 | void _BeginAccept(); 20 | void _ProcessConnection(const std::shared_ptr& socket); 21 | 22 | std::unique_ptr _listener; 23 | http_listener_callback_t _requestHandler; 24 | boost::asio::io_service* _io { nullptr }; 25 | 26 | std::atomic _numConnections { 0 }; 27 | }; 28 | -------------------------------------------------------------------------------- /client-lite/src/ipc/rest_http_listener_conn.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include "do_http_parser.h" 9 | 10 | class HttpListenerConnection; 11 | using http_listener_callback_t = std::function&, 12 | HttpListenerConnection&)>; 13 | 14 | class HttpListenerConnection : public std::enable_shared_from_this 15 | { 16 | public: 17 | HttpListenerConnection(boost::asio::io_service& ioService, std::shared_ptr socket); 18 | ~HttpListenerConnection(); 19 | 20 | static std::shared_ptr Make(boost::asio::io_service& ioService, 21 | std::shared_ptr socket); 22 | 23 | void Receive(http_listener_callback_t& callback); 24 | void Reply(unsigned int statusCode); 25 | void Reply(unsigned int statusCode, const std::string& body); 26 | 27 | boost::asio::ip::tcp::endpoint RemoteEndpoint() const; 28 | 29 | private: 30 | void _OnData(const boost::system::error_code& ec, size_t cbRead, http_listener_callback_t& callback); 31 | 32 | std::shared_ptr _socket; 33 | boost::asio::io_service& _io; 34 | 35 | std::vector _recvBuf; 36 | microsoft::deliveryoptimization::details::HttpParser _httpParser; 37 | }; 38 | -------------------------------------------------------------------------------- /client-lite/src/threading/do_event.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "do_event.h" 6 | 7 | #include 8 | 9 | #ifdef DEBUG 10 | inline void _VerifyWaitTime(std::chrono::milliseconds timeout) 11 | { 12 | // Wait time must be small enough to not overflow when added to now() 13 | const auto now = std::chrono::steady_clock::now(); 14 | DO_ASSERT((now + (timeout)) >= now); 15 | } 16 | #else 17 | #define _VerifyWaitTime(t) 18 | #endif 19 | 20 | // AutoResetEvent 21 | 22 | AutoResetEvent::AutoResetEvent(bool isSignaled) : 23 | _isSignaled(isSignaled) 24 | { 25 | } 26 | 27 | void AutoResetEvent::SetEvent() noexcept 28 | { 29 | std::lock_guard lock(_mutex); 30 | _isSignaled = true; 31 | _cv.notify_one(); 32 | } 33 | 34 | void AutoResetEvent::ResetEvent() noexcept 35 | { 36 | std::lock_guard lock(_mutex); 37 | _isSignaled = false; 38 | } 39 | 40 | bool AutoResetEvent::IsSignaled() const noexcept 41 | { 42 | std::lock_guard lock(_mutex); 43 | return _isSignaled; 44 | } 45 | 46 | bool AutoResetEvent::Wait(std::chrono::milliseconds timeout) noexcept 47 | { 48 | _VerifyWaitTime(timeout); 49 | 50 | std::unique_lock lock(_mutex); 51 | if (!_isSignaled) 52 | { 53 | if (!_cv.wait_for(lock, timeout, [this] { return _isSignaled; })) 54 | { 55 | return false; 56 | } 57 | } 58 | _isSignaled = false; 59 | return true; 60 | } 61 | 62 | // ManualResetEvent 63 | 64 | ManualResetEvent::ManualResetEvent(bool isSignaled) : 65 | _isSignaled(isSignaled) 66 | { 67 | } 68 | 69 | void ManualResetEvent::SetEvent() noexcept 70 | { 71 | std::lock_guard lock(_mutex); 72 | _isSignaled = true; 73 | _cv.notify_all(); 74 | } 75 | 76 | void ManualResetEvent::ResetEvent() noexcept 77 | { 78 | std::lock_guard lock(_mutex); 79 | _isSignaled = false; 80 | } 81 | 82 | bool ManualResetEvent::IsSignaled() const noexcept 83 | { 84 | std::lock_guard lock(_mutex); 85 | return _isSignaled; 86 | } 87 | 88 | bool ManualResetEvent::Wait(std::chrono::milliseconds timeout) noexcept 89 | { 90 | _VerifyWaitTime(timeout); 91 | 92 | std::unique_lock lock(_mutex); 93 | if (!_isSignaled) 94 | { 95 | if (!_cv.wait_for(lock, timeout, [this] { return _isSignaled; })) 96 | { 97 | return false; 98 | } 99 | } 100 | return true; 101 | } 102 | -------------------------------------------------------------------------------- /client-lite/src/threading/do_event.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include "config_defaults.h" 10 | #include "do_noncopyable.h" 11 | 12 | class AutoResetEvent : private DONonCopyable 13 | { 14 | public: 15 | AutoResetEvent(bool isSignaled = false); 16 | 17 | void SetEvent() noexcept; 18 | void ResetEvent() noexcept; 19 | bool IsSignaled() const noexcept; 20 | bool Wait(std::chrono::milliseconds timeout = g_steadyClockInfiniteWaitTime) noexcept; 21 | 22 | private: 23 | mutable std::mutex _mutex; 24 | std::condition_variable _cv; 25 | bool _isSignaled { false }; 26 | }; 27 | 28 | class ManualResetEvent : private DONonCopyable 29 | { 30 | public: 31 | ManualResetEvent(bool isSignaled = false); 32 | 33 | void SetEvent() noexcept; 34 | void ResetEvent() noexcept; 35 | bool IsSignaled() const noexcept; 36 | bool Wait(std::chrono::milliseconds timeout = g_steadyClockInfiniteWaitTime) noexcept; 37 | 38 | private: 39 | mutable std::mutex _mutex; 40 | std::condition_variable _cv; 41 | bool _isSignaled { false }; 42 | }; 43 | -------------------------------------------------------------------------------- /client-lite/src/threading/task_queue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "task_queue.h" 6 | 7 | void TaskQueue::_Add(std::unique_ptr&& spTask, const duration_t& delay) 8 | { 9 | const auto adjustedDelay = std::max(duration_t(0), delay); 10 | 11 | auto opTime = clock_t::now() + adjustedDelay; 12 | 13 | _ops.emplace(opTime, std::move(spTask)); 14 | } 15 | 16 | void TaskQueue::_AddFront(std::unique_ptr&& spTask) 17 | { 18 | timepoint_t now = clock_t::now(); 19 | timepoint_t earliest = NextTime(); 20 | timepoint_t opTime; 21 | if (now < earliest) 22 | { 23 | opTime = now - duration_t(1); 24 | } 25 | else 26 | { 27 | opTime = earliest - duration_t(1); 28 | } 29 | 30 | _ops.emplace(opTime, std::move(spTask)); 31 | } 32 | 33 | std::unique_ptr TaskQueue::popNextReady(_Out_opt_ const void** tagp) 34 | { 35 | if (tagp != nullptr) 36 | { 37 | *tagp = nullptr; 38 | } 39 | 40 | std::unique_ptr rval; 41 | OpsMap::iterator it = _ops.begin(); 42 | if ((it != _ops.end()) && (it->first <= clock_t::now())) 43 | { 44 | rval = std::move(it->second); 45 | if (tagp) 46 | { 47 | *tagp = rval->Tag(); 48 | } 49 | _ops.erase(it); 50 | } 51 | return rval; 52 | } 53 | 54 | void TaskQueue::Remove(_In_opt_ const void* tag) 55 | { 56 | if (tag != nullptr) 57 | { 58 | // This is why we want boost::multi_index_container 59 | OpsMap::iterator it = _ops.begin(); 60 | while (it != _ops.end()) 61 | { 62 | if (it->second->Tag() == tag) 63 | { 64 | it = _ops.erase(it); 65 | } 66 | else 67 | { 68 | ++it; 69 | } 70 | } 71 | } 72 | } 73 | 74 | bool TaskQueue::Exists(_In_opt_ const void* tag) const 75 | { 76 | bool result = false; 77 | if (tag != nullptr) 78 | { 79 | for (const auto& entry : _ops) 80 | { 81 | if (entry.second->Tag() == tag) 82 | { 83 | result = true; 84 | break; 85 | } 86 | } 87 | } 88 | return result; 89 | } 90 | 91 | TaskQueue::timepoint_t TaskQueue::NextTime() const 92 | { 93 | OpsMap::const_iterator it = _ops.begin(); 94 | return (it != _ops.end()) ? it->first : timepoint_t::max(); 95 | } -------------------------------------------------------------------------------- /client-lite/src/threading/task_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Note: Locking is left to the queue's owner 11 | class TaskQueue 12 | { 13 | public: 14 | using clock_t = std::chrono::steady_clock; 15 | using timepoint_t = clock_t::time_point; 16 | using duration_t = std::chrono::milliseconds; 17 | 18 | class Task 19 | { 20 | public: 21 | virtual ~Task() {} 22 | virtual void Run() = 0; 23 | virtual const void* Tag() = 0; 24 | }; 25 | 26 | private: 27 | template 28 | class Op : public Task, std::remove_reference_t 29 | { 30 | Op(const Op&) = delete; 31 | Op& operator=(const Op&) = delete; 32 | 33 | public: 34 | Op(TLambda&& lambda, const void* tag) : 35 | std::remove_reference_t(std::forward(lambda)), 36 | _tag(tag) 37 | { 38 | } 39 | 40 | void Run() override 41 | { 42 | (*this)(); 43 | } 44 | 45 | const void* Tag() override 46 | { 47 | return _tag; 48 | } 49 | 50 | private: 51 | const void* _tag; 52 | }; 53 | 54 | void _Add(std::unique_ptr&& spTask, const duration_t& delay); 55 | void _AddFront(std::unique_ptr&& spTask); 56 | 57 | typedef std::multimap> OpsMap; 58 | OpsMap _ops; 59 | 60 | public: 61 | // Note: If tag == nullptr, the op can't be removed. remove(nullptr) is a no-op. 62 | template 63 | void Add(TLambda&& func, TDuration delay, _In_opt_ const void* tag = nullptr) 64 | { 65 | _Add(std::make_unique>(std::forward(func), tag), std::chrono::duration_cast(delay)); 66 | } 67 | 68 | template 69 | void AddFront(TLambda&& func, _In_opt_ const void* tag = nullptr) 70 | { 71 | _AddFront(std::make_unique>(std::forward(func), tag)); 72 | } 73 | 74 | std::unique_ptr popNextReady(_Out_opt_ const void** tagp = nullptr); 75 | void Remove(_In_opt_ const void* tag); 76 | bool Exists(_In_opt_ const void* tag) const; 77 | 78 | timepoint_t NextTime() const; 79 | }; 80 | -------------------------------------------------------------------------------- /client-lite/src/threading/task_thread.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "task_thread.h" 6 | 7 | #include 8 | #include "do_event.h" 9 | 10 | TaskThread::TaskThread() 11 | { 12 | _thread = std::thread([this]() { _DoPoll(); }); 13 | } 14 | 15 | TaskThread::~TaskThread() 16 | { 17 | SchedImmediate([this]() 18 | { 19 | _fRunning = false; 20 | }); 21 | _thread.join(); 22 | } 23 | 24 | void TaskThread::Unschedule(_In_opt_ const void* tag) 25 | { 26 | if (tag != nullptr) 27 | { 28 | std::unique_lock lock(_taskQMutex); 29 | _taskQ.Remove(tag); 30 | _taskQCond.notify_all(); 31 | } 32 | } 33 | 34 | void TaskThread::_DoPoll() 35 | { 36 | while (_fRunning) 37 | { 38 | std::unique_lock lock(_taskQMutex); 39 | const auto next = _taskQ.NextTime(); 40 | const auto now = TaskQueue::clock_t::now(); 41 | if (next <= now) 42 | { 43 | std::unique_ptr spTask = _taskQ.popNextReady(); 44 | if (spTask) 45 | { 46 | lock.unlock(); 47 | 48 | spTask->Run(); 49 | } 50 | } 51 | else if (next == TaskQueue::timepoint_t::max()) 52 | { 53 | _taskQCond.wait(lock); 54 | } 55 | else 56 | { 57 | _taskQCond.wait_for(lock, next - now); 58 | } 59 | } 60 | 61 | DoLogInfo("TaskThread exit"); 62 | } 63 | 64 | void TaskThread::SchedBlock(const std::function& func, bool immediate) 65 | { 66 | if (IsCurrentThread()) 67 | { 68 | func(); 69 | return; 70 | } 71 | 72 | AutoResetEvent completionEvent; 73 | HRESULT hr = S_OK; 74 | 75 | auto execOp = [&completionEvent, &func, &hr]() 76 | { 77 | try 78 | { 79 | func(); 80 | } 81 | catch (...) 82 | { 83 | hr = LOG_CAUGHT_EXCEPTION(); 84 | } 85 | completionEvent.SetEvent(); 86 | }; 87 | 88 | if (immediate) 89 | { 90 | SchedImmediate(std::move(execOp)); 91 | } 92 | else 93 | { 94 | Sched(std::move(execOp)); 95 | } 96 | 97 | (void)completionEvent.Wait(); 98 | 99 | THROW_IF_FAILED(hr); 100 | } 101 | 102 | bool TaskThread::IsScheduled(_In_opt_ const void* tag) const 103 | { 104 | std::unique_lock lock(_taskQMutex); 105 | return _taskQ.Exists(tag); 106 | } 107 | -------------------------------------------------------------------------------- /client-lite/src/trace/do_log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace DOLog 9 | { 10 | 11 | enum class Level 12 | { 13 | Error, 14 | Warning, 15 | Info, 16 | Verbose, 17 | }; 18 | 19 | void Init(const std::string& logDir, Level maxLogLevel); 20 | void Close(); 21 | void Write(Level level, const char* pszFunc, unsigned int uLine, const char* pszFmt, ...); 22 | void WriteResult(Level level, const char* pszFunc, unsigned int uLine, HRESULT hr, const char* pszFmt, ...); 23 | 24 | } // namespace DOLog 25 | 26 | #define EVENT_LEVEL_ERROR DOLog::Level::Error 27 | #define EVENT_LEVEL_WARNING DOLog::Level::Warning 28 | #define EVENT_LEVEL_INFO DOLog::Level::Info 29 | #define EVENT_LEVEL_VERBOSE DOLog::Level::Verbose 30 | 31 | // '##' is required before __VA_ARGS__ to allow an empty arg list to the variadic macro. 32 | // MSVC supports this by default but GCC requires '##'. C++2a has added VA_OPT macro 33 | // to officially support this behavior. 34 | 35 | #define DoLogMessage(level, msg, ...) DOLog::Write((level), __FUNCTION__, __LINE__, (msg), ##__VA_ARGS__) 36 | #define DoLogResult(level, hr, msg, ...) DOLog::WriteResult((level), __FUNCTION__, __LINE__, (hr), (msg), ##__VA_ARGS__) 37 | 38 | #define DoLogError(msg, ...) DoLogMessage(EVENT_LEVEL_ERROR, (msg), ##__VA_ARGS__) 39 | #define DoLogWarning(msg, ...) DoLogMessage(EVENT_LEVEL_WARNING, (msg), ##__VA_ARGS__) 40 | #define DoLogInfo(msg, ...) DoLogMessage(EVENT_LEVEL_INFO, (msg), ##__VA_ARGS__) 41 | #define DoLogVerbose(msg, ...) DoLogMessage(EVENT_LEVEL_VERBOSE, (msg), ##__VA_ARGS__) 42 | 43 | #ifdef DEBUG 44 | #define DoLogDebug(msg, ...) DoLogMessage(EVENT_LEVEL_VERBOSE, (msg), ##__VA_ARGS__) 45 | #else 46 | #define DoLogDebug(msg, ...) 47 | #endif 48 | 49 | #define DoLogErrorHr(hr, msg, ...) DoLogResult(EVENT_LEVEL_ERROR, (hr), (msg), ##__VA_ARGS__) 50 | #define DoLogWarningHr(hr, msg, ...) DoLogResult(EVENT_LEVEL_WARNING, (hr), (msg), ##__VA_ARGS__) 51 | #define DoLogInfoHr(hr, msg, ...) DoLogResult(EVENT_LEVEL_INFO, (hr), (msg), ##__VA_ARGS__) 52 | #define DoLogVerboseHr(hr, msg, ...) DoLogResult(EVENT_LEVEL_VERBOSE, (hr), (msg), ##__VA_ARGS__) 53 | -------------------------------------------------------------------------------- /client-lite/src/trace/event_data.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "event_data.h" 6 | 7 | #include "download.h" 8 | 9 | TelDataDownloadInfo::TelDataDownloadInfo(const Download& download) : 10 | _guid(download.GetId()), 11 | _status(download.GetStatus()), 12 | _url(download.GetUrl()), 13 | _destinationPath(download.GetDestinationPath()), 14 | _mccHost(download.GetMCCHost()) 15 | { 16 | } 17 | 18 | EventDataDownloadStarted::EventDataDownloadStarted(const Download& download) : 19 | _commonData(download) 20 | { 21 | } 22 | 23 | EventDataDownloadCompleted::EventDataDownloadCompleted(const Download& download) : 24 | _commonData(download), 25 | _elapsedTime(download.GetElapsedTime()) 26 | { 27 | } 28 | 29 | EventDataDownloadPaused::EventDataDownloadPaused(const Download& download) : 30 | _commonData(download) 31 | { 32 | } 33 | 34 | EventDataDownloadCanceled::EventDataDownloadCanceled(const Download& download) : 35 | _commonData(download) 36 | { 37 | } 38 | 39 | EventDataDownloadStatus::EventDataDownloadStatus(const Download& download) : 40 | id(download.GetId()), 41 | status(download.Status()), 42 | httpStatusCode(download.HttpStatusCode()) 43 | { 44 | } 45 | -------------------------------------------------------------------------------- /client-lite/src/trace/event_data.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include "do_guid.h" 10 | #include "download_status.h" 11 | 12 | class Download; 13 | 14 | struct TelDataDownloadInfo 15 | { 16 | TelDataDownloadInfo(const Download& download); 17 | TelDataDownloadInfo() = default; 18 | 19 | GUID _guid; 20 | DownloadStatus _status; 21 | std::string _url; 22 | std::string _destinationPath; 23 | std::string _mccHost; 24 | }; 25 | 26 | struct EventDataDownloadStarted 27 | { 28 | EventDataDownloadStarted(const Download& download); 29 | 30 | TelDataDownloadInfo _commonData; 31 | }; 32 | 33 | struct EventDataDownloadCompleted 34 | { 35 | EventDataDownloadCompleted(const Download& download); 36 | 37 | TelDataDownloadInfo _commonData; 38 | std::chrono::milliseconds _elapsedTime; 39 | }; 40 | 41 | struct EventDataDownloadPaused 42 | { 43 | EventDataDownloadPaused(const Download& download); 44 | 45 | TelDataDownloadInfo _commonData; 46 | }; 47 | 48 | struct EventDataDownloadCanceled 49 | { 50 | EventDataDownloadCanceled(const Download& download); 51 | 52 | TelDataDownloadInfo _commonData; 53 | }; 54 | 55 | struct EventDataDownloadStatus 56 | { 57 | EventDataDownloadStatus(const Download& download); 58 | 59 | GUID id; 60 | DownloadStatus status; 61 | UINT httpStatusCode; 62 | }; 63 | -------------------------------------------------------------------------------- /client-lite/src/trace/telemetry_logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "telemetry_logger.h" 6 | 7 | TelemetryLogger& TelemetryLogger::getInstance() 8 | { 9 | static TelemetryLogger myInstance; 10 | return myInstance; 11 | } 12 | 13 | void TelemetryLogger::TraceDownloadStart(const EventDataDownloadStarted& eventData) 14 | { 15 | DoLogInfoHr(eventData._commonData._status.Error, "id: %s, url: %s, filePath: %s, mccHost: %s", 16 | GuidToString(eventData._commonData._guid).data(), eventData._commonData._url.c_str(), 17 | eventData._commonData._destinationPath.c_str(), eventData._commonData._mccHost.c_str()); 18 | } 19 | 20 | void TelemetryLogger::TraceDownloadCompleted(const EventDataDownloadCompleted& eventData) 21 | { 22 | DoLogInfo("id: %s, url: %s, mccHost: %s, filePath: %s, bytes: [total: %ld, down: %ld], timeMS: %ld", 23 | GuidToString(eventData._commonData._guid).data(), eventData._commonData._url.c_str(), eventData._commonData._mccHost.c_str(), 24 | eventData._commonData._destinationPath.c_str(), eventData._commonData._status.BytesTotal, 25 | eventData._commonData._status.BytesTransferred, eventData._elapsedTime.count()); 26 | } 27 | 28 | void TelemetryLogger::TraceDownloadPaused(const EventDataDownloadPaused& eventData) 29 | { 30 | DoLogInfoHr(eventData._commonData._status.Error, "id: %s, extError: %x, cdnUrl: %s, mccHost: %s, filePath: %s, bytes: [total: %ld, down: %ld]", 31 | GuidToString(eventData._commonData._guid).data(), eventData._commonData._status.ExtendedError, eventData._commonData._url.c_str(), 32 | eventData._commonData._mccHost.c_str(), eventData._commonData._destinationPath.c_str(), eventData._commonData._status.BytesTotal, 33 | eventData._commonData._status.BytesTransferred); 34 | } 35 | 36 | void TelemetryLogger::TraceDownloadCanceled(const EventDataDownloadCanceled& eventData) 37 | { 38 | DoLogInfoHr(eventData._commonData._status.Error, "id: %s, extError: %x, cdnUrl: %s, mccHost: %s, filePath: %s, bytes: [total: %ld, down: %ld]", 39 | GuidToString(eventData._commonData._guid).data(), eventData._commonData._status.ExtendedError, eventData._commonData._url.c_str(), 40 | eventData._commonData._mccHost.c_str(), eventData._commonData._destinationPath.c_str(), eventData._commonData._status.BytesTotal, 41 | eventData._commonData._status.BytesTransferred); 42 | } 43 | 44 | void TelemetryLogger::TraceDownloadStatus(const EventDataDownloadStatus& eventData) 45 | { 46 | DoLogVerbose("id: %s, %d, codes: [%u, 0x%x, 0x%x], %llu / %llu", GuidToString(eventData.id).c_str(), eventData.status.State, 47 | eventData.httpStatusCode, eventData.status.Error, eventData.status.ExtendedError, eventData.status.BytesTransferred, 48 | eventData.status.BytesTotal); 49 | } 50 | -------------------------------------------------------------------------------- /client-lite/src/trace/telemetry_logger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "do_noncopyable.h" 7 | #include "event_data.h" 8 | 9 | // TODO: Instrument telemetry provider 10 | // Until telemetry is instrumented, this class serves as a wrapper for logging telemetry events with no data being sent 11 | class TelemetryLogger : DONonCopyable 12 | { 13 | public: 14 | static TelemetryLogger& getInstance(); 15 | 16 | void TraceDownloadStart(const EventDataDownloadStarted& eventData); 17 | void TraceDownloadCompleted(const EventDataDownloadCompleted& eventData); 18 | void TraceDownloadPaused(const EventDataDownloadPaused& eventData); 19 | void TraceDownloadCanceled(const EventDataDownloadCanceled& eventData); 20 | void TraceDownloadStatus(const EventDataDownloadStatus& eventData); 21 | }; -------------------------------------------------------------------------------- /client-lite/src/util/do_date_time.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include 6 | 7 | using filetime_duration_t = std::chrono::duration>; 8 | using wall_clock_t = std::chrono::system_clock; 9 | 10 | #define filetime_cast(d) std::chrono::duration_cast(d) 11 | #define seconds_cast(d) std::chrono::duration_cast(d) 12 | 13 | inline std::array SysTimePointToUTCString(wall_clock_t::time_point timePoint) 14 | { 15 | const auto tt = wall_clock_t::to_time_t(timePoint); 16 | struct tm st = {}; 17 | gmtime_r(&tt, &st); 18 | 19 | auto ft = filetime_cast(timePoint.time_since_epoch()); 20 | auto fractionalSeconds = ft - seconds_cast(ft); 21 | std::array timebuf = {}; 22 | snprintf(timebuf.data(), timebuf.size(), "%04d-%02d-%02dT%02d:%02d:%02d.%07dZ", 23 | st.tm_year + 1900, st.tm_mon + 1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, static_cast(fractionalSeconds.count())); 24 | return timebuf; 25 | } 26 | 27 | inline std::array SysTimePointToFileNameString(wall_clock_t::time_point timePoint) 28 | { 29 | const auto tt = wall_clock_t::to_time_t(timePoint); 30 | struct tm st = {}; 31 | gmtime_r(&tt, &st); 32 | 33 | auto ft = filetime_cast(timePoint.time_since_epoch()); 34 | std::array timebuf = {}; 35 | snprintf(timebuf.data(), timebuf.size(), "%04d%02d%02d_%02d%02d%02d", 36 | st.tm_year + 1900, st.tm_mon + 1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec); 37 | return timebuf; 38 | } 39 | -------------------------------------------------------------------------------- /client-lite/src/util/do_file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "do_file.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | DOFile::DOFile(int fd) : 12 | _fd(fd) 13 | { 14 | DO_ASSERT(_fd >= 0); 15 | } 16 | 17 | DOFile DOFile::Create(const std::string& path) 18 | { 19 | int fd = open(path.data(), O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 20 | const HRESULT hr = (fd != -1) ? S_OK : HRESULT_FROM_XPLAT_SYSERR(errno); 21 | DoLogInfoHr(hr, "Create file %s", path.data()); 22 | THROW_IF_FAILED(hr); 23 | return DOFile{fd}; 24 | } 25 | 26 | DOFile DOFile::Open(const std::string& path) 27 | { 28 | int fd = open(path.data(), O_APPEND | O_WRONLY); 29 | const HRESULT hr = (fd != -1) ? S_OK : HRESULT_FROM_XPLAT_SYSERR(errno); 30 | DoLogInfoHr(hr, "Open file %s", path.data()); 31 | THROW_IF_FAILED(hr); 32 | return DOFile{fd}; 33 | } 34 | 35 | void DOFile::Delete(const std::string& path) 36 | { 37 | HRESULT hr = S_OK; 38 | if (remove(path.data()) == -1) 39 | { 40 | const auto err = errno; 41 | if (err != ENOENT) 42 | { 43 | hr = HRESULT_FROM_XPLAT_SYSERR(err); 44 | } 45 | } 46 | DoLogInfoHr(hr, "Delete file %s", path.data()); 47 | } 48 | 49 | void DOFile::Append(_In_reads_bytes_(cbData) BYTE* pData, UINT cbData) const 50 | { 51 | const ssize_t cbWritten = write(_fd, pData, cbData); 52 | if (cbWritten == -1) 53 | { 54 | THROW_HR(HRESULT_FROM_XPLAT_SYSERR(errno)); 55 | } 56 | THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_LENGTH), cbWritten != static_cast(cbData)); 57 | } 58 | 59 | void DOFile::Close() 60 | { 61 | if (_fd != -1) 62 | { 63 | if (close(_fd) == -1) 64 | { 65 | THROW_HR(HRESULT_FROM_XPLAT_SYSERR(errno)); 66 | } 67 | _fd = -1; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /client-lite/src/util/do_file.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "do_noncopyable.h" 7 | 8 | // Write-only, binary file wrapper. 9 | // Uses POSIX APIs to provide better error codes than std::fstream/boost::fstream. 10 | class DOFile : DONonCopyable 11 | { 12 | private: 13 | DOFile(int fd); 14 | 15 | public: 16 | DOFile() = default; 17 | 18 | DOFile(DOFile&& other) noexcept : 19 | DOFile() 20 | { 21 | DO_ASSERT(!IsValid()); 22 | *this = std::move(other); 23 | } 24 | 25 | DOFile& operator=(DOFile&& other) noexcept 26 | { 27 | Close(); 28 | std::swap(_fd, other._fd); 29 | DO_ASSERT(!other.IsValid()); 30 | return *this; 31 | } 32 | 33 | static DOFile Create(const std::string& path); 34 | static DOFile Open(const std::string& path); 35 | static void Delete(const std::string& path); 36 | 37 | void Append(_In_reads_bytes_(cbData) BYTE* pData, UINT cbData) const; 38 | void Close(); 39 | 40 | operator bool() const noexcept { return IsValid(); } 41 | bool IsValid() const noexcept { return (_fd >= 0) ;} 42 | 43 | private: 44 | int _fd { -1 }; 45 | }; 46 | -------------------------------------------------------------------------------- /client-lite/src/util/do_guid.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "do_guid.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include "string_ops.h" 11 | 12 | GUID CreateNewGuid() 13 | { 14 | static_assert(sizeof(GUID) == boost::uuids::uuid::static_size()); 15 | boost::uuids::uuid id = boost::uuids::random_generator()(); 16 | GUID newGuid; 17 | memcpy(&newGuid, &id, sizeof(newGuid)); 18 | return newGuid; 19 | } 20 | 21 | bool StringToGuid(PCSTR guidStr, GUID* guidVal) 22 | { 23 | constexpr size_t GUIDSTR_MIN = GUIDSTR_MAX - 2 - 1; // without braces and null-terminator 24 | constexpr int GUID_SEGMENTS = 11; 25 | const size_t len = strlen(guidStr); 26 | if ((GUIDSTR_MIN <= len) && (len <= (GUIDSTR_MAX - 1))) 27 | { 28 | std::string localGuidStr(guidStr); 29 | StringCleanup(localGuidStr, "{}"); 30 | boost::algorithm::to_lower(localGuidStr); 31 | if (localGuidStr.size() == GUIDSTR_MIN) 32 | { 33 | GUID tempGuid; 34 | const int ret = StringScanf(localGuidStr.data(), "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", 35 | &tempGuid.Data1, &tempGuid.Data2, &tempGuid.Data3, 36 | &tempGuid.Data4[0], &tempGuid.Data4[1], &tempGuid.Data4[2], &tempGuid.Data4[3], 37 | &tempGuid.Data4[4], &tempGuid.Data4[5], &tempGuid.Data4[6], &tempGuid.Data4[7]); 38 | if (ret == GUID_SEGMENTS) 39 | { 40 | assign_to_opt_param(guidVal, tempGuid); 41 | return true; 42 | } 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | std::string GuidToString(REFGUID guid) 49 | { 50 | std::array guidStr = {}; 51 | StringPrintf(guidStr.data(), guidStr.size(), "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x", 52 | guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], 53 | guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); 54 | return guidStr.data(); 55 | } 56 | -------------------------------------------------------------------------------- /client-lite/src/util/do_guid.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | typedef struct 7 | { 8 | uint32_t Data1; // Can't use unsigned long here because it is 8bytes on linux 9 | uint16_t Data2; 10 | uint16_t Data3; 11 | uint8_t Data4[8]; 12 | } GUID, IID; 13 | 14 | using REFGUID = const GUID&; 15 | 16 | #ifndef GUIDSTR_MAX 17 | #define GUIDSTR_MAX (1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1) 18 | #endif 19 | 20 | GUID CreateNewGuid(); 21 | bool StringToGuid(PCSTR guidStr, GUID* guidVal = nullptr); 22 | std::string GuidToString(REFGUID guid); 23 | -------------------------------------------------------------------------------- /client-lite/src/util/do_json_parser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "do_json_parser.h" 6 | 7 | #include "do_filesystem.h" 8 | #include 9 | 10 | std::chrono::seconds JsonParser::RefreshInterval = std::chrono::seconds(60); 11 | 12 | // Stores file path provided. Loads from the file later when a value is queried for. 13 | JsonParser::JsonParser(const std::string& jsonFilePath, bool alwaysCreateFile) : 14 | _jsonFilePath(jsonFilePath) 15 | { 16 | if (alwaysCreateFile && !(fs::exists(_jsonFilePath))) 17 | { 18 | DoLogInfo("json file not found at %s, creating file", _jsonFilePath.data()); 19 | boost::property_tree::ptree json; 20 | boost::property_tree::write_json(_jsonFilePath, json); 21 | } 22 | } 23 | 24 | void JsonParser::Refresh() 25 | { 26 | _TryRefresh(true); 27 | } 28 | 29 | void JsonParser::_TryRefresh(bool force) 30 | { 31 | if (!force && std::chrono::steady_clock::now() < _nextRefreshTime) 32 | { 33 | return; 34 | } 35 | 36 | if (fs::exists(_jsonFilePath)) 37 | { 38 | try 39 | { 40 | boost::property_tree::read_json(_jsonFilePath, _tree); 41 | DoLogInfo("Read json config file %s", _jsonFilePath.data()); 42 | } 43 | catch (const std::exception& ex) 44 | { 45 | DoLogWarning("Could not read json config file %s: %s", _jsonFilePath.data(), ex.what()); 46 | } 47 | catch (...) 48 | { 49 | DoLogWarning("Caught unexpected exception when reading json config file %s", _jsonFilePath.data()); 50 | } 51 | } 52 | else 53 | { 54 | DoLogVerbose("json file not found at %s", _jsonFilePath.data()); 55 | _tree.clear(); 56 | } 57 | 58 | _nextRefreshTime = std::chrono::steady_clock::now() + RefreshInterval; 59 | } 60 | -------------------------------------------------------------------------------- /client-lite/src/util/do_json_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | class JsonParser 13 | { 14 | public: 15 | static std::chrono::seconds RefreshInterval; 16 | 17 | JsonParser(const std::string& jsonFilePath, bool alwaysCreateFile = false); 18 | 19 | void Refresh(); 20 | 21 | template 22 | boost::optional Get(const std::string& key) 23 | { 24 | _TryRefresh(); 25 | boost::optional value; 26 | try 27 | { 28 | value = _tree.get(key); 29 | } 30 | catch (...) 31 | { 32 | } 33 | return value; 34 | } 35 | 36 | private: 37 | void _TryRefresh(bool force = false); 38 | 39 | const std::string _jsonFilePath; 40 | boost::property_tree::ptree _tree; 41 | std::chrono::steady_clock::time_point _nextRefreshTime{}; 42 | 43 | }; -------------------------------------------------------------------------------- /client-lite/src/util/do_noncopyable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | // Handy base class to create non-copyable but movable classes 7 | class DONonCopyable 8 | { 9 | public: 10 | DONonCopyable(const DONonCopyable&) = delete; 11 | DONonCopyable& operator=(const DONonCopyable&) = delete; 12 | 13 | DONonCopyable(DONonCopyable&&) noexcept = default; 14 | DONonCopyable& operator=(DONonCopyable&&) noexcept = default; 15 | 16 | protected: 17 | DONonCopyable() {} 18 | }; 19 | -------------------------------------------------------------------------------- /client-lite/src/util/do_persistence.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "do_persistence.h" 6 | 7 | #include // std::getenv 8 | 9 | namespace docli 10 | { 11 | 12 | static std::string ConstructPath(const char* suffix) 13 | { 14 | std::string outputPath; 15 | #ifdef DO_BUILD_FOR_SNAP 16 | const char* snapDataRoot = std::getenv("SNAP_DATA"); 17 | THROW_HR_IF(E_UNEXPECTED, (snapDataRoot == nullptr) || (*snapDataRoot == '\0')); 18 | outputPath = snapDataRoot; 19 | if (outputPath.back() != '/') 20 | { 21 | outputPath.push_back('/'); 22 | } 23 | if (*suffix == '/') 24 | { 25 | ++suffix; 26 | } 27 | outputPath += suffix; 28 | #else 29 | outputPath = suffix; 30 | #endif 31 | return outputPath; 32 | } 33 | 34 | const std::string& GetLogDirectory() 35 | { 36 | static std::string logDirectory(ConstructPath(DO_AGENT_LOG_DIRECTORY_PATH)); 37 | return logDirectory; 38 | } 39 | 40 | const std::string& GetRuntimeDirectory() 41 | { 42 | static std::string runDirectory(ConstructPath(DO_RUN_DIRECTORY_PATH)); 43 | return runDirectory; 44 | } 45 | 46 | const std::string& GetConfigDirectory() 47 | { 48 | static std::string configDirectory(ConstructPath(DO_CONFIG_DIRECTORY_PATH)); 49 | return configDirectory; 50 | } 51 | 52 | const std::string& GetSDKConfigFilePath() 53 | { 54 | static std::string configFilePath(ConstructPath(DO_CONFIG_DIRECTORY_PATH "/sdk-config.json")); 55 | return configFilePath; 56 | } 57 | 58 | const std::string& GetAdminConfigFilePath() 59 | { 60 | static std::string configFilePath(ConstructPath(DO_CONFIG_DIRECTORY_PATH "/admin-config.json")); 61 | return configFilePath; 62 | } 63 | 64 | } // namespace docli 65 | -------------------------------------------------------------------------------- /client-lite/src/util/do_persistence.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | namespace docli 7 | { 8 | const std::string& GetLogDirectory(); 9 | const std::string& GetRuntimeDirectory(); 10 | const std::string& GetConfigDirectory(); 11 | const std::string& GetSDKConfigFilePath(); 12 | const std::string& GetAdminConfigFilePath(); 13 | } 14 | -------------------------------------------------------------------------------- /client-lite/src/util/http_agent_interface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | enum class HttpAgentHeaders 7 | { 8 | Range, 9 | }; 10 | 11 | class IHttpAgent 12 | { 13 | public: 14 | virtual ~IHttpAgent() = default; 15 | virtual HRESULT SendRequest(PCSTR url, PCSTR proxyUrl = nullptr, PCSTR range = nullptr, UINT connectTimeoutSecs = 0) = 0; 16 | virtual void Close() = 0; 17 | virtual HRESULT QueryStatusCode(_Out_ UINT *statusCode) const = 0; 18 | virtual HRESULT QueryContentLength(_Out_ UINT64 *contentLength) = 0; 19 | virtual HRESULT QueryContentLengthFromRange(_Out_ UINT64 *contentLength) = 0; 20 | virtual HRESULT QueryHeaders(_In_opt_z_ PCSTR name, std::string& headers) const noexcept = 0; 21 | virtual HRESULT QueryHeadersByType(HttpAgentHeaders type, std::string& headers) noexcept = 0; 22 | }; 23 | 24 | class IHttpAgentEvents 25 | { 26 | public: 27 | virtual ~IHttpAgentEvents() = default; 28 | virtual HRESULT OnHeadersAvailable() = 0; 29 | virtual HRESULT OnData(_In_reads_bytes_(cbData) BYTE* pData, UINT cbData) = 0; 30 | virtual HRESULT OnComplete(HRESULT hrRequest, HRESULT hrCallback) = 0; 31 | }; 32 | -------------------------------------------------------------------------------- /client-lite/src/util/proxy_finder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "proxy_finder.h" 6 | 7 | #ifdef DO_PROXY_SUPPORT 8 | #include // libproxy 9 | #endif 10 | 11 | static char** GetProxiesForURL(const char* url) 12 | { 13 | char** proxies = nullptr; 14 | #ifdef DO_PROXY_SUPPORT 15 | auto proxyFactory = px_proxy_factory_new(); 16 | if (proxyFactory != nullptr) 17 | { 18 | proxies = px_proxy_factory_get_proxies(proxyFactory, url); 19 | px_proxy_factory_free(proxyFactory); 20 | } 21 | #endif 22 | return proxies; 23 | } 24 | 25 | ProxyFinder::ProxyFinder(PCSTR url) 26 | { 27 | _proxyList = GetProxiesForURL(url); 28 | } 29 | 30 | ProxyFinder::~ProxyFinder() 31 | { 32 | if (_proxyList != nullptr) 33 | { 34 | for (size_t i = 0; _proxyList[i]; ++i) 35 | { 36 | free(_proxyList[i]); 37 | } 38 | free(_proxyList); 39 | } 40 | } 41 | 42 | ProxyFinder::proxy_list_t ProxyFinder::Get() const 43 | { 44 | proxy_list_t result; 45 | if (_proxyList != nullptr) 46 | { 47 | for (size_t i = 0; _proxyList[i]; ++i) 48 | { 49 | char* cur = _proxyList[i]; 50 | DoLogDebug("Proxy[%zu]: %s", i, cur); 51 | // direct is used to denote 'no proxy' 52 | if (strcmp(cur, "direct://") != 0) 53 | { 54 | result.emplace_back(cur); 55 | } 56 | } 57 | } 58 | return result; 59 | } 60 | 61 | void ProxyList::Refresh(const std::string& url) 62 | { 63 | _candidateProxies = ProxyFinder(url.data()).Get(); 64 | Reset(); 65 | } 66 | 67 | // Iterate through the list in round-robin fashion 68 | const ProxyList::proxy_value_t& ProxyList::Next() 69 | { 70 | static proxy_value_t empty; 71 | if (Empty()) 72 | { 73 | return empty; 74 | } 75 | const auto& ret = *_it++; 76 | if (_it == _candidateProxies.cend()) 77 | { 78 | Reset(); 79 | } 80 | return ret; 81 | } 82 | -------------------------------------------------------------------------------- /client-lite/src/util/proxy_finder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | class ProxyFinder 10 | { 11 | public: 12 | using proxy_list_t = std::vector; 13 | 14 | ProxyFinder(PCSTR url); 15 | ~ProxyFinder(); 16 | 17 | proxy_list_t Get() const; 18 | 19 | private: 20 | char** _proxyList { nullptr }; 21 | }; 22 | 23 | class ProxyList 24 | { 25 | public: 26 | using proxy_value_t = ProxyFinder::proxy_list_t::value_type; 27 | 28 | ProxyList() 29 | { 30 | Reset(); 31 | } 32 | 33 | void Refresh(const std::string& url); 34 | 35 | const proxy_value_t& Next(); 36 | 37 | void Reset() 38 | { 39 | _it = _candidateProxies.cbegin(); 40 | } 41 | 42 | size_t Size() const 43 | { 44 | return _candidateProxies.size(); 45 | } 46 | 47 | bool Empty() const 48 | { 49 | return (Size() == 0); 50 | } 51 | 52 | private: 53 | ProxyFinder::proxy_list_t _candidateProxies; 54 | ProxyFinder::proxy_list_t::const_iterator _it; 55 | }; 56 | -------------------------------------------------------------------------------- /client-lite/src/util/safe_int.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #define INTSAFE_E_ARITHMETIC_OVERFLOW ((HRESULT)0x80070216L) // 0x216 = 534 = ERROR_ARITHMETIC_OVERFLOW 7 | 8 | inline UINT64 UInt64Add(UINT64 ullAugend, UINT64 ullAddend) 9 | { 10 | THROW_HR_IF(INTSAFE_E_ARITHMETIC_OVERFLOW, (ullAugend + ullAddend) < ullAugend); 11 | return (ullAugend + ullAddend); 12 | } 13 | 14 | inline UINT64 UInt64Sub(UINT64 ullMinuend, UINT64 ullSubtrahend) 15 | { 16 | THROW_HR_IF(INTSAFE_E_ARITHMETIC_OVERFLOW, ullMinuend < ullSubtrahend); 17 | return (ullMinuend - ullSubtrahend); 18 | } 19 | -------------------------------------------------------------------------------- /client-lite/src/util/stop_watch.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "stop_watch.h" 6 | 7 | StopWatch StopWatch::StartNew() 8 | { 9 | StopWatch sw; 10 | sw.Start(); 11 | return sw; 12 | } 13 | 14 | void StopWatch::Start() 15 | { 16 | if (!_fRunning) 17 | { 18 | _startTime = std::chrono::steady_clock::now(); 19 | _fRunning = true; 20 | } 21 | } 22 | 23 | void StopWatch::Stop() 24 | { 25 | if (_fRunning) 26 | { 27 | _elapsedTime += std::chrono::steady_clock::now() - _startTime; 28 | _fRunning = false; 29 | } 30 | } 31 | 32 | std::chrono::steady_clock::duration StopWatch::_GetElapsedTime() const 33 | { 34 | std::chrono::steady_clock::duration totalElapsedTime = _elapsedTime; 35 | if (_fRunning) 36 | { 37 | auto now = std::chrono::steady_clock::now(); 38 | DO_ASSERT(_startTime <= now); 39 | totalElapsedTime += (now - _startTime); 40 | } 41 | return totalElapsedTime; 42 | } 43 | -------------------------------------------------------------------------------- /client-lite/src/util/stop_watch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // Stopwatch implementation using std::chrono::steady_clock. 9 | // Returns elapsed time in milliseconds. 10 | class StopWatch 11 | { 12 | public: 13 | static StopWatch StartNew(); 14 | 15 | void Start(); 16 | void Stop(); 17 | 18 | std::chrono::milliseconds GetElapsedInterval() const 19 | { 20 | return std::chrono::duration_cast(_GetElapsedTime()); 21 | } 22 | 23 | private: 24 | std::chrono::steady_clock::duration _GetElapsedTime() const; 25 | 26 | std::chrono::steady_clock::time_point _startTime{}; 27 | std::chrono::steady_clock::duration _elapsedTime{ 0 }; 28 | bool _fRunning{ false }; 29 | }; 30 | -------------------------------------------------------------------------------- /client-lite/src/util/string_ops.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_common.h" 5 | #include "string_ops.h" 6 | 7 | int StringCompareCaseInsensitive(PCSTR left, PCSTR right) 8 | { 9 | while ((*left != '\0') && (*right != '\0')) 10 | { 11 | if (std::toupper(*left) < std::toupper(*right)) return -1; 12 | if (std::toupper(*left) > std::toupper(*right)) return 1; 13 | ++left; 14 | ++right; 15 | } 16 | if ((*left == '\0') && (*right != '\0')) return -1; 17 | if ((*left != '\0') && (*right == '\0')) return 1; 18 | return 0; 19 | } 20 | 21 | // Split input string into two substrings separated by the specified separator. 22 | // Empty substrings are not included in the return value. 23 | std::vector StringPartition(const std::string& input, char separator) 24 | { 25 | std::vector parts; 26 | auto pos = input.find(separator); 27 | if (pos != std::string::npos) 28 | { 29 | if (pos > 0) 30 | { 31 | parts.emplace_back(input.substr(0, pos)); 32 | } 33 | 34 | if (pos < (input.size() - 1)) 35 | { 36 | parts.emplace_back(input.substr(pos + 1)); 37 | } 38 | } 39 | return parts; 40 | } 41 | 42 | namespace docli 43 | { 44 | namespace string_conversions 45 | { 46 | 47 | UINT ToUInt(const std::string& val) 48 | { 49 | UINT ret = 0; 50 | 51 | // Catch std exceptions and convert into DOResultException so that 52 | // upper layer CATCH_RETURN blocks don't convert it to ERROR_UNHANDLED_EXCEPTION. 53 | try 54 | { 55 | ret = std::stoul(val); 56 | } 57 | catch(const std::invalid_argument&) 58 | { 59 | THROW_HR(E_INVALIDARG); 60 | } 61 | catch (const std::out_of_range&) 62 | { 63 | THROW_HR(E_INVALIDARG); 64 | } 65 | return ret; 66 | } 67 | 68 | } // namespace string_conversions 69 | } // namespace docli 70 | -------------------------------------------------------------------------------- /client-lite/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | find_package(Boost COMPONENTS program_options REQUIRED) 5 | find_package(GTest REQUIRED) 6 | 7 | file (GLOB files_docs_tests 8 | *.cpp) 9 | add_executable(deliveryoptimization-agent-tests ${files_docs_tests}) 10 | add_platform_interface_definitions(deliveryoptimization-agent-tests) 11 | target_link_libraries(deliveryoptimization-agent-tests 12 | docs_common 13 | dotestutil 14 | ${Boost_LIBRARIES} 15 | GTest::GTest 16 | ${CXX_FILESYSTEM_LIBS} 17 | ) 18 | -------------------------------------------------------------------------------- /client-lite/test/CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | filter=-runtime/string -------------------------------------------------------------------------------- /client-lite/test/do_log_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_common.h" 5 | 6 | #include 7 | #include "do_log.h" 8 | 9 | class DOLoggerTests : public ::testing::Test 10 | { 11 | public: 12 | void SetUp() override 13 | { 14 | ClearTestTempDir(); 15 | } 16 | }; 17 | 18 | TEST_F(DOLoggerTests, BasicWriteToFile) 19 | { 20 | const char* pTestData[] = { "First line", "Second line", "{~LoggerImpl} Logger stats" }; 21 | 22 | DOLog::Init(g_testTempDir.string(), DOLog::Level::Verbose); 23 | DoLogInfo("%s", pTestData[0]); 24 | DoLogError("%s", pTestData[1]); 25 | // The 3rd string is written by the logger on close 26 | DOLog::Close(); 27 | 28 | UINT nLogFilesFound = 0; 29 | for (fs::recursive_directory_iterator itr(g_testTempDir); itr != fs::recursive_directory_iterator{}; ++itr) 30 | { 31 | ++nLogFilesFound; 32 | const auto filePath = itr->path(); 33 | ASSERT_NE(strstr(filePath.c_str(), "do-agent."), nullptr); 34 | ASSERT_GT(fs::file_size(filePath), 0); 35 | 36 | std::ifstream fs; 37 | fs.open(filePath.string()); 38 | ASSERT_TRUE(fs.is_open()); 39 | 40 | char readBuf[256]; 41 | UINT i = 0; 42 | // Expect the 2 lines and the final stats line for a total of 3 lines 43 | while (fs.getline(readBuf, ARRAYSIZE(readBuf))) 44 | { 45 | ASSERT_LT(i, 3); 46 | ASSERT_NE(strstr(readBuf, pTestData[i]), nullptr); 47 | ++i; 48 | } 49 | ASSERT_EQ(i, 3); 50 | } 51 | ASSERT_EQ(nLogFilesFound, 1); 52 | } 53 | -------------------------------------------------------------------------------- /client-lite/test/docs_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_common.h" 5 | 6 | #include 7 | #include "test_data.h" 8 | 9 | const fs::path g_testTempDir = "/tmp/docs_test_scratch"; 10 | 11 | int main(int argc, char** argv) 12 | { 13 | testing::InitGoogleTest(&argc, argv); 14 | // DoTraceLoggingRegister(); // enable failure output to console 15 | // TODO(shishirb) enable console only logging 16 | 17 | std::error_code ec; 18 | fs::create_directories(g_testTempDir, ec); 19 | if (ec) 20 | { 21 | printf("Failed to create test dir: %s\n", g_testTempDir.string().data()); 22 | return ec.value(); 23 | } 24 | 25 | namespace po = boost::program_options; 26 | po::options_description desc("Options"); 27 | desc.add_options() 28 | ("help,h", "Show help messages") 29 | ("mcc-host,m", po::value(), "MCC hostname to use (optional override)"); 30 | po::variables_map vm; 31 | po::store(po::parse_command_line(argc, argv, desc), vm); 32 | auto it = vm.find("mcc-host"); 33 | if (it != vm.end()) 34 | { 35 | g_mccHostName = it->second.as(); 36 | std::cout << "Got overriden MCC host: " << g_mccHostName << '\n'; 37 | } 38 | 39 | return RUN_ALL_TESTS(); 40 | } 41 | -------------------------------------------------------------------------------- /client-lite/test/network_monitor_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_common.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "network_monitor.h" 10 | #include "test_helpers.h" 11 | 12 | using namespace std::chrono_literals; // NOLINT(build/namespaces) 13 | 14 | class NetworkMonitorTests : public ::testing::Test 15 | { 16 | public: 17 | void SetUp() override; 18 | void TearDown() override; 19 | 20 | void _EnableNetwork(); 21 | void _DisableNetwork(); 22 | }; 23 | 24 | void NetworkMonitorTests::SetUp() 25 | { 26 | _EnableNetwork(); 27 | } 28 | 29 | void NetworkMonitorTests::TearDown() 30 | { 31 | _EnableNetwork(); 32 | } 33 | 34 | // This causes build pipeline failure due to cutting off communication between the agent and backend pipeline infra. 35 | // We can remove this test once we have it running as part of the E2E test suite. 36 | TEST_F(NetworkMonitorTests, DISABLED_VerifyNetworkReconnect) 37 | { 38 | ASSERT_TRUE(NetworkMonitor::HasViableInterface()); 39 | 40 | _DisableNetwork(); 41 | ASSERT_FALSE(NetworkMonitor::HasViableInterface()); 42 | 43 | _EnableNetwork(); 44 | ASSERT_TRUE(NetworkMonitor::HasViableInterface()); 45 | } 46 | 47 | // Execute this test after manually changing network interface name. 48 | // Ubuntu: changing eth0 to eno1 by creating this file and rebooting: 49 | // $ cat /etc/udev/rules.d/10-rename-network.rules 50 | // SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:xx", NAME="eno1" 51 | // Automating the name change is not feasible - requires reboot, and can make build agents go offline. 52 | TEST_F(NetworkMonitorTests, BasicNetworkConnected) 53 | { 54 | ASSERT_TRUE(NetworkMonitor::HasViableInterface()); 55 | } 56 | 57 | void NetworkMonitorTests::_EnableNetwork() 58 | { 59 | TestHelpers::EnableNetwork(); 60 | std::this_thread::sleep_for(10s); 61 | } 62 | 63 | void NetworkMonitorTests::_DisableNetwork() 64 | { 65 | TestHelpers::DisableNetwork(); 66 | std::this_thread::sleep_for(10s); 67 | } 68 | -------------------------------------------------------------------------------- /client-lite/test/rest_http_listener_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_common.h" 5 | #include "rest_http_listener.h" 6 | 7 | #include "do_test_helpers.h" 8 | 9 | using btcp_t = boost::asio::ip::tcp; 10 | using bsock_t = boost::asio::ip::tcp::socket; 11 | 12 | TEST(RestListenerTests, ManyPortsInUse) 13 | { 14 | // Listener searches for ports from 50000 to 60999. 15 | // We try to block all ports until 54999 and check time taken for RestHttpListener::Start(). 16 | 17 | dotest::util::BoostAsioWorker asioWorker; 18 | 19 | // Create and bind as many sockets as possible (usual limit is 1024 total open file descriptors) 20 | std::vector sockets; 21 | UINT portsAlreadyInUse = 0; 22 | for (UINT port = 50000; port <= 54999; ++port) 23 | { 24 | try 25 | { 26 | bsock_t sock(asioWorker.Service()); 27 | sock.open(btcp_t::v4()); 28 | sock.bind(btcp_t::endpoint(btcp_t::v4(), port)); 29 | sockets.push_back(std::move(sock)); 30 | } 31 | catch (const boost::system::system_error& e) 32 | { 33 | if (e.code() == boost::system::errc::address_in_use) 34 | { 35 | ++portsAlreadyInUse; 36 | continue; 37 | } 38 | 39 | if (e.code() == boost::system::errc::too_many_files_open) 40 | { 41 | std::cout << "Caught exception: " << e.what() << ". Iteration " << port - 50000 + 1 << " now.\n"; 42 | // Free up some sockets to allow REST listener to start 43 | for (UINT c = 1; c <= 10; ++c) 44 | { 45 | if (sockets.empty()) 46 | { 47 | break; 48 | } 49 | sockets.pop_back(); 50 | } 51 | break; 52 | } 53 | 54 | throw; 55 | } 56 | } 57 | 58 | std::cout << "Total number of ports already in use: " << portsAlreadyInUse << "\n"; 59 | std::cout << "Total number of sockets open: " << sockets.size() << "\n"; 60 | 61 | RestHttpListener listener; 62 | const auto before = std::chrono::steady_clock::now(); 63 | listener.Start(asioWorker.Service(), http_listener_callback_t{}); 64 | const auto after = std::chrono::steady_clock::now(); 65 | std::cout << "Listener started at: " << listener.Endpoint() << "\n"; 66 | const auto elapsedMsecs = std::chrono::duration_cast(after - before).count(); 67 | std::cout << "Time taken for listener to start: " << elapsedMsecs << "ms\n"; 68 | EXPECT_LT(elapsedMsecs, 2000); 69 | } 70 | -------------------------------------------------------------------------------- /client-lite/test/test_common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include "do_common.h" 7 | #include "do_filesystem.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | extern const fs::path g_testTempDir; 14 | 15 | inline void ClearTestTempDir() 16 | { 17 | if (fs::exists(g_testTempDir)) 18 | { 19 | fs::remove_all(g_testTempDir); 20 | } 21 | fs::create_directories(g_testTempDir); 22 | } 23 | -------------------------------------------------------------------------------- /client-lite/test/test_data.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_data.h" 5 | 6 | const uint64_t g_prodFileSizeBytes = 25006511u; 7 | 8 | const std::string g_smallFileUrl = "http://download.windowsupdate.com/phf/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/43A54FC03C6A979E9AAEAE2493757D1429A5C8A8D093FB7B8103E8CC8DF7B6B6"; 9 | const std::string g_smallFile2Url = "http://extorigin-int.dcat.dsp.mp.microsoft.com/filestreamingservice/files/81701079-fa80-48b4-825a-27af227e4192"; 10 | const std::string g_largeFileUrl = "http://download.windowsupdate.com/phf/dotc/ReplacementDCATFile.txt"; 11 | const std::string g_404Url = "http://download.windowsupdate.com/phf/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/nonexistent"; 12 | const std::string g_prodFileUrl = "http://dl.delivery.mp.microsoft.com/filestreamingservice/files/52fa8751-747d-479d-8f22-e32730cc0eb1"; 13 | 14 | // This MCC instance only works within our test lab azure VMs. Can be overriden via cmdline. 15 | std::string g_mccHostName = "10.1.0.70"; 16 | -------------------------------------------------------------------------------- /client-lite/test/test_data.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | extern const uint64_t g_prodFileSizeBytes; 9 | 10 | extern const std::string g_smallFileUrl; 11 | extern const std::string g_smallFile2Url; 12 | extern const std::string g_largeFileUrl; 13 | extern const std::string g_404Url; 14 | extern const std::string g_prodFileUrl; 15 | 16 | extern std::string g_mccHostName; 17 | -------------------------------------------------------------------------------- /client-lite/test/test_helpers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_common.h" 5 | #include "test_helpers.h" 6 | 7 | #include "do_test_helpers.h" 8 | namespace dtu = dotest::util; 9 | 10 | void TestHelpers::DisableNetwork() 11 | { 12 | dtu::ExecuteSystemCommand("ifconfig eth0 down"); 13 | std::cout << "Disabled eth0" << '\n'; 14 | } 15 | 16 | void TestHelpers::EnableNetwork() 17 | { 18 | dtu::ExecuteSystemCommand("ifconfig eth0 up"); 19 | std::cout << "Enabled eth0" << '\n'; 20 | } 21 | -------------------------------------------------------------------------------- /client-lite/test/test_helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | class TestHelpers 7 | { 8 | public: 9 | static void DisableNetwork(); 10 | static void EnableNetwork(); 11 | 12 | private: 13 | // Disallow creating an instance of this object 14 | TestHelpers() {} 15 | }; 16 | -------------------------------------------------------------------------------- /common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | if (DO_PLATFORM_LINUX OR DO_PLATFORM_MAC) 5 | if (DO_INCLUDE_AGENT OR DO_INCLUDE_SDK) 6 | add_subdirectory(lib-dohttp) 7 | endif() 8 | endif() 9 | 10 | if (DO_BUILD_TESTS) 11 | # Plugins subproject doesn't have test code 12 | if (DO_INCLUDE_AGENT OR DO_INCLUDE_SDK) 13 | add_subdirectory(lib-dotestutil) 14 | endif() 15 | endif() 16 | 17 | add_subdirectory(lib-doversion) 18 | -------------------------------------------------------------------------------- /common/cmake/FindUUID.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | cmake_minimum_required (VERSION 3.7) 5 | 6 | # cmake find module for the UUID linux library 7 | 8 | include(FindPackageHandleStandardArgs) 9 | 10 | find_path( 11 | UUID_INCLUDE_DIR 12 | NAMES uuid.h 13 | PATH_SUFFIXES uuid 14 | ) 15 | find_library(UUID_LIBRARY uuid) 16 | 17 | find_package_handle_standard_args( 18 | UUID 19 | DEFAULT_MSG 20 | UUID_INCLUDE_DIR 21 | UUID_LIBRARY 22 | ) 23 | 24 | if(UUID_FOUND) 25 | set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR}) 26 | set(UUID_LIBRARIES ${UUID_LIBRARY}) 27 | 28 | if(NOT TARGET UUID::UUID) 29 | add_library(UUID::UUID INTERFACE IMPORTED) 30 | set_target_properties( 31 | UUID::UUID 32 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${UUID_INCLUDE_DIR}" 33 | INTERFACE_LINK_LIBRARIES "${UUID_LIBRARIES}" 34 | ) 35 | endif() 36 | endif() 37 | 38 | mark_as_advanced(UUID_INCLUDE_DIR UUID_LIBRARY) 39 | -------------------------------------------------------------------------------- /common/cmake/Findlibproxy.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | cmake_minimum_required (VERSION 3.7) 5 | 6 | # cmake find module for the libproxy library and headers 7 | 8 | include(FindPackageHandleStandardArgs) 9 | 10 | find_path( 11 | libproxy_INCLUDE_DIR 12 | NAMES proxy.h 13 | PATH_SUFFIXES include 14 | ) 15 | 16 | find_library(libproxy_LIBRARY proxy) 17 | 18 | find_package_handle_standard_args( 19 | libproxy 20 | DEFAULT_MSG 21 | libproxy_INCLUDE_DIR 22 | libproxy_LIBRARY 23 | ) 24 | 25 | if(libproxy_FOUND) 26 | set(libproxy_INCLUDE_DIRS ${libproxy_INCLUDE_DIR}) 27 | set(libproxy_LIBRARIES ${libproxy_LIBRARY}) 28 | 29 | message(STATUS "lib: ${libproxy_LIBRARY}, found: ${libproxy_FOUND}") 30 | 31 | if(NOT TARGET libproxy::proxy) 32 | add_library (libproxy::proxy INTERFACE IMPORTED) 33 | set_target_properties( 34 | libproxy::proxy 35 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${libproxy_INCLUDE_DIRS}" 36 | INTERFACE_LINK_LIBRARIES "${libproxy_LIBRARIES}" 37 | ) 38 | endif() 39 | endif() 40 | -------------------------------------------------------------------------------- /common/cmake/do-packaging-helpers.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | macro (set_common_cpack_vars name description) 5 | set(CPACK_PACKAGE_NAME ${name}) 6 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${description}) 7 | set(CPACK_PACKAGE_VENDOR "Delivery Optimization Team") 8 | set(CPACK_PACKAGE_CONTACT "docloss@Microsoft.com") 9 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/microsoft/do-client") 10 | set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md") 11 | set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) 12 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") 13 | 14 | if (DO_PACKAGE_TYPE STREQUAL "DEB") 15 | set(CPACK_GENERATOR "DEB") 16 | set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) 17 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/microsoft/do-client") 18 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "docloss@microsoft.com") 19 | # Automatically detect and enforced shared lib dependencies. 20 | # Note: Automatic dependency resolution requires integration with a package repository. 21 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) 22 | elseif (DO_PACKAGE_TYPE STREQUAL "RPM") 23 | set(CPACK_GENERATOR "RPM") 24 | set(CPACK_RPM_FILE_NAME RPM-DEFAULT) 25 | endif () 26 | endmacro () 27 | -------------------------------------------------------------------------------- /common/lib-dohttp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | find_path(GSL_INCLUDE_DIR gsl) 5 | if (GSL_INCLUDE_DIR STREQUAL "GSL_INCLUDE_DIR-NOTFOUND") 6 | message(FATAL_ERROR "Could not find MS Guidelines Support Library.") 7 | endif() 8 | 9 | add_library(dohttp STATIC 10 | do_cpprest_uri_builder.cpp 11 | do_cpprest_uri.cpp 12 | do_cpprest_utils.cpp 13 | do_http_defines.cpp 14 | do_http_parser.cpp 15 | ) 16 | target_include_directories(dohttp 17 | INTERFACE 18 | ${CMAKE_CURRENT_SOURCE_DIR} 19 | PUBLIC 20 | ${GSL_INCLUDE_DIR} 21 | ) 22 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_cpprest_uri.h: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (C) Microsoft. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 | * 5 | * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 | * 7 | * Protocol independent support for URIs. 8 | * 9 | * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk 10 | * 11 | * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 12 | ****/ 13 | 14 | #ifndef _DELIVERY_OPTIMIZATION_DO_URI_H 15 | #define _DELIVERY_OPTIMIZATION_DO_URI_H 16 | 17 | #include "do_cpprest_base_uri.h" 18 | #include "do_cpprest_uri_builder.h" 19 | 20 | #endif // _DELIVERY_OPTIMIZATION_DO_URI_H 21 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_cpprest_utils.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (C) Microsoft. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 | * 5 | * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 | * 7 | * Utilities 8 | * 9 | * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk 10 | * 11 | * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 12 | ****/ 13 | 14 | #include "do_cpprest_utils.h" 15 | 16 | namespace microsoft 17 | { 18 | namespace deliveryoptimization 19 | { 20 | namespace details 21 | { 22 | namespace cpprest_utils 23 | { 24 | 25 | namespace 26 | { 27 | struct to_lower_ch_impl 28 | { 29 | char operator()(char c) const CPPREST_NOEXCEPT 30 | { 31 | if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); 32 | return c; 33 | } 34 | 35 | wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT 36 | { 37 | if (c >= L'A' && c <= L'Z') return static_cast(c - L'A' + L'a'); 38 | return c; 39 | } 40 | }; 41 | 42 | CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {}; 43 | } // namespace 44 | 45 | _ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT 46 | { 47 | for (auto& ch : target) 48 | { 49 | ch = to_lower_ch(ch); 50 | } 51 | } 52 | 53 | } // namespace cpprest_utils 54 | } // namespace details 55 | } // namespace deliveryoptimization 56 | } // namespace microsoft 57 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_http_defines.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_http_defines.h" 5 | 6 | namespace microsoft 7 | { 8 | namespace deliveryoptimization 9 | { 10 | namespace details 11 | { 12 | 13 | const char* const http_methods::GET = "GET"; 14 | const char* const http_methods::POST = "POST"; 15 | 16 | } // namespace details 17 | } // namespace deliveryoptimization 18 | } // namespace microsoft 19 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_http_defines.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_HTTP_DEFINES_H 5 | #define _DELIVERY_OPTIMIZATION_DO_HTTP_DEFINES_H 6 | 7 | namespace microsoft 8 | { 9 | namespace deliveryoptimization 10 | { 11 | namespace details 12 | { 13 | 14 | class http_methods 15 | { 16 | public: 17 | static const char* const GET; 18 | static const char* const POST; 19 | }; 20 | 21 | enum http_status_codes 22 | { 23 | OK = 200, 24 | Created = 201, 25 | Accepted = 202, 26 | NonAuthInfo = 203, 27 | NoContent = 204, 28 | PartialContent = 206, 29 | 30 | MultipleChoices = 300, 31 | MovedPermanently = 301, 32 | Found = 302, 33 | SeeOther = 303, 34 | UseProxy = 305, 35 | 36 | BadRequest = 400, 37 | Unauthorized = 401, 38 | Forbidden = 403, 39 | NotFound = 404, 40 | MethodNotAllowed = 405, 41 | NotAcceptable = 406, 42 | ProxyAuthRequired = 407, 43 | RequestTimeout = 408, 44 | Conflict = 409, 45 | Gone = 410, 46 | LengthRequired = 411, 47 | PreconditionFailed = 412, 48 | RequestEntityTooLarge = 413, 49 | RequestUriTooLarge = 414, 50 | UnsupportedMediaType = 415, 51 | 52 | InternalError = 500, 53 | NotImplemented = 501, 54 | BadGateway = 502, 55 | ServiceUnavailable = 503, 56 | GatewayTimeout = 504, 57 | HttpVersionNotSupported = 505, 58 | }; 59 | 60 | } // namespace details 61 | } // namespace deliveryoptimization 62 | } // namespace microsoft 63 | 64 | #endif // _DELIVERY_OPTIMIZATION_DO_HTTP_DEFINES_H 65 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_http_packet.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_HTTP_PACKET_H 5 | #define _DELIVERY_OPTIMIZATION_DO_HTTP_PACKET_H 6 | 7 | #include 8 | #include 9 | #include "do_cpprest_uri.h" 10 | 11 | namespace microsoft 12 | { 13 | namespace deliveryoptimization 14 | { 15 | namespace details 16 | { 17 | 18 | struct HttpPacket 19 | { 20 | std::string method; // request 21 | cpprest_web::uri url; // request or response 22 | unsigned int statusCode { 0 }; // response 23 | size_t contentLength { 0 }; // request or response 24 | std::stringstream body; // request or response 25 | }; 26 | 27 | } // namespace details 28 | } // namespace deliveryoptimization 29 | } // namespace microsoft 30 | 31 | #endif // _DELIVERY_OPTIMIZATION_DO_HTTP_PACKET_H 32 | -------------------------------------------------------------------------------- /common/lib-dohttp/do_http_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_HTTP_PARSER_H 5 | #define _DELIVERY_OPTIMIZATION_DO_HTTP_PARSER_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "do_http_packet.h" 12 | 13 | namespace microsoft 14 | { 15 | namespace deliveryoptimization 16 | { 17 | namespace details 18 | { 19 | 20 | // Very limited parsing abilities, just enough to support the SDK/Agent's requests and responses. 21 | // Credit: Code takes a little inspiration from Boost.Beast. Too bad it is not available on Ubuntu 18.04. 22 | class HttpParser 23 | { 24 | public: 25 | HttpParser(); 26 | 27 | void OnData(const char* pData, size_t cb); 28 | void Reset(); 29 | 30 | bool Done() const noexcept 31 | { 32 | return (_state == ParserState::Complete); 33 | } 34 | 35 | const std::string& Method() const { return _parsedData->method; } 36 | const cpprest_web::uri& Url() const { return _parsedData->url; } 37 | unsigned int StatusCode() const { return _parsedData->statusCode; } 38 | std::stringstream& Body() { return _parsedData->body; } 39 | const std::shared_ptr& ParsedData() const { return _parsedData; } 40 | 41 | private: 42 | bool _ParseBuf(); 43 | bool _ParseNextField(); 44 | std::vector::iterator _FindCRLF(std::vector::iterator itStart); 45 | 46 | // Parsing info 47 | enum class ParserState 48 | { 49 | FirstLine, 50 | Fields, 51 | Body, 52 | Complete 53 | }; 54 | 55 | ParserState _state { ParserState::FirstLine }; 56 | std::vector _incomingDataBuf; 57 | std::vector::iterator _itParseFrom; 58 | 59 | std::shared_ptr _parsedData; 60 | }; 61 | 62 | } // namespace details 63 | } // namespace deliveryoptimization 64 | } // namespace microsoft 65 | #endif 66 | -------------------------------------------------------------------------------- /common/lib-dotestutil/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | set(test_lib_name dotestutil) 5 | 6 | find_package(Boost COMPONENTS system) # for asio 7 | 8 | add_library(${test_lib_name} STATIC do_test_helpers.cpp) 9 | add_platform_interface_definitions(${test_lib_name}) 10 | target_include_directories(${test_lib_name} 11 | PUBLIC 12 | ${Boost_INCLUDE_DIRS} #boost/asio.hpp 13 | INTERFACE 14 | ${CMAKE_CURRENT_SOURCE_DIR} 15 | ) -------------------------------------------------------------------------------- /common/lib-doversion/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | set(version_lib_name doversion) 5 | add_library(${version_lib_name} STATIC do_version.cpp) 6 | target_include_directories(${version_lib_name} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 7 | if (DO_COMPONENT_NAME AND DO_COMPONENT_VERSION) 8 | add_component_version_definitions(${version_lib_name} ${DO_COMPONENT_NAME} ${DO_COMPONENT_VERSION}) 9 | else () 10 | message (FATAL_ERROR "Component info not set. Make sure it is set before adding this library.") 11 | endif () 12 | -------------------------------------------------------------------------------- /common/lib-doversion/do_version.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_version.h" 5 | #include 6 | #include 7 | #include 8 | 9 | const char* const g_appBuilder = DO_VER_BUILDER_IDENTIFIER; 10 | const char* const g_appName = DO_VER_COMPONENT_NAME; 11 | const char* const g_appVersion = DO_VER_COMPONENT_VERSION; 12 | const char* const g_appBuildTime = DO_VER_BUILD_TIME; 13 | const char* const g_gitHeadRev = DO_VER_GIT_HEAD_REVISION; 14 | const char* const g_gitHeadName = DO_VER_GIT_HEAD_NAME; 15 | 16 | namespace microsoft 17 | { 18 | namespace deliveryoptimization 19 | { 20 | namespace util 21 | { 22 | namespace details 23 | { 24 | 25 | const char* SimpleVersion() 26 | { 27 | return g_appVersion; 28 | } 29 | 30 | // Example version string returned: deliveryoptimization-plugin-apt/v0.1.1+20200819.000911.70fc72d 31 | // Format is: /v<3 part version>+.[.] 32 | std::string ComponentVersion(bool fIncludeExtras) 33 | { 34 | std::stringstream ss; 35 | if (*g_appBuilder != '\0') 36 | { 37 | ss << g_appBuilder << ";"; 38 | } 39 | ss << g_appName << "/v" << g_appVersion; 40 | if (*g_appBuildTime != '\0') 41 | { 42 | ss << '+' << g_appBuildTime; 43 | } 44 | if (*g_gitHeadRev != '\0') 45 | { 46 | ss << ((*g_appBuildTime != '\0') ? '.' : '+') << g_gitHeadRev; 47 | } 48 | if (fIncludeExtras && (*g_gitHeadName != '\0')) 49 | { 50 | ss << " (" << g_gitHeadName << ')'; 51 | } 52 | return ss.str(); 53 | } 54 | 55 | bool OutputVersionIfNeeded(int argc, char** argv) 56 | { 57 | if (argc == 2) 58 | { 59 | const bool fVersionEx = (strcmp(argv[1], "--version-extra") == 0); 60 | if (fVersionEx || ((strcmp(argv[1], "--version") == 0) || (strcmp(argv[1], "-v") == 0))) 61 | { 62 | const auto ver = ComponentVersion(fVersionEx); 63 | printf("%s\n", ver.c_str()); 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | } // namespace details 71 | } // namespace util 72 | } // namespace deliveryoptimization 73 | } // namespace microsoft 74 | -------------------------------------------------------------------------------- /common/lib-doversion/do_version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace microsoft 9 | { 10 | namespace deliveryoptimization 11 | { 12 | namespace util 13 | { 14 | namespace details 15 | { 16 | 17 | const char* SimpleVersion(); 18 | std::string ComponentVersion(bool fIncludeExtras = true); 19 | bool OutputVersionIfNeeded(int argc, char** argv); 20 | 21 | } // namespace details 22 | } // namespace util 23 | } // namespace deliveryoptimization 24 | } // namespace microsoft 25 | -------------------------------------------------------------------------------- /plugins/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # To build via cmdline we can run the following: 5 | # mkdir build.d 6 | # cd build.d 7 | # cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. 8 | # cmake --build . [--clean-first] 9 | 10 | add_subdirectory(linux-apt) 11 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_date_time.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using filetime_duration_t = std::chrono::duration>; 10 | using wall_clock_t = std::chrono::system_clock; 11 | 12 | #define filetime_cast(d) std::chrono::duration_cast(d) 13 | #define seconds_cast(d) std::chrono::duration_cast(d) 14 | 15 | inline std::array SysTimePointToUTCString(wall_clock_t::time_point timePoint) 16 | { 17 | const auto tt = wall_clock_t::to_time_t(timePoint); 18 | struct tm st = {}; 19 | gmtime_r(&tt, &st); 20 | 21 | auto ft = filetime_cast(timePoint.time_since_epoch()); 22 | auto fractionalSeconds = ft - seconds_cast(ft); 23 | std::array timebuf = {}; 24 | snprintf(timebuf.data(), timebuf.size(), "%04d-%02d-%02dT%02d:%02d:%02d.%07dZ", 25 | st.tm_year + 1900, st.tm_mon + 1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, static_cast(fractionalSeconds.count())); 26 | return timebuf; 27 | } 28 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_hash.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | struct HashResult 9 | { 10 | size_t fileSizeBytes; 11 | std::string md5Digest; 12 | std::string md5SumDigest; 13 | std::string sha1Digest; 14 | std::string sha256Digest; 15 | std::string sha512Digest; 16 | }; 17 | 18 | HashResult FileHashes(const std::string& filePath); 19 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_log.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_log.h" 5 | 6 | #include // getpid 7 | #include // SYS_gettid 8 | #include 9 | #include 10 | #include "do_date_time.h" 11 | #include "do_plugin_exception.h" 12 | 13 | static std::array g_GetLogLinePrefix() 14 | { 15 | static const pid_t pid = getpid(); 16 | const auto tid = static_cast(syscall(SYS_gettid)); // using syscall because glibc wrapper is unavailable 17 | const auto timeStr = SysTimePointToUTCString(wall_clock_t::now()); 18 | 19 | // Timestamp ProcessID ThreadID msg 20 | std::array prefixBuf; 21 | snprintf(prefixBuf.data(), prefixBuf.size(), "%s %-5d %-5d ", timeStr.data(), pid, tid); 22 | return prefixBuf; 23 | } 24 | 25 | #ifdef DEBUG 26 | 27 | void LogDebugMessage(const std::string& msg) 28 | { 29 | static std::fstream logStream; 30 | if (!logStream.is_open()) 31 | { 32 | logStream.open(DO_PLUGIN_APT_LOG_PATH, std::ios_base::app | std::ios_base::ate); 33 | if (logStream.bad()) 34 | { 35 | std::cerr << "Couldn't open debug log file at " << DO_PLUGIN_APT_LOG_PATH << std::endl; 36 | return; 37 | } 38 | } 39 | 40 | auto prefixBuf = g_GetLogLinePrefix(); 41 | logStream << prefixBuf.data() << " " << msg << "\n"; 42 | logStream.flush(); 43 | } 44 | 45 | #endif // DEBUG 46 | 47 | void LogErrorMessage(const std::string& msg) 48 | { 49 | auto prefixBuf = g_GetLogLinePrefix(); 50 | std::cerr << prefixBuf.data() << " " << msg << "\n"; 51 | std::cerr.flush(); 52 | 53 | #ifdef DEBUG 54 | // Also write to debug log 55 | LogDebugMessage(msg); 56 | #endif 57 | } 58 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "do_string_util.h" 8 | 9 | #ifdef DEBUG 10 | 11 | void LogDebugMessage(const std::string& msg); 12 | 13 | template 14 | void LogDebug(const char* fmt, Args&&... args) 15 | { 16 | const std::string msg = strutil::FormatString(fmt, std::forward(args)...); 17 | LogDebugMessage(msg); 18 | } 19 | 20 | #else 21 | 22 | #define LogDebug(...) 23 | 24 | #endif // DEBUG 25 | 26 | void LogErrorMessage(const std::string& msg); 27 | 28 | template 29 | void LogError(const char* fmt, Args&&... args) 30 | { 31 | const std::string msg = strutil::FormatString(fmt, std::forward(args)...); 32 | LogErrorMessage(msg); 33 | } 34 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_plugin_exception.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "do_string_util.h" 8 | 9 | class DOPluginException 10 | { 11 | private: 12 | std::string _msg; 13 | 14 | public: 15 | DOPluginException(std::string msg) : 16 | _msg(std::move(msg)) 17 | { 18 | } 19 | 20 | // This explicit overload prevents format-security warning 21 | // for callers who log a simple string without any format arguments. 22 | explicit DOPluginException(const char* msg) : 23 | _msg(msg) 24 | { 25 | } 26 | 27 | template 28 | explicit DOPluginException(const char* fmt, Args&&... args) 29 | { 30 | _msg = strutil::FormatString(fmt, std::forward(args)...); 31 | } 32 | 33 | const char* what() const { return _msg.c_str(); } 34 | }; 35 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_string_util.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_string_util.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace strutil 10 | { 11 | 12 | std::string HexEncode(const unsigned char* src, size_t len) 13 | { 14 | std::string strResult; 15 | boost::algorithm::hex(src, src + len, std::back_inserter(strResult)); 16 | boost::algorithm::to_lower(strResult); 17 | return strResult; 18 | } 19 | 20 | } // namespace strutil 21 | -------------------------------------------------------------------------------- /plugins/linux-apt/do_string_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace strutil 10 | { 11 | 12 | template 13 | std::string MapToString(const T& map) 14 | { 15 | std::stringstream ss; 16 | for (const auto& item : map) 17 | { 18 | ss << item.first << ": " << item.second << "\n"; 19 | } 20 | return ss.str(); 21 | } 22 | 23 | // This explicit overload prevents format-security warning 24 | // at call sites that do not pass any format arguments. 25 | inline std::string FormatString(const char* msg) 26 | { 27 | return std::string{msg}; 28 | } 29 | 30 | template 31 | std::string FormatString(const char* fmt, Args&&... args) 32 | { 33 | char msgBuf[1024]; 34 | snprintf(msgBuf, sizeof(msgBuf), fmt, std::forward(args)...); 35 | return { msgBuf }; 36 | } 37 | 38 | std::string HexEncode(const unsigned char* src, size_t len); 39 | 40 | inline bool StartsWith(const std::string& str, const char* s) noexcept 41 | { 42 | return (str.rfind(s, 0) == 0); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /plugins/linux-apt/scripts/postinst.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # Exit early to fail the install if any command here fails 7 | set -e 8 | 9 | echo "Running post-install script for @target_bin@" 10 | 11 | echo "Configuring DO plugin as the http(s) method" 12 | if [ ! -f @dopapt_bin_path@ ]; then 13 | echo "Plugin binary cannot be found at @dopapt_bin_path@"; 14 | return 1 15 | fi 16 | 17 | if [ ! -d @dopapt_cache_path@ ]; then 18 | echo "Creating @dopapt_cache_path@" 19 | mkdir -p @dopapt_cache_path@ 20 | fi 21 | 22 | if getent passwd @do_user_group@ > /dev/null; then 23 | echo "Configuring @do_user_group@ for write access into plugin cache" 24 | chgrp @do_user_group@ @dopapt_cache_path@ 25 | chmod g+w @dopapt_cache_path@ 26 | else 27 | echo "DO user not found. Not changing permissions for @dopapt_cache_path@." 28 | fi 29 | 30 | # Backup 31 | if [ -f @apt_methods_root@/http ]; then 32 | mv @apt_methods_root@/http @apt_methods_root@/http-backup 33 | # https is usually a symlink to http so no need to back it up 34 | ls -l @apt_methods_root@/http-backup 35 | else 36 | echo "http method not found in @apt_methods_root@. Nothing to backup." 37 | fi 38 | 39 | echo "Creating symlink @apt_methods_root@/http -> @dopapt_bin_path@" 40 | ln -s @dopapt_bin_path@ @apt_methods_root@/http 41 | 42 | echo "Configured http(s) method to use the DO plugin" 43 | ls -l @apt_methods_root@/http 44 | 45 | echo "Done!" 46 | -------------------------------------------------------------------------------- /plugins/linux-apt/scripts/postrm.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # Exit early to fail the remove if any command here fails 7 | set -e 8 | 9 | echo "Running post-removal script for @target_bin@" 10 | 11 | if [ -d @dopapt_cache_path@ ]; then 12 | echo "Removing @dopapt_cache_path@." 13 | rm -rf @dopapt_cache_path@ 14 | fi 15 | 16 | if [ -f @dopapt_log_path@ ]; then 17 | echo "Removing @dopapt_log_path@." 18 | rm @dopapt_log_path@ 19 | fi 20 | 21 | echo "Done!" 22 | -------------------------------------------------------------------------------- /plugins/linux-apt/scripts/prerm.in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # Exit early to fail the remove if any command here fails 7 | set -e 8 | 9 | echo "Running pre-removal script for @target_bin@" 10 | 11 | echo "Restoring method" 12 | if [ -f @apt_methods_root@/http-backup ]; then 13 | mv @apt_methods_root@/http-backup @apt_methods_root@/http 14 | echo "Restored original methods." 15 | else 16 | echo "Method backup not found. Not doing anything." 17 | fi 18 | 19 | ls -l @apt_methods_root@/http 20 | 21 | echo "Done!" 22 | -------------------------------------------------------------------------------- /sdk-cpp/build/cleanup-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # Script to manually uninstall the SDK from the system. 7 | # Use this when not using a package manager to install the SDK. 8 | 9 | # This method deletes stuff so beware of the path provided! 10 | remove_sdk() { 11 | if [ $1 == "" ]; then 12 | echo "Base path not provided" 13 | elif [ $2 == "" ]; then 14 | echo "sdk name not provided" 15 | else 16 | base_path=$1 17 | lib_name=$2 18 | echo "Removing from $base_path" 19 | rm $base_path/lib/lib$lib_name.* 20 | rm -r $base_path/lib/cmake/$lib_name/ 21 | rm -r $base_path/include/$lib_name/ 22 | rm $base_path/bin/docs 23 | rm -r $base_path/bin/docs-daemon/ 24 | ls -lr $base_path/lib/ | grep *$lib_name* 25 | ls -l $base_path/include/ | grep $lib_name 26 | fi 27 | } 28 | 29 | remove_sdk /usr deliveryoptimization 30 | remove_sdk /usr/local deliveryoptimization 31 | # Remove cmake config files as well 32 | remove_sdk /usr deliveryoptimization_sdk 33 | remove_sdk /usr/local deliveryoptimization_sdk 34 | 35 | # Also look for remnants using the older name 36 | remove_sdk /usr ms-do-sdk 37 | remove_sdk /usr/local ms-do-sdk 38 | # Remove cmake config files as well 39 | remove_sdk /usr ms_do_sdk 40 | remove_sdk /usr/local ms_do_sdk 41 | remove_sdk /usr dosdkcpp 42 | remove_sdk /usr/local dosdkcpp 43 | remove_sdk /usr docsdk 44 | remove_sdk /usr/local docsdk 45 | -------------------------------------------------------------------------------- /sdk-cpp/build/cmake/deliveryoptimization_sdk-config.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | if (NOT TARGET Microsoft::deliveryoptimization) 5 | include("${CMAKE_CURRENT_LIST_DIR}/deliveryoptimization_sdk-targets.cmake") 6 | endif () 7 | 8 | if (TARGET Microsoft::deliveryoptimization) 9 | if (CMAKE_SYSTEM_NAME MATCHES Linux) 10 | 11 | # Ubuntu 20.04 symlinks /lib to /usr/lib but there is no equivalent /include path. 12 | # This makes cmake's auto-generated targets file set include dirs to the non-existent 13 | # /include path. Fix it by updating the INTERFACE_INCLUDE_DIRECTORIES property directly. 14 | 15 | get_target_property(DOSDK_INC_DIR_PRIV Microsoft::deliveryoptimization INTERFACE_INCLUDE_DIRECTORIES) 16 | if (${DOSDK_INC_DIR_PRIV} MATCHES "^/include" AND NOT EXISTS "/include") 17 | message(WARNING "/include does not exist. Updating include dirs from ${DOSDK_INC_DIR_PRIV} to /usr${DOSDK_INC_DIR_PRIV}.") 18 | set_target_properties(Microsoft::deliveryoptimization PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "/usr${DOSDK_INC_DIR_PRIV}") 19 | endif () 20 | 21 | # Cleanup temporary variable 22 | set(DOSDK_INC_DIR_PRIV) 23 | 24 | endif () 25 | endif () 26 | -------------------------------------------------------------------------------- /sdk-cpp/include/do_config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_CONFIG_H 5 | #define _DELIVERY_OPTIMIZATION_DO_CONFIG_H 6 | 7 | /* 8 | This file exposes apis for helping set an iot_connection_string for the DO agent, so that an application can supply a Microsoft Connected Cache device's hostname 9 | While this file will compile for all platforms, they only serve a purpose for the DeliveryOptimization Agent on linux devices, all other usage will fail and return errc::not_impl 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" 14 | { 15 | #endif 16 | 17 | // Keeping this as a C API to enable ADU client to call from its lower layer 18 | 19 | // Call this method to let DO know the IoT connection string being used for the device. 20 | // Ideally this will be called early in the IoT application startup phase. For example, 21 | // after ADU client successfully establishes connection with IoT hub. 22 | // Returns: 0 on success, error code otherwise 23 | int deliveryoptimization_set_iot_connection_string(const char* value); 24 | 25 | // Future: Version and any future methods could be moved out from the extern C area 26 | // (and use C++ features) if ADU client does not have a need to call these from its lower layer. 27 | 28 | // Free the returned char pointer using deliveryoptimization_free_version_buf(). 29 | // Return can be NULL if memory allocation failed. 30 | char* deliveryoptimization_get_components_version(); 31 | 32 | void deliveryoptimization_free_version_buf(char** ppBuffer); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif // _DELIVERY_OPTIMIZATION_DO_CONFIG_H 39 | -------------------------------------------------------------------------------- /sdk-cpp/include/do_download_status.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_STATUS_H 5 | #define _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_STATUS_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "do_errors.h" 12 | 13 | namespace microsoft 14 | { 15 | namespace deliveryoptimization 16 | { 17 | 18 | enum class download_state 19 | { 20 | created, 21 | transferring, 22 | transferred, 23 | finalized, 24 | aborted, 25 | paused, 26 | }; 27 | 28 | class download_status 29 | { 30 | public: 31 | download_status() = default; 32 | 33 | download_status(uint64_t bytesTotal, uint64_t bytesTransferred, int32_t errorCode, int32_t extendedErrorCode, download_state state) noexcept : 34 | _bytesTotal(bytesTotal), 35 | _bytesTransferred(bytesTransferred), 36 | _errorCode(errorCode), 37 | _extendedErrorCode(extendedErrorCode), 38 | _state(state) 39 | { 40 | } 41 | 42 | bool is_error() const noexcept; 43 | 44 | bool is_transient_error() const noexcept; 45 | 46 | bool is_complete() const noexcept; 47 | 48 | uint64_t bytes_total() const noexcept 49 | { 50 | return _bytesTotal; 51 | } 52 | 53 | uint64_t bytes_transferred() const noexcept 54 | { 55 | return _bytesTransferred; 56 | } 57 | 58 | std::error_code error_code() const noexcept; 59 | std::error_code extended_error_code() const noexcept; 60 | 61 | download_state state() const noexcept 62 | { 63 | return _state; 64 | } 65 | 66 | private: 67 | uint64_t _bytesTotal { 0 }; 68 | uint64_t _bytesTransferred { 0 }; 69 | int32_t _errorCode { 0 }; 70 | int32_t _extendedErrorCode { 0 }; 71 | download_state _state { download_state::created }; 72 | }; 73 | 74 | class download; 75 | using status_callback_t = std::function; 76 | 77 | // Future: use std::span (requires C++20) 78 | using output_stream_callback_t = std::function; 79 | 80 | // TODO: create a new header for things that aren't 'status' related, such as output_stream_callback_t and ranges 81 | struct download_range 82 | { 83 | uint64_t offset; 84 | uint64_t length; 85 | }; 86 | static constexpr uint64_t length_to_eof = static_cast(-1); 87 | 88 | } // namespace deliveryoptimization 89 | } // namespace microsoft 90 | 91 | #endif // _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_STATUS_H 92 | -------------------------------------------------------------------------------- /sdk-cpp/include/do_errors.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_ERRORS_H 5 | #define _DELIVERY_OPTIMIZATION_DO_ERRORS_H 6 | 7 | #include 8 | #include 9 | 10 | namespace microsoft 11 | { 12 | namespace deliveryoptimization 13 | { 14 | namespace errc 15 | { 16 | 17 | constexpr auto not_impl = static_cast(0x80004001); // E_NOTIMPL 18 | constexpr auto unexpected = static_cast(0x8000FFFF); // E_UNEXPECTED 19 | constexpr auto invalid_arg = static_cast(0x80070057); // E_INVALIDARG 20 | constexpr auto not_found = static_cast(0x80070490); // E_NOT_SET (ERROR_NOT_FOUND) 21 | constexpr auto no_service = static_cast(0x80D01001); // DO_E_NO_SERVICE 22 | constexpr auto download_no_progress = static_cast(0x80D02002); // DO_E_DOWNLOAD_NO_PROGRESS 23 | constexpr auto no_downloads = static_cast(0x80D02005); // DO_E_NO_DOWNLOADS 24 | constexpr auto unknown_property_id = static_cast(0x80D02011); // DO_E_UNKNOWN_PROPERTY_ID 25 | constexpr auto invalid_state = static_cast(0x80D02013); // DO_E_INVALID_STATE 26 | 27 | } //namespace errc 28 | } //namespace deliveryoptimization 29 | } //namespace microsoft 30 | 31 | #endif //_DELIVERY_OPTIMIZATION_DO_ERRORS_H 32 | -------------------------------------------------------------------------------- /sdk-cpp/src/do_config.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_config.h" 5 | #include "do_config_internal.h" 6 | 7 | extern "C" int deliveryoptimization_set_iot_connection_string(const char* value) 8 | { 9 | return internal_set_iot_connection_string(value); 10 | } 11 | 12 | extern "C" char* deliveryoptimization_get_components_version() 13 | { 14 | return internal_get_components_version(); 15 | } 16 | 17 | extern "C" void deliveryoptimization_free_version_buf(char** ppBuffer) 18 | { 19 | internal_free_version_buf(ppBuffer); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /sdk-cpp/src/do_download_property.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_download_property.h" 5 | 6 | #include "do_download_property_internal.h" 7 | #include "do_error_helpers.h" 8 | 9 | namespace msdod = microsoft::deliveryoptimization::details; 10 | 11 | namespace microsoft 12 | { 13 | namespace deliveryoptimization 14 | { 15 | 16 | download_property_value::download_property_value() 17 | { 18 | _val = std::make_shared(); 19 | } 20 | 21 | std::error_code download_property_value::make(const std::string& val, download_property_value& out) 22 | { 23 | download_property_value temp; 24 | std::error_code code = temp._val->Init(val); 25 | DO_RETURN_IF_FAILED(code); 26 | 27 | out = temp; 28 | return DO_OK; 29 | } 30 | 31 | std::error_code download_property_value::make(const std::wstring& val, download_property_value& out) 32 | { 33 | download_property_value temp; 34 | std::error_code code = temp._val->Init(val); 35 | DO_RETURN_IF_FAILED(code); 36 | 37 | out = temp; 38 | return DO_OK; 39 | } 40 | 41 | std::error_code download_property_value::make(uint32_t val, download_property_value& out) 42 | { 43 | download_property_value temp; 44 | std::error_code code = temp._val->Init(val); 45 | DO_RETURN_IF_FAILED(code); 46 | 47 | out = temp; 48 | return DO_OK; 49 | } 50 | 51 | std::error_code download_property_value::make(uint64_t val, download_property_value& out) 52 | { 53 | download_property_value temp; 54 | std::error_code code = temp._val->Init(val); 55 | DO_RETURN_IF_FAILED(code); 56 | 57 | out = temp; 58 | return DO_OK; 59 | } 60 | 61 | std::error_code download_property_value::make(bool val, download_property_value& out) 62 | { 63 | download_property_value temp; 64 | std::error_code code = temp._val->Init(val); 65 | DO_RETURN_IF_FAILED(code); 66 | 67 | out = temp; 68 | return DO_OK; 69 | } 70 | 71 | std::error_code download_property_value::as(bool& val) const noexcept 72 | { 73 | return _val->As(val); 74 | } 75 | 76 | std::error_code download_property_value::as(uint32_t& val) const noexcept 77 | { 78 | return _val->As(val); 79 | } 80 | 81 | std::error_code download_property_value::as(uint64_t& val) const noexcept 82 | { 83 | return _val->As(val); 84 | } 85 | 86 | std::error_code download_property_value::as(std::string& val) const noexcept 87 | { 88 | return _val->As(val); 89 | } 90 | 91 | std::error_code download_property_value::as(std::wstring& val) const noexcept 92 | { 93 | return _val->As(val); 94 | } 95 | 96 | } // deliveryoptimization 97 | } // microsoft 98 | -------------------------------------------------------------------------------- /sdk-cpp/src/do_download_status.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_download_status.h" 5 | #include "do_error_helpers.h" 6 | 7 | namespace microsoft 8 | { 9 | namespace deliveryoptimization 10 | { 11 | 12 | bool download_status::is_error() const noexcept 13 | { 14 | return _errorCode != 0; 15 | } 16 | 17 | bool download_status::is_transient_error() const noexcept 18 | { 19 | return (_state == download_state::paused) && (_errorCode == 0) && (_extendedErrorCode != 0); 20 | } 21 | 22 | bool download_status::is_complete() const noexcept 23 | { 24 | return _state == download_state::transferred; 25 | } 26 | 27 | std::error_code download_status::error_code() const noexcept 28 | { 29 | return details::make_error_code(_errorCode); 30 | } 31 | 32 | std::error_code download_status::extended_error_code() const noexcept 33 | { 34 | return details::make_error_code(_extendedErrorCode); 35 | } 36 | 37 | } // namespace deliveryoptimization 38 | } // namespace microsoft 39 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/com/do_config_internal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "do_config_internal.h" 3 | 4 | #include "do_errors.h" 5 | 6 | namespace msdo = microsoft::deliveryoptimization; 7 | 8 | int internal_set_iot_connection_string(const char* value) 9 | { 10 | return msdo::errc::not_impl; 11 | } 12 | 13 | char* internal_get_components_version() 14 | { 15 | return nullptr; 16 | } 17 | 18 | void internal_free_version_buf(char** ppBuffer) 19 | { 20 | if (*ppBuffer) 21 | { 22 | free(*ppBuffer); 23 | *ppBuffer = nullptr; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/do_config_internal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_CONFIG_INTERNAL_H 5 | #define _DELIVERY_OPTIMIZATION_CONFIG_INTERNAL_H 6 | 7 | // Since the public api is declared as extern c, these can't be defined using the same namespace convention (namespace mangling occurs due to declaration of extern c) 8 | 9 | int internal_set_iot_connection_string(const char* value); 10 | 11 | char* internal_get_components_version(); 12 | 13 | void internal_free_version_buf(char** ppBuffer); 14 | 15 | #endif // _DELIVERY_OPTIMIZATION_INTERNAL_CONFIG_H 16 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/do_download_property_internal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_PROPERTY_INTERNAL_H 5 | #define _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_PROPERTY_INTERNAL_H 6 | 7 | #if defined(DO_INTERFACE_COM) 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | 15 | #include "do_download_property.h" 16 | #include "do_errors.h" 17 | 18 | namespace microsoft 19 | { 20 | namespace deliveryoptimization 21 | { 22 | namespace details 23 | { 24 | 25 | #if defined(DO_INTERFACE_COM) 26 | std::error_code UTF8toWstr(const std::string& str, std::wstring& wstr); 27 | std::error_code WstrToUTF8(const std::wstring& wstr, std::string& str); 28 | 29 | struct unique_variant : VARIANT 30 | { 31 | unique_variant(); 32 | explicit unique_variant(const VARIANT& other) noexcept; // takes ownership via shallow copy 33 | unique_variant(unique_variant&& other) noexcept; 34 | unique_variant& operator=(unique_variant&& other) noexcept; 35 | ~unique_variant(); 36 | 37 | unique_variant(const unique_variant& other) = delete; 38 | unique_variant& operator=(const unique_variant&) = delete; 39 | unique_variant& operator=(const VARIANT&) = delete; 40 | }; 41 | #endif 42 | 43 | class CDownloadPropertyValueInternal 44 | { 45 | public: 46 | #if defined(DO_INTERFACE_COM) 47 | using native_type = unique_variant; 48 | #else 49 | using native_type = boost::variant; 50 | #endif 51 | 52 | CDownloadPropertyValueInternal() = default; 53 | ~CDownloadPropertyValueInternal() = default; 54 | 55 | std::error_code Init(const std::string& val) noexcept; 56 | std::error_code Init(const std::wstring& val) noexcept; 57 | std::error_code Init(uint32_t val) noexcept; 58 | std::error_code Init(uint64_t val) noexcept; 59 | std::error_code Init(bool val) noexcept; 60 | 61 | std::error_code As(bool& val) const noexcept; 62 | std::error_code As(uint32_t& val) const noexcept; 63 | std::error_code As(uint64_t& val) const noexcept; 64 | std::error_code As(std::string& val) const noexcept; 65 | std::error_code As(std::wstring& val) const noexcept; 66 | 67 | const native_type& native_value() const noexcept { return _var; } 68 | 69 | private: 70 | native_type _var; 71 | }; 72 | 73 | } // namespace details 74 | } // namespace deliveryoptimization 75 | } // namespace microsoft 76 | 77 | #endif // _DELIVERY_OPTIMIZATION_DO_DOWNLOAD_PROPERTY_H 78 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/do_error_helpers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_error_helpers.h" 5 | 6 | namespace microsoft 7 | { 8 | namespace deliveryoptimization 9 | { 10 | namespace details 11 | { 12 | 13 | const error_category& do_category() 14 | { 15 | static error_category instance; 16 | return instance; 17 | } 18 | 19 | const char* error_category::name() const noexcept 20 | { 21 | return "delivery optimization error"; 22 | } 23 | std::string error_category::message(int32_t /*code*/) const 24 | { 25 | // TODO(shishirb) describe common error codes 26 | return "unrecognized error"; 27 | } 28 | 29 | } // namespace details 30 | } // namespace deliveryoptimization 31 | } // namespace microsoft 32 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/do_filesystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | 6 | #if defined(__cpp_lib_filesystem) 7 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 8 | #elif defined(__cpp_lib_experimental_filesystem) 9 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 10 | #elif !defined(__has_include) 11 | // Cannot check if headers exist, assume experimental 12 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 13 | #elif __has_include() 14 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 15 | #elif __has_include() 16 | #define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 17 | #else 18 | #error Could not find system header "" or "" 19 | #endif 20 | 21 | #if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 22 | #include 23 | namespace fs = std::experimental::filesystem; 24 | #else 25 | #include 26 | namespace fs = std::filesystem; 27 | #endif 28 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/do_noncopyable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_NONCOPYABLE_H 5 | #define _DELIVERY_OPTIMIZATION_DO_NONCOPYABLE_H 6 | 7 | // Handy base class to create non-copyable classes 8 | class CDONoncopyable 9 | { 10 | public: 11 | CDONoncopyable(const CDONoncopyable&) = delete; 12 | CDONoncopyable& operator=(const CDONoncopyable&) = delete; 13 | 14 | protected: 15 | CDONoncopyable() {} 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/download_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DOWNLOAD_IMPL_H 5 | #define _DELIVERY_OPTIMIZATION_DOWNLOAD_IMPL_H 6 | 7 | #include 8 | 9 | #include "download_interface.h" 10 | 11 | #if defined(DO_INTERFACE_COM) 12 | #include 13 | 14 | // Future: delete the local copy of deliveryoptimization.h and require Windows SDK 22621+ 15 | #include // IDODownload, etc. 16 | #endif 17 | 18 | namespace microsoft 19 | { 20 | namespace deliveryoptimization 21 | { 22 | namespace details 23 | { 24 | class CDownloadImpl : public IDownload 25 | { 26 | public: 27 | CDownloadImpl() = default; 28 | 29 | std::error_code Init(const std::string& uri, const std::string& downloadFilePath) noexcept override; 30 | 31 | std::error_code Start() noexcept override; 32 | std::error_code Pause() noexcept override; 33 | std::error_code Resume() noexcept override; 34 | std::error_code Finalize() noexcept override; 35 | std::error_code Abort() noexcept override; 36 | 37 | std::error_code GetStatus(download_status& status) noexcept override; 38 | std::error_code SetStatusCallback(const status_callback_t& callback, download& download) noexcept override; 39 | std::error_code SetStreamCallback(const output_stream_callback_t& callback) noexcept override; 40 | std::error_code GetProperty(download_property key, download_property_value& value) noexcept override; 41 | std::error_code SetProperty(download_property key, const download_property_value& val) noexcept override; 42 | std::error_code SetRanges(const download_range* ranges, size_t count) noexcept override; 43 | std::error_code SetClientCert(const unsigned char* data, size_t size) noexcept override; 44 | 45 | static std::error_code EnumDownloads(std::vector>& out) noexcept; 46 | static std::error_code EnumDownloads(download_property prop, const std::string& value, std::vector>& out) noexcept; 47 | static std::error_code EnumDownloads(download_property prop, const std::wstring& value, std::vector>& out) noexcept; 48 | 49 | private: 50 | #if defined(DO_INTERFACE_COM) 51 | static std::error_code _EnumDownloads(const DO_DOWNLOAD_ENUM_CATEGORY* pCategory, std::vector>& out) noexcept; 52 | 53 | Microsoft::WRL::ComPtr _spDownload; 54 | std::unique_ptr _spRanges; 55 | #elif defined(DO_INTERFACE_REST) 56 | std::error_code _DownloadOperationCall(const std::string& type) noexcept; 57 | 58 | std::string _id; 59 | #endif 60 | }; 61 | 62 | } // namespace details 63 | } // namespace deliveryoptimization 64 | } // namespace microsoft 65 | 66 | #endif // _DELIVERY_OPTIMIZATION_DOWNLOAD_IMPL_H 67 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/download_interface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DOWNLOAD_INTERFACE_H 5 | #define _DELIVERY_OPTIMIZATION_DOWNLOAD_INTERFACE_H 6 | 7 | #include 8 | 9 | #include "do_download_status.h" 10 | #include "do_download_property.h" 11 | 12 | namespace microsoft 13 | { 14 | namespace deliveryoptimization 15 | { 16 | class download; 17 | 18 | namespace details 19 | { 20 | 21 | class IDownload 22 | { 23 | public: 24 | virtual ~IDownload() = default; 25 | 26 | virtual std::error_code Init(const std::string& uri, const std::string& downloadFilePath) noexcept = 0; 27 | 28 | virtual std::error_code Start() noexcept = 0; 29 | virtual std::error_code Pause() noexcept = 0; 30 | virtual std::error_code Resume() noexcept = 0; 31 | virtual std::error_code Finalize() noexcept = 0; 32 | virtual std::error_code Abort() noexcept = 0; 33 | 34 | virtual std::error_code GetStatus(download_status& status) noexcept = 0; 35 | virtual std::error_code SetStatusCallback(const status_callback_t& callback, download& download) noexcept = 0; 36 | virtual std::error_code SetStreamCallback(const output_stream_callback_t& callback) noexcept = 0; 37 | 38 | virtual std::error_code GetProperty(download_property key, download_property_value& value) noexcept = 0; 39 | virtual std::error_code SetProperty(download_property key, const download_property_value& val) noexcept = 0; 40 | 41 | virtual std::error_code SetRanges(const download_range* ranges, size_t count) noexcept = 0; 42 | virtual std::error_code SetClientCert(const unsigned char* data, size_t size) noexcept = 0; 43 | 44 | }; 45 | } // namespace details 46 | } // namespace deliveryoptimization 47 | } // namespace microsoft 48 | #endif 49 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/do_download_property_internal.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_download_property_internal.h" 5 | 6 | #include "do_error_helpers.h" 7 | 8 | namespace microsoft 9 | { 10 | namespace deliveryoptimization 11 | { 12 | namespace details 13 | { 14 | 15 | std::error_code CDownloadPropertyValueInternal::Init(const std::string& val) noexcept 16 | { 17 | return make_error_code(errc::not_impl); 18 | } 19 | 20 | std::error_code CDownloadPropertyValueInternal::Init(const std::wstring& val) noexcept 21 | { 22 | return make_error_code(errc::not_impl); 23 | } 24 | 25 | std::error_code CDownloadPropertyValueInternal::Init(uint32_t val) noexcept 26 | { 27 | return make_error_code(errc::not_impl); 28 | } 29 | 30 | std::error_code CDownloadPropertyValueInternal::Init(uint64_t val) noexcept 31 | { 32 | return make_error_code(errc::not_impl); 33 | } 34 | 35 | std::error_code CDownloadPropertyValueInternal::Init(bool val) noexcept 36 | { 37 | return make_error_code(errc::not_impl); 38 | } 39 | 40 | std::error_code CDownloadPropertyValueInternal::As(bool& val) const noexcept 41 | { 42 | return make_error_code(errc::not_impl); 43 | } 44 | 45 | std::error_code CDownloadPropertyValueInternal::As(uint32_t& val) const noexcept 46 | { 47 | return make_error_code(errc::not_impl); 48 | } 49 | 50 | std::error_code CDownloadPropertyValueInternal::As(uint64_t& val) const noexcept 51 | { 52 | return make_error_code(errc::not_impl); 53 | } 54 | 55 | std::error_code CDownloadPropertyValueInternal::As(std::string& val) const noexcept 56 | { 57 | return make_error_code(errc::not_impl); 58 | } 59 | 60 | std::error_code CDownloadPropertyValueInternal::As(std::wstring& val) const noexcept 61 | { 62 | return make_error_code(errc::not_impl); 63 | } 64 | 65 | } // namespace details 66 | } // namespace deliveryoptimization 67 | } // namespace microsoft 68 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_http_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _DELIVERY_OPTIMIZATION_DO_HTTP_CLIENT_H 2 | #define _DELIVERY_OPTIMIZATION_DO_HTTP_CLIENT_H 3 | 4 | #include 5 | #include 6 | #include "do_http_message.h" 7 | #include "do_noncopyable.h" 8 | 9 | namespace microsoft 10 | { 11 | namespace deliveryoptimization 12 | { 13 | namespace details 14 | { 15 | 16 | class CHttpClientImpl; 17 | 18 | class CHttpClient : CDONoncopyable 19 | { 20 | public: 21 | ~CHttpClient(); 22 | static CHttpClient& GetInstance(); 23 | boost::property_tree::ptree SendRequest(HttpRequest::Method method, const std::string& url, bool retry = true); 24 | 25 | private: 26 | CHttpClient(); 27 | void _InitializeDOConnection(bool launchClientFirst = false); 28 | 29 | mutable std::mutex _mutex; 30 | std::unique_ptr _httpClientImpl; 31 | }; 32 | } // namespace details 33 | } // namespace deliveryoptimization 34 | } // namespace microsoft 35 | #endif 36 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_http_message.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "do_http_message.h" 5 | 6 | #ifdef DO_DEBUG_REST_INTERFACE 7 | #include 8 | #endif 9 | #include 10 | #include 11 | #include "do_http_defines.h" 12 | 13 | namespace net = boost::asio; // from 14 | 15 | namespace microsoft 16 | { 17 | namespace deliveryoptimization 18 | { 19 | namespace details 20 | { 21 | 22 | HttpRequest::HttpRequest(Method method, const std::string& url) : 23 | _method(method), 24 | _url(url.data()) 25 | { 26 | } 27 | 28 | void HttpRequest::Serialize(boost::asio::ip::tcp::socket& socket) const 29 | { 30 | std::stringstream request; 31 | const char* pVerb = (_method == Method::GET) ? http_methods::GET : http_methods::POST; 32 | request << pVerb << ' ' << _url << ' ' << "HTTP/1.1\r\n"; 33 | request << "Host: 127.0.0.1\r\n"; 34 | request << "User-Agent: DO-SDK-CPP\r\n"; 35 | request << "\r\n"; 36 | 37 | const auto req = request.str(); 38 | #ifdef DO_DEBUG_REST_INTERFACE 39 | std::cout << "Sending request:\n" << req << std::endl; 40 | #endif 41 | net::write(socket, net::buffer(req.data(), req.size())); 42 | } 43 | 44 | void HttpResponse::Deserialize(boost::asio::ip::tcp::socket& socket) 45 | { 46 | std::vector readBuf(1024); 47 | do 48 | { 49 | auto bytesRead = socket.read_some(net::buffer(readBuf.data(), readBuf.size())); 50 | _parser.OnData(readBuf.data(), bytesRead); 51 | } while (!_parser.Done()); 52 | } 53 | 54 | boost::property_tree::ptree HttpResponse::ExtractJsonBody() 55 | { 56 | boost::property_tree::ptree responseBodyJson; 57 | if (_parser.Body().rdbuf()->in_avail() > 0) 58 | { 59 | try 60 | { 61 | boost::property_tree::read_json(_parser.Body(), responseBodyJson); 62 | } 63 | catch (...) 64 | { 65 | } 66 | } 67 | return responseBodyJson; 68 | } 69 | 70 | } // namespace details 71 | } // namespace deliveryoptimization 72 | } // namespace microsoft -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_http_message.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_HTTP_MESSAGE_H 5 | #define _DELIVERY_OPTIMIZATION_DO_HTTP_MESSAGE_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "do_http_parser.h" 12 | 13 | namespace microsoft 14 | { 15 | namespace deliveryoptimization 16 | { 17 | namespace details 18 | { 19 | 20 | // Credit: Code takes a little inspiration from Boost.Beast. Too bad it is not available on Ubuntu 18.04. 21 | class HttpRequest 22 | { 23 | public: 24 | enum Method 25 | { 26 | GET, 27 | POST 28 | }; 29 | 30 | HttpRequest(Method method, const std::string& url); 31 | void Serialize(boost::asio::ip::tcp::socket& socket) const; 32 | 33 | private: 34 | Method _method; 35 | const char* _url; 36 | }; 37 | 38 | class HttpResponse 39 | { 40 | public: 41 | void Deserialize(boost::asio::ip::tcp::socket& socket); 42 | 43 | unsigned int StatusCode() const { return _parser.StatusCode(); } 44 | boost::property_tree::ptree ExtractJsonBody(); 45 | 46 | private: 47 | HttpParser _parser; 48 | }; 49 | 50 | } // namespace details 51 | } // namespace deliveryoptimization 52 | } // namespace microsoft 53 | #endif 54 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_persistence.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include 5 | #include "do_persistence.h" 6 | 7 | #include "do_errors.h" 8 | #include "do_error_helpers.h" 9 | 10 | namespace microsoft 11 | { 12 | namespace deliveryoptimization 13 | { 14 | namespace details 15 | { 16 | 17 | #ifdef DO_BUILD_FOR_SNAP 18 | static std::string ConstructPath(const char* suffix) 19 | { 20 | const char* snapDataRoot = std::getenv("SNAP_DATA"); 21 | if ((snapDataRoot == nullptr) || (*snapDataRoot == '\0')) 22 | { 23 | ThrowException(microsoft::deliveryoptimization::errc::unexpected); 24 | } 25 | std::string outputPath = snapDataRoot; 26 | if (outputPath.back() != '/') 27 | { 28 | outputPath.push_back('/'); 29 | } 30 | if (*suffix == '/') 31 | { 32 | ++suffix; 33 | } 34 | outputPath += suffix; 35 | return outputPath; 36 | } 37 | #endif 38 | 39 | const std::string& GetRuntimeDirectory() 40 | { 41 | #ifdef DO_BUILD_FOR_SNAP 42 | static std::string runDirectory(ConstructPath("do-port-numbers")); 43 | #elif defined(DO_DEV_DEBUG) 44 | static std::string runDirectory("/tmp/run/deliveryoptimization-agent"); 45 | #else 46 | static std::string runDirectory("/var/run/deliveryoptimization-agent"); 47 | #endif 48 | return runDirectory; 49 | } 50 | 51 | const std::string& GetConfigFilePath() 52 | { 53 | #ifdef DO_BUILD_FOR_SNAP 54 | static std::string configFilePath(ConstructPath("do-configs/sdk-config.json")); 55 | #elif defined(DO_DEV_DEBUG) 56 | static std::string configFilePath("/tmp/etc/deliveryoptimization-agent/sdk-config.json"); 57 | #else 58 | static std::string configFilePath("/etc/deliveryoptimization-agent/sdk-config.json"); 59 | #endif 60 | return configFilePath; 61 | } 62 | 63 | // TODO(shishirb): this is used only in test 64 | const std::string& GetAdminConfigFilePath() 65 | { 66 | #ifdef DO_BUILD_FOR_SNAP 67 | static std::string configFilePath(ConstructPath("do-configs/admin-config.json")); 68 | #elif defined(DO_DEV_DEBUG) 69 | static std::string configFilePath("/tmp/etc/deliveryoptimization-agent/admin-config.json"); 70 | #else 71 | static std::string configFilePath("/etc/deliveryoptimization-agent/admin-config.json"); 72 | #endif 73 | return configFilePath; 74 | } 75 | 76 | } // namespace details 77 | } // namespace deliveryoptimization 78 | } // namespace microsoft 79 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_persistence.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_DO_PERSISTENCE_H 5 | #define _DELIVERY_OPTIMIZATION_DO_PERSISTENCE_H 6 | 7 | namespace microsoft 8 | { 9 | namespace deliveryoptimization 10 | { 11 | namespace details 12 | { 13 | 14 | const std::string& GetRuntimeDirectory(); 15 | const std::string& GetConfigFilePath(); 16 | const std::string& GetAdminConfigFilePath(); 17 | 18 | } // namespace details 19 | } // namespace deliveryoptimization 20 | } // namespace microsoft 21 | #endif 22 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_port_finder.cpp: -------------------------------------------------------------------------------- 1 | #include "do_port_finder.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "do_errors.h" 10 | #include "do_error_helpers.h" 11 | #include "do_filesystem.h" 12 | #include "do_persistence.h" 13 | 14 | using namespace std::chrono_literals; // NOLINT(build/namespaces) 15 | 16 | const int32_t g_maxNumPortFileReadAttempts = 3; 17 | 18 | namespace microsoft 19 | { 20 | namespace deliveryoptimization 21 | { 22 | namespace details 23 | { 24 | 25 | static std::string g_DiscoverDOPort() 26 | { 27 | const std::string runtimeDirectory = GetRuntimeDirectory(); 28 | if (!fs::exists(runtimeDirectory)) 29 | { 30 | return std::string(); 31 | } 32 | 33 | fs::path mostRecentFile(""); 34 | auto mostRecentTime = fs::file_time_type::min(); 35 | for (fs::directory_iterator itr(runtimeDirectory); itr != fs::directory_iterator(); ++itr) 36 | { 37 | const fs::path& dirEntry = itr->path(); 38 | if (dirEntry.filename().string().find("restport") != std::string::npos) 39 | { 40 | auto currTime = fs::last_write_time(dirEntry); 41 | if (currTime > mostRecentTime) 42 | { 43 | mostRecentTime = currTime; 44 | mostRecentFile = dirEntry; 45 | } 46 | } 47 | } 48 | std::ifstream file(mostRecentFile.string()); 49 | std::string port; 50 | std::getline(file, port); 51 | 52 | return port; 53 | } 54 | 55 | std::string CPortFinder::GetDOPort(bool launchClientFirst) 56 | { 57 | std::string port; 58 | 59 | for (int numAttempts = 1; (numAttempts <= g_maxNumPortFileReadAttempts) && port.empty(); numAttempts++) 60 | { 61 | // If built as service, then we expect client to already be running. 62 | // We still wait+discover loop below in case client started just now. 63 | // TODO(jimson) Do attempt to start the service if launchClientFirst==true 64 | 65 | // Wait up to 1s for client to write the restport file 66 | for (int i = 1; (i <= 4) && port.empty(); ++i) 67 | { 68 | std::this_thread::sleep_for(250ms); 69 | port = g_DiscoverDOPort(); 70 | } 71 | launchClientFirst = port.empty(); // force launch on further attempts 72 | } 73 | if (port.empty()) 74 | { 75 | ThrowException(errc::no_service); 76 | } 77 | return port; 78 | } 79 | 80 | } // namespace details 81 | } // namespace deliveryoptimization 82 | } // namespace microsoft 83 | -------------------------------------------------------------------------------- /sdk-cpp/src/internal/rest/util/do_port_finder.h: -------------------------------------------------------------------------------- 1 | #ifndef _DELIVERY_OPTIMIZATION_DO_PORT_FINDER_H 2 | #define _DELIVERY_OPTIMIZATION_DO_PORT_FINDER_H 3 | 4 | #include 5 | 6 | namespace microsoft 7 | { 8 | namespace deliveryoptimization 9 | { 10 | namespace details 11 | { 12 | 13 | class CPortFinder 14 | { 15 | public: 16 | static std::string GetDOPort(bool launchClientFirst = false); 17 | }; 18 | 19 | } // namespace details 20 | } // namespace deliveryoptimization 21 | } // namespace microsoft 22 | #endif 23 | -------------------------------------------------------------------------------- /sdk-cpp/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | ## Tests for DO SDK 5 | 6 | find_package(Boost COMPONENTS program_options REQUIRED) 7 | find_package(GTest REQUIRED) 8 | 9 | set(sdk_tests_linked_libs_common 10 | Microsoft::deliveryoptimization 11 | ${Boost_LIBRARIES} 12 | GTest::GTest 13 | dotestutil 14 | ) 15 | 16 | set(dosdkcpp_private_includes_common 17 | "." 18 | "../include" 19 | "../src/internal" 20 | ) 21 | 22 | set(test_source_common 23 | "*.cpp" 24 | ) 25 | 26 | if(DO_PLATFORM_LINUX) 27 | 28 | set(dosdkcpp_private_includes 29 | "../src/internal/rest" 30 | "../src/internal/rest/util" 31 | ) 32 | 33 | set(test_source_private 34 | "rest/*.cpp" 35 | ) 36 | 37 | elseif (DO_PLATFORM_MAC) 38 | 39 | set(dosdkcpp_private_includes 40 | "../src/internal/rest" 41 | "../src/internal/rest/util" 42 | ) 43 | 44 | # Many of the rest interface tests test simple client specific functionality, should make a seperate folder for simple client 45 | set(test_source_private 46 | "rest/test_helpers.cpp" 47 | ) 48 | 49 | elseif(DO_PLATFORM_WINDOWS) 50 | 51 | set(dosdkcpp_private_includes 52 | "../src/internal/com" 53 | "../src/internal/com/util" 54 | ) 55 | 56 | endif() # Windows 57 | 58 | file (GLOB test_source 59 | ${test_source_common} 60 | ${test_source_private} 61 | ) 62 | 63 | add_executable(deliveryoptimization-sdk-tests ${test_source}) 64 | # Tests make use of C++ exceptions. MSEdge build on Windows disables C++ exceptions but also does not build our tests. 65 | target_compile_definitions(deliveryoptimization-sdk-tests PRIVATE DO_ENABLE_EXCEPTIONS) 66 | add_boost_definitions(deliveryoptimization-sdk-tests PRIVATE) 67 | add_platform_interface_definitions(deliveryoptimization-sdk-tests) 68 | 69 | if (DO_BUILD_FOR_SNAP) 70 | target_compile_definitions(deliveryoptimization-sdk-tests PRIVATE DO_BUILD_FOR_SNAP) 71 | endif () 72 | 73 | target_include_directories(deliveryoptimization-sdk-tests 74 | PRIVATE 75 | ${dosdkcpp_private_includes} 76 | ${dosdkcpp_private_includes_common} 77 | ${Boost_INCLUDE_DIRS} 78 | ) 79 | 80 | target_link_libraries(deliveryoptimization-sdk-tests 81 | ${sdk_tests_linked_libs_common} 82 | ${CXX_FILESYSTEM_LIBS} 83 | ) -------------------------------------------------------------------------------- /sdk-cpp/tests/CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | filter=-runtime/string -------------------------------------------------------------------------------- /sdk-cpp/tests/dosdkcpp_testrunner.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "tests_common.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include "test_data.h" 11 | 12 | #if defined(DO_INTERFACE_COM) 13 | #include 14 | #endif 15 | 16 | int main(int argc, char** argv) 17 | { 18 | testing::InitGoogleTest(&argc, argv); 19 | 20 | namespace po = boost::program_options; 21 | po::options_description desc("Options"); 22 | desc.add_options() 23 | ("help,h", "Show help messages") 24 | ("mcc-host,m", po::value(), "MCC hostname to use (optional override)") 25 | ("manual-start", po::bool_switch()->default_value(false), "Wait for command line input to start tests (useful for debugging)"); 26 | po::variables_map vm; 27 | po::store(po::parse_command_line(argc, argv, desc), vm); 28 | 29 | auto it = vm.find("mcc-host"); 30 | if (it != vm.end()) 31 | { 32 | g_mccHostName = it->second.as(); 33 | std::cout << "Got overriden MCC host: " << g_mccHostName << '\n'; 34 | } 35 | 36 | #if defined(DO_INTERFACE_COM) 37 | // SDK leaves com init up to caller, so initialize in test exe here 38 | const auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); 39 | if (FAILED(hr)) 40 | { 41 | std::cout << "CoInitializeEx failed with " << hr << std::endl; 42 | return hr; 43 | } 44 | #endif 45 | 46 | auto manualStart = vm["manual-start"].as(); 47 | if (manualStart) 48 | { 49 | do 50 | { 51 | std::cout << '\n' << "Press a key to continue..."; 52 | } while (std::cin.get() != '\n'); 53 | } 54 | 55 | int testsResult = RUN_ALL_TESTS(); 56 | 57 | #if defined(DO_INTERFACE_COM) 58 | CoUninitialize(); 59 | #endif 60 | 61 | return testsResult; 62 | } 63 | -------------------------------------------------------------------------------- /sdk-cpp/tests/download_status_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "tests_common.h" 5 | 6 | #include "do_download_status.h" 7 | #include "do_errors.h" 8 | 9 | namespace msdo = microsoft::deliveryoptimization; 10 | 11 | TEST(DownloadStatusTests, TestAccessors) 12 | { 13 | msdo::download_status status; 14 | ASSERT_EQ(status.bytes_total(), 0); 15 | ASSERT_EQ(status.bytes_transferred(), 0); 16 | ASSERT_EQ(status.error_code().value(), 0); 17 | ASSERT_EQ(status.extended_error_code().value(), 0); 18 | ASSERT_EQ(status.state(), msdo::download_state::created); 19 | 20 | status = msdo::download_status(100, 50, -1, -1, msdo::download_state::transferring); 21 | ASSERT_EQ(status.bytes_total(), 100); 22 | ASSERT_EQ(status.bytes_transferred(), 50); 23 | ASSERT_EQ(status.error_code().value(), -1); 24 | ASSERT_EQ(status.extended_error_code().value(), -1); 25 | ASSERT_EQ(status.state(), msdo::download_state::transferring); 26 | } 27 | 28 | TEST(DownloadStatusTests, TestIsError) 29 | { 30 | msdo::download_status status(0, 0, -1, 0, msdo::download_state::created); 31 | ASSERT_TRUE(status.is_error()); 32 | 33 | status = msdo::download_status(0, 0, 0, 0, msdo::download_state::created); 34 | ASSERT_FALSE(status.is_error()); 35 | } 36 | 37 | TEST(DownloadStatusTests, TestIsTransientError) 38 | { 39 | msdo::download_status status(0, 0, 0, -1, msdo::download_state::paused); 40 | ASSERT_TRUE(status.is_transient_error()); 41 | 42 | status = msdo::download_status(0, 0, -1, -1, msdo::download_state::created); 43 | ASSERT_FALSE(status.is_transient_error()); 44 | 45 | status = msdo::download_status(0, 0, -1, 0, msdo::download_state::created); 46 | ASSERT_FALSE(status.is_transient_error()); 47 | } 48 | 49 | TEST(DownloadStatusTests, TestIsComplete) 50 | { 51 | msdo::download_status status(100, 100, 0, 0, msdo::download_state::transferred); 52 | ASSERT_TRUE(status.is_complete()); 53 | 54 | status = msdo::download_status(100, 100, -1, -1, msdo::download_state::transferred); 55 | ASSERT_TRUE(status.is_complete()); 56 | 57 | status = msdo::download_status(100, 99, 0, -1, msdo::download_state::transferring); 58 | ASSERT_FALSE(status.is_complete()); 59 | 60 | status = msdo::download_status(100, 0, 0, 0, msdo::download_state::created); 61 | ASSERT_FALSE(status.is_complete()); 62 | } 63 | -------------------------------------------------------------------------------- /sdk-cpp/tests/rest/config_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "tests_common.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include "do_config.h" 10 | #include "do_persistence.h" 11 | #include "test_data.h" 12 | #include "test_helpers.h" 13 | 14 | namespace msdo = microsoft::deliveryoptimization; 15 | namespace msdod = microsoft::deliveryoptimization::details; 16 | 17 | class ConfigTests : public ::testing::Test 18 | { 19 | public: 20 | void SetUp() override 21 | { 22 | TestHelpers::CleanTestDir(); 23 | if (fs::exists(msdod::GetConfigFilePath())) 24 | { 25 | fs::remove(msdod::GetConfigFilePath()); 26 | } 27 | } 28 | 29 | void TearDown() override 30 | { 31 | SetUp(); 32 | // Make sure docs reloads config immediately 33 | TestHelpers::RestartService(g_docsSvcName); 34 | } 35 | }; 36 | 37 | TEST_F(ConfigTests, IoTConnectionString) 38 | { 39 | const char* expectedValue = "HostName=instance-company-iothub-ver.host.tld;DeviceId=user-dev-name;SharedAccessKey=abcdefghijklmnopqrstuvwxyzABCDE123456789012=;GatewayHostName=10.0.0.200"; 40 | int ret = deliveryoptimization_set_iot_connection_string(expectedValue); 41 | ASSERT_EQ(ret, 0); 42 | 43 | boost::property_tree::ptree configTree; 44 | boost::property_tree::read_json(msdod::GetConfigFilePath(), configTree); 45 | 46 | const std::string value = configTree.get("ADUC_IoTConnectionString"); 47 | ASSERT_EQ(value, std::string{expectedValue}); 48 | } 49 | 50 | TEST(ConfigVersionTests, GetVersion) 51 | { 52 | char* version = deliveryoptimization_get_components_version(); 53 | ASSERT_NE(*version, '\0'); 54 | ASSERT_EQ(strchr(version, '\n'), nullptr); 55 | std::cout << "All versions: " << version << std::endl; 56 | ASSERT_EQ(strstr(version, "DU;lib"), version); // ; found at beginning 57 | ASSERT_EQ(strstr(version, "deliveryoptimization-"), nullptr); // prefix not found in any component 58 | deliveryoptimization_free_version_buf(&version); 59 | ASSERT_EQ(version, nullptr); 60 | } 61 | -------------------------------------------------------------------------------- /sdk-cpp/tests/rest/network_connectivity_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "tests_common.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "do_download.h" 10 | #include "do_download_status.h" 11 | #include "do_errors.h" 12 | #include "test_data.h" 13 | #include "test_helpers.h" 14 | 15 | namespace msdo = microsoft::deliveryoptimization; 16 | namespace msdod = microsoft::deliveryoptimization::details; 17 | using namespace std::chrono_literals; // NOLINT(build/namespaces) 18 | 19 | // These tests cause build pipeline failure due to cutting off communication between the agent and backend pipeline infra. 20 | // TODO(jimson): Look into moving network connectivity tests to E2E tests when that infrastructure has been set up 21 | class NetworkConnectivityTests : public ::testing::Test 22 | { 23 | public: 24 | void SetUp() override; 25 | void TearDown() override; 26 | }; 27 | 28 | void NetworkConnectivityTests::SetUp() 29 | { 30 | TestHelpers::CleanTestDir(); 31 | TestHelpers::EnableNetwork(); 32 | } 33 | 34 | void NetworkConnectivityTests::TearDown() 35 | { 36 | TestHelpers::CleanTestDir(); 37 | TestHelpers::EnableNetwork(); 38 | } 39 | 40 | TEST_F(NetworkConnectivityTests, DISABLED_SimpleBlockingDownloadNetworkDisconnect) 41 | { 42 | std::thread downloadThread([&]() 43 | { 44 | try 45 | { 46 | msdo::download::download_url_to_path(g_largeFileUrl, g_tmpFileName, 60s); 47 | ASSERT_TRUE(false); 48 | } 49 | catch (const msdod::exception& e) 50 | { 51 | ASSERT_EQ(e.error_code().value(), static_cast(std::errc::timed_out)); 52 | } 53 | }); 54 | TestHelpers::DisableNetwork(); 55 | std::this_thread::sleep_for(90s); 56 | downloadThread.join(); 57 | } 58 | 59 | TEST_F(NetworkConnectivityTests, DISABLED_SimpleBlockingDownloadNetworkReconnect) 60 | { 61 | std::thread downloadThread([&]() 62 | { 63 | try 64 | { 65 | msdo::download::download_url_to_path(g_largeFileUrl, g_tmpFileName); 66 | ASSERT_EQ(fs::file_size(fs::path(g_tmpFileName)), g_largeFileSizeBytes); 67 | } 68 | catch (const msdod::exception& e) 69 | { 70 | ASSERT_TRUE(false); 71 | } 72 | }); 73 | TestHelpers::DisableNetwork(); 74 | std::this_thread::sleep_for(60s); 75 | TestHelpers::EnableNetwork(); 76 | downloadThread.join(); 77 | } -------------------------------------------------------------------------------- /sdk-cpp/tests/rest/port_discovery_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | #include 4 | 5 | #include "do_persistence.h" 6 | #include "do_port_finder.h" 7 | #include "test_data.h" 8 | #include "test_helpers.h" 9 | 10 | namespace msdod = microsoft::deliveryoptimization::details; 11 | 12 | static const char* const samplePortNumber = "50000"; 13 | 14 | class PortDiscoveryTests : public ::testing::Test 15 | { 16 | public: 17 | PortDiscoveryTests() 18 | { 19 | _testFilePath = std::string(msdod::GetRuntimeDirectory()) + std::string("/restport.0"); 20 | } 21 | 22 | void SetUp() override; 23 | void TearDown() override; 24 | 25 | void DiscoverPortTest(); 26 | 27 | private: 28 | std::string _testFilePath; 29 | }; 30 | 31 | void PortDiscoveryTests::SetUp() 32 | { 33 | TestHelpers::StopService(g_docsSvcName); // ensure agent does not write to port file 34 | TestHelpers::DeleteRestPortFiles(); 35 | if (!fs::exists(msdod::GetRuntimeDirectory())) 36 | { 37 | fs::create_directories(msdod::GetRuntimeDirectory()); 38 | } 39 | 40 | std::ofstream file(_testFilePath); 41 | file << samplePortNumber << std::endl; 42 | } 43 | 44 | void PortDiscoveryTests::TearDown() 45 | { 46 | if (fs::exists(_testFilePath)) 47 | { 48 | fs::remove(_testFilePath); 49 | } 50 | TestHelpers::StartService(g_docsSvcName); 51 | } 52 | 53 | // SNAP: tests only have read permissions into the 'port numbers' directory 54 | #ifndef DO_BUILD_FOR_SNAP 55 | 56 | TEST_F(PortDiscoveryTests, DiscoverPortTest) 57 | { 58 | std::string foundPort = msdod::CPortFinder::GetDOPort(false); 59 | ASSERT_EQ(foundPort, samplePortNumber); 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /sdk-cpp/tests/test_data.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "test_data.h" 5 | #include "do_filesystem.h" 6 | 7 | using namespace std::chrono_literals; // NOLINT(build/namespaces) 8 | 9 | const uint64_t g_smallFileSizeBytes = 1837u; 10 | const uint64_t g_largeFileSizeBytes = 536870440u; 11 | const uint64_t g_prodFileSizeBytes = 25006511u; 12 | 13 | #ifdef DO_BUILD_FOR_SNAP 14 | // For testing production scenario, use the same path DU agent will use 15 | static const std::string downloadPath = "/var/lib/deviceupdate-agent-downloads"; 16 | #else 17 | static const std::string downloadPath = fs::temp_directory_path().string(); 18 | #endif 19 | 20 | const std::string g_largeFileUrl = "http://download.windowsupdate.com/phf/dotc/ReplacementDCATFile.txt"; 21 | const std::string g_malformedFilePath = "?f309adfasdf///dfasdfj39fjasldfjasdf/// ///.1"; 22 | const std::string g_tmpFileName = (downloadPath / fs::path("docsdk_testfile.txt")).string(); 23 | const std::string g_tmpFileName2 = (downloadPath / fs::path("docsdk_testfile2.txt")).string(); 24 | const std::string g_tmpFileName3 = (downloadPath / fs::path("docsdk_testfile3.txt")).string(); 25 | const std::string g_smallFileUrl = "http://download.windowsupdate.com/phf/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/43A54FC03C6A979E9AAEAE2493757D1429A5C8A8D093FB7B8103E8CC8DF7B6B6"; 26 | const std::string g_smallFilePhfInfoJson = R"( 27 | { 28 | "PiecesHashFileUrl":"https://storage4do.blob.core.windows.net/testdata/SmallFile.phf", 29 | "HashOfHashes":"Q6VPwDxql56arq4kk3V9FCmlyKjQk/t7gQPozI33trY=" 30 | })"; 31 | const std::string g_404Url = "http://download.windowsupdate.com/phf/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/nonexistent"; 32 | const std::string g_prodFileUrl = "http://dl.delivery.mp.microsoft.com/filestreamingservice/files/52fa8751-747d-479d-8f22-e32730cc0eb1"; 33 | 34 | const std::chrono::seconds g_smallFileWaitTime = 10s; 35 | 36 | const std::chrono::seconds g_largeFileWaitTime = 5min; 37 | 38 | #if defined(DO_INTERFACE_REST) 39 | 40 | #ifdef DO_BUILD_FOR_SNAP 41 | const std::string g_docsSvcName = "deliveryoptimization-agent.agent"; 42 | #else 43 | const std::string g_docsSvcName = "deliveryoptimization-agent.service"; 44 | #endif 45 | 46 | #endif 47 | 48 | // This MCC instance only works within our test lab azure VMs. Can be overriden via cmdline. 49 | std::string g_mccHostName = "10.1.0.70"; 50 | -------------------------------------------------------------------------------- /sdk-cpp/tests/test_data.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_TEST_DATA_H 5 | #define _DELIVERY_OPTIMIZATION_TEST_DATA_H 6 | 7 | #include 8 | #include 9 | 10 | #ifdef DO_BUILD_FOR_SNAP 11 | // Using systemctl within snap package results in "Running in chroot, ignoring request". 12 | // Use the snap command instead. 13 | #define DO_SERVICE_CONTROLLER "snap" 14 | #else 15 | #define DO_SERVICE_CONTROLLER "systemctl" 16 | #endif 17 | 18 | extern const uint64_t g_smallFileSizeBytes; 19 | extern const uint64_t g_largeFileSizeBytes; 20 | extern const uint64_t g_prodFileSizeBytes; 21 | 22 | #if defined(DO_INTERFACE_REST) 23 | extern const std::string g_docsSvcName; 24 | #elif defined(DO_INTERFACE_COM) 25 | extern const std::string g_smallFilePhfInfoJson; 26 | #endif 27 | 28 | extern const std::string g_largeFileUrl; 29 | extern const std::string g_malformedFilePath; 30 | extern const std::string g_tmpFileName; 31 | extern const std::string g_tmpFileName2; 32 | extern const std::string g_tmpFileName3; 33 | extern const std::string g_smallFileUrl; 34 | extern const std::string g_smallFilePhfInfoJson; 35 | extern const std::string g_404Url; 36 | extern const std::string g_prodFileUrl; // Use this for MCC downloads. Other hostnames aren't enabled in MCC. 37 | 38 | extern const std::chrono::seconds g_smallFileWaitTime; 39 | extern const std::chrono::seconds g_largeFileWaitTime; 40 | 41 | extern std::string g_mccHostName; 42 | #endif 43 | -------------------------------------------------------------------------------- /sdk-cpp/tests/tests_common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef _DELIVERY_OPTIMIZATION_TESTS_COMMON_H 5 | #define _DELIVERY_OPTIMIZATION_TESTS_COMMON_H 6 | 7 | #include 8 | #include 9 | 10 | #include "do_error_helpers.h" 11 | #include "do_filesystem.h" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /snapcraft-options/connect-snaps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | 6 | # $ snap connect : : 7 | 8 | set -e 9 | 10 | # Plug agent snap into sdk-tests snap downloads slot 11 | sudo snap connect deliveryoptimization-agent:deviceupdate-agent-downloads deliveryoptimization-sdk-tests:downloads-folder 12 | 13 | # Plug sdk-tests snap into agent's run and config slots 14 | sudo snap connect deliveryoptimization-sdk-tests:do-port-numbers deliveryoptimization-agent:do-port-numbers 15 | sudo snap connect deliveryoptimization-sdk-tests:do-configs deliveryoptimization-agent:do-configs 16 | --------------------------------------------------------------------------------