├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── cmake └── winhttppalConfig.cmake.in ├── include └── winhttppal.h ├── src ├── winhttppal.cpp └── winhttppal_imp.h └── winhttppaltargets.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(libwinhttppal VERSION 1.0.0 LANGUAGES CXX) 3 | 4 | #Make sure that custom modules like FindRapidJSON are found 5 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake) 6 | 7 | ############################################## 8 | # Declare dependencies 9 | find_package(CURL 7.57 REQUIRED MODULE) 10 | 11 | ############################################## 12 | # Create target and set properties 13 | 14 | add_library(winhttppal 15 | src/winhttppal.cpp 16 | ) 17 | 18 | #Add an alias so that library can be used inside the build tree, e.g. when testing 19 | add_library(winhttppal::winhttppal ALIAS winhttppal) 20 | 21 | #Set target properties 22 | target_include_directories(winhttppal 23 | PUBLIC 24 | $ 25 | $ 26 | PRIVATE 27 | ${CMAKE_CURRENT_SOURCE_DIR}/src 28 | ) 29 | 30 | target_compile_features(winhttppal PRIVATE cxx_auto_type) 31 | target_compile_options(winhttppal PRIVATE $<$:-Wall -fPIC>) 32 | 33 | target_link_libraries(winhttppal 34 | PUBLIC 35 | ${CURL_LIBRARIES} 36 | ) 37 | 38 | ############################################## 39 | # Installation instructions 40 | 41 | include(GNUInstallDirs) 42 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/winhttppal) 43 | 44 | install(TARGETS winhttppal 45 | EXPORT winhttppal-targets 46 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 47 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 48 | ) 49 | 50 | #This is required so that the exported target has the name winhttppal and not winhttppal 51 | set_target_properties(winhttppal PROPERTIES EXPORT_NAME winhttppal) 52 | 53 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 54 | 55 | #Export the targets to a script 56 | install(EXPORT winhttppal-targets 57 | FILE 58 | winhttppalTargets.cmake 59 | NAMESPACE 60 | winhttppal:: 61 | DESTINATION 62 | ${INSTALL_CONFIGDIR} 63 | ) 64 | 65 | #Create a ConfigVersion.cmake file 66 | include(CMakePackageConfigHelpers) 67 | write_basic_package_version_file( 68 | ${CMAKE_CURRENT_BINARY_DIR}/winhttppalConfigVersion.cmake 69 | VERSION ${PROJECT_VERSION} 70 | COMPATIBILITY AnyNewerVersion 71 | ) 72 | 73 | configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/winhttppalConfig.cmake.in 74 | ${CMAKE_CURRENT_BINARY_DIR}/winhttppalConfig.cmake 75 | INSTALL_DESTINATION ${INSTALL_CONFIGDIR} 76 | ) 77 | 78 | #Install the config, configversion and custom find modules 79 | install(FILES 80 | ${CMAKE_CURRENT_BINARY_DIR}/winhttppalConfig.cmake 81 | ${CMAKE_CURRENT_BINARY_DIR}/winhttppalConfigVersion.cmake 82 | DESTINATION ${INSTALL_CONFIGDIR} 83 | ) 84 | 85 | ############################################## 86 | ## Exporting from the build tree 87 | export(EXPORT winhttppal-targets FILE ${CMAKE_CURRENT_BINARY_DIR}/winhttppalTargets.cmake NAMESPACE winhttppal::) 88 | 89 | #Register package in user's package registry 90 | #export(PACKAGE winhttppal) 91 | 92 | #add_subdirectory(test) -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /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 | 2 | # Contributing 3 | 4 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 5 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 6 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 7 | 8 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 9 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 10 | provided by the bot. You will only need to do this once across all repos using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 14 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), 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://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 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://aka.ms/opensource/security/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://aka.ms/opensource/security/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://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /cmake/winhttppalConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(winhttppal_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | include(CMakeFindDependencyMacro) 3 | 4 | list(APPEND CMAKE_MODULE_PATH ${winhttppal_CMAKE_DIR}) 5 | 6 | # NOTE Had to use find_package because find_dependency does not support COMPONENTS or MODULE until 3.8.0 7 | 8 | find_package(CURL 1.57 REQUIRED MODULE) 9 | list(REMOVE_AT CMAKE_MODULE_PATH -1) 10 | 11 | if(NOT TARGET winhttppal::winhttppal) 12 | include("${winhttppal_CMAKE_DIR}/winhttppalTargets.cmake") 13 | endif() 14 | 15 | set(winhttppal_LIBRARIES winhttppal::winhttppal) 16 | -------------------------------------------------------------------------------- /include/winhttppal.h: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (C) Microsoft. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 | * 5 | * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 | * 7 | * HTTP Library: Client-side APIs. 8 | * 9 | * For the latest on this and related APIs, please see: https://github.com/microsoft/WinHttpPAL 10 | * 11 | * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 12 | ****/ 13 | typedef void VOID; 14 | typedef void* LPVOID; 15 | typedef unsigned long DWORD; 16 | typedef const void* LPCVOID; 17 | typedef long LONG; 18 | typedef unsigned char BYTE; 19 | #define __int3264 long int 20 | typedef unsigned __int3264 ULONG_PTR; 21 | typedef ULONG_PTR DWORD_PTR; 22 | typedef DWORD* PDWORD; 23 | typedef DWORD* LPDWORD; 24 | typedef char* LPSTR; 25 | typedef const char* LPCSTR; 26 | 27 | #ifdef UNICODE 28 | typedef wchar_t TCHAR; 29 | typedef const wchar_t* LPCTSTR; 30 | typedef wchar_t* LPTSTR; 31 | typedef wchar_t* PTSTR; 32 | typedef const wchar_t* LPCTSTR; 33 | typedef const wchar_t* PCTSTR; 34 | #else 35 | typedef char TCHAR; 36 | typedef const char* LPCTSTR; 37 | typedef char* LPTSTR; 38 | typedef char* PTSTR; 39 | typedef const char* LPCTSTR; 40 | typedef const char* PCTSTR; 41 | #endif 42 | 43 | typedef LPVOID HINTERNET; 44 | typedef bool BOOL; 45 | 46 | typedef VOID (* WINHTTP_STATUS_CALLBACK)( 47 | HINTERNET hInternet, 48 | DWORD_PTR dwContext, 49 | DWORD dwInternetStatus, 50 | LPVOID lpvStatusInformation, 51 | DWORD dwStatusInformationLength 52 | ); 53 | 54 | enum 55 | { 56 | API_RECEIVE_RESPONSE = 1, 57 | API_QUERY_DATA_AVAILABLE, 58 | API_READ_DATA, 59 | API_WRITE_DATA, 60 | API_SEND_REQUEST, 61 | }; 62 | 63 | typedef struct 64 | { 65 | DWORD_PTR dwResult; 66 | DWORD dwError; 67 | } 68 | WINHTTP_ASYNC_RESULT, * LPWINHTTP_ASYNC_RESULT; 69 | 70 | typedef struct 71 | { 72 | DWORD dwMajorVersion; 73 | DWORD dwMinorVersion; 74 | } 75 | HTTP_VERSION_INFO; 76 | 77 | #define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 78 | 79 | enum 80 | { 81 | WINHTTP_QUERY_RAW_HEADERS, 82 | WINHTTP_QUERY_STATUS_CODE, 83 | WINHTTP_QUERY_VERSION, 84 | WINHTTP_QUERY_RAW_HEADERS_CRLF, 85 | WINHTTP_QUERY_STATUS_TEXT, 86 | WINHTTP_QUERY_FLAG_NUMBER = 0x80000000, 87 | }; 88 | 89 | enum 90 | { 91 | WINHTTP_AUTH_SCHEME_NEGOTIATE = 1, 92 | WINHTTP_AUTH_SCHEME_NTLM = 2, 93 | WINHTTP_AUTH_SCHEME_PASSPORT = 4, 94 | WINHTTP_AUTH_SCHEME_DIGEST = 8, 95 | WINHTTP_AUTH_SCHEME_BASIC = 0x10, 96 | WINHTTP_AUTH_TARGET_PROXY = 0x20, 97 | WINHTTP_AUTH_TARGET_SERVER = 0x40, 98 | }; 99 | 100 | #define WINHTTP_INVALID_STATUS_CALLBACK (WINHTTP_STATUS_CALLBACK)-1 101 | 102 | #define WINHTTP_NO_REFERER NULL 103 | #define WINHTTP_DEFAULT_ACCEPT_TYPES NULL 104 | #define WINHTTP_NO_ADDITIONAL_HEADERS NULL 105 | #define WINHTTP_NO_REQUEST_DATA NULL 106 | #define WINHTTP_NO_PROXY_NAME NULL 107 | #define WINHTTP_NO_PROXY_BYPASS NULL 108 | #define WINHTTP_NO_HEADER_INDEX NULL 109 | #define WINHTTP_HEADER_NAME_BY_INDEX NULL 110 | #define WINHTTP_NO_OUTPUT_BUFFER NULL 111 | 112 | #define WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH 0 113 | #define WINHTTP_ADDREQ_FLAG_ADD 0 114 | #define WINHTTP_ENABLE_SSL_REVOCATION 1 115 | 116 | enum 117 | { 118 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00080000, 119 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00100000, 120 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00200000, 121 | WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = 0x00400000, 122 | WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = 0x00040000, 123 | WINHTTP_FLAG_SECURE = 0x00800000, 124 | WINHTTP_FLAG_ASYNC = 0x10000000, 125 | WINHTTP_FLAG_REFRESH = 0x00000100, 126 | WINHTTP_FLAG_ESCAPE_DISABLE = 0x20000000, 127 | }; 128 | 129 | enum 130 | { 131 | WINHTTP_CALLBACK_STATUS_READ_COMPLETE = 0x1, 132 | WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE = 0x2, 133 | WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE = 0x4, 134 | WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE = 0x8, 135 | WINHTTP_CALLBACK_STATUS_REQUEST_ERROR = 0x10, 136 | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING = 0x20, 137 | WINHTTP_CALLBACK_STATUS_SECURE_FAILURE = 0x40, 138 | WINHTTP_CALLBACK_STATUS_SENDING_REQUEST = 0x80, 139 | WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE = 0x100, 140 | WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = 0x200, 141 | WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = 0x400, 142 | WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = 0x800, 143 | WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = 0x1000, 144 | WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = 0x2000, 145 | WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = 0x4000, 146 | WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = 0x8000, 147 | WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE = 0x10000, 148 | WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = 0x20000, 149 | WINHTTP_CALLBACK_STATUS_REQUEST_SENT = 0x40000, 150 | WINHTTP_CALLBACK_STATUS_REDIRECT = 0x80000, 151 | WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = 0x100000, 152 | WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = 0x200000, 153 | WINHTTP_CALLBACK_FLAG_HANDLES = 0x400000, 154 | WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = 0x800000, 155 | WINHTTP_CALLBACK_FLAG_SEND_REQUEST = 0x1000000, 156 | }; 157 | 158 | enum 159 | { 160 | WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS = 1, 161 | }; 162 | 163 | enum 164 | { 165 | WINHTTP_OPTION_CONTEXT_VALUE = 1, 166 | WINHTTP_OPTION_CONNECT_TIMEOUT, 167 | WINHTTP_OPTION_CALLBACK, 168 | WINHTTP_OPTION_URL, 169 | WINHTTP_OPTION_HTTP_VERSION, 170 | WINHTTP_OPTION_MAX_CONNS_PER_SERVER, 171 | WINHTTP_OPTION_SECURE_PROTOCOLS, 172 | WINHTTP_OPTION_PROXY, 173 | WINHTTP_OPTION_AUTOLOGON_POLICY, 174 | WINHTTP_OPTION_ENABLE_FEATURE, 175 | WINHTTP_OPTION_SECURITY_FLAGS, 176 | }; 177 | 178 | enum 179 | { 180 | SECURITY_FLAG_IGNORE_UNKNOWN_CA = 0x01, 181 | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID = 0x02, 182 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID = 0x04, 183 | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE = 0x10, 184 | }; 185 | enum 186 | { 187 | ERROR_WINHTTP_OPERATION_CANCELLED = 12017, 188 | }; 189 | 190 | enum 191 | { 192 | WINHTTP_ACCESS_TYPE_NAMED_PROXY = 1, 193 | WINHTTP_ACCESS_TYPE_DEFAULT_PROXY =2, 194 | WINHTTP_ACCESS_TYPE_NO_PROXY = 3, 195 | WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4, 196 | }; 197 | 198 | typedef unsigned int INTERNET_PORT; 199 | 200 | #define INTERNET_DEFAULT_HTTP_PORT 80 201 | 202 | 203 | BOOL WinHttpCloseHandle( 204 | HINTERNET hInternet 205 | ); 206 | 207 | BOOL 208 | WinHttpReceiveResponse 209 | ( 210 | HINTERNET hRequest, 211 | LPVOID lpReserved 212 | ); 213 | 214 | BOOL 215 | WinHttpSetTimeouts 216 | ( 217 | HINTERNET hInternet, // Session/Request handle. 218 | int nResolveTimeout, 219 | int nConnectTimeout, 220 | int nSendTimeout, 221 | int nReceiveTimeout 222 | ); 223 | 224 | WINHTTP_STATUS_CALLBACK 225 | WinHttpSetStatusCallback 226 | ( 227 | HINTERNET hInternet, 228 | WINHTTP_STATUS_CALLBACK lpfnInternetCallback, 229 | DWORD dwNotificationFlags, 230 | DWORD_PTR dwReserved 231 | ); 232 | 233 | BOOL WinHttpSetOption( 234 | HINTERNET hInternet, 235 | DWORD dwOption, 236 | LPVOID lpBuffer, 237 | DWORD dwBufferLength 238 | ); 239 | 240 | BOOL WinHttpSendRequest 241 | ( 242 | HINTERNET hRequest, 243 | LPCTSTR lpszHeaders, 244 | DWORD dwHeadersLength, 245 | LPVOID lpOptional, 246 | DWORD dwOptionalLength, 247 | DWORD dwTotalLength, 248 | DWORD_PTR dwContext 249 | ); 250 | 251 | BOOL 252 | WinHttpReadData 253 | ( 254 | HINTERNET hRequest, 255 | LPVOID lpBuffer, 256 | DWORD dwNumberOfBytesToRead, 257 | LPDWORD lpdwNumberOfBytesRead 258 | ); 259 | 260 | BOOL WinHttpQueryHeaders( 261 | HINTERNET hRequest, 262 | DWORD dwInfoLevel, 263 | LPCTSTR pwszName, 264 | LPVOID lpBuffer, 265 | LPDWORD lpdwBufferLength, 266 | LPDWORD lpdwIndex 267 | ); 268 | 269 | BOOL 270 | WinHttpQueryDataAvailable 271 | ( 272 | HINTERNET hRequest, 273 | LPDWORD lpdwNumberOfBytesAvailable 274 | ); 275 | 276 | HINTERNET WinHttpOpenRequest 277 | ( 278 | HINTERNET hConnect, 279 | LPCTSTR pwszVerb, 280 | LPCTSTR pwszObjectName, 281 | LPCTSTR pwszVersion, 282 | LPCTSTR pwszReferrer, 283 | LPCTSTR * ppwszAcceptTypes, 284 | DWORD dwFlags 285 | ); 286 | 287 | HINTERNET WinHttpOpen 288 | ( 289 | LPCTSTR pszAgentW, 290 | DWORD dwAccessType, 291 | LPCTSTR pszProxyW, 292 | LPCTSTR pszProxyBypassW, 293 | DWORD dwFlags 294 | ); 295 | 296 | HINTERNET WinHttpConnect 297 | ( 298 | HINTERNET hSession, 299 | LPCTSTR pswzServerName, 300 | INTERNET_PORT nServerPort, 301 | DWORD dwReserved 302 | ); 303 | 304 | BOOL 305 | WinHttpAddRequestHeaders 306 | ( 307 | HINTERNET hRequest, 308 | LPCTSTR lpszHeaders, 309 | DWORD dwHeadersLength, 310 | DWORD dwModifiers 311 | ); 312 | 313 | BOOL 314 | WinHttpQueryOption 315 | ( 316 | HINTERNET hInternet, 317 | DWORD dwOption, 318 | LPVOID lpBuffer, 319 | LPDWORD lpdwBufferLength 320 | ); 321 | 322 | BOOL WinHttpWriteData( 323 | HINTERNET hRequest, 324 | LPCVOID lpBuffer, 325 | DWORD dwNumberOfBytesToWrite, 326 | LPDWORD lpdwNumberOfBytesWritten 327 | ); 328 | 329 | #define ARRAYSIZE(n) sizeof(n)/sizeof(n[0]) 330 | 331 | #define GetLastError() errno 332 | #define CALLBACK 333 | 334 | 335 | typedef struct { 336 | DWORD dwAccessType; 337 | LPTSTR lpszProxy; 338 | LPTSTR lpszProxyBypass; 339 | } WINHTTP_PROXY_INFO, *LPWINHTTP_PROXY_INFO; 340 | 341 | typedef struct { 342 | BOOL fAutoDetect; 343 | LPTSTR lpszAutoConfigUrl; 344 | LPTSTR lpszProxy; 345 | LPTSTR lpszProxyBypass; 346 | } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; 347 | 348 | typedef struct { 349 | DWORD dwFlags; 350 | DWORD dwAutoDetectFlags; 351 | LPCTSTR lpszAutoConfigUrl; 352 | LPVOID lpvReserved; 353 | DWORD dwReserved; 354 | BOOL fAutoLogonIfChallenged; 355 | } WINHTTP_AUTOPROXY_OPTIONS; 356 | 357 | BOOL WinHttpGetProxyForUrl( 358 | HINTERNET hSession, 359 | LPCTSTR lpcwszUrl, 360 | WINHTTP_AUTOPROXY_OPTIONS *pAutoProxyOptions, 361 | WINHTTP_PROXY_INFO *pProxyInfo 362 | ); 363 | 364 | 365 | BOOL WinHttpQueryAuthSchemes( 366 | HINTERNET hRequest, 367 | LPDWORD lpdwSupportedSchemes, 368 | LPDWORD lpdwFirstScheme, 369 | LPDWORD pdwAuthTarget 370 | ); 371 | 372 | BOOL WinHttpSetCredentials( 373 | HINTERNET hRequest, 374 | DWORD AuthTargets, 375 | DWORD AuthScheme, 376 | LPCTSTR pwszUserName, 377 | LPCTSTR pwszPassword, 378 | LPVOID pAuthParams 379 | ); 380 | 381 | BOOL WinHttpGetDefaultProxyConfiguration(WINHTTP_PROXY_INFO *pProxyInfo); 382 | 383 | BOOL WinHttpGetIEProxyConfigForCurrentUser(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *pProxyConfig); 384 | 385 | enum 386 | { 387 | WINHTTP_AUTOPROXY_AUTO_DETECT, 388 | WINHTTP_AUTO_DETECT_TYPE_DHCP, 389 | WINHTTP_AUTO_DETECT_TYPE_DNS_A, 390 | WINHTTP_AUTOPROXY_CONFIG_URL, 391 | }; 392 | 393 | 394 | #define GlobalFree free 395 | 396 | #define FALSE 0 397 | #define TRUE 1 398 | 399 | #define INTERNET_DEFAULT_HTTPS_PORT 443 400 | #define S_OK 0 401 | 402 | #define ERROR_INSUFFICIENT_BUFFER ENOMEM 403 | #define ERROR_WINHTTP_RESEND_REQUEST EBUSY 404 | #define ERROR_SUCCESS 0 405 | #define ERROR_OPERATION_ABORTED EINVAL 406 | #define ERROR_NOT_ENOUGH_MEMORY ENOMEM 407 | #define ERROR_WINHTTP_TIMEOUT ETIMEDOUT 408 | #define ERROR_INVALID_PARAMETER EINVAL 409 | 410 | #include 411 | 412 | template 413 | std::unique_ptr make_unique(Args&&... args) { 414 | return std::unique_ptr(new T(std::forward(args)...)); 415 | } 416 | 417 | #define BOOLAPI BOOL 418 | #define SetLastError(val) errno = val 419 | #define WINHTTPAPI 420 | #define WINAPI 421 | 422 | typedef enum { 423 | INTERNET_SCHEME_HTTP = 1, 424 | INTERNET_SCHEME_HTTPS, 425 | } INTERNET_SCHEME; 426 | 427 | typedef struct { 428 | DWORD dwStructSize; 429 | LPTSTR lpszScheme; 430 | DWORD dwSchemeLength; 431 | INTERNET_SCHEME nScheme; 432 | LPTSTR lpszHostName; 433 | DWORD dwHostNameLength; 434 | INTERNET_PORT nPort; 435 | LPTSTR lpszUserName; 436 | DWORD dwUserNameLength; 437 | LPTSTR lpszPassword; 438 | DWORD dwPasswordLength; 439 | LPTSTR lpszUrlPath; 440 | DWORD dwUrlPathLength; 441 | LPTSTR lpszExtraInfo; 442 | DWORD dwExtraInfoLength; 443 | } URL_COMPONENTS, *LPURL_COMPONENTS; 444 | 445 | -------------------------------------------------------------------------------- /src/winhttppal.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (C) Microsoft. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 | * 5 | * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 | * 7 | * HTTP Library: Client-side APIs. 8 | * 9 | * This file contains WinHttp client implementation using CURL. 10 | * 11 | * For the latest on this and related APIs, please see: https://github.com/microsoft/WinHttpPAL 12 | * 13 | * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 14 | ****/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #ifdef UNICODE 44 | #include 45 | #endif 46 | 47 | #ifdef WIN32 48 | #define localtime_r(_Time, _Tm) localtime_s(_Tm, _Time) 49 | #endif 50 | 51 | #define _WINHTTP_INTERNAL_ 52 | #ifdef _MSC_VER 53 | #include 54 | #include "winhttp.h" 55 | #else 56 | #include 57 | #include 58 | #include "winhttppal.h" 59 | #endif 60 | 61 | #include "winhttppal_imp.h" 62 | 63 | #ifndef WINHTTP_CURL_MAX_WRITE_SIZE 64 | #define WINHTTP_CURL_MAX_WRITE_SIZE CURL_MAX_WRITE_SIZE 65 | #endif 66 | 67 | class WinHttpSessionImp; 68 | class WinHttpRequestImp; 69 | 70 | #ifdef min 71 | #define MIN min 72 | #define MAX max 73 | #else 74 | #define MIN std::min 75 | #define MAX std::max 76 | #endif 77 | 78 | #ifdef UNICODE 79 | #define WCTLEN wcslen 80 | #define TSTRINGSTREAM std::wstringstream 81 | #define TSTRING std::wstring 82 | #define STRING_LITERAL "%S" 83 | #define TO_STRING std::to_wstring 84 | #define TREGEX std::wregex 85 | #define TREGEX_SEARCH std::regex_search 86 | #define TREGEX_MATCH std::wsmatch 87 | #else 88 | #define WCTLEN strlen 89 | #define TSTRINGSTREAM std::stringstream 90 | #define TEXT(T) T 91 | #define TSTRING std::string 92 | #define STRING_LITERAL "%s" 93 | #define TO_STRING std::to_string 94 | #define TREGEX std::regex 95 | #define TREGEX_SEARCH std::regex_search 96 | #define TREGEX_MATCH std::smatch 97 | #endif 98 | 99 | #ifdef _MSC_VER 100 | #pragma comment(lib, "Ws2_32.lib") 101 | #endif 102 | 103 | static const std::map StatusCodeMap = { 104 | { 100, TEXT("Continue") }, 105 | { 101, TEXT("Switching Protocols") }, 106 | { 200, TEXT("OK") }, 107 | { 201, TEXT("Created") }, 108 | { 202, TEXT("Accepted") }, 109 | { 203, TEXT("Non-Authoritative Information") }, 110 | { 204, TEXT("No Content") }, 111 | { 205, TEXT("Reset Content") }, 112 | { 206, TEXT("Partial Content") }, 113 | { 300, TEXT("Multiple Choices") }, 114 | { 301, TEXT("Moved Permanently") }, 115 | { 302, TEXT("Found") }, 116 | { 303, TEXT("See Other") }, 117 | { 304, TEXT("Not Modified") }, 118 | { 305, TEXT("Use Proxy") }, 119 | { 306, TEXT("(Unused)") }, 120 | { 307, TEXT("Temporary Redirect") }, 121 | { 400, TEXT("Bad Request") }, 122 | { 401, TEXT("Unauthorized") }, 123 | { 402, TEXT("Payment Required") }, 124 | { 403, TEXT("Forbidden") }, 125 | { 404, TEXT("Not Found") }, 126 | { 405, TEXT("Method Not Allowed") }, 127 | { 406, TEXT("Not Acceptable") }, 128 | { 407, TEXT("Proxy Authentication Required") }, 129 | { 408, TEXT("Request Timeout") }, 130 | { 409, TEXT("Conflict") }, 131 | { 410, TEXT("Gone") }, 132 | { 411, TEXT("Length Required") }, 133 | { 412, TEXT("Precondition Failed") }, 134 | { 413, TEXT("Request Entity Too Large") }, 135 | { 414, TEXT("Request-URI Too Long") }, 136 | { 415, TEXT("Unsupported Media Type") }, 137 | { 416, TEXT("Requested Range Not Satisfiable") }, 138 | { 417, TEXT("Expectation Failed") }, 139 | { 500, TEXT("Internal Server Error") }, 140 | { 501, TEXT("Not Implemented") }, 141 | { 502, TEXT("Bad Gateway") }, 142 | { 503, TEXT("Service Unavailable") }, 143 | { 504, TEXT("Gateway Timeout") }, 144 | { 505, TEXT("HTTP Version Not Supported") }, 145 | }; 146 | 147 | enum 148 | { 149 | WINHTTP_CLASS_SESSION, 150 | WINHTTP_CLASS_REQUEST, 151 | WINHTTP_CLASS_IMP, 152 | }; 153 | 154 | static int winhttp_tracing = false; 155 | static int winhttp_tracing_verbose = false; 156 | 157 | #ifdef _MSC_VER 158 | int gettimeofday(struct timeval * tp, struct timezone * tzp); 159 | #endif 160 | 161 | static std::mutex trcmtx; 162 | 163 | #ifndef _MSC_VER 164 | static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 165 | #endif 166 | 167 | static TSTRING FindRegex(const TSTRING &subject, const TSTRING ®str); 168 | static std::vector FindRegexA(const std::string &str, const std::string ®str); 169 | 170 | static void TRACE_INTERNAL(const char *fmt, ...) 171 | { 172 | std::lock_guard lck(trcmtx); 173 | va_list args; 174 | va_start(args, fmt); 175 | 176 | tm localTime; 177 | std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); 178 | time_t now = std::chrono::system_clock::to_time_t(t); 179 | localtime_r(&now, &localTime); 180 | 181 | const std::chrono::duration tse = t.time_since_epoch(); 182 | std::chrono::seconds::rep milliseconds = std::chrono::duration_cast(tse).count() % 1000; 183 | 184 | char szBuffer[512]; 185 | vsnprintf(szBuffer, sizeof szBuffer - 1, fmt, args); 186 | szBuffer[sizeof(szBuffer) / sizeof(szBuffer[0]) - 1] = '\0'; 187 | 188 | std::cout << (1900 + localTime.tm_year) << '-' 189 | << std::setfill('0') << std::setw(2) << (localTime.tm_mon + 1) << '-' 190 | << std::setfill('0') << std::setw(2) << localTime.tm_mday << ' ' 191 | << std::setfill('0') << std::setw(2) << localTime.tm_hour << ':' 192 | << std::setfill('0') << std::setw(2) << localTime.tm_min << ':' 193 | << std::setfill('0') << std::setw(2) << localTime.tm_sec << '.' 194 | << std::setfill('0') << std::setw(3) << milliseconds << " " << szBuffer; 195 | 196 | va_end(args); 197 | } 198 | 199 | #define TRACE(fmt, ...) \ 200 | do { if (winhttp_tracing) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) 201 | 202 | #define TRACE_VERBOSE(fmt, ...) \ 203 | do { if (winhttp_tracing_verbose) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) 204 | 205 | #define CURL_BAILOUT_ONERROR(res, request, retval) \ 206 | if ((res) != CURLE_OK) \ 207 | { \ 208 | TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)(request), (res)); \ 209 | return (retval); \ 210 | } 211 | 212 | #define MUTEX_TYPE std::mutex 213 | #define MUTEX_SETUP(x) 214 | #define MUTEX_CLEANUP(x) 215 | #define MUTEX_LOCK(x) x.lock() 216 | #define MUTEX_UNLOCK(x) x.unlock() 217 | 218 | class WinHttpBase; 219 | class WinHttpConnectImp; 220 | 221 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 222 | /* This array will store all of the mutexes available to OpenSSL. */ 223 | static MUTEX_TYPE *mutex_buf = NULL; 224 | 225 | static void locking_function(int mode, int n, const char *file, int line) 226 | { 227 | if (mode & CRYPTO_LOCK) 228 | MUTEX_LOCK(mutex_buf[n]); 229 | else 230 | MUTEX_UNLOCK(mutex_buf[n]); 231 | } 232 | 233 | static unsigned long id_function() 234 | { 235 | return static_cast(THREAD_ID); 236 | } 237 | #endif 238 | 239 | static int thread_setup() 240 | { 241 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 242 | int i; 243 | 244 | mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; 245 | if (!mutex_buf) 246 | return 0; 247 | for (i = 0; i < CRYPTO_num_locks(); i++) 248 | MUTEX_SETUP(mutex_buf[i]); 249 | CRYPTO_set_id_callback(id_function); 250 | CRYPTO_set_locking_callback(locking_function); 251 | #endif 252 | return 1; 253 | } 254 | 255 | static int thread_cleanup() 256 | { 257 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 258 | int i; 259 | 260 | if (!mutex_buf) 261 | return 0; 262 | CRYPTO_set_id_callback(NULL); 263 | CRYPTO_set_locking_callback(NULL); 264 | for (i = 0; i < CRYPTO_num_locks(); i++) 265 | MUTEX_CLEANUP(mutex_buf[i]); 266 | delete [] mutex_buf; 267 | mutex_buf = NULL; 268 | #endif 269 | return 1; 270 | } 271 | 272 | static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &target) 273 | { 274 | #ifdef UNICODE 275 | std::wstring_convert> conv; 276 | target = conv.to_bytes(std::wstring(lpstr, cLen)); 277 | #else 278 | target.assign(lpstr, cLen); 279 | #endif 280 | } 281 | 282 | static std::vector Split(std::string &str, char delimiter) { 283 | std::vector internal; 284 | std::stringstream ss(str); // Turn the string into a stream. 285 | std::string tok; 286 | 287 | while (getline(ss, tok, delimiter)) { 288 | internal.push_back(tok); 289 | } 290 | 291 | return internal; 292 | } 293 | 294 | static BOOL SizeCheck(LPVOID lpBuffer, LPDWORD lpdwBufferLength, DWORD Required) 295 | { 296 | if (!lpBuffer) 297 | { 298 | if (lpdwBufferLength) 299 | *lpdwBufferLength = Required; 300 | SetLastError(ERROR_INSUFFICIENT_BUFFER); 301 | return FALSE; 302 | } 303 | 304 | if (lpdwBufferLength && (*lpdwBufferLength < Required)) { 305 | *lpdwBufferLength = Required; 306 | 307 | SetLastError(ERROR_INSUFFICIENT_BUFFER); 308 | return FALSE; 309 | } 310 | 311 | if ((Required == 0) || (lpdwBufferLength && (*lpdwBufferLength == 0))) 312 | return FALSE; 313 | 314 | if (!lpBuffer) 315 | return FALSE; 316 | 317 | if (lpdwBufferLength && (*lpdwBufferLength < Required)) 318 | return FALSE; 319 | 320 | return TRUE; 321 | } 322 | 323 | static DWORD ConvertSecurityProtocol(DWORD offered) 324 | { 325 | DWORD min = 0; 326 | DWORD max = 0; 327 | 328 | if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { 329 | min = CURL_SSLVERSION_SSLv2; 330 | } 331 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { 332 | min = CURL_SSLVERSION_SSLv3; 333 | } 334 | else 335 | min = CURL_SSLVERSION_DEFAULT; 336 | 337 | if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { 338 | min = CURL_SSLVERSION_TLSv1_0; 339 | } 340 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { 341 | min = CURL_SSLVERSION_TLSv1_1; 342 | } 343 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { 344 | min = CURL_SSLVERSION_TLSv1_2; 345 | } 346 | 347 | #if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 61) 348 | if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { 349 | max = CURL_SSLVERSION_MAX_TLSv1_2; 350 | } 351 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { 352 | max = CURL_SSLVERSION_MAX_TLSv1_1; 353 | } 354 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { 355 | max = CURL_SSLVERSION_MAX_TLSv1_0; 356 | } 357 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { 358 | max = CURL_SSLVERSION_SSLv3; 359 | } 360 | else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { 361 | max = CURL_SSLVERSION_SSLv2; 362 | } 363 | else 364 | max = CURL_SSLVERSION_MAX_DEFAULT; 365 | #endif 366 | 367 | return min | max; 368 | } 369 | 370 | template 371 | static BOOL CallMemberFunction(WinHttpBase *base, std::function fn, LPVOID lpBuffer) 372 | { 373 | T *obj; 374 | 375 | if (!lpBuffer) 376 | return FALSE; 377 | 378 | if ((obj = dynamic_cast(base))) 379 | { 380 | if (fn(obj, static_cast(lpBuffer))) 381 | return TRUE; 382 | } 383 | return FALSE; 384 | } 385 | 386 | WinHttpSessionImp *GetImp(WinHttpBase *base) 387 | { 388 | WinHttpConnectImp *connect; 389 | WinHttpSessionImp *session; 390 | WinHttpRequestImp *request; 391 | 392 | if ((connect = dynamic_cast(base))) 393 | { 394 | session = connect->GetHandle(); 395 | } 396 | else if ((request = dynamic_cast(base))) 397 | { 398 | connect = request->GetSession(); 399 | if (!connect) 400 | return NULL; 401 | session = connect->GetHandle(); 402 | } 403 | else 404 | { 405 | session = dynamic_cast(base); 406 | } 407 | 408 | return session; 409 | } 410 | 411 | static void QueueBufferRequest(std::vector &store, LPVOID buffer, size_t length) 412 | { 413 | BufferRequest shr; 414 | shr.m_Buffer = buffer; 415 | shr.m_Length = length; 416 | shr.m_Used = 0; 417 | store.push_back(shr); 418 | } 419 | 420 | static BufferRequest GetBufferRequest(std::vector &store) 421 | { 422 | if (store.empty()) 423 | return BufferRequest(); 424 | BufferRequest shr = store.front(); 425 | store.erase(store.begin()); 426 | return shr; 427 | } 428 | 429 | static BufferRequest &PeekBufferRequest(std::vector &store) 430 | { 431 | return store.front(); 432 | } 433 | 434 | EnvInit::EnvInit() 435 | { 436 | if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG")) 437 | winhttp_tracing = std::stoi(std::string(env_p)); 438 | 439 | if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG_VERBOSE")) 440 | winhttp_tracing_verbose = std::stoi(std::string(env_p)); 441 | } 442 | 443 | static EnvInit envinit; 444 | 445 | THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadParameter) 446 | { 447 | UserCallbackContainer *cbContainer = static_cast(lpThreadParameter); 448 | 449 | while (true) 450 | { 451 | { 452 | std::unique_lock GetEventHndlMtx(cbContainer->m_hEventMtx); 453 | while (!cbContainer->m_EventCounter) 454 | cbContainer->m_hEvent.wait(GetEventHndlMtx); 455 | GetEventHndlMtx.unlock(); 456 | 457 | cbContainer->m_EventCounter--; 458 | } 459 | 460 | if (cbContainer->GetClosing()) 461 | { 462 | TRACE("%s", "exiting\n"); 463 | break; 464 | } 465 | 466 | while (true) 467 | { 468 | cbContainer->m_MapMutex.lock(); 469 | UserCallbackContext *ctx = NULL; 470 | ctx = cbContainer->GetNext(); 471 | cbContainer->m_MapMutex.unlock(); 472 | if (!ctx) 473 | break; 474 | 475 | void *statusInformation = NULL; 476 | 477 | if (ctx->GetStatusInformationValid()) 478 | statusInformation = ctx->GetStatusInformation(); 479 | 480 | if (!ctx->GetRequestRef()->GetClosed() && ctx->GetCb()) { 481 | TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", 482 | __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), 483 | ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); 484 | 485 | ctx->GetCb()(ctx->GetRequest(), 486 | (DWORD_PTR)(ctx->GetUserdata()), 487 | ctx->GetInternetStatus(), 488 | statusInformation, 489 | ctx->GetStatusInformationLength()); 490 | 491 | TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", 492 | __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), 493 | ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); 494 | } 495 | ctx->GetRequestCompletionCb()(ctx->GetRequestRef(), ctx->GetInternetStatus()); 496 | delete ctx; 497 | } 498 | } 499 | 500 | #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L 501 | ERR_remove_thread_state(NULL); 502 | #elif OPENSSL_VERSION_NUMBER < 0x10000000L 503 | ERR_remove_state(0); 504 | #endif 505 | 506 | return 0; 507 | } 508 | 509 | UserCallbackContext* UserCallbackContainer::GetNext() 510 | { 511 | UserCallbackContext *ctx = NULL; 512 | // show content: 513 | if (!GetCallbackQueue().empty()) 514 | { 515 | ctx = GetCallbackQueue().front(); 516 | GetCallbackQueue().erase(GetCallbackQueue().begin()); 517 | } 518 | 519 | return ctx; 520 | } 521 | 522 | BOOL UserCallbackContainer::Queue(UserCallbackContext *ctx) 523 | { 524 | if (!ctx) 525 | return FALSE; 526 | 527 | { 528 | std::lock_guard lck(m_MapMutex); 529 | 530 | TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p userdata = %p dwInternetStatus = %p\n", 531 | __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), 532 | ctx->GetUserdata(), ctx->GetStatusInformation()); 533 | GetCallbackQueue().push_back(ctx); 534 | } 535 | { 536 | std::lock_guard lck(m_hEventMtx); 537 | m_EventCounter++; 538 | m_hEvent.notify_all(); 539 | } 540 | return TRUE; 541 | } 542 | 543 | void UserCallbackContainer::DrainQueue() 544 | { 545 | while (true) 546 | { 547 | m_MapMutex.lock(); 548 | UserCallbackContext *ctx = GetNext(); 549 | m_MapMutex.unlock(); 550 | 551 | if (!ctx) 552 | break; 553 | 554 | delete ctx; 555 | } 556 | } 557 | 558 | CURL *ComContainer::AllocCURL() 559 | { 560 | CURL *ptr; 561 | 562 | m_MultiMutex.lock(); 563 | ptr = curl_easy_init(); 564 | m_MultiMutex.unlock(); 565 | 566 | return ptr; 567 | } 568 | 569 | void ComContainer::FreeCURL(CURL *ptr) 570 | { 571 | m_MultiMutex.lock(); 572 | curl_easy_cleanup(ptr); 573 | m_MultiMutex.unlock(); 574 | } 575 | 576 | ComContainer &ComContainer::GetInstance() 577 | { 578 | static ComContainer *the_instance = new ComContainer(); 579 | return *the_instance; 580 | } 581 | 582 | void ComContainer::ResumeTransfer(CURL *handle, int bitmask) 583 | { 584 | int still_running; 585 | 586 | m_MultiMutex.lock(); 587 | curl_easy_pause(handle, bitmask); 588 | curl_multi_perform(m_curlm, &still_running); 589 | m_MultiMutex.unlock(); 590 | } 591 | 592 | BOOL ComContainer::AddHandle(std::shared_ptr &srequest, CURL *handle) 593 | { 594 | CURLMcode mres = CURLM_OK; 595 | 596 | m_MultiMutex.lock(); 597 | if (std::find(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle) != m_ActiveCurl.end()) { 598 | mres = curl_multi_remove_handle(m_curlm, handle); 599 | m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); 600 | } 601 | if (std::find(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest) != m_ActiveRequests.end()) { 602 | m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); 603 | } 604 | m_MultiMutex.unlock(); 605 | if (mres != CURLM_OK) 606 | { 607 | TRACE("curl_multi_remove_handle() failed: %s\n", curl_multi_strerror(mres)); 608 | return FALSE; 609 | } 610 | 611 | m_MultiMutex.lock(); 612 | mres = curl_multi_add_handle(m_curlm, handle); 613 | m_ActiveCurl.push_back(handle); 614 | m_ActiveRequests.push_back(srequest); 615 | m_MultiMutex.unlock(); 616 | if (mres != CURLM_OK) 617 | { 618 | TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); 619 | return FALSE; 620 | } 621 | 622 | return TRUE; 623 | } 624 | 625 | BOOL ComContainer::RemoveHandle(std::shared_ptr &srequest, CURL *handle, bool clearPrivate) 626 | { 627 | CURLMcode mres; 628 | 629 | m_MultiMutex.lock(); 630 | mres = curl_multi_remove_handle(m_curlm, handle); 631 | 632 | if (clearPrivate) 633 | curl_easy_getinfo(handle, CURLINFO_PRIVATE, NULL); 634 | 635 | m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); 636 | m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); 637 | m_MultiMutex.unlock(); 638 | if (mres != CURLM_OK) 639 | { 640 | TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); 641 | return FALSE; 642 | } 643 | 644 | return TRUE; 645 | } 646 | 647 | void ComContainer::KickStart() 648 | { 649 | std::lock_guard lck(m_hAsyncEventMtx); 650 | m_hAsyncEventCounter++; 651 | m_hAsyncEvent.notify_all(); 652 | } 653 | 654 | ComContainer::ComContainer(): m_hAsyncEventCounter(0) 655 | { 656 | curl_global_init(CURL_GLOBAL_ALL); 657 | thread_setup(); 658 | 659 | m_curlm = curl_multi_init(); 660 | 661 | DWORD thread_id; 662 | m_hAsyncThread = CREATETHREAD( 663 | AsyncThreadFunction, // thread function name 664 | this, &thread_id); // argument to thread function 665 | } 666 | 667 | ComContainer::~ComContainer() 668 | { 669 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 670 | m_closing = true; 671 | { 672 | std::lock_guard lck(m_hAsyncEventMtx); 673 | m_hAsyncEventCounter++; 674 | m_hAsyncEvent.notify_all(); 675 | } 676 | THREADJOIN(m_hAsyncThread); 677 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 678 | 679 | curl_multi_cleanup(m_curlm); 680 | curl_global_cleanup(); 681 | thread_cleanup(); 682 | } 683 | 684 | template 685 | void WinHttpHandleContainer::UnRegister(T *val) 686 | { 687 | typedef std::shared_ptr t_shared; 688 | typename std::vector::iterator findit; 689 | bool found = false; 690 | std::lock_guard lck(m_ActiveRequestMtx); 691 | 692 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)val); 693 | 694 | for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) 695 | { 696 | auto v = (*it); 697 | if (v.get() == val) 698 | { 699 | findit = it; 700 | found = true; 701 | break; 702 | } 703 | } 704 | if (found) 705 | m_ActiveRequests.erase(findit); 706 | } 707 | 708 | template 709 | bool WinHttpHandleContainer::IsRegistered(T *val) 710 | { 711 | bool found = false; 712 | std::lock_guard lck(m_ActiveRequestMtx); 713 | 714 | for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) 715 | { 716 | auto v = (*it); 717 | if (v.get() == val) 718 | { 719 | found = true; 720 | break; 721 | } 722 | } 723 | TRACE("%-35s:%-8d:%-16p found:%d\n", __func__, __LINE__, (void*)val, found); 724 | 725 | return found; 726 | } 727 | 728 | template 729 | void WinHttpHandleContainer::Register(std::shared_ptr rqst) 730 | { 731 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)(rqst.get())); 732 | std::lock_guard lck(m_ActiveRequestMtx); 733 | m_ActiveRequests.push_back(rqst); 734 | } 735 | 736 | WinHttpSessionImp::~WinHttpSessionImp() 737 | { 738 | TRACE("%-35s:%-8d:%-16p sesion\n", __func__, __LINE__, (void*)this); 739 | SetCallback(NULL, 0); 740 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 741 | } 742 | 743 | THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) 744 | { 745 | ComContainer *comContainer = static_cast(lpThreadParameter); 746 | 747 | while (true) 748 | { 749 | { 750 | std::unique_lock getAsyncEventHndlMtx(comContainer->m_hAsyncEventMtx); 751 | while (!comContainer->m_hAsyncEventCounter) 752 | comContainer->m_hAsyncEvent.wait(getAsyncEventHndlMtx); 753 | getAsyncEventHndlMtx.unlock(); 754 | 755 | comContainer->m_hAsyncEventCounter--; 756 | } 757 | 758 | int still_running = 1; 759 | 760 | while (still_running && !comContainer->GetThreadClosing()) 761 | { 762 | WinHttpRequestImp *request = NULL; 763 | 764 | comContainer->QueryData(&still_running); 765 | 766 | //TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 767 | struct CURLMsg *m; 768 | 769 | /* call curl_multi_perform or curl_multi_socket_action first, then loop 770 | through and check if there are any transfers that have completed */ 771 | 772 | do { 773 | int msgq = 0; 774 | request = NULL; 775 | std::shared_ptr srequest; 776 | 777 | comContainer->m_MultiMutex.lock(); 778 | m = curl_multi_info_read(comContainer->m_curlm, &msgq); 779 | if (m) 780 | curl_easy_getinfo(m->easy_handle, CURLINFO_PRIVATE, &request); 781 | if (request) 782 | { 783 | srequest = request->shared_from_this(); 784 | } 785 | comContainer->m_MultiMutex.unlock(); 786 | 787 | if (m && (m->msg == CURLMSG_DONE) && request && srequest) { 788 | WINHTTP_ASYNC_RESULT result = { 0, 0 }; 789 | DWORD dwInternetStatus; 790 | 791 | TRACE("%-35s:%-8d:%-16p type:%s result:%d\n", __func__, __LINE__, (void*)request, request->GetType().c_str(), m->data.result); 792 | request->GetCompletionCode() = m->data.result; 793 | 794 | if (m->data.result == CURLE_OK) 795 | { 796 | if (request->HandleQueryDataNotifications(srequest, 0)) 797 | { 798 | TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); 799 | } 800 | { 801 | std::lock_guard lck(request->GetQueryDataEventMtx()); 802 | request->GetQueryDataEventState() = true; 803 | } 804 | request->HandleQueryDataNotifications(srequest, 0); 805 | } 806 | 807 | request->GetBodyStringMutex().lock(); 808 | 809 | request->GetCompletionStatus() = true; 810 | 811 | if (m->data.result == CURLE_OK) 812 | { 813 | void *ptr = request->GetResponseString().data(); 814 | size_t available = request->GetResponseString().size(); 815 | size_t totalread = 0; 816 | request->ConsumeIncoming(srequest, ptr, available, totalread); 817 | if (totalread) 818 | { 819 | TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); 820 | } 821 | request->FlushIncoming(srequest); 822 | } 823 | else if (m->data.result == CURLE_OPERATION_TIMEDOUT) 824 | { 825 | result.dwError = ERROR_WINHTTP_TIMEOUT; 826 | dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; 827 | request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); 828 | TRACE("%-35s:%-8d:%-16p request done type = %s CURLE_OPERATION_TIMEDOUT\n", 829 | __func__, __LINE__, (void*)request, request->GetType().c_str()); 830 | } 831 | else 832 | { 833 | result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; 834 | dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; 835 | TRACE("%-35s:%-8d:%-16p unknown async request done m->data.result = %d\n", 836 | __func__, __LINE__, (void*)request, m->data.result); 837 | #ifdef _DEBUG 838 | assert(0); 839 | #endif 840 | request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); 841 | } 842 | 843 | request->GetBodyStringMutex().unlock(); 844 | 845 | } else if (m && (m->msg != CURLMSG_DONE)) { 846 | TRACE("%-35s:%-8d:%-16p unknown async request done\n", __func__, __LINE__, (void*)request); 847 | DWORD dwInternetStatus; 848 | WINHTTP_ASYNC_RESULT result = { 0, 0 }; 849 | result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; 850 | dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; 851 | #ifdef _DEBUG 852 | assert(0); 853 | #endif 854 | if (request) 855 | request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); 856 | } 857 | 858 | if (request) 859 | { 860 | comContainer->RemoveHandle(srequest, request->GetCurl(), true); 861 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 862 | } 863 | } while (m); 864 | } 865 | if (comContainer->GetThreadClosing()) 866 | { 867 | TRACE("%s:%d exiting\n", __func__, __LINE__); 868 | break; 869 | } 870 | } 871 | 872 | #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L 873 | ERR_remove_thread_state(NULL); 874 | #elif OPENSSL_VERSION_NUMBER < 0x10000000L 875 | ERR_remove_state(0); 876 | #endif 877 | return 0; 878 | } 879 | 880 | int ComContainer::QueryData(int *still_running) 881 | { 882 | if (!still_running) 883 | return 0; 884 | 885 | int rc = 0; 886 | struct timeval timeout; 887 | 888 | fd_set fdread; 889 | fd_set fdwrite; 890 | fd_set fdexcep; 891 | int maxfd = -1; 892 | 893 | long curl_timeo = -1; 894 | 895 | FD_ZERO(&fdread); 896 | FD_ZERO(&fdwrite); 897 | FD_ZERO(&fdexcep); 898 | 899 | /* set a suitable timeout to play around with */ 900 | timeout.tv_sec = 1; 901 | timeout.tv_usec = 0; 902 | 903 | m_MultiMutex.lock(); 904 | curl_multi_timeout(m_curlm, &curl_timeo); 905 | m_MultiMutex.unlock(); 906 | if (curl_timeo < 0) 907 | /* no set timeout, use a default */ 908 | curl_timeo = 10000; 909 | 910 | if (curl_timeo > 0) { 911 | timeout.tv_sec = curl_timeo / 1000; 912 | if (timeout.tv_sec > 1) 913 | timeout.tv_sec = 1; 914 | else 915 | timeout.tv_usec = (curl_timeo % 1000) * 1000; 916 | } 917 | 918 | /* get file descriptors from the transfers */ 919 | m_MultiMutex.lock(); 920 | rc = curl_multi_fdset(m_curlm, &fdread, &fdwrite, &fdexcep, &maxfd); 921 | m_MultiMutex.unlock(); 922 | 923 | if ((maxfd == -1) || (rc != CURLM_OK)) { 924 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 925 | } 926 | else 927 | rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); 928 | 929 | switch (rc) { 930 | case -1: 931 | /* select error */ 932 | *still_running = 0; 933 | TRACE("%s\n", "select() returns error, this is badness\n"); 934 | break; 935 | case 0: 936 | default: 937 | /* timeout or readable/writable sockets */ 938 | m_MultiMutex.lock(); 939 | curl_multi_perform(m_curlm, still_running); 940 | m_MultiMutex.unlock(); 941 | break; 942 | } 943 | 944 | return rc; 945 | } 946 | 947 | THREADRETURN WinHttpRequestImp::UploadThreadFunction(THREADPARAM lpThreadParameter) 948 | { 949 | WinHttpRequestImp *request = static_cast(lpThreadParameter); 950 | if (!request) 951 | return NULL; 952 | 953 | CURL *curl = request->GetCurl(); 954 | CURLcode res; 955 | 956 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 957 | /* Perform the request, res will get the return code */ 958 | res = curl_easy_perform(curl); 959 | /* Check for errors */ 960 | CURL_BAILOUT_ONERROR(res, request, NULL); 961 | 962 | TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); 963 | request->GetUploadThreadExitStatus() = true; 964 | 965 | #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L 966 | ERR_remove_thread_state(NULL); 967 | #elif OPENSSL_VERSION_NUMBER < 0x10000000L 968 | ERR_remove_state(0); 969 | #endif 970 | return 0; 971 | } 972 | 973 | BOOL WinHttpRequestImp::SetProxy(std::vector &proxies) 974 | { 975 | std::vector::iterator it; 976 | 977 | for (it = proxies.begin(); it != proxies.end(); it++) 978 | { 979 | std::string &urlstr = (*it); 980 | CURLcode res; 981 | 982 | res = curl_easy_setopt(GetCurl(), CURLOPT_PROXY, urlstr.c_str()); 983 | CURL_BAILOUT_ONERROR(res, this, FALSE); 984 | } 985 | 986 | return TRUE; 987 | } 988 | 989 | BOOL WinHttpRequestImp::SetServer(std::string &ServerName, int nServerPort) 990 | { 991 | CURLcode res; 992 | 993 | res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName.c_str()); 994 | CURL_BAILOUT_ONERROR(res, this, FALSE); 995 | 996 | res = curl_easy_setopt(GetCurl(), CURLOPT_PORT, nServerPort); 997 | CURL_BAILOUT_ONERROR(res, this, FALSE); 998 | 999 | return TRUE; 1000 | } 1001 | 1002 | void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr &srequest) 1003 | { 1004 | bool expected = true; 1005 | 1006 | bool result = std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, false); 1007 | if (result) 1008 | { 1009 | bool redirectPending; 1010 | { 1011 | expected = true; 1012 | redirectPending = GetRedirectPending(); 1013 | TRACE("%-35s:%-8d:%-16p redirectPending = %d ResponseCallbackSendCounter = %d\n", __func__, __LINE__, (void*)this, 1014 | redirectPending, (int)ResponseCallbackSendCounter()); 1015 | } 1016 | GetReceiveResponseMutex().lock(); 1017 | if (redirectPending) 1018 | { 1019 | if (ResponseCallbackSendCounter() == 0) 1020 | { 1021 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)this); 1022 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); 1023 | ResponseCallbackSendCounter()++; 1024 | } 1025 | if (ResponseCallbackSendCounter() == 1) 1026 | { 1027 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)this); 1028 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); 1029 | ResponseCallbackSendCounter()++; 1030 | } 1031 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REDIRECT, 0, NULL, 0, false); 1032 | ResponseCallbackSendCounter()++; 1033 | } 1034 | if (ResponseCallbackSendCounter() == (0 + redirectPending * 3)) 1035 | { 1036 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)this); 1037 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); 1038 | ResponseCallbackSendCounter()++; 1039 | } 1040 | if (ResponseCallbackSendCounter() == (1 + redirectPending * 3)) 1041 | { 1042 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)this); 1043 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); 1044 | ResponseCallbackSendCounter()++; 1045 | } 1046 | if ((ResponseCallbackSendCounter() == (2 + redirectPending * 3)) && (GetCompletionCode() == CURLE_OK)) 1047 | { 1048 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__, (void*)this); 1049 | SetHeaderReceiveComplete(); 1050 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); 1051 | ResponseCallbackSendCounter()++; 1052 | } 1053 | GetReceiveResponseMutex().unlock(); 1054 | } 1055 | } 1056 | 1057 | size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { 1058 | WinHttpRequestImp *request = static_cast(rqst); 1059 | if (!request) 1060 | return 0; 1061 | 1062 | std::shared_ptr srequest = request->shared_from_this(); 1063 | if (!srequest) 1064 | return size * nmemb; 1065 | bool EofHeaders = false; 1066 | 1067 | TRACE("%-35s:%-8d:%-16p %zu\n", __func__, __LINE__, (void*)request, size * nmemb); 1068 | { 1069 | std::lock_guard lck(request->GetHeaderStringMutex()); 1070 | request->GetHeaderString().append(static_cast(ptr), size * nmemb); 1071 | 1072 | if (request->GetHeaderString().find("\r\n\r\n") != std::string::npos) 1073 | EofHeaders = true; 1074 | if (request->GetHeaderString().find("\n\n") != std::string::npos) 1075 | EofHeaders = true; 1076 | } 1077 | if (EofHeaders && request->GetAsync()) 1078 | { 1079 | std::string regstr; 1080 | DWORD retValue = 501; 1081 | 1082 | TRACE_VERBOSE("%-35s:%-8d:%-16p Header string:%s\n", __func__, __LINE__, (void*)request, request->GetHeaderString().c_str()); 1083 | regstr.append("^HTTP/[0-9.]* [0-9]{3}"); 1084 | std::vector result = FindRegexA(request->GetHeaderString(), regstr); 1085 | for (auto codestr : result) 1086 | { 1087 | std::string code = codestr.substr(codestr.length() - 3); 1088 | retValue = stoi(code); 1089 | } 1090 | 1091 | if ((retValue == 302) || (retValue == 301)) 1092 | { 1093 | std::lock_guard lck(request->GetHeaderStringMutex()); 1094 | request->GetHeaderString() = ""; 1095 | TRACE("%-35s:%-8d:%-16p Redirect \n", __func__, __LINE__, (void*)request); 1096 | request->GetRedirectPending() = true; 1097 | return size * nmemb; 1098 | } 1099 | 1100 | if (retValue != 100) 1101 | { 1102 | std::lock_guard lck(request->GetReceiveCompletionEventMtx()); 1103 | request->ResponseCallbackEventCounter()++; 1104 | request->HandleReceiveNotifications(srequest); 1105 | TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); 1106 | } 1107 | else 1108 | { 1109 | std::lock_guard lck(request->GetHeaderStringMutex()); 1110 | request->GetHeaderString() = ""; 1111 | TRACE("%-35s:%-8d:%-16p retValue = %lu \n", __func__, __LINE__, (void*)request, retValue); 1112 | } 1113 | 1114 | } 1115 | return size * nmemb; 1116 | } 1117 | 1118 | size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { 1119 | size_t read = 0; 1120 | WinHttpRequestImp *request = static_cast(rqst); 1121 | if (!request) 1122 | return 0; 1123 | 1124 | std::shared_ptr srequest = request->shared_from_this(); 1125 | if (!srequest) 1126 | return 0; 1127 | 1128 | { 1129 | std::lock_guard lck(request->GetBodyStringMutex()); 1130 | void *buf = request->GetResponseString().data(); 1131 | size_t available = request->GetResponseString().size(); 1132 | size_t totalread = 0; 1133 | 1134 | request->ConsumeIncoming(srequest, buf, available, totalread); 1135 | if (totalread) 1136 | { 1137 | TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); 1138 | request->GetReadLength() += totalread; 1139 | request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + totalread); 1140 | request->GetReadData().shrink_to_fit(); 1141 | } 1142 | } 1143 | 1144 | size_t available = size * nmemb; 1145 | { 1146 | std::lock_guard lck(request->GetBodyStringMutex()); 1147 | void *buf = ptr; 1148 | 1149 | request->ConsumeIncoming(srequest, buf, available, read); 1150 | 1151 | if (available) 1152 | { 1153 | request->GetResponseString().insert(request->GetResponseString().end(), 1154 | reinterpret_cast(buf), 1155 | reinterpret_cast(buf) + available); 1156 | 1157 | read += available; 1158 | } 1159 | } 1160 | 1161 | if (request->GetAsync()) { 1162 | if (available && request->HandleQueryDataNotifications(srequest, available)) 1163 | TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); 1164 | } 1165 | 1166 | return read; 1167 | } 1168 | 1169 | void WinHttpRequestImp::ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read) 1170 | { 1171 | while (available) 1172 | { 1173 | BufferRequest buf = GetBufferRequest(srequest->GetOutstandingReads()); 1174 | if (!buf.m_Length) 1175 | break; 1176 | 1177 | size_t len = MIN(buf.m_Length, available); 1178 | 1179 | if (len) 1180 | { 1181 | TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu %p %ld\n", 1182 | __func__, __LINE__, (void*)srequest.get(), len, read, buf.m_Buffer, buf.m_Length); 1183 | memcpy(static_cast(buf.m_Buffer), ptr, len); 1184 | } 1185 | 1186 | ptr = static_cast(ptr) + len; 1187 | available -= len; 1188 | 1189 | if (len) 1190 | { 1191 | LPVOID StatusInformation = buf.m_Buffer; 1192 | 1193 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)srequest.get(), buf.m_Buffer, len); 1194 | srequest->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); 1195 | } 1196 | 1197 | read += len; 1198 | } 1199 | } 1200 | 1201 | void WinHttpRequestImp::FlushIncoming(std::shared_ptr &srequest) 1202 | { 1203 | while (1) 1204 | { 1205 | BufferRequest buf = GetBufferRequest(srequest->GetOutstandingReads()); 1206 | if (!buf.m_Length) 1207 | break; 1208 | 1209 | LPVOID StatusInformation = buf.m_Buffer; 1210 | 1211 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%d\n", __func__, __LINE__, (void*)srequest.get(), buf.m_Buffer, 0); 1212 | srequest->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 0, StatusInformation, sizeof(StatusInformation), false); 1213 | } 1214 | } 1215 | 1216 | void WinHttpRequestImp::CleanUp() 1217 | { 1218 | m_CompletionCode = CURLE_OK; 1219 | m_ResponseString.clear(); 1220 | m_HeaderString.clear(); 1221 | m_TotalReceiveSize = 0; 1222 | m_ReadData.clear(); 1223 | m_ReadDataEventCounter = 0; 1224 | m_UploadThreadExitStatus = false; 1225 | m_QueryDataPending = false; 1226 | m_ReceiveResponsePending = false; 1227 | m_RedirectPending = false; 1228 | m_ReceiveResponseEventCounter = 0; 1229 | m_ReceiveResponseSendCounter = 0; 1230 | m_OutstandingWrites.clear(); 1231 | m_OutstandingReads.clear(); 1232 | m_Completion = false; 1233 | } 1234 | 1235 | WinHttpRequestImp::WinHttpRequestImp(): 1236 | m_UploadCallbackThread(), 1237 | m_QueryDataPending(false), 1238 | m_ReceiveResponsePending(false), 1239 | m_ReceiveResponseEventCounter(0), 1240 | m_RedirectPending(false) 1241 | { 1242 | m_curl = ComContainer::GetInstance().AllocCURL(); 1243 | if (!m_curl) 1244 | return; 1245 | } 1246 | 1247 | WinHttpRequestImp::~WinHttpRequestImp() 1248 | { 1249 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 1250 | 1251 | m_closed = true; 1252 | 1253 | if (!GetAsync() && Uploading()) 1254 | { 1255 | { 1256 | std::lock_guard lck(GetReadDataEventMtx()); 1257 | GetReadDataEventCounter()++; 1258 | } 1259 | THREADJOIN(m_UploadCallbackThread); 1260 | } 1261 | 1262 | if (GetAsync()) { 1263 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 1264 | } 1265 | else 1266 | curl_easy_getinfo(GetCurl(), CURLINFO_PRIVATE, NULL); 1267 | 1268 | /* always cleanup */ 1269 | ComContainer::GetInstance().FreeCURL(m_curl); 1270 | 1271 | /* free the custom headers */ 1272 | if (m_HeaderList) 1273 | curl_slist_free_all(m_HeaderList); 1274 | 1275 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); 1276 | } 1277 | 1278 | static void RequestCompletionCb(std::shared_ptr &requestRef, DWORD status) 1279 | { 1280 | if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) 1281 | { 1282 | TRACE_VERBOSE("%-35s:%-8d:%-16p status:0x%lx\n", __func__, __LINE__, (void*)(requestRef.get()), status); 1283 | requestRef->GetClosed() = true; 1284 | } 1285 | } 1286 | 1287 | BOOL WinHttpRequestImp::AsyncQueue(std::shared_ptr &requestRef, 1288 | DWORD dwInternetStatus, size_t statusInformationLength, 1289 | LPVOID statusInformation, DWORD statusInformationCopySize, 1290 | bool allocate) 1291 | { 1292 | UserCallbackContext* ctx = NULL; 1293 | DWORD dwNotificationFlags; 1294 | WINHTTP_STATUS_CALLBACK cb = GetCallback(&dwNotificationFlags); 1295 | LPVOID userdata = NULL; 1296 | 1297 | if (!requestRef->GetAsync()) 1298 | return FALSE; 1299 | 1300 | userdata = GetUserData(); 1301 | 1302 | ctx = new UserCallbackContext(requestRef, dwInternetStatus, static_cast(statusInformationLength), 1303 | dwNotificationFlags, cb, userdata, statusInformation, 1304 | statusInformationCopySize, allocate, RequestCompletionCb); 1305 | if (ctx) { 1306 | UserCallbackContainer::GetInstance().Queue(ctx); 1307 | } 1308 | else 1309 | return FALSE; 1310 | 1311 | 1312 | return TRUE; 1313 | } 1314 | 1315 | 1316 | size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp) 1317 | { 1318 | WinHttpRequestImp *request = static_cast(userp); 1319 | std::shared_ptr srequest = request->shared_from_this(); 1320 | if (!srequest) 1321 | return size * nmemb; 1322 | 1323 | size_t len = 0; 1324 | 1325 | TRACE("request->GetTotalLength(): %lu\n", request->GetTotalLength()); 1326 | if (request->GetOptionalData().length() > 0) 1327 | { 1328 | len = MIN(request->GetOptionalData().length(), size * nmemb); 1329 | TRACE("%-35s:%-8d:%-16p writing optional length of %lu\n", __func__, __LINE__, (void*)request, len); 1330 | std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), static_cast(ptr)); 1331 | request->GetOptionalData().erase(0, len); 1332 | request->GetReadLength() += len; 1333 | return len; 1334 | } 1335 | 1336 | if (request->GetClosed()) 1337 | return -1; 1338 | 1339 | TRACE("%-35s:%-8d:%-16p request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, (void*)request, request->GetTotalLength(), request->GetReadLength()); 1340 | if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) || 1341 | (request->GetTotalLength() != request->GetReadLength())) 1342 | { 1343 | std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); 1344 | if (!request->GetReadDataEventCounter()) 1345 | { 1346 | TRACE("%-35s:%-8d:%-16p transfer paused:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); 1347 | return CURL_READFUNC_PAUSE; 1348 | } 1349 | TRACE("%-35s:%-8d:%-16p transfer resumed as already signalled:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); 1350 | } 1351 | 1352 | if (request->GetAsync()) 1353 | { 1354 | std::lock_guard lck(request->GetReadDataEventMtx()); 1355 | if (!request->GetOutstandingWrites().empty()) 1356 | { 1357 | BufferRequest &buf = PeekBufferRequest(request->GetOutstandingWrites()); 1358 | len = MIN(buf.m_Length - buf.m_Used, size * nmemb); 1359 | 1360 | if (request->GetTotalLength()) 1361 | { 1362 | size_t remaining = request->GetTotalLength() - request->GetReadLength(); 1363 | len = MIN(len, remaining); 1364 | } 1365 | 1366 | request->GetReadLength() += len; 1367 | TRACE("%-35s:%-8d:%-16p writing additional length:%lu buf.m_Length:%lu buf.m_Buffer:%p buf.m_Used:%lu\n", 1368 | __func__, __LINE__, (void*)request, len, buf.m_Length, buf.m_Buffer, buf.m_Used); 1369 | 1370 | if (len) 1371 | { 1372 | memcpy(static_cast(ptr), static_cast(buf.m_Buffer) + buf.m_Used, len); 1373 | } 1374 | 1375 | buf.m_Used += len; 1376 | 1377 | if (buf.m_Used == buf.m_Length) 1378 | { 1379 | DWORD dwInternetStatus; 1380 | DWORD result; 1381 | 1382 | result = buf.m_Length; 1383 | dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; 1384 | request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); 1385 | GetBufferRequest(request->GetOutstandingWrites()); 1386 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:%p buf.m_Length:%lu\n", __func__, __LINE__, (void*)request, buf.m_Buffer, buf.m_Length); 1387 | request->GetReadDataEventCounter()--; 1388 | } 1389 | 1390 | TRACE("%-35s:%-8d:%-16p chunk written:%lu\n", __func__, __LINE__, (void*)request, len); 1391 | } 1392 | } 1393 | else 1394 | { 1395 | request->GetReadDataEventMtx().lock(); 1396 | request->GetReadDataEventCounter()--; 1397 | len = MIN(request->GetReadData().size(), size * nmemb); 1398 | TRACE("%-35s:%-8d:%-16p writing additional length:%lu\n", __func__, __LINE__, (void*)request, len); 1399 | std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, static_cast(ptr)); 1400 | request->GetReadLength() += len; 1401 | request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); 1402 | request->GetReadData().shrink_to_fit(); 1403 | request->GetReadDataEventMtx().unlock(); 1404 | } 1405 | return len; 1406 | } 1407 | 1408 | int WinHttpRequestImp::SocketCallback(CURL *handle, curl_infotype type, 1409 | char *data, size_t size, 1410 | void *userp) 1411 | { 1412 | const char *text = ""; 1413 | 1414 | switch (type) { 1415 | case CURLINFO_TEXT: 1416 | TRACE_VERBOSE("%-35s:%-8d:%-16p == Info: %s", __func__, __LINE__, (void*)userp, data); 1417 | return 0; 1418 | default: /* in case a new one is introduced to shock us */ 1419 | TRACE_VERBOSE("%-35s:%-8d:%-16p type:%d\n", __func__, __LINE__, (void*)userp, type); 1420 | return 0; 1421 | 1422 | case CURLINFO_HEADER_IN: 1423 | text = "=> Receive header"; 1424 | break; 1425 | case CURLINFO_HEADER_OUT: 1426 | text = "=> Send header"; 1427 | break; 1428 | case CURLINFO_DATA_OUT: 1429 | text = "=> Send data"; 1430 | break; 1431 | case CURLINFO_SSL_DATA_OUT: 1432 | text = "=> Send SSL data"; 1433 | break; 1434 | case CURLINFO_DATA_IN: 1435 | text = "<= Recv data"; 1436 | break; 1437 | case CURLINFO_SSL_DATA_IN: 1438 | text = "<= Recv SSL data"; 1439 | break; 1440 | } 1441 | TRACE_VERBOSE("%-35s:%-8d:%-16p %s\n", __func__, __LINE__, (void*)userp, text); 1442 | 1443 | return 0; 1444 | } 1445 | 1446 | bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr &srequest, size_t available) 1447 | { 1448 | bool expected = true; 1449 | bool result = std::atomic_compare_exchange_strong(&GetQueryDataPending(), &expected, false); 1450 | if (result) 1451 | { 1452 | if (!available) 1453 | { 1454 | size_t length; 1455 | 1456 | GetBodyStringMutex().lock(); 1457 | length = GetResponseString().size(); 1458 | available = length; 1459 | GetBodyStringMutex().unlock(); 1460 | } 1461 | 1462 | DWORD lpvStatusInformation = static_cast(available); 1463 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)this, lpvStatusInformation); 1464 | AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), 1465 | (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); 1466 | } 1467 | return result; 1468 | } 1469 | 1470 | void WinHttpRequestImp::WaitAsyncQueryDataCompletion(std::shared_ptr &srequest) 1471 | { 1472 | bool completed; 1473 | GetQueryDataPending() = true; 1474 | TRACE("%-35s:%-8d:%-16p GetQueryDataPending() = %d\n", __func__, __LINE__, (void*)this, (int)GetQueryDataPending()); 1475 | { 1476 | std::lock_guard lck(GetQueryDataEventMtx()); 1477 | completed = GetQueryDataEventState(); 1478 | } 1479 | 1480 | if (completed) { 1481 | TRACE("%-35s:%-8d:%-16p transfer already finished\n", __func__, __LINE__, (void*)this); 1482 | HandleQueryDataNotifications(srequest, 0); 1483 | } 1484 | } 1485 | 1486 | void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr &srequest) 1487 | { 1488 | bool expected = false; 1489 | std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); 1490 | 1491 | TRACE("%-35s:%-8d:%-16p GetReceiveResponsePending() = %d\n", __func__, __LINE__, (void*)this, (int) GetReceiveResponsePending()); 1492 | { 1493 | std::lock_guard lck(GetReceiveCompletionEventMtx()); 1494 | if (ResponseCallbackEventCounter()) 1495 | { 1496 | TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)this); 1497 | HandleReceiveNotifications(srequest); 1498 | } 1499 | } 1500 | } 1501 | 1502 | WINHTTPAPI HINTERNET WINAPI WinHttpOpen 1503 | ( 1504 | LPCTSTR pszAgentW, 1505 | DWORD dwAccessType, 1506 | LPCTSTR pszProxyW, 1507 | LPCTSTR pszProxyBypassW, 1508 | DWORD dwFlags 1509 | ) 1510 | { 1511 | std::shared_ptr session = std::make_shared (); 1512 | if (!session) 1513 | return NULL; 1514 | 1515 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*) (session.get())); 1516 | if (dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) 1517 | { 1518 | if (!pszProxyW) 1519 | return NULL; 1520 | 1521 | TRACE("%-35s:%-8d:%-16p proxy: " STRING_LITERAL " \n", __func__, __LINE__, (void*) (session.get()), pszProxyW); 1522 | ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), session->GetProxy()); 1523 | 1524 | std::vector proxies = Split(session->GetProxy(), ';'); 1525 | if (proxies.empty()) 1526 | { 1527 | std::string sproxy; 1528 | 1529 | ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), sproxy); 1530 | proxies.push_back(sproxy); 1531 | } 1532 | session->SetProxies(proxies); 1533 | } 1534 | 1535 | if (dwFlags & WINHTTP_FLAG_ASYNC) { 1536 | session->SetAsync(); 1537 | } 1538 | 1539 | WinHttpHandleContainer::Instance().Register(session); 1540 | return session.get(); 1541 | } 1542 | 1543 | WINHTTPAPI HINTERNET WINAPI WinHttpConnect 1544 | ( 1545 | HINTERNET hSession, 1546 | LPCTSTR pswzServerName, 1547 | INTERNET_PORT nServerPort, 1548 | DWORD dwReserved 1549 | ) 1550 | { 1551 | WinHttpSessionImp *session = static_cast(hSession); 1552 | if (!session) 1553 | return NULL; 1554 | 1555 | TRACE("%-35s:%-8d:%-16p pswzServerName: " STRING_LITERAL " nServerPort:%d\n", 1556 | __func__, __LINE__, (void*)session, pswzServerName, nServerPort); 1557 | ConvertCstrAssign(pswzServerName, WCTLEN(pswzServerName), session->GetServerName()); 1558 | 1559 | session->SetServerPort(nServerPort); 1560 | std::shared_ptr connect = std::make_shared (); 1561 | if (!connect) 1562 | return NULL; 1563 | 1564 | connect->SetHandle(session); 1565 | WinHttpHandleContainer::Instance().Register(connect); 1566 | return connect.get(); 1567 | } 1568 | 1569 | 1570 | BOOLAPI WinHttpCloseHandle( 1571 | HINTERNET hInternet 1572 | ) 1573 | { 1574 | WinHttpBase *base = static_cast(hInternet); 1575 | 1576 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); 1577 | if (!base) 1578 | return FALSE; 1579 | 1580 | if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) 1581 | { 1582 | WinHttpRequestImp *request = dynamic_cast(base); 1583 | if (!request) 1584 | return FALSE; 1585 | 1586 | std::shared_ptr srequest = request->shared_from_this(); 1587 | if (!srequest) 1588 | return FALSE; 1589 | 1590 | request->GetClosing() = true; 1591 | WinHttpHandleContainer::Instance().UnRegister(request); 1592 | return TRUE; 1593 | } 1594 | 1595 | if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) 1596 | { 1597 | WinHttpSessionImp *session = dynamic_cast(base); 1598 | if (session) 1599 | { 1600 | WinHttpHandleContainer::Instance().UnRegister(session); 1601 | return TRUE; 1602 | } 1603 | return TRUE; 1604 | } 1605 | 1606 | if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) 1607 | { 1608 | WinHttpConnectImp *connect = dynamic_cast(base); 1609 | if (connect) 1610 | { 1611 | WinHttpHandleContainer::Instance().UnRegister(connect); 1612 | return TRUE; 1613 | } 1614 | } 1615 | 1616 | return TRUE; 1617 | } 1618 | 1619 | WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( 1620 | HINTERNET hConnect, 1621 | LPCTSTR pwszVerb, /* include "GET", "POST", and "HEAD" */ 1622 | LPCTSTR pwszObjectName, 1623 | LPCTSTR pwszVersion, 1624 | LPCTSTR pwszReferrer, 1625 | LPCTSTR * ppwszAcceptTypes, 1626 | DWORD dwFlags /* isSecurePort ? (WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE) : WINHTTP_FLAG_REFRESH */ 1627 | ) 1628 | { 1629 | WinHttpConnectImp *connect = static_cast(hConnect); 1630 | if (!connect) 1631 | return NULL; 1632 | 1633 | WinHttpSessionImp *session = connect->GetHandle(); 1634 | if (!session) 1635 | return NULL; 1636 | 1637 | CURLcode res; 1638 | 1639 | if (!pwszVerb) 1640 | pwszVerb = TEXT("GET"); 1641 | 1642 | TRACE("%-35s:%-8d:%-16p pwszVerb = " STRING_LITERAL " pwszObjectName: " STRING_LITERAL " pwszVersion = " STRING_LITERAL " pwszReferrer = " STRING_LITERAL "\n", 1643 | __func__, __LINE__, (void*)session, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer); 1644 | 1645 | std::shared_ptr srequest(new WinHttpRequestImp, [](WinHttpRequestImp *request) { 1646 | 1647 | std::shared_ptr close_request(request); 1648 | TRACE("%-35s:%-8d:%-16p reference count reached to 0\n", __func__, __LINE__, (void*)request); 1649 | 1650 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", __func__, __LINE__, (void*)request); 1651 | request->AsyncQueue(close_request, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, NULL, 0, false); 1652 | }); 1653 | 1654 | if (!srequest) 1655 | return NULL; 1656 | WinHttpRequestImp *request = srequest.get(); 1657 | if (!request) 1658 | return NULL; 1659 | 1660 | if (session->GetAsync()) 1661 | { 1662 | DWORD flag; 1663 | WINHTTP_STATUS_CALLBACK cb; 1664 | LPVOID userdata = NULL; 1665 | 1666 | cb = session->GetCallback(&flag); 1667 | request->SetAsync(); 1668 | request->SetCallback(cb, flag); 1669 | 1670 | if (connect->GetUserData()) 1671 | userdata = (LPVOID)connect->GetUserData(); 1672 | else if (session->GetUserData()) 1673 | userdata = (LPVOID)session->GetUserData(); 1674 | 1675 | if (userdata) 1676 | request->SetUserData(&userdata); 1677 | 1678 | } 1679 | 1680 | const char *prefix; 1681 | std::string server = session->GetServerName(); 1682 | if (dwFlags & WINHTTP_FLAG_SECURE) 1683 | { 1684 | prefix = "https://"; 1685 | request->GetSecure() = true; 1686 | } 1687 | else 1688 | { 1689 | prefix = "http://"; 1690 | request->GetSecure() = false; 1691 | } 1692 | 1693 | if (server.find("http://") == std::string::npos) 1694 | server = prefix + server; 1695 | if (pwszObjectName) 1696 | { 1697 | std::string objectname; 1698 | 1699 | ConvertCstrAssign(pwszObjectName, WCTLEN(pwszObjectName), objectname); 1700 | 1701 | size_t index = 0; 1702 | // convert # to %23 to support links to fragments 1703 | while (true) { 1704 | /* Locate the substring to replace. */ 1705 | index = objectname.find('#', index); 1706 | if (index == std::string::npos) break; 1707 | /* Make the replacement. */ 1708 | objectname.replace(index, 1, "%23"); 1709 | /* Advance index forward so the next iteration doesn't pick it up as well. */ 1710 | index += 3; 1711 | } 1712 | request->SetFullPath(server, objectname); 1713 | if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { 1714 | return NULL; 1715 | } 1716 | } 1717 | else 1718 | { 1719 | std::string nullstr; 1720 | request->SetFullPath(server, nullstr); 1721 | if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { 1722 | return NULL; 1723 | } 1724 | } 1725 | 1726 | if (session->GetConnectionTimeoutMs() > 0) 1727 | { 1728 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_CONNECTTIMEOUT, session->GetConnectionTimeoutMs()/1000); 1729 | CURL_BAILOUT_ONERROR(res, request, NULL); 1730 | } 1731 | 1732 | if (session->GetReceiveTimeoutMs() > 0) 1733 | { 1734 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, session->GetReceiveTimeoutMs()); 1735 | CURL_BAILOUT_ONERROR(res, request, NULL); 1736 | } 1737 | 1738 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_TIME, 60L); 1739 | CURL_BAILOUT_ONERROR(res, request, NULL); 1740 | 1741 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_LIMIT, 1L); 1742 | CURL_BAILOUT_ONERROR(res, request, NULL); 1743 | 1744 | request->SetProxy(session->GetProxies()); 1745 | TSTRING verb; 1746 | verb.assign(pwszVerb); 1747 | if (verb == TEXT("PUT")) 1748 | { 1749 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_PUT, 1L); 1750 | CURL_BAILOUT_ONERROR(res, request, NULL); 1751 | 1752 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); 1753 | CURL_BAILOUT_ONERROR(res, request, NULL); 1754 | 1755 | request->Uploading() = true; 1756 | } 1757 | else if (verb == TEXT("GET")) 1758 | { 1759 | } 1760 | else if (verb == TEXT("POST")) 1761 | { 1762 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_POST, 1L); 1763 | CURL_BAILOUT_ONERROR(res, request, NULL); 1764 | } 1765 | else 1766 | { 1767 | std::string verbcst; 1768 | 1769 | ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verbcst); 1770 | TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verbcst.c_str()); 1771 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verbcst.c_str()); 1772 | CURL_BAILOUT_ONERROR(res, request, NULL); 1773 | 1774 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); 1775 | CURL_BAILOUT_ONERROR(res, request, NULL); 1776 | 1777 | request->Uploading() = true; 1778 | } 1779 | 1780 | #if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) 1781 | if (pwszVersion) 1782 | { 1783 | int ver; 1784 | TSTRING version; 1785 | version.assign(pwszVersion); 1786 | 1787 | if (version == TEXT("1.0")) 1788 | ver = CURL_HTTP_VERSION_1_0; 1789 | else if (version == TEXT("1.1")) 1790 | ver = CURL_HTTP_VERSION_1_1; 1791 | else if (version == TEXT("2.0")) 1792 | ver = CURL_HTTP_VERSION_2_0; 1793 | else 1794 | return NULL; 1795 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTP_VERSION, ver); 1796 | CURL_BAILOUT_ONERROR(res, request, NULL); 1797 | } 1798 | #endif 1799 | 1800 | if (pwszReferrer) 1801 | { 1802 | ConvertCstrAssign(pwszReferrer, WCTLEN(pwszReferrer), session->GetReferrer()); 1803 | 1804 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_REFERER, session->GetReferrer().c_str()); 1805 | CURL_BAILOUT_ONERROR(res, request, NULL); 1806 | } 1807 | 1808 | if (dwFlags & WINHTTP_FLAG_SECURE) 1809 | { 1810 | const char *pKeyName; 1811 | const char *pKeyType; 1812 | const char *pEngine; 1813 | static const char *pCertFile = "testcert.pem"; 1814 | static const char *pCACertFile = "cacert.pem"; 1815 | //static const char *pHeaderFile = "dumpit"; 1816 | const char *pPassphrase = NULL; 1817 | 1818 | pKeyName = NULL; 1819 | pKeyType = "PEM"; 1820 | pCertFile = NULL; 1821 | pCACertFile = NULL; 1822 | pEngine = NULL; 1823 | 1824 | if (const char* env_p = std::getenv("WINHTTP_PAL_KEYNAME")) 1825 | pKeyName = env_p; 1826 | 1827 | if (const char* env_p = std::getenv("WINHTTP_PAL_KEYTYPE")) 1828 | pKeyType = env_p; 1829 | 1830 | if (const char* env_p = std::getenv("WINHTTP_PAL_CERTFILE")) 1831 | pCertFile = env_p; 1832 | 1833 | if (const char* env_p = std::getenv("WINHTTP_PAL_CACERTFILE")) 1834 | pCACertFile = env_p; 1835 | 1836 | if (const char* env_p = std::getenv("WINHTTP_PAL_ENGINE")) 1837 | pEngine = env_p; 1838 | 1839 | if (const char* env_p = std::getenv("WINHTTP_PAL_KEY_PASSPHRASE")) 1840 | pPassphrase = env_p; 1841 | 1842 | if (pEngine) { 1843 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine); 1844 | CURL_BAILOUT_ONERROR(res, request, NULL); 1845 | 1846 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE_DEFAULT, 1L); 1847 | CURL_BAILOUT_ONERROR(res, request, NULL); 1848 | } 1849 | /* cert is stored PEM coded in file... */ 1850 | /* since PEM is default, we needn't set it for PEM */ 1851 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERTTYPE, "PEM"); 1852 | CURL_BAILOUT_ONERROR(res, request, NULL); 1853 | 1854 | /* set the cert for client authentication */ 1855 | if (pCertFile) { 1856 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERT, pCertFile); 1857 | CURL_BAILOUT_ONERROR(res, request, NULL); 1858 | } 1859 | 1860 | /* sorry, for engine we must set the passphrase 1861 | (if the key has one...) */ 1862 | if (pPassphrase) { 1863 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_KEYPASSWD, pPassphrase); 1864 | CURL_BAILOUT_ONERROR(res, request, NULL); 1865 | } 1866 | 1867 | /* if we use a key stored in a crypto engine, 1868 | we must set the key type to "ENG" */ 1869 | if (pKeyType) { 1870 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEYTYPE, pKeyType); 1871 | CURL_BAILOUT_ONERROR(res, request, NULL); 1872 | } 1873 | 1874 | /* set the private key (file or ID in engine) */ 1875 | if (pKeyName) { 1876 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEY, pKeyName); 1877 | CURL_BAILOUT_ONERROR(res, request, NULL); 1878 | } 1879 | 1880 | /* set the file with the certs vaildating the server */ 1881 | if (pCACertFile) { 1882 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_CAINFO, pCACertFile); 1883 | CURL_BAILOUT_ONERROR(res, request, NULL); 1884 | } 1885 | } 1886 | if (pwszVerb) 1887 | { 1888 | ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), request->GetType()); 1889 | } 1890 | request->SetSession(connect); 1891 | WinHttpHandleContainer::Instance().Register(srequest); 1892 | 1893 | return request; 1894 | } 1895 | 1896 | BOOLAPI 1897 | WinHttpAddRequestHeaders 1898 | ( 1899 | HINTERNET hRequest, 1900 | LPCTSTR lpszHeaders, 1901 | DWORD dwHeadersLength, 1902 | DWORD dwModifiers 1903 | ) 1904 | { 1905 | CURLcode res; 1906 | WinHttpRequestImp *request = static_cast(hRequest); 1907 | if (!request) 1908 | return FALSE; 1909 | 1910 | std::shared_ptr srequest = request->shared_from_this(); 1911 | if (!srequest) 1912 | return FALSE; 1913 | 1914 | TRACE("%-35s:%-8d:%-16p lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, (void*)request, 1915 | lpszHeaders ? lpszHeaders: TEXT(""), dwModifiers); 1916 | 1917 | if (dwHeadersLength == (DWORD)-1) 1918 | { 1919 | dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; 1920 | } 1921 | 1922 | if (lpszHeaders) 1923 | { 1924 | std::string &headers = request->GetOutgoingHeaderList(); 1925 | 1926 | ConvertCstrAssign(lpszHeaders, dwHeadersLength, headers); 1927 | request->AddHeader(headers); 1928 | 1929 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPHEADER, request->GetHeaderList()); 1930 | CURL_BAILOUT_ONERROR(res, request, FALSE); 1931 | } 1932 | 1933 | return TRUE; 1934 | } 1935 | 1936 | BOOLAPI WinHttpSendRequest 1937 | ( 1938 | HINTERNET hRequest, 1939 | LPCTSTR lpszHeaders, 1940 | DWORD dwHeadersLength, 1941 | LPVOID lpOptional, 1942 | DWORD dwOptionalLength, 1943 | DWORD dwTotalLength, 1944 | DWORD_PTR dwContext 1945 | ) 1946 | { 1947 | CURLcode res; 1948 | WinHttpRequestImp *request = static_cast(hRequest); 1949 | if (!request) 1950 | return FALSE; 1951 | 1952 | WinHttpConnectImp *connect = request->GetSession(); 1953 | if (!connect) 1954 | return FALSE; 1955 | 1956 | WinHttpSessionImp *session = connect->GetHandle(); 1957 | 1958 | std::shared_ptr srequest = request->shared_from_this(); 1959 | if (!srequest) 1960 | return FALSE; 1961 | 1962 | TSTRING customHeader; 1963 | 1964 | if (dwHeadersLength == (DWORD)-1) 1965 | { 1966 | dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; 1967 | } 1968 | 1969 | if (lpszHeaders) 1970 | customHeader.assign(lpszHeaders, dwHeadersLength); 1971 | 1972 | if ((dwTotalLength == 0) && (dwOptionalLength == 0) && request->Uploading()) 1973 | customHeader += TEXT("Transfer-Encoding: chunked\r\n"); 1974 | 1975 | TRACE("%-35s:%-8d:%-16p lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", 1976 | __func__, __LINE__, (void*)request, (const void*)lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); 1977 | 1978 | if (!customHeader.empty() && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) 1979 | return FALSE; 1980 | 1981 | if (lpOptional) 1982 | { 1983 | if (dwOptionalLength == 0) { 1984 | SetLastError(ERROR_INVALID_PARAMETER); 1985 | return FALSE; 1986 | } 1987 | 1988 | if (!request->SetOptionalData(lpOptional, dwOptionalLength)) return FALSE; 1989 | 1990 | if (request->GetType() == "POST") 1991 | { 1992 | /* Now specify the POST data */ 1993 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDS, request->GetOptionalData().c_str()); 1994 | CURL_BAILOUT_ONERROR(res, request, FALSE); 1995 | } 1996 | else if (request->GetType() == "PUT") 1997 | { 1998 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, "PUT"); 1999 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2000 | 2001 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDS, request->GetOptionalData().c_str()); // data goes here 2002 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2003 | 2004 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDSIZE, dwOptionalLength); // length is a must 2005 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2006 | } 2007 | } 2008 | 2009 | if (request->Uploading() || (request->GetType() == "POST")) 2010 | { 2011 | if (!request->GetAsync() && (dwTotalLength == 0)) { 2012 | SetLastError(ERROR_INVALID_PARAMETER); 2013 | return FALSE; 2014 | } 2015 | } 2016 | 2017 | if (dwOptionalLength == (DWORD)-1) 2018 | dwOptionalLength = request->GetOptionalData().length(); 2019 | 2020 | DWORD totalsize = MAX(dwOptionalLength, dwTotalLength); 2021 | /* provide the size of the upload, we specicially typecast the value 2022 | to curl_off_t since we must be sure to use the correct data size */ 2023 | if (request->GetType() == "POST") 2024 | { 2025 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDSIZE, (long)totalsize); 2026 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2027 | } 2028 | else if (totalsize) 2029 | { 2030 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_INFILESIZE_LARGE, (curl_off_t)totalsize); 2031 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2032 | } 2033 | 2034 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_BUFFERSIZE, WINHTTP_CURL_MAX_WRITE_SIZE); 2035 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2036 | 2037 | request->GetTotalLength() = dwTotalLength; 2038 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_READDATA, request); 2039 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2040 | 2041 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_READFUNCTION, request->ReadCallback); 2042 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2043 | 2044 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGFUNCTION, request->SocketCallback); 2045 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2046 | 2047 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGDATA, request); 2048 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2049 | 2050 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEFUNCTION, request->WriteBodyFunction); 2051 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2052 | 2053 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEDATA, request); 2054 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2055 | 2056 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERFUNCTION, request->WriteHeaderFunction); 2057 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2058 | 2059 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERDATA, request); 2060 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2061 | 2062 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_PRIVATE, request); 2063 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2064 | 2065 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_FOLLOWLOCATION, 1L); 2066 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2067 | 2068 | if (winhttp_tracing_verbose) 2069 | { 2070 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_VERBOSE, 1); 2071 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2072 | } 2073 | 2074 | /* enable TCP keep-alive for this transfer */ 2075 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPALIVE, 1L); 2076 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2077 | 2078 | /* keep-alive idle time to 120 seconds */ 2079 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPIDLE, 120L); 2080 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2081 | 2082 | /* interval time between keep-alive probes: 60 seconds */ 2083 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPINTVL, 60L); 2084 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2085 | 2086 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYPEER, request->VerifyPeer()); 2087 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2088 | 2089 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYHOST, request->VerifyHost()); 2090 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2091 | 2092 | DWORD maxConnections = 0; 2093 | 2094 | if (request->GetMaxConnections()) 2095 | maxConnections = request->GetMaxConnections(); 2096 | else if (session->GetMaxConnections()) 2097 | maxConnections = session->GetMaxConnections(); 2098 | 2099 | if (maxConnections) { 2100 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_MAXCONNECTS, maxConnections); 2101 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2102 | } 2103 | 2104 | if (dwContext) 2105 | request->SetUserData(reinterpret_cast(&dwContext)); 2106 | 2107 | DWORD securityProtocols = 0; 2108 | 2109 | if (request->GetSecureProtocol()) 2110 | securityProtocols = request->GetSecureProtocol(); 2111 | else if (session->GetSecureProtocol()) 2112 | securityProtocols = session->GetSecureProtocol(); 2113 | 2114 | if (securityProtocols) { 2115 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLVERSION, securityProtocols); 2116 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2117 | } 2118 | 2119 | if (request->GetAsync()) 2120 | { 2121 | if (request->GetClosing()) 2122 | { 2123 | TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); 2124 | return FALSE; 2125 | } 2126 | 2127 | request->CleanUp(); 2128 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0, NULL, 0, false); 2129 | 2130 | if (!ComContainer::GetInstance().AddHandle(srequest, request->GetCurl())) 2131 | return FALSE; 2132 | 2133 | ComContainer::GetInstance().KickStart(); 2134 | 2135 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0, NULL, 0, false); 2136 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0, NULL, 0, false); 2137 | 2138 | TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); 2139 | } 2140 | else 2141 | { 2142 | if (dwTotalLength && (request->GetType() != "POST")) 2143 | { 2144 | DWORD thread_id; 2145 | request->GetUploadThread() = CREATETHREAD( 2146 | WinHttpRequestImp::UploadThreadFunction, // thread function name 2147 | request, &thread_id); // argument to thread function 2148 | } 2149 | else 2150 | { 2151 | /* Perform the request, res will get the return code */ 2152 | res = curl_easy_perform(request->GetCurl()); 2153 | /* Check for errors */ 2154 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2155 | } 2156 | } 2157 | 2158 | return TRUE; 2159 | } 2160 | 2161 | WINHTTPAPI 2162 | BOOL 2163 | WINAPI 2164 | WinHttpReceiveResponse 2165 | ( 2166 | HINTERNET hRequest, 2167 | LPVOID lpReserved 2168 | ) 2169 | { 2170 | WinHttpRequestImp *request = static_cast(hRequest); 2171 | if (!request) 2172 | return FALSE; 2173 | 2174 | std::shared_ptr srequest = request->shared_from_this(); 2175 | if (!srequest) 2176 | return FALSE; 2177 | 2178 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 2179 | 2180 | if ((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) 2181 | { 2182 | if (request->GetAsync()) 2183 | { 2184 | { 2185 | std::lock_guard lck(request->GetReadDataEventMtx()); 2186 | request->GetReadDataEventCounter()++; 2187 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 2188 | } 2189 | 2190 | ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); 2191 | } 2192 | } 2193 | 2194 | if (request->GetAsync()) 2195 | { 2196 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 2197 | request->WaitAsyncReceiveCompletion(srequest); 2198 | } 2199 | else 2200 | { 2201 | if (request->Uploading()) 2202 | { 2203 | while (request->GetTotalLength() != request->GetReadLength()) { 2204 | if (request->GetUploadThreadExitStatus()) { 2205 | if (request->GetTotalLength() != request->GetReadLength()) { 2206 | SetLastError(ERROR_WINHTTP_OPERATION_CANCELLED); 2207 | return FALSE; 2208 | } 2209 | } 2210 | 2211 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 2212 | } 2213 | 2214 | return TRUE; 2215 | } 2216 | size_t headerLength; 2217 | { 2218 | std::lock_guard lck(request->GetHeaderStringMutex()); 2219 | headerLength = request->GetHeaderString().length(); 2220 | } 2221 | 2222 | return headerLength > 0; 2223 | } 2224 | return TRUE; 2225 | } 2226 | 2227 | BOOLAPI 2228 | WinHttpQueryDataAvailable 2229 | ( 2230 | HINTERNET hRequest, 2231 | LPDWORD lpdwNumberOfBytesAvailable 2232 | ) 2233 | { 2234 | WinHttpRequestImp *request = static_cast(hRequest); 2235 | if (!request) 2236 | return FALSE; 2237 | 2238 | std::shared_ptr srequest = request->shared_from_this(); 2239 | if (!srequest) 2240 | return FALSE; 2241 | 2242 | if (request->GetClosing()) 2243 | { 2244 | TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); 2245 | return FALSE; 2246 | } 2247 | 2248 | size_t length; 2249 | 2250 | request->GetBodyStringMutex().lock(); 2251 | length = request->GetResponseString().size(); 2252 | size_t available = length; 2253 | request->GetBodyStringMutex().unlock(); 2254 | 2255 | if (request->GetAsync()) 2256 | { 2257 | if (available == 0) 2258 | { 2259 | TRACE("%-35s:%-8d:%-16p !!!!!!!\n", __func__, __LINE__, (void*)request); 2260 | request->WaitAsyncQueryDataCompletion(srequest); 2261 | request->GetBodyStringMutex().lock(); 2262 | length = request->GetResponseString().size(); 2263 | available = length; 2264 | TRACE("%-35s:%-8d:%-16p available = %lu\n", __func__, __LINE__, (void*)request, available); 2265 | request->GetBodyStringMutex().unlock(); 2266 | } 2267 | else 2268 | { 2269 | DWORD lpvStatusInformation = available; 2270 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)request, lpvStatusInformation); 2271 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), 2272 | (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); 2273 | 2274 | } 2275 | 2276 | } 2277 | 2278 | 2279 | if (lpdwNumberOfBytesAvailable) 2280 | *lpdwNumberOfBytesAvailable = available; 2281 | 2282 | return TRUE; 2283 | } 2284 | 2285 | BOOLAPI 2286 | WinHttpReadData 2287 | ( 2288 | HINTERNET hRequest, 2289 | LPVOID lpBuffer, 2290 | DWORD dwNumberOfBytesToRead, 2291 | LPDWORD lpdwNumberOfBytesRead 2292 | ) 2293 | { 2294 | WinHttpRequestImp *request = static_cast(hRequest); 2295 | if (!request) 2296 | return FALSE; 2297 | 2298 | std::shared_ptr srequest = request->shared_from_this(); 2299 | if (!srequest) 2300 | return FALSE; 2301 | 2302 | if (request->GetClosing()) 2303 | { 2304 | TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); 2305 | return FALSE; 2306 | } 2307 | 2308 | size_t readLength; 2309 | 2310 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 2311 | if (dwNumberOfBytesToRead == 0) 2312 | { 2313 | if (lpdwNumberOfBytesRead) 2314 | *lpdwNumberOfBytesRead = 0; 2315 | 2316 | if (request->GetAsync()) 2317 | { 2318 | LPVOID StatusInformation = (LPVOID)lpBuffer; 2319 | 2320 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%d\n", __func__, __LINE__, (void*)request, lpBuffer, 0); 2321 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 0, (LPVOID)StatusInformation, sizeof(StatusInformation), false); 2322 | } 2323 | return TRUE; 2324 | } 2325 | 2326 | request->GetBodyStringMutex().lock(); 2327 | readLength = request->GetResponseString().size(); 2328 | if (readLength) 2329 | { 2330 | if (readLength > dwNumberOfBytesToRead) 2331 | { 2332 | readLength = dwNumberOfBytesToRead; 2333 | } 2334 | std::copy(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength, static_cast(lpBuffer)); 2335 | request->GetResponseString().erase(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength); 2336 | request->GetResponseString().shrink_to_fit(); 2337 | } 2338 | 2339 | if (request->GetAsync()) 2340 | { 2341 | if ((readLength == 0) && (!request->GetCompletionStatus())) 2342 | { 2343 | TRACE("%-35s:%-8d:%-16p Queueing pending reads %p %ld\n", __func__, __LINE__, (void*)request, lpBuffer, dwNumberOfBytesToRead); 2344 | QueueBufferRequest(request->GetOutstandingReads(), lpBuffer, dwNumberOfBytesToRead); 2345 | } 2346 | else 2347 | { 2348 | LPVOID StatusInformation = (LPVOID)lpBuffer; 2349 | 2350 | TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)request, lpBuffer, readLength); 2351 | request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); 2352 | } 2353 | } 2354 | request->GetBodyStringMutex().unlock(); 2355 | 2356 | if (lpdwNumberOfBytesRead) 2357 | *lpdwNumberOfBytesRead = (DWORD)readLength; 2358 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); 2359 | return TRUE; 2360 | } 2361 | 2362 | BOOLAPI 2363 | WinHttpSetTimeouts 2364 | ( 2365 | HINTERNET hInternet, // Session/Request handle. 2366 | int nResolveTimeout, 2367 | int nConnectTimeout, 2368 | int nSendTimeout, 2369 | int nReceiveTimeout 2370 | ) 2371 | { 2372 | WinHttpBase *base = static_cast(hInternet); 2373 | WinHttpSessionImp *session; 2374 | WinHttpRequestImp *request; 2375 | CURLcode res; 2376 | 2377 | TRACE("%-35s:%-8d:%-16p nResolveTimeout:%d nConnectTimeout:%d nSendTimeout:%d nReceiveTimeout:%d\n", 2378 | __func__, __LINE__, (void*)base, nResolveTimeout, nConnectTimeout, nSendTimeout, nReceiveTimeout); 2379 | if ((session = dynamic_cast(base))) 2380 | { 2381 | session->SetReceiveTimeoutMs(nReceiveTimeout); 2382 | session->SetConnectionTimeoutMs(nConnectTimeout); 2383 | } 2384 | else if ((request = dynamic_cast(base))) 2385 | { 2386 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, nReceiveTimeout); 2387 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2388 | res = curl_easy_setopt(request->GetCurl(), CURLOPT_CONNECTTIMEOUT, nConnectTimeout/1000); 2389 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2390 | } 2391 | else 2392 | return FALSE; 2393 | 2394 | return TRUE; 2395 | } 2396 | 2397 | static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) 2398 | { 2399 | TSTRING result; 2400 | try { 2401 | TREGEX re(regstr, std::regex_constants::icase); 2402 | TREGEX_MATCH match; 2403 | if (TREGEX_SEARCH(subject, match, re)) { 2404 | 2405 | result = match.str(0); 2406 | } else { 2407 | result = TEXT(""); 2408 | return TEXT(""); 2409 | } 2410 | } catch (std::regex_error&) { 2411 | return TEXT(""); 2412 | } 2413 | return result; 2414 | } 2415 | 2416 | static std::vector FindRegexA(const std::string &str,const std::string ®str) 2417 | { 2418 | std::vector results; 2419 | try { 2420 | std::regex re(regstr, std::regex_constants::icase); 2421 | std::smatch match; 2422 | std::istringstream f(str); 2423 | std::string line; 2424 | while (std::getline(f, line)) { 2425 | std::string::const_iterator searchStart(line.cbegin()); 2426 | while (std::regex_search(searchStart, line.cend(), match, re)) { 2427 | results.push_back(match[0]); 2428 | searchStart = match.suffix().first; 2429 | } 2430 | } 2431 | } catch (std::regex_error&) { 2432 | return results; 2433 | } 2434 | return results; 2435 | } 2436 | 2437 | bool is_newline(char i) 2438 | { 2439 | return (i == '\n') || (i == '\r'); 2440 | } 2441 | 2442 | template 2443 | std::basic_string nullize_newlines(const std::basic_string& str) { 2444 | std::basic_string result; 2445 | result.reserve(str.size()); 2446 | 2447 | auto cursor = str.begin(); 2448 | const auto end = str.end(); 2449 | for (;;) { 2450 | cursor = std::find_if_not(cursor, end, is_newline); 2451 | if (cursor == end) { 2452 | return result; 2453 | } 2454 | 2455 | const auto nextNewline = std::find_if(cursor, end, is_newline); 2456 | result.append(cursor, nextNewline); 2457 | result.push_back(CharT{}); 2458 | cursor = nextNewline; 2459 | } 2460 | } 2461 | 2462 | static BOOL ReadCurlValue(WinHttpRequestImp *request, LPVOID lpBuffer, LPDWORD lpdwBufferLength, 2463 | CURLINFO curlparam, bool returnDWORD) 2464 | { 2465 | CURLcode res; 2466 | DWORD retValue; 2467 | 2468 | res = curl_easy_getinfo(request->GetCurl(), curlparam, &retValue); 2469 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2470 | 2471 | if (!returnDWORD) 2472 | { 2473 | TSTRING str = TO_STRING(retValue); 2474 | if (SizeCheck(lpBuffer, lpdwBufferLength, (str.size() + 1) * sizeof(TCHAR)) == FALSE) 2475 | return FALSE; 2476 | } 2477 | else 2478 | { 2479 | if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) 2480 | return FALSE; 2481 | } 2482 | 2483 | if (returnDWORD) 2484 | { 2485 | memcpy(lpBuffer, &retValue, sizeof(retValue)); 2486 | } 2487 | else 2488 | { 2489 | TCHAR *outbuf = static_cast(lpBuffer); 2490 | TSTRING str = TO_STRING(retValue); 2491 | std::copy(str.begin(), str.end(), outbuf); 2492 | outbuf[str.size()] = TEXT('\0'); 2493 | 2494 | if (lpdwBufferLength) 2495 | *lpdwBufferLength = (str.size() + 1) * sizeof(TCHAR); 2496 | } 2497 | TRACE("%-35s:%-8d:%-16p curlparam:%d code :%lu\n", __func__, __LINE__, (void*)request, curlparam, retValue); 2498 | return TRUE; 2499 | } 2500 | 2501 | BOOLAPI WinHttpQueryHeaders( 2502 | HINTERNET hRequest, 2503 | DWORD dwInfoLevel, 2504 | LPCTSTR pwszName, 2505 | LPVOID lpBuffer, 2506 | LPDWORD lpdwBufferLength, 2507 | LPDWORD lpdwIndex 2508 | ) 2509 | { 2510 | WinHttpRequestImp *request = static_cast(hRequest); 2511 | if (!request) 2512 | return FALSE; 2513 | 2514 | std::shared_ptr srequest = request->shared_from_this(); 2515 | if (!srequest) 2516 | return FALSE; 2517 | 2518 | bool returnDWORD = false; 2519 | 2520 | if (pwszName != WINHTTP_HEADER_NAME_BY_INDEX) 2521 | return FALSE; 2522 | 2523 | if (lpdwIndex != WINHTTP_NO_HEADER_INDEX) 2524 | return FALSE; 2525 | 2526 | if (request->GetHeaderString().length() == 0) 2527 | return FALSE; 2528 | 2529 | if (dwInfoLevel & WINHTTP_QUERY_FLAG_NUMBER) 2530 | { 2531 | dwInfoLevel &= ~WINHTTP_QUERY_FLAG_NUMBER; 2532 | returnDWORD = true; 2533 | } 2534 | TRACE("%-35s:%-8d:%-16p dwInfoLevel = 0x%lx\n", __func__, __LINE__, (void*)request, dwInfoLevel); 2535 | 2536 | if (returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) 2537 | return FALSE; 2538 | 2539 | #if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) 2540 | if (dwInfoLevel == WINHTTP_QUERY_VERSION) 2541 | { 2542 | return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_VERSION, returnDWORD); 2543 | } 2544 | #endif 2545 | if (dwInfoLevel == WINHTTP_QUERY_STATUS_CODE) 2546 | { 2547 | WinHttpSessionImp *session; 2548 | 2549 | session = GetImp(request); 2550 | if (!session->GetProxies().empty() && request->GetSecure()) 2551 | return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_CONNECTCODE, returnDWORD); 2552 | else 2553 | return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_RESPONSE_CODE, returnDWORD); 2554 | } 2555 | 2556 | if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) 2557 | { 2558 | CURLcode res; 2559 | DWORD responseCode; 2560 | 2561 | res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); 2562 | CURL_BAILOUT_ONERROR(res, request, FALSE); 2563 | 2564 | std::lock_guard lck(request->GetHeaderStringMutex()); 2565 | 2566 | #ifdef UNICODE 2567 | TSTRING subject; 2568 | 2569 | std::wstring_convert> conv; 2570 | subject = conv.from_bytes(request->GetHeaderString()); 2571 | #else 2572 | TSTRING &subject = request->GetHeaderString(); 2573 | #endif 2574 | 2575 | TSTRING regstr; 2576 | 2577 | regstr.append(TEXT("HTTP.*")); 2578 | regstr.append(TO_STRING(responseCode)); 2579 | 2580 | TSTRING result = FindRegex(subject, regstr); 2581 | if (!result.empty()) 2582 | { 2583 | size_t offset = subject.find(result); 2584 | if (offset == std::string::npos) 2585 | return FALSE; 2586 | 2587 | size_t offsetendofline = subject.find(TEXT("\r\n"), offset); 2588 | if (offsetendofline == std::string::npos) 2589 | return FALSE; 2590 | 2591 | size_t startpos = offset + result.length() + 1; 2592 | if (offsetendofline <= startpos) 2593 | return FALSE; 2594 | 2595 | size_t linelength = offsetendofline - startpos; 2596 | if (SizeCheck(lpBuffer, lpdwBufferLength, (linelength + 1) * sizeof(TCHAR)) == FALSE) 2597 | return FALSE; 2598 | 2599 | std::copy(subject.begin() + startpos, subject.begin() + offsetendofline, 2600 | (TCHAR*)lpBuffer); 2601 | ((TCHAR*)lpBuffer)[linelength] = TEXT('\0'); 2602 | return TRUE; 2603 | } 2604 | else 2605 | { 2606 | TSTRING retStr = StatusCodeMap.at(responseCode); 2607 | if (retStr.empty()) 2608 | return FALSE; 2609 | 2610 | if (SizeCheck(lpBuffer, lpdwBufferLength, (retStr.size() + 1) * sizeof(TCHAR)) == FALSE) 2611 | return FALSE; 2612 | 2613 | std::copy(retStr.begin(), retStr.end(), (TCHAR*)lpBuffer); 2614 | ((TCHAR*)lpBuffer)[retStr.size()] = TEXT('\0'); 2615 | } 2616 | return TRUE; 2617 | } 2618 | 2619 | if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS) 2620 | { 2621 | std::string header; 2622 | TCHAR *wbuffer = static_cast(lpBuffer); 2623 | 2624 | std::lock_guard lck(request->GetHeaderStringMutex()); 2625 | header.append(request->GetHeaderString()); 2626 | 2627 | header = nullize_newlines(header); 2628 | header.resize(header.size() + 1); 2629 | 2630 | if (SizeCheck(lpBuffer, lpdwBufferLength, (header.length() + 1) * sizeof(TCHAR)) == FALSE) 2631 | return FALSE; 2632 | 2633 | #ifdef UNICODE 2634 | std::wstring_convert> conv; 2635 | std::wstring wc = conv.from_bytes(header); 2636 | 2637 | if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) 2638 | return FALSE; 2639 | 2640 | std::copy(wc.begin(), wc.end(), wbuffer); 2641 | #else 2642 | std::copy(header.begin(), header.end(), wbuffer); 2643 | #endif 2644 | wbuffer[header.length()] = TEXT('\0'); 2645 | if (lpdwBufferLength) 2646 | *lpdwBufferLength = (DWORD)header.length(); 2647 | return TRUE; 2648 | } 2649 | 2650 | if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS_CRLF) 2651 | { 2652 | std::lock_guard lck(request->GetHeaderStringMutex()); 2653 | size_t length; 2654 | TCHAR *wbuffer = static_cast(lpBuffer); 2655 | 2656 | length = request->GetHeaderString().length(); 2657 | 2658 | if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) 2659 | return FALSE; 2660 | 2661 | #ifdef UNICODE 2662 | std::wstring_convert> conv; 2663 | std::wstring wc = conv.from_bytes(request->GetHeaderString()); 2664 | if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) 2665 | return FALSE; 2666 | 2667 | std::copy(wc.begin(), wc.end(), wbuffer); 2668 | #else 2669 | std::copy(request->GetHeaderString().begin(), request->GetHeaderString().end(), wbuffer); 2670 | #endif 2671 | wbuffer[length] = TEXT('\0'); 2672 | if (lpdwBufferLength) 2673 | *lpdwBufferLength = (DWORD)(length * sizeof(TCHAR)); 2674 | return TRUE; 2675 | } 2676 | 2677 | 2678 | return FALSE; 2679 | } 2680 | 2681 | BOOLAPI WinHttpSetOption( 2682 | HINTERNET hInternet, 2683 | DWORD dwOption, 2684 | LPVOID lpBuffer, 2685 | DWORD dwBufferLength 2686 | ) 2687 | { 2688 | WinHttpBase *base = static_cast(hInternet); 2689 | 2690 | TRACE("%-35s:%-8d:%-16p dwOption:%lu\n", __func__, __LINE__, (void*)base, dwOption); 2691 | 2692 | if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) 2693 | { 2694 | if (dwBufferLength != sizeof(DWORD)) 2695 | return FALSE; 2696 | 2697 | if (CallMemberFunction(base, &WinHttpSessionImp::SetMaxConnections, lpBuffer)) 2698 | return TRUE; 2699 | 2700 | if (CallMemberFunction(base, &WinHttpRequestImp::SetMaxConnections, lpBuffer)) 2701 | return TRUE; 2702 | 2703 | return FALSE; 2704 | } 2705 | else if (dwOption == WINHTTP_OPTION_CONTEXT_VALUE) 2706 | { 2707 | if (dwBufferLength != sizeof(void*)) 2708 | return FALSE; 2709 | 2710 | if (CallMemberFunction(base, &WinHttpConnectImp::SetUserData, lpBuffer)) 2711 | return TRUE; 2712 | 2713 | if (CallMemberFunction(base, &WinHttpSessionImp::SetUserData, lpBuffer)) 2714 | return TRUE; 2715 | 2716 | if (CallMemberFunction(base, &WinHttpRequestImp::SetUserData, lpBuffer)) 2717 | return TRUE; 2718 | 2719 | return FALSE; 2720 | } 2721 | else if (dwOption == WINHTTP_OPTION_SECURE_PROTOCOLS) 2722 | { 2723 | if (dwBufferLength != sizeof(DWORD)) 2724 | return FALSE; 2725 | 2726 | DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); 2727 | if (CallMemberFunction(base, &WinHttpSessionImp::SetSecureProtocol, &curlOffered)) 2728 | return TRUE; 2729 | 2730 | if (CallMemberFunction(base, &WinHttpRequestImp::SetSecureProtocol, &curlOffered)) 2731 | return TRUE; 2732 | 2733 | return FALSE; 2734 | } 2735 | else if (dwOption == WINHTTP_OPTION_ENABLE_FEATURE) 2736 | { 2737 | if (dwBufferLength != sizeof(DWORD)) 2738 | return FALSE; 2739 | 2740 | DWORD dwEnableSSLRevocationOpt = *static_cast(lpBuffer); 2741 | 2742 | if (dwEnableSSLRevocationOpt == WINHTTP_ENABLE_SSL_REVOCATION) 2743 | return TRUE; 2744 | 2745 | return FALSE; 2746 | } 2747 | else if (dwOption == WINHTTP_OPTION_SECURITY_FLAGS) 2748 | { 2749 | WinHttpRequestImp *request; 2750 | 2751 | if (dwBufferLength != sizeof(DWORD)) 2752 | return FALSE; 2753 | 2754 | if ((request = dynamic_cast(base))) 2755 | { 2756 | if (!lpBuffer) 2757 | return FALSE; 2758 | 2759 | DWORD value = *static_cast(lpBuffer); 2760 | if (value == (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | 2761 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE)) 2762 | { 2763 | request->VerifyPeer() = 0L; 2764 | request->VerifyHost() = 0L; 2765 | } 2766 | else if (value == SECURITY_FLAG_IGNORE_CERT_CN_INVALID) 2767 | { 2768 | request->VerifyPeer() = 1L; 2769 | request->VerifyHost() = 0L; 2770 | } 2771 | else if (!value) 2772 | { 2773 | request->VerifyPeer() = 1L; 2774 | request->VerifyHost() = 2L; 2775 | } 2776 | else 2777 | return FALSE; 2778 | 2779 | return TRUE; 2780 | } 2781 | else 2782 | return FALSE; 2783 | } 2784 | 2785 | return FALSE; 2786 | } 2787 | 2788 | WINHTTPAPI 2789 | WINHTTP_STATUS_CALLBACK 2790 | WINAPI 2791 | WinHttpSetStatusCallback 2792 | ( 2793 | HINTERNET hInternet, 2794 | WINHTTP_STATUS_CALLBACK lpfnInternetCallback, 2795 | DWORD dwNotificationFlags, 2796 | DWORD_PTR dwReserved 2797 | ) 2798 | { 2799 | if (hInternet == NULL) 2800 | return WINHTTP_INVALID_STATUS_CALLBACK; 2801 | 2802 | WinHttpBase *base = static_cast(hInternet); 2803 | WinHttpSessionImp *session; 2804 | WinHttpRequestImp *request; 2805 | 2806 | WINHTTP_STATUS_CALLBACK oldcb; 2807 | DWORD olddwNotificationFlags; 2808 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)hInternet); 2809 | 2810 | if ((session = dynamic_cast(base))) 2811 | { 2812 | oldcb = session->GetCallback(&olddwNotificationFlags); 2813 | session->SetCallback(lpfnInternetCallback, dwNotificationFlags); 2814 | } 2815 | else if ((request = dynamic_cast(base))) 2816 | { 2817 | oldcb = request->GetCallback(&olddwNotificationFlags); 2818 | request->SetCallback(lpfnInternetCallback, dwNotificationFlags); 2819 | } 2820 | else 2821 | return FALSE; 2822 | 2823 | return oldcb; 2824 | } 2825 | 2826 | BOOLAPI 2827 | WinHttpQueryOption 2828 | ( 2829 | HINTERNET hInternet, 2830 | DWORD dwOption, 2831 | LPVOID lpBuffer, 2832 | LPDWORD lpdwBufferLength 2833 | ) 2834 | { 2835 | WinHttpBase *base = static_cast(hInternet); 2836 | WinHttpSessionImp *session; 2837 | 2838 | TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); 2839 | 2840 | if (!base) 2841 | return FALSE; 2842 | 2843 | if (WINHTTP_OPTION_CONNECT_TIMEOUT == dwOption) 2844 | { 2845 | if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) 2846 | return FALSE; 2847 | 2848 | session = GetImp(base); 2849 | if (!session) 2850 | return FALSE; 2851 | 2852 | *static_cast(lpBuffer) = session->GetConnectionTimeoutMs(); 2853 | } 2854 | if (WINHTTP_OPTION_CALLBACK == dwOption) 2855 | { 2856 | if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(LPVOID)) == FALSE) 2857 | return FALSE; 2858 | 2859 | session = GetImp(base); 2860 | if (!session) 2861 | return FALSE; 2862 | 2863 | DWORD dwNotificationFlags; 2864 | WINHTTP_STATUS_CALLBACK cb = session->GetCallback(&dwNotificationFlags); 2865 | *static_cast(lpBuffer) = cb; 2866 | } 2867 | else if (WINHTTP_OPTION_URL == dwOption) 2868 | { 2869 | WinHttpRequestImp *request; 2870 | 2871 | if (!(request = dynamic_cast(base))) 2872 | return FALSE; 2873 | 2874 | char *url = NULL; 2875 | curl_easy_getinfo(request->GetCurl(), CURLINFO_EFFECTIVE_URL, &url); 2876 | if (!url) 2877 | return FALSE; 2878 | 2879 | if (SizeCheck(lpBuffer, lpdwBufferLength, (strlen(url) + 1) * sizeof(TCHAR)) == FALSE) 2880 | return FALSE; 2881 | 2882 | TCHAR *wbuffer = static_cast(lpBuffer); 2883 | size_t length = strlen(url); 2884 | #ifdef UNICODE 2885 | std::wstring_convert> conv; 2886 | std::wstring wc = conv.from_bytes(std::string(url)); 2887 | if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) 2888 | return FALSE; 2889 | 2890 | std::copy(wc.begin(), wc.end(), wbuffer); 2891 | #else 2892 | std::string urlstr; 2893 | urlstr.assign(url); 2894 | std::copy(urlstr.begin(), urlstr.end(), wbuffer); 2895 | #endif 2896 | wbuffer[strlen(url)] = TEXT('\0'); 2897 | if (lpdwBufferLength) 2898 | *lpdwBufferLength = (DWORD)length; 2899 | } 2900 | else if (WINHTTP_OPTION_HTTP_VERSION == dwOption) 2901 | { 2902 | WinHttpRequestImp *request; 2903 | 2904 | if (!(request = dynamic_cast(base))) 2905 | return FALSE; 2906 | 2907 | if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(HTTP_VERSION_INFO)) == FALSE) 2908 | return FALSE; 2909 | 2910 | HTTP_VERSION_INFO version; 2911 | 2912 | #if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) 2913 | long curlversion; 2914 | CURLcode rc; 2915 | 2916 | rc = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &curlversion); 2917 | if (rc != CURLE_OK) 2918 | return FALSE; 2919 | 2920 | 2921 | if (curlversion == CURL_HTTP_VERSION_1_0) 2922 | { 2923 | version.dwMinorVersion = 0; 2924 | version.dwMajorVersion = 1; 2925 | } 2926 | else if (curlversion == CURL_HTTP_VERSION_1_1) 2927 | { 2928 | version.dwMinorVersion = 1; 2929 | version.dwMajorVersion = 1; 2930 | } 2931 | else if (curlversion == CURL_HTTP_VERSION_2_0) 2932 | { 2933 | version.dwMinorVersion = 0; 2934 | version.dwMajorVersion = 2; 2935 | } 2936 | else 2937 | return FALSE; 2938 | #else 2939 | version.dwMinorVersion = 1; 2940 | version.dwMajorVersion = 1; 2941 | #endif 2942 | 2943 | memcpy(lpBuffer, &version, sizeof(version)); 2944 | if (lpdwBufferLength) 2945 | *lpdwBufferLength = (DWORD)sizeof(version); 2946 | } 2947 | 2948 | return TRUE; 2949 | } 2950 | 2951 | #ifdef CURL_SUPPORTS_URL_API 2952 | BOOLAPI WinHttpCrackUrl( 2953 | LPCTSTR pwszUrl, 2954 | DWORD dwUrlLength, 2955 | DWORD dwFlags, 2956 | LPURL_COMPONENTS lpUrlComponents 2957 | ) 2958 | { 2959 | DWORD urlLen; 2960 | 2961 | if (!pwszUrl || !lpUrlComponents) 2962 | return FALSE; 2963 | 2964 | if (!lpUrlComponents->dwStructSize) 2965 | return FALSE; 2966 | 2967 | if (lpUrlComponents->dwStructSize != sizeof(*lpUrlComponents)) 2968 | return FALSE; 2969 | 2970 | if (dwUrlLength == 0) 2971 | urlLen = WCTLEN(pwszUrl); 2972 | else 2973 | urlLen = dwUrlLength; 2974 | 2975 | std::string urlstr; 2976 | ConvertCstrAssign(pwszUrl, urlLen, urlstr); 2977 | 2978 | CURLUcode rc; 2979 | CURLU *url = curl_url(); 2980 | rc = curl_url_set(url, CURLUPART_URL, urlstr.c_str(), 0); 2981 | if (rc) 2982 | return FALSE; 2983 | 2984 | char *host; 2985 | rc = curl_url_get(url, CURLUPART_HOST, &host, 0); 2986 | if (!rc) { 2987 | size_t pos = urlstr.find(host); 2988 | if (pos != std::string::npos) 2989 | { 2990 | if (lpUrlComponents->dwHostNameLength != (DWORD)-1) 2991 | { 2992 | if (lpUrlComponents->dwHostNameLength >= (strlen(host) + 1)) { 2993 | TSTRING hoststr; 2994 | hoststr.assign((TCHAR*)pwszUrl + pos, strlen(host)); 2995 | 2996 | std::copy(hoststr.begin(), hoststr.end(), lpUrlComponents->lpszHostName); 2997 | lpUrlComponents->lpszHostName[strlen(host)] = TEXT('\0'); 2998 | lpUrlComponents->dwHostNameLength = strlen(host); 2999 | } 3000 | } 3001 | else 3002 | { 3003 | lpUrlComponents->lpszHostName = const_cast(pwszUrl) + pos; 3004 | lpUrlComponents->dwHostNameLength = strlen(host); 3005 | } 3006 | } 3007 | curl_free(host); 3008 | } 3009 | 3010 | char *scheme; 3011 | rc = curl_url_get(url, CURLUPART_SCHEME, &scheme, 0); 3012 | if (!rc) { 3013 | size_t pos = urlstr.find(scheme); 3014 | if (pos != std::string::npos) 3015 | { 3016 | if (lpUrlComponents->dwSchemeLength != (DWORD)-1) 3017 | { 3018 | if (lpUrlComponents->dwSchemeLength >= (strlen(scheme) + 1)) { 3019 | TSTRING schemestr; 3020 | schemestr.assign((TCHAR*)pwszUrl + pos, strlen(scheme)); 3021 | 3022 | std::copy(schemestr.begin(), schemestr.end(), lpUrlComponents->lpszScheme); 3023 | lpUrlComponents->lpszScheme[strlen(scheme)] = TEXT('\0'); 3024 | lpUrlComponents->dwSchemeLength = strlen(scheme); 3025 | } 3026 | } 3027 | else 3028 | { 3029 | lpUrlComponents->lpszScheme = const_cast(pwszUrl) + pos; 3030 | lpUrlComponents->dwSchemeLength = strlen(scheme); 3031 | } 3032 | 3033 | if (strcmp(scheme, "http") == 0) { 3034 | lpUrlComponents->nPort = 80; 3035 | lpUrlComponents->nScheme = INTERNET_SCHEME_HTTP; 3036 | } 3037 | else if (strcmp(scheme, "https") == 0) 3038 | { 3039 | lpUrlComponents->nPort = 443; 3040 | lpUrlComponents->nScheme = INTERNET_SCHEME_HTTPS; 3041 | } 3042 | } 3043 | curl_free(scheme); 3044 | } 3045 | char *path; 3046 | rc = curl_url_get(url, CURLUPART_PATH, &path, 0); 3047 | if (!rc) { 3048 | size_t pos = urlstr.find(path); 3049 | if (pos != std::string::npos) 3050 | { 3051 | if (lpUrlComponents->dwUrlPathLength != (DWORD)-1) 3052 | { 3053 | if (lpUrlComponents->dwUrlPathLength >= (strlen(path) + 1)) { 3054 | TSTRING urlstr; 3055 | urlstr.assign((TCHAR*)pwszUrl + pos, strlen(path)); 3056 | 3057 | std::copy(urlstr.begin(), urlstr.end(), lpUrlComponents->lpszUrlPath); 3058 | lpUrlComponents->lpszUrlPath[strlen(path)] = TEXT('\0'); 3059 | lpUrlComponents->dwUrlPathLength = strlen(path); 3060 | } 3061 | } 3062 | else 3063 | { 3064 | if (strcmp(path, "/") == 0) 3065 | { 3066 | lpUrlComponents->lpszUrlPath = (LPWSTR)TEXT(""); 3067 | lpUrlComponents->dwUrlPathLength = 0; 3068 | } 3069 | else 3070 | { 3071 | lpUrlComponents->lpszUrlPath = const_cast(pwszUrl) + pos; 3072 | lpUrlComponents->dwUrlPathLength = strlen(path); 3073 | } 3074 | } 3075 | } 3076 | curl_free(path); 3077 | } 3078 | char *query; 3079 | rc = curl_url_get(url, CURLUPART_QUERY, &query, 0); 3080 | if (!rc) { 3081 | size_t pos = urlstr.find(query); 3082 | if (pos != std::string::npos) 3083 | { 3084 | if (lpUrlComponents->dwExtraInfoLength != (DWORD)-1) 3085 | { 3086 | if (lpUrlComponents->dwExtraInfoLength >= (strlen(query) + 1)) { 3087 | TSTRING extrainfo; 3088 | extrainfo.assign((TCHAR*)pwszUrl + pos - 1, strlen(query)); 3089 | 3090 | std::copy(extrainfo.begin(), extrainfo.end(), lpUrlComponents->lpszExtraInfo); 3091 | lpUrlComponents->lpszExtraInfo[strlen(query)] = TEXT('\0'); 3092 | lpUrlComponents->dwExtraInfoLength = strlen(query); 3093 | } 3094 | } 3095 | else 3096 | { 3097 | lpUrlComponents->lpszExtraInfo = const_cast(pwszUrl) + pos - 1; 3098 | lpUrlComponents->dwExtraInfoLength = strlen(query) + 1; 3099 | } 3100 | } 3101 | curl_free(query); 3102 | } 3103 | char *user; 3104 | rc = curl_url_get(url, CURLUPART_USER, &user, 0); 3105 | if (!rc) { 3106 | size_t pos = urlstr.find(user); 3107 | if (pos != std::string::npos) 3108 | { 3109 | if (lpUrlComponents->dwUserNameLength != (DWORD)-1) 3110 | { 3111 | if (lpUrlComponents->dwUserNameLength >= (strlen(user) + 1)) { 3112 | TSTRING userstr; 3113 | userstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(user)); 3114 | 3115 | std::copy(userstr.begin(), userstr.end(), lpUrlComponents->lpszUserName); 3116 | lpUrlComponents->lpszUserName[strlen(user)] = TEXT('\0'); 3117 | lpUrlComponents->dwUserNameLength = strlen(user); 3118 | } 3119 | } 3120 | else 3121 | { 3122 | lpUrlComponents->lpszUserName = const_cast(pwszUrl) + pos - 1; 3123 | lpUrlComponents->dwUserNameLength = strlen(user); 3124 | } 3125 | } 3126 | curl_free(user); 3127 | } 3128 | char *pw; 3129 | rc = curl_url_get(url, CURLUPART_PASSWORD, &pw, 0); 3130 | if (!rc) { 3131 | size_t pos = urlstr.find(pw); 3132 | if (pos != std::string::npos) 3133 | { 3134 | if (lpUrlComponents->dwPasswordLength != (DWORD)-1) 3135 | { 3136 | if (lpUrlComponents->dwPasswordLength >= (strlen(pw) + 1)) { 3137 | TSTRING pwstr; 3138 | pwstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(pw)); 3139 | 3140 | std::copy(pwstr.begin(), pwstr.end(), lpUrlComponents->lpszPassword); 3141 | 3142 | lpUrlComponents->lpszPassword[strlen(pw)] = TEXT('\0'); 3143 | lpUrlComponents->dwPasswordLength = strlen(pw); 3144 | } 3145 | } 3146 | else 3147 | { 3148 | lpUrlComponents->lpszPassword = const_cast(pwszUrl) + pos - 1; 3149 | lpUrlComponents->dwPasswordLength = strlen(pw); 3150 | } 3151 | } 3152 | curl_free(pw); 3153 | } 3154 | curl_url_cleanup(url); 3155 | 3156 | return TRUE; 3157 | } 3158 | 3159 | #endif 3160 | 3161 | 3162 | BOOLAPI WinHttpWriteData 3163 | ( 3164 | HINTERNET hRequest, 3165 | LPCVOID lpBuffer, 3166 | DWORD dwNumberOfBytesToWrite, 3167 | LPDWORD lpdwNumberOfBytesWritten 3168 | ) 3169 | { 3170 | WinHttpRequestImp *request = static_cast(hRequest); 3171 | if (!request) 3172 | return FALSE; 3173 | 3174 | std::shared_ptr srequest = request->shared_from_this(); 3175 | if (!srequest) 3176 | return FALSE; 3177 | 3178 | if (request->GetClosing()) 3179 | { 3180 | TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); 3181 | return FALSE; 3182 | } 3183 | 3184 | TRACE("%-35s:%-8d:%-16p dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, (void*)request, dwNumberOfBytesToWrite); 3185 | if (request->GetAsync()) 3186 | { 3187 | { 3188 | std::lock_guard lck(request->GetReadDataEventMtx()); 3189 | QueueBufferRequest(request->GetOutstandingWrites(), const_cast(lpBuffer), dwNumberOfBytesToWrite); 3190 | request->GetReadDataEventCounter()++; 3191 | } 3192 | 3193 | ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); 3194 | } 3195 | else 3196 | { 3197 | request->AppendReadData(lpBuffer, dwNumberOfBytesToWrite); 3198 | { 3199 | std::lock_guard lck(request->GetReadDataEventMtx()); 3200 | request->GetReadDataEventCounter()++; 3201 | } 3202 | } 3203 | 3204 | if (lpdwNumberOfBytesWritten) 3205 | *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite; 3206 | 3207 | return TRUE; 3208 | } 3209 | 3210 | 3211 | BOOL WinHttpGetProxyForUrl( 3212 | HINTERNET hSession, 3213 | LPCTSTR lpcwszUrl, 3214 | WINHTTP_AUTOPROXY_OPTIONS *pAutoProxyOptions, 3215 | WINHTTP_PROXY_INFO *pProxyInfo 3216 | ) 3217 | { 3218 | return FALSE; 3219 | } 3220 | 3221 | BOOL WinHttpGetDefaultProxyConfiguration(WINHTTP_PROXY_INFO *pProxyInfo) 3222 | { 3223 | return FALSE; 3224 | } 3225 | 3226 | BOOL WinHttpGetIEProxyConfigForCurrentUser(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *pProxyConfig) 3227 | { 3228 | return FALSE; 3229 | } 3230 | 3231 | BOOL WinHttpSetCredentials( 3232 | HINTERNET hRequest, 3233 | DWORD AuthTargets, 3234 | DWORD AuthScheme, 3235 | LPCTSTR pwszUserName, 3236 | LPCTSTR pwszPassword, 3237 | LPVOID pAuthParams 3238 | ) 3239 | { 3240 | WinHttpRequestImp *request = static_cast(hRequest); 3241 | if (!request) 3242 | return FALSE; 3243 | 3244 | std::shared_ptr srequest = request->shared_from_this(); 3245 | if (!srequest) 3246 | return FALSE; 3247 | 3248 | std::string username; 3249 | std::string password; 3250 | 3251 | ConvertCstrAssign(pwszUserName, WCTLEN(pwszUserName), username); 3252 | ConvertCstrAssign(pwszPassword, WCTLEN(pwszPassword), password); 3253 | 3254 | std::string userpwd = username + ":" + password; 3255 | 3256 | if (WINHTTP_AUTH_TARGET_PROXY == AuthTargets) 3257 | { 3258 | curl_easy_setopt(request->GetCurl(), CURLOPT_PROXYUSERPWD, userpwd.c_str()); 3259 | curl_easy_setopt(request->GetCurl(), CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE); 3260 | } 3261 | else 3262 | { 3263 | curl_easy_setopt(request->GetCurl(), CURLOPT_USERPWD, userpwd.c_str()); 3264 | curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE); 3265 | } 3266 | 3267 | return TRUE; 3268 | } 3269 | 3270 | BOOL WinHttpQueryAuthSchemes( 3271 | HINTERNET hRequest, 3272 | LPDWORD lpdwSupportedSchemes, 3273 | LPDWORD lpdwFirstScheme, 3274 | LPDWORD pdwAuthTarget 3275 | ) 3276 | { 3277 | *lpdwSupportedSchemes = 0xFF; 3278 | *lpdwFirstScheme = WINHTTP_AUTH_SCHEME_NEGOTIATE; 3279 | *pdwAuthTarget = WINHTTP_AUTH_TARGET_SERVER; 3280 | return TRUE; 3281 | } 3282 | -------------------------------------------------------------------------------- /src/winhttppal_imp.h: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (C) Microsoft. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 | * 5 | * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 | * 7 | * HTTP Library: Client-side APIs. 8 | * 9 | * This file contains WinHttpPAL class declarations. 10 | * 11 | * For the latest on this and related APIs, please see: https://github.com/microsoft/WinHttpPAL 12 | * 13 | * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 14 | ****/ 15 | 16 | extern "C" 17 | { 18 | #include 19 | #include 20 | } 21 | 22 | #ifdef WIN32 23 | #define THREAD_ID GetCurrentThreadId() 24 | #define THREADPARAM LPVOID 25 | #define CREATETHREAD(func, param, id) \ 26 | CreateThread( \ 27 | NULL, \ 28 | 0, \ 29 | (LPTHREAD_START_ROUTINE)func, \ 30 | param, \ 31 | 0, \ 32 | id) 33 | 34 | #define THREADJOIN(h) WaitForSingleObject(h, INFINITE); 35 | #define THREADRETURN DWORD 36 | #define THREAD_HANDLE HANDLE 37 | #else 38 | #define THREADPARAM void* 39 | #define THREADRETURN void* 40 | #define THREAD_ID pthread_self() 41 | #define THREADJOIN(x) pthread_join(x, NULL) 42 | #define THREAD_HANDLE pthread_t 43 | 44 | typedef void* (*LPTHREAD_START_ROUTINE)(void *); 45 | static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID param, pthread_t *id) 46 | { 47 | pthread_t inc_x_thread; 48 | pthread_create(&inc_x_thread, NULL, func, param); 49 | return inc_x_thread; 50 | } 51 | #endif 52 | 53 | class WinHttpSessionImp; 54 | 55 | class WinHttpBase 56 | { 57 | public: 58 | virtual ~WinHttpBase() {} 59 | }; 60 | 61 | class WinHttpSessionImp :public WinHttpBase 62 | { 63 | std::string m_ServerName; 64 | std::string m_Referrer; 65 | std::vector m_Proxies; 66 | std::string m_Proxy; 67 | WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; 68 | DWORD m_NotificationFlags = 0; 69 | 70 | int m_ServerPort = 0; 71 | long m_ReceiveTimeoutMs = 15000; 72 | long m_ConnectionTimeoutMs = 15000; 73 | BOOL m_Async = false; 74 | 75 | bool m_closing = false; 76 | DWORD m_MaxConnections = 0; 77 | DWORD m_SecureProtocol = 0; 78 | void *m_UserBuffer = NULL; 79 | 80 | public: 81 | 82 | BOOL SetUserData(void **data) 83 | { 84 | if (!data) 85 | return FALSE; 86 | 87 | m_UserBuffer = *data; 88 | return TRUE; 89 | } 90 | void *GetUserData() { return m_UserBuffer; } 91 | 92 | BOOL SetSecureProtocol(DWORD *data) 93 | { 94 | if (!data) 95 | return FALSE; 96 | 97 | m_SecureProtocol = *data; 98 | return TRUE; 99 | } 100 | DWORD GetSecureProtocol() const { return m_SecureProtocol; } 101 | 102 | BOOL SetMaxConnections(DWORD *data) 103 | { 104 | if (!data) 105 | return FALSE; 106 | 107 | m_MaxConnections = *data; 108 | return TRUE; 109 | } 110 | DWORD GetMaxConnections() const { return m_MaxConnections; } 111 | 112 | void SetAsync() { m_Async = TRUE; } 113 | BOOL GetAsync() const { return m_Async; } 114 | 115 | BOOL GetThreadClosing() const { return m_closing; } 116 | 117 | std::vector &GetProxies() { return m_Proxies; } 118 | void SetProxies(std::vector &proxies) { m_Proxies = proxies; } 119 | 120 | std::string &GetProxy() { return m_Proxy; } 121 | 122 | int GetServerPort() const { return m_ServerPort; } 123 | void SetServerPort(int port) { m_ServerPort = port; } 124 | 125 | long GetReceiveTimeoutMs() const { return m_ReceiveTimeoutMs; }; 126 | void SetReceiveTimeoutMs(long timeout) { m_ReceiveTimeoutMs = timeout; } 127 | 128 | long GetConnectionTimeoutMs() const { return m_ConnectionTimeoutMs; } 129 | void SetConnectionTimeoutMs(long timeout) { m_ConnectionTimeoutMs = timeout; } 130 | 131 | std::string &GetServerName() { return m_ServerName; } 132 | 133 | std::string &GetReferrer() { return m_Referrer; } 134 | 135 | void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { 136 | m_InternetCallback = lpfnInternetCallback; 137 | m_NotificationFlags = dwNotificationFlags; 138 | } 139 | WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) 140 | { 141 | if (dwNotificationFlags) 142 | *dwNotificationFlags = m_NotificationFlags; 143 | return m_InternetCallback; 144 | } 145 | 146 | ~WinHttpSessionImp(); 147 | }; 148 | 149 | class WinHttpConnectImp :public WinHttpBase 150 | { 151 | WinHttpSessionImp *m_Handle = NULL; 152 | void *m_UserBuffer = NULL; 153 | 154 | public: 155 | void SetHandle(WinHttpSessionImp *session) { m_Handle = session; } 156 | WinHttpSessionImp *GetHandle() { return m_Handle; } 157 | 158 | BOOL SetUserData(void **data) 159 | { 160 | if (!data) 161 | return FALSE; 162 | 163 | m_UserBuffer = *data; 164 | return TRUE; 165 | } 166 | void *GetUserData() { return m_UserBuffer; } 167 | }; 168 | 169 | struct BufferRequest 170 | { 171 | LPVOID m_Buffer = NULL; 172 | size_t m_Length = 0; 173 | size_t m_Used = 0; 174 | }; 175 | 176 | class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this 177 | { 178 | CURL *m_curl = NULL; 179 | std::vector m_ResponseString; 180 | std::string m_HeaderString; 181 | std::string m_Header; 182 | std::string m_FullPath; 183 | std::string m_OptionalData; 184 | size_t m_TotalSize = 0; 185 | size_t m_TotalReceiveSize = 0; 186 | std::vector m_ReadData; 187 | 188 | std::mutex m_ReadDataEventMtx; 189 | DWORD m_ReadDataEventCounter = 0; 190 | 191 | THREAD_HANDLE m_UploadCallbackThread; 192 | bool m_UploadThreadExitStatus = false; 193 | 194 | DWORD m_SecureProtocol = 0; 195 | DWORD m_MaxConnections = 0; 196 | 197 | std::string m_Type; 198 | LPVOID m_UserBuffer = NULL; 199 | bool m_HeaderReceiveComplete = false; 200 | 201 | struct curl_slist *m_HeaderList = NULL; 202 | WinHttpConnectImp *m_Session = NULL; 203 | std::mutex m_HeaderStringMutex; 204 | std::mutex m_BodyStringMutex; 205 | 206 | std::mutex m_QueryDataEventMtx; 207 | bool m_QueryDataEventState = false; 208 | std::atomic m_QueryDataPending; 209 | 210 | std::mutex m_ReceiveCompletionEventMtx; 211 | 212 | std::mutex m_ReceiveResponseMutex; 213 | std::atomic m_ReceiveResponsePending; 214 | std::atomic m_ReceiveResponseEventCounter; 215 | int m_ReceiveResponseSendCounter = 0; 216 | 217 | std::atomic m_RedirectPending; 218 | 219 | bool m_closing = false; 220 | bool m_closed = false; 221 | bool m_Completion = false; 222 | bool m_Async = false; 223 | CURLcode m_CompletionCode = CURLE_OK; 224 | long m_VerifyPeer = 1; 225 | long m_VerifyHost = 2; 226 | bool m_Uploading = false; 227 | 228 | WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; 229 | DWORD m_NotificationFlags = 0; 230 | std::vector m_OutstandingWrites; 231 | std::vector m_OutstandingReads; 232 | bool m_Secure = false; 233 | 234 | public: 235 | bool &GetSecure() { return m_Secure; } 236 | std::vector &GetOutstandingWrites() { return m_OutstandingWrites; } 237 | std::vector &GetOutstandingReads() { return m_OutstandingReads; } 238 | 239 | long &VerifyPeer() { return m_VerifyPeer; } 240 | long &VerifyHost() { return m_VerifyHost; } 241 | 242 | static size_t WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst); 243 | static size_t WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst); 244 | void ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read); 245 | void FlushIncoming(std::shared_ptr &srequest); 246 | void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { 247 | m_InternetCallback = lpfnInternetCallback; 248 | m_NotificationFlags = dwNotificationFlags; 249 | } 250 | WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) 251 | { 252 | if (dwNotificationFlags) 253 | *dwNotificationFlags = m_NotificationFlags; 254 | return m_InternetCallback; 255 | } 256 | 257 | void SetAsync() { m_Async = TRUE; } 258 | BOOL GetAsync() { return m_Async; } 259 | 260 | WinHttpRequestImp(); 261 | 262 | bool &GetQueryDataEventState() { return m_QueryDataEventState; } 263 | std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } 264 | 265 | bool HandleQueryDataNotifications(std::shared_ptr &, size_t available); 266 | void WaitAsyncQueryDataCompletion(std::shared_ptr &); 267 | 268 | void HandleReceiveNotifications(std::shared_ptr &srequest); 269 | void WaitAsyncReceiveCompletion(std::shared_ptr &srequest); 270 | 271 | CURLcode &GetCompletionCode() { return m_CompletionCode; } 272 | bool &GetCompletionStatus() { return m_Completion; } 273 | bool &GetClosing() { return m_closing; } 274 | bool &GetClosed() { return m_closed; } 275 | void CleanUp(); 276 | ~WinHttpRequestImp(); 277 | 278 | bool &GetUploadThreadExitStatus() { return m_UploadThreadExitStatus; } 279 | std::atomic &GetQueryDataPending() { return m_QueryDataPending; } 280 | 281 | THREAD_HANDLE &GetUploadThread() { 282 | return m_UploadCallbackThread; 283 | } 284 | 285 | static THREADRETURN UploadThreadFunction(THREADPARAM lpThreadParameter); 286 | 287 | // used to wake up CURL ReadCallback triggered on a upload request 288 | std::mutex &GetReadDataEventMtx() { return m_ReadDataEventMtx; } 289 | DWORD &GetReadDataEventCounter() { return m_ReadDataEventCounter; } 290 | 291 | std::mutex &GetReceiveCompletionEventMtx() { return m_ReceiveCompletionEventMtx; } 292 | 293 | // counters used to see which one of these events are observed: ResponseCallbackEventCounter 294 | // counters used to see which one of these events are broadcasted: ResponseCallbackSendCounter 295 | // WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE 296 | // WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED 297 | // WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE 298 | // 299 | // These counters need to be 3 and identical under normal circumstances 300 | std::atomic &ResponseCallbackEventCounter() { return m_ReceiveResponseEventCounter; } 301 | int &ResponseCallbackSendCounter() { return m_ReceiveResponseSendCounter; } 302 | std::atomic &GetReceiveResponsePending() { return m_ReceiveResponsePending; } 303 | std::mutex &GetReceiveResponseMutex() { return m_ReceiveResponseMutex; } 304 | 305 | std::atomic &GetRedirectPending() { return m_RedirectPending; } 306 | 307 | std::mutex &GetHeaderStringMutex() { return m_HeaderStringMutex; } 308 | std::mutex &GetBodyStringMutex() { return m_BodyStringMutex; } 309 | 310 | BOOL AsyncQueue(std::shared_ptr &, 311 | DWORD dwInternetStatus, size_t statusInformationLength, 312 | LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate); 313 | 314 | void SetHeaderReceiveComplete() { m_HeaderReceiveComplete = TRUE; } 315 | 316 | BOOL SetSecureProtocol(DWORD *data) 317 | { 318 | if (!data) 319 | return FALSE; 320 | 321 | m_SecureProtocol = *data; 322 | return TRUE; 323 | } 324 | DWORD GetSecureProtocol() { return m_SecureProtocol; } 325 | 326 | BOOL SetMaxConnections(DWORD *data) 327 | { 328 | if (!data) 329 | return FALSE; 330 | 331 | m_MaxConnections = *data; 332 | return TRUE; 333 | } 334 | DWORD GetMaxConnections() { return m_MaxConnections; } 335 | 336 | BOOL SetUserData(void **data) 337 | { 338 | if (!data) 339 | return FALSE; 340 | 341 | m_UserBuffer = *data; 342 | return TRUE; 343 | } 344 | LPVOID GetUserData() { return m_UserBuffer; } 345 | 346 | void SetFullPath(std::string &server, std::string &relative) 347 | { 348 | m_FullPath.append(server); 349 | m_FullPath.append(relative); 350 | } 351 | std::string &GetFullPath() { return m_FullPath; } 352 | std::string &GetType() { return m_Type; } 353 | bool &Uploading() { return m_Uploading; } 354 | 355 | void SetSession(WinHttpConnectImp *connect) { m_Session = connect; } 356 | WinHttpConnectImp *GetSession() { return m_Session; } 357 | 358 | struct curl_slist *GetHeaderList() { return m_HeaderList; } 359 | 360 | void SetHeaderList(struct curl_slist *list) { m_HeaderList = list; } 361 | std::string &GetOutgoingHeaderList() { return m_Header; } 362 | 363 | void AddHeader(std::string &headers) { 364 | std::stringstream check1(headers); 365 | std::string str; 366 | 367 | while(getline(check1, str, '\n')) 368 | { 369 | str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); 370 | SetHeaderList(curl_slist_append(GetHeaderList(), str.c_str())); 371 | } 372 | } 373 | 374 | std::vector &GetResponseString() { return m_ResponseString; } 375 | std::string &GetHeaderString() { return m_HeaderString; } 376 | CURL *GetCurl() { return m_curl; } 377 | 378 | BOOL SetProxy(std::vector &proxies); 379 | BOOL SetServer(std::string &ServerName, int nServerPort); 380 | 381 | size_t &GetTotalLength() { return m_TotalSize; } 382 | size_t &GetReadLength() { return m_TotalReceiveSize; } 383 | 384 | std::string &GetOptionalData() { return m_OptionalData; } 385 | BOOL SetOptionalData(void *lpOptional, size_t dwOptionalLength) 386 | { 387 | if (!lpOptional || !dwOptionalLength) 388 | return FALSE; 389 | 390 | m_OptionalData.assign(&(static_cast(lpOptional))[0], dwOptionalLength); 391 | return TRUE; 392 | } 393 | 394 | std::vector &GetReadData() { return m_ReadData; } 395 | 396 | void AppendReadData(const void *data, size_t len) 397 | { 398 | std::lock_guard lck(GetReadDataEventMtx()); 399 | m_ReadData.insert(m_ReadData.end(), static_cast(data), static_cast(data) + len); 400 | } 401 | 402 | static int SocketCallback(CURL *handle, curl_infotype type, 403 | char *data, size_t size, 404 | void *userp); 405 | 406 | static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp); 407 | }; 408 | 409 | class UserCallbackContext 410 | { 411 | typedef void (*CompletionCb)(std::shared_ptr &, DWORD status); 412 | 413 | std::shared_ptr m_request; 414 | DWORD m_dwInternetStatus; 415 | DWORD m_dwStatusInformationLength; 416 | DWORD m_dwNotificationFlags; 417 | WINHTTP_STATUS_CALLBACK m_cb; 418 | LPVOID m_userdata; 419 | LPVOID m_StatusInformationVal = NULL; 420 | BYTE* m_StatusInformation = NULL; 421 | bool m_allocate = FALSE; 422 | BOOL m_AsyncResultValid = false; 423 | CompletionCb m_requestCompletionCb; 424 | 425 | BOOL SetAsyncResult(LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate) { 426 | if (allocate) 427 | { 428 | m_StatusInformation = new BYTE[statusInformationCopySize]; 429 | if (m_StatusInformation) 430 | { 431 | memcpy(m_StatusInformation, statusInformation, statusInformationCopySize); 432 | } 433 | else 434 | return FALSE; 435 | } 436 | else 437 | { 438 | m_StatusInformationVal = statusInformation; 439 | } 440 | m_AsyncResultValid = true; 441 | m_allocate = allocate; 442 | 443 | return TRUE; 444 | } 445 | 446 | public: 447 | UserCallbackContext(std::shared_ptr &request, 448 | DWORD dwInternetStatus, 449 | DWORD dwStatusInformationLength, 450 | DWORD dwNotificationFlags, 451 | WINHTTP_STATUS_CALLBACK cb, 452 | LPVOID userdata, 453 | LPVOID statusInformation, 454 | DWORD statusInformationCopySize, bool allocate, 455 | CompletionCb completion) 456 | : m_request(request), m_dwInternetStatus(dwInternetStatus), 457 | m_dwStatusInformationLength(dwStatusInformationLength), 458 | m_dwNotificationFlags(dwNotificationFlags), m_cb(cb), m_userdata(userdata), 459 | m_requestCompletionCb(completion) 460 | { 461 | if (statusInformation) 462 | SetAsyncResult(statusInformation, statusInformationCopySize, allocate); 463 | } 464 | 465 | ~UserCallbackContext() 466 | { 467 | delete [] m_StatusInformation; 468 | } 469 | 470 | std::shared_ptr &GetRequestRef() { return m_request; } 471 | WinHttpRequestImp *GetRequest() { return m_request.get(); } 472 | DWORD GetInternetStatus() const { return m_dwInternetStatus; } 473 | DWORD GetStatusInformationLength() const { return m_dwStatusInformationLength; } 474 | DWORD GetNotificationFlags() const { return m_dwNotificationFlags; } 475 | WINHTTP_STATUS_CALLBACK &GetCb() { return m_cb; } 476 | CompletionCb &GetRequestCompletionCb() { return m_requestCompletionCb; } 477 | LPVOID GetUserdata() { return m_userdata; } 478 | LPVOID GetStatusInformation() { 479 | if (m_allocate) 480 | return m_StatusInformation; 481 | else 482 | return m_StatusInformationVal; 483 | } 484 | BOOL GetStatusInformationValid() const { return m_AsyncResultValid; } 485 | 486 | private: 487 | UserCallbackContext(const UserCallbackContext&); 488 | UserCallbackContext& operator=(const UserCallbackContext&); 489 | }; 490 | 491 | template 492 | class WinHttpHandleContainer 493 | { 494 | std::mutex m_ActiveRequestMtx; 495 | std::vector> m_ActiveRequests; 496 | 497 | public: 498 | static WinHttpHandleContainer &Instance() 499 | { 500 | static WinHttpHandleContainer *the_instance = new WinHttpHandleContainer(); 501 | return *the_instance; 502 | } 503 | 504 | void UnRegister(T *val); 505 | bool IsRegistered(T *val); 506 | void Register(std::shared_ptr rqst); 507 | }; 508 | 509 | 510 | class UserCallbackContainer 511 | { 512 | typedef std::vector UserCallbackQueue; 513 | 514 | UserCallbackQueue m_Queue; 515 | 516 | // used to protect GetCallbackMap() 517 | std::mutex m_MapMutex; 518 | 519 | THREAD_HANDLE m_hThread; 520 | 521 | // used by UserCallbackThreadFunction as a wake-up signal 522 | std::mutex m_hEventMtx; 523 | std::condition_variable m_hEvent; 524 | 525 | // cleared by UserCallbackThreadFunction, set by multiple event providers 526 | std::atomic m_EventCounter; 527 | 528 | bool m_closing = false; 529 | UserCallbackQueue &GetCallbackQueue() { return m_Queue; } 530 | 531 | 532 | public: 533 | 534 | bool GetClosing() const { return m_closing; } 535 | 536 | UserCallbackContext* GetNext(); 537 | 538 | static THREADRETURN UserCallbackThreadFunction(LPVOID lpThreadParameter); 539 | 540 | BOOL Queue(UserCallbackContext *ctx); 541 | void DrainQueue(); 542 | 543 | UserCallbackContainer(): m_EventCounter(0) 544 | { 545 | DWORD thread_id; 546 | m_hThread = CREATETHREAD( 547 | UserCallbackThreadFunction, // thread function name 548 | this, // argument to thread function 549 | &thread_id 550 | ); 551 | } 552 | 553 | ~UserCallbackContainer() 554 | { 555 | m_closing = true; 556 | { 557 | std::lock_guard lck(m_hEventMtx); 558 | m_EventCounter++; 559 | m_hEvent.notify_all(); 560 | } 561 | THREADJOIN(m_hThread); 562 | DrainQueue(); 563 | } 564 | 565 | static UserCallbackContainer &GetInstance() 566 | { 567 | static UserCallbackContainer the_instance; 568 | return the_instance; 569 | } 570 | private: 571 | UserCallbackContainer(const UserCallbackContainer&); 572 | UserCallbackContainer& operator=(const UserCallbackContainer&); 573 | }; 574 | 575 | class EnvInit 576 | { 577 | public: 578 | EnvInit(); 579 | }; 580 | 581 | class ComContainer 582 | { 583 | THREAD_HANDLE m_hAsyncThread; 584 | std::mutex m_hAsyncEventMtx; 585 | 586 | // used to wake up the Async Thread 587 | // Set by external components, cleared by the Async thread 588 | std::atomic m_hAsyncEventCounter; 589 | std::condition_variable m_hAsyncEvent; 590 | std::mutex m_MultiMutex; 591 | CURLM *m_curlm = NULL; 592 | std::vector< CURL *> m_ActiveCurl; 593 | std::vector> m_ActiveRequests; 594 | BOOL m_closing = FALSE; 595 | 596 | // used to protect CURLM data structures 597 | BOOL GetThreadClosing() const { return m_closing; } 598 | 599 | public: 600 | CURL *AllocCURL(); 601 | void FreeCURL(CURL *ptr); 602 | static ComContainer &GetInstance(); 603 | void ResumeTransfer(CURL *handle, int bitmask); 604 | BOOL AddHandle(std::shared_ptr &srequest, CURL *handle); 605 | BOOL RemoveHandle(std::shared_ptr &srequest, CURL *handle, bool clearPrivate); 606 | void KickStart(); 607 | ComContainer(); 608 | ~ComContainer(); 609 | 610 | static THREADRETURN AsyncThreadFunction(THREADPARAM lpThreadParameter); 611 | 612 | int QueryData(int *still_running); 613 | private: 614 | ComContainer(const ComContainer&); 615 | ComContainer& operator=(const ComContainer&); 616 | }; 617 | -------------------------------------------------------------------------------- /winhttppaltargets.cmake: -------------------------------------------------------------------------------- 1 | include(GNUInstallDirs) 2 | install(TARGETS winhttppal 3 | EXPORT winhttppal-export 4 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 5 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 6 | ) 7 | --------------------------------------------------------------------------------