HttpClient exception:ExceptionType:System.Threading.Tasks.TaskCanceledException: The operation was c...

本文探讨了在使用Socket进行网络通信时遇到的System.Threading.Tasks.TaskCanceledException异常,尤其是在大量并发请求下出现的问题。文中详细分析了错误日志,讨论了可能与连接池、超时设置、Socket句柄继承有关的几个关键点,并通过修改超时设置和处理子进程的句柄继承问题找到了临时解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

error msg:

System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.IO.IOException: Unable to read data from the transport connection: Operation canceled. ---> System.Net.Sockets.SocketException: Operation canceled --- End of inner exception stack trace --- at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token) at System.Net.Http.HttpConnection.ReadAsync(Memory`1 destination) at System.Net.Http.HttpConnection.RawConnectionStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken) at System.Net.Security.SslStreamInternal.<FillBufferAsync>g__InternalFillBufferAsync|38_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial) at System.Net.Security.SslStreamInternal.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer) at System.Net.Http.HttpConnection.FillAsync() at System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(Boolean foldedHeadersAllowed) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) --- End of inner exception stack trace ---

 

 

相关bug

1.

Most / all of these requests were using POST operations (or other methods that have method bodies).
We suspect, but aren't sure, that this might be related to connection pooling in some manner.

 

2.

That error message is for SocketError.OperationAborted, which likely means the socket was closed while the send or receive was in progress.

 

3.

Is there anything here that we can look at?
To reproduce this we need to schedule > 5 runs of 4,000+ tests that takes an hour plus.
In such scenario, we typically see 1 - 3 such failures using SocketsHttpHandler.

Note that we never use SocketsHttpHandler directly, so I don't think that we improperly using it.
We got through HttpClient, a shared instance that is re-used among threads, as recommended.

The code is actually doing something like:

try
{
    return await httpHandler.SendAsync(...);
}
catch(HttpRequestException e)
{
}

So we can catch the exception and then check state / logging / something.

Anything you can recommend that we'll look at to get to the root cause?

 

4.

Is it possible you're hitting a timeout? It shouldn't manifest this way, but it would give us a place to start looking. For example, if you change your HttpClient's Timeout to Timeout.InfiniteTimeSpan, does this still happen?

Or if it's potentially related to connection pooling, what happens if you change the SocketHttpHandler's PooledConnectionLifetime and PooledConnectionIdleTimeout to infinite... does it still repro?

 

5.

And... I have a repro. On my machine, this reproduce the scenario instantly.
You were correct about the timeout causing this.

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace ConsoleApp15
{
    class Program
    {
        static async Task Main(string[] args)
        {

            // http://slowwly.robertomurray.co.uk/delay/3000/url/http://www.google.co.uk


            var client = new HttpClient(new SocketsHttpHandler
            {
            })
            {
                Timeout = TimeSpan.FromMilliseconds(250)
            };

            var tasks = new[]
            {
                Run(client),
                Run(client),
                Run(client),
                Run(client),
                Run(client),
                Run(client),
                Run(client),
                Run(client),
                Run(client),
            };

            await Task.WhenAll(tasks);

            Console.WriteLine("Done");

        }

        public static async Task Run(HttpClient client)
        {
            for (int i = 0; i < 25; i++)
            {
                try
                {
                    await client.GetStringAsync("http://slowwly.robertomurray.co.uk/delay/3000/url/http://www.google.co.uk");
                    Console.WriteLine("Should timeout");
                }
                catch (Exception e)
                {
                }
                await client.GetStringAsync("http://slowwly.robertomurray.co.uk/delay/100/url/http://www.google.co.uk");
            }

        }
    }
}

  

6.

That's hitting a timeout and throwing an OperationCanceledException... that's expected.

What's not expected is getting an IOException (not wrapped in an OperationCanceledException) out of SendAsync and when nothing on the request is being canceled / timing out.

 

7.

I captured System.Net logs and Wireshark traces of another failure. Here's where I see the failure in Wireshark:

1990370 214.439692    127.0.0.1 → 127.0.0.1    TCP 84 51686 → 51687 [FIN, ACK] Seq=1 Ack=10334254 Win=525568 Len=0
1990371 214.439736    127.0.0.1 → 127.0.0.1    TCP 84 51687 → 51686 [ACK] Seq=10334254 Ack=2 Win=525568 Len=0
1990408 274.618073    127.0.0.1 → 127.0.0.1    TCP 84 51687 → 51686 [RST, ACK] Seq=10334254 Ack=2 Win=0 Len=0

The RST looks unusual to me, especially since it's occurring after the connection should have been closed. The timestamp on the RST makes it look like is was sent 60 seconds after the connection was closed, but I'm still investigating whether or not that's accurate or just an issue with the logs I collected.

I'm going to try capture another network trace to see if the issue occurs after a FIN again, since that could be valuable info.

 

 

8. 

We are seeing the exact same thing when running our applications on Linux. A temporary "workaround" for us has been to set the timeout to 100 seconds(it's not an acceptable work around).

When we have the timeout to something more reasonable, like 10 seconds or something, we get the error every 500 request or something like that.

The httpendpoint that we are calling are under our control and when the error happens, we can't even see that the request has reached the service. So it seems like the request doesn't leave the box.
We are using dotnet core 2.1.

I've started a stopwatch for each requests and then Im catching TaskCanceledException and logs the following(this is when we have the timeout set to 15 seconds):

Task was cancelled after 15000ms System.Threading.Tasks.TaskCanceledException:
The operation was canceled. ---> System.IO.IOException: Unable to read data from the transport connection: Operation canceled. ---> System.Net.Sockets.SocketException: Operation canceled
--- End of inner exception stack trace ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
at System.Net.Security.SslStreamInternal.g__InternalFillBufferAsync|38_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial)

 

 

9.

Thanks for the additional info. It's possible the behavior you're seeing is related, but it sounds more like an actual timeout. In this case there are two differences from what I've observed. (1) in all other cases I've seen the connection is actually sending the data, as observed via wireshark and (2) in other cases the failure occurs well before a timeout should have occurred.

There's an open issue right now (#20296) tracking the fact that we throw TaskCanceledException on an actual request timeout. I think that's probably the actual root cause here.

If you think that your issue isn't actually being caused by timeouts though, feel free to open a new issue with all of the info you shared here.

 

 

10.

Interesting development. We have encountered this issue after adding to our test suite a test that spawns a process with powershell script (very simple one - writing 1 line to a file). ~30 seconds after this tests there is a failure in 1-3 random subsequent tests.

Because of that we thought that this might be related to processes inheriting handles from the parent one and it appears that we are right.

We have made a PR that unskipps those tests and switches the implementation of Process to the one that sets 'inherit handles' to 'false'. After doing so everything seems to be fine, it looks like the issue is with handle inheritance and the behavior of those handles after that child process ends.

The PR in question is here: ravendb/ravendb#8028

Our idea is that, at least as a workaround, it would be good to be able to set the

retVal = Interop.Kernel32.CreateProcess(
                                null,                // we don't need this since all the info is in commandLine
                                commandLine,         // pointer to the command line string
                                ref unused_SecAttrs, // address to process security attributes, we don't need to inherit the handle
                                ref unused_SecAttrs, // address to thread security attributes.
                                true,                // handle inheritance flag
                                creationFlags,       // creation flags
                                (IntPtr)environmentBlockPtr, // pointer to new environment block
                                workingDirectory,    // pointer to current directory name
                                ref startupInfo,     // pointer to STARTUPINFO
                                ref processInfo      // pointer to PROCESS_INFORMATION
                            );

  

11.

That's very interesting -- thanks for sharing that info. I briefly looked into the possibility that this was related to socket handles a few weeks ago after observing similar behavior with the h2spec process spawned by the Kestrel tests. It's probably worth looking into in more detail if you're seeing the same behavior.

 

 

12.

Note that as of 3.0, socket handles are no longer inherited: #32903

 

 

13.

that would probably solve the issue, but:

  1. Can this be applied to 2.1?

or

  1. Can you add private bool _inheritHandles = true field to Process.cs that we would be able to set via reflection to false at least?

 

 

14.

If that actually "fixes" it, I have trouble believing it's anything other than masking a symptom, and we should actually find the root cause and fix that.

Unless we believe the root cause really is that the socket handle is getting inherited and somehow that child process then closing is causing problems for the socket? In which case we should be able to repro that pretty easily, and if that's actually the problem, then I agree it would be a candidate for back porting.

 

 

15.

Fixing the root cause would be best, but till then could you consider applying point 2. so we can workaround this?

 

 

16.

Applying it where... as a servicing fix to 2.1? That's unlikely.

 

 

17.

Adding that private field in Process.cs (probably Process.Windows.cs) and using it instead of

would be a problem?

 

 

18.

The bar for servicing fixes is very high. You're suggesting adding unexposed, unvalidated, functionality-accessible-only-via-reflection-and-that-we-encourage-developers-to-use-via-reflection as a servicing fix. Hence "unlikely". If you need it immediately, you can of course make the change in your own local fork.

Let's get to the bottom of the problem and then figure out the right fix.

 

 

19.

I agree with @stephentoub.
I feel we are getting closer. @rmkerr invested already weeks of his time trying to get to the bottom of this (incl. extensive discussion with winsock team in Windows). Let's see if this new information can help root cause it and address the root cause.
If it doesn't help too much, we can think about either porting the change #32903 to 2.1, or doing something to enable people work around the suspectible root cause.

 

 

20.

I've been trying to build a repro that launches a process and then has several sockets sending and receiving data, but I have not been able to force the issue to occur. @ppekrol, would it be possible to set up a project with just the affected test and the code you're using to spawn the process?

If not I'll put some more time into the repro when I'm back in the office next week.

 

 

 

21.

Hi @rmkerr
Apologies for the delay, I really doubt that this would be doable. We already spend significant amount of hours to isolate the issue, create a smaller subset of tests, but for an unknown reason, it only appears for us on a larger set of tests e.g. our full suite. I do not have an idea what else could be done to isolate this. We tried moving that from SlowTests project (takes around 20 min to complete) to FastsTests (around 90 seconds), but it does not reproduce then.

 

 

 

 

22.

No problem @ppekrol. The info you shared is still valuable. I'm back in the office today, and will spend some time trying to cut down the size of the repro.

 

 

 

23.

I'm able to reproduce the behavior described by @ppekrol with a modified version of the kestrel test suite.

To start, I disabled all of the tests except for the H2Spec tests (which are launches as a separate process) andConnectionNotClosedWhenClientSatisfiesMinimumDataRateGivenLargeResponseHeaders, which was the test that I observed failing the most frequently. In order to reproduce the issue more consistently, I set up the ConnectionNotClosed... test to run five times.

I was able to further minimize the repro by replacing the H2Spec tests with the following code:

[Fact]
public void LaunchProcess()
{
    for (int i = 0; i < 100; ++i)
    {
        //Thread.Sleep(100);
        Process.Start("Powershell.exe", @"""C:\Tools\HelloWorld.ps1""").WaitForExit();
    }
}

The targeted script just prints "hello world" to the console.

When running the test suites after this modification, I was able to reproduce the failure on 6 out of 10 runs. After replacing Process.Start with Thread.Sleep (as commented out above), the tests passed on 10 out of 10 runs.

Obviously this is pretty rough testing, and there isn't a lot of data. But I do think the behavior is interesting.

 

转载于:https://www.cnblogs.com/panpanwelcome/p/10329331.html

PS C:\Users\Administrator> # 1. 修复模块路径问题 PS C:\Users\Administrator> $moduleBase = "E:\PowerShellModules\PSHttpClient" PS C:\Users\Administrator> $modulePath = "$moduleBase\1.2.1" PS C:\Users\Administrator> $moduleFile = "$modulePath\PSHttpClient.psm1" PS C:\Users\Administrator> $manifestFile = "$modulePath\PSHttpClient.psd1" PS C:\Users\Administrator> PS C:\Users\Administrator> # 确保目录存在 PS C:\Users\Administrator> New-Item -Path $modulePath -ItemType Directory -Force | Out-Null PS C:\Users\Administrator> PS C:\Users\Administrator> # 2. 创建完全修复的模块文件 PS C:\Users\Administrator> @' >> function Invoke-EnhancedCurlRequest { >> [CmdletBinding()] >> param( >> [Parameter(Mandatory=$true)] >> [string]$Uri, >> [ValidateSet('GET','POST','PUT','DELETE','PATCH','HEAD','OPTIONS')] >> [string]$Method = 'GET', >> [hashtable]$Headers = @{}, >> [object]$Body, >> [int]$Timeout = 30, >> [switch]$SkipCertificateCheck, >> [switch]$UseGzipCompression, >> [switch]$EnableHttp2 >> ) >> >> # 错误类型定义 >> $connectionErrors = @( >> [System.Net.Http.HttpRequestException], >> [System.Net.WebException], >> [System.Net.Sockets.SocketException], >> [System.Threading.Tasks.TaskCanceledException] >> ) >> >> $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() >> >> try { >> # 关键修复1:显式加载必需程序集 >> Add-Type -AssemblyName System.Net.Http -ErrorAction Stop >> Add-Type -AssemblyName System.Collections -ErrorAction Stop >> Add-Type -AssemblyName System.Runtime -ErrorAction Stop >> >> # 关键修复2:扩展TLS兼容性 >> $tlsSettings = [System.Net.SecurityProtocolType]::Tls12 >> $availableProtocols = [enum]::GetValues([System.Net.SecurityProtocolType]) >> >> if ($availableProtocols -contains [System.Net.SecurityProtocolType]::Tls13) { >> $tlsSettings = $tlsSettings -bor [System.Net.SecurityProtocolType]::Tls13 >> } >> if ($availableProtocols -contains [System.Net.SecurityProtocolType]::Tls11) { >> $tlsSettings = $tlsSettings -bor [System.Net.SecurityProtocolType]::Tls11 >> } >> if ($availableProtocols -contains [System.Net.SecurityProtocolType]::Tls) { >> $tlsSettings = $tlsSettings -bor [System.Net.SecurityProtocolType]::Tls >> } >> if ($availableProtocols -contains [System.Net.SecurityProtocolType]::Ssl3) { >> $tlsSettings = $tlsSettings -bor [System.Net.SecurityProtocolType]::Ssl3 >> } >> >> [System.Net.ServicePointManager]::SecurityProtocol = $tlsSettings >> >> # 创建HttpClientHandler >> $handler = New-Object System.Net.Http.HttpClientHandler >> >> # 关键修复3:增强证书验证 >> if ($SkipCertificateCheck) { >> $handler.ServerCertificateCustomValidationCallback = { >> param($sender, $cert, $chain, $errors) >> return $true >> } >> } >> >> if ($UseGzipCompression) { >> $handler.AutomaticDecompression = [System.Net.DecompressionMethods]::GZip >> } >> >> # 创建HttpClient >> $client = New-Object System.Net.Http.HttpClient($handler) >> $client.Timeout = [System.TimeSpan]::FromSeconds($Timeout) >> if ($EnableHttp2) { >> $client.DefaultRequestVersion = [System.Net.HttpVersion]::Version20 >> } >> >> # 创建请求消息 >> $request = New-Object System.Net.Http.HttpRequestMessage([System.Net.Http.HttpMethod]::$Method, $Uri) >> >> # 设置User-Agent >> if (-not $Headers.ContainsKey("User-Agent")) { >> $request.Headers.Add("User-Agent", "PowerShell-HTTP-Client/1.0") >> } >> >> # 添加自定义请求头 >> foreach ($key in $Headers.Keys) { >> $request.Headers.TryAddWithoutValidation($key, $Headers[$key]) | Out-Null >> } >> >> # 关键修复4:表单处理(保持之前成功的实现) >> if ($null -ne $Body) { >> $content = $null >> >> if ($Body -is [System.Collections.IDictionary]) { >> $formContent = [System.Net.Http.MultipartFormDataContent]::new() >> >> foreach ($key in $Body.Keys) { >> $value = $Body[$key] >> >> if ($value -is [System.IO.FileInfo]) { >> $fileStream = [System.IO.File]::OpenRead($value.FullName) >> $fileContent = New-Object System.Net.Http.StreamContent($fileStream) >> $formContent.Add($fileContent, $key, $value.Name) >> } >> elseif ($value -is [array]) { >> foreach ($item in $value) { >> $stringContent = New-Object System.Net.Http.StringContent($item.ToString()) >> $formContent.Add($stringContent, $key) >> } >> } >> else { >> $stringContent = New-Object System.Net.Http.StringContent($value.ToString()) >> $formContent.Add($stringContent, $key) >> } >> } >> >> $content = $formContent >> } >> elseif ($Body -is [string] -and $Body.StartsWith("{") -and $Body.EndsWith("}")) { >> $content = New-Object System.Net.Http.StringContent($Body, [System.Text.Encoding]::UTF8) >> $content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("application/json") >> } >> elseif ($Body -is [string]) { >> $content = New-Object System.Net.Http.StringContent($Body, [System.Text.Encoding]::UTF8) >> $content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("text/plain") >> } >> elseif ($Body -is [byte[]]) { >> $content = New-Object System.Net.Http.ByteArrayContent($Body) >> } >> else { >> $content = New-Object System.Net.Http.StringContent($Body.ToString(), [System.Text.Encoding]::UTF8) >> } >> >> $request.Content = $content >> } >> >> # 关键修复5:重构异步调用(解决ScriptHalted问题) >> try { >> # 创建异步任务 >> $task = $client.SendAsync($request) >> >> # 使用更可靠的异步等待方式 >> $asyncResult = $task.GetAwaiter().GetResult() >> >> # 检查任务状态 >> if ($task.IsFaulted) { >> throw $task.Exception.InnerException >> } >> >> if ($task.IsCanceled) { >> throw [System.Threading.Tasks.TaskCanceledException]::new("Request was canceled") >> } >> >> $response = $asyncResult >> >> # 检查响应状态 >> if (-not $response.IsSuccessStatusCode) { >> $errorResponse = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult() >> throw [System.Net.Http.HttpRequestException]::new( >> "HTTP error: $([int]$response.StatusCode) $($response.ReasonPhrase). Response: $errorResponse" >> ) >> } >> } >> catch [System.AggregateException] { >> throw $_.InnerException >> } >> >> # 关键修复6:空值检查 >> if ($null -eq $response) { >> throw [System.Net.WebException]::new("Response is null") >> } >> >> # 处理响应 >> $responseContent = $null >> if ($null -ne $response.Content) { >> $responseContent = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult() >> } >> >> # 返回结果对象 >> [PSCustomObject]@{ >> StatusCode = [int]$response.StatusCode >> StatusMessage = $response.ReasonPhrase >> Content = $responseContent >> Headers = $response.Headers >> Technology = "HttpClient (.NET)" >> Protocol = $response.Version.ToString() >> ElapsedMs = $stopwatch.ElapsedMilliseconds >> } >> } >> catch { >> $statusCode = 500 >> $errorMsg = $_.Exception.Message >> $exceptionType = $_.Exception.GetType().Name >> >> # 识别特定错误 >> if ($_.Exception -is [System.Net.Http.HttpRequestException]) { >> $statusCode = 503 >> $errorMsg = "HTTP Request Error: $errorMsg" >> } >> elseif ($_.Exception -is [System.Net.WebException]) { >> $statusCode = 503 >> $errorMsg = "Web Exception: $errorMsg" >> } >> elseif ($_.Exception -is [System.Net.Sockets.SocketException]) { >> $statusCode = 503 >> $errorMsg = "Network Error: $errorMsg" >> } >> elseif ($_.Exception -is [System.Threading.Tasks.TaskCanceledException]) { >> $statusCode = 504 >> $errorMsg = "Request Timeout: $errorMsg" >> } >> elseif ($_.Exception -is [System.Security.Authentication.AuthenticationException]) { >> $statusCode = 495 >> $errorMsg = "SSL/TLS Handshake Failed: $errorMsg" >> } >> >> # 返回错误对象 >> [PSCustomObject]@{ >> StatusCode = $statusCode >> StatusMessage = "Request Failed" >> Error = $errorMsg >> ExceptionType = $exceptionType >> StackTrace = $_.ScriptStackTrace >> } >> } >> finally { >> $stopwatch.Stop() >> if ($client) { $client.Dispose() } >> if ($handler) { $handler.Dispose() } >> } >> } >> >> Export-ModuleMember -Function Invoke-EnhancedCurlRequest >> '@ | Out-File $moduleFile -Encoding UTF8 -Force >> PS C:\Users\Administrator> # 3. 创建模块清单 PS C:\Users\Administrator> @" >> @{ >> RootModule = 'PSHttpClient.psm1' >> ModuleVersion = '1.2.1' >> GUID = '$([guid]::NewGuid().ToString())' >> Author = 'PowerShell User' >> CompanyName = 'N/A' >> Copyright = '(c) $(Get-Date -Format yyyy). All rights reserved.' >> Description = 'Enhanced HTTP client for PowerShell' >> PowerShellVersion = '5.1' >> FunctionsToExport = @('Invoke-EnhancedCurlRequest') >> RequiredAssemblies = @('System.Net.Http') >> } >> "@ | Out-File $manifestFile -Encoding UTF8 -Force >> PS C:\Users\Administrator> # 4. 配置模块路径 PS C:\Users\Administrator> $env:PSModulePath = "$moduleBase;$env:PSModulePath" PS C:\Users\Administrator> [Environment]::SetEnvironmentVariable('PSModulePath', $env:PSModulePath, 'User') PS C:\Users\Administrator> PS C:\Users\Administrator> # 5. 创建全局http函数 PS C:\Users\Administrator> function global:http { >> param( >> [Parameter(Mandatory=$true)] >> [string]$Uri, >> [ValidateSet('GET','POST','PUT','DELETE','PATCH','HEAD','OPTIONS')] >> [string]$Method = 'GET', >> [object]$Body, >> [hashtable]$Headers, >> [int]$Timeout, >> [switch]$SkipCertificateCheck, >> [switch]$UseGzipCompression, >> [switch]$EnableHttp2 >> ) >> >> $params = @{ >> Uri = $Uri >> Method = $Method >> } >> >> if ($PSBoundParameters.ContainsKey('Body')) { $params['Body'] = $Body } >> if ($PSBoundParameters.ContainsKey('Headers')) { $params['Headers'] = $Headers } >> if ($PSBoundParameters.ContainsKey('Timeout')) { $params['Timeout'] = $Timeout } >> if ($SkipCertificateCheck) { $params['SkipCertificateCheck'] = $true } >> if ($UseGzipCompression) { $params['UseGzipCompression'] = $true } >> if ($EnableHttp2) { $params['EnableHttp2'] = $true } >> >> Invoke-EnhancedCurlRequest @params >> } >> PS C:\Users\Administrator> # 6. 导入模块(使用绝对路径) PS C:\Users\Administrator> Remove-Module PSHttpClient -ErrorAction SilentlyContinue PS C:\Users\Administrator> Import-Module $manifestFile -Force -ErrorAction Stop PS C:\Users\Administrator> PS C:\Users\Administrator> # 7. 运行最终测试 PS C:\Users\Administrator> Write-Host "`n=== 最终测试 ===" -ForegroundColor Cyan === 最终测试 === PS C:\Users\Administrator> PS C:\Users\Administrator> # 测试1:GET请求验证User-Agent PS C:\Users\Administrator> Write-Host "`n[测试1] GET请求 - 验证User-Agent" -ForegroundColor Yellow [测试1] GET请求 - 验证User-Agent PS C:\Users\Administrator> $getResult = http -Method GET -Uri "https://httpbin.org/user-agent" PS C:\Users\Administrator> $getResult | Format-List StatusCode, StatusMessage, Content, Technology, Protocol, ElapsedMs StatusCode : 200 StatusMessage : OK Content : { "user-agent": "PowerShell-HTTP-Client/1.0" } Technology : HttpClient (.NET) Protocol : 1.1 ElapsedMs : 1142 PS C:\Users\Administrator> PS C:\Users\Administrator> # 测试2:POST表单数据 PS C:\Users\Administrator> Write-Host "`n[测试2] POST表单数据" -ForegroundColor Yellow [测试2] POST表单数据 PS C:\Users\Administrator> $formData = @{ >> username = "admin" >> password = "P@ssw0rd!" >> role = "administrator" >> } >> $postResult = http -Method POST -Uri "https://httpbin.org/post" -Body $formData >> $postResult | Format-List StatusCode, StatusMessage, Content, Technology, Protocol, ElapsedMs >> StatusCode : 200 StatusMessage : OK Content : { "args": {}, "data": "", "files": {}, "form": { "password": "P@ssw0rd!", "role": "administrator", "username": "admin" }, "headers": { "Content-Length": "461", "Content-Type": "multipart/form-data; boundary=\"b57ffbcf-bd54-4bcd-8eae-749acf779889\"", "Host": "httpbin.org", "User-Agent": "PowerShell-HTTP-Client/1.0", "X-Amzn-Trace-Id": "Root=1-68a0a9cf-5d1cc77c0569b5e70210eb9c" }, "json": null, "origin": "112.40.123.16", "url": "https://httpbin.org/post" } Technology : HttpClient (.NET) Protocol : 1.1 ElapsedMs : 1379 PS C:\Users\Administrator> # 测试3:自签名证书验证 PS C:\Users\Administrator> Write-Host "`n[测试3] 自签名证书验证" -ForegroundColor Yellow [测试3] 自签名证书验证 PS C:\Users\Administrator> $sslResult = http -Method GET -Uri "https://self-signed.badssl.com" -SkipCertificateCheck PS C:\Users\Administrator> $sslResult | Format-List StatusCode, StatusMessage, Content, Technology, Protocol, ElapsedMs StatusCode : 500 StatusMessage : Request Failed PS C:\Users\Administrator> PS C:\Users\Administrator> # 验证结果 PS C:\Users\Administrator> if ($postResult.StatusCode -eq 200 -and $postResult.Content) { >> $json = $postResult.Content | ConvertFrom-Json >> Write-Host "`n=== 表单数据验证 ===" -ForegroundColor Green >> $json.form >> } else { >> Write-Host "`nPOST请求失败!详情:" -ForegroundColor Red >> $postResult | Format-List * >> } >> === 表单数据验证 === password role username -------- ---- -------- P@ssw0rd! administrator admin PS C:\Users\Administrator> if ($sslResult.StatusCode -eq 200) { >> Write-Host "`n自签名证书验证成功!" -ForegroundColor Green >> } else { >> Write-Host "`n自签名证书验证失败!详情:" -ForegroundColor Red >> $sslResult | Format-List * >> } >> 自签名证书验证失败!详情: StatusCode : 500 StatusMessage : Request Failed Error : 使用“0”个参数调用“GetResult”时发生异常:“发送请求时出错。” ExceptionType : MethodInvocationException StackTrace : 在 Invoke-EnhancedCurlRequest、E:\PowerShellModules\PSHttpClient\1.2.1\PSHttpClient.psm1 中: 第 118 行 在 global:http、<无文件> 中: 第 24 行 在 <ScriptBlock>、<无文件> 中: 第 1 行 PS C:\Users\Administrator> Write-Host "`n所有修复已完成!系统准备就绪`n" -ForegroundColor Green 所有修复已完成!系统准备就绪
最新发布
08-17
PS C:\Users\Administrator> # 加载必要程序集 PS C:\Users\Administrator> Add-Type -AssemblyName System.Net.Http -ErrorAction Stop PS C:\Users\Administrator> PS C:\Users\Administrator> function Invoke-EnhancedCurlRequest { >> [CmdletBinding()] >> param( >> [Parameter(Mandatory=$true)] >> [string]$Uri, >> [ValidateSet('GET','POST','PUT','DELETE','PATCH','HEAD','OPTIONS')] >> [string]$Method = 'GET', >> [hashtable]$Headers = @{}, >> [object]$Body, >> [int]$Timeout = 30, >> [switch]$SkipCertificateCheck, >> [switch]$UseGzipCompression >> ) >> >> $resultTemplate = [PSCustomObject]@{ >> StatusCode = 0 >> StatusMessage = "NotExecuted" >> Headers = [ordered]@{} >> Content = $null >> IsSuccess = $false >> Technology = "None" >> ErrorMessage = $null >> ElapsedMs = 0 >> } >> >> $timer = [System.Diagnostics.Stopwatch]::StartNew() >> $result = $resultTemplate.PSObject.Copy() >> $result.Technology = "HttpClient" >> >> $handler = $null >> $client = $null >> $request = $null >> $response = $null >> >> try { >> # 创建 HttpClientHandler >> $handler = New-Object System.Net.Http.HttpClientHandler >> >> # 修复证书验证 - 兼容所有 .NET 版本 >> if ($SkipCertificateCheck) { >> # 方法1: 使用反射设置回调(兼容旧版 .NET) >> $handlerType = $handler.GetType() >> $prop = $handlerType.GetProperty("ServerCertificateCustomValidationCallback") >> >> if ($null -ne $prop) { >> $callback = { >> param($sender, $cert, $chain, $sslPolicyErrors) >> return $true >> } >> $prop.SetValue($handler, $callback) >> } >> # 方法2: 使用全局设置(备用方案) >> else { >> $originalCallback = [System.Net.ServicePointManager]::ServerCertificateValidationCallback >> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { >> param($sender, $cert, $chain, $sslPolicyErrors) >> return $true >> } >> } >> } >> >> if ($UseGzipCompression) { >> $handler.AutomaticDecompression = [System.Net.DecompressionMethods]::GZip >> } >> >> # 创建 HttpClient >> $client = New-Object System.Net.Http.HttpClient($handler) >> $client.Timeout = [System.TimeSpan]::FromSeconds($Timeout) >> >> # 创建请求方法对象 >> $httpMethod = [System.Net.Http.HttpMethod]::new($Method) >> if (-not $httpMethod) { >> $httpMethod = New-Object System.Net.Http.HttpMethod($Method) >> } >> >> # 创建请求消息 >> $request = New-Object System.Net.Http.HttpRequestMessage($httpMethod, $Uri) >> >> # 添加默认 User-Agent >> if (-not $Headers.ContainsKey('User-Agent')) { >> $request.Headers.TryAddWithoutValidation("User-Agent", "PowerShell-HTTPClient/1.0") >> } >> >> # 添加自定义头 >> foreach ($key in $Headers.Keys) { >> if (-not $request.Headers.TryAddWithoutValidation($key, $Headers[$key])) { >> if (-not $request.Content) { >> $request.Content = New-Object System.Net.Http.StringContent("") >> } >> $request.Content.Headers.TryAddWithoutValidation($key, $Headers[$key]) >> } >> } >> >> # 处理请求体 >> if ($Body -and @('POST','PUT','PATCH') -contains $Method) { >> if ($Body -is [byte[]]) { >> $request.Content = New-Object System.Net.Http.ByteArrayContent($Body) >> } >> elseif ($Body -is [hashtable] -or $Body -is [System.Collections.IDictionary]) { >> $jsonBody = $Body | ConvertTo-Json -Depth 5 -Compress >> $request.Content = New-Object System.Net.Http.StringContent( >> $jsonBody, >> [System.Text.Encoding]::UTF8, >> "application/json" >> ) >> } >> elseif ($Body -is [string]) { >> $request.Content = New-Object System.Net.Http.StringContent( >> $Body, >> [System.Text.Encoding]::UTF8 >> ) >> } >> else { >> throw "Unsupported body type: $($Body.GetType().Name)" >> } >> } >> >> # 发送请求 >> $response = $client.SendAsync($request).GetAwaiter().GetResult() >> >> # 解析响应 >> $result.StatusCode = [int]$response.StatusCode >> $result.StatusMessage = $response.ReasonPhrase >> $result.IsSuccess = $response.IsSuccessStatusCode >> >> $result.Headers = [ordered]@{} >> foreach ($header in $response.Headers) { >> $result.Headers[$header.Key] = $header.Value -join ", " >> } >> >> foreach ($header in $response.Content.Headers) { >> $result.Headers[$header.Key] = $header.Value -join ", " >> } >> >> $result.Content = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult() >> return $result >> } >> catch [System.Threading.Tasks.TaskCanceledException] { >> $result.ErrorMessage = "Request timed out after $Timeout seconds" >> $result.StatusCode = 408 >> $result.StatusMessage = "Timeout" >> return $result >> } >> catch [System.Net.Http.HttpRequestException] { >> # 处理特定HTTP请求异常 >> $ex = $_.Exception >> $result.ErrorMessage = $ex.ToString() >> >> if ($ex.InnerException -is [System.Net.Sockets.SocketException]) { >> $result.StatusCode = 0 >> $result.StatusMessage = "DNSResolutionFailed" >> } >> else { >> $result.StatusCode = 500 >> $result.StatusMessage = "HttpRequestError" >> } >> >> return $result >> } >> catch { >> # 通用错误处理 >> $result.ErrorMessage = $_.Exception.ToString() >> $result.StatusCode = 500 >> $result.StatusMessage = "InternalError" >> return $result >> } >> finally { >> $timer.Stop() >> $result.ElapsedMs = $timer.ElapsedMilliseconds >> >> # 安全清理资源 >> if ($response -is [IDisposable]) { $response.Dispose() } >> if ($request -is [IDisposable]) { $request.Dispose() } >> if ($client -is [IDisposable]) { $client.Dispose() } >> if ($handler -is [IDisposable]) { $handler.Dispose() } >> >> # 恢复全局证书验证设置 >> if ($SkipCertificateCheck -and $null -ne $originalCallback) { >> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $originalCallback >> } >> } >> } >> PS C:\Users\Administrator> # 使用反射设置回调(兼容旧版 .NET) PS C:\Users\Administrator> $handlerType = $handler.GetType() 不能对 Null 值表达式调用方法。 所在位置 行:1 字符: 1 + $handlerType = $handler.GetType() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS C:\Users\Administrator> $prop = $handlerType.GetProperty("ServerCertificateCustomValidationCallback") 不能对 Null 值表达式调用方法。 所在位置 行:1 字符: 1 + $prop = $handlerType.GetProperty("ServerCertificateCustomValidationCa ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS C:\Users\Administrator> if ($null -ne $prop) { >> $callback = { param($sender, $cert, $chain, $sslPolicyErrors) return $true } >> $prop.SetValue($handler, $callback) >> } >> # 备用方案:全局设置 >> else { >> $originalCallback = [System.Net.ServicePointManager]::ServerCertificateValidationCallback >> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { >> param($sender, $cert, $chain, $sslPolicyErrors) return $true >> } >> } >> PS C:\Users\Administrator> catch [System.Net.Http.HttpRequestException] { >> $ex = $_.Exception >> if ($ex.InnerException -is [System.Net.Sockets.SocketException]) { >> $result.StatusCode = 0 >> $result.StatusMessage = "DNSResolutionFailed" >> } >> } >> catch : 无法将“catch”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正 确,然后再试一次。 所在位置 行:1 字符: 1 + catch [System.Net.Http.HttpRequestException] { + ~~~~~ + CategoryInfo : ObjectNotFound: (catch:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException PS C:\Users\Administrator> finally { >> # 恢复全局证书验证设置 >> if ($SkipCertificateCheck -and $null -ne $originalCallback) { >> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $originalCallback >> } >> } >> finally : 无法将“finally”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路 径正确,然后再试一次。 所在位置 行:1 字符: 1 + finally { + ~~~~~~~ + CategoryInfo : ObjectNotFound: (finally:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException PS C:\Users\Administrator> # 创建模块目录 PS C:\Users\Administrator> $moduleDir = "$env:ProgramFiles\WindowsPowerShell\Modules\PSHttpClient" PS C:\Users\Administrator> New-Item -Path $moduleDir -ItemType Directory -Force | Out-Null PS C:\Users\Administrator> PS C:\Users\Administrator> # 生成并保存模块文件 PS C:\Users\Administrator> $fixedModuleCode = @' >> # 将上面修复后的完整函数代码复制到这里 >> '@ >> PS C:\Users\Administrator> $fixedModuleCode | Out-File "$moduleDir\PSHttpClient.psm1" -Encoding UTF8 -Force PS C:\Users\Administrator> PS C:\Users\Administrator> # 重新加载模块 PS C:\Users\Administrator> Remove-Module PSHttpClient -ErrorAction SilentlyContinue PS C:\Users\Administrator> Import-Module PSHttpClient -Force -PassThru ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Script 1.3 PSHttpClient PS C:\Users\Administrator> PS C:\Users\Administrator> # 测试自签名证书网站 PS C:\Users\Administrator> $result = Invoke-EnhancedCurlRequest -Uri "https://self-signed.badssl.com/" -SkipCertificateCheck PS C:\Users\Administrator> PS C:\Users\Administrator> if ($result.IsSuccess) { >> Write-Host "✅ 自签名证书网站访问成功" -ForegroundColor Green >> } else { >> Write-Host "❌ 自签名证书网站访问失败: $($result.ErrorMessage)" -ForegroundColor Red >> } >> ❌ 自签名证书网站访问失败: System.Management.Automation.MethodInvocationException: 使用“2”个参数调用“SetValue”时发生异常:“类型“System.Management.Automation.ScriptBlock”的对象无法转换为类型“System.Func`5[System.Net.Http.HttpRequestMessage,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.X509Certificates.X509Chain,System.Net.Security.SslPolicyErrors,System.Boolean]”。” ---> System.ArgumentException: 类型“System.Management.Automation.ScriptBlock”的对象无法转换为类型“System.Func`5[System.Net.Http.HttpRequestMessage,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.X509Certificates.X509Chain,System.Net.Security.SslPolicyErrors,System.Boolean]”。 在 System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) 在 System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) 在 System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) 在 CallSite.Target(Closure , CallSite , Object , Object , Object ) --- 内部异常堆栈跟踪的结尾 ---System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception) 在 System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame) 在 System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) 在 System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) PS C:\Users\Administrator> # 测试无效域名 PS C:\Users\Administrator> $result = Invoke-EnhancedCurlRequest -Uri "https://invalid.domain.abc/" PS C:\Users\Administrator> PS C:\Users\Administrator> if ($result.StatusCode -eq 0) { >> Write-Host "✅ DNS错误处理成功" -ForegroundColor Green >> } else { >> Write-Host "❌ DNS错误处理失败: $($result.StatusCode)" -ForegroundColor Red >> } >> ❌ DNS错误处理失败: 500 PS C:\Users\Administrator>
08-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值