// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
+#nullable enable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
+using System.Net.Cache;
using System.Net.Http;
using System.Text;
using System.Threading;
{
public static TimeSpan DefaultTimeout { get; set; }
+ public static TimeSpan UploadImageTimeout { get; set; }
+
/// <summary>
/// 通信に使用するプロキシの種類
/// </summary>
- public static ProxyType ProxyType
- {
- get { return proxyType; }
- }
+ public static ProxyType ProxyType { get; private set; } = ProxyType.IE;
/// <summary>
/// 通信に使用するプロキシ
/// </summary>
- public static IWebProxy Proxy
- {
- get { return proxy; }
- }
+ public static IWebProxy? Proxy { get; private set; } = null;
/// <summary>
/// OpenTween 内で共通して使用する HttpClient インスタンス
/// </summary>
- public static HttpClient Http
+ public static HttpClient Http => globalHttpClient;
+
+ /// <summary>
+ /// pbs.twimg.com で IPv4 を強制的に使用する
+ /// </summary>
+ public static bool ForceIPv4
{
- get { return globalHttpClient; }
+ get => forceIPv4;
+ set
+ {
+ if (forceIPv4 == value)
+ return;
+
+ forceIPv4 = value;
+
+ // Network.Http を再作成させる
+ OnWebProxyChanged(EventArgs.Empty);
+ }
}
/// <summary>
/// Webプロキシの設定が変更された場合に発生します
/// </summary>
- public static event EventHandler WebProxyChanged;
+ public static event EventHandler? WebProxyChanged;
private static bool initialized = false;
private static HttpClient globalHttpClient;
- private static ProxyType proxyType = ProxyType.IE;
- private static IWebProxy proxy = null;
+ private static bool forceIPv4 = false;
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
static Networking()
{
DefaultTimeout = TimeSpan.FromSeconds(20);
+ UploadImageTimeout = TimeSpan.FromSeconds(60);
globalHttpClient = CreateHttpClient(new HttpClientHandler());
}
Networking.initialized = true;
ServicePointManager.Expect100Continue = false;
+ ServicePointManager.CheckCertificateRevocationList = true;
}
- public static void SetWebProxy(ProxyType proxyType, string proxyAddress, int proxyPort,
- string proxyUser, string proxyPassword)
+ public static void SetWebProxy(
+ ProxyType proxyType,
+ string proxyAddress,
+ int proxyPort,
+ string proxyUser,
+ string proxyPassword)
{
- IWebProxy proxy;
+ IWebProxy? proxy;
switch (proxyType)
{
case ProxyType.None:
break;
case ProxyType.Specified:
proxy = new WebProxy(proxyAddress, proxyPort);
- if (!string.IsNullOrEmpty(proxyUser) || !string.IsNullOrEmpty(proxyPassword))
+ if (!MyCommon.IsNullOrEmpty(proxyUser) || !MyCommon.IsNullOrEmpty(proxyPassword))
proxy.Credentials = new NetworkCredential(proxyUser, proxyPassword);
break;
case ProxyType.IE:
break;
}
- Networking.proxyType = proxyType;
- Networking.proxy = proxy;
+ Networking.ProxyType = proxyType;
+ Networking.Proxy = proxy;
- NativeMethods.SetProxy(proxyType, proxyAddress, proxyPort, proxyUser, proxyPassword);
+ NativeMethods.SetProxy(proxyType, proxyAddress, proxyPort);
OnWebProxyChanged(EventArgs.Empty);
}
/// <summary>
- /// プロキシ等の設定を施した HttpClient インスタンスを生成します
+ /// OpenTween で必要な設定を施した HttpClientHandler インスタンスを生成します
/// </summary>
- /// <remarks>
- /// 通常は Networking.Http を使用すべきです。
- /// このメソッドを使用する場合は、WebProxyChanged イベントが発生する度に HttpClient を生成し直すように実装してください。
- /// </remarks>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
- public static HttpClient CreateHttpClient(HttpClientHandler handler)
+ public static WebRequestHandler CreateHttpClientHandler()
{
+ var handler = new WebRequestHandler
+ {
+ UseCookies = false,
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ };
+
if (Networking.Proxy != null)
{
handler.UseProxy = true;
handler.UseProxy = false;
}
- var client = new HttpClient(handler);
+ return handler;
+ }
+
+ /// <summary>
+ /// OpenTween で必要な設定を施した HttpClient インスタンスを生成します
+ /// </summary>
+ /// <remarks>
+ /// 通常は Networking.Http を使用すべきです。
+ /// このメソッドを使用する場合は、WebProxyChanged イベントが発生する度に HttpClient を生成し直すように実装してください。
+ /// </remarks>
+ [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+ public static HttpClient CreateHttpClient(HttpMessageHandler handler)
+ {
+ HttpClient client;
+ if (ForceIPv4)
+ client = new HttpClient(new ForceIPv4Handler(handler));
+ else
+ client = new HttpClient(handler);
+
client.Timeout = Networking.DefaultTimeout;
- client.DefaultRequestHeaders.Add("User-Agent", Networking.GetUserAgentString());
return client;
}
public static string GetUserAgentString(bool fakeMSIE = false)
{
if (fakeMSIE)
- return MyCommon.GetAssemblyName() + "/" + MyCommon.FileVersion + " (compatible; MSIE 10.0)";
+ return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion + " (compatible; MSIE 10.0)";
else
- return MyCommon.GetAssemblyName() + "/" + MyCommon.FileVersion;
+ return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion;
}
/// <summary>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
private static void OnWebProxyChanged(EventArgs e)
{
- var newClient = Networking.CreateHttpClient(new HttpClientHandler());
+ var newClient = Networking.CreateHttpClient(Networking.CreateHttpClientHandler());
var oldClient = Interlocked.Exchange(ref globalHttpClient, newClient);
oldClient.Dispose();
- if (WebProxyChanged != null)
- WebProxyChanged(null, e);
+ WebProxyChanged?.Invoke(null, e);
+ }
+
+ private class ForceIPv4Handler : DelegatingHandler
+ {
+ private readonly IPAddress? ipv4Address;
+
+ public ForceIPv4Handler(HttpMessageHandler innerHandler)
+ : base(innerHandler)
+ {
+ foreach (var address in Dns.GetHostAddresses("pbs.twimg.com"))
+ {
+ if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
+ this.ipv4Address = address;
+ }
+ }
+
+ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ if (this.ipv4Address != null)
+ {
+ var requestUri = request.RequestUri;
+ if (requestUri.Host == "pbs.twimg.com")
+ {
+ var rewriteUriStr = requestUri.GetLeftPart(UriPartial.Scheme) + this.ipv4Address + requestUri.PathAndQuery;
+ request.RequestUri = new Uri(rewriteUriStr);
+ request.Headers.Host = "pbs.twimg.com";
+ }
+ }
+
+ return base.SendAsync(request, cancellationToken);
+ }
}
}