13
13
// limitations under the License.
14
14
15
15
using System . Net ;
16
+ using System . Web ;
16
17
using RestSharp . Extensions ;
17
18
18
19
namespace RestSharp ;
@@ -141,8 +142,7 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
141
142
142
143
if ( request . OnAfterRequest != null ) await request . OnAfterRequest ( responseMessage ) . ConfigureAwait ( false ) ;
143
144
144
- if ( ! IsRedirect ( responseMessage ) ) {
145
- // || !Options.FollowRedirects) {
145
+ if ( ! IsRedirect ( Options . RedirectOptions , responseMessage ) ) {
146
146
break ;
147
147
}
148
148
@@ -159,26 +159,30 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
159
159
// Mirror HttpClient redirection behavior as of 07/25/2023:
160
160
// Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
161
161
// fragment should inherit the fragment from the original URI.
162
- string requestFragment = originalUrl . Fragment ;
163
- if ( ! string . IsNullOrEmpty ( requestFragment ) ) {
164
- string redirectFragment = location . Fragment ;
165
- if ( string . IsNullOrEmpty ( redirectFragment ) ) {
166
- location = new UriBuilder ( location ) { Fragment = requestFragment } . Uri ;
162
+ if ( Options . RedirectOptions . ForwardFragment ) {
163
+ string requestFragment = originalUrl . Fragment ;
164
+ if ( ! string . IsNullOrEmpty ( requestFragment ) ) {
165
+ string redirectFragment = location . Fragment ;
166
+ if ( string . IsNullOrEmpty ( redirectFragment ) ) {
167
+ location = new UriBuilder ( location ) { Fragment = requestFragment } . Uri ;
168
+ }
167
169
}
168
170
}
169
171
170
172
// Disallow automatic redirection from secure to non-secure schemes
171
- // From HttpClient's RedirectHandler :
172
- // if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(location.Scheme)) {
173
- // if (NetEventSource.Log.IsEnabled()) {
174
- // TraceError($"Insecure https to http redirect from '{requestUri}' to '{location}' blocked.", response.RequestMessage!.GetHashCode());
175
- // }
176
- // break;
177
- // }
173
+ // based on the option setting :
174
+ if ( HttpUtilities . IsSupportedSecureScheme ( requestUri . Scheme )
175
+ && ! HttpUtilities . IsSupportedSecureScheme ( location . Scheme )
176
+ && ! Options . RedirectOptions . FollowRedirectsToInsecure ) {
177
+ // TODO: Log here...
178
+ break ;
179
+ }
178
180
179
181
if ( responseMessage . StatusCode == HttpStatusCode . RedirectMethod ) {
182
+ // TODO: Add RedirectionOptions property for this decision:
180
183
httpMethod = HttpMethod . Get ;
181
184
}
185
+
182
186
// Based on Wikipedia https://en.wikipedia.org/wiki/HTTP_302:
183
187
// Many web browsers implemented this code in a manner that violated this standard, changing
184
188
// the request type of the new request to GET, regardless of the type employed in the original request
@@ -192,13 +196,20 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
192
196
// solves this problem by a helper method:
193
197
if ( RedirectRequestRequiresForceGet ( responseMessage . StatusCode , httpMethod ) ) {
194
198
httpMethod = HttpMethod . Get ;
195
- // HttpClient sets request.Content to null here:
196
- // TODO: However... should we be allowed to modify Request like that here?
197
- message . Content = null ;
198
- // HttpClient Redirect handler also does this:
199
- //if (message.Headers.TansferEncodingChunked == true) {
200
- // request.Headers.TransferEncodingChunked = false;
201
- //}
199
+ if ( ! Options . RedirectOptions . ForceForwardBody ) {
200
+ // HttpClient RedirectHandler sets request.Content to null here:
201
+ message . Content = null ;
202
+ // HttpClient Redirect handler also does this:
203
+ //if (message.Headers.TansferEncodingChunked == true) {
204
+ // request.Headers.TransferEncodingChunked = false;
205
+ //}
206
+ Parameter ? transferEncoding = request . Parameters . TryFind ( KnownHeaders . TransferEncoding ) ;
207
+ if ( transferEncoding != null
208
+ && transferEncoding . Type == ParameterType . HttpHeader
209
+ && string . Equals ( ( string ) transferEncoding . Value ! , "chunked" , StringComparison . OrdinalIgnoreCase ) ) {
210
+ message . Headers . Remove ( KnownHeaders . TransferEncoding ) ;
211
+ }
212
+ }
202
213
}
203
214
204
215
url = location ;
@@ -257,6 +268,35 @@ async Task OnAfterRequest(HttpResponseMessage responseMessage) {
257
268
}
258
269
}
259
270
271
+ /// <summary>
272
+ /// From https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs
273
+ /// </summary>
274
+ private static class HttpUtilities {
275
+ internal static bool IsSupportedScheme ( string scheme ) =>
276
+ IsSupportedNonSecureScheme ( scheme ) ||
277
+ IsSupportedSecureScheme ( scheme ) ;
278
+
279
+ internal static bool IsSupportedNonSecureScheme ( string scheme ) =>
280
+ string . Equals ( scheme , "http" , StringComparison . OrdinalIgnoreCase ) || IsNonSecureWebSocketScheme ( scheme ) ;
281
+
282
+ internal static bool IsSupportedSecureScheme ( string scheme ) =>
283
+ string . Equals ( scheme , "https" , StringComparison . OrdinalIgnoreCase ) || IsSecureWebSocketScheme ( scheme ) ;
284
+
285
+ internal static bool IsNonSecureWebSocketScheme ( string scheme ) =>
286
+ string . Equals ( scheme , "ws" , StringComparison . OrdinalIgnoreCase ) ;
287
+
288
+ internal static bool IsSecureWebSocketScheme ( string scheme ) =>
289
+ string . Equals ( scheme , "wss" , StringComparison . OrdinalIgnoreCase ) ;
290
+
291
+ internal static bool IsSupportedProxyScheme ( string scheme ) =>
292
+ string . Equals ( scheme , "http" , StringComparison . OrdinalIgnoreCase ) || string . Equals ( scheme , "https" , StringComparison . OrdinalIgnoreCase ) || IsSocksScheme ( scheme ) ;
293
+
294
+ internal static bool IsSocksScheme ( string scheme ) =>
295
+ string . Equals ( scheme , "socks5" , StringComparison . OrdinalIgnoreCase ) ||
296
+ string . Equals ( scheme , "socks4a" , StringComparison . OrdinalIgnoreCase ) ||
297
+ string . Equals ( scheme , "socks4" , StringComparison . OrdinalIgnoreCase ) ;
298
+ }
299
+
260
300
/// <summary>
261
301
/// Based on .net core RedirectHandler class:
262
302
/// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs
@@ -283,17 +323,10 @@ HttpRequestMessage PrepareRequestMessage(HttpMethod httpMethod, Uri url, HttpCon
283
323
return message ;
284
324
}
285
325
286
- static bool IsRedirect ( HttpResponseMessage responseMessage )
287
- => responseMessage . StatusCode switch {
288
- HttpStatusCode . MovedPermanently => true ,
289
- HttpStatusCode . SeeOther => true ,
290
- HttpStatusCode . TemporaryRedirect => true ,
291
- HttpStatusCode . Redirect => true ,
292
- #if NET
293
- HttpStatusCode . PermanentRedirect => true,
294
- #endif
295
- _ => false
296
- } ;
326
+ static bool IsRedirect ( RestClientRedirectionOptions options , HttpResponseMessage responseMessage )
327
+ {
328
+ return options . RedirectStatusCodes . Contains ( responseMessage . StatusCode ) ;
329
+ }
297
330
298
331
record HttpResponse (
299
332
HttpResponseMessage ? ResponseMessage ,
0 commit comments