Skip to content
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

Resolve location selection against remote locations #764

Closed
wants to merge 17 commits into from

Conversation

graeme
Copy link
Contributor

@graeme graeme commented Apr 5, 2024

Please review the release process for BrowserServicesKit here.

Required:

Task/Issue URL: https://app.asana.com/0/1203137811378537/1206773442486707/f
iOS PR: duckduckgo/iOS#2688
macOS PR: duckduckgo/macos-browser#2805
What kind of version bump will this require?: Major

Description:

The VPN geoswitching logic currently doesn’t handle the case that a location becomes unavailable. We should fall back to the country, if a city isn’t available, or nearest if the country isn’t available.

Steps to test this PR:
See platform PRs

OS Testing:

  • iOS 14
  • iOS 15
  • iOS 16
  • macOS 10.15
  • macOS 11
  • macOS 12

Internal references:

Software Engineering Expectations
Technical Design Template

Copy link
Contributor

@diegoreymendez diegoreymendez left a comment

Choose a reason for hiding this comment

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

I think direction is ok for helping us prevent issues, and I think this direction is great... but this is not recovery. Are we planning to follow up with proper recovery from failed registration / connection attempts?

PS: Just being extra cautious here since I'm the one asking for recovery - if we do add recovery, let's do a follow up PR to avoid this becoming huge / neverending etc. I'm happy to keep things separated.

@@ -1041,9 +993,6 @@ open class PacketTunnelProvider: NEPacketTunnelProvider {
}

settings.selectedServer = .endpoint(serverName)
if case .connected = connectionStatus {
try? await updateTunnelConfiguration(environment: settings.selectedEnvironment, serverSelectionMethod: .preferredServer(serverName: serverName), reassert: true)
Copy link
Contributor

Choose a reason for hiding this comment

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

This change is actually changing behaviour. Why is it no longer necessary to update the tunnel configuration when the server selection changes?

func sanitizedServerSelectionMethod() async -> NetworkProtectionServerSelectionMethod
}

public final class VPNServerSelectionSanitizer: VPNServerSelectionSanitizing {
Copy link
Contributor

Choose a reason for hiding this comment

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

A few comments here:

  1. Let's add unit tests to cover this class in its entirety.
  2. The name of this class sounds off to me. This does more than just sanitize the selection - it effectively manages the entire selection logic (locations vs servers in settings). Additionally when referencing this in the init parameter below (for PacketTunnelProvider) the instance is named serverSelection, which makes me think you agree. Why not name this something more generic like VPNServerSelector or VPNServerSelectionResolver.
  3. To further expand on point 2, I really think that to the PacketTunnelProvider whether this does sanitization or not is an implementation detail - what matters is that this class provides a server / location to connect to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the naming here gave me a lot of trouble. So just decided to open it up to get opinions. Thanks for the input.

Comment on lines 922 to 994
case .setSelectedServer(let selectedServer):
let serverSelectionMethod: NetworkProtectionServerSelectionMethod

switch selectedServer {
case .automatic:
serverSelectionMethod = .automatic
case .endpoint(let serverName):
serverSelectionMethod = .preferredServer(serverName: serverName)
}

Task { @MainActor in
if case .connected = connectionStatus {
try? await updateTunnelConfiguration(environment: settings.selectedEnvironment, serverSelectionMethod: serverSelectionMethod, reassert: true)
}
completionHandler?(nil)
}
case .setSelectedLocation(let selectedLocation):
let serverSelectionMethod: NetworkProtectionServerSelectionMethod

switch selectedLocation {
case .nearest:
serverSelectionMethod = .automatic
case .location(let location):
serverSelectionMethod = .preferredLocation(location)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain why this is all going away? Admittedly I'm not looking to deeply into this and opting to ask as I want to avoid making (mistaken) assumptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes sure. It’s essentially extracting this logic into the… class that has yet to be named ;). Let’s go with ServerSelector. I’ve been trying to avoid adding too much more logic to the PacketTunnelProvider, but this selection logic seemed closely related to the responsibilities of the ServerSelector. The extraction should allow it to all be unit tested too which I’ll do before moving this PR out of draft.

serverSelectionMethod = .preferredLocation(location)
}

case .setSelectedServer, .setSelectedLocation:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that this is now handling both server selection and location selection cases.

@graeme
Copy link
Contributor Author

graeme commented Apr 10, 2024

@diegoreymendez I agree about this not fully handling recovery. This however, does add some improvement to the robustness of the location / server selection logic and I’m trying to go for incremental improvements. However, recovering from a failed register request feels a bit more generic and not just relating to locations…

…again though, however, I’m also not 100% sure if this is the right approach or whether the location fallback logic should not be pre-emptive as it currently is, but a reaction of a railed register request.

@graeme graeme changed the title Sanitize location selection against remote locations Resolve location selection against remote locations May 22, 2024
@graeme graeme marked this pull request as ready for review May 24, 2024 12:53
* main:
  Replace unowned reference with weak one (#832)
  Removes VPN waitlist and beta code (#801)
  Add `survey` action and Privacy Pro attributes (#826)
  Autofill engagement KPIs for pixel reporting (#830)
  Bump C-S-S to 5.17.0 (#828)
  ensure bookmarks can be shown in top hits (#818)
  Subscription refactoring (#815)
@duckduckgo duckduckgo deleted a comment from github-actions bot Jun 1, 2024
@github-actions github-actions bot removed the stale label Jun 2, 2024
Copy link
Member

@quanganhdo quanganhdo left a comment

Choose a reason for hiding this comment

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

Good to see unit tests to cover different scenarios. Left some suggestions to improve code readability.

self.vpnSettings = vpnSettings
}

public func resolvedServerSelectionMethod() async -> NetworkProtectionServerSelectionMethod {
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: Include the server selection fallback logic in a comment to make it more readable.

The VPN geoswitching logic currently doesn’t handle the case that a location becomes unavailable. We should fall back to the country, if a city isn’t available, or nearest if the country isn’t available.

break
case .endpoint(let string):
// Selecting a specific server will override locations setting
// Only available in debug
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: Enforce this logic in the code (e.g. guarding .setSelectedServer(.endpoint(String)) inside ALPHA || DEBUG)

Copy link

This PR has been inactive for more than 7 days and will be automatically closed 7 days from now.

@github-actions github-actions bot added the stale label Jun 15, 2024
Copy link

This PR has been closed after 14 days of inactivity. Feel free to reopen it if you plan to continue working on it or have further discussions.

@github-actions github-actions bot closed this Jun 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants