Skip to content

add support for redirect limits #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Oct 23, 2019
Merged

add support for redirect limits #113

merged 12 commits into from
Oct 23, 2019

Conversation

artemredkin
Copy link
Collaborator

This PR adds support for 2 types of redirect limits - one that detects loops (by keeping history of all visited URL's for a single request), and one that just counts the number of redirects.

fixes #76

Copy link
Contributor

@tomerd tomerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some small suggestions and questions

/// Redirects are not followed.
case disallow
/// Redirects are followed with a specified limit. Cycle detection reqiures that all visited URL's are kept in memory.
case allow(max: Int, allowCycles: Bool)
Copy link
Contributor

@tomerd tomerd Oct 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "disable" and "enable" seem more natural than "disallow" and "allow"? since this is a feature flag not a security policy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this struct was renamed to Policy, do you think RedirectPolicy.enable is better than RedirectPolicy.allow? I don't have a strong opinion here, wdyt? cc @ianpartridge

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the tests it reads as redirects: .allow(max: 5, allowCycles: false) which seems fine (although I would prefer redirection: instead of redirects:). .allow makes sense to me because redirection is something that is requested by the remote HTTP server via an HTTP return code, and we decide whether to allow it or not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about RedirectConfiguration.disallow and RedirectConfiguration.follow?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like .follow 🙂

@@ -423,6 +427,19 @@ extension HTTPClient.Configuration {
self.read = read
}
}

/// Specifies redirect processing settings.
public enum RedirectPolicy {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! What do people think about calling this RedirectConfiguration to match TLSConfiguration etc?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 from me, @tomerd wdyt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

/// Redirects are not followed.
case disallow
/// Redirects are followed with a specified limit. Cycle detection reqiures that all visited URL's are kept in memory.
case allow(max: Int, allowCycles: Bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the tests it reads as redirects: .allow(max: 5, allowCycles: false) which seems fine (although I would prefer redirection: instead of redirects:). .allow makes sense to me because redirection is something that is requested by the remote HTTP server via an HTTP return code, and we decide whether to allow it or not.

XCTAssertThrowsError(try httpClient.get(url: "https://localhost:\(httpBin.port)/redirect/infinite1").wait(), "Should fail with redirect limit") { error in
XCTAssertEqual(error as! HTTPClientError, HTTPClientError.redirectLimitReached)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to test case insensitive loops?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean when schema and path are capitalised differently?

Copy link
Contributor

@weissi weissi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks really good, two suggestions, most importantly, we shouldn't make the policy enum public.

/// Default client timeout, defaults to no timeouts.
public var timeout: Timeout
/// Upstream proxy, defaults to no proxy.
public var proxy: Proxy?
/// Ignore TLS unclean shutdown error, defaults to `false`.
public var ignoreUncleanSSLShutdown: Bool

public init(tlsConfiguration: TLSConfiguration? = nil, followRedirects: Bool = false, timeout: Timeout = Timeout(), proxy: Proxy? = nil) {
self.init(tlsConfiguration: tlsConfiguration, followRedirects: followRedirects, timeout: timeout, proxy: proxy, ignoreUncleanSSLShutdown: false)
public init(tlsConfiguration: TLSConfiguration? = nil, redirects: RedirectPolicy = .allow(max: 5, allowCycles: false), timeout: Timeout = Timeout(), proxy: Proxy? = nil) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's quite a lot of code duplication here, with each initializer having the same hard-coded .allow(max: 5, allowCycles: false) default value. We should pull that out into a separate

static let defaultRedirectPolicy: RedirectPolicy = .allow(max: 5, allowCycles: false)

then this can be:

public init(tlsConfiguration: TLSConfiguration? = nil, redirects: RedirectPolicy = HTTPClient.defaultRedirectPolicy, timeout: Timeout = Timeout(), proxy: Proxy? = nil) {
  ...
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then defaultRedirectPolicy would also have to be public, what if it will be optional instead?

@weissi
Copy link
Contributor

weissi commented Oct 11, 2019

@artemredkin

Test Case 'HTTPClientInternalTests.testUploadStreamingBackpressure' started at 2019-10-11 13:58:29.876
/code/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift:208: error: HTTPClientInternalTests.testUploadStreamingBackpressure : XCTAssertEqual failed: ("0") is not equal to ("1") - 
Test Case 'HTTPClientInternalTests.testUploadStreamingBackpressure' failed (0.012 seconds)

@artemredkin
Copy link
Collaborator Author

@weissi I rewrote the backpressure, could you please take a look at a comment to see if it makes sense?

@weissi
Copy link
Contributor

weissi commented Oct 16, 2019

@artemredkin cool, looks good! On top of the test with actual channels, did the EmbeddedChannel not work?

@weissi
Copy link
Contributor

weissi commented Oct 23, 2019

ping @artemredkin on the above question

@artemredkin
Copy link
Collaborator Author

@weissi not yet, do you think its critical? we have "integration-style" test for backpressure for now

@weissi
Copy link
Contributor

weissi commented Oct 23, 2019

@artemredkin I think it would be good to have both especially given that the integration test might turn out to not be a 100% reliable (I think it is but hard to tell for sure). I also think the integration test will be much easier to follow for people who aren't you & me because we worked on that integration test together.

@artemredkin
Copy link
Collaborator Author

@weissi I filed #117 to implement that test for 1.1.0. Test is passing now and test itself is not part of this PR (redirects). wdyt, should we continue as is in order to release 1.0.0?

@weissi
Copy link
Contributor

weissi commented Oct 23, 2019

@artemredkin perfect, no, let’s not block 1.0.0

@artemredkin
Copy link
Collaborator Author

@weissi @Lukasa @tomerd @ianpartridge any additional comments?

@Lukasa
Copy link
Collaborator

Lukasa commented Oct 23, 2019

None from me.

@ianpartridge
Copy link
Contributor

LGTM.

Copy link
Contributor

@weissi weissi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! LGTM

@artemredkin artemredkin merged commit 51dc885 into master Oct 23, 2019
@artemredkin artemredkin deleted the redirect_limits branch October 23, 2019 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Stack overflow in promise chain when performing web scraping.
5 participants