Skip to content

[Websocket] Update options parameter to headers. Update to spec. #6016

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

Closed
wants to merge 1 commit into from

Conversation

christopherdro
Copy link
Contributor

This is a follow up of 9b87e6c.

PR Overview:

  • Allows custom headers on connection request
  • Adds a default origin header to Android, just like iOS

Introduces no breaking changes.

PR Detail:

I was working on something similar and would like to propose a few changes that make the API more consistent across both iOS and Android platforms and brings this closer to spec.

I believe @aprock first implementation of adding custom headers was correct. It makes sense naming this argument headers since we have no other general options available, and the current options field is being used to pass in a header anyway.

My use case for custom headers was attaching a token to the Authorization header on the connection request. I have been testing this by passing a JWT inside the Authorization header and verifying it on the server before establishing a connection.

I tried to take this this abstraction further and read the response headers from the server to see if a new token was available if the connection was denied from a expired token. Unfortunately, this was only possible with Android since the third party library used by iOS (SquareRocket) does not return the server response on connection failures.

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @aprock, @hharnisc and @satya164 to be potential reviewers.

@facebook-github-bot facebook-github-bot added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Feb 18, 2016
return defaultOrigin;

} catch(URISyntaxException e) {
// Handle error
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mkonicek @satya164 Not sure the best way to handle the error here. Ideas?

Copy link
Contributor

Choose a reason for hiding this comment

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

@christopherdro Since this is a user error, we should throw the error to show a redbox (IllegalArgumentException). This matches the behaviour on the web where it throws an error.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@christopherdro
Copy link
Contributor Author

@satya164 I updated the PR to handle the error message. However, it appears that passing a invalid url scheme will be caught beforehand through Request.Builder

@@ -61,11 +61,11 @@ extern NSString *const RCTSRHTTPResponseErrorKey;

// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
// options can contain a custom "origin" NSString
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols options:(NSDictionary<NSString *, NSString *> *)options NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols headers:(NSDictionary<NSString *, NSString *> *)headers NS_DESIGNATED_INITIALIZER;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah this used to be called options but it's really HTTP headers?

Copy link
Contributor Author

@christopherdro christopherdro Feb 21, 2016

Choose a reason for hiding this comment

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

Yea, these were recently merged via 9b87e6c

I noticed that his original implementation did have an option for using headers but was asked to remove it because only the origin header was required by the contributor.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@@ -245,7 +245,7 @@ + (void)initialize;
CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
}

- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols options:(NSDictionary<NSString *, NSString *> *)options
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols headers:(NSDictionary<NSString *, NSString *> *)headers
Copy link
Contributor

Choose a reason for hiding this comment

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

Since NSURLRequest already includes headers, is there any reason for the seperate headers parameter? Can't we just include them in the request?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nicklockwood That makes sense but it appears there are two other places that headers are being set.

  1. // Set host first so it defaults
    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
    NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
    SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
    _secKey = [keyBytes base64EncodedStringWithOptions:0];
    assert([_secKey length] == 24);
    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket"));
    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade"));
    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey);
    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]);
  2. [request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]];

So im not sure the best way to handle this.

@mkonicek
Copy link
Contributor

We talked about this briefly at React.js conf but don't remember the conclusion. Regarding the setDefaultOriginMethod - why is the origin header needed in React Native? I thought it was designed for browsers?

@christopherdro
Copy link
Contributor Author

@mkonicek Your are right that it is browser specfic. I was just trying to make things consistent across platform. If it's unnecessary then we should remove it from iOS as well.

@nicklockwood Thoughts on whether we need to have a default origin on iOS?

@mkonicek
Copy link
Contributor

Cool, thanks for the explanation! I vote for getting rid of the origin headers if they're not needed on mobile.

@christopherdro christopherdro force-pushed the ws-update branch 2 times, most recently from 5fb2301 to 66d7682 Compare February 26, 2016 01:33
@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@christopherdro
Copy link
Contributor Author

@nicklockwood Updated the PR based on some of your comments and made further notes on ones that I wasn't sure about.

@mkonicek
Copy link
Contributor

mkonicek commented Mar 1, 2016

Is the method setDefaultOrigin needed? Do we need to set an origin header if we're not in a browser (maybe yes, I just don't have the context)?

@christopherdro
Copy link
Contributor Author

Technically no since RN is not a browser client.

The |Origin| header field [RFC6454] is used to protect against
unauthorized cross-origin use of a WebSocket server by scripts using
the WebSocket API in a web browser. The server is informed of the
script origin generating the WebSocket connection request. If the
server does not wish to accept connections from this origin, it can
choose to reject the connection by sending an appropriate HTTP error
code. This header field is sent by browser clients; for non-browser
clients, this header field may be sent if it makes sense in the
context of those clients.

I was waiting to see what @nicklockwood thought about removing the default origin header from iOS.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@christopherdro
Copy link
Contributor Author

@nicklockwood Any chance you can chime in here again so I can go ahead and wrap up this PR? Thanks!

@mkonicek
Copy link
Contributor

This PR does two things which makes it a bit harder to review and land:

  1. Allows custom headers on connection request
  2. Adds a default origin header to Android, just like iOS

Do you think you could send the "Allows custom headers on connection request" part separately especially since we're not sure 2. is needed? Then it should be fairly straightforward to merge.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@christopherdro
Copy link
Contributor Author

@mkonicek Fair enough. I'll update the the PR to only update the headers. Should have it done by this weekend and will ping you again.

@zxcpoiu
Copy link
Contributor

zxcpoiu commented Mar 11, 2016

Hi @christopherdro,

What is your opinion to keep the argument name options, and use 'headers' as a key for setting up additional header ?

*ex: *

options = {
    headers: {}
    otherOptions: ...
}

for future workaround flags or custom actions.

@christopherdro
Copy link
Contributor Author

@zxcpoiu I believe that was my original thought but avoided since it seems that the general attitude of PR's is to implement whats required atm and not over engineer. I will however revisit this shortly.

Can you provide any examples of what some future options might be?

@zxcpoiu
Copy link
Contributor

zxcpoiu commented Mar 11, 2016

Hi @christopherdro

you're right, not over engineer is a good point.

Currently what I can think of potential use cases are: maybe to tweak options of android OkHttpClient Consturrctor when create ws instance

for example:

  • read/write Timeout
  • self-signed certificate verify
  • force TLS versions

It's not that critical, and I don't want to hold this decent PR.
Just treat it as a note here for reference :)

@nicklockwood
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to Phabricator to review.

@facebook-github-bot
Copy link
Contributor

@christopherdro updated the pull request.

@ghost ghost closed this in 205b5d4 Mar 15, 2016
ghost pushed a commit that referenced this pull request Mar 15, 2016
Summary:This is a follow up of 9b87e6c.

- Allows custom headers on connection request
- Adds a default `origin` header to Android, just like iOS

**Introduces no breaking changes.**

I was working on something similar and would like to propose a few changes that make the API more consistent across both iOS and Android platforms and brings this closer to [spec](https://tools.ietf.org/html/rfc6455).

I believe aprock first implementation of adding custom `headers` was correct. It makes sense naming this argument `headers` since we have no other general options available, and the current `options` field is being used to pass in a header anyway.

My use case for custom headers was attaching a token to the `Authorization` header on the connection request. I have been testing this by passing a JWT inside the `Authorization` header and verifying it on the server before establishing a connection.
Closes #6016

Differential Revision: D3040735

fb-gh-sync-id: 183744d2415b895f9d9fd8ecf6023a546e18a546
shipit-source-id: 183744d2415b895f9d9fd8ecf6023a546e18a546
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants