1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
23 using System.Collections.Generic;
24 using System.Diagnostics.CodeAnalysis;
27 using System.Net.Cache;
28 using System.Net.Http;
30 using System.Threading;
31 using System.Threading.Tasks;
33 namespace OpenTween.Connection
35 public static class Networking
37 public static TimeSpan DefaultTimeout { get; set; }
38 public static TimeSpan UploadImageTimeout { get; set; }
43 public static ProxyType ProxyType { get; private set; } = ProxyType.IE;
48 public static IWebProxy Proxy { get; private set; } = null;
51 /// OpenTween 内で共通して使用する HttpClient インスタンス
53 public static HttpClient Http => globalHttpClient;
56 /// pbs.twimg.com で IPv4 を強制的に使用する
58 public static bool ForceIPv4
63 if (forceIPv4 == value)
68 // Network.Http を再作成させる
69 OnWebProxyChanged(EventArgs.Empty);
73 private static bool IsWindows7
77 var os = Environment.OSVersion;
78 return os.Platform == PlatformID.Win32NT && os.Version.Major == 6 && os.Version.Minor == 1;
83 /// Webプロキシの設定が変更された場合に発生します
85 public static event EventHandler WebProxyChanged;
87 private static bool initialized = false;
88 private static HttpClient globalHttpClient;
89 private static bool forceIPv4 = false;
91 [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
94 DefaultTimeout = TimeSpan.FromSeconds(20);
95 UploadImageTimeout = TimeSpan.FromSeconds(60);
96 globalHttpClient = CreateHttpClient(new HttpClientHandler());
100 /// ネットワーク接続前に行う処理。起動時に一回だけ実行する必要があります。
102 public static void Initialize()
104 Networking.initialized = true;
106 ServicePointManager.Expect100Continue = false;
107 ServicePointManager.CheckCertificateRevocationList = true;
109 // Win7 では SystemDefault が SSL3.0 または TLS1.0 のため、明示的にバージョンを引き上げる必要がある
111 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
114 public static void SetWebProxy(ProxyType proxyType, string proxyAddress, int proxyPort,
115 string proxyUser, string proxyPassword)
123 case ProxyType.Specified:
124 proxy = new WebProxy(proxyAddress, proxyPort);
125 if (!string.IsNullOrEmpty(proxyUser) || !string.IsNullOrEmpty(proxyPassword))
126 proxy.Credentials = new NetworkCredential(proxyUser, proxyPassword);
130 proxy = WebRequest.GetSystemWebProxy();
134 Networking.ProxyType = proxyType;
135 Networking.Proxy = proxy;
137 NativeMethods.SetProxy(proxyType, proxyAddress, proxyPort);
139 OnWebProxyChanged(EventArgs.Empty);
143 /// OpenTween で必要な設定を施した HttpClientHandler インスタンスを生成します
145 [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
146 public static WebRequestHandler CreateHttpClientHandler()
148 var handler = new WebRequestHandler
150 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
153 if (Networking.Proxy != null)
155 handler.UseProxy = true;
156 handler.Proxy = Networking.Proxy;
160 handler.UseProxy = false;
167 /// OpenTween で必要な設定を施した HttpClient インスタンスを生成します
170 /// 通常は Networking.Http を使用すべきです。
171 /// このメソッドを使用する場合は、WebProxyChanged イベントが発生する度に HttpClient を生成し直すように実装してください。
173 [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
174 public static HttpClient CreateHttpClient(HttpMessageHandler handler)
178 client = new HttpClient(new ForceIPv4Handler(handler));
180 client = new HttpClient(handler);
182 client.Timeout = Networking.DefaultTimeout;
183 client.DefaultRequestHeaders.Add("User-Agent", Networking.GetUserAgentString());
188 public static string GetUserAgentString(bool fakeMSIE = false)
191 return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion + " (compatible; MSIE 10.0)";
193 return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion;
197 /// Initialize() メソッドが事前に呼ばれているか確認します
199 internal static void CheckInitialized()
201 if (!Networking.initialized)
202 throw new InvalidOperationException("Sequence error.(not initialized)");
205 [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
206 private static void OnWebProxyChanged(EventArgs e)
208 var newClient = Networking.CreateHttpClient(Networking.CreateHttpClientHandler());
209 var oldClient = Interlocked.Exchange(ref globalHttpClient, newClient);
212 WebProxyChanged?.Invoke(null, e);
215 private class ForceIPv4Handler : DelegatingHandler
217 private readonly IPAddress ipv4Address;
219 public ForceIPv4Handler(HttpMessageHandler innerHandler)
222 foreach (var address in Dns.GetHostAddresses("pbs.twimg.com"))
223 if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
224 this.ipv4Address = address;
227 protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
229 var requestUri = request.RequestUri;
230 if (requestUri.Host == "pbs.twimg.com")
232 var rewriteUriStr = requestUri.GetLeftPart(UriPartial.Scheme) + this.ipv4Address + requestUri.PathAndQuery;
233 request.RequestUri = new Uri(rewriteUriStr);
234 request.Headers.Host = "pbs.twimg.com";
237 return base.SendAsync(request, cancellationToken);
242 public enum ProxyType