From 15803366de3a072dfdc5fbc7b1d3f4de2278ca05 Mon Sep 17 00:00:00 2001 From: Philipp Wallrich Date: Mon, 16 Jul 2018 08:40:18 +0200 Subject: [PATCH 1/8] Added Cookie Handling --- ios/RCTWKWebView/RCTWKWebView.m | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index e1348c81..6d8aa007 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -306,11 +306,49 @@ - (void)stopLoading [_webView stopLoading]; } +- (void) copyCookies { + + NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray* array = [storage cookies]; + + + if (@available(ios 11,*)) { + + // The webView websiteDataStore only gets initialized, when needed. Setting cookies on the dataStore's + // httpCookieStore doesn't seem to initialize it. That's why fetchDataRecordsOfTypes is called. + // All the cookies of the sharedHttpCookieStorage, which is used in react-native-cookie, + // are copied to the webSiteDataStore's httpCookieStore. + // https://bugs.webkit.org/show_bug.cgi?id=185483 + [_webView.configuration.websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeCookies] completionHandler:^(NSArray *records) { + for (NSHTTPCookie* cookie in array) { + [_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil]; + } + }]; + } else { + // Create WKUserScript for each cookie + // Cookies are injected with Javascript AtDocumentStart + for (NSHTTPCookie* cookie in array){ + NSString* cookieSource = [NSString stringWithFormat:@"document.cookie = '%@'", [self cookieDescription:cookie]]; + WKUserScript* cookieScript = [[WKUserScript alloc] + initWithSource:cookieSource + injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; + + + [_webView.configuration.userContentController addUserScript:cookieScript]; + } + } +} + - (void)setSource:(NSDictionary *)source { if (![_source isEqualToDictionary:source]) { _source = [source copy]; _sendCookies = [source[@"sendCookies"] boolValue]; + + if (_sendCookies) { + [self copyCookies]; + } + if ([source[@"customUserAgent"] length] != 0 && [_webView respondsToSelector:@selector(setCustomUserAgent:)]) { [_webView setCustomUserAgent:source[@"customUserAgent"]]; } From d54fd07a04bf2d9eebc34580e08324582184b3c8 Mon Sep 17 00:00:00 2001 From: Philipp Wallrich Date: Tue, 17 Jul 2018 13:49:05 +0200 Subject: [PATCH 2/8] added missing Method --- ios/RCTWKWebView/RCTWKWebView.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index 6d8aa007..ee8094d0 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -306,6 +306,23 @@ - (void)stopLoading [_webView stopLoading]; } +- (NSString *) cookieDescription:(NSHTTPCookie *)cookie { + + NSMutableString *cDesc = [[NSMutableString alloc] init]; + [cDesc appendFormat:@"%@=%@;", + [[cookie name] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding], + [[cookie value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + if ([cookie.domain length] > 0) + [cDesc appendFormat:@"domain=%@;", [cookie domain]]; + if ([cookie.path length] > 0) + [cDesc appendFormat:@"path=%@;", [cookie path]]; + if (cookie.expiresDate != nil) + [cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]]; + + + return cDesc; +} + - (void) copyCookies { NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; From 232de7e2f66dce6d38da048222a49a2ced5ff2a0 Mon Sep 17 00:00:00 2001 From: Philipp Wallrich Date: Tue, 24 Jul 2018 09:43:43 +0200 Subject: [PATCH 3/8] Added new useWKCookieStore option --- README.md | 4 ++++ WKWebView.ios.js | 7 ++++++- ios/RCTWKWebView/RCTWKWebView.m | 20 +++++++++++--------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ac0ae09a..2b731f39 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ If set to true, links with `target="_blank"` or `window.open` will be opened in Set `sendCookies` to true to copy cookies from `sharedHTTPCookieStorage` when calling loadRequest. This emulates the behavior of react-native's `WebView` component. You can set cookies using `react-native-cookies` Default is false. +- **useWKCookieStore** + +Set `useWKCookieStore` to true to use the webView's `WKHTTPCookieStorage`. All Cookies from `sharedHTTPCookieStorage` will be copied to it. + - **source={{file: '', allowingReadAccessToURL: '' }}** This allows WKWebView loads a local HTML file. Please note the underlying API is only introduced in iOS 9+. So in iOS 8, it will simple ignores these two properties. diff --git a/WKWebView.ios.js b/WKWebView.ios.js index f9a37eb1..5b508e5b 100644 --- a/WKWebView.ios.js +++ b/WKWebView.ios.js @@ -232,6 +232,10 @@ class WKWebView extends React.Component { * Set this to true to emulate behavior of WebView component. */ sendCookies: PropTypes.bool, + /** + * Initializes the webView's WKHTTPCookieStorage and copies all cookies from sharedHTTPCookieStorage + */ + useWKCookieStore: PropTypes.bool, /** * If set to true, target="_blank" or window.open will be opened in WebView, instead * of new window. Default is false to be backward compatible. @@ -316,7 +320,8 @@ class WKWebView extends React.Component { if (this.props.source && typeof this.props.source === 'object') { source = Object.assign({}, this.props.source, { sendCookies: this.props.sendCookies, - customUserAgent: this.props.customUserAgent || this.props.userAgent + customUserAgent: this.props.customUserAgent || this.props.userAgent, + useWKCookieStore: this.props.useWKCookieStore }); if (this.props.html) { diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index ee8094d0..25500bf4 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -35,6 +35,7 @@ @interface RCTWKWebView () Date: Wed, 15 Aug 2018 10:35:53 +0200 Subject: [PATCH 4/8] clean spaces --- ios/RCTWKWebView/RCTWKWebView.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index 25500bf4..34b3f67a 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -308,7 +308,7 @@ - (void)stopLoading } - (NSString *) cookieDescription:(NSHTTPCookie *)cookie { - + NSMutableString *cDesc = [[NSMutableString alloc] init]; [cDesc appendFormat:@"%@=%@;", [[cookie name] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding], @@ -319,8 +319,8 @@ - (NSString *) cookieDescription:(NSHTTPCookie *)cookie { [cDesc appendFormat:@"path=%@;", [cookie path]]; if (cookie.expiresDate != nil) [cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]]; - - + + return cDesc; } From 3c8c29dee2149c863a8f85a71317807f9d9a511a Mon Sep 17 00:00:00 2001 From: w Date: Thu, 26 Apr 2018 18:40:43 +0800 Subject: [PATCH 5/8] Set cookie from cookiesWithResponseHeaderFields --- ios/RCTWKWebView/RCTWKWebView.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index 34b3f67a..2fd43f69 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -512,6 +512,18 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView #pragma mark - WKNavigationDelegate methods +- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { + if (_sendCookies) { + NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response; + NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL]; + for (NSHTTPCookie *cookie in cookies) { + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; + } + } + + decisionHandler(WKNavigationResponsePolicyAllow); +} + #if DEBUG - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { NSURLCredential * credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust]; From 0dd8f0639a09130eefb4bddde1f369957ba71e20 Mon Sep 17 00:00:00 2001 From: Jens Fahnenbruck Date: Mon, 1 Oct 2018 14:59:18 +0200 Subject: [PATCH 6/8] remove spaces --- ios/RCTWKWebView/RCTWKWebView.m | 58 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index 2fd43f69..b810d14a 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -64,18 +64,18 @@ - (instancetype)initWithProcessPool:(WKProcessPool *)processPool super.backgroundColor = [UIColor clearColor]; _automaticallyAdjustContentInsets = YES; _contentInset = UIEdgeInsetsZero; - + WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; config.processPool = processPool; WKUserContentController* userController = [[WKUserContentController alloc]init]; [userController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"reactNative"]; config.userContentController = userController; - + _webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:config]; _webView.UIDelegate = self; _webView.navigationDelegate = self; _webView.scrollView.delegate = self; - + #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ // `contentInsetAdjustmentBehavior` is only available since iOS 11. // We set the default behavior to "never" so that iOS @@ -164,7 +164,7 @@ - (void)loadRequest:(NSURLRequest *)request request = mutableRequest; } } - + [_webView loadRequest:request]; } @@ -180,29 +180,29 @@ -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView if (!hideKeyboardAccessoryView) { return; } - + UIView* subview; for (UIView* view in _webView.scrollView.subviews) { if([[view.class description] hasPrefix:@"WKContent"]) subview = view; } - + if(subview == nil) return; - + NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelperWK", subview.class.superclass]; Class newClass = NSClassFromString(name); - + if(newClass == nil) { newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); if(!newClass) return; - + Method method = class_getInstanceMethod([_SwizzleHelperWK class], @selector(inputAccessoryView)); class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); - + objc_registerClassPair(newClass); } - + object_setClass(subview, newClass); } @@ -213,7 +213,7 @@ -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAct if (!keyboardDisplayRequiresUserAction) { Class class = NSClassFromString(@"WKContentView"); NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0}; - + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) { SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:"); Method method = class_getInstanceMethod(class, selector); @@ -371,20 +371,20 @@ - (void)setSource:(NSDictionary *)source if ([source[@"customUserAgent"] length] != 0 && [_webView respondsToSelector:@selector(setCustomUserAgent:)]) { [_webView setCustomUserAgent:source[@"customUserAgent"]]; } - + // Allow loading local files: // // Only works for iOS 9+. So iOS 8 will simply ignore those two values NSString *file = [RCTConvert NSString:source[@"file"]]; NSString *allowingReadAccessToURL = [RCTConvert NSString:source[@"allowingReadAccessToURL"]]; - + if (file && [_webView respondsToSelector:@selector(loadFileURL:allowingReadAccessToURL:)]) { NSURL *fileURL = [RCTConvert NSURL:file]; NSURL *baseURL = [RCTConvert NSURL:allowingReadAccessToURL]; [_webView loadFileURL:fileURL allowingReadAccessToURL:baseURL]; return; } - + // Check for a static html source first NSString *html = [RCTConvert NSString:source[@"html"]]; if (html) { @@ -395,7 +395,7 @@ - (void)setSource:(NSDictionary *)source [_webView loadHTMLString:html baseURL:baseURL]; return; } - + NSURLRequest *request = [RCTConvert NSURLRequest:source]; // Because of the way React works, as pages redirect, we actually end up // passing the redirect urls back here, so we ignore them if trying to load @@ -448,7 +448,7 @@ - (UIColor *)backgroundColor @"canGoBack": @(_webView.canGoBack), @"canGoForward" : @(_webView.canGoForward), }]; - + return event; } @@ -520,7 +520,7 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNaviga [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; } } - + decisionHandler(WKNavigationResponsePolicyAllow); } @@ -537,9 +537,9 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W NSURLRequest *request = navigationAction.request; NSURL* url = request.URL; NSString* scheme = url.scheme; - + BOOL isJSNavigation = [scheme isEqualToString:RCTJSNavigationScheme]; - + // handle mailto and tel schemes if ([scheme isEqualToString:@"mailto"] || [scheme isEqualToString:@"tel"]) { if ([app canOpenURL:url]) { @@ -548,7 +548,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W return; } } - + // skip this for the JS Navigation handler if (!isJSNavigation && _onShouldStartLoadWithRequest) { NSMutableDictionary *event = [self baseEvent]; @@ -562,7 +562,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W return decisionHandler(WKNavigationActionPolicyCancel); } } - + if (_onLoadingStart) { // We have this check to filter out iframe requests and whatnot BOOL isTopFrame = [url isEqual:request.mainDocumentURL]; @@ -575,7 +575,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W _onLoadingStart(event); } } - + if (isJSNavigation) { decisionHandler(WKNavigationActionPolicyCancel); } @@ -594,7 +594,7 @@ - (void)webView:(__unused WKWebView *)webView didFailProvisionalNavigation:(__un // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os return; } - + NSMutableDictionary *event = [self baseEvent]; [event addEntriesFromDictionary:@{ @"domain": error.domain, @@ -617,7 +617,7 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(__unused WKNavigation - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { completionHandler(); }]]; @@ -626,7 +626,7 @@ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSStrin } - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler { - + // TODO We have to think message to confirm "YES" UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { @@ -640,17 +640,17 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr } - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler { - + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { textField.text = defaultText; }]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { NSString *input = ((UITextField *)alertController.textFields.firstObject).text; completionHandler(input); }]]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { completionHandler(nil); }]]; From 1c08e36059f0cd4ed703cfa1030edfeb42df37f1 Mon Sep 17 00:00:00 2001 From: Jens Fahnenbruck Date: Mon, 1 Oct 2018 17:43:06 +0200 Subject: [PATCH 7/8] remove duplicated method --- ios/RCTWKWebView/RCTWKWebView.m | 41 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index b810d14a..d7e8475a 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -513,15 +513,27 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView #pragma mark - WKNavigationDelegate methods - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - if (_sendCookies) { - NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response; - NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL]; - for (NSHTTPCookie *cookie in cookies) { - [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; - } + if (_sendCookies) { + NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response; + NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL]; + for (NSHTTPCookie *cookie in cookies) { + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; } + } - decisionHandler(WKNavigationResponsePolicyAllow); + if (_onNavigationResponse) { + NSDictionary *headers = ((NSHTTPURLResponse *)navigationResponse.response).allHeaderFields; + NSInteger statusCode = ((NSHTTPURLResponse *)navigationResponse.response).statusCode; + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary:@{ + @"headers": headers, + @"status": [NSHTTPURLResponse localizedStringForStatusCode:statusCode], + @"statusCode": @(statusCode), + }]; + _onNavigationResponse(event); + } + + decisionHandler(WKNavigationResponsePolicyAllow); } #if DEBUG @@ -678,20 +690,5 @@ - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView RCTLogWarn(@"Webview Process Terminated"); } -- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - if (_onNavigationResponse) { - NSDictionary *headers = ((NSHTTPURLResponse *)navigationResponse.response).allHeaderFields; - NSInteger statusCode = ((NSHTTPURLResponse *)navigationResponse.response).statusCode; - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary:@{ - @"headers": headers, - @"status": [NSHTTPURLResponse localizedStringForStatusCode:statusCode], - @"statusCode": @(statusCode), - }]; - _onNavigationResponse(event); - } - - decisionHandler(WKNavigationResponsePolicyAllow); -} @end From 8d4382e41249cd47efb1ce825c28cdf06d232e6b Mon Sep 17 00:00:00 2001 From: Jens Fahnenbruck Date: Tue, 2 Oct 2018 10:03:41 +0200 Subject: [PATCH 8/8] add Secure and HttpOnly flags to cookie --- ios/RCTWKWebView/RCTWKWebView.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ios/RCTWKWebView/RCTWKWebView.m b/ios/RCTWKWebView/RCTWKWebView.m index d7e8475a..e497e01d 100644 --- a/ios/RCTWKWebView/RCTWKWebView.m +++ b/ios/RCTWKWebView/RCTWKWebView.m @@ -314,11 +314,15 @@ - (NSString *) cookieDescription:(NSHTTPCookie *)cookie { [[cookie name] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [[cookie value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; if ([cookie.domain length] > 0) - [cDesc appendFormat:@"domain=%@;", [cookie domain]]; + [cDesc appendFormat:@"domain=%@;", [cookie domain]]; if ([cookie.path length] > 0) - [cDesc appendFormat:@"path=%@;", [cookie path]]; + [cDesc appendFormat:@"path=%@;", [cookie path]]; if (cookie.expiresDate != nil) - [cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]]; + [cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]]; + if (cookie.HTTPOnly == YES) + [cDesc appendString:@"HttpOnly;"]; + if (cookie.secure == YES) + [cDesc appendString:@"Secure;"]; return cDesc;