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 ;
@@ -124,8 +125,7 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
124
125
125
126
if ( request . OnAfterRequest != null ) await request . OnAfterRequest ( responseMessage ) . ConfigureAwait ( false ) ;
126
127
127
- if ( ! IsRedirect ( responseMessage ) ) {
128
- // || !Options.FollowRedirects) {
128
+ if ( ! IsRedirect ( Options . RedirectOptions , responseMessage ) ) {
129
129
break ;
130
130
}
131
131
@@ -142,26 +142,30 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
142
142
// Mirror HttpClient redirection behavior as of 07/25/2023:
143
143
// Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
144
144
// fragment should inherit the fragment from the original URI.
145
- string requestFragment = originalUrl . Fragment ;
146
- if ( ! string . IsNullOrEmpty ( requestFragment ) ) {
147
- string redirectFragment = location . Fragment ;
148
- if ( string . IsNullOrEmpty ( redirectFragment ) ) {
149
- location = new UriBuilder ( location ) { Fragment = requestFragment } . Uri ;
145
+ if ( Options . RedirectOptions . ForwardFragment ) {
146
+ string requestFragment = originalUrl . Fragment ;
147
+ if ( ! string . IsNullOrEmpty ( requestFragment ) ) {
148
+ string redirectFragment = location . Fragment ;
149
+ if ( string . IsNullOrEmpty ( redirectFragment ) ) {
150
+ location = new UriBuilder ( location ) { Fragment = requestFragment } . Uri ;
151
+ }
150
152
}
151
153
}
152
154
153
155
// Disallow automatic redirection from secure to non-secure schemes
154
- // From HttpClient's RedirectHandler :
155
- // if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(location.Scheme)) {
156
- // if (NetEventSource.Log.IsEnabled()) {
157
- // TraceError($"Insecure https to http redirect from '{requestUri}' to '{location}' blocked.", response.RequestMessage!.GetHashCode());
158
- // }
159
- // break;
160
- // }
156
+ // based on the option setting :
157
+ if ( HttpUtilities . IsSupportedSecureScheme ( requestUri . Scheme )
158
+ && ! HttpUtilities . IsSupportedSecureScheme ( location . Scheme )
159
+ && ! Options . RedirectOptions . FollowRedirectsToInsecure ) {
160
+ // TODO: Log here...
161
+ break ;
162
+ }
161
163
162
164
if ( responseMessage . StatusCode == HttpStatusCode . RedirectMethod ) {
165
+ // TODO: Add RedirectionOptions property for this decision:
163
166
httpMethod = HttpMethod . Get ;
164
167
}
168
+
165
169
// Based on Wikipedia https://en.wikipedia.org/wiki/HTTP_302:
166
170
// Many web browsers implemented this code in a manner that violated this standard, changing
167
171
// the request type of the new request to GET, regardless of the type employed in the original request
@@ -175,13 +179,20 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
175
179
// solves this problem by a helper method:
176
180
if ( RedirectRequestRequiresForceGet ( responseMessage . StatusCode , httpMethod ) ) {
177
181
httpMethod = HttpMethod . Get ;
178
- // HttpClient sets request.Content to null here:
179
- // TODO: However... should we be allowed to modify Request like that here?
180
- message . Content = null ;
181
- // HttpClient Redirect handler also does this:
182
- //if (message.Headers.TansferEncodingChunked == true) {
183
- // request.Headers.TransferEncodingChunked = false;
184
- //}
182
+ if ( ! Options . RedirectOptions . ForceForwardBody ) {
183
+ // HttpClient RedirectHandler sets request.Content to null here:
184
+ message . Content = null ;
185
+ // HttpClient Redirect handler also does this:
186
+ //if (message.Headers.TansferEncodingChunked == true) {
187
+ // request.Headers.TransferEncodingChunked = false;
188
+ //}
189
+ Parameter ? transferEncoding = request . Parameters . TryFind ( KnownHeaders . TransferEncoding ) ;
190
+ if ( transferEncoding != null
191
+ && transferEncoding . Type == ParameterType . HttpHeader
192
+ && string . Equals ( ( string ) transferEncoding . Value ! , "chunked" , StringComparison . OrdinalIgnoreCase ) ) {
193
+ message . Headers . Remove ( KnownHeaders . TransferEncoding ) ;
194
+ }
195
+ }
185
196
}
186
197
187
198
url = location ;
@@ -212,6 +223,35 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
212
223
}
213
224
}
214
225
226
+ /// <summary>
227
+ /// From https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs
228
+ /// </summary>
229
+ private static class HttpUtilities {
230
+ internal static bool IsSupportedScheme ( string scheme ) =>
231
+ IsSupportedNonSecureScheme ( scheme ) ||
232
+ IsSupportedSecureScheme ( scheme ) ;
233
+
234
+ internal static bool IsSupportedNonSecureScheme ( string scheme ) =>
235
+ string . Equals ( scheme , "http" , StringComparison . OrdinalIgnoreCase ) || IsNonSecureWebSocketScheme ( scheme ) ;
236
+
237
+ internal static bool IsSupportedSecureScheme ( string scheme ) =>
238
+ string . Equals ( scheme , "https" , StringComparison . OrdinalIgnoreCase ) || IsSecureWebSocketScheme ( scheme ) ;
239
+
240
+ internal static bool IsNonSecureWebSocketScheme ( string scheme ) =>
241
+ string . Equals ( scheme , "ws" , StringComparison . OrdinalIgnoreCase ) ;
242
+
243
+ internal static bool IsSecureWebSocketScheme ( string scheme ) =>
244
+ string . Equals ( scheme , "wss" , StringComparison . OrdinalIgnoreCase ) ;
245
+
246
+ internal static bool IsSupportedProxyScheme ( string scheme ) =>
247
+ string . Equals ( scheme , "http" , StringComparison . OrdinalIgnoreCase ) || string . Equals ( scheme , "https" , StringComparison . OrdinalIgnoreCase ) || IsSocksScheme ( scheme ) ;
248
+
249
+ internal static bool IsSocksScheme ( string scheme ) =>
250
+ string . Equals ( scheme , "socks5" , StringComparison . OrdinalIgnoreCase ) ||
251
+ string . Equals ( scheme , "socks4a" , StringComparison . OrdinalIgnoreCase ) ||
252
+ string . Equals ( scheme , "socks4" , StringComparison . OrdinalIgnoreCase ) ;
253
+ }
254
+
215
255
/// <summary>
216
256
/// Based on .net core RedirectHandler class:
217
257
/// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs
@@ -238,17 +278,10 @@ HttpRequestMessage PrepareRequestMessage(HttpMethod httpMethod, Uri url, HttpCon
238
278
return message ;
239
279
}
240
280
241
- static bool IsRedirect ( HttpResponseMessage responseMessage )
242
- => responseMessage . StatusCode switch {
243
- HttpStatusCode . MovedPermanently => true ,
244
- HttpStatusCode . SeeOther => true ,
245
- HttpStatusCode . TemporaryRedirect => true ,
246
- HttpStatusCode . Redirect => true ,
247
- #if NET
248
- HttpStatusCode . PermanentRedirect => true,
249
- #endif
250
- _ => false
251
- } ;
281
+ static bool IsRedirect ( RestClientRedirectionOptions options , HttpResponseMessage responseMessage )
282
+ {
283
+ return options . RedirectStatusCodes . Contains ( responseMessage . StatusCode ) ;
284
+ }
252
285
253
286
record HttpResponse (
254
287
HttpResponseMessage ? ResponseMessage ,
0 commit comments