Skip to content

Commit 9ba81fb

Browse files
committed
MM-15216 Fix crash when cookie does not set expiration date on iOS
1 parent 689ef9c commit 9ba81fb

File tree

2 files changed

+277
-0
lines changed

2 files changed

+277
-0
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ post-install:
8080
@cp ./native_modules/RNCWebViewManager.java node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
8181
@cp ./native_modules/RNCWKWebView.m node_modules/react-native-webview/ios/RNCWKWebView.m
8282

83+
# Need to copy custom RNCookieManagerIOS.m that fixes a crash when cookies does not have expiration date set
84+
@cp ./native_modules/RNCookieManagerIOS.m node_modules/react-native-cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m
85+
8386
@rm -f node_modules/intl/.babelrc
8487
@# Hack to get react-intl and its dependencies to work with react-native
8588
@# Based off of https://github.com/este/este/blob/master/gulp/native-fix.js
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#import "RNCookieManagerIOS.h"
2+
#if __has_include("RCTConvert.h")
3+
#import "RCTConvert.h"
4+
#else
5+
#import <React/RCTConvert.h>
6+
#endif
7+
8+
static NSString * const NOT_AVAILABLE_ERROR_MESSAGE = @"WebKit/WebKit-Components are only available with iOS11 and higher!";
9+
10+
@implementation RNCookieManagerIOS
11+
12+
- (instancetype)init
13+
{
14+
self = [super init];
15+
if (self) {
16+
self.formatter = [NSDateFormatter new];
17+
[self.formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
18+
}
19+
return self;
20+
}
21+
22+
+ (BOOL)requiresMainQueueSetup
23+
{
24+
return NO;
25+
}
26+
27+
RCT_EXPORT_MODULE()
28+
29+
RCT_EXPORT_METHOD(
30+
set:(NSDictionary *)props
31+
useWebKit:(BOOL)useWebKit
32+
resolver:(RCTPromiseResolveBlock)resolve
33+
rejecter:(RCTPromiseRejectBlock)reject)
34+
{
35+
NSString *name = [RCTConvert NSString:props[@"name"]];
36+
NSString *value = [RCTConvert NSString:props[@"value"]];
37+
NSString *domain = [RCTConvert NSString:props[@"domain"]];
38+
NSString *origin = [RCTConvert NSString:props[@"origin"]];
39+
NSString *path = [RCTConvert NSString:props[@"path"]];
40+
NSString *version = [RCTConvert NSString:props[@"version"]];
41+
NSDate *expiration = [RCTConvert NSDate:props[@"expiration"]];
42+
43+
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
44+
[cookieProperties setObject:name forKey:NSHTTPCookieName];
45+
[cookieProperties setObject:value forKey:NSHTTPCookieValue];
46+
[cookieProperties setObject:domain forKey:NSHTTPCookieDomain];
47+
[cookieProperties setObject:origin forKey:NSHTTPCookieOriginURL];
48+
[cookieProperties setObject:path forKey:NSHTTPCookiePath];
49+
[cookieProperties setObject:version forKey:NSHTTPCookieVersion];
50+
[cookieProperties setObject:expiration forKey:NSHTTPCookieExpires];
51+
52+
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
53+
54+
NSLog(@"SETTING COOKIE");
55+
NSLog(@"%@", cookie);
56+
57+
if (useWebKit) {
58+
if (@available(iOS 11.0, *)) {
59+
dispatch_async(dispatch_get_main_queue(), ^(){
60+
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
61+
[cookieStore setCookie:cookie completionHandler:nil];
62+
resolve(nil);
63+
});
64+
} else {
65+
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
66+
}
67+
} else {
68+
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
69+
resolve(nil);
70+
}
71+
}
72+
73+
RCT_EXPORT_METHOD(setFromResponse:(NSURL *)url
74+
value:(NSString *)value
75+
resolver:(RCTPromiseResolveBlock)resolve
76+
rejecter:(RCTPromiseRejectBlock)reject) {
77+
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{@"Set-Cookie": value} forURL:url];
78+
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:NULL];
79+
resolve(nil);
80+
}
81+
82+
RCT_EXPORT_METHOD(getFromResponse:(NSURL *)url
83+
resolver:(RCTPromiseResolveBlock)resolve
84+
rejecter:(RCTPromiseRejectBlock)reject) {
85+
NSURLRequest *request = [NSURLRequest requestWithURL:url];
86+
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init]
87+
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
88+
89+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
90+
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:response.URL];
91+
NSMutableDictionary *dics = [NSMutableDictionary dictionary];
92+
93+
for (int i = 0; i < cookies.count; i++) {
94+
NSHTTPCookie *cookie = [cookies objectAtIndex:i];
95+
[dics setObject:cookie.value forKey:cookie.name];
96+
NSLog(@"cookie: name=%@, value=%@", cookie.name, cookie.value);
97+
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
98+
}
99+
resolve(dics);
100+
}];
101+
}
102+
103+
-(NSString *)getDomainName:(NSURL *) url
104+
{
105+
NSString *separator = @".";
106+
NSInteger maxLength = 2;
107+
108+
NSURLComponents *components = [[NSURLComponents alloc]initWithURL:url resolvingAgainstBaseURL:FALSE];
109+
NSArray<NSString *> *separatedHost = [components.host componentsSeparatedByString:separator];
110+
NSInteger count = [separatedHost count];
111+
NSInteger endPosition = count;
112+
NSInteger startPosition = count - maxLength;
113+
114+
NSMutableString *result = [[NSMutableString alloc]init];
115+
for (NSUInteger i = startPosition; i != endPosition; i++) {
116+
[result appendString:separator];
117+
[result appendString:[separatedHost objectAtIndex:i]];
118+
}
119+
return result;
120+
}
121+
122+
RCT_EXPORT_METHOD(
123+
get:(NSURL *) url
124+
useWebKit:(BOOL)useWebKit
125+
resolver:(RCTPromiseResolveBlock)resolve
126+
rejecter:(RCTPromiseRejectBlock)reject)
127+
{
128+
if (useWebKit) {
129+
if (@available(iOS 11.0, *)) {
130+
dispatch_async(dispatch_get_main_queue(), ^(){
131+
NSString *topLevelDomain = [self getDomainName:url];
132+
133+
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
134+
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
135+
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
136+
for(NSHTTPCookie *currentCookie in allCookies) {
137+
if([currentCookie.domain containsString:topLevelDomain]) {
138+
[cookies setObject:currentCookie.value forKey:currentCookie.name];
139+
}
140+
}
141+
resolve(cookies);
142+
}];
143+
});
144+
} else {
145+
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
146+
}
147+
} else {
148+
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
149+
for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url]) {
150+
NSMutableDictionary *d = [NSMutableDictionary dictionary];
151+
[d setObject:c.value forKey:@"value"];
152+
[d setObject:c.name forKey:@"name"];
153+
[d setObject:c.domain forKey:@"domain"];
154+
[d setObject:c.path forKey:@"path"];
155+
NSString *expires = [self.formatter stringFromDate:c.expiresDate];
156+
if (expires != nil) {
157+
[d setObject:expires forKey:@"expiresDate"];
158+
}
159+
[cookies setObject:d forKey:c.name];
160+
}
161+
resolve(cookies);
162+
}
163+
}
164+
165+
RCT_EXPORT_METHOD(
166+
clearAll:(BOOL)useWebKit
167+
resolver:(RCTPromiseResolveBlock)resolve
168+
rejecter:(RCTPromiseRejectBlock)reject)
169+
{
170+
if (useWebKit) {
171+
if (@available(iOS 11.0, *)) {
172+
dispatch_async(dispatch_get_main_queue(), ^(){
173+
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
174+
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
175+
for(NSHTTPCookie *currentCookie in allCookies) {
176+
// Uses the NSHTTPCookie directly has no effect, nor deleted the cookie nor thrown an error.
177+
// Create a new cookie with the given values and delete this one do the work.
178+
NSMutableDictionary<NSHTTPCookiePropertyKey, id> *cookieData = [NSMutableDictionary dictionary];
179+
[cookieData setValue:currentCookie.name forKey:NSHTTPCookieName];
180+
[cookieData setValue:currentCookie.value forKey:NSHTTPCookieValue];
181+
[cookieData setValue:currentCookie.domain forKey:NSHTTPCookieDomain];
182+
[cookieData setValue:currentCookie.path forKey:NSHTTPCookiePath];
183+
184+
NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:cookieData];
185+
[cookieStore deleteCookie:newCookie completionHandler:^{}];
186+
}
187+
resolve(nil);
188+
}];
189+
});
190+
} else {
191+
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
192+
}
193+
} else {
194+
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
195+
for (NSHTTPCookie *c in cookieStorage.cookies) {
196+
[cookieStorage deleteCookie:c];
197+
}
198+
resolve(nil);
199+
}
200+
}
201+
202+
RCT_EXPORT_METHOD(clearByName:(NSString *) name
203+
resolver:(RCTPromiseResolveBlock)resolve
204+
rejecter:(RCTPromiseRejectBlock)reject) {
205+
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
206+
for (NSHTTPCookie *c in cookieStorage.cookies) {
207+
if ([[c name] isEqualToString:name]) {
208+
[cookieStorage deleteCookie:c];
209+
}
210+
}
211+
resolve(nil);
212+
}
213+
214+
RCT_EXPORT_METHOD(
215+
getAll:(BOOL)useWebKit
216+
resolver:(RCTPromiseResolveBlock)resolve
217+
rejecter:(RCTPromiseRejectBlock)reject)
218+
{
219+
if (useWebKit) {
220+
if (@available(iOS 11.0, *)) {
221+
dispatch_async(dispatch_get_main_queue(), ^(){
222+
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
223+
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
224+
resolve([self createCookieList: allCookies]);
225+
}];
226+
});
227+
} else {
228+
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
229+
}
230+
} else {
231+
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
232+
resolve([self createCookieList:cookieStorage.cookies]);
233+
}
234+
}
235+
236+
RCT_EXPORT_METHOD(getAll:(RCTPromiseResolveBlock)resolve
237+
rejecter:(RCTPromiseRejectBlock)reject) {
238+
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
239+
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
240+
for (NSHTTPCookie *c in cookieStorage.cookies) {
241+
NSMutableDictionary *d = [NSMutableDictionary dictionary];
242+
[d setObject:c.value forKey:@"value"];
243+
[d setObject:c.name forKey:@"name"];
244+
[d setObject:c.domain forKey:@"domain"];
245+
[d setObject:c.path forKey:@"path"];
246+
NSString *expires = [self.formatter stringFromDate:c.expiresDate];
247+
if (expires != nil) {
248+
[d setObject:expires forKey:@"expiresDate"];
249+
}
250+
[cookies setObject:d forKey:c.name];
251+
}
252+
}
253+
254+
-(NSDictionary *)createCookieList:(NSArray<NSHTTPCookie *>*)cookies
255+
{
256+
NSMutableDictionary *cookieList = [NSMutableDictionary dictionary];
257+
for (NSHTTPCookie *cookie in cookies) {
258+
// NSLog(@"COOKIE: %@", cookie);
259+
[cookieList setObject:[self createCookieData:cookie] forKey:cookie.name];
260+
}
261+
return cookieList;
262+
}
263+
264+
-(NSDictionary *)createCookieData:(NSHTTPCookie *)cookie
265+
{
266+
NSMutableDictionary *cookieData = [NSMutableDictionary dictionary];
267+
[cookieData setObject:cookie.value forKey:@"value"];
268+
[cookieData setObject:cookie.name forKey:@"name"];
269+
[cookieData setObject:cookie.domain forKey:@"domain"];
270+
[cookieData setObject:cookie.path forKey:@"path"];
271+
return cookieData;
272+
}
273+
274+
@end

0 commit comments

Comments
 (0)