-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[io/http] Add a HTTP Client parameter on WebSocket.connect to allow a custom HTTP Client for web socket connections. #46040
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
[io/http] Add a HTTP Client parameter on WebSocket.connect to allow a custom HTTP Client for web socket connections. #46040
Conversation
@athomas it was such minor code, I just created a new PR. |
This fixes #34284 by opening up the http client used in the web socket code, allowing users to add a custom Security Context and cert failure callback to the web socket. Currently, the only solution is to create a global http override, which isn't an ideal solution. |
Code review is https://dart-review.googlesource.com/c/sdk/+/200262 |
The signature of |
@a-siva |
sdk/lib/_http/websocket_impl.dart
Outdated
HttpClient customClient}) { | ||
//overriding _httpClient if user specifies a custom http client. | ||
if (customClient != null) { | ||
_httpClient = customClient..userAgent ??= _httpClient.userAgent; |
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 think overriding static _httpClient
here will be confusing because it means that if you call connect
twice with different customClients the first customClient will be overridden with second one. Currently it will result in userAgent
for the fist connect reporting the value from customClient of the last connect.
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 agree, however, with how its set up, it would require the httpClient to be tied to the webSocket connection itself (which I think it should be). However that problem exists in the current implementation does it not? So it doesn't solve that problem, but it does solve having a custom httpClient for all webSocket connections.
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 agree, however, with how its set up, it would require the httpClient to be tied to the webSocket connection itself (which I think it should be).
Yeah, I think having httpClient as an instance field of _WebSocketImpl would be cleaner. It could default to static final HttpClient that can be used if user didn't provide customClient.
However that problem exists in the current implementation does it not?
Well, currently there is only one httpClient used by all web socket connections, so it being static is okay.
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.
In that case, would it be best to create a separate ticket for instancing the httpClient with the webSocket? That said, I think that it would be much more clear to the user to have getters and setters for the static httpClient instead of having it passed as a method parameter. If you guys agree, i'll update this PR to reflect that.
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.
If it wasn't clear by my proposal, having two separate tasks, one having the existing framework unchanged, but allowing the user to change the httpClient for all webSocket connections, and the other instancing the httpClient on a per webSocket connection basis. Since the first is literally making a private variable accessible, it shouldn't have much, if any, technical debt on the second task.
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.
@aam I am not authorized to change the message in gerrit, so I updated the github message.
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.
Thank you! Looking at how this ends up being copied into Gerrit by Copybara, I think [io/http] <One-liner...
should be put into the subject of this PR(replacing exposed http client for web sockets
subject) leaving only <Paragarph... etc
in the description.
Also word TEST
in Test=...
lines should be all capital and there should be no space around equal sign.
You can see how we attempt to follow this convention in https://github.com/dart-lang/sdk/commits/main
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.
@aam fixed, also added secure tests to web_socket_test.dart in both standalones.
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.
FYI, if you start with a PR it's best to keep modifying the PR and just treat Gerrit as read-only (if you modify the review, that will likely break the automatic syncing from GitHub because of the conflicting edits).
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.
@athomas I did not know that, sorry about that. I don't think I have any permission to edit the gerrit, so I "think" it should good, however I will keep that in mind for any future changes that I do.
Yes, only Googlers can trigger tryjobs at this time.
…On Thu, 9 Sep 2021, 18:22 Alexander Aprelev, ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In sdk/lib/_http/websocket_impl.dart
<#46040 (comment)>:
>
static Future<WebSocket> connect(
String url, Iterable<String>? protocols, Map<String, dynamic>? headers,
- {CompressionOptions compression =
- CompressionOptions.compressionDefault}) {
+ {CompressionOptions compression = CompressionOptions.compressionDefault,
+ HttpClient customClient}) {
+ //overriding _httpClient if user specifies a custom http client.
+ if (customClient != null) {
+ _httpClient = customClient..userAgent ??= _httpClient.userAgent;
@athomas <https://github.com/athomas> do you know if this is by design
where PR authors are not able to trigger dry runs in Gerrit?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#46040 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEYRJB6ZWBWWT3G6X3VGO2LUBDNLXANCNFSM45A4APRA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
Future<WebSocket> createClient(int port) => secure | ||
? WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/', | ||
customClient: HttpClient(context: clientContext)) | ||
: WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/'); |
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.
perhaps this can be simplified by passing null
customClient
if secure
is false
?
Future<WebSocket> createClient(int port) =>
WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/',
customClient: secure ? HttpClient(context: clientContext): null);
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.
completely agree, i wanted to keep the same logic as the server but didn't notice they were calling different functions. I have the customUserAgent there now for the added test.
WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/'); | ||
Future<WebSocket> createClient(int port, {String customUserAgent}) => | ||
WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/', | ||
customClient: secure |
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.
Not sure if you are able to run the web_socket_test to troubleshoot the failure on Gerrit, hopefully you are.
Just to confirm - the issue here is that testShouldSetUserAgent
when run with SecurityConfiguration
secure
:true
fails due to the fact that global WebSocket.userAgent
is not used in that setting - the connection is made using customClient
with its userAgent
.
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 am not able to actually test it. However, WebSocket.userAgent comes from a final static, unless something else is setting the user agent, it shouldn't it never get changed? Would be a lot easier if I could debug it though.
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.
You could try following https://github.com/dart-lang/sdk/wiki/Building to get the sdk built locally, test running.
Right, https://github.com/dart-lang/sdk/blob/main/tests/standalone/io/web_socket_test.dart#L550 from the test that fails is setting WebSocket.userAgent directly.
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.
Took a while to get it up and running, but it was a simple fix. Because of how the tests are ran and because WebSocket.userAgent comes from a static final, testShouldSetUserAgent and testStaticClientUserAgentStaysTheSame were setting the userAgent to different values at roughly the same time. By just making sure that they are the same value, the static final variable doesn't get changed.
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.
Thank you, it looks like there are more failures due to use of WebSocket.connect() when secure
is true
but without customClient that is needed.
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'm going to push a fix to the test to this PR if you don't mind @rnewquist.
It's basically about several places in the test where WebSocket.connect
is invoked without provided customClient
which is needed when running in secure configuration
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.
@aam that would be perfect, I am a little busy this week, sorry about that.
sdk/lib/_http/websocket_impl.dart
Outdated
@@ -1022,7 +1022,9 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket { | |||
path: uri.path, | |||
query: uri.query, | |||
fragment: uri.fragment); | |||
return _httpClient.openUrl("GET", uri).then((request) { | |||
// Use Custom HTTP Client first is it exists, else |
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 think this comment can be omitted as it just describes what is happening on the next line.
Ideally comments are answering the question "why?", rather than "what?", because normally code answers "what?" question already.
…e headers and user are passed when needed
thank you @rnewquist once again! |
I noticed that the status files has a skip |
The WebSocket abstract class was changed to allow an optional parameter called customClient that takes in a HTTPClient and passes it to the WebSocket Implementation.
The WebSocket implementation takes the customClient, checks if its null, if its not null, it uses the customClient in place of the static HTTPClient that the WebSocket Implementation offers.
This custom client does not override the static HTTPClient, so all previous functionality remains the same when the customClient is not present.
TEST=testStaticClientUserAgentStaysTheSame() in web_socket_test.dart in standalone_2/standalone
TEST=new SecurityConfiguration(secure: true).runTests(); in web_socket_error_test.dart and web_socket_test.dart in standalone_2/standalone
Bug: #34284