UNPKG

5.96 kBPlain TextView Raw
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import <React/RCTWebSocketModule.h>
9
10#import <objc/runtime.h>
11
12#import <FBReactNativeSpec/FBReactNativeSpec.h>
13#import <React/RCTConvert.h>
14#import <React/RCTUtils.h>
15#import <React/RCTSRWebSocket.h>
16
17#import "CoreModulesPlugins.h"
18
19@implementation RCTSRWebSocket (React)
20
21- (NSNumber *)reactTag
22{
23 return objc_getAssociatedObject(self, _cmd);
24}
25
26- (void)setReactTag:(NSNumber *)reactTag
27{
28 objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
29}
30
31@end
32
33@interface RCTWebSocketModule () <RCTSRWebSocketDelegate, NativeWebSocketModuleSpec>
34
35@end
36
37@implementation RCTWebSocketModule
38{
39 NSMutableDictionary<NSNumber *, RCTSRWebSocket *> *_sockets;
40 NSMutableDictionary<NSNumber *, id<RCTWebSocketContentHandler>> *_contentHandlers;
41}
42
43RCT_EXPORT_MODULE()
44
45- (dispatch_queue_t)methodQueue
46{
47 return dispatch_get_main_queue();
48}
49
50- (NSArray *)supportedEvents
51{
52 return @[@"websocketMessage",
53 @"websocketOpen",
54 @"websocketFailed",
55 @"websocketClosed"];
56}
57
58- (void)invalidate
59{
60 _contentHandlers = nil;
61 for (RCTSRWebSocket *socket in _sockets.allValues) {
62 socket.delegate = nil;
63 [socket close];
64 }
65}
66
67RCT_EXPORT_METHOD(connect:(NSURL *)URL protocols:(NSArray *)protocols options:(JS::NativeWebSocketModule::SpecConnectOptions &)options socketID:(double)socketID)
68{
69 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
70
71 // We load cookies from sharedHTTPCookieStorage (shared with XHR and
72 // fetch). To get secure cookies for wss URLs, replace wss with https
73 // in the URL.
74 NSURLComponents *components = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:true];
75 if ([components.scheme.lowercaseString isEqualToString:@"wss"]) {
76 components.scheme = @"https";
77 }
78
79 // Load and set the cookie header.
80 NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:components.URL];
81 request.allHTTPHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
82
83 // Load supplied headers
84 if ([options.headers() isKindOfClass:NSDictionary.class]) {
85 NSDictionary *headers = (NSDictionary *)options.headers();
86 [headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
87 [request addValue:[RCTConvert NSString:value] forHTTPHeaderField:key];
88 }];
89 }
90
91 RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols];
92 [webSocket setDelegateDispatchQueue:[self methodQueue]];
93 webSocket.delegate = self;
94 webSocket.reactTag = @(socketID);
95 if (!_sockets) {
96 _sockets = [NSMutableDictionary new];
97 }
98 _sockets[@(socketID)] = webSocket;
99 [webSocket open];
100}
101
102RCT_EXPORT_METHOD(send:(NSString *)message forSocketID:(double)socketID)
103{
104 [_sockets[@(socketID)] send:message];
105}
106
107RCT_EXPORT_METHOD(sendBinary:(NSString *)base64String forSocketID:(double)socketID)
108{
109 [self sendData:[[NSData alloc] initWithBase64EncodedString:base64String options:0] forSocketID:@(socketID)];
110}
111
112- (void)sendData:(NSData *)data forSocketID:(NSNumber * __nonnull)socketID
113{
114 [_sockets[socketID] send:data];
115}
116
117RCT_EXPORT_METHOD(ping:(double)socketID)
118{
119 [_sockets[@(socketID)] sendPing:NULL];
120}
121
122RCT_EXPORT_METHOD(close:(double)code reason:(NSString *)reason socketID:(double)socketID)
123{
124 [_sockets[@(socketID)] closeWithCode:code reason:reason];
125 [_sockets removeObjectForKey:@(socketID)];
126}
127
128- (void)setContentHandler:(id<RCTWebSocketContentHandler>)handler forSocketID:(NSString *)socketID
129{
130 if (!_contentHandlers) {
131 _contentHandlers = [NSMutableDictionary new];
132 }
133 _contentHandlers[socketID] = handler;
134}
135
136#pragma mark - RCTSRWebSocketDelegate methods
137
138- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
139{
140 NSString *type;
141
142 NSNumber *socketID = [webSocket reactTag];
143 id contentHandler = _contentHandlers[socketID];
144 if (contentHandler) {
145 message = [contentHandler processWebsocketMessage:message forSocketID:socketID withType:&type];
146 } else {
147 if ([message isKindOfClass:[NSData class]]) {
148 type = @"binary";
149 message = [message base64EncodedStringWithOptions:0];
150 } else {
151 type = @"text";
152 }
153 }
154
155 [self sendEventWithName:@"websocketMessage" body:@{
156 @"data": message,
157 @"type": type,
158 @"id": webSocket.reactTag
159 }];
160}
161
162- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
163{
164 [self sendEventWithName:@"websocketOpen" body:@{
165 @"id": webSocket.reactTag,
166 @"protocol": webSocket.protocol ? webSocket.protocol : @""
167 }];
168}
169
170- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
171{
172 NSNumber *socketID = [webSocket reactTag];
173 _contentHandlers[socketID] = nil;
174 _sockets[socketID] = nil;
175 [self sendEventWithName:@"websocketFailed" body:@{
176 @"message": error.localizedDescription,
177 @"id": socketID
178 }];
179}
180
181- (void)webSocket:(RCTSRWebSocket *)webSocket
182 didCloseWithCode:(NSInteger)code
183 reason:(NSString *)reason
184 wasClean:(BOOL)wasClean
185{
186 NSNumber *socketID = [webSocket reactTag];
187 _contentHandlers[socketID] = nil;
188 _sockets[socketID] = nil;
189 [self sendEventWithName:@"websocketClosed" body:@{
190 @"code": @(code),
191 @"reason": RCTNullIfNil(reason),
192 @"clean": @(wasClean),
193 @"id": socketID
194 }];
195}
196
197- (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
198{
199 return std::make_shared<facebook::react::NativeWebSocketModuleSpecJSI>(self, jsInvoker);
200}
201
202@end
203
204@implementation RCTBridge (RCTWebSocketModule)
205
206- (RCTWebSocketModule *)webSocketModule
207{
208 return [self moduleForClass:[RCTWebSocketModule class]];
209}
210
211@end
212
213Class RCTWebSocketModuleCls(void) {
214 return RCTWebSocketModule.class;
215}