1313// limitations under the License.
1414
1515using System . Net ;
16+ using System . Web ;
1617using RestSharp . Extensions ;
1718
1819namespace RestSharp ;
@@ -141,8 +142,7 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
141142
142143 if ( request . OnAfterRequest != null ) await request . OnAfterRequest ( responseMessage ) . ConfigureAwait ( false ) ;
143144
144- if ( ! IsRedirect ( responseMessage ) ) {
145- // || !Options.FollowRedirects) {
145+ if ( ! IsRedirect ( Options . RedirectOptions , responseMessage ) ) {
146146 break ;
147147 }
148148
@@ -159,26 +159,30 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
159159 // Mirror HttpClient redirection behavior as of 07/25/2023:
160160 // Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
161161 // 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+ }
167169 }
168170 }
169171
170172 // 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+ }
178180
179181 if ( responseMessage . StatusCode == HttpStatusCode . RedirectMethod ) {
182+ // TODO: Add RedirectionOptions property for this decision:
180183 httpMethod = HttpMethod . Get ;
181184 }
185+
182186 // Based on Wikipedia https://en.wikipedia.org/wiki/HTTP_302:
183187 // Many web browsers implemented this code in a manner that violated this standard, changing
184188 // 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
192196 // solves this problem by a helper method:
193197 if ( RedirectRequestRequiresForceGet ( responseMessage . StatusCode , httpMethod ) ) {
194198 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+ }
202213 }
203214
204215 url = location ;
@@ -257,6 +268,35 @@ async Task OnAfterRequest(HttpResponseMessage responseMessage) {
257268 }
258269 }
259270
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+
260300 /// <summary>
261301 /// Based on .net core RedirectHandler class:
262302 /// 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
283323 return message ;
284324 }
285325
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+ }
297330
298331 record HttpResponse (
299332 HttpResponseMessage ? ResponseMessage ,
0 commit comments