OSDN Git Service

使用されていないパラメータを削除 (IDE0060)
[opentween/open-tween.git] / OpenTween / Connection / Networking.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
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)
10 // any later version.
11 //
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
15 // for more details.
16 //
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.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Diagnostics.CodeAnalysis;
25 using System.Linq;
26 using System.Net;
27 using System.Net.Cache;
28 using System.Net.Http;
29 using System.Text;
30 using System.Threading;
31 using System.Threading.Tasks;
32
33 namespace OpenTween.Connection
34 {
35     public static class Networking
36     {
37         public static TimeSpan DefaultTimeout { get; set; }
38         public static TimeSpan UploadImageTimeout { get; set; }
39
40         /// <summary>
41         /// 通信に使用するプロキシの種類
42         /// </summary>
43         public static ProxyType ProxyType { get; private set; } = ProxyType.IE;
44
45         /// <summary>
46         /// 通信に使用するプロキシ
47         /// </summary>
48         public static IWebProxy Proxy { get; private set; } = null;
49
50         /// <summary>
51         /// OpenTween 内で共通して使用する HttpClient インスタンス
52         /// </summary>
53         public static HttpClient Http => globalHttpClient;
54
55         /// <summary>
56         /// pbs.twimg.com で IPv4 を強制的に使用する
57         /// </summary>
58         public static bool ForceIPv4
59         {
60             get => forceIPv4;
61             set
62             {
63                 if (forceIPv4 == value)
64                     return;
65
66                 forceIPv4 = value;
67
68                 // Network.Http を再作成させる
69                 OnWebProxyChanged(EventArgs.Empty);
70             }
71         }
72
73         private static bool IsWindows7
74         {
75             get
76             {
77                 var os = Environment.OSVersion;
78                 return os.Platform == PlatformID.Win32NT && os.Version.Major == 6 && os.Version.Minor == 1;
79             }
80         }
81
82         /// <summary>
83         /// Webプロキシの設定が変更された場合に発生します
84         /// </summary>
85         public static event EventHandler WebProxyChanged;
86
87         private static bool initialized = false;
88         private static HttpClient globalHttpClient;
89         private static bool forceIPv4 = false;
90
91         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
92         static Networking()
93         {
94             DefaultTimeout = TimeSpan.FromSeconds(20);
95             UploadImageTimeout = TimeSpan.FromSeconds(60);
96             globalHttpClient = CreateHttpClient(new HttpClientHandler());
97         }
98
99         /// <summary>
100         /// ネットワーク接続前に行う処理。起動時に一回だけ実行する必要があります。
101         /// </summary>
102         public static void Initialize()
103         {
104             Networking.initialized = true;
105
106             ServicePointManager.Expect100Continue = false;
107             ServicePointManager.CheckCertificateRevocationList = true;
108
109             // Win7 では SystemDefault が SSL3.0 または TLS1.0 のため、明示的にバージョンを引き上げる必要がある
110             if (IsWindows7)
111                 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
112         }
113
114         public static void SetWebProxy(ProxyType proxyType, string proxyAddress, int proxyPort,
115             string proxyUser, string proxyPassword)
116         {
117             IWebProxy proxy;
118             switch (proxyType)
119             {
120                 case ProxyType.None:
121                     proxy = null;
122                     break;
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);
127                     break;
128                 case ProxyType.IE:
129                 default:
130                     proxy = WebRequest.GetSystemWebProxy();
131                     break;
132             }
133
134             Networking.ProxyType = proxyType;
135             Networking.Proxy = proxy;
136
137             NativeMethods.SetProxy(proxyType, proxyAddress, proxyPort);
138
139             OnWebProxyChanged(EventArgs.Empty);
140         }
141
142         /// <summary>
143         /// OpenTween で必要な設定を施した HttpClientHandler インスタンスを生成します
144         /// </summary>
145         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
146         public static WebRequestHandler CreateHttpClientHandler()
147         {
148             var handler = new WebRequestHandler
149             {
150                 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
151             };
152
153             if (Networking.Proxy != null)
154             {
155                 handler.UseProxy = true;
156                 handler.Proxy = Networking.Proxy;
157             }
158             else
159             {
160                 handler.UseProxy = false;
161             }
162
163             return handler;
164         }
165
166         /// <summary>
167         /// OpenTween で必要な設定を施した HttpClient インスタンスを生成します
168         /// </summary>
169         /// <remarks>
170         /// 通常は Networking.Http を使用すべきです。
171         /// このメソッドを使用する場合は、WebProxyChanged イベントが発生する度に HttpClient を生成し直すように実装してください。
172         /// </remarks>
173         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
174         public static HttpClient CreateHttpClient(HttpMessageHandler handler)
175         {
176             HttpClient client;
177             if (ForceIPv4)
178                 client = new HttpClient(new ForceIPv4Handler(handler));
179             else
180                 client = new HttpClient(handler);
181
182             client.Timeout = Networking.DefaultTimeout;
183             client.DefaultRequestHeaders.Add("User-Agent", Networking.GetUserAgentString());
184
185             return client;
186         }
187
188         public static string GetUserAgentString(bool fakeMSIE = false)
189         {
190             if (fakeMSIE)
191                 return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion + " (compatible; MSIE 10.0)";
192             else
193                 return ApplicationSettings.AssemblyName + "/" + MyCommon.FileVersion;
194         }
195
196         /// <summary>
197         /// Initialize() メソッドが事前に呼ばれているか確認します
198         /// </summary>
199         internal static void CheckInitialized()
200         {
201             if (!Networking.initialized)
202                 throw new InvalidOperationException("Sequence error.(not initialized)");
203         }
204
205         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
206         private static void OnWebProxyChanged(EventArgs e)
207         {
208             var newClient = Networking.CreateHttpClient(Networking.CreateHttpClientHandler());
209             var oldClient = Interlocked.Exchange(ref globalHttpClient, newClient);
210             oldClient.Dispose();
211
212             WebProxyChanged?.Invoke(null, e);
213         }
214
215         private class ForceIPv4Handler : DelegatingHandler
216         {
217             private readonly IPAddress ipv4Address;
218
219             public ForceIPv4Handler(HttpMessageHandler innerHandler)
220                 : base(innerHandler)
221             {
222                 foreach (var address in Dns.GetHostAddresses("pbs.twimg.com"))
223                     if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
224                         this.ipv4Address = address;
225             }
226
227             protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
228             {
229                 var requestUri = request.RequestUri;
230                 if (requestUri.Host == "pbs.twimg.com")
231                 {
232                     var rewriteUriStr = requestUri.GetLeftPart(UriPartial.Scheme) + this.ipv4Address + requestUri.PathAndQuery;
233                     request.RequestUri = new Uri(rewriteUriStr);
234                     request.Headers.Host = "pbs.twimg.com";
235                 }
236
237                 return base.SendAsync(request, cancellationToken);
238             }
239         }
240     }
241
242     public enum ProxyType
243     {
244         None,
245         IE,
246         Specified,
247     }
248 }