1313// limitations under the License.
1414
1515using System . Net ;
16+ using System . Web ;
1617using RestSharp . Extensions ;
1718
1819namespace RestSharp ;
@@ -124,8 +125,7 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
124125
125126 if ( request . OnAfterRequest != null ) await request . OnAfterRequest ( responseMessage ) . ConfigureAwait ( false ) ;
126127
127- if ( ! IsRedirect ( responseMessage ) ) {
128- // || !Options.FollowRedirects) {
128+ if ( ! IsRedirect ( Options . RedirectOptions , responseMessage ) ) {
129129 break ;
130130 }
131131
@@ -142,26 +142,30 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
142142 // Mirror HttpClient redirection behavior as of 07/25/2023:
143143 // Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
144144 // 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+ }
150152 }
151153 }
152154
153155 // 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+ }
161163
162164 if ( responseMessage . StatusCode == HttpStatusCode . RedirectMethod ) {
165+ // TODO: Add RedirectionOptions property for this decision:
163166 httpMethod = HttpMethod . Get ;
164167 }
168+
165169 // Based on Wikipedia https://en.wikipedia.org/wiki/HTTP_302:
166170 // Many web browsers implemented this code in a manner that violated this standard, changing
167171 // 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
175179 // solves this problem by a helper method:
176180 if ( RedirectRequestRequiresForceGet ( responseMessage . StatusCode , httpMethod ) ) {
177181 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+ }
185196 }
186197
187198 url = location ;
@@ -212,6 +223,35 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
212223 }
213224 }
214225
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+
215255 /// <summary>
216256 /// Based on .net core RedirectHandler class:
217257 /// 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
238278 return message ;
239279 }
240280
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+ }
252285
253286 record HttpResponse (
254287 HttpResponseMessage ? ResponseMessage ,
0 commit comments