@@ -36,12 +36,7 @@ namespace DotNetOpenAuth.OpenId {
3636 /// If a particular host would be permitted but is in the blacklist, it is not allowed.
3737 /// If a particular host would not be permitted but is in the whitelist, it is allowed.
3838 /// </remarks>
39- public class UntrustedWebRequestHandler : HttpMessageHandler {
40- /// <summary>
41- /// The inner handler.
42- /// </summary>
43- private readonly InternalWebRequestHandler innerHandler ;
44-
39+ public class UntrustedWebRequestHandler : DelegatingHandler {
4540 /// <summary>
4641 /// The set of URI schemes allowed in untrusted web requests.
4742 /// </summary>
@@ -71,7 +66,13 @@ public class UntrustedWebRequestHandler : HttpMessageHandler {
7166 /// The maximum redirections to follow in the course of a single request.
7267 /// </summary>
7368 [ DebuggerBrowsable ( DebuggerBrowsableState . Never ) ]
74- private int maximumRedirections = Configuration . MaximumRedirections ;
69+ private int maxAutomaticRedirections = Configuration . MaximumRedirections ;
70+
71+ /// <summary>
72+ /// A value indicating whether to automatically follow redirects.
73+ /// </summary>
74+ [ DebuggerBrowsable ( DebuggerBrowsableState . Never ) ]
75+ private bool allowAutoRedirect = true ;
7576
7677 /// <summary>
7778 /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler" /> class.
@@ -80,9 +81,8 @@ public class UntrustedWebRequestHandler : HttpMessageHandler {
8081 /// The inner handler. This handler will be modified to suit the purposes of this wrapping handler,
8182 /// and should not be used independently of this wrapper after construction of this object.
8283 /// </param>
83- public UntrustedWebRequestHandler ( WebRequestHandler innerHandler = null ) {
84- this . innerHandler = new InternalWebRequestHandler ( innerHandler ?? new WebRequestHandler ( ) ) ;
85-
84+ public UntrustedWebRequestHandler ( WebRequestHandler innerHandler = null )
85+ : base ( innerHandler ?? new WebRequestHandler ( ) ) {
8686 // If SSL is required throughout, we cannot allow auto redirects because
8787 // it may include a pass through an unprotected HTTP request.
8888 // We have to follow redirects manually.
@@ -100,6 +100,18 @@ public UntrustedWebRequestHandler(WebRequestHandler innerHandler = null) {
100100 }
101101 }
102102
103+ /// <summary>
104+ /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler"/> class
105+ /// for use in unit testing.
106+ /// </summary>
107+ /// <param name="innerHandler">
108+ /// The inner handler which is responsible for processing the HTTP response messages.
109+ /// This handler should NOT automatically follow redirects.
110+ /// </param>
111+ internal UntrustedWebRequestHandler ( HttpMessageHandler innerHandler )
112+ : base ( innerHandler ) {
113+ }
114+
103115 /// <summary>
104116 /// Gets or sets a value indicating whether all requests must use SSL.
105117 /// </summary>
@@ -108,26 +120,37 @@ public UntrustedWebRequestHandler(WebRequestHandler innerHandler = null) {
108120 /// </value>
109121 public bool IsSslRequired { get ; set ; }
110122
111- /// <summary>
112- /// Gets or sets the cache policy.
113- /// </summary>
114- public RequestCachePolicy CachePolicy {
115- get { return this . InnerWebRequestHandler . CachePolicy ; }
116- set { this . InnerWebRequestHandler . CachePolicy = value ; }
117- }
118-
119123 /// <summary>
120124 /// Gets or sets the total number of redirections to allow on any one request.
121125 /// Default is 10.
122126 /// </summary>
123127 public int MaxAutomaticRedirections {
124128 get {
125- return this . maximumRedirections ;
129+ return base . InnerHandler is WebRequestHandler ? this . InnerWebRequestHandler . MaxAutomaticRedirections : this . maxAutomaticRedirections ;
126130 }
127131
128132 set {
129133 Requires . Range ( value >= 0 , "value" ) ;
130- this . maximumRedirections = value ;
134+ this . maxAutomaticRedirections = value ;
135+ if ( base . InnerHandler is WebRequestHandler ) {
136+ this . InnerWebRequestHandler . MaxAutomaticRedirections = value ;
137+ }
138+ }
139+ }
140+
141+ /// <summary>
142+ /// Gets or sets a value indicating whether to automatically follow redirects.
143+ /// </summary>
144+ public bool AllowAutoRedirect {
145+ get {
146+ return base . InnerHandler is WebRequestHandler ? this . InnerWebRequestHandler . AllowAutoRedirect : this . allowAutoRedirect ;
147+ }
148+
149+ set {
150+ this . allowAutoRedirect = value ;
151+ if ( base . InnerHandler is WebRequestHandler ) {
152+ this . InnerWebRequestHandler . AllowAutoRedirect = value ;
153+ }
131154 }
132155 }
133156
@@ -190,8 +213,8 @@ public ICollection<Regex> BlacklistHostsRegex {
190213 /// <value>
191214 /// The inner web request handler.
192215 /// </value>
193- protected WebRequestHandler InnerWebRequestHandler {
194- get { return ( WebRequestHandler ) this . innerHandler . InnerHandler ; }
216+ public WebRequestHandler InnerWebRequestHandler {
217+ get { return ( WebRequestHandler ) this . InnerHandler ; }
195218 }
196219
197220 /// <summary>
@@ -253,7 +276,8 @@ internal static bool IsExceptionFrom417ExpectationFailed(Exception ex) {
253276 /// <returns>
254277 /// Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.
255278 /// </returns>
256- protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken ) {
279+ protected override async Task < HttpResponseMessage > SendAsync (
280+ HttpRequestMessage request , CancellationToken cancellationToken ) {
257281 this . EnsureAllowableRequestUri ( request . RequestUri ) ;
258282
259283 // Since we may require SSL for every redirect, we handle each redirect manually
@@ -264,16 +288,20 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
264288 int i ;
265289 for ( i = 0 ; i < this . MaxAutomaticRedirections ; i ++ ) {
266290 this . EnsureAllowableRequestUri ( request . RequestUri ) ;
267- var response = await this . innerHandler . SendAsync ( request , cancellationToken ) ;
268- if ( response . StatusCode == HttpStatusCode . MovedPermanently || response . StatusCode == HttpStatusCode . Redirect
269- || response . StatusCode == HttpStatusCode . RedirectMethod || response . StatusCode == HttpStatusCode . RedirectKeepVerb ) {
270- // We have no copy of the post entity stream to repeat on our manually
271- // cloned HttpWebRequest, so we have to bail.
272- ErrorUtilities . VerifyProtocol ( request . Method != HttpMethod . Post , MessagingStrings . UntrustedRedirectsOnPOSTNotSupported ) ;
273- Uri redirectUri = new Uri ( request . RequestUri , response . Headers . Location ) ;
274- request = request . Clone ( ) ;
275- request . RequestUri = redirectUri ;
276- continue ;
291+ var response = await base . SendAsync ( request , cancellationToken ) ;
292+ if ( this . AllowAutoRedirect ) {
293+ if ( response . StatusCode == HttpStatusCode . MovedPermanently || response . StatusCode == HttpStatusCode . Redirect
294+ || response . StatusCode == HttpStatusCode . RedirectMethod
295+ || response . StatusCode == HttpStatusCode . RedirectKeepVerb ) {
296+ // We have no copy of the post entity stream to repeat on our manually
297+ // cloned HttpWebRequest, so we have to bail.
298+ ErrorUtilities . VerifyProtocol (
299+ request . Method != HttpMethod . Post , MessagingStrings . UntrustedRedirectsOnPOSTNotSupported ) ;
300+ Uri redirectUri = new Uri ( request . RequestUri , response . Headers . Location ) ;
301+ request = request . Clone ( ) ;
302+ request . RequestUri = redirectUri ;
303+ continue ;
304+ }
277305 }
278306
279307 if ( response . StatusCode == HttpStatusCode . ExpectationFailed ) {
@@ -286,7 +314,9 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
286314 // the web site's global behavior when calling that host.
287315 // TODO: verify that this still works in DNOA 5.0
288316 var servicePoint = ServicePointManager . FindServicePoint ( request . RequestUri ) ;
289- Logger . Http . InfoFormat ( "HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time." , request . RequestUri ) ;
317+ Logger . Http . InfoFormat (
318+ "HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time." ,
319+ request . RequestUri ) ;
290320 servicePoint . Expect100Continue = false ;
291321 }
292322
@@ -441,30 +471,5 @@ private bool IsUriAllowable(Uri uri) {
441471 }
442472 return true ;
443473 }
444-
445- /// <summary>
446- /// A <see cref="DelegatingHandler" /> derived type that makes its SendAsync method available internally.
447- /// </summary>
448- private class InternalWebRequestHandler : DelegatingHandler {
449- /// <summary>
450- /// Initializes a new instance of the <see cref="InternalWebRequestHandler"/> class.
451- /// </summary>
452- /// <param name="innerHandler">The inner handler which is responsible for processing the HTTP response messages.</param>
453- internal InternalWebRequestHandler ( HttpMessageHandler innerHandler )
454- : base ( innerHandler ) {
455- }
456-
457- /// <summary>
458- /// Creates an instance of <see cref="T:System.Net.Http.HttpResponseMessage" /> based on the information provided in the <see cref="T:System.Net.Http.HttpRequestMessage" /> as an operation that will not block.
459- /// </summary>
460- /// <param name="request">The HTTP request message.</param>
461- /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
462- /// <returns>
463- /// Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.
464- /// </returns>
465- protected internal new Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken ) {
466- return base . SendAsync ( request , cancellationToken ) ;
467- }
468- }
469474 }
470475}
0 commit comments