-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
Feature Request: Support for Custom NSURLSessionConfiguration in Native WebSocket Implementation #111
base: master
Are you sure you want to change the base?
Conversation
To example added websocket proxy params setup
Hello @shalom-aviv Many thanks for PR! Just letting you know I noticed it, will try to review it as soon as I can, but may take a bit more time than usual these days. |
No problem |
@FZambia Hey |
Hello, this year definitely :) Sorry, really busy working on v6 + holidays. Will come back to this very soon! |
Happy New Year! 🥳 |
Sources/SwiftCentrifuge/Client.swift
Outdated
token: String = Defaults.token, | ||
data: Data? = Defaults.data, | ||
debug: Bool = Defaults.debug, | ||
useNativeWebSocket: Bool = Defaults.webSocketTransport.isNativeWebSocket, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand right that we now have 2 different constructors here? It seems too much for the change, maybe it's possible to continue accepting useNativeWebSocket
, but extend existing init with a new option to provide a configuration for Native WebSocket?
In this way the the cognitive overhead on my non-Swift brain should be less. But probably I am missing something here which prevent making it in a suggested way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first congratulate on the release of the new version of Centrifuge, and welcome back!
Thank you for your feedback! Let me explain my reasoning behind adding a second constructor.
The goal was to maintain backward compatibility with previous versions of the library. By introducing a new constructor with new parameter, we ensure that the existing codebases depending on the older constructor won't break.
Two potential approaches:
-
Combine both configurations into a single constructor
This would work, but it introduces non-obvious logic for handling parameters. Specifically:- The constructor would now have two parameters for configuring the same option (
useNativeWebSocket
andwebSocketTransport
), which could potentially conflict. - To determine the final behavior, users would need to dive into the library's source code. This could create confusion for those unfamiliar with the internal logic.
Example of the combined constructor:
public init( timeout: Double = Defaults.timeout, headers: [String: String] = Defaults.headers, tlsSkipVerify: Bool = Defaults.tlsSkipVerify, minReconnectDelay: Double = Defaults.minReconnectDelay, maxReconnectDelay: Double = Defaults.maxReconnectDelay, maxServerPingDelay: Double = Defaults.maxServerPingDelay, name: String = Defaults.name, version: String = Defaults.version, token: String = Defaults.token, data: Data? = Defaults.data, debug: Bool = Defaults.debug, useNativeWebSocket: Bool = Defaults.webSocketTransport.isNativeWebSocket, webSocketTransport: WebSocketTransport? = nil, tokenGetter: CentrifugeConnectionTokenGetter? = Defaults.tokenGetter, logger: CentrifugeLogger? = Defaults.logger ) { // Non-obvious resolution logic let webSocketTransportOldSetter: WebSocketTransport = useNativeWebSocket ? .native() : .starscream self.webSocketTransport = webSocketTransport ?? webSocketTransportOldSetter // Other initializations... }
While this approach simplifies the public API (one constructor), it complicates the parameter behavior.
- The constructor would now have two parameters for configuring the same option (
-
Introduce a new constructor and mark the old one as deprecated
- The older constructor would remain for backward compatibility but include a
@available
attribute with a clear deprecation message. - This ensures developers receive explicit guidance to migrate to the new constructor, without introducing ambiguity in parameter usage.
Example:
@available(*, deprecated, message: "useNativeWebSocket is deprecated. Please use webSocketTransport instead.") public init( timeout: Double = Defaults.timeout, headers: [String: String] = Defaults.headers, tlsSkipVerify: Bool = Defaults.tlsSkipVerify, ... ) { self.init( timeout: timeout, headers: headers, ..., webSocketTransport: useNativeWebSocket ? .native() : .starscream, ... ) }
- The older constructor would remain for backward compatibility but include a
While the 1. option is functional, I believe the 2. approach is more user-friendly and clear for developers using the library. It allows for backward compatibility while guiding users toward the improved API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the change requires more complex way forward than possible. If we will improve constructors over deprecations we may end up with even more than two. And in my head it seems rather straightforward to have two options. In this way:
// useNativeWebSocket enables using native URLSessionWebSocketTask for WebSocket connections
// instead of our fork of Starscream v3. If you need custom configuration for URLSessionWebSocketTask
// then use urlSessionConfigurationProvider option to configure desired behaviour.
useNativeWebSocket: Bool
// urlSessionConfigurationProvider allows setting custom options for URLSessionWebSocketTask.
urlSessionConfigurationProvider: URLSessionConfigurationProvider
(probably can be improved more, but I hope the main idea here is that when enabling useNativeWebSocket we clearly say how it can be customized in a comment).
I think this does the job without introducing deprecations and maintenance overhead. For me finding a balance in SDK maintenance complexity is very important, so sometimes more simple solution seems better than more idiomatic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, update is ready. Please check it.
…e all icon variations from a single 1024×1024 pixel image. https://developer.apple.com/documentation/xcode/configuring-your-app-icon#Specify-app-icon-variations
Reason for Changes
We encountered an issue on iOS versions earlier than iOs 17. In these versions, system-wide proxy settings are not applied to new instances of URLSessionWebSocketTask. This means that if the application relies on system-level proxy configurations, the WebSocket connections will bypass them.
To enable proxying for iOS versions below 17, proxy settings must be explicitly provided when creating the URLSession. Without this capability, developers cannot ensure that WebSocket traffic respects proxy configurations, leading to potential security and compliance issues in controlled network environments.
This limitation makes it critical to allow developers to pass a custom NSURLSessionConfiguration to the NativeWebSocket implementation.
proxyman: Capture WS/WSS from iOS
Description
Currently, the NativeWebSocket implementation uses URLSessionConfiguration.default for its NSURLSessionConfiguration. While this works for standard use cases, it becomes a limitation for advanced configurations where custom settings (e.g., proxy settings) are required. Without a way to inject a custom NSURLSessionConfiguration, developers are forced to use method swizzling, which is not ideal because:
To address this, I have implemented changes that allow developers to pass a custom NSURLSessionConfiguration through the WebSocketTransport configuration. These changes are backward-compatible and provide a more flexible approach for advanced configurations.
Key Features Added:
• Developers can now pass a custom URLSessionConfiguration provider to the NativeWebSocket through the WebSocketTransport configuration.
• Example use case: Adding proxy settings to the WebSocket connection.
• The default behavior remains unchanged, using URLSessionConfiguration.default if no custom provider is specified.
Code Changes:
Extended Example, now user can specify proxy params for centrifuge client.
Risks or Considerations:
• Potential Misuse of Configuration Provider: Developers need to ensure the custom URLSessionConfiguration is well-defined, as incorrect configurations could cause unexpected behavior.
Screenshot Descriptions
Below are screenshots to assist with configuring Charles Proxy and testing proxying of WebSocket traffic for CentrifugeClient:
1. Charles Proxy Configuration:
• The Charles Proxy server is set up on the local IP address 192.168.1.80 and listens for traffic on port 8889.
• The screenshot shows the necessary settings in the Charles application to enable WebSocket proxying.
2. Captured Traffic in Charles:
• This screenshot demonstrates successful interception of WebSocket traffic between the CentrifugeClient and the Centrifugo server.
• The server hosting Centrifugo is running on 192.168.1.136 with port 8000.
3. Application Proxy Settings:
• Screenshots show the proxy settings applied directly within the application using the urlSessionConfigurationProvider configuration.