OLD | NEW |
1 /* | 1 /* |
2 Copyright 2013 Google Inc | 2 Copyright 2013 Google Inc |
3 | 3 |
4 Licensed under the Apache License, Version 2.0 (the "License"); | 4 Licensed under the Apache License, Version 2.0 (the "License"); |
5 you may not use this file except in compliance with the License. | 5 you may not use this file except in compliance with the License. |
6 You may obtain a copy of the License at | 6 You may obtain a copy of the License at |
7 | 7 |
8 http://www.apache.org/licenses/LICENSE-2.0 | 8 http://www.apache.org/licenses/LICENSE-2.0 |
9 | 9 |
10 Unless required by applicable law or agreed to in writing, software | 10 Unless required by applicable law or agreed to in writing, software |
11 distributed under the License is distributed on an "AS IS" BASIS, | 11 distributed under the License is distributed on an "AS IS" BASIS, |
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 See the License for the specific language governing permissions and | 13 See the License for the specific language governing permissions and |
14 limitations under the License. | 14 limitations under the License. |
15 */ | 15 */ |
16 | 16 |
17 using System; | 17 using System; |
18 using System.Net.Http; | 18 using System.Net.Http; |
19 using System.Threading; | 19 using System.Threading; |
20 using System.Threading.Tasks; | 20 using System.Threading.Tasks; |
| 21 |
21 using Google.Apis.Logging; | 22 using Google.Apis.Logging; |
22 using Google.Apis.Util; | 23 using Google.Apis.Util; |
23 | 24 |
24 namespace Google.Apis.Http | 25 namespace Google.Apis.Http |
25 { | 26 { |
26 /// <summary> | 27 /// <summary> |
27 /// A thread-safe back-off handler which handles an abnormal Http response o
r an exception with· | 28 /// A thread-safe back-off handler which handles an abnormal HTTP response o
r an exception with· |
28 /// <see cref="IBackOff"/>. | 29 /// <see cref="IBackOff"/>. |
29 /// </summary> | 30 /// </summary> |
30 public class BackOffHandler : IHttpUnsuccessfulResponseHandler, IHttpExcepti
onHandler | 31 public class BackOffHandler : IHttpUnsuccessfulResponseHandler, IHttpExcepti
onHandler |
31 { | 32 { |
32 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy
pe<BackOffHandler>(); | 33 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy
pe<BackOffHandler>(); |
33 | 34 |
34 /// <summary> An initializer class to initialize a back-off handler. </s
ummary> | 35 /// <summary>An initializer class to initialize a back-off handler.</sum
mary> |
35 public class Initializer | 36 public class Initializer |
36 { | 37 { |
37 /// <summary> Gets the back-off policy used by this back-off handler
. </summary> | 38 /// <summary>Gets the back-off policy used by this back-off handler.
</summary> |
38 public IBackOff BackOff { get; private set; } | 39 public IBackOff BackOff { get; private set; } |
39 | 40 |
40 /// <summary> | 41 /// <summary> |
41 /// Gets or sets the maximum time span to wait. If the back-off inst
ance returns a greater time span then· | 42 /// Gets or sets the maximum time span to wait. If the back-off inst
ance returns a greater time span then· |
42 /// this value, this handler returns <c>false</c> to both <see cref=
"HandleException"/> and· | 43 /// this value, this handler returns <c>false</c> to both <see cref=
"HandleExceptionAsync"/> and· |
43 /// <see cref="HandleResponse"/>. Default value is 5 seconds per a r
etry request. | 44 /// <see cref="HandleResponseAsync"/>. Default value is 5 seconds pe
r a retry request. |
44 /// </summary> | 45 /// </summary> |
45 public TimeSpan MaxTimeSpan { get; set; } | 46 public TimeSpan MaxTimeSpan { get; set; } |
46 | 47 |
47 /// <summary> | 48 /// <summary> |
48 /// Gets or sets a delegate function which indicates if this back-of
f handler should handle an abnormal· | 49 /// Gets or sets a delegate function which indicates if this back-of
f handler should handle an abnormal· |
49 /// Http response. The default is <see cref="DefaultHandleUnsuccessf
ulResponse"/>.· | 50 /// HTTP response. The default is <see cref="DefaultHandleUnsuccessf
ulResponseFunc"/>.· |
50 /// </summary> | 51 /// </summary> |
51 public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFun
c { get; set; } | 52 public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFun
c { get; set; } |
52 | 53 |
53 /// <summary> | 54 /// <summary> |
54 /// Gets or sets a delegate function which indicates if this back-of
f handler should handle an exception.· | 55 /// Gets or sets a delegate function which indicates if this back-of
f handler should handle an exception.· |
55 /// The default is <see cref="DefaultHandleException"/>.· | 56 /// The default is <see cref="DefaultHandleExceptionFunc"/>.· |
56 /// </summary> | 57 /// </summary> |
57 public Func<Exception, bool> HandleExceptionFunc { get; set; } | 58 public Func<Exception, bool> HandleExceptionFunc { get; set; } |
58 | 59 |
59 /// <summary> Default function which handles server errors (503). </
summary> | 60 /// <summary>Default function which handles server errors (503).</su
mmary> |
60 public static readonly Func<HttpResponseMessage, bool> DefaultHandle
UnsuccessfulResponseFunc = | 61 public static readonly Func<HttpResponseMessage, bool> DefaultHandle
UnsuccessfulResponseFunc = |
61 (r) => (int)r.StatusCode == 503; | 62 (r) => (int)r.StatusCode == 503; |
62 | 63 |
63 /// <summary> | 64 /// <summary> |
64 /// Default function which handles exception which aren't· | 65 /// Default function which handles exception which aren't· |
65 /// <seealso cref="System.Threading.Tasks.TaskCanceledException"/> o
r· | 66 /// <seealso cref="System.Threading.Tasks.TaskCanceledException"/> o
r· |
66 /// <seealso cref="System.OperationCanceledException"/>. Those excep
tions represent a task or an operation | 67 /// <seealso cref="System.OperationCanceledException"/>. Those excep
tions represent a task or an operation |
67 /// which was canceled and we shouldn't retry. | 68 /// which was canceled and we shouldn't retry. |
68 /// </summary> | 69 /// </summary> |
69 public static readonly Func<Exception, bool> DefaultHandleExceptionF
unc = | 70 public static readonly Func<Exception, bool> DefaultHandleExceptionF
unc = |
70 (ex) => !(ex is TaskCanceledException || ex is OperationCanceled
Exception); | 71 (ex) => !(ex is TaskCanceledException || ex is OperationCanceled
Exception); |
71 | 72 |
72 /// <summary> Constructs a new initializer by the given back-off. </
summary> | 73 /// <summary>Constructs a new initializer by the given back-off.</su
mmary> |
73 public Initializer(IBackOff backOff) | 74 public Initializer(IBackOff backOff) |
74 { | 75 { |
75 BackOff = backOff; | 76 BackOff = backOff; |
76 HandleExceptionFunc = DefaultHandleExceptionFunc; | 77 HandleExceptionFunc = DefaultHandleExceptionFunc; |
77 HandleUnsuccessfulResponseFunc = DefaultHandleUnsuccessfulRespon
seFunc; | 78 HandleUnsuccessfulResponseFunc = DefaultHandleUnsuccessfulRespon
seFunc; |
78 MaxTimeSpan = TimeSpan.FromSeconds(5); | 79 MaxTimeSpan = TimeSpan.FromSeconds(5); |
79 } | 80 } |
80 } | 81 } |
81 | 82 |
82 /// <summary> Gets the back-off policy used by this back-off handler. </
summary> | 83 /// <summary>Gets the back-off policy used by this back-off handler.</su
mmary> |
83 public IBackOff BackOff { get; private set; } | 84 public IBackOff BackOff { get; private set; } |
84 | 85 |
85 /// <summary> | 86 /// <summary> |
86 /// Gets the maximum time span to wait. If the back-off instance returns
a greater time span, the handle method | 87 /// Gets the maximum time span to wait. If the back-off instance returns
a greater time span, the handle method |
87 /// returns <c>false</c>. Default value is 5 seconds per a retry request
. | 88 /// returns <c>false</c>. Default value is 5 seconds per a retry request
. |
88 /// </summary> | 89 /// </summary> |
89 public TimeSpan MaxTimeSpan { get; private set; } | 90 public TimeSpan MaxTimeSpan { get; private set; } |
90 | 91 |
91 /// <summary> | 92 /// <summary> |
92 /// Gets a delegate function which indicates if this back-off handler sh
ould handle an abnormal Http response.· | 93 /// Gets a delegate function which indicates if this back-off handler sh
ould handle an abnormal HTTP response.· |
93 /// The default is <see cref="DefaultHandleUnsuccessfulResponse"/>.· | 94 /// The default is <see cref="DefaultHandleUnsuccessfulResponseFunc"/>.· |
94 /// </summary> | 95 /// </summary> |
95 public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFunc {
get; private set; } | 96 public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFunc {
get; private set; } |
96 | 97 |
97 /// <summary> | 98 /// <summary> |
98 /// Gets a delegate function which indicates if this back-off handler sh
ould handle an exception. The· | 99 /// Gets a delegate function which indicates if this back-off handler sh
ould handle an exception. The· |
99 /// default is <see cref="DefaultHandleException"/>.· | 100 /// default is <see cref="DefaultHandleExceptionFunc"/>.· |
100 /// </summary> | 101 /// </summary> |
101 public Func<Exception, bool> HandleExceptionFunc { get; private set; } | 102 public Func<Exception, bool> HandleExceptionFunc { get; private set; } |
102 | 103 |
103 /// <summary> Constructs a new back-off handler with the given back-off.
</summary> | 104 /// <summary>Constructs a new back-off handler with the given back-off.<
/summary> |
| 105 /// <param name="backOff">The back-off policy</param> |
104 public BackOffHandler(IBackOff backOff) | 106 public BackOffHandler(IBackOff backOff) |
105 : this(new Initializer(backOff)) | 107 : this(new Initializer(backOff)) |
106 { | 108 { |
107 } | 109 } |
108 | 110 |
109 /// <summary> Constructs a new back-off handler with the given initializ
er. </summary> | 111 /// <summary>Constructs a new back-off handler with the given initialize
r.</summary> |
110 public BackOffHandler(Initializer initializer) | 112 public BackOffHandler(Initializer initializer) |
111 { | 113 { |
112 BackOff = initializer.BackOff; | 114 BackOff = initializer.BackOff; |
113 MaxTimeSpan = initializer.MaxTimeSpan; | 115 MaxTimeSpan = initializer.MaxTimeSpan; |
114 HandleExceptionFunc = initializer.HandleExceptionFunc; | 116 HandleExceptionFunc = initializer.HandleExceptionFunc; |
115 HandleUnsuccessfulResponseFunc = initializer.HandleUnsuccessfulRespo
nseFunc; | 117 HandleUnsuccessfulResponseFunc = initializer.HandleUnsuccessfulRespo
nseFunc; |
116 } | 118 } |
117 | 119 |
118 #region IHttpUnsuccessfulResponseHandler | 120 #region IHttpUnsuccessfulResponseHandler |
119 | 121 |
120 public virtual bool HandleResponse(HandleUnsuccessfulResponseArgs args) | 122 public virtual async Task<bool> HandleResponseAsync(HandleUnsuccessfulRe
sponseArgs args) |
121 { | 123 { |
122 // if the func returns true try to handle this current failed try | 124 // if the func returns true try to handle this current failed try |
123 return HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulR
esponseFunc(args.Response) && | 125 return HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulR
esponseFunc(args.Response) && |
124 Handle(args.SupportsRetry, args.CurrentFailedTry, args.Cancellat
ionToken); | 126 await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, arg
s.CancellationToken); |
125 } | 127 } |
126 | 128 |
127 #endregion | 129 #endregion |
128 | 130 |
129 #region IHttpExceptionHandler | 131 #region IHttpExceptionHandler |
130 | 132 |
131 public virtual bool HandleException(HandleExceptionArgs args) | 133 public virtual async Task<bool> HandleExceptionAsync(HandleExceptionArgs
args) |
132 { | 134 { |
133 // if the func returns true try to handle this current failed try | 135 // if the func returns true try to handle this current failed try |
134 return HandleExceptionFunc != null && HandleExceptionFunc(args.Excep
tion) && | 136 return HandleExceptionFunc != null && HandleExceptionFunc(args.Excep
tion) && |
135 Handle(args.SupportsRetry, args.CurrentFailedTry, args.Cancellat
ionToken); | 137 await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, arg
s.CancellationToken); |
136 } | 138 } |
137 | 139 |
138 #endregion | 140 #endregion |
139 | 141 |
140 /// <summary> | 142 /// <summary> |
141 /// Handles back-off. In case the request doesn't support retry or the b
ack-off time span is greater than the | 143 /// Handles back-off. In case the request doesn't support retry or the b
ack-off time span is greater than the |
142 /// maximum time span allowed for a request, the handler returns <c>fals
e</c>. Otherwise, current thread will | 144 /// maximum time span allowed for a request, the handler returns <c>fals
e</c>. Otherwise, current thread will |
143 /// block for x milliseconds (x is defined by the <see cref="BackOff"/>
instance), and this handler returns· | 145 /// block for x milliseconds (x is defined by the <see cref="BackOff"/>
instance), and this handler returns· |
144 /// <c>true</c>. | 146 /// <c>true</c>. |
145 /// </summary> | 147 /// </summary> |
146 private bool Handle(bool supportsRetry, int currentFailedTry, Cancellati
onToken cancellationToken) | 148 private async Task<bool> HandleAsync(bool supportsRetry, int currentFail
edTry, |
| 149 CancellationToken cancellationToken) |
147 { | 150 { |
148 if (!supportsRetry || BackOff.MaxNumOfRetries < currentFailedTry) | 151 if (!supportsRetry || BackOff.MaxNumOfRetries < currentFailedTry) |
149 { | 152 { |
150 return false; | 153 return false; |
151 } | 154 } |
152 | 155 |
153 TimeSpan ts = BackOff.GetNextBackOff(currentFailedTry); | 156 TimeSpan ts = BackOff.GetNextBackOff(currentFailedTry); |
154 if (ts > MaxTimeSpan || ts < TimeSpan.Zero) | 157 if (ts > MaxTimeSpan || ts < TimeSpan.Zero) |
155 { | 158 { |
156 return false; | 159 return false; |
157 } | 160 } |
158 | 161 |
159 Wait(ts, cancellationToken); | 162 await Wait(ts, cancellationToken); |
160 Logger.Debug("Back-Off handled the error. Waited {0}ms before next r
etry...", ts.TotalMilliseconds); | 163 Logger.Debug("Back-Off handled the error. Waited {0}ms before next r
etry...", ts.TotalMilliseconds); |
161 return true; | 164 return true; |
162 } | 165 } |
163 | 166 |
164 /// <summary> Waits the given time span. Override this method is recomme
nded for mocking purposes.</summary> | 167 /// <summary>Waits the given time span. Override this method is recommen
ded for mocking purposes.</summary> |
165 /// <param name="ts">TimeSpan to wait (and block the current thread)</pa
ram> | 168 /// <param name="ts">TimeSpan to wait (and block the current thread)</pa
ram> |
166 /// <param name="cancellationToken">The cancellation token in case the u
ser wants to cancel the operation in· | 169 /// <param name="cancellationToken">The cancellation token in case the u
ser wants to cancel the operation in· |
167 /// the middle</param> | 170 /// the middle</param> |
168 protected virtual void Wait(TimeSpan ts, CancellationToken cancellationT
oken) | 171 protected virtual async Task Wait(TimeSpan ts, CancellationToken cancell
ationToken) |
169 { | 172 { |
170 try | 173 await TaskEx.Delay(ts, cancellationToken); |
171 { | |
172 TaskEx.Delay(ts, cancellationToken).Wait(); | |
173 } | |
174 catch (Exception) { } | |
175 } | 174 } |
176 } | 175 } |
177 } | 176 } |
OLD | NEW |