├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── SECURITY.MD ├── build └── .vsts-ci.yml ├── configs ├── azure_uhttpFunctions.cmake └── uhttpConfig.cmake ├── devdoc └── requirement_docs │ └── uhttp_requirements.md ├── inc └── azure_uhttp_c │ └── uhttp.h ├── jenkins ├── debian_c.sh ├── linux_c_option_test.sh ├── linux_install_deps.sh ├── linux_wolfssl.sh ├── osx_gcc_openssl.sh ├── osx_xcode_native.sh ├── raspberrypi_c_buster.sh ├── ubuntu_c.sh ├── ubuntu_clang.sh ├── windows_c.cmd ├── windows_c_VsDevCmd.cmd └── windows_c_build_as_dynamic.cmd ├── samples ├── CMakeLists.txt └── uhttp_sample │ ├── CMakeLists.txt │ └── uhttp_sample.c ├── src └── uhttp.c └── tests ├── CMakeLists.txt └── uhttp_ut ├── CMakeLists.txt ├── main.c └── uhttp_ut.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled object files 2 | *.o 3 | *.opp 4 | 5 | # Compiled static libraries 6 | *.a 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | /build/release/Maven 24 | !/build/release/ 25 | [Cc]make/ 26 | 27 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 28 | !packages/*/build/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | *_i.c 35 | *_p.c 36 | *.ilk 37 | *.meta 38 | *.obj 39 | *.pch 40 | *.pdb 41 | *.pgc 42 | *.pgd 43 | *.rsp 44 | *.sbr 45 | *.tlb 46 | *.tli 47 | *.tlh 48 | *.tmp 49 | *.tmp_proj 50 | *.log 51 | *.vspscc 52 | *.vssscc 53 | .builds 54 | *.pidb 55 | *.log 56 | *.scc 57 | 58 | # Visual C++ cache files 59 | ipch/ 60 | *.aps 61 | *.ncb 62 | *.opensdf 63 | *.sdf 64 | *.cachefile 65 | 66 | # Visual Studio profiler 67 | *.psess 68 | *.vsp 69 | *.vspx 70 | 71 | # Guidance Automation Toolkit 72 | *.gpState 73 | 74 | # ReSharper is a .NET coding add-in 75 | _ReSharper*/ 76 | *.[Rr]e[Ss]harper 77 | *.VC.opendb 78 | 79 | # TeamCity is a build add-in 80 | _TeamCity* 81 | 82 | # DotCover is a Code Coverage Tool 83 | *.dotCover 84 | 85 | # NCrunch 86 | *.ncrunch* 87 | .*crunch*.local.xml 88 | 89 | # Installshield output folder 90 | [Ee]xpress/ 91 | 92 | # DocProject is a documentation generator add-in 93 | DocProject/buildhelp/ 94 | DocProject/Help/*.HxT 95 | DocProject/Help/*.HxC 96 | DocProject/Help/*.hhc 97 | DocProject/Help/*.hhk 98 | DocProject/Help/*.hhp 99 | DocProject/Help/Html2 100 | DocProject/Help/html 101 | 102 | # Click-Once directory 103 | publish/ 104 | 105 | # Publish Web Output 106 | *.Publish.xml 107 | 108 | # NuGet Packages Directory 109 | packages/ 110 | 111 | # Windows Azure Build Output 112 | csx 113 | *.build.csdef 114 | 115 | # Windows Store app package directory 116 | AppPackages/ 117 | 118 | # Others 119 | sql/ 120 | *.Cache 121 | ClientBin/ 122 | [Ss]tyle[Cc]op.* 123 | ~$* 124 | *~ 125 | *.dbmdl 126 | *.[Pp]ublish.xml 127 | *.pfx 128 | *.publishsettings 129 | *.jar 130 | 131 | # RIA/Silverlight projects 132 | Generated_Code/ 133 | 134 | # Backup & report files from converting an old project file to a newer 135 | # Visual Studio version. Backup files are not needed, because we have git ;-) 136 | _UpgradeReport_Files/ 137 | Backup*/ 138 | UpgradeLog*.XML 139 | UpgradeLog*.htm 140 | 141 | # SQL Server files 142 | App_Data/*.mdf 143 | App_Data/*.ldf 144 | 145 | 146 | #LightSwitch generated files 147 | GeneratedArtifacts/ 148 | _Pvt_Extensions/ 149 | ModelManifest.xml 150 | 151 | # CPython & Wheels 152 | *.pyc 153 | *.pyd 154 | *.whl 155 | *.egg-info 156 | 157 | # ========================= 158 | # Windows detritus 159 | # ========================= 160 | 161 | # Windows image file caches 162 | Thumbs.db 163 | ehthumbs.db 164 | 165 | # Folder config file 166 | Desktop.ini 167 | 168 | # Recycle Bin used on file shares 169 | $RECYCLE.BIN/ 170 | 171 | # Mac desktop service store files 172 | .DS_Store 173 | 174 | # Visual studio build artifacts 175 | *.tlog 176 | *.lastbuildstate 177 | *.idb 178 | *.exp 179 | *.lib 180 | *.dll 181 | 182 | # Visual Studio Temporary files 183 | *.VC.db 184 | 185 | # Windows CE build artifacts 186 | Build.err 187 | Build.wrn 188 | Buildx86retail.dat 189 | *.dat 190 | 191 | # Tools EXE that doesn't end up in a typical build directory 192 | common/tools/macro_utils_h_generator/macro_utils_h_generator.exe 193 | 194 | # hg directories should be ignored 195 | **/hg/ 196 | 197 | # VS Code stuff 198 | **/typings/** 199 | **/.vscode/** 200 | # C/C++ extension for VS Code 201 | .browse.VC.db* 202 | GPATH 203 | GRTAGS 204 | GTAGS 205 | 206 | build_all/windows/nuget.exe 207 | 208 | /cmake 209 | /build 210 | *.cert 211 | /deps/ctest/ 212 | /deps/testrunnerswitcher/ 213 | /deps/umock-c/ 214 | /.vs 215 | /out/build 216 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/c-utility"] 2 | path = deps/c-utility 3 | url = https://github.com/Azure/azure-c-shared-utility.git 4 | [submodule "deps/azure-macro-utils-c"] 5 | path = deps/azure-macro-utils-c 6 | url = https://github.com/Azure/azure-macro-utils-c.git 7 | [submodule "deps/azure-c-testrunnerswitcher"] 8 | path = deps/azure-c-testrunnerswitcher 9 | url = https://github.com/Azure/azure-c-testrunnerswitcher.git 10 | [submodule "deps/azure-ctest"] 11 | path = deps/azure-ctest 12 | url = https://github.com/Azure/azure-ctest.git 13 | [submodule "deps/umock-c"] 14 | path = deps/umock-c 15 | url = https://github.com/Azure/umock-c.git 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | cmake_minimum_required(VERSION 3.5) 5 | 6 | project(uhttp) 7 | 8 | set(UHTTP_VERSION 1.0.1) 9 | 10 | option(run_e2e_tests "set run_e2e_tests to ON to run e2e tests (default is OFF)" OFF) 11 | option(run_unittests "set run_unittests to ON to run unittests (default is OFF)" OFF) 12 | option(skip_samples "set skip_samples to ON to skip building samples (default is OFF)[if possible, they are always built]" OFF) 13 | option(use_installed_dependencies "set use_installed_dependencies to ON to use installed packages instead of building dependencies from submodules" OFF) 14 | option(use_custom_heap "use externally defined heap functions instead of the malloc family" OFF) 15 | option(use_cppunittest "set use_cppunittest to ON to build CppUnitTest tests on Windows (default is ON)" ON) 16 | 17 | if(${use_custom_heap}) 18 | add_definitions(-DGB_USE_CUSTOM_HEAP) 19 | endif() 20 | 21 | if(${no_logging}) 22 | add_definitions(-DNO_LOGGING) 23 | endif() 24 | 25 | #do not add or build any tests of the dependencies 26 | set(original_run_e2e_tests ${run_e2e_tests}) 27 | set(original_run_int_tests ${run_int_tests}) 28 | set(original_run_unittests ${run_unittests}) 29 | set(original_skip_samples ${skip_samples}) 30 | 31 | set(run_e2e_tests OFF) 32 | set(run_int_tests OFF) 33 | set(run_unittests OFF) 34 | set(skip_samples ON) 35 | 36 | if (NOT ${use_installed_dependencies}) 37 | if (NOT TARGET azure_macro_utils_c AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/deps/azure-macro-utils-c/CMakeLists.txt") 38 | add_subdirectory(deps/azure-macro-utils-c) 39 | include_directories(${MACRO_UTILS_INC_FOLDER}) 40 | endif() 41 | if (NOT TARGET umock_c) 42 | add_subdirectory(deps/umock-c) 43 | include_directories(${MACRO_UTILS_INC_FOLDER}) 44 | include_directories(${UMOCK_C_INC_FOLDER}) 45 | endif() 46 | if (${original_run_e2e_tests} OR ${original_run_unittests}) 47 | if (NOT TARGET testrunnerswitcher) 48 | add_subdirectory(deps/azure-c-testrunnerswitcher) 49 | endif() 50 | if (NOT TARGET ctest) 51 | add_subdirectory(deps/azure-ctest) 52 | endif() 53 | enable_testing() 54 | endif() 55 | if (NOT TARGET aziotsharedutil AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/deps/c-utility/CMakeLists.txt") 56 | add_subdirectory(deps/c-utility) 57 | if (${original_run_unittests}) 58 | set(SHARED_UTIL_REAL_TEST_FOLDER ${CMAKE_CURRENT_LIST_DIR}/deps/c-utility/tests/real_test_files CACHE INTERNAL "this is what needs to be included when doing test sources" FORCE) 59 | endif() 60 | endif() 61 | else() 62 | if (NOT azure_macro_utils_cFOUND) 63 | find_package(azure_macro_utils_c REQUIRED CONFIG) 64 | endif () 65 | if (NOT umock_cFOUND) 66 | find_package(umock_c REQUIRED CONFIG) 67 | endif () 68 | if (NOT azure_c_shared_utility_FOUND) 69 | find_package(azure_c_shared_utility REQUIRED CONFIG) 70 | endif () 71 | 72 | include(${azure_c_shared_utility_DIR}/azure_c_shared_utilityConfig.cmake) 73 | include(${azure_c_shared_utility_DIR}/azure_c_shared_utilityFunctions.cmake) 74 | include(${azure_c_shared_utility_DIR}/azure_iot_build_rules.cmake) 75 | endif() 76 | 77 | set(run_e2e_tests ${original_run_e2e_tests}) 78 | set(run_int_tests ${original_run_int_tests}) 79 | set(run_unittests ${original_run_unittests}) 80 | set(skip_samples ${original_skip_samples}) 81 | 82 | if(${use_openssl}) 83 | add_definitions(-DUSE_OPENSSL) 84 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_OPENSSL") 85 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_OPENSSL") 86 | endif() 87 | 88 | #Use solution folders. 89 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 90 | 91 | if(${memory_trace}) 92 | add_definitions(-DGB_MEASURE_MEMORY_FOR_THIS -DGB_DEBUG_ALLOC) 93 | endif() 94 | 95 | IF((WIN32) AND (NOT(MINGW))) 96 | #windows needs this define 97 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 98 | # Make warning as error 99 | add_definitions(/WX) 100 | ELSE() 101 | # Make warning as error 102 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 103 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") 104 | ENDIF() 105 | 106 | if(MSVC) 107 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 108 | endif() 109 | 110 | set(UHTTP_C_INC_FOLDER ${CMAKE_CURRENT_LIST_DIR}/inc CACHE INTERNAL "this is what needs to be included if using sharedLib lib" FORCE) 111 | 112 | set_platform_files(${SHARED_UTIL_FOLDER}) 113 | 114 | set(uhttp_c_files 115 | ./src/uhttp.c 116 | ) 117 | 118 | set(uhttp_h_files 119 | ./inc/azure_uhttp_c/uhttp.h 120 | ) 121 | 122 | include_directories(./inc) 123 | include_directories(${SHARED_UTIL_INC_FOLDER} ${MACRO_UTILS_INC_FOLDER} ${UMOCK_C_INC_FOLDER}) 124 | 125 | include("configs/azure_uhttpFunctions.cmake") 126 | 127 | add_library(uhttp ${uhttp_c_files} ${uhttp_h_files}) 128 | target_include_directories(uhttp 129 | PUBLIC 130 | $ 131 | $ 132 | ) 133 | setTargetBuildProperties(uhttp) 134 | target_link_libraries(uhttp aziotsharedutil) 135 | 136 | if (${run_unittests}) 137 | #include("dependencies-test.cmake") 138 | add_subdirectory(tests) 139 | endif() 140 | 141 | if (NOT ${skip_samples}) 142 | add_subdirectory(samples) 143 | endif() 144 | 145 | # Set CMAKE_INSTALL_LIBDIR if not defined 146 | include(GNUInstallDirs) 147 | 148 | # Install umqtt 149 | set(package_location "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 150 | 151 | if(NOT DEFINED CMAKE_INSTALL_LIBDIR) 152 | set(CMAKE_INSTALL_LIBDIR "lib") 153 | endif() 154 | 155 | install(TARGETS uhttp EXPORT uhttpTargets 156 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 157 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 158 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 159 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/azureiot 160 | ) 161 | install(FILES ${uhttp_h_files} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/azureiot/azure_uhttp_c) 162 | 163 | include(CMakePackageConfigHelpers) 164 | 165 | write_basic_package_version_file( 166 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake" 167 | VERSION ${UHTTP_VERSION} 168 | COMPATIBILITY SameMajorVersion 169 | ) 170 | 171 | configure_file("configs/${PROJECT_NAME}Config.cmake" 172 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake" 173 | COPYONLY 174 | ) 175 | 176 | install(EXPORT uhttpTargets 177 | FILE 178 | "${PROJECT_NAME}Targets.cmake" 179 | DESTINATION 180 | ${package_location} 181 | ) 182 | 183 | install( 184 | FILES 185 | "configs/${PROJECT_NAME}Config.cmake" 186 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake" 187 | DESTINATION 188 | ${package_location} 189 | ) 190 | compileTargetAsC99(uhttp) 191 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uhttp 2 | 3 | The uhttp provides a platform independent http implementation via the Azure C SDKs. 4 | 5 | ## Dependencies 6 | 7 | azure-uhttp-c depends on azure-c-shared. 8 | 9 | azure-uhttp-c uses cmake for configuring build files. 10 | 11 | ## Setup 12 | 13 | 1. Clone **azure-uhttp-c** using the recursive option: 14 | 15 | ``` 16 | git clone --recursive https://github.com/Azure/azure-uhttp-c.git 17 | ``` 18 | 19 | 2. Create a folder called *cmake* under *azure-uhttp-c 20 | 21 | 3. Switch to the *cmake* folder and run 22 | ``` 23 | cmake .. 24 | ``` 25 | 26 | 4. Build 27 | 28 | ``` 29 | cmake --build . 30 | ``` 31 | 32 | ### Installation and Use 33 | Optionally, you may choose to install azure-c-shared-utility on your machine: 34 | 35 | 1. Switch to the *cmake* folder and run 36 | ``` 37 | cmake -Duse_installed_dependencies=ON ../ 38 | ``` 39 | ``` 40 | cmake --build . --target install 41 | ``` 42 | 43 | or install using the follow commands for each platform: 44 | 45 | On Linux: 46 | ``` 47 | sudo make install 48 | ``` 49 | 50 | 2. Use it in your project (if installed) 51 | ``` 52 | find_package(azure_uhttp_c REQUIRED CONFIG) 53 | target_link_library(yourlib uhttp) 54 | ``` 55 | 56 | _If running tests, this requires that umock-c, azure-ctest, and azure-c-testrunnerswitcher, azure-c-shared-utility 57 | are installed (through CMake) on your machine._ 58 | 59 | ## Configuration options 60 | 61 | In order to turn on/off the tlsio implementations use the following CMAKE options: 62 | 63 | * `-Duse_custom_heap:bool={ON/OFF}` - turns disables/enables the implementations in `gballoc.c` and requires that an external library implement the `gballoc_malloc` family. 64 | * `-Dno_logging:bool={ON/OFF}` - turns on/off logging 65 | * `-Duse_openssl:bool={ON/OFF}` - turns on/off the OpenSSL support. If this option is use an environment variable name OpenSSLDir should be set to point to the OpenSSL folder. 66 | * `-Dmemory_trace:bool={ON/OFF}` - turns on/off gballoc_xxx functions for memory alocation 67 | * `-Duse_installed_dependencies:bool={ON/OFF}` - turns on/off building azure-c-shared-utility using installed dependencies. This package may only be installed if this flag is ON. 68 | * `-Drun_unittests:bool={ON/OFF}` - enables building of unit tests. Default is OFF. 69 | 70 | ## Contributing 71 | 72 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 73 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 74 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 75 | 76 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 77 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 78 | provided by the bot. You will only need to do this once across all repos using our CLA. 79 | 80 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 81 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 82 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 83 | -------------------------------------------------------------------------------- /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 [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, 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 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 | -------------------------------------------------------------------------------- /build/.vsts-ci.yml: -------------------------------------------------------------------------------- 1 | name: $(BuildID)_$(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) 2 | variables: 3 | runCodesignValidationInjection: false 4 | resources: 5 | containers: 6 | - container: linux-c-ubuntu-2004 7 | endpoint: csdk-containers 8 | image: csdkcontainerregistry.azurecr.io/linux-c-ubuntu-2004:latest 9 | - container: linux-c-ubuntu-clang 10 | endpoint: csdk-containers 11 | image: csdkcontainerregistry.azurecr.io/linux-c-ubuntu-clang:latest 12 | - container: linux-c-debian-buster 13 | endpoint: csdk-containers 14 | image: csdkcontainerregistry.azurecr.io/linux-c-debian-buster:latest 15 | - container: raspberrypi-c-buster 16 | endpoint: csdk-containers 17 | image: csdkcontainerregistry.azurecr.io/raspberrypi-c-buster:brown 18 | - container: linux-c-ubuntu-wolfssl 19 | endpoint: csdk-containers 20 | image: csdkcontainerregistry.azurecr.io/linux-c-ubuntu-wolfssl:latest 21 | jobs: 22 | - job: checksubmodule 23 | variables: 24 | CodeQL.Enabled: false 25 | displayName: Check Submodules 26 | pool: 27 | vmImage: 'ubuntu-20.04' 28 | steps: 29 | - script: | 30 | sudo apt-get update && apt-get install -y \ 31 | curl \ 32 | git \ 33 | python-software-properties \ 34 | build-essential \ 35 | pkg-config 36 | sudo curl -sL https://deb.nodesource.com/setup_6.x | bash - 37 | sudo apt-get install -y nodejs 38 | displayName: 'setup' 39 | - script: | 40 | npm install check_submodules 41 | node_modules/.bin/check_submodules . master 42 | displayName: 'build' 43 | - job: windowsx86 44 | displayName: Windows x86 45 | pool: 46 | name: 'sdk-c--win-vs2022' 47 | steps: 48 | - script: | 49 | if exist jenkins\windows_c.cmd ( 50 | call jenkins\windows_c_VsDevCmd.cmd 51 | call jenkins\windows_c.cmd) 52 | displayName: 'build' 53 | env: 54 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 55 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 56 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 57 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 58 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 59 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 60 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 61 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 62 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 63 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 64 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 65 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 66 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 67 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 68 | - job: windowsx64 69 | displayName: Windows x64 70 | variables: 71 | CodeQL.Enabled: true 72 | CodeQL.Language: cpp 73 | pool: 74 | name: 'sdk-c--win-vs2022' 75 | steps: 76 | - script: | 77 | if exist jenkins\windows_c.cmd ( 78 | call jenkins\windows_c_VsDevCmd.cmd x64 79 | call jenkins\windows_c.cmd --platform x64) 80 | displayName: 'build' 81 | env: 82 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 83 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 84 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 85 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 86 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 87 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 88 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 89 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 90 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 91 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 92 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 93 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 94 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 95 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 96 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 97 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 98 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 99 | - job: windowsdynamic 100 | displayName: Windows Dynamic 101 | pool: 102 | name: 'sdk-c--win-vs2022' 103 | steps: 104 | - script: | 105 | if exist jenkins\windows_c_build_as_dynamic.cmd ( 106 | call jenkins\windows_c_VsDevCmd.cmd 107 | call jenkins\windows_c_build_as_dynamic.cmd) 108 | displayName: 'build' 109 | env: 110 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 111 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 112 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 113 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 114 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 115 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 116 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 117 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 118 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 119 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 120 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 121 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 122 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 123 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 124 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 125 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 126 | - job: clang 127 | container: linux-c-ubuntu-clang 128 | pool: 129 | name: 'sdk-c--ubuntu-22' 130 | displayName: 'Linux Ubuntu Clang' 131 | steps: 132 | - script: | 133 | if [ -f "jenkins/ubuntu_clang.sh" ] 134 | then 135 | export OPENSSL_ia32cap=0x00000000 136 | sudo chmod -R 755 . 137 | sudo -E ./jenkins/ubuntu_clang.sh 138 | fi 139 | displayName: 'build' 140 | env: 141 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 142 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 143 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 144 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 145 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 146 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 147 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 148 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 149 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 150 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 151 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 152 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 153 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 154 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 155 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 156 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 157 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 158 | - job: ubuntu2004 159 | container: linux-c-ubuntu-2004 160 | pool: 161 | name: 'sdk-c--ubuntu-22' 162 | displayName: 'Linux Ubuntu 20.04' 163 | steps: 164 | - script: | 165 | if [ -f "jenkins/ubuntu_c.sh" ] 166 | then 167 | export OPENSSL_ia32cap=0x00000000 168 | sudo chmod -R 755 . 169 | sudo -E ./jenkins/ubuntu_c.sh 170 | fi 171 | displayName: 'build' 172 | env: 173 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 174 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 175 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 176 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 177 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 178 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 179 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 180 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 181 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 182 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 183 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 184 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 185 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 186 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 187 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 188 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 189 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 190 | - job: linuxoptions 191 | container: linux-c-ubuntu-2004 192 | pool: 193 | name: 'sdk-c--ubuntu-22' 194 | displayName: 'Linux Ubuntu 20.04 with Options' 195 | steps: 196 | - script: | 197 | if [ -f "jenkins/linux_c_option_test.sh" ] 198 | then 199 | sudo ./jenkins/linux_c_option_test.sh 200 | fi 201 | displayName: 'build' 202 | condition: always() 203 | - job: wolfssl 204 | container: linux-c-ubuntu-wolfssl 205 | pool: 206 | name: 'sdk-c--ubuntu-22' 207 | displayName: 'Linux Ubuntu with WolfSSL' 208 | steps: 209 | - script: | 210 | if [ -f "jenkins/linux_wolfssl.sh" ] 211 | then 212 | LD_LIBRARY_PATH=/usr/local/lib 213 | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_library/ 214 | sudo ./jenkins/linux_wolfssl.sh 215 | fi 216 | displayName: 'build' 217 | env: 218 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 219 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 220 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 221 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 222 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 223 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 224 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 225 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 226 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 227 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 228 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 229 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 230 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 231 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 232 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 233 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 234 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 235 | - job: debian 236 | container: linux-c-debian-buster 237 | pool: 238 | name: 'sdk-c--ubuntu-22' 239 | displayName: 'Linux Debian (Buster)' 240 | steps: 241 | - script: | 242 | if [ -f "jenkins/debian_c.sh" ] 243 | then 244 | sudo chmod 755 jenkins/debian_c.sh 245 | sudo -E ./jenkins/debian_c.sh 246 | sudo ./jenkins/debian_c.sh 247 | fi 248 | displayName: 'build' 249 | env: 250 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 251 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 252 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 253 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 254 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 255 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 256 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 257 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 258 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 259 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 260 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 261 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 262 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 263 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 264 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 265 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 266 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 267 | - job: linux_install_deps 268 | container: linux-c-debian-buster 269 | pool: 270 | name: 'sdk-c--ubuntu-22' 271 | displayName: 'Linux Debian (Buster) with Installed Deps' 272 | steps: 273 | - script: | 274 | if [ -f "jenkins/linux_install_deps.sh" ] 275 | then 276 | sudo chmod 755 jenkins/linux_install_deps.sh 277 | sudo -E ./jenkins/linux_install_deps.sh 278 | sudo jenkins/linux_install_deps.sh 279 | fi 280 | displayName: 'build' 281 | env: 282 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 283 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 284 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 285 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 286 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 287 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 288 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 289 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 290 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 291 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 292 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 293 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 294 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 295 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 296 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 297 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 298 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 299 | - job: OSX 300 | displayName: OSX 301 | variables: 302 | CodeQL.Enabled: false 303 | pool: 304 | vmImage: 'macOS-13' 305 | steps: 306 | - script: | 307 | if [ -f "jenkins/osx_gcc_openssl.sh" ] 308 | then 309 | ./jenkins/osx_gcc_openssl.sh 310 | fi 311 | displayName: 'build' 312 | env: 313 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 314 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 315 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 316 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 317 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 318 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 319 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 320 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 321 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 322 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 323 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 324 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 325 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 326 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 327 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 328 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 329 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 330 | - task: ComponentGovernanceComponentDetection@0 331 | displayName: 'Component Detection' 332 | - script: rm -rf $(Agent.BuildDirectory)/* 333 | displayName: 'cleanup' 334 | condition: always() 335 | - job: xcodenative 336 | displayName: Xcode Native 337 | variables: 338 | CodeQL.Enabled: false 339 | pool: 340 | vmImage: 'macOS-13' 341 | steps: 342 | - script: | 343 | if [ -f "jenkins/osx_xcode_native.sh" ] 344 | then 345 | DYLD_LIBRARY_PATH=/usr/local/Cellar/curl/7.61.0/lib 346 | ./jenkins/osx_xcode_native.sh 347 | fi 348 | displayName: 'build' 349 | env: 350 | IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) 351 | IOTHUB_EVENTHUB_CONNECTION_STRING: $(IOTHUB-EVENTHUB-CONNECTION-STRING) 352 | IOTHUB_E2E_X509_CERT_BASE64: $(IOTHUB-E2E-X509-CERT-BASE64) 353 | IOTHUB_E2E_X509_PRIVATE_KEY_BASE64: $(IOTHUB-E2E-X509-PRIVATE-KEY-BASE64) 354 | IOTHUB_E2E_X509_THUMBPRINT: $(IOTHUB-E2E-X509-THUMBPRINT) 355 | IOTHUB_POLICY_KEY: $(IOTHUB-POLICY-KEY) 356 | IOTHUB_PARTITION_COUNT: $(IOTHUB-PARTITION-COUNT) 357 | STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) 358 | IOT_DPS_CONNECTION_STRING: $(IOT-DPS-CONNECTION-STRING) 359 | IOT_DPS_ID_SCOPE: $(IOT-DPS-ID-SCOPE) 360 | IOTHUB_CA_ROOT_CERT: $(IOTHUB-CA-ROOT-CERT) 361 | IOTHUB_CA_ROOT_CERT_KEY: $(IOTHUB-CA-ROOT-CERT-KEY) 362 | IOT_DPS_GLOBAL_ENDPOINT: $(IOT-DPS-GLOBAL-ENDPOINT) 363 | IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) 364 | IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) 365 | DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) 366 | PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) 367 | - task: ComponentGovernanceComponentDetection@0 368 | displayName: 'Component Detection' 369 | - script: rm -rf $(Agent.BuildDirectory)/* 370 | displayName: 'cleanup' 371 | condition: always() 372 | - job: raspberrypi 373 | container: raspberrypi-c-buster 374 | pool: 375 | name: 'sdk-c--ubuntu-22' 376 | displayName: Raspberry Pi 377 | steps: 378 | - script: | 379 | chmod +x jenkins/raspberrypi_c_buster.sh 380 | ./jenkins/raspberrypi_c_buster.sh 381 | -------------------------------------------------------------------------------- /configs/azure_uhttpFunctions.cmake: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | function(linkSharedUtil whatIsBuilding) 5 | target_link_libraries(${whatIsBuilding} aziotsharedutil) 6 | endfunction(linkSharedUtil) 7 | 8 | function(add_unittest_directory test_directory) 9 | if (${run_unittests}) 10 | add_subdirectory(${test_directory}) 11 | endif() 12 | endfunction() 13 | -------------------------------------------------------------------------------- /configs/uhttpConfig.cmake: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/uhttpTargets.cmake") 5 | 6 | get_target_property(UHTTP_INCLUDES uhttp INTERFACE_INCLUDE_DIRECTORIES) 7 | 8 | set(UHTTP_INCLUDES ${UHTTP_INCLUDES} CACHE INTERNAL "") 9 | -------------------------------------------------------------------------------- /devdoc/requirement_docs/uhttp_requirements.md: -------------------------------------------------------------------------------- 1 | # uhttp Requirements 2 | 3 | ## Overview 4 | 5 | The uhttp module provides a platform independent http implementation 6 | 7 | ## References 8 | 9 | [RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1](https://tools.ietf.org/html/rfc2616) 10 | 11 | [RFC 7230 - Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing](https://tools.ietf.org/html/rfc7230) 12 | 13 | 14 | ### Features not yet supported 15 | 16 | [4.1 Sending Chunking message](https://tools.ietf.org/html/rfc7230#section-4.1) 17 | 18 | [8.1 Persistent Connections](https://tools.ietf.org/html/rfc2616#section-8.1) 19 | 20 | [8.2.3 100 Continue Response](https://tools.ietf.org/html/rfc2616#section-8.2.3) 21 | 22 | [14.33 Proxy Authentication](https://tools.ietf.org/html/rfc2616#section-14.33) 23 | 24 | execute_request Timeout 25 | 26 | ## Exposed API 27 | 28 | ```c 29 | typedef struct HTTP_CLIENT_HANDLE_DATA_TAG* HTTP_CLIENT_HANDLE; 30 | 31 | #define HTTP_CLIENT_RESULT_VALUES \ 32 | HTTP_CLIENT_OK, \ 33 | HTTP_CLIENT_INVALID_ARG, \ 34 | HTTP_CLIENT_ERROR, \ 35 | HTTP_CLIENT_OPEN_FAILED, \ 36 | HTTP_CLIENT_SEND_FAILED, \ 37 | HTTP_CLIENT_ALREADY_INIT, \ 38 | HTTP_CLIENT_HTTP_HEADERS_FAILED 39 | 40 | /** @brief Enumeration specifying the possible return values for the APIs in 41 | * this module. 42 | */ 43 | MU_DEFINE_ENUM(HTTP_CLIENT_RESULT, HTTP_CLIENT_RESULT_VALUES); 44 | 45 | #define HTTP_CLIENT_REQUEST_TYPE_VALUES \ 46 | HTTP_CLIENT_REQUEST_OPTIONS, \ 47 | HTTP_CLIENT_REQUEST_GET, \ 48 | HTTP_CLIENT_REQUEST_POST, \ 49 | HTTP_CLIENT_REQUEST_PUT, \ 50 | HTTP_CLIENT_REQUEST_DELETE, \ 51 | HTTP_CLIENT_REQUEST_PATCH 52 | 53 | MU_DEFINE_ENUM(HTTP_CLIENT_REQUEST_TYPE, HTTP_CLIENT_REQUEST_TYPE_VALUES); 54 | 55 | #define HTTP_CALLBACK_REASON_VALUES \ 56 | HTTP_CALLBACK_REASON_OK, \ 57 | HTTP_CALLBACK_REASON_DESTROY, \ 58 | HTTP_CALLBACK_REASON_DISCONNECTED 59 | 60 | MU_DEFINE_ENUM(HTTP_CALLBACK_REASON, HTTP_CALLBACK_REASON_VALUES); 61 | 62 | typedef void(*ON_HTTP_OPEN_COMPLETE_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON open_result); 63 | typedef void(*ON_HTTP_ERROR_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON error_result); 64 | typedef void(*ON_HTTP_REQUEST_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_length, unsigned int status_code, 65 | HTTP_HEADERS_HANDLE response_headers); 66 | typedef void(*ON_HTTP_CLOSED_CALLBACK)(void* callback_ctx); 67 | 68 | MOCKABLE_FUNCTION(, HTTP_CLIENT_HANDLE, uhttp_client_create, const IO_INTERFACE_DESCRIPTION*, io_interface_desc, const void*, xio_param, ON_HTTP_ERROR_CALLBACK, on_http_error, void*, callback_ctx); 69 | 70 | MOCKABLE_FUNCTION(, void, uhttp_client_destroy, HTTP_CLIENT_HANDLE, handle); 71 | 72 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_open, HTTP_CLIENT_HANDLE, handle, const char*, host, int, port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK, on_connect, void*, callback_ctx); 73 | 74 | MOCKABLE_FUNCTION(, void, uhttp_client_close, HTTP_CLIENT_HANDLE, handle, ON_HTTP_CLOSED_CALLBACK, on_close_callback, void*, callback_ctx); 75 | 76 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_execute_request, HTTP_CLIENT_HANDLE, handle, HTTP_CLIENT_REQUEST_TYPE, request_type, const char*, relative_path, 77 | HTTP_HEADERS_HANDLE, http_header_handle, const unsigned char*, content, size_t, content_length, ON_HTTP_REQUEST_CALLBACK, on_request_callback, void*, callback_ctx); 78 | 79 | MOCKABLE_FUNCTION(, void, uhttp_client_dowork, HTTP_CLIENT_HANDLE, handle); 80 | 81 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trace, HTTP_CLIENT_HANDLE, handle, bool, trace_on); 82 | ``` 83 | 84 | ### uhttp_client_create 85 | 86 | ```c 87 | HTTP_CLIENT_HANDLE uhttp_client_create(const IO_INTERFACE_DESCRIPTION* io_interface_desc, const void* xio_param, ON_HTTP_ERROR_CALLBACK on_http_error, void* callback_ctx) 88 | ``` 89 | 90 | http_client_create initializes the http client object. 91 | 92 | **SRS_UHTTP_07_002: [** If io_interface_desc is NULL, `uhttp_client_create` shall return NULL. **]** 93 | 94 | **SRS_UHTTP_07_001: [** `uhttp_client_create` shall return and initialize the http client handle.**]** 95 | 96 | **SRS_UHTTP_07_003: [** If `uhttp_client_create` encounters any error then it shall return NULL **]** 97 | 98 | ### uhttp_client_destroy 99 | 100 | ```c 101 | void uhttp_client_destroy(HTTP_CLIENT_HANDLE handle); 102 | ``` 103 | 104 | uhttp_client_destroy cleans up all items that have been allocated. 105 | 106 | **SRS_UHTTP_07_004: [** If `handle` is NULL then `uhttp_client_destroy` shall do nothing **]** 107 | 108 | **SRS_UHTTP_07_005: [** `http_client_destroy` shall free any resource that is associated with `handle`. **]** 109 | 110 | ### uhttp_client_open 111 | 112 | ```c 113 | HTTP_CLIENT_RESULT uhttp_client_open(HTTP_CLIENT_HANDLE handle, const char* host, int port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect, void* callback_ctx) 114 | ``` 115 | 116 | uhttp_client_open opens the xio_handle calling the on_open callback if supplied. 117 | 118 | **SRS_UHTTP_07_006: [** If handle or host is NULL then `uhttp_client_open` shall return HTTP_CLIENT_INVALID_ARG **]** 119 | 120 | **SRS_UHTTP_07_007: [** `uhttp_client_open` shall call `xio_open` on the xio_handle.**]** 121 | 122 | **SRS_UHTTP_07_044: [** if a failure is encountered on `xio_open`, `uhttp_client_open` shall return `HTTP_CLIENT_OPEN_REQUEST_FAILED`. **]** 123 | 124 | **SRS_UHTTP_07_008: [** If `uhttp_client_open` succeeds then it shall return HTTP_CLIENT_OK **]** 125 | 126 | ### uhttp_client_close 127 | 128 | ```c 129 | void uhttp_client_close(HTTP_CLIENT_HANDLE handle, ON_HTTP_CLOSED_CALLBACK on_close_callback, void* callback_ctx) 130 | ``` 131 | 132 | uhttp_client_close closes the xioHandle connection. 133 | 134 | **SRS_UHTTP_07_009: [** If handle is NULL then `uhttp_client_close` shall do nothing **]** 135 | 136 | **SRS_UHTTP_07_010: [** If the xio_handle is NOT NULL `uhttp_client_close` shall call `xio_close` **]** 137 | 138 | **SRS_UHTTP_07_049: [** If the state has been previously set to `state_closed`, `uhttp_client_close` shall do nothing. **]** 139 | 140 | ### uhttp_client_execute_request 141 | 142 | ```c 143 | HTTP_CLIENT_RESULT uhttp_client_execute_request(HTTP_CLIENT_HANDLE handle, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, 144 | HTTP_HEADERS_HANDLE http_header_handle, const unsigned char* content, size_t content_len, ON_HTTP_REPLY_RECV_CALLBACK on_http_reply_recv, void* callback_ctx) 145 | ``` 146 | 147 | uhttp_client_execute_request allocates data to be sent to the http endpoint. 148 | 149 | **SRS_UHTTP_07_012: [** If `handle`, or `on_http_reply_recv` is NULL then `uhttp_client_execute_request` shall return HTTP_CLIENT_INVALID_ARG. **]** 150 | 151 | **SRS_UHTTP_07_013: [** if content is not NULL and `contentLength` is 0 or content is NULL and contentLength is not 0 then `uhttp_client_execute_request` shall return HTTP_CLIENT_INVALID_ARG. **]** 152 | 153 | **SRS_UHTTP_07_041: [** HTTP_CLIENT_REQUEST_TYPE shall support all request types specified under RFC 2616 section 9.1.2 in the spec. **]** 154 | 155 | **SRS_UHTTP_07_016: [** `uhttp_client_execute_request` shall queue the http headers data and content to be sent to the http endpoint. **]** 156 | 157 | **SRS_UHTTP_07_015: [** `uhttp_client_execute_request` shall add the Content-Length to the request if the contentLength is `>` 0. **]** 158 | 159 | **SRS_UHTTP_07_011: [** `uhttp_client_execute_request` shall add the HOST http header item to the request if not supplied (RFC 7230 - 5.4). **]** 160 | 161 | **SRS_UHTTP_07_018: [** upon success `uhttp_client_execute_request` shall return HTTP_CLIENT_OK. **]** 162 | 163 | **SRS_UHTTP_07_017: [** If any failure is encountered `uhttp_client_execute_request` shall return HTTP_CLIENT_ERROR. **]** 164 | 165 | ### uhttp_client_dowork 166 | 167 | ```c 168 | void uhttp_client_dowork(HTTP_CLIENT_HANDLE handle); 169 | ``` 170 | 171 | uhttp_client_dowork executes the http work that includes sends and receives. 172 | 173 | **SRS_UHTTP_07_036: [** If handle is NULL then `uhttp_client_dowork` shall do nothing. **]** 174 | 175 | **SRS_UHTTP_07_037: [** `uhttp_client_dowork` shall call the underlying xio_dowork function. **]** 176 | 177 | **SRS_UHTTP_07_016: [** `uhttp_client_dowork` shall iterate through the queued Data using the xio interface to send the http request in the following ways... **]** 178 | 179 | **SRS_UHTTP_07_052: [** `uhttp_client_dowork` shall call xio_send to transmits the header information... **]** 180 | 181 | **SRS_UHTTP_07_053: [** Then `uhttp_client_dowork` shall use xio_send to transmit the content of the http request if supplied. **]** 182 | 183 | **SRS_UHTTP_07_046: [** `uhttp_client_dowork` shall free resouces queued to send to the http endpoint. **]** 184 | 185 | ### http_client_set_trace 186 | 187 | ```c 188 | HTTP_CLIENT_RESULT uhttp_client_set_trace(HTTP_CLIENT_HANDLE handle, bool trace_on) 189 | ``` 190 | 191 | http_client_set_trace turns on or off log tracing. 192 | 193 | **SRS_UHTTP_07_038: [** If handle is NULL then `http_client_set_trace` shall return HTTP_CLIENT_INVALID_ARG **]** 194 | 195 | **SRS_UHTTP_07_039: [** `http_client_set_trace` shall set the HTTP tracing to the trace_on variable. **]** 196 | 197 | **SRS_UHTTP_07_040: [** if `http_client_set_trace` finishes successfully then it shall return HTTP_CLIENT_OK. **]** 198 | 199 | ### on_bytes_received 200 | 201 | ```c 202 | static void on_bytes_received(void* context, const unsigned char* buffer, size_t len) 203 | ``` 204 | 205 | **SRS_UHTTP_07_047: [** If context or buffer is NULL `on_bytes_received` shall do nothing. **]** 206 | 207 | **SRS_UHTTP_07_048: [** If any error is encountered `on_bytes_received` shall set the stop processing the request. **]** 208 | 209 | ### Chunk Response 210 | 211 | **SRS_UHTTP_07_054: [** If the http header does not include a content length then it indicates a chunk response. **]** 212 | 213 | **SRS_UHTTP_07_055: [** `on_bytes_received` shall convert the hexs length supplied in the response to the data length of the chunked data. **]** 214 | 215 | **SRS_UHTTP_07_056: [** After the response chunk is parsed it shall be placed in a `BUFFER_HANDLE`. **]** 216 | 217 | **SRS_UHTTP_07_057: [** Once the response is stored `on_bytes_received` shall free the bytes that are stored and shrink the stored bytes buffer. **]** 218 | 219 | **SRS_UHTTP_07_058: [** Once a chunk size value of 0 is encountered `on_bytes_received` shall call the on_request_callback with the http message **]** 220 | 221 | **SRS_UHTTP_07_059: [** `on_bytes_received` shall loop throught the stored data to find the /r/n separator. **]** 222 | 223 | **SRS_UHTTP_07_060: [** if the `data_length` specified in the chunk is beyond the amound of data recieved, the parsing shall end and wait for more data. **]** 224 | 225 | ### on_xio_close_complete 226 | 227 | ```c 228 | static void on_xio_close_complete(void* context) 229 | ``` 230 | 231 | **SRS_UHTTP_07_045: [** If `on_close_callback` is not NULL, `on_close_callback` shall be called once the underlying xio is closed. **]** 232 | 233 | ### on_xio_open_complete 234 | 235 | ```c 236 | static void on_xio_open_complete(void* context, IO_OPEN_RESULT open_result) 237 | ``` 238 | 239 | **SRS_UHTTP_07_049: [** If not NULL `uhttp_client_open` shall call the `on_connect` callback with the `callback_ctx`, once the underlying xio's open is complete. **]** 240 | 241 | **SRS_UHTTP_07_042: [** If the underlying XIO object opens successfully the `on_connect` callback shall be call with HTTP_CLIENT_OK... **]** 242 | 243 | **SRS_UHTTP_07_043: [** Otherwise `on_connect` callback shall be call with `HTTP_CLIENT_OPEN_REQUEST_FAILED`. **]** 244 | 245 | ### on_io_error 246 | 247 | ```c 248 | static void on_io_error(void* context) 249 | ``` 250 | 251 | **SRS_UHTTP_07_050: [** if `context` is NULL `on_io_error` shall do nothing. **]** 252 | 253 | **SRS_UHTTP_07_051: [** if on_error callback is not NULL, `on_io_error` shall call on_error callback. **]** 254 | -------------------------------------------------------------------------------- /inc/azure_uhttp_c/uhttp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef UHTTP_H 5 | #define UHTTP_H 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #include 10 | #else 11 | #include 12 | #include 13 | #endif /* __cplusplus */ 14 | 15 | #include "azure_c_shared_utility/httpheaders.h" 16 | #include "azure_macro_utils/macro_utils.h" 17 | #include "azure_c_shared_utility/xio.h" 18 | #include "umock_c/umock_c_prod.h" 19 | 20 | typedef struct HTTP_CLIENT_HANDLE_DATA_TAG* HTTP_CLIENT_HANDLE; 21 | 22 | #define HTTP_CLIENT_RESULT_VALUES \ 23 | HTTP_CLIENT_OK, \ 24 | HTTP_CLIENT_INVALID_ARG, \ 25 | HTTP_CLIENT_ERROR, \ 26 | HTTP_CLIENT_OPEN_FAILED, \ 27 | HTTP_CLIENT_SEND_FAILED, \ 28 | HTTP_CLIENT_ALREADY_INIT, \ 29 | HTTP_CLIENT_HTTP_HEADERS_FAILED, \ 30 | HTTP_CLIENT_INVALID_STATE \ 31 | 32 | 33 | /** @brief Enumeration specifying the possible return values for the APIs in 34 | * this module. 35 | */ 36 | MU_DEFINE_ENUM(HTTP_CLIENT_RESULT, HTTP_CLIENT_RESULT_VALUES); 37 | 38 | #define HTTP_CLIENT_REQUEST_TYPE_VALUES \ 39 | HTTP_CLIENT_REQUEST_OPTIONS, \ 40 | HTTP_CLIENT_REQUEST_GET, \ 41 | HTTP_CLIENT_REQUEST_POST, \ 42 | HTTP_CLIENT_REQUEST_PUT, \ 43 | HTTP_CLIENT_REQUEST_DELETE, \ 44 | HTTP_CLIENT_REQUEST_PATCH 45 | 46 | MU_DEFINE_ENUM(HTTP_CLIENT_REQUEST_TYPE, HTTP_CLIENT_REQUEST_TYPE_VALUES); 47 | 48 | #define HTTP_CALLBACK_REASON_VALUES \ 49 | HTTP_CALLBACK_REASON_OK, \ 50 | HTTP_CALLBACK_REASON_OPEN_FAILED, \ 51 | HTTP_CALLBACK_REASON_SEND_FAILED, \ 52 | HTTP_CALLBACK_REASON_ERROR, \ 53 | HTTP_CALLBACK_REASON_PARSING_ERROR, \ 54 | HTTP_CALLBACK_REASON_DESTROY, \ 55 | HTTP_CALLBACK_REASON_DISCONNECTED 56 | 57 | MU_DEFINE_ENUM(HTTP_CALLBACK_REASON, HTTP_CALLBACK_REASON_VALUES); 58 | 59 | typedef void(*ON_HTTP_OPEN_COMPLETE_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON open_result); 60 | typedef void(*ON_HTTP_ERROR_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON error_result); 61 | typedef void(*ON_HTTP_REQUEST_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_length, unsigned int status_code, 62 | HTTP_HEADERS_HANDLE response_headers); 63 | typedef void(*ON_HTTP_CLOSED_CALLBACK)(void* callback_ctx); 64 | 65 | MOCKABLE_FUNCTION(, HTTP_CLIENT_HANDLE, uhttp_client_create, const IO_INTERFACE_DESCRIPTION*, io_interface_desc, const void*, xio_param, ON_HTTP_ERROR_CALLBACK, on_http_error, void*, callback_ctx); 66 | MOCKABLE_FUNCTION(, void, uhttp_client_destroy, HTTP_CLIENT_HANDLE, handle); 67 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_open, HTTP_CLIENT_HANDLE, handle, const char*, host, int, port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK, on_connect, void*, callback_ctx); 68 | MOCKABLE_FUNCTION(, void, uhttp_client_close, HTTP_CLIENT_HANDLE, handle, ON_HTTP_CLOSED_CALLBACK, on_close_callback, void*, callback_ctx); 69 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_execute_request, HTTP_CLIENT_HANDLE, handle, HTTP_CLIENT_REQUEST_TYPE, request_type, const char*, relative_path, 70 | HTTP_HEADERS_HANDLE, http_header_handle, const unsigned char*, content, size_t, content_length, ON_HTTP_REQUEST_CALLBACK, on_request_callback, void*, callback_ctx); 71 | 72 | MOCKABLE_FUNCTION(, void, uhttp_client_dowork, HTTP_CLIENT_HANDLE, handle); 73 | 74 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trace, HTTP_CLIENT_HANDLE, handle, bool, trace_on, bool, trace_data); 75 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_X509_cert, HTTP_CLIENT_HANDLE, handle, bool, ecc_type, const char*, certificate, const char*, private_key); 76 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trusted_cert, HTTP_CLIENT_HANDLE, handle, const char*, certificate); 77 | MOCKABLE_FUNCTION(, const char*, uhttp_client_get_trusted_cert, HTTP_CLIENT_HANDLE, handle); 78 | MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_option, HTTP_CLIENT_HANDLE, handle, const char*, optionName, const void*, value); 79 | MOCKABLE_FUNCTION(, XIO_HANDLE, uhttp_client_get_underlying_xio, HTTP_CLIENT_HANDLE, handle); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif /* __cplusplus */ 84 | 85 | #endif /* UHTTP_H */ 86 | -------------------------------------------------------------------------------- /jenkins/debian_c.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | 6 | set -e 7 | 8 | script_dir=$(cd "$(dirname "$0")" && pwd) 9 | build_root=$(cd "${script_dir}/.." && pwd) 10 | log_dir=$build_root 11 | make_install= 12 | build_folder=$build_root"/cmake/uhttp" 13 | 14 | rm -r -f $build_folder 15 | mkdir -p $build_folder 16 | pushd $build_folder 17 | cmake ../.. -Drun_valgrind:BOOL=ON -Drun_unittests:bool=ON 18 | if [ $? != 0 ];then 19 | echo "Failure running cmake" 20 | fi 21 | 22 | cmake --build . -- --jobs=$(nproc) 23 | if [ $? != 0 ];then 24 | echo "Failure running build" 25 | fi 26 | 27 | ctest -j $(nproc) -C "debug" -V 28 | if [ $? != 0 ];then 29 | echo "Failure running ctest" 30 | fi 31 | 32 | popd 33 | : -------------------------------------------------------------------------------- /jenkins/linux_c_option_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -o pipefail 3 | # 4 | # Copyright (c) Microsoft. All rights reserved. 5 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 6 | 7 | set -e 8 | 9 | cat /etc/*release | grep VERSION* 10 | gcc --version 11 | openssl version 12 | 13 | script_dir=$(cd "$(dirname "$0")" && pwd) 14 | build_root=$(cd "${script_dir}/.." && pwd) 15 | build_folder=$build_root"/cmake/mqtt_option" 16 | 17 | # Set the default cores 18 | MAKE_CORES=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) 19 | 20 | echo "Initial MAKE_CORES=$MAKE_CORES" 21 | 22 | # Make sure there is enough virtual memory on the device to handle more than one job 23 | MINVSPACE="1500000" 24 | 25 | # Acquire total memory and total swap space setting them to zero in the event the command fails 26 | MEMAR=( $(sed -n -e 's/^MemTotal:[^0-9]*\([0-9][0-9]*\).*/\1/p' -e 's/^SwapTotal:[^0-9]*\([0-9][0-9]*\).*/\1/p' /proc/meminfo) ) 27 | [ -z "${MEMAR[0]##*[!0-9]*}" ] && MEMAR[0]=0 28 | [ -z "${MEMAR[1]##*[!0-9]*}" ] && MEMAR[1]=0 29 | 30 | let VSPACE=${MEMAR[0]}+${MEMAR[1]} 31 | 32 | if [ "$VSPACE" -lt "$MINVSPACE" ] ; then 33 | echo "WARNING: Not enough space. Setting MAKE_CORES=1" 34 | MAKE_CORES=1 35 | fi 36 | 37 | declare -a arr=( 38 | "-Dskip_samples=ON" 39 | "-Dno_logging=ON" 40 | ) 41 | 42 | for item in "${arr[@]}" 43 | do 44 | rm -r -f $build_folder 45 | mkdir -p $build_folder 46 | pushd $build_folder 47 | 48 | echo "executing cmake/make with options <<$item>>" 49 | cmake $build_root "$item" 50 | 51 | make --jobs=$MAKE_CORES 52 | done 53 | popd 54 | -------------------------------------------------------------------------------- /jenkins/linux_install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | 5 | set -e 6 | 7 | # Print version 8 | cat /etc/*release | grep VERSION* 9 | gcc --version 10 | 11 | # Set the default cores 12 | CORES=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) 13 | 14 | build_root=$(cd "$(dirname "$0")/.." && pwd) 15 | clone_root=$(cd "$(dirname "$0")/../.." && pwd) 16 | cd $build_root 17 | 18 | echo "Build Root $build_root" 19 | 20 | sdk_build_folder=$build_root"/cmake/install_deps" 21 | 22 | # Build the SDK 23 | rm -rf $sdk_build_folder 24 | mkdir -p $sdk_build_folder 25 | pushd $sdk_build_folder 26 | cmake $build_root 27 | make install --jobs=$CORES 28 | popd 29 | 30 | # Now use the deps 31 | rm -rf $sdk_build_folder 32 | mkdir -p $sdk_build_folder 33 | pushd $sdk_build_folder 34 | cmake $build_root -Duse_installed_dependencies=ON 35 | make --jobs=$CORES 36 | popd 37 | -------------------------------------------------------------------------------- /jenkins/linux_wolfssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | 5 | set -e 6 | 7 | build_root=$(cd "$(dirname "$0")/.." && pwd) 8 | cd $build_root 9 | 10 | build_folder=$build_root"/cmake/shared-util_linux" 11 | 12 | # Set the default cores 13 | CORES=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) 14 | 15 | rm -r -f $build_folder 16 | mkdir -p $build_folder 17 | pushd $build_folder 18 | cmake $build_root -Drun_unittests:BOOL=ON -Duse_wolfssl:BOOL=ON -Duse_openssl:BOOL=OFF -D CMAKE_C_COMPILER=gcc 19 | make --jobs=$CORES 20 | 21 | #use doctored openssl 22 | export LD_LIBRARY_PATH=/usr/local/ssl/lib 23 | ctest -j $CORES --output-on-failure 24 | export LD_LIBRARY_PATH= 25 | 26 | popd 27 | -------------------------------------------------------------------------------- /jenkins/osx_gcc_openssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | 6 | set -e 7 | 8 | script_dir=$(cd "$(dirname "$0")" && pwd) 9 | build_root=$(cd "${script_dir}/.." && pwd) 10 | build_folder=$build_root"/cmake/uhttp" 11 | 12 | CORES=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) 13 | 14 | rm -r -f $build_folder 15 | mkdir -p $build_folder 16 | pushd $build_folder 17 | cmake ../.. -DOPENSSL_ROOT_DIR:PATH=/usr/local/opt/openssl -Duse_openssl:bool=ON -Drun_unittests:bool=ON 18 | cmake --build . -- --jobs=$CORES 19 | ctest -C "debug" -V 20 | popd 21 | -------------------------------------------------------------------------------- /jenkins/osx_xcode_native.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | 6 | set -e 7 | 8 | script_dir=$(cd "$(dirname "$0")" && pwd) 9 | build_root=$(cd "${script_dir}/.." && pwd) 10 | build_folder=$build_root"/cmake/uhttp" 11 | 12 | CORES=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu) 13 | 14 | rm -r -f $build_folder 15 | mkdir -p $build_folder 16 | pushd $build_folder 17 | cmake ../.. -Drun_unittests:bool=ON -G Xcode 18 | cmake --build . -- --jobs=$CORES 19 | ctest -C "debug" -V 20 | popd 21 | -------------------------------------------------------------------------------- /jenkins/raspberrypi_c_buster.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # assume directory is root of downloaded repo 3 | mkdir cmake 4 | cd cmake 5 | ls -al 6 | 7 | # Create a cmake toolchain file on the fly 8 | echo "SET(CMAKE_SYSTEM_NAME Linux) # this one is important" > toolchain.cmake 9 | echo "SET(CMAKE_SYSTEM_VERSION 1) # this one not so much" >> toolchain.cmake 10 | 11 | echo "SET(CMAKE_C_COMPILER ${TOOLCHAIN_EXES}/${TOOLCHAIN_NAME}-gcc)" >> toolchain.cmake 12 | echo "SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_EXES}/${TOOLCHAIN_NAME}-g++)" >> toolchain.cmake 13 | echo "SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_SYSROOT})" >> toolchain.cmake 14 | echo "SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)" >> toolchain.cmake 15 | echo "SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)" >> toolchain.cmake 16 | echo "SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)" >> toolchain.cmake 17 | ls -al 18 | 19 | # Build the SDK. This will use the OpenSSL, cURL and uuid binaries that we built before 20 | cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -Duse_prov_client=OFF -DCMAKE_INSTALL_PREFIX=${TOOLCHAIN_PREFIX} -Drun_e2e_tests=ON -Drun_unittests=ON .. 21 | make -j 2 22 | ls -al 23 | -------------------------------------------------------------------------------- /jenkins/ubuntu_c.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | 6 | set -e 7 | 8 | script_dir=$(cd "$(dirname "$0")" && pwd) 9 | build_root=$(cd "${script_dir}/.." && pwd) 10 | log_dir=$build_root 11 | make_install= 12 | build_folder=$build_root"/cmake/uhttp" 13 | 14 | rm -r -f $build_folder 15 | mkdir -p $build_folder 16 | pushd $build_folder 17 | cmake ../.. -Drun_valgrind:BOOL=ON -Drun_unittests:bool=ON 18 | if [ $? != 0 ];then 19 | echo "Failure running cmake" 20 | fi 21 | 22 | cmake --build . -- --jobs=$(nproc) 23 | if [ $? != 0 ];then 24 | echo "Failure running build" 25 | fi 26 | 27 | ctest -C "debug" -V 28 | if [ $? != 0 ];then 29 | echo "Failure running tests" 30 | fi 31 | 32 | popd 33 | : -------------------------------------------------------------------------------- /jenkins/ubuntu_clang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | 6 | set -e 7 | 8 | script_dir=$(cd "$(dirname "$0")" && pwd) 9 | build_root=$(cd "${script_dir}/.." && pwd) 10 | log_dir=$build_root 11 | make_install= 12 | build_folder=$build_root"/cmake/uhttp" 13 | 14 | rm -r -f $build_folder 15 | mkdir -p $build_folder 16 | pushd $build_folder 17 | cmake ../.. -Drun_valgrind:BOOL=ON -Drun_unittests:bool=ON 18 | cmake --build . -- --jobs=$(nproc) 19 | ctest -C "debug" -V 20 | 21 | popd 22 | : -------------------------------------------------------------------------------- /jenkins/windows_c.cmd: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) Microsoft. All rights reserved. 2 | @REM Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | @setlocal EnableExtensions EnableDelayedExpansion 5 | @echo off 6 | 7 | set current-path=%~dp0 8 | rem // remove trailing slash 9 | set current-path=%current-path:~0,-1% 10 | 11 | echo Current Path: %current-path% 12 | 13 | set build-root=%current-path%\.. 14 | rem // resolve to fully qualified path 15 | for %%i in ("%build-root%") do set build-root=%%~fi 16 | 17 | set repo_root=%build-root% 18 | rem // resolve to fully qualified path 19 | for %%i in ("%repo_root%") do set repo_root=%%~fi 20 | 21 | set CMAKE_DIR=uhttp_win32 22 | set build-config=Debug 23 | set build-platform=Win32 24 | 25 | echo Build Root: %build-root% 26 | echo Repo Root: %repo_root% 27 | 28 | rem //begin building 29 | 30 | if EXIST %build-root%\cmake\%CMAKE_DIR% ( 31 | rmdir /s/q %build-root%\cmake\%CMAKE_DIR% 32 | rem no error checking 33 | ) 34 | 35 | echo CMAKE Output Path: %build-root%\cmake\%CMAKE_DIR% 36 | mkdir %build-root%\cmake\%CMAKE_DIR% 37 | rem no error checking 38 | pushd %build-root%\cmake\%CMAKE_DIR% 39 | 40 | echo ***checking msbuild*** 41 | where msbuild 42 | 43 | if %build-platform% == x64 ( 44 | echo ***Running CMAKE for Win64*** 45 | cmake %build-root% -LAH -Duse_cppunittest:BOOL=OFF -Drun_unittests:BOOL=ON -A x64 -G %VSVERSION% 46 | ) else ( 47 | echo ***Running CMAKE for Win32*** 48 | cmake %build-root% -LAH -Duse_cppunittest:BOOL=OFF -Drun_unittests:BOOL=ON -A Win32 -G %VSVERSION% 49 | ) 50 | if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! 51 | 52 | msbuild /m uhttp.sln "/p:Configuration=%build-config%;Platform=%build-platform%" 53 | if !ERRORLEVEL! neq 0 exit /b !ERRORLEVEL! 54 | 55 | if %build-platform% neq arm ( 56 | ctest -C "debug" -V 57 | if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! 58 | ) 59 | 60 | popd -------------------------------------------------------------------------------- /jenkins/windows_c_VsDevCmd.cmd: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) Microsoft. All rights reserved. 2 | @REM Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | echo ***setting VC paths*** 5 | IF EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsMSBuildCmd.bat" ( 6 | call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsMSBuildCmd.bat" 7 | set VSVERSION="Visual Studio 15 2017" 8 | ) ELSE IF EXIST "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" ( 9 | IF "%1" == "x64" ( 10 | call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 11 | ) ELSE ( 12 | call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" 13 | ) 14 | set VSVERSION="Visual Studio 17 2022" 15 | ) ELSE ( 16 | echo "ERROR: Did not find Microsoft Visual Studio" 17 | EXIT 1 18 | ) 19 | 20 | where /q msbuild 21 | IF ERRORLEVEL 1 ( 22 | echo "ERROR: Did not find msbuild" 23 | exit %ERRORLEVEL% 24 | ) 25 | 26 | where msbuild 27 | msbuild -version 28 | -------------------------------------------------------------------------------- /jenkins/windows_c_build_as_dynamic.cmd: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) Microsoft. All rights reserved. 2 | @REM Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | @setlocal 5 | @echo off 6 | 7 | set cmake_dir=dynamic_build 8 | set build-root=%~dp0.. 9 | @REM // resolve to fully qualified path 10 | for %%i in ("%build-root%") do set build-root=%%~fi 11 | 12 | if EXIST %build-root%\cmake\%cmake_dir% ( 13 | rmdir /s/q %build-root%\cmake\%cmake_dir% 14 | ) 15 | mkdir %build-root%\cmake\%cmake_dir% 16 | if errorlevel 1 goto :eof 17 | 18 | cd %build-root%\cmake\%cmake_dir% 19 | 20 | cmake -Dskip_samples=ON -Dbuild_as_dynamic=ON -G %VSVERSION% ..\.. 21 | if errorlevel 1 goto :eof 22 | 23 | cmake --build . -- /m 24 | if errorlevel 1 goto :eof 25 | -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #this is CMakeLists.txt for samples. There's noithig here, except redirections to 5 | #individual protocol samples 6 | 7 | usePermissiveRulesForSamplesAndTests() 8 | 9 | function(add_sample_directory whatIsBuilding) 10 | add_subdirectory(${whatIsBuilding}) 11 | 12 | set_target_properties(${whatIsBuilding} 13 | PROPERTIES 14 | FOLDER "UHTTP_Samples") 15 | endfunction() 16 | 17 | add_sample_directory(uhttp_sample) 18 | -------------------------------------------------------------------------------- /samples/uhttp_sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | set(uhttp_sample_c_files 5 | uhttp_sample.c 6 | ) 7 | 8 | set(uhttp_sample_h_files 9 | ) 10 | 11 | IF(WIN32) 12 | #windows needs this define 13 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 14 | add_definitions(-DGB_MEASURE_MEMORY_FOR_THIS -DGB_DEBUG_ALLOC) 15 | ENDIF(WIN32) 16 | 17 | include_directories(.) 18 | include_directories(${DEV_AUTH_MODULES_CLIENT_INC_FOLDER}) 19 | include_directories(${SHARED_UTIL_INC_FOLDER}) 20 | 21 | add_executable(uhttp_sample ${uhttp_sample_c_files} ${uhttp_sample_h_files}) 22 | 23 | compileTargetAsC99(uhttp_sample) 24 | 25 | target_link_libraries(uhttp_sample uhttp) 26 | linkSharedUtil(uhttp_sample) 27 | 28 | -------------------------------------------------------------------------------- /samples/uhttp_sample/uhttp_sample.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "azure_c_shared_utility/threadapi.h" 9 | #include "azure_c_shared_utility/tickcounter.h" 10 | #include "azure_c_shared_utility/crt_abstractions.h" 11 | #include "azure_c_shared_utility/platform.h" 12 | #include "azure_c_shared_utility/socketio.h" 13 | #include "azure_c_shared_utility/tlsio.h" 14 | #include "azure_uhttp_c/uhttp.h" 15 | 16 | #define HTTP_PORT_NUM 80 17 | #define HTTPS_PORT_NUM 443 18 | 19 | typedef struct HTTP_SAMPLE_INFO_TAG 20 | { 21 | int stop_running; 22 | } HTTP_SAMPLE_INFO; 23 | 24 | static void on_http_connected(void* callback_ctx, HTTP_CALLBACK_REASON connect_result) 25 | { 26 | (void)callback_ctx; 27 | if (connect_result == HTTP_CALLBACK_REASON_OK) 28 | { 29 | (void)printf ("HTTP Connected\r\n"); 30 | } 31 | else 32 | { 33 | (void)printf ("HTTP Connection FAILED\r\n"); 34 | } 35 | } 36 | 37 | static void on_http_recv(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_len, unsigned int statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle) 38 | { 39 | (void)responseHeadersHandle; 40 | (void)request_result; 41 | (void)content_len; 42 | (void)statusCode; 43 | (void)content; 44 | if (callback_ctx != NULL) 45 | { 46 | HTTP_SAMPLE_INFO* http_info = (HTTP_SAMPLE_INFO*)callback_ctx; 47 | http_info->stop_running = 1; 48 | } 49 | else 50 | { 51 | (void)printf("callback_ctx is NULL!!!!!\r\n"); 52 | } 53 | } 54 | 55 | static void on_error(void* callback_ctx, HTTP_CALLBACK_REASON error_result) 56 | { 57 | (void)callback_ctx; 58 | (void)error_result; 59 | printf("HTTP client Error Called\r\n"); 60 | } 61 | 62 | static void on_closed_callback(void* callback_ctx) 63 | { 64 | (void)callback_ctx; 65 | printf("Connection closed callback\r\n"); 66 | } 67 | 68 | static HTTP_CLIENT_HANDLE create_uhttp_client_handle(HTTP_SAMPLE_INFO* sample_info, const char* host_name, int port_num) 69 | { 70 | SOCKETIO_CONFIG config; 71 | TLSIO_CONFIG tls_io_config; 72 | const void* xio_param; 73 | const IO_INTERFACE_DESCRIPTION* interface_desc; 74 | if (port_num == HTTPS_PORT_NUM) 75 | { 76 | tls_io_config.hostname = host_name; 77 | tls_io_config.port = port_num; 78 | tls_io_config.underlying_io_interface = NULL; 79 | tls_io_config.underlying_io_parameters = NULL; 80 | xio_param = &tls_io_config; 81 | // Get the TLS definition 82 | interface_desc = platform_get_default_tlsio(); 83 | } 84 | else 85 | { 86 | config.accepted_socket = NULL; 87 | config.hostname = host_name; 88 | config.port = port_num; 89 | xio_param = &config; 90 | // Get the socket definition 91 | interface_desc = socketio_get_interface_description(); 92 | } 93 | return uhttp_client_create(interface_desc, xio_param, on_error, sample_info); 94 | } 95 | 96 | static void test_http_get(void) 97 | { 98 | const char* host_name = "httpbin.org"; 99 | int port_num = HTTP_PORT_NUM; 100 | HTTP_SAMPLE_INFO sample_info; 101 | sample_info.stop_running = 0; 102 | 103 | HTTP_CLIENT_HANDLE http_handle = create_uhttp_client_handle(&sample_info, host_name, port_num); 104 | if (http_handle == NULL) 105 | { 106 | (void)printf("FAILED HERE\r\n"); 107 | } 108 | else 109 | { 110 | (void)uhttp_client_set_trace(http_handle, true, true); 111 | if (uhttp_client_open(http_handle, host_name, port_num, on_http_connected, &sample_info) != HTTP_CLIENT_OK) 112 | { 113 | (void)printf("FAILED MORE HERE\r\n"); 114 | } 115 | else 116 | { 117 | if (uhttp_client_execute_request(http_handle, HTTP_CLIENT_REQUEST_GET, "/get", NULL, NULL, 0, on_http_recv, &sample_info) != HTTP_CLIENT_OK) 118 | { 119 | (void)printf("FAILED FURTHER HERE\r\n"); 120 | } 121 | else 122 | { 123 | do 124 | { 125 | uhttp_client_dowork(http_handle); 126 | } while (sample_info.stop_running == 0); 127 | } 128 | uhttp_client_close(http_handle, on_closed_callback, NULL); 129 | } 130 | uhttp_client_destroy(http_handle); 131 | } 132 | } 133 | 134 | void test_http_post(void) 135 | { 136 | const char* host_name = "httpbin.org"; 137 | int port_num = HTTP_PORT_NUM; 138 | HTTP_SAMPLE_INFO sample_info; 139 | sample_info.stop_running = 0; 140 | 141 | HTTP_CLIENT_HANDLE http_handle = create_uhttp_client_handle(&sample_info, host_name, port_num); 142 | if (http_handle == NULL) 143 | { 144 | (void)printf("FAILED MORE HERE\r\n"); 145 | } 146 | else 147 | { 148 | (void)uhttp_client_set_trace(http_handle, true, true); 149 | if (uhttp_client_open(http_handle, host_name, port_num, on_http_connected, &sample_info) != HTTP_CLIENT_OK) 150 | { 151 | (void)printf("FAILED MORE HERE\r\n"); 152 | } 153 | else 154 | { 155 | HTTP_HEADERS_HANDLE http_headers = HTTPHeaders_Alloc(); 156 | if (http_headers == NULL) 157 | { 158 | (void)printf("http alloc failed\r\n"); 159 | } 160 | else 161 | { 162 | HTTPHeaders_AddHeaderNameValuePair(http_headers, "User-Agent", "uhttp/1.0"); 163 | HTTPHeaders_AddHeaderNameValuePair(http_headers, "Content-Type", "application/json"); 164 | 165 | const char* post_data = "{ \"encryptedData\": { \"authKey\": \"xxxxxxx\", \"secondaryAuthKey\": \"xxxxxxx\", nonce: \"blah\" }, correlationId: \"ASDDSA\" }"; 166 | size_t len = strlen(post_data); 167 | if (uhttp_client_execute_request(http_handle, HTTP_CLIENT_REQUEST_POST, "/post", http_headers, (const unsigned char*)post_data, len, on_http_recv, &sample_info) != HTTP_CLIENT_OK) 168 | { 169 | (void)printf("FAILED FURTHER HERE\r\n"); 170 | } 171 | else 172 | { 173 | do 174 | { 175 | uhttp_client_dowork(http_handle); 176 | } while (sample_info.stop_running == 0); 177 | } 178 | HTTPHeaders_Free(http_headers); 179 | } 180 | uhttp_client_close(http_handle, on_closed_callback, NULL); 181 | } 182 | uhttp_client_destroy(http_handle); 183 | } 184 | } 185 | 186 | int main(void) 187 | { 188 | int result; 189 | 190 | if (platform_init() != 0) 191 | { 192 | (void)printf("platform_init\r\n"); 193 | result = __LINE__; 194 | } 195 | else 196 | { 197 | result = 0; 198 | 199 | (void)printf("\r\nSending HTTP GET\r\n\r\n"); 200 | test_http_get(); 201 | 202 | (void)printf("\r\nSending HTTP POST\r\n\r\n"); 203 | test_http_post(); 204 | 205 | platform_deinit(); 206 | } 207 | 208 | (void)printf("Press any key to continue:"); 209 | (void)getchar(); 210 | return result; 211 | } 212 | -------------------------------------------------------------------------------- /src/uhttp.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "umock_c/umock_c_prod.h" 10 | #include "azure_c_shared_utility/gballoc.h" 11 | 12 | #include 13 | #include "azure_uhttp_c/uhttp.h" 14 | #include "azure_c_shared_utility/httpheaders.h" 15 | #include "azure_c_shared_utility/crt_abstractions.h" 16 | #include "azure_c_shared_utility/strings.h" 17 | #include "azure_c_shared_utility/xlogging.h" 18 | #include "azure_c_shared_utility/buffer_.h" 19 | #include "azure_c_shared_utility/singlylinkedlist.h" 20 | #include "azure_c_shared_utility/shared_util_options.h" 21 | #include "azure_c_shared_utility/optimize_size.h" 22 | #include "azure_c_shared_utility/safe_math.h" 23 | 24 | #define MAX_HOSTNAME 64 25 | #define TIME_MAX_BUFFER 16 26 | #define HTTP_CRLF_LEN 2 27 | #define HTTP_END_TOKEN_LEN 4 28 | #define MAX_CONTENT_LENGTH 16 29 | 30 | static const char* HTTP_REQUEST_LINE_FMT = "%s %s HTTP/1.1\r\n"; 31 | static const char* HTTP_HOST = "Host"; 32 | // The following header names MUST be lowercase as they are used for HTTP response header comparison: 33 | static const char* HTTP_CONTENT_LEN = "content-length"; 34 | static const char* HTTP_TRANSFER_ENCODING = "transfer-encoding"; 35 | static const char* HTTP_CRLF_VALUE = "\r\n"; 36 | static const char* HTTP_AUTH_HEADER = "\r\nAuthorization:"; 37 | static const char* HTTP_PROXY_AUTH_HEADER = "\r\nProxy-Authorization:"; 38 | 39 | typedef enum RESPONSE_MESSAGE_STATE_TAG 40 | { 41 | state_initial, 42 | state_opening, 43 | state_open, 44 | state_process_status_line, 45 | state_process_headers, 46 | state_process_body, 47 | state_process_chunked_body, 48 | 49 | state_send_user_callback, 50 | state_parse_complete, 51 | 52 | state_closing, 53 | state_closed, 54 | state_error 55 | } RESPONSE_MESSAGE_STATE; 56 | 57 | typedef struct HTTP_RECV_DATA_TAG 58 | { 59 | ON_HTTP_REQUEST_CALLBACK on_request_callback; 60 | void* user_ctx; 61 | int status_code; 62 | RESPONSE_MESSAGE_STATE recv_state; 63 | HTTP_HEADERS_HANDLE resp_header; 64 | BUFFER_HANDLE msg_body; 65 | size_t total_body_len; 66 | BUFFER_HANDLE accrual_buff; 67 | bool chunked_reply; 68 | } HTTP_RECV_DATA; 69 | 70 | typedef struct HTTP_CLIENT_HANDLE_DATA_TAG 71 | { 72 | XIO_HANDLE xio_handle; 73 | ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect; 74 | void* connect_user_ctx; 75 | ON_HTTP_ERROR_CALLBACK on_error; 76 | void* error_user_ctx; 77 | ON_HTTP_CLOSED_CALLBACK on_close_callback; 78 | void* close_user_ctx; 79 | HTTP_RECV_DATA recv_msg; 80 | bool chunk_request; 81 | bool trace_on; 82 | bool trace_body; 83 | char* host_name; 84 | int port_num; 85 | SINGLYLINKEDLIST_HANDLE data_list; 86 | bool cert_type_ecc; 87 | char* x509_cert; 88 | char* x509_pk; 89 | char* certificate; 90 | int connected; 91 | } HTTP_CLIENT_HANDLE_DATA; 92 | 93 | typedef struct HTTP_SEND_DATA_TAG 94 | { 95 | HTTP_CLIENT_REQUEST_TYPE request_type; 96 | STRING_HANDLE relative_path; 97 | STRING_HANDLE header_line; 98 | BUFFER_HANDLE content; 99 | } HTTP_SEND_DATA; 100 | 101 | static void send_complete_callback(void* context, IO_SEND_RESULT send_result) 102 | { 103 | (void)context;(void)send_result; 104 | } 105 | 106 | static int initialize_received_data(HTTP_CLIENT_HANDLE_DATA* http_data) 107 | { 108 | int result = 0; 109 | 110 | // Initialize data if necessary 111 | if (http_data->recv_msg.resp_header == NULL) 112 | { 113 | http_data->recv_msg.resp_header = HTTPHeaders_Alloc(); 114 | if (http_data->recv_msg.resp_header == NULL) 115 | { 116 | /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ 117 | LogError("Failure creating Http header."); 118 | result = MU_FAILURE; 119 | } 120 | } 121 | if (result == 0 && http_data->recv_msg.accrual_buff == NULL) 122 | { 123 | http_data->recv_msg.accrual_buff = BUFFER_new(); 124 | if (http_data->recv_msg.accrual_buff == NULL) 125 | { 126 | /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ 127 | LogError("Failure creating accrual buffer."); 128 | result = MU_FAILURE; 129 | } 130 | } 131 | http_data->recv_msg.chunked_reply = false; 132 | return result; 133 | } 134 | 135 | static int process_status_code_line(const unsigned char* buffer, size_t len, size_t* position, int* statusLen) 136 | { 137 | int result = MU_FAILURE; 138 | size_t index; 139 | int spaceFound = 0; 140 | const char* initSpace = NULL; 141 | char status_code[4] = { 0 }; 142 | 143 | for (index = 0; index < len; index++) 144 | { 145 | if (buffer[index] == ' ') 146 | { 147 | if (spaceFound == 1) 148 | { 149 | (void)memcpy(status_code, initSpace, 3); 150 | status_code[3] = '\0'; 151 | } 152 | else 153 | { 154 | initSpace = (const char*)buffer + index + 1; 155 | } 156 | spaceFound++; 157 | } 158 | else if (buffer[index] == '\n') 159 | { 160 | *statusLen = (int)atol(status_code); 161 | if (index < len) 162 | { 163 | *position = index + 1; 164 | } 165 | else 166 | { 167 | *position = index; 168 | } 169 | result = 0; 170 | break; 171 | } 172 | } 173 | return result; 174 | } 175 | 176 | static int process_header_line(const unsigned char* buffer, size_t len, size_t* position, HTTP_HEADERS_HANDLE resp_header, size_t* contentLen, bool* isChunked) 177 | { 178 | int result = MU_FAILURE; 179 | size_t index; 180 | const unsigned char* targetPos = buffer; 181 | bool crlfEncounted = false; 182 | bool colonEncountered = false; 183 | char* headerKey = NULL; 184 | bool continueProcessing = true; 185 | 186 | for (index = 0; index < len && continueProcessing; index++) 187 | { 188 | if (buffer[index] == ':' && !colonEncountered) 189 | { 190 | colonEncountered = true; 191 | size_t keyLen = (&buffer[index]) - targetPos; 192 | 193 | if (keyLen == 0) 194 | { 195 | LogError("Invalid header name with zero length."); 196 | result = MU_FAILURE; 197 | continueProcessing = false; 198 | } 199 | else 200 | { 201 | if (headerKey != NULL) 202 | { 203 | free(headerKey); 204 | headerKey = NULL; 205 | } 206 | size_t malloc_size = safe_add_size_t(keyLen, 1); 207 | if (malloc_size == SIZE_MAX || 208 | (headerKey = (char*)malloc(malloc_size)) == NULL) 209 | { 210 | LogError("Cannot malloc headerKey, size:%zu", malloc_size); 211 | result = MU_FAILURE; 212 | continueProcessing = false; 213 | } 214 | else 215 | { 216 | memcpy(headerKey, targetPos, keyLen); 217 | headerKey[keyLen] = '\0'; 218 | 219 | // Convert to lower case 220 | for (size_t inner = 0; inner < keyLen; inner++) 221 | { 222 | headerKey[inner] = (char)tolower(headerKey[inner]); 223 | } 224 | 225 | targetPos = buffer+index+1; 226 | crlfEncounted = false; 227 | } 228 | } 229 | } 230 | else if (buffer[index] == '\r') 231 | { 232 | if (headerKey != NULL) 233 | { 234 | // Remove leading spaces 235 | while (*targetPos == 32) { targetPos++; } 236 | 237 | size_t valueLen = safe_subtract_size_t((&buffer[index]), targetPos); 238 | size_t malloc_size = safe_add_size_t(valueLen, 1); 239 | 240 | char* headerValue; 241 | if (malloc_size == SIZE_MAX || 242 | (headerValue = (char*)malloc(malloc_size)) == NULL) 243 | { 244 | LogError("Cannot malloc headerValue, size:%zu", malloc_size); 245 | result = MU_FAILURE; 246 | continueProcessing = false; 247 | headerValue = NULL; 248 | } 249 | else 250 | { 251 | memcpy(headerValue, targetPos, valueLen); 252 | headerValue[valueLen] = '\0'; 253 | 254 | if (HTTPHeaders_AddHeaderNameValuePair(resp_header, headerKey, headerValue) != HTTP_HEADERS_OK) 255 | { 256 | result = MU_FAILURE; 257 | continueProcessing = false; 258 | } 259 | else 260 | { 261 | if (strcmp(headerKey, HTTP_CONTENT_LEN) == 0) 262 | { 263 | *isChunked = false; 264 | *contentLen = atol(headerValue); 265 | } 266 | else if (strcmp(headerKey, HTTP_TRANSFER_ENCODING) == 0) 267 | { 268 | *isChunked = true; 269 | *contentLen = 0; 270 | } 271 | if (index < len) 272 | { 273 | *position = index; 274 | } 275 | else 276 | { 277 | *position = index-1; 278 | } 279 | } 280 | } 281 | free(headerKey); 282 | headerKey = NULL; 283 | free(headerValue); 284 | } 285 | } 286 | else if (buffer[index] == '\n') 287 | { 288 | if (crlfEncounted) 289 | { 290 | if (index < len) 291 | { 292 | *position = index+1; 293 | } 294 | else 295 | { 296 | *position = index; 297 | } 298 | result = 0; 299 | break; 300 | } 301 | else 302 | { 303 | colonEncountered = false; 304 | crlfEncounted = true; 305 | targetPos = buffer+index+1; 306 | } 307 | } 308 | else 309 | { 310 | crlfEncounted = false; 311 | } 312 | } 313 | if (headerKey != NULL) 314 | { 315 | free(headerKey); 316 | } 317 | return result; 318 | } 319 | 320 | static void log_text_line(const char* text_line) 321 | { 322 | char* authStart = strstr(text_line, HTTP_AUTH_HEADER); 323 | char* proxyAuthStart = strstr(text_line, HTTP_PROXY_AUTH_HEADER); 324 | char* authEol = NULL; 325 | char* proxyAuthEol = NULL; 326 | 327 | if (authStart != NULL) 328 | { 329 | authEol = strstr((authStart + 1), HTTP_CRLF_VALUE); 330 | } 331 | 332 | if (proxyAuthStart != NULL) 333 | { 334 | proxyAuthEol = strstr((proxyAuthStart + 1), HTTP_CRLF_VALUE); 335 | } 336 | 337 | if (authStart == NULL && proxyAuthStart == NULL) 338 | { 339 | LOG(AZ_LOG_TRACE, LOG_LINE, "%s", text_line); 340 | } 341 | else if (proxyAuthStart == NULL) 342 | { 343 | if (authEol != NULL) 344 | { 345 | LOG(AZ_LOG_TRACE, LOG_LINE, "%.*s \r\nAuthorization: *** %.*s", 346 | (int)(authStart - text_line), text_line, 347 | (int)(strlen(text_line) - (authEol - text_line)), authEol); 348 | } 349 | } 350 | else if (authStart == NULL) 351 | { 352 | if (proxyAuthEol != NULL) 353 | { 354 | LOG(AZ_LOG_TRACE, LOG_LINE, "%.*s \r\nProxy-Authorization: *** %.*s", 355 | (int)(proxyAuthStart - text_line), text_line, 356 | (int)(strlen(text_line) - (proxyAuthEol - text_line)), proxyAuthEol); 357 | } 358 | } 359 | else 360 | { 361 | if(authStart < proxyAuthStart) 362 | { 363 | LOG(AZ_LOG_TRACE, LOG_LINE, "%.*s \r\nAuthorization: *** %.*s \r\nProxy-Authorization: *** %.*s", 364 | (int)(authStart - text_line), text_line, 365 | (int)(proxyAuthStart - authEol), authEol, 366 | (int)(proxyAuthEol - text_line), proxyAuthEol); 367 | } 368 | else 369 | { 370 | LOG(AZ_LOG_TRACE, LOG_LINE, "%.*s \r\nProxy-Authorization: *** %.*s \r\nAuthorization: *** %.*s", 371 | (int)(proxyAuthStart - text_line), text_line, 372 | (int)(authStart - proxyAuthEol), proxyAuthEol, 373 | (int)(authEol - text_line), authEol); 374 | } 375 | } 376 | } 377 | 378 | static int write_text_line(HTTP_CLIENT_HANDLE_DATA* http_data, const char* text_line) 379 | { 380 | int result; 381 | 382 | if (xio_send(http_data->xio_handle, text_line, strlen(text_line), send_complete_callback, NULL) != 0) 383 | { 384 | LogError("Failure calling xio_send."); 385 | result = MU_FAILURE; 386 | } 387 | else 388 | { 389 | result = 0; 390 | if (http_data->trace_on) 391 | { 392 | log_text_line(text_line); 393 | } 394 | } 395 | return result; 396 | } 397 | 398 | static int write_data_line(HTTP_CLIENT_HANDLE_DATA* http_data, const unsigned char* data_line, size_t length) 399 | { 400 | int result; 401 | if (xio_send(http_data->xio_handle, data_line, length, send_complete_callback, NULL) != 0) 402 | { 403 | LogError("Failure calling xio_send."); 404 | result = MU_FAILURE; 405 | } 406 | else 407 | { 408 | result = 0; 409 | if (http_data->trace_on) 410 | { 411 | if (length > 0) 412 | { 413 | if (http_data->trace_body) 414 | { 415 | LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n%.*s\r\n", (int)length, (int)length, data_line); 416 | } 417 | else 418 | { 419 | LOG(AZ_LOG_TRACE, LOG_LINE, " len: %d\r\n", (int)length); 420 | } 421 | } 422 | else 423 | { 424 | LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n", (int)length); 425 | } 426 | } 427 | } 428 | return result; 429 | } 430 | 431 | static int convert_char_to_hex(const unsigned char* hexText, size_t len) 432 | { 433 | int result = 0; 434 | for (size_t index = 0; index < len; index++) 435 | { 436 | if (hexText[index] == ';') 437 | { 438 | break; 439 | } 440 | else 441 | { 442 | int accumulator = 0; 443 | if (hexText[index] >= 48 && hexText[index] <= 57) 444 | { 445 | accumulator = hexText[index] - 48; 446 | } 447 | else if (hexText[index] >= 65 && hexText[index] <= 70) 448 | { 449 | accumulator = hexText[index] - 55; 450 | } 451 | else if (hexText[index] >= 97 && hexText[index] <= 102) 452 | { 453 | accumulator = hexText[index] - 87; 454 | } 455 | if (index > 0) 456 | { 457 | result = result << 4; 458 | } 459 | result += accumulator; 460 | } 461 | } 462 | return result; 463 | } 464 | 465 | static int setup_init_recv_msg(HTTP_RECV_DATA* recv_msg) 466 | { 467 | int result; 468 | recv_msg->status_code = 0; 469 | recv_msg->recv_state = state_initial; 470 | recv_msg->total_body_len = 0; 471 | if (recv_msg->resp_header != NULL) 472 | { 473 | HTTPHeaders_Free(recv_msg->resp_header); 474 | } 475 | if (recv_msg->msg_body != NULL) 476 | { 477 | BUFFER_delete(recv_msg->msg_body); 478 | } 479 | if ((recv_msg->resp_header = HTTPHeaders_Alloc()) == NULL) 480 | { 481 | /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */ 482 | LogError("Failure allocating http http_data items"); 483 | result = HTTP_CLIENT_ERROR; 484 | } 485 | else if ((recv_msg->msg_body = BUFFER_new()) == NULL) 486 | { 487 | /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */ 488 | LogError("Failure allocating http data items"); 489 | HTTPHeaders_Free(recv_msg->resp_header); 490 | recv_msg->resp_header = NULL; 491 | result = HTTP_CLIENT_ERROR; 492 | } 493 | else 494 | { 495 | result = 0; 496 | } 497 | return result; 498 | } 499 | 500 | static void on_bytes_received(void* context, const unsigned char* buffer, size_t len) 501 | { 502 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; 503 | 504 | if (http_data != NULL && buffer != NULL && len > 0 && http_data->recv_msg.recv_state != state_error) 505 | { 506 | if (http_data->recv_msg.recv_state == state_parse_complete) 507 | { 508 | // The callback is getting called during a new send. 509 | setup_init_recv_msg(&http_data->recv_msg); 510 | 511 | } 512 | 513 | if (http_data->recv_msg.recv_state == state_initial || http_data->recv_msg.recv_state == state_open) 514 | { 515 | if (initialize_received_data(http_data) != 0) 516 | { 517 | http_data->recv_msg.recv_state = state_error; 518 | } 519 | else 520 | { 521 | http_data->recv_msg.recv_state = state_process_status_line; 522 | } 523 | } 524 | 525 | // Put the data in the buffer 526 | if (BUFFER_append_build(http_data->recv_msg.accrual_buff, buffer, len) != 0) 527 | { 528 | /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */ 529 | LogError("Failure appending bytes to buffer."); 530 | http_data->recv_msg.recv_state = state_error; 531 | } 532 | 533 | if (http_data->recv_msg.recv_state == state_process_status_line) 534 | { 535 | size_t index = 0; 536 | const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff); 537 | size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); 538 | 539 | int lineComplete = process_status_code_line(stored_bytes, stored_len, &index, &http_data->recv_msg.status_code); 540 | if (lineComplete == 0 && http_data->recv_msg.status_code > 0) 541 | { 542 | if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0) 543 | { 544 | LogError("Failure appending bytes to buffer."); 545 | http_data->recv_msg.recv_state = state_error; 546 | } 547 | else 548 | { 549 | http_data->recv_msg.recv_state = state_process_headers; 550 | } 551 | } 552 | } 553 | 554 | if (http_data->recv_msg.recv_state == state_process_headers) 555 | { 556 | size_t index = 0; 557 | const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff); 558 | size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); 559 | 560 | int headerComplete = process_header_line(stored_bytes, stored_len, &index, http_data->recv_msg.resp_header, &http_data->recv_msg.total_body_len, &http_data->recv_msg.chunked_reply); 561 | if (headerComplete == 0) 562 | { 563 | if (http_data->recv_msg.total_body_len == 0) 564 | { 565 | if (http_data->recv_msg.chunked_reply) 566 | { 567 | 568 | /* Codes_SRS_UHTTP_07_054: [ If the http header does not include a content length then it indicates a chunk response. ] */ 569 | http_data->recv_msg.recv_state = state_process_chunked_body; 570 | } 571 | else 572 | { 573 | // Content len is 0 so we are finished with the body 574 | http_data->recv_msg.recv_state = state_send_user_callback; 575 | } 576 | } 577 | else 578 | { 579 | http_data->recv_msg.recv_state = state_process_body; 580 | } 581 | } 582 | if (index > 0) 583 | { 584 | if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0) 585 | { 586 | LogError("Failure appending bytes to buffer."); 587 | http_data->recv_msg.recv_state = state_error; 588 | } 589 | } 590 | } 591 | 592 | if (http_data->recv_msg.recv_state == state_process_body) 593 | { 594 | if (http_data->recv_msg.total_body_len != 0) 595 | { 596 | size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff); 597 | 598 | if ((http_data->recv_msg.total_body_len == stored_len) || (http_data->recv_msg.total_body_len == (stored_len - HTTP_END_TOKEN_LEN))) 599 | { 600 | if (http_data->recv_msg.msg_body != NULL) 601 | { 602 | BUFFER_delete(http_data->recv_msg.msg_body); 603 | } 604 | if ((http_data->recv_msg.msg_body = BUFFER_clone(http_data->recv_msg.accrual_buff)) == NULL) 605 | { 606 | LogError("Failure cloning BUFFER."); 607 | http_data->recv_msg.recv_state = state_error; 608 | } 609 | else 610 | { 611 | http_data->recv_msg.recv_state = state_send_user_callback; 612 | } 613 | } 614 | else if (stored_len > http_data->recv_msg.total_body_len) 615 | { 616 | LogError("Failure bytes encountered is greater then body length."); 617 | http_data->recv_msg.recv_state = state_error; 618 | } 619 | } 620 | } 621 | 622 | if (http_data->recv_msg.recv_state == state_process_chunked_body) 623 | { 624 | const unsigned char* iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff); 625 | const unsigned char* initial_pos = iterator; 626 | const unsigned char* begin = iterator; 627 | const unsigned char* end = iterator; 628 | size_t accural_len = BUFFER_length(http_data->recv_msg.accrual_buff); 629 | 630 | /* Codes_SRS_UHTTP_07_059: [ on_bytes_received shall loop throught the stored data to find the /r/n separator. ] */ 631 | while (iterator < (initial_pos + accural_len)) 632 | { 633 | if (*iterator == '\r') 634 | { 635 | // Don't need anything 636 | end = iterator; 637 | iterator++; 638 | } 639 | else if (*iterator == '\n') 640 | { 641 | size_t data_length = 0; 642 | 643 | /* Codes_SRS_UHTTP_07_055: [ on_bytes_received shall convert the hexs length supplied in the response to the data length of the chunked data. ] */ 644 | size_t hex_len = end - begin; 645 | data_length = convert_char_to_hex(begin, hex_len); 646 | if (data_length == 0) 647 | { 648 | if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN) 649 | { 650 | http_data->recv_msg.recv_state = state_send_user_callback; 651 | } 652 | else 653 | { 654 | // Need to continue parsing 655 | http_data->recv_msg.recv_state = state_process_headers; 656 | } 657 | break; 658 | } 659 | else if ((data_length + HTTP_CRLF_LEN) < accural_len - (iterator - initial_pos)) 660 | { 661 | /* Codes_SRS_UHTTP_07_056: [ After the response chunk is parsed it shall be placed in a BUFFER_HANDLE. ] */ 662 | iterator += 1; 663 | if (BUFFER_append_build(http_data->recv_msg.msg_body, iterator, data_length) != 0) 664 | { 665 | /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the stop processing the request. ] */ 666 | LogError("Failure building buffer for chunked data."); 667 | http_data->recv_msg.recv_state = state_error; 668 | } 669 | else 670 | { 671 | /* Codes_SRS_UHTTP_07_060: [ if the data_length specified in the chunk is beyond the amount of data recieved, the parsing shall end and wait for more data. ] */ 672 | if (iterator + (data_length + HTTP_CRLF_LEN) > initial_pos + accural_len) 673 | { 674 | LogError("Invalid length specified."); 675 | http_data->recv_msg.recv_state = state_error; 676 | break; 677 | } 678 | else if (iterator + (data_length + HTTP_CRLF_LEN) == initial_pos + accural_len) 679 | { 680 | if (BUFFER_shrink(http_data->recv_msg.accrual_buff, accural_len, false) != 0) 681 | { 682 | LogError("Failure shrinking accural buffer."); 683 | http_data->recv_msg.recv_state = state_error; 684 | } 685 | break; 686 | } 687 | else 688 | { 689 | // Move the iterator beyond the data we read and the /r/n 690 | iterator += (data_length + HTTP_CRLF_LEN); 691 | } 692 | 693 | /* Codes_SRS_UHTTP_07_058: [ Once a chunk size value of 0 is encountered on_bytes_received shall call the on_request_callback with the http message ] */ 694 | if (*iterator == '0' && (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)) 695 | { 696 | if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN) 697 | { 698 | http_data->recv_msg.recv_state = state_send_user_callback; 699 | } 700 | else 701 | { 702 | // Need to continue parsing 703 | http_data->recv_msg.recv_state = state_process_headers; 704 | } 705 | break; 706 | } 707 | else 708 | { 709 | size_t shrink_len = iterator - initial_pos; 710 | if (shrink_len > 0) 711 | { 712 | if (BUFFER_shrink(http_data->recv_msg.accrual_buff, shrink_len, false) != 0) 713 | { 714 | LogError("Failure shrinking accrual buffer."); 715 | http_data->recv_msg.recv_state = state_error; 716 | } 717 | else 718 | { 719 | accural_len = BUFFER_length(http_data->recv_msg.accrual_buff); 720 | initial_pos = iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff); 721 | } 722 | } 723 | } 724 | } 725 | begin = end = iterator; 726 | } 727 | else 728 | { 729 | break; 730 | } 731 | } 732 | else 733 | { 734 | end = iterator; 735 | iterator++; 736 | } 737 | } 738 | } 739 | 740 | if (http_data->recv_msg.recv_state == state_send_user_callback || http_data->recv_msg.recv_state == state_error) 741 | { 742 | const unsigned char* reply_data = NULL; 743 | size_t reply_len = 0; 744 | HTTP_CALLBACK_REASON http_reason = HTTP_CALLBACK_REASON_OK; 745 | if (http_data->recv_msg.msg_body != NULL) 746 | { 747 | reply_data = BUFFER_u_char(http_data->recv_msg.msg_body); 748 | reply_len = BUFFER_length(http_data->recv_msg.msg_body); 749 | } 750 | if (http_data->recv_msg.recv_state == state_error) 751 | { 752 | http_reason = HTTP_CALLBACK_REASON_PARSING_ERROR; 753 | } 754 | if (http_data->trace_on) 755 | { 756 | LOG(AZ_LOG_TRACE, LOG_LINE, "\r\nHTTP Status: %d\r\n", http_data->recv_msg.status_code); 757 | 758 | // Loop through headers 759 | size_t count; 760 | if (HTTPHeaders_GetHeaderCount(http_data->recv_msg.resp_header, &count) == 0) 761 | { 762 | for (size_t index = 0; index < count; index++) 763 | { 764 | char* header; 765 | if (HTTPHeaders_GetHeader(http_data->recv_msg.resp_header, index, &header) == HTTP_HEADERS_OK) 766 | { 767 | LOG(AZ_LOG_TRACE, LOG_LINE, "%s", header); 768 | free(header); 769 | } 770 | } 771 | } 772 | if (http_data->trace_body && reply_len > 0) 773 | { 774 | LOG(AZ_LOG_TRACE, LOG_LINE, "\r\n%.*s\r\n", (int)reply_len, reply_data); 775 | } 776 | } 777 | http_data->recv_msg.on_request_callback(http_data->recv_msg.user_ctx, http_reason, reply_data, reply_len, http_data->recv_msg.status_code, http_data->recv_msg.resp_header); 778 | http_data->recv_msg.recv_state = state_parse_complete; 779 | } 780 | 781 | if (http_data->recv_msg.recv_state == state_parse_complete) 782 | { 783 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 784 | http_data->recv_msg.resp_header = NULL; 785 | BUFFER_delete(http_data->recv_msg.msg_body); 786 | http_data->recv_msg.msg_body = NULL; 787 | BUFFER_delete(http_data->recv_msg.accrual_buff); 788 | http_data->recv_msg.accrual_buff = NULL; 789 | } 790 | } 791 | } 792 | 793 | static void on_xio_close_complete(void* context) 794 | { 795 | if (context != NULL) 796 | { 797 | /* Codes_SRS_UHTTP_07_045: [ If on_close_callback is not NULL, on_close_callback shall be called once the underlying xio is closed. ] */ 798 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; 799 | if (http_data->on_close_callback) 800 | { 801 | http_data->on_close_callback(http_data->close_user_ctx); 802 | } 803 | http_data->recv_msg.recv_state = state_closed; 804 | http_data->connected = 0; 805 | } 806 | } 807 | 808 | static void on_xio_open_complete(void* context, IO_OPEN_RESULT open_result) 809 | { 810 | /* Codes_SRS_UHTTP_07_049: [ If not NULL uhttp_client_open shall call the on_connect callback with the callback_ctx, once the underlying xio's open is complete. ] */ 811 | if (context != NULL) 812 | { 813 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; 814 | if (open_result == IO_OPEN_OK) 815 | { 816 | /* Codes_SRS_UHTTP_07_042: [ If the underlying socket opens successfully the on_connect callback shall be call with HTTP_CALLBACK_REASON_OK... ] */ 817 | if (http_data->on_connect != NULL) 818 | { 819 | http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OK); 820 | } 821 | http_data->recv_msg.recv_state = state_open; 822 | http_data->connected = 1; 823 | } 824 | else 825 | { 826 | /* Codes_SRS_UHTTP_07_043: [ Otherwise on_connect callback shall be call with HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ 827 | if (http_data->on_connect != NULL) 828 | { 829 | http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OPEN_FAILED); 830 | } 831 | } 832 | } 833 | else 834 | { 835 | LogError("Context on_xio_open_complete is NULL"); 836 | } 837 | } 838 | 839 | static void on_io_error(void* context) 840 | { 841 | /* Codes_SRS_UHTTP_07_050: [ if context is NULL on_io_error shall do nothing. ] */ 842 | if (context != NULL) 843 | { 844 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context; 845 | /* Codes_SRS_UHTTP_07_051: [ if on_error callback is not NULL, on_io_error shall call on_error callback. ] */ 846 | if (http_data->on_error) 847 | { 848 | http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_ERROR); 849 | } 850 | http_data->connected = 0; 851 | } 852 | else 853 | { 854 | LogError("Context on_io_error is NULL"); 855 | } 856 | } 857 | 858 | static int construct_http_headers(HTTP_HEADERS_HANDLE http_header, size_t content_len, STRING_HANDLE buffData, bool chunk_data, const char* hostname, int port_num) 859 | { 860 | (void)chunk_data; 861 | int result = 0; 862 | size_t headerCnt = 0; 863 | if ( (http_header != NULL) && HTTPHeaders_GetHeaderCount(http_header, &headerCnt) != HTTP_HEADERS_OK) 864 | { 865 | LogError("Failed in HTTPHeaders_GetHeaderCount"); 866 | result = MU_FAILURE; 867 | } 868 | else 869 | { 870 | bool hostname_found = false; 871 | for (size_t index = 0; index < headerCnt && result == 0; index++) 872 | { 873 | char* header; 874 | if (HTTPHeaders_GetHeader(http_header, index, &header) != HTTP_HEADERS_OK) 875 | { 876 | result = MU_FAILURE; 877 | LogError("Failed in HTTPHeaders_GetHeader"); 878 | } 879 | else 880 | { 881 | size_t dataLen = safe_add_size_t(strlen(header), 2); 882 | size_t malloc_size = safe_add_size_t(dataLen, 1); 883 | 884 | char* sendData; 885 | if (malloc_size == SIZE_MAX || 886 | (sendData = malloc(malloc_size)) == NULL) 887 | { 888 | result = MU_FAILURE; 889 | LogError("Failed in allocating header data, size:%zu", malloc_size); 890 | } 891 | else 892 | { 893 | if (strcmp(header, HTTP_HOST) == 0) 894 | { 895 | hostname_found = true; 896 | } 897 | 898 | if (snprintf(sendData, malloc_size, "%s\r\n", header) <= 0) 899 | { 900 | result = MU_FAILURE; 901 | LogError("Failed in constructing header data"); 902 | } 903 | else 904 | { 905 | if (STRING_concat(buffData, sendData) != 0) 906 | { 907 | result = MU_FAILURE; 908 | LogError("Failed in building header data"); 909 | } 910 | } 911 | free(sendData); 912 | } 913 | free(header); 914 | } 915 | } 916 | if (!hostname_found) 917 | { 918 | // calculate the size of the host header 919 | size_t host_len = safe_add_size_t(strlen(HTTP_HOST), strlen(hostname)); 920 | host_len = safe_add_size_t(host_len, MAX_CONTENT_LENGTH); 921 | host_len = safe_add_size_t(host_len, 2); 922 | 923 | size_t malloc_size = safe_add_size_t(host_len, 1); 924 | char* host_header; 925 | if (malloc_size == SIZE_MAX || 926 | (host_header = malloc(malloc_size)) == NULL) 927 | { 928 | LogError("Failed allocating host header, size:%zu", malloc_size); 929 | result = MU_FAILURE; 930 | } 931 | else 932 | { 933 | if (snprintf(host_header, malloc_size, "%s: %s:%d\r\n", HTTP_HOST, hostname, port_num) <= 0) 934 | { 935 | LogError("Failed constructing host header"); 936 | result = MU_FAILURE; 937 | } 938 | else if (STRING_concat(buffData, host_header) != 0) 939 | { 940 | LogError("Failed adding the host header to the http item"); 941 | result = MU_FAILURE; 942 | } 943 | free(host_header); 944 | } 945 | } 946 | 947 | if (result == 0) 948 | { 949 | /* Codes_SRS_UHTTP_07_015: [uhttp_client_execute_request shall add the Content-Length to the request if the contentLength is > 0] */ 950 | size_t fmtLen = safe_add_size_t(strlen(HTTP_CONTENT_LEN), HTTP_CRLF_LEN); 951 | fmtLen = safe_add_size_t(fmtLen, 25); 952 | 953 | size_t malloc_size = safe_add_size_t(fmtLen, 1); 954 | char* content; 955 | if ((content = malloc(malloc_size)) == NULL) 956 | { 957 | LogError("Failed allocating chunk header, size:%zu", malloc_size); 958 | result = MU_FAILURE; 959 | } 960 | else 961 | { 962 | /* Codes_SRS_UHTTP_07_015: [on_bytes_received shall add the Content-Length http header item to the request.] */ 963 | if (sprintf(content, "%s: %u%s", HTTP_CONTENT_LEN, (unsigned int)content_len, HTTP_CRLF_VALUE) <= 0) 964 | { 965 | result = MU_FAILURE; 966 | LogError("Failed allocating content len header data"); 967 | } 968 | else 969 | { 970 | if (STRING_concat(buffData, content) != 0) 971 | { 972 | result = MU_FAILURE; 973 | LogError("Failed building content len header data"); 974 | } 975 | } 976 | free(content); 977 | } 978 | 979 | if (STRING_concat(buffData, "\r\n") != 0) 980 | { 981 | result = MU_FAILURE; 982 | LogError("Failed sending header finalization data"); 983 | } 984 | } 985 | } 986 | return result; 987 | } 988 | 989 | static STRING_HANDLE construct_http_data(HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, STRING_HANDLE http_line) 990 | { 991 | STRING_HANDLE result; 992 | 993 | const char* method = (request_type == HTTP_CLIENT_REQUEST_GET) ? "GET" 994 | : (request_type == HTTP_CLIENT_REQUEST_OPTIONS) ? "OPTIONS" 995 | : (request_type == HTTP_CLIENT_REQUEST_POST) ? "POST" 996 | : (request_type == HTTP_CLIENT_REQUEST_PUT) ? "PUT" 997 | : (request_type == HTTP_CLIENT_REQUEST_DELETE) ? "DELETE" 998 | : (request_type == HTTP_CLIENT_REQUEST_PATCH) ? "PATCH" 999 | : NULL; 1000 | /* Codes_SRS_UHTTP_07_014: [If the request_type is not a valid request http_client_execute_request shall return HTTP_CLIENT_ERROR] */ 1001 | if (method == NULL) 1002 | { 1003 | LogError("Invalid request method %s specified", method); 1004 | result = NULL; 1005 | } 1006 | else 1007 | { 1008 | size_t buffLen = safe_add_size_t(strlen(HTTP_REQUEST_LINE_FMT), strlen(method)); 1009 | buffLen = safe_add_size_t(buffLen, strlen(relative_path)); 1010 | 1011 | size_t malloc_size = safe_add_size_t(buffLen, 1); 1012 | char* request; 1013 | if (malloc_size == SIZE_MAX || 1014 | (request = malloc(malloc_size)) == NULL) 1015 | { 1016 | result = NULL; 1017 | LogError("Failure allocating Request data, size:%zu", malloc_size); 1018 | } 1019 | else 1020 | { 1021 | if (snprintf(request, malloc_size, HTTP_REQUEST_LINE_FMT, method, relative_path) <= 0) 1022 | { 1023 | result = NULL; 1024 | LogError("Failure writing request buffer"); 1025 | } 1026 | else 1027 | { 1028 | result = STRING_construct(request); 1029 | if (result == NULL) 1030 | { 1031 | LogError("Failure creating buffer object"); 1032 | } 1033 | else if (STRING_concat_with_STRING(result, http_line) != 0) 1034 | { 1035 | STRING_delete(result); 1036 | result = NULL; 1037 | LogError("Failure writing request buffers"); 1038 | } 1039 | } 1040 | free(request); 1041 | } 1042 | } 1043 | return result; 1044 | } 1045 | 1046 | static int send_http_data(HTTP_CLIENT_HANDLE_DATA* http_data, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, 1047 | STRING_HANDLE http_line) 1048 | { 1049 | int result; 1050 | STRING_HANDLE transmit_data = construct_http_data(request_type, relative_path, http_line); 1051 | if (transmit_data == NULL) 1052 | { 1053 | LogError("Failure constructing http data"); 1054 | result = MU_FAILURE; 1055 | } 1056 | else 1057 | { 1058 | /* Tests_SRS_UHTTP_07_016: [http_client_execute_request shall transmit the http headers data through a call to xio_send;]*/ 1059 | if (write_text_line(http_data, STRING_c_str(transmit_data) ) != 0) 1060 | { 1061 | result = MU_FAILURE; 1062 | LogError("Failure writing request buffer"); 1063 | } 1064 | else 1065 | { 1066 | result = 0; 1067 | } 1068 | STRING_delete(transmit_data); 1069 | } 1070 | return result; 1071 | } 1072 | 1073 | HTTP_CLIENT_HANDLE uhttp_client_create(const IO_INTERFACE_DESCRIPTION* io_interface_desc, const void* xio_param, ON_HTTP_ERROR_CALLBACK on_http_error, void* callback_ctx) 1074 | { 1075 | HTTP_CLIENT_HANDLE_DATA* result; 1076 | /* Codes_SRS_UHTTP_07_002: [If io_interface_desc is NULL, uhttp_client_create shall return NULL.] */ 1077 | if (io_interface_desc == NULL) 1078 | { 1079 | LogError("Invalid Parameter io_interface_desc is NULL"); 1080 | result = NULL; 1081 | } 1082 | else 1083 | { 1084 | result = malloc(sizeof(HTTP_CLIENT_HANDLE_DATA)); 1085 | if (result == NULL) 1086 | { 1087 | /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */ 1088 | LogError("Failure allocating http_client_handle"); 1089 | } 1090 | else 1091 | { 1092 | memset(result, 0, sizeof(HTTP_CLIENT_HANDLE_DATA) ); 1093 | if ((result->data_list = singlylinkedlist_create() ) == NULL) 1094 | { 1095 | /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */ 1096 | LogError("Failure allocating data list"); 1097 | free(result); 1098 | result = NULL; 1099 | } 1100 | else if ((result->xio_handle = xio_create(io_interface_desc, xio_param) ) == NULL) 1101 | { 1102 | /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ 1103 | LogError("xio create failed"); 1104 | singlylinkedlist_destroy(result->data_list); 1105 | free(result); 1106 | result = NULL; 1107 | } 1108 | else 1109 | { 1110 | /* Codes_SRS_UHTTP_07_001: [uhttp_client_create shall return an initialize the http client handle.] */ 1111 | result->on_error = on_http_error; 1112 | result->error_user_ctx = callback_ctx; 1113 | result->recv_msg.recv_state = state_initial; 1114 | result->chunk_request = false; 1115 | result->trace_on = false; 1116 | } 1117 | } 1118 | } 1119 | return (HTTP_CLIENT_HANDLE)result; 1120 | } 1121 | 1122 | void uhttp_client_destroy(HTTP_CLIENT_HANDLE handle) 1123 | { 1124 | /* Codes_SRS_UHTTP_07_004: [ If handle is NULL then uhttp_client_destroy shall do nothing ] */ 1125 | if (handle != NULL) 1126 | { 1127 | /* Codes_SRS_UHTTP_07_005: [uhttp_client_destroy shall free any resource that is allocated in this translation unit] */ 1128 | if(handle->host_name != NULL) 1129 | { 1130 | free(handle->host_name); 1131 | handle->host_name = NULL; 1132 | } 1133 | singlylinkedlist_destroy(handle->data_list); 1134 | xio_destroy(handle->xio_handle); 1135 | free(handle->certificate); 1136 | free(handle->x509_pk); 1137 | free(handle->x509_cert); 1138 | free(handle); 1139 | } 1140 | } 1141 | 1142 | HTTP_CLIENT_RESULT uhttp_client_open(HTTP_CLIENT_HANDLE handle, const char* host, int port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect, void* callback_ctx) 1143 | { 1144 | HTTP_CLIENT_RESULT result; 1145 | if (handle == NULL || host == NULL) 1146 | { 1147 | /* Codes_SRS_UHTTP_07_006: [If handle, io_interface_desc or host is NULL then `uhttp_client_open` shall return HTTP_CLIENT_INVALID_ARG] */ 1148 | LogError("Invalid handle value"); 1149 | result = HTTP_CLIENT_INVALID_ARG; 1150 | } 1151 | else 1152 | { 1153 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; 1154 | 1155 | if ((http_data->recv_msg.recv_state != state_initial) && 1156 | (http_data->recv_msg.recv_state != state_error) && 1157 | (http_data->recv_msg.recv_state != state_closed)) 1158 | { 1159 | LogError("Unable to open previously open client."); 1160 | result = HTTP_CLIENT_INVALID_STATE; 1161 | } 1162 | else 1163 | { 1164 | if (http_data->host_name != NULL) 1165 | { 1166 | free(http_data->host_name); 1167 | handle->host_name = NULL; 1168 | } 1169 | 1170 | if (mallocAndStrcpy_s(&http_data->host_name, host) != 0) 1171 | { 1172 | LogError("copying hostname has failed"); 1173 | result = HTTP_CLIENT_ERROR; 1174 | } 1175 | /* Codes_SRS_UHTTP_07_007: [http_client_connect shall attempt to open the xio_handle. ] */ 1176 | else 1177 | { 1178 | result = HTTP_CLIENT_OK; 1179 | http_data->recv_msg.recv_state = state_opening; 1180 | http_data->on_connect = on_connect; 1181 | http_data->connect_user_ctx = callback_ctx; 1182 | http_data->port_num = port_num; 1183 | 1184 | if ((http_data->x509_cert != NULL) && (http_data->x509_pk != NULL)) 1185 | { 1186 | if ((xio_setoption(http_data->xio_handle, SU_OPTION_X509_CERT, http_data->x509_cert) != 0) || 1187 | (xio_setoption(http_data->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_data->x509_pk) != 0)) 1188 | { 1189 | LogError("Failed setting x509 certificate"); 1190 | result = HTTP_CLIENT_ERROR; 1191 | free(http_data->host_name); 1192 | http_data->host_name = NULL; 1193 | http_data->on_connect = NULL; 1194 | http_data->connect_user_ctx = NULL; 1195 | http_data->port_num = 0; 1196 | } 1197 | } 1198 | 1199 | if ((result == HTTP_CLIENT_OK) && (http_data->certificate != NULL)) 1200 | { 1201 | if (xio_setoption(http_data->xio_handle, OPTION_TRUSTED_CERT, http_data->certificate) != 0) 1202 | { 1203 | LogError("Failed setting Trusted certificate"); 1204 | result = HTTP_CLIENT_ERROR; 1205 | free(http_data->host_name); 1206 | http_data->host_name = NULL; 1207 | http_data->on_connect = NULL; 1208 | http_data->connect_user_ctx = NULL; 1209 | http_data->port_num = 0; 1210 | } 1211 | } 1212 | 1213 | if (result == HTTP_CLIENT_OK) 1214 | { 1215 | if (xio_open(http_data->xio_handle, on_xio_open_complete, http_data, on_bytes_received, http_data, on_io_error, http_data) != 0) 1216 | { 1217 | /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */ 1218 | LogError("opening xio failed"); 1219 | free(http_data->host_name); 1220 | http_data->host_name = NULL; 1221 | http_data->on_connect = NULL; 1222 | http_data->connect_user_ctx = NULL; 1223 | http_data->port_num = 0; 1224 | http_data->recv_msg.recv_state = state_error; 1225 | 1226 | result = HTTP_CLIENT_OPEN_FAILED; 1227 | } 1228 | else 1229 | { 1230 | /* Codes_SRS_UHTTP_07_008: [If http_client_connect succeeds then it shall return HTTP_CLIENT_OK] */ 1231 | result = HTTP_CLIENT_OK; 1232 | } 1233 | } 1234 | } 1235 | } 1236 | } 1237 | return result; 1238 | } 1239 | 1240 | void uhttp_client_close(HTTP_CLIENT_HANDLE handle, ON_HTTP_CLOSED_CALLBACK on_close_callback, void* callback_ctx) 1241 | { 1242 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; 1243 | /* Codes_SRS_UHTTP_07_009: [If handle is NULL then http_client_close shall do nothing] */ 1244 | /* Codes_SRS_UHTTP_07_049: [ If the state has been previously set to state_closed, uhttp_client_close shall do nothing. ] */ 1245 | if (http_data != NULL && http_data->recv_msg.recv_state != state_closed && http_data->recv_msg.recv_state != state_closing) 1246 | { 1247 | http_data->on_close_callback = on_close_callback; 1248 | http_data->close_user_ctx = callback_ctx; 1249 | /* Codes_SRS_UHTTP_07_010: [If the xio_handle is NOT NULL http_client_close shall call xio_close to close the handle] */ 1250 | (void)xio_close(http_data->xio_handle, on_xio_close_complete, http_data); 1251 | 1252 | LIST_ITEM_HANDLE pending_list_item; 1253 | while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL) 1254 | { 1255 | HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item); 1256 | if (send_data != NULL) 1257 | { 1258 | STRING_delete(send_data->relative_path); 1259 | BUFFER_delete(send_data->content); 1260 | STRING_delete(send_data->header_line); 1261 | free(send_data); 1262 | } 1263 | singlylinkedlist_remove(http_data->data_list, pending_list_item); 1264 | } 1265 | 1266 | http_data->recv_msg.status_code = 0; 1267 | http_data->recv_msg.recv_state = state_closed; 1268 | http_data->recv_msg.total_body_len = 0; 1269 | free(http_data->host_name); 1270 | http_data->host_name = NULL; 1271 | 1272 | /* Codes_SRS_UHTTP_07_011: [http_client_close shall free any HTTPHeader object that has not been freed] */ 1273 | if (http_data->recv_msg.resp_header != NULL) 1274 | { 1275 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1276 | http_data->recv_msg.resp_header = NULL; 1277 | } 1278 | if (http_data->recv_msg.msg_body != NULL) 1279 | { 1280 | BUFFER_delete(http_data->recv_msg.msg_body); 1281 | http_data->recv_msg.msg_body = NULL; 1282 | } 1283 | } 1284 | } 1285 | 1286 | HTTP_CLIENT_RESULT uhttp_client_execute_request(HTTP_CLIENT_HANDLE handle, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, 1287 | HTTP_HEADERS_HANDLE http_header_handle, const unsigned char* content, size_t content_len, ON_HTTP_REQUEST_CALLBACK on_request_callback, void* callback_ctx) 1288 | { 1289 | HTTP_CLIENT_RESULT result; 1290 | LIST_ITEM_HANDLE list_item; 1291 | 1292 | /* Codes_SRS_UHTTP_07_012: [If handle, relativePath, or httpHeadersHandle is NULL then http_client_execute_request shall return HTTP_CLIENT_INVALID_ARG] */ 1293 | if (handle == NULL || on_request_callback == NULL || 1294 | (content != NULL && content_len == 0) || (content == NULL && content_len != 0) ) 1295 | { 1296 | result = HTTP_CLIENT_INVALID_ARG; 1297 | LogError("Invalid parameter sent to execute_request"); 1298 | } 1299 | else 1300 | { 1301 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; 1302 | 1303 | http_data->recv_msg.on_request_callback = on_request_callback; 1304 | http_data->recv_msg.user_ctx = callback_ctx; 1305 | if (setup_init_recv_msg(&http_data->recv_msg) != 0) 1306 | { 1307 | /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */ 1308 | LogError("Failure allocating http http_data items"); 1309 | result = HTTP_CLIENT_ERROR; 1310 | } 1311 | else 1312 | { 1313 | HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)malloc(sizeof(HTTP_SEND_DATA)); 1314 | if (send_data == NULL) 1315 | { 1316 | LogError("Failure allocating http data items"); 1317 | BUFFER_delete(http_data->recv_msg.msg_body); 1318 | http_data->recv_msg.msg_body = NULL; 1319 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1320 | http_data->recv_msg.resp_header = NULL; 1321 | result = HTTP_CLIENT_ERROR; 1322 | } 1323 | else 1324 | { 1325 | memset(send_data, 0, sizeof(HTTP_SEND_DATA)); 1326 | /* Codes_SRS_UHTTP_07_041: [HTTP_CLIENT_REQUEST_TYPE shall support all request types specified under section 9.1.2 in the spec.] */ 1327 | send_data->request_type = request_type; 1328 | if ( (content_len > 0) && (send_data->content = BUFFER_create(content, content_len)) == NULL) 1329 | { 1330 | LogError("Failure allocating content buffer"); 1331 | result = HTTP_CLIENT_ERROR; 1332 | BUFFER_delete(http_data->recv_msg.msg_body); 1333 | http_data->recv_msg.msg_body = NULL; 1334 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1335 | http_data->recv_msg.resp_header = NULL; 1336 | free(send_data); 1337 | } 1338 | else if ((send_data->header_line = STRING_new()) == NULL) 1339 | { 1340 | LogError("Failure allocating content buffer"); 1341 | result = HTTP_CLIENT_ERROR; 1342 | BUFFER_delete(send_data->content); 1343 | BUFFER_delete(http_data->recv_msg.msg_body); 1344 | http_data->recv_msg.msg_body = NULL; 1345 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1346 | http_data->recv_msg.resp_header = NULL; 1347 | free(send_data); 1348 | } 1349 | else if (construct_http_headers(http_header_handle, content_len, send_data->header_line, false, http_data->host_name, http_data->port_num)) 1350 | { 1351 | LogError("Failure allocating content buffer"); 1352 | result = HTTP_CLIENT_ERROR; 1353 | BUFFER_delete(send_data->content); 1354 | STRING_delete(send_data->header_line); 1355 | BUFFER_delete(http_data->recv_msg.msg_body); 1356 | http_data->recv_msg.msg_body = NULL; 1357 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1358 | http_data->recv_msg.resp_header = NULL; 1359 | free(send_data); 1360 | } 1361 | else if ((list_item = singlylinkedlist_add(http_data->data_list, send_data)) == NULL) 1362 | { 1363 | STRING_delete(send_data->header_line); 1364 | BUFFER_delete(send_data->content); 1365 | LogError("Failure adding send data to list"); 1366 | result = HTTP_CLIENT_ERROR; 1367 | BUFFER_delete(http_data->recv_msg.msg_body); 1368 | http_data->recv_msg.msg_body = NULL; 1369 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1370 | http_data->recv_msg.resp_header = NULL; 1371 | free(send_data); 1372 | } 1373 | else 1374 | { 1375 | if (relative_path != NULL) 1376 | { 1377 | if ((send_data->relative_path = STRING_construct(relative_path)) == NULL) 1378 | { 1379 | (void)singlylinkedlist_remove(http_data->data_list, list_item); 1380 | STRING_delete(send_data->header_line); 1381 | BUFFER_delete(send_data->content); 1382 | LogError("Failure allocating relative path buffer"); 1383 | BUFFER_delete(http_data->recv_msg.msg_body); 1384 | http_data->recv_msg.msg_body = NULL; 1385 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1386 | http_data->recv_msg.resp_header = NULL; 1387 | free(send_data); 1388 | result = HTTP_CLIENT_ERROR; 1389 | } 1390 | else 1391 | { 1392 | /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */ 1393 | result = HTTP_CLIENT_OK; 1394 | } 1395 | } 1396 | else 1397 | { 1398 | if ((send_data->relative_path = STRING_construct("/")) == NULL) 1399 | { 1400 | (void)singlylinkedlist_remove(http_data->data_list, list_item); 1401 | STRING_delete(send_data->header_line); 1402 | BUFFER_delete(send_data->content); 1403 | LogError("Failure allocating relative path buffer"); 1404 | BUFFER_delete(http_data->recv_msg.msg_body); 1405 | http_data->recv_msg.msg_body = NULL; 1406 | HTTPHeaders_Free(http_data->recv_msg.resp_header); 1407 | http_data->recv_msg.resp_header = NULL; 1408 | free(send_data); 1409 | result = HTTP_CLIENT_ERROR; 1410 | } 1411 | else 1412 | { 1413 | /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */ 1414 | result = HTTP_CLIENT_OK; 1415 | } 1416 | } 1417 | } 1418 | } 1419 | } 1420 | } 1421 | return result; 1422 | } 1423 | 1424 | void uhttp_client_dowork(HTTP_CLIENT_HANDLE handle) 1425 | { 1426 | if (handle != NULL) 1427 | { 1428 | /* Codes_SRS_UHTTP_07_037: [http_client_dowork shall call the underlying xio_dowork function. ] */ 1429 | HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle; 1430 | xio_dowork(http_data->xio_handle); 1431 | 1432 | // Wait till I'm connected 1433 | if (handle->connected == 1) 1434 | { 1435 | LIST_ITEM_HANDLE pending_list_item; 1436 | /* Codes_SRS_UHTTP_07_016: [http_client_dowork shall iterate through the queued Data using the xio interface to send the http request in the following ways...] */ 1437 | while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL) 1438 | { 1439 | HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item); 1440 | if (send_data != NULL) 1441 | { 1442 | size_t content_len = BUFFER_length(send_data->content); 1443 | /* Codes_SRS_UHTTP_07_052: [uhttp_client_dowork shall call xio_send to transmits the header information... ] */ 1444 | if (send_http_data(http_data, send_data->request_type, STRING_c_str(send_data->relative_path), send_data->header_line) != 0) 1445 | { 1446 | LogError("Failure writing content buffer"); 1447 | if (http_data->on_error) 1448 | { 1449 | http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED); 1450 | } 1451 | } 1452 | else if (content_len > 0) 1453 | { 1454 | /* Codes_SRS_UHTTP_07_053: [ Then uhttp_client_dowork shall use xio_send to transmit the content of the http request. ] */ 1455 | if (write_data_line(http_data, BUFFER_u_char(send_data->content), content_len) != 0) 1456 | { 1457 | LogError("Failure writing content buffer"); 1458 | if (http_data->on_error) 1459 | { 1460 | http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED); 1461 | } 1462 | } 1463 | } 1464 | 1465 | /* Codes_SRS_UHTTP_07_046: [ http_client_dowork shall free resouces queued to send to the http endpoint. ] */ 1466 | STRING_delete(send_data->relative_path); 1467 | BUFFER_delete(send_data->content); 1468 | STRING_delete(send_data->header_line); 1469 | free(send_data); 1470 | } 1471 | (void)singlylinkedlist_remove(http_data->data_list, pending_list_item); 1472 | } 1473 | } 1474 | } 1475 | } 1476 | 1477 | HTTP_CLIENT_RESULT uhttp_client_set_trace(HTTP_CLIENT_HANDLE handle, bool trace_on, bool trace_data) 1478 | { 1479 | HTTP_CLIENT_RESULT result; 1480 | if (handle == NULL) 1481 | { 1482 | /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ 1483 | result = HTTP_CLIENT_INVALID_ARG; 1484 | LogError("invalid parameter (NULL) passed to http_client_set_trace"); 1485 | } 1486 | else 1487 | { 1488 | /* Codes_SRS_UHTTP_07_039: [http_client_set_trace shall set the HTTP tracing to the trace_on variable.] */ 1489 | handle->trace_on = trace_on; 1490 | handle->trace_body = trace_data; 1491 | /* Codes_SRS_UHTTP_07_040: [if http_client_set_trace finishes successfully then it shall return HTTP_CLIENT_OK.] */ 1492 | result = HTTP_CLIENT_OK; 1493 | } 1494 | return result; 1495 | } 1496 | 1497 | HTTP_CLIENT_RESULT uhttp_client_set_X509_cert(HTTP_CLIENT_HANDLE handle, bool ecc_type, const char* certificate, const char* private_key) 1498 | { 1499 | HTTP_CLIENT_RESULT result; 1500 | if (handle == NULL || certificate == NULL || private_key == NULL) 1501 | { 1502 | /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ 1503 | result = HTTP_CLIENT_INVALID_ARG; 1504 | LogError("invalid parameter handle: %p certificate: %p private_key: %p", handle, certificate, private_key); 1505 | } 1506 | else if (handle->recv_msg.recv_state != state_initial) 1507 | { 1508 | result = HTTP_CLIENT_INVALID_STATE; 1509 | LogError("You must set the X509 certificates before opening the connection"); 1510 | } 1511 | else 1512 | { 1513 | handle->cert_type_ecc = ecc_type; 1514 | if (mallocAndStrcpy_s(&handle->x509_cert, certificate) != 0) 1515 | { 1516 | result = HTTP_CLIENT_ERROR; 1517 | LogError("failure allocating certificate"); 1518 | } 1519 | else if (mallocAndStrcpy_s(&handle->x509_pk, private_key) != 0) 1520 | { 1521 | free(handle->x509_cert); 1522 | handle->x509_cert = NULL; 1523 | result = HTTP_CLIENT_ERROR; 1524 | LogError("failure allocating private key"); 1525 | } 1526 | else 1527 | { 1528 | result = HTTP_CLIENT_OK; 1529 | } 1530 | } 1531 | return result; 1532 | } 1533 | 1534 | HTTP_CLIENT_RESULT uhttp_client_set_trusted_cert(HTTP_CLIENT_HANDLE handle, const char* certificate) 1535 | { 1536 | HTTP_CLIENT_RESULT result; 1537 | if (handle == NULL || certificate == NULL) 1538 | { 1539 | /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ 1540 | result = HTTP_CLIENT_INVALID_ARG; 1541 | LogError("invalid parameter handle: %p certificate: %p", handle, certificate); 1542 | } 1543 | else if (handle->recv_msg.recv_state != state_initial) 1544 | { 1545 | result = HTTP_CLIENT_INVALID_STATE; 1546 | LogError("You must set the certificates before opening the connection"); 1547 | } 1548 | else 1549 | { 1550 | if (mallocAndStrcpy_s(&handle->certificate, certificate) != 0) 1551 | { 1552 | result = HTTP_CLIENT_ERROR; 1553 | LogError("failure allocating certificate"); 1554 | } 1555 | else 1556 | { 1557 | result = HTTP_CLIENT_OK; 1558 | } 1559 | } 1560 | return result; 1561 | } 1562 | 1563 | const char* uhttp_client_get_trusted_cert(HTTP_CLIENT_HANDLE handle) 1564 | { 1565 | const char* result; 1566 | if (handle == NULL) 1567 | { 1568 | result = NULL; 1569 | LogError("invalid parameter NULL handle"); 1570 | } 1571 | else 1572 | { 1573 | result = handle->certificate; 1574 | } 1575 | return result; 1576 | } 1577 | 1578 | HTTP_CLIENT_RESULT uhttp_client_set_option(HTTP_CLIENT_HANDLE handle, const char* optionName, const void* value) 1579 | { 1580 | HTTP_CLIENT_RESULT result; 1581 | if (handle == NULL) 1582 | { 1583 | /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */ 1584 | result = HTTP_CLIENT_INVALID_ARG; 1585 | LogError("invalid parameter handle: %p", handle); 1586 | } 1587 | else 1588 | { 1589 | int setoption_result = xio_setoption(handle->xio_handle, optionName, value); 1590 | if (setoption_result != 0) 1591 | { 1592 | LogError("xio_setoption fails, returns %d", setoption_result); 1593 | result = HTTP_CLIENT_ERROR; 1594 | } 1595 | else 1596 | { 1597 | result = HTTP_CLIENT_OK; 1598 | } 1599 | 1600 | } 1601 | 1602 | return result; 1603 | } 1604 | 1605 | XIO_HANDLE uhttp_client_get_underlying_xio(HTTP_CLIENT_HANDLE handle) 1606 | { 1607 | XIO_HANDLE result; 1608 | if (handle == NULL) 1609 | { 1610 | LogError("invalid parameter handle: %p", handle); 1611 | result = NULL; 1612 | } 1613 | else 1614 | { 1615 | result = handle->xio_handle; 1616 | } 1617 | return result; 1618 | } 1619 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | cmake_minimum_required(VERSION 3.5) 5 | 6 | usePermissiveRulesForSamplesAndTests() 7 | 8 | add_unittest_directory(uhttp_ut) -------------------------------------------------------------------------------- /tests/uhttp_ut/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Copyright (c) Microsoft. All rights reserved. 2 | #Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | cmake_minimum_required(VERSION 3.5) 5 | 6 | set(theseTestsName uhttp_ut) 7 | 8 | if(${use_openssl}) 9 | add_definitions(-DUSE_OPENSSL) 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_OPENSSL") 11 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_OPENSSL") 12 | endif() 13 | 14 | set(${theseTestsName}_test_files 15 | ${theseTestsName}.c 16 | ) 17 | 18 | include_directories(${SHARED_UTIL_REAL_TEST_FOLDER}) 19 | 20 | set(${theseTestsName}_c_files 21 | ../../src/uhttp.c 22 | ${SHARED_UTIL_REAL_TEST_FOLDER}/real_buffer.c 23 | ) 24 | 25 | set(${theseTestsName}_h_files 26 | ) 27 | 28 | build_c_test_artifacts(${theseTestsName} ON "tests/uhttp_tests") 29 | 30 | compile_c_test_artifacts_as(${theseTestsName} C99) 31 | -------------------------------------------------------------------------------- /tests/uhttp_ut/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include "testrunnerswitcher.h" 5 | 6 | int main(void) 7 | { 8 | size_t failedTestCount = 0; 9 | RUN_TEST_SUITE(uhttp_ut, failedTestCount); 10 | return (int)failedTestCount; 11 | } 12 | --------------------------------------------------------------------------------