.NET FrameworkでHttpClientを使う機会があった.何も気にせずに使い始めると通信にProxyの設定が適用される.
(2023.06.11 追記 下の例のアプリではHttpClientを1回しか使わないから問題ないが、短時間に複数回使用していく用途ではusingで囲うとリソースが枯渇するリスクが上がる。特に最新の.NET Coreや.NET 5.0以降の使用例ではDisposeせずに残している。記事内でKeepAliveなどを気にせずと書いているが、usingブロックから出るときにインスタンスが破棄されるため、KeepAliveなどが持続しないので注意。)
.NET Framework 4.5からの機能だとおもうが,HttpClientというものが使えるようになった.TcpClientでは気にする必要があるKeepAlive/Httpsなどの実装を気にせずに使用できるようだ.参考となるページはHttpClientクラスでWebページを取得するには?[C#、VB]であろうか.
(ただこの記事が書かれた時と状況が異なる点がSystem.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;の部分である.現在では脆弱性の懸念から,SSL 3やTLS1.1などが無効化され始めており,最初からTLS1.2でつなげに行かなければ失敗するサーバーが多くなっている点である.)
さて,外のサーバーに接続する場合には
HttpClientHandler handler = new HttpClientHandler();
handler.UseProxy = false;
もいらない(はずである).
しかし,ローカルにプロキシサーバーを設けている場合にはローカルアドレスに対してプロキシを使用しない設定にしても,http://192.168.1.1/などにリクエストを送ろうとすると,Windows の仕様(ほぼバグ)によりプロキシを経由しようとする.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// 取得したいWebページのURI
Uri webUri = new Uri("https://www.valuestar.work/");
// Call the method "GetWebPageAsync"
Task<string> webTask = GetWebPageAsync(webUri);
webTask.Wait(); // Mainメソッド内でawaitのかわりの待機
string result = webTask.Result; // content of the result
Console.WriteLine(result);
#if DEBUG
Console.ReadKey();
#endif
}
static async Task<string> GetWebPageAsync(Uri uri)
{
// Forced to use TLS1.2
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
// Setting: "Do not use proxy"
HttpClientHandler handler = new HttpClientHandler();
handler.UseProxy = false;
using (HttpClient client = new HttpClient(handler))
{
// setting timeout (optional)
client.Timeout = TimeSpan.FromSeconds(10.0);
try
{
// Main part of this function
return await client.GetStringAsync(uri);
}
catch (HttpRequestException e)
{
// Related to HTTP
Console.WriteLine("\nHTTP Exception");
Exception ex = e;
while (ex != null)
{
Console.WriteLine(ex.Message);
ex = ex.InnerException;
}
}
catch (TaskCanceledException e)
{
// Timeout
Console.WriteLine("\nTimeout");
Console.WriteLine(e.Message);
}
catch (Exception e)
{
// Others
Console.WriteLine(e.Message);
}
return null;
}
}
}
}
困った問題である.http://127.0.0.1/などはループバックなのにプロキシを経由されるというほぼバグ仕様によりデバッグ中に相当苦労させられた.