Skip to content

Commit 205b5d4

Browse files
christopherdroFacebook Github Bot 6
authored andcommitted
Update options parameter to headers. Update to spec.
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 Pulled By: nicklockwood fb-gh-sync-id: f81bd14ccbdba36309b9d4b4850fb66fe4deae11 shipit-source-id: f81bd14ccbdba36309b9d4b4850fb66fe4deae11
1 parent 79d6ced commit 205b5d4

File tree

5 files changed

+73
-30
lines changed

5 files changed

+73
-30
lines changed

Libraries/WebSocket/RCTSRWebSocket.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,17 @@ extern NSString *const RCTSRHTTPResponseErrorKey;
6060
@property (nonatomic, readonly, copy) NSString *protocol;
6161

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

6766
// Some helper constructors.
68-
- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)protocols options:(NSDictionary<NSString *, NSString *> *)options;
6967
- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)protocols;
7068
- (instancetype)initWithURL:(NSURL *)url;
7169

7270
// Delegate queue will be dispatch_main_queue by default.
7371
// You cannot set both OperationQueue and dispatch_queue.
74-
- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
75-
- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
72+
- (void)setDelegateOperationQueue:(NSOperationQueue *)queue;
73+
- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue;
7674

7775
// By default, it will schedule itself on +[NSRunLoop RCTSR_networkRunLoop] using defaultModes.
7876
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;

Libraries/WebSocket/RCTSRWebSocket.m

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ @implementation RCTSRWebSocket
234234
__strong RCTSRWebSocket *_selfRetain;
235235

236236
NSArray<NSString *> *_requestedProtocols;
237-
NSDictionary<NSString *, NSString *> *_requestedOptions;
238237
RCTSRIOConsumerPool *_consumerPool;
239238
}
240239

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

248-
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols options:(NSDictionary<NSString *, NSString *> *)options
247+
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols
249248
{
250249
RCTAssertParam(request);
251250

@@ -254,7 +253,6 @@ - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NS
254253
_urlRequest = request;
255254

256255
_requestedProtocols = [protocols copy];
257-
_requestedOptions = [options copy];
258256

259257
[self _RCTSR_commonInit];
260258
}
@@ -265,20 +263,15 @@ - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NS
265263

266264
- (instancetype)initWithURLRequest:(NSURLRequest *)request;
267265
{
268-
return [self initWithURLRequest:request protocols:nil options: nil];
266+
return [self initWithURLRequest:request protocols:nil];
269267
}
270268

271269
- (instancetype)initWithURL:(NSURL *)URL;
272270
{
273-
return [self initWithURL:URL protocols:nil options:nil];
271+
return [self initWithURL:URL protocols:nil];
274272
}
275273

276274
- (instancetype)initWithURL:(NSURL *)URL protocols:(NSArray<NSString *> *)protocols;
277-
{
278-
return [self initWithURL:URL protocols:protocols options:nil];
279-
}
280-
281-
- (instancetype)initWithURL:(NSURL *)URL protocols:(NSArray<NSString *> *)protocols options:(NSDictionary<NSString *, id> *)options
282275
{
283276
NSMutableURLRequest *request;
284277
if (URL) {
@@ -297,7 +290,7 @@ - (instancetype)initWithURL:(NSURL *)URL protocols:(NSArray<NSString *> *)protoc
297290
NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:components.URL];
298291
[request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]];
299292
}
300-
return [self initWithURLRequest:request protocols:protocols options:options];
293+
return [self initWithURLRequest:request protocols:protocols];
301294
}
302295

303296
- (void)_RCTSR_commonInit;
@@ -488,12 +481,12 @@ - (void)didConnect
488481
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey);
489482
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]);
490483

484+
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.RCTSR_origin);
485+
491486
if (_requestedProtocols) {
492487
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]);
493488
}
494489

495-
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)(_requestedOptions[@"origin"] ?: _url.RCTSR_origin));
496-
497490
[_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
498491
CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
499492
}];

Libraries/WebSocket/RCTWebSocketModule.m

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#import "RCTBridge.h"
1313
#import "RCTEventDispatcher.h"
14+
#import "RCTConvert.h"
1415
#import "RCTUtils.h"
1516

1617
@implementation RCTSRWebSocket (React)
@@ -44,9 +45,14 @@ - (void)dealloc
4445
}
4546
}
4647

47-
RCT_EXPORT_METHOD(connect:(NSURL *)URL protocols:(NSArray *)protocols options:(NSDictionary *)options socketID:(nonnull NSNumber *)socketID)
48+
RCT_EXPORT_METHOD(connect:(NSURL *)URL protocols:(NSArray *)protocols headers:(NSDictionary *)headers socketID:(nonnull NSNumber *)socketID)
4849
{
49-
RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURL:URL protocols:protocols options:options];
50+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
51+
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
52+
[request addValue:[RCTConvert NSString:value] forHTTPHeaderField:key];
53+
}];
54+
55+
RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols];
5056
webSocket.delegate = self;
5157
webSocket.reactTag = socketID;
5258
if (!_sockets) {

Libraries/WebSocket/WebSocket.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class WebSocket extends WebSocketBase {
3333
_socketId: number;
3434
_subs: any;
3535

36-
connectToSocketImpl(url: string, protocols: ?Array<string>, options: ?{origin?: string}): void {
36+
connectToSocketImpl(url: string, protocols: ?Array<string>, headers: ?Object): void {
3737
this._socketId = WebSocketId++;
3838

39-
RCTWebSocketModule.connect(url, protocols, options, this._socketId);
39+
RCTWebSocketModule.connect(url, protocols, headers, this._socketId);
4040

4141
this._registerEvents(this._socketId);
4242
}

ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import com.squareup.okhttp.ws.WebSocketCall;
3535
import com.squareup.okhttp.ws.WebSocketListener;
3636

37+
import java.net.URISyntaxException;
38+
import java.net.URI;
3739
import java.util.HashMap;
3840
import java.util.Map;
3941
import java.util.concurrent.TimeUnit;
@@ -63,7 +65,7 @@ public String getName() {
6365
}
6466

6567
@ReactMethod
66-
public void connect(final String url, @Nullable final ReadableArray protocols, @Nullable final ReadableMap options, final int id) {
68+
public void connect(final String url, @Nullable final ReadableArray protocols, @Nullable final ReadableMap headers, final int id) {
6769
// ignoring protocols, since OKHttp overrides them.
6870
OkHttpClient client = new OkHttpClient();
6971

@@ -76,14 +78,25 @@ public void connect(final String url, @Nullable final ReadableArray protocols, @
7678
.tag(id)
7779
.url(url);
7880

79-
if (options != null && options.hasKey("origin")) {
80-
if (ReadableType.String.equals(options.getType("origin"))) {
81-
builder.addHeader("Origin", options.getString("origin"));
82-
} else {
83-
FLog.w(
84-
ReactConstants.TAG,
85-
"Ignoring: requested origin, value not a string");
81+
if (headers != null) {
82+
ReadableMapKeySetIterator iterator = headers.keySetIterator();
83+
84+
if (!headers.hasKey("origin")) {
85+
builder.addHeader("origin", setDefaultOrigin(url));
8686
}
87+
88+
while (iterator.hasNextKey()) {
89+
String key = iterator.nextKey();
90+
if (ReadableType.String.equals(headers.getType(key))) {
91+
builder.addHeader(key, headers.getString(key));
92+
} else {
93+
FLog.w(
94+
ReactConstants.TAG,
95+
"Ignoring: requested " + key + ", value not a string");
96+
}
97+
}
98+
} else {
99+
builder.addHeader("origin", setDefaultOrigin(url));
87100
}
88101

89102
WebSocketCall.create(client, builder.build()).enqueue(new WebSocketListener() {
@@ -188,4 +201,37 @@ private void notifyWebSocketFailed(int id, String message) {
188201
params.putString("message", message);
189202
sendEvent("websocketFailed", params);
190203
}
204+
205+
/**
206+
* Set a default origin
207+
*
208+
* @param Websocket connection endpoint
209+
* @return A string of the endpoint converted to HTTP protocol
210+
*/
211+
212+
private static String setDefaultOrigin(String uri) {
213+
try {
214+
String defaultOrigin;
215+
String scheme = "";
216+
217+
URI requestURI = new URI(uri);
218+
if (requestURI.getScheme().equals("wss")) {
219+
scheme += "https";
220+
} else if (requestURI.getScheme().equals("ws")) {
221+
scheme += "http";
222+
}
223+
224+
if (requestURI.getPort() != -1) {
225+
defaultOrigin = String.format("%s://%s:%s", scheme, requestURI.getHost(), requestURI.getPort());
226+
} else {
227+
defaultOrigin = String.format("%s://%s/", scheme, requestURI.getHost());
228+
}
229+
230+
return defaultOrigin;
231+
232+
} catch(URISyntaxException e) {
233+
throw new IllegalArgumentException("Unable to set " + uri + " as default origin header.");
234+
}
235+
}
236+
191237
}

0 commit comments

Comments
 (0)