OSDN Git Service

67436322c9629a6058c844f3d863fc1f42f5e04c
[wptscs/wpts.git] / Wptscs / Utilities / AppConfigWebProxy.cs
1 // ================================================================================================
2 // <summary>
3 //      アプリケーション設定値を用いてウェブアクセスするプロキシクラスソース</summary>
4 //
5 // <copyright file="AppConfigWebProxy.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Wptscs.Utilities
12 {
13     using System;
14     using System.IO;
15     using System.Net;
16     using System.Reflection;
17     using System.Threading;
18     using Honememo.Utilities;
19     using Honememo.Wptscs.Properties;
20     
21     /// <summary>
22     /// アプリケーション設定値を用いてウェブアクセスするプロキシクラスです。
23     /// </summary>
24     public class AppConfigWebProxy : IWebProxy
25     {
26         #region private変数
27
28         /// <summary>
29         /// このプロキシで使用するUserAgent。
30         /// </summary>
31         private string userAgent;
32
33         /// <summary>
34         /// このプロキシで使用するReferer。
35         /// </summary>
36         private string referer;
37
38         #endregion
39
40         #region インタフェース実装プロパティ
41
42         /// <summary>
43         /// このプロキシで使用するUserAgent。
44         /// </summary>
45         /// <remarks>アプリ設定値→プロパティ→アプリデフォルト値を生成 の順に見つけたものを返す。</remarks>
46         public string UserAgent
47         {
48             get
49             {
50                 // アプリ設定値を確認
51                 string ua = Settings.Default.UserAgent;
52                 if (!String.IsNullOrEmpty(ua))
53                 {
54                     return ua;
55                 }
56
57                 // 存在しない場合、このプロパティの値を確認
58                 if (this.userAgent != null)
59                 {
60                     return this.userAgent;
61                 }
62
63                 // いずれも存在しない場合は、デフォルトの値を生成して返す
64                 Version ver = Assembly.GetExecutingAssembly().GetName().Version;
65                 return String.Format(Settings.Default.DefaultUserAgent, ver.Major, ver.Minor);
66             }
67
68             set
69             {
70                 this.userAgent = value;
71             }
72         }
73
74         /// <summary>
75         /// このプロキシで使用するReferer。
76         /// </summary>
77         /// <remarks>アプリ設定値→プロパティ の順に見つけたものを返す。</remarks>
78         public string Referer
79         {
80             get
81             {
82                 // アプリ設定値を確認
83                 string r = Settings.Default.Referer;
84                 if (String.IsNullOrEmpty(r))
85                 {
86                     // 存在しない場合、このプロパティの値を返す
87                     r = this.referer;
88                 }
89
90                 return StringUtils.DefaultString(r);
91             }
92
93             set
94             {
95                 this.referer = value;
96             }
97         }
98
99         #endregion
100
101         #region インタフェース実装メソッド
102
103         /// <summary>
104         /// 指定されたURIの情報をストリームで取得。
105         /// </summary>
106         /// <param name="uri">取得対象のURI。</param>
107         /// <returns>取得したストリーム。使用後は必ずクローズすること。</returns>
108         /// <remarks>
109         /// 通信エラー等の場合、アプリケーション設定に指定されている回数リトライする。
110         /// それでも取得できない場合は例外を投げる。
111         /// </remarks>
112         public Stream GetStream(Uri uri)
113         {
114             // 実際の処理はサブメソッドで行い、このメソッドでは通信エラー時のリトライを行う
115             int retry = Settings.Default.MaxConnectRetries;
116             int wait = Settings.Default.ConnectRetryTime;
117             while (true)
118             {
119                 try
120                 {
121                     return this.GetStreamBody(uri);
122                 }
123                 catch (WebException e)
124                 {
125                     // 通信エラーの場合、指定回数までリトライを試みる
126                     if (--retry < 0 || !this.IsRetryable(e))
127                     {
128                         // リトライ回数を消化、またはリトライしても意味が無い場合、そのまま例外を投げる
129                         throw e;
130                     }
131
132                     // 時間を置いてからリトライする(0はスレッド停止のため除外)
133                     System.Diagnostics.Debug.WriteLine("AppConfigWebProxy.GetStream > retry : " + e.Message);
134                     if (wait > 0)
135                     {
136                         Thread.Sleep(wait);
137                     }
138                 }
139             }
140         }
141
142         #endregion
143
144         #region 内部処理用メソッド
145
146         /// <summary>
147         /// 指定されたURIの情報をストリームで取得。
148         /// </summary>
149         /// <param name="uri">取得対象のURI。</param>
150         /// <returns>取得したストリーム。使用後は必ずクローズすること。</returns>
151         /// <remarks>
152         /// 通信エラー等の場合、アプリケーション設定に指定されている回数リトライする。
153         /// それでも取得できない場合は例外を投げる。
154         /// </remarks>
155         private Stream GetStreamBody(Uri uri)
156         {
157             // URIに応じたWebRequestを取得
158             WebRequest req = WebRequest.Create(uri);
159
160             // Requestに応じた設定を行う
161             // ※ HttpWebRequest, FileWebRequestを想定(後者は特に処理無し)
162             //    それ以外については通すが未確認
163             if (req is HttpWebRequest)
164             {
165                 // HTTP/HTTPSの場合
166                 this.InitializeHttpWebRequest((HttpWebRequest)req);
167             }
168
169             // 応答データを受信するためのStreamを取得し、データを取得
170             return req.GetResponse().GetResponseStream();
171         }
172
173         /// <summary>
174         /// <see cref="HttpWebRequest"/>用の設定を行う。
175         /// </summary>
176         /// <param name="req">設定対象の<see cref="HttpWebRequest"/>。</param>
177         private void InitializeHttpWebRequest(HttpWebRequest req)
178         {
179             // UserAgent設定
180             req.UserAgent = this.UserAgent;
181
182             // Referer設定
183             req.Referer = this.Referer;
184
185             // 可能であれば自動的に圧縮を行う用設定
186             req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
187         }
188
189         /// <summary>
190         /// 渡された<see cref="WebException"/>がリトライ可能なものか?
191         /// </summary>
192         /// <param name="e">発生した<see cref="WebException"/>。</param>
193         /// <returns>リトライ可能な場合<c>true</c>。</returns>
194         private bool IsRetryable(WebException e)
195         {
196             // HTTPプロトコルエラーの場合、ステータスコードで判断する
197             if (e.Status == WebExceptionStatus.ProtocolError)
198             {
199                 HttpStatusCode sc = ((HttpWebResponse)e.Response).StatusCode;
200                 return sc == HttpStatusCode.InternalServerError
201                     || sc == HttpStatusCode.BadGateway
202                     || sc == HttpStatusCode.ServiceUnavailable
203                     || sc == HttpStatusCode.GatewayTimeout;
204             }
205
206             // それ以外は、応答ステータスで判断する
207             // ※ fileスキームのエラー等はUnknownErrorで来るので注意
208             return e.Status != WebExceptionStatus.TrustFailure
209                 && e.Status != WebExceptionStatus.UnknownError
210                 && e.Status != WebExceptionStatus.RequestProhibitedByCachePolicy
211                 && e.Status != WebExceptionStatus.RequestProhibitedByProxy;
212         }
213
214         #endregion
215     }
216 }