First part of CMIS-874 (Switch from NSURLConnection to NSURLSession).

The CMISHttpXXX classes have been moved to use NSURLSession but they currently always use data tasks. To support background upload and download operations the appropriate task has to be used. A different session configuration object is also required to run in the background, the 2nd part will address these shortcomings.

git-svn-id: https://svn.apache.org/repos/asf/chemistry/objectivecmis/trunk@1644593 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ObjectiveCMIS/Client/CMISDocument.h b/ObjectiveCMIS/Client/CMISDocument.h
index 89c96df..b6a769c 100644
--- a/ObjectiveCMIS/Client/CMISDocument.h
+++ b/ObjectiveCMIS/Client/CMISDocument.h
@@ -22,7 +22,7 @@
 @class CMISOperationContext;
 @class CMISRequest;
 
-@interface CMISDocument : CMISFileableObject <NSURLConnectionDataDelegate>
+@interface CMISDocument : CMISFileableObject
 
 @property (nonatomic, strong, readonly) NSString *contentStreamId;
 @property (nonatomic, strong, readonly) NSString *contentStreamFileName;
diff --git a/ObjectiveCMIS/Common/CMISAuthenticationProvider.h b/ObjectiveCMIS/Common/CMISAuthenticationProvider.h
index b1e6bec..fe2ed1d 100644
--- a/ObjectiveCMIS/Common/CMISAuthenticationProvider.h
+++ b/ObjectiveCMIS/Common/CMISAuthenticationProvider.h
@@ -51,4 +51,10 @@
  */
 - (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
 
+/**
+ * callback when authentication challenge was received using NSURLSession
+ */
+- (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+          completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler;
+
 @end
diff --git a/ObjectiveCMIS/Common/CMISErrors.h b/ObjectiveCMIS/Common/CMISErrors.h
index f5bbdfe..4260372 100644
--- a/ObjectiveCMIS/Common/CMISErrors.h
+++ b/ObjectiveCMIS/Common/CMISErrors.h
@@ -106,7 +106,7 @@
  CMIS errors are created either 
  
  - directly. This is the case when an error is captured by one of the methods/classes in the CMIS library
- - indirectly. Errors have been created by classes/methods outside the CMIS library. Example errors created through NSURLConnection. In this case, the underlying error is copied into the CMIS error using the NSUnderlyingErrorKey in the userInfo Dictionary.
+ - indirectly. Errors have been created by classes/methods outside the CMIS library. Example errors created through NSURLSession. In this case, the underlying error is copied into the CMIS error using the NSUnderlyingErrorKey in the userInfo Dictionary.
  
  */
 @interface CMISErrors : NSObject
diff --git a/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m b/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m
index 5506ae7..f4989af 100644
--- a/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m
+++ b/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m
@@ -62,6 +62,10 @@
     return [NSDictionary dictionary];
 }
 
+- (void)updateWithHttpURLResponse:(NSHTTPURLResponse*)httpUrlResponse
+{
+    // nothing to do in the default implementation
+}
 
 - (BOOL)canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
 {
@@ -108,11 +112,29 @@
     }
 }
 
-
-- (void)updateWithHttpURLResponse:(NSHTTPURLResponse*)httpUrlResponse
+- (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+          completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
 {
-    // nothing to do in the default implementation
+    if (challenge.previousFailureCount == 0) {
+        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] &&
+            self.credential.identity) {
+            CMISLogDebug(@"Authenticating with client certificate");
+            completionHandler(NSURLSessionAuthChallengeUseCredential, self.credential);
+        } else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] &&
+                   self.credential.user && self.credential.hasPassword) {
+            CMISLogDebug(@"Authenticating with username and password");
+            completionHandler(NSURLSessionAuthChallengeUseCredential, self.credential);
+        } else if (challenge.proposedCredential) {
+            CMISLogDebug(@"Authenticating with proposed credential");
+            completionHandler(NSURLSessionAuthChallengeUseCredential, challenge.proposedCredential);
+        } else {
+            CMISLogDebug(@"Authenticating without credential");
+            completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+        }
+    } else {
+        CMISLogDebug(@"Authentication failed, cancelling logon");
+        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
+    }
 }
 
-
 @end
diff --git a/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m b/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m
index 455e4cd..ba4e2ef 100644
--- a/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m
+++ b/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m
@@ -18,6 +18,7 @@
  */
 
 #import "CMISStandardUntrustedSSLAuthenticationProvider.h"
+#import "CMISLog.h"
 
 @implementation CMISStandardUntrustedSSLAuthenticationProvider
 
@@ -40,10 +41,24 @@
     if ((challenge.previousFailureCount == 0) &&
         ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]))
     {
+        CMISLogWarning(@"Allowing self-signed certificate");
         [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
     } else {
         [super didReceiveAuthenticationChallenge:challenge];
     }
 }
 
+- (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+          completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
+{
+    if ((challenge.previousFailureCount == 0) &&
+        ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]))
+    {
+        CMISLogWarning(@"Allowing self-signed certificate");
+        completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+    } else {
+        [super didReceiveChallenge:challenge completionHandler:completionHandler];
+    }
+}
+
 @end
diff --git a/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m b/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m
index 9fbc51c..4292708 100644
--- a/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m
+++ b/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m
@@ -105,54 +105,32 @@
     return self;
 }
 
+#pragma mark CMISCancellableRequest method
 
 - (void)cancel
 {
-    [self.outputStream close];
+    [super cancel];
     
+    // clean up
+    [self.outputStream close];
+    self.progressBlock = nil;
+}
+
+#pragma mark Session delegate methods
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
+{
+    // clean up
+    [self.outputStream close];
     self.progressBlock = nil;
     
-    [super cancel];
+    [super URLSession:session task:task didCompleteWithError:error];
 }
 
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-{
-    [super connection:connection didReceiveResponse:response];
-    
-    // update statistics
-    if (self.bytesExpected == 0 && response.expectedContentLength != NSURLResponseUnknownLength) {
-        self.bytesExpected = response.expectedContentLength;
-    }
-    self.bytesDownloaded = 0;
-
-    // set up output stream if available
-    if (self.outputStream) { // otherwise store data in memory in self.data
-        // create file for downloaded content
-        BOOL isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
-        if (!isStreamReady) {
-            [self.outputStream open];
-            isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
-        }
-    
-        if (!isStreamReady) {
-            [connection cancel];
-            
-            if (self.completionBlock)
-            {
-                NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeStorage
-                                                     detailedDescription:@"Could not open output stream"];
-                self.completionBlock(nil, cmisError);
-            }
-        }
-    }
-}
-
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
 {
     if (self.outputStream == nil) { // if there is no outputStream then store data in memory in self.data
-        [super connection:connection didReceiveData:data];
+        [super URLSession:session dataTask:dataTask didReceiveData:data];
     } else {
         const uint8_t *bytes = data.bytes;
         NSUInteger length = data.length;
@@ -160,8 +138,8 @@
         do {
             NSUInteger written = [self.outputStream write:&bytes[offset] maxLength:length - offset];
             if (written <= 0) {
-                CMISLogError(@"Error while writing downloaded data to file");
-                [connection cancel];
+                CMISLogError(@"Error while writing downloaded data to stream");
+                [session invalidateAndCancel];
                 return;
             } else {
                 offset += written;
@@ -171,31 +149,52 @@
     
     // update statistics
     self.bytesDownloaded += data.length;
-    // pass progress to progressBlock
+    
+    // pass progress to progressBlock, on the main thread
     if (self.progressBlock) {
-        self.progressBlock(self.bytesDownloaded, self.bytesExpected);
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (self.progressBlock) {
+                self.progressBlock(self.bytesDownloaded, self.bytesExpected);
+            }
+        });
+    }
+}
+
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
+{
+    // update statistics
+    if (self.bytesExpected == 0 && self.sessionTask.countOfBytesExpectedToReceive != NSURLSessionTransferSizeUnknown) {
+        self.bytesExpected = self.sessionTask.countOfBytesExpectedToReceive;
     }
     
-}
-
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
-{
-    [self.outputStream close];
-
-    self.progressBlock = nil;
-
-    [super connection:connection didFailWithError:error];
-}
-
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
-    [self.outputStream close];
-
-    self.progressBlock = nil;
-
-    [super connectionDidFinishLoading:connection];
+    self.bytesDownloaded = 0;
+    
+    // set up output stream if available
+    if (self.outputStream) { // otherwise store data in memory in self.data
+        // create file for downloaded content
+        BOOL isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
+        if (!isStreamReady) {
+            [self.outputStream open];
+            isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
+        }
+        
+        if (isStreamReady) {
+            [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
+        } else {
+            [session invalidateAndCancel];
+            
+            if (self.completionBlock)
+            {
+                NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeStorage
+                                                     detailedDescription:@"Could not open output stream"];
+                
+                // call the completion block on the main thread
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    self.completionBlock(nil, cmisError);
+                });
+            }
+        }
+    }
 }
 
 @end
diff --git a/ObjectiveCMIS/Utils/CMISHttpRequest.h b/ObjectiveCMIS/Utils/CMISHttpRequest.h
index 436d9a7..7dcaabc 100644
--- a/ObjectiveCMIS/Utils/CMISHttpRequest.h
+++ b/ObjectiveCMIS/Utils/CMISHttpRequest.h
@@ -23,10 +23,11 @@
 #import "CMISRequest.h"
 @class CMISAuthenticationProvider;
 
-@interface CMISHttpRequest : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate, CMISCancellableRequest>
+@interface CMISHttpRequest : NSObject <CMISCancellableRequest, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
 
 @property (nonatomic, assign) CMISHttpRequestMethod requestMethod;
-@property (nonatomic, strong) NSURLConnection *connection;
+@property (nonatomic, strong) NSURLSession *urlSession;
+@property (nonatomic, strong) NSURLSessionTask *sessionTask;
 @property (nonatomic, strong) NSData *requestBody;
 @property (nonatomic, strong) NSMutableData *responseBody;
 @property (nonatomic, strong) NSDictionary *additionalHeaders;
diff --git a/ObjectiveCMIS/Utils/CMISHttpRequest.m b/ObjectiveCMIS/Utils/CMISHttpRequest.m
index dd8aa59..d8a915c 100644
--- a/ObjectiveCMIS/Utils/CMISHttpRequest.m
+++ b/ObjectiveCMIS/Utils/CMISHttpRequest.m
@@ -101,20 +101,24 @@
         }
     }];
     
-    self.connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:NO];
+    // create session and task
+    self.urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
+                                                    delegate:self
+                                               delegateQueue:nil];
+    self.sessionTask = [self.urlSession dataTaskWithRequest:urlRequest];
+    
     CMISReachability *reachability = [CMISReachability networkReachability];
     
-    if (self.connection && reachability.hasNetworkConnection) {
-        [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
-        [self.connection start];
+    if (self.sessionTask && reachability.hasNetworkConnection) {
+        [self.sessionTask resume];
         startedRequest = YES;
     } else if (!reachability.hasNetworkConnection) {
         NSError *noConnectionError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeNoNetworkConnection detailedDescription:kCMISErrorDescriptionNoNetworkConnection];
-        [self connection:self.connection didFailWithError:noConnectionError];
+        [self URLSession:self.urlSession task:self.sessionTask didCompleteWithError:noConnectionError];
     }
     else {
         if (self.completionBlock) {
-            NSString *detailedDescription = [NSString stringWithFormat:@"Could not create connection to %@", urlRequest.URL];
+            NSString *detailedDescription = [NSString stringWithFormat:@"Could not connect to %@", urlRequest.URL];
             NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:detailedDescription];
             self.completionBlock(nil, cmisError);
         }
@@ -123,96 +127,84 @@
     return startedRequest;
 }
 
+#pragma mark CMISCancellableRequest method
+
 - (void)cancel
 {
-    if (self.connection) {
+    if (self.urlSession) {
         void (^completionBlock)(CMISHttpResponse *httpResponse, NSError *error);
         completionBlock = self.completionBlock; // remember completion block in order to invoke it after the connection was cancelled
         
-        self.completionBlock = nil; // prevent potential NSURLConnection delegate callbacks to invoke the completion block redundantly
+        self.completionBlock = nil; // prevent potential NSURLSession delegate callbacks to invoke the completion block redundantly
         
-        [self.connection cancel];
+        [self.urlSession invalidateAndCancel];
         
-        self.connection = nil;
+        self.urlSession = nil;
         
         NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeCancelled detailedDescription:@"Request was cancelled"];
         completionBlock(nil, cmisError);
     }
 }
 
+#pragma mark Session delegate methods
 
-- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
 {
-    return [self.authenticationProvider canAuthenticateAgainstProtectionSpace:protectionSpace];
+    [self.authenticationProvider updateWithHttpURLResponse:self.response];
+
+    if (self.completionBlock) {
+        
+        NSError *cmisError = nil;
+        CMISHttpResponse *httpResponse = nil;
+        
+        if (error) {
+            CMISErrorCodes cmisErrorCode = kCMISErrorCodeConnection;
+            
+            // swap error code if necessary
+            if (error.code == NSURLErrorCancelled) {
+                cmisErrorCode = kCMISErrorCodeCancelled;
+            } else if (error.code == kCMISErrorCodeNoNetworkConnection) {
+                cmisErrorCode = kCMISErrorCodeNoNetworkConnection;
+            }
+            
+            cmisError = [CMISErrors cmisError:error cmisErrorCode:cmisErrorCode];
+        } else {
+            // no error returned but we also need to check response code
+            httpResponse = [CMISHttpResponse responseUsingURLHTTPResponse:self.response data:self.responseBody];
+            if (![self checkStatusCodeForResponse:httpResponse httpRequestMethod:self.requestMethod error:&cmisError]) {
+                httpResponse = nil;
+            }
+        }
+        
+        // call the completion block on the main thread
+        dispatch_async(dispatch_get_main_queue(), ^{
+            self.completionBlock(httpResponse, cmisError);
+        });
+    }
+    
+    // clean up
+    self.sessionTask = nil;
+    self.urlSession = nil;
 }
 
-
-- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
 {
-    [self.authenticationProvider didCancelAuthenticationChallenge:challenge];
+    [self.responseBody appendData:data];
 }
 
-
-- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-{
-    [self.authenticationProvider didReceiveAuthenticationChallenge:challenge];
-}
-
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
 {
     self.responseBody = [[NSMutableData alloc] init];
     if ([response isKindOfClass:NSHTTPURLResponse.class]) {
         self.response = (NSHTTPURLResponse*)response;
     }
+    
+    completionHandler(NSURLSessionResponseAllow);
 }
 
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
 {
-    [self.responseBody appendData:data];
-}
-
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
-{
-    [self.authenticationProvider updateWithHttpURLResponse:self.response];
-
-    if (self.completionBlock) {
-        CMISErrorCodes cmisErrorCode = kCMISErrorCodeConnection;
-        
-        // swap error code if necessary
-        if (error.code == NSURLErrorCancelled) {
-            cmisErrorCode = kCMISErrorCodeCancelled;
-        } else if (error.code == kCMISErrorCodeNoNetworkConnection) {
-            cmisErrorCode = kCMISErrorCodeNoNetworkConnection;
-        }
-        
-        NSError *cmisError = [CMISErrors cmisError:error cmisErrorCode:cmisErrorCode];
-        self.completionBlock(nil, cmisError);
-    }
-    
-    self.completionBlock = nil;
-    self.connection = nil;
-}
-
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
-    [self.authenticationProvider updateWithHttpURLResponse:self.response];
-    
-    if (self.completionBlock) {
-        NSError *cmisError = nil;
-        CMISHttpResponse *httpResponse = [CMISHttpResponse responseUsingURLHTTPResponse:self.response data:self.responseBody];
-        if ([self checkStatusCodeForResponse:httpResponse httpRequestMethod:self.requestMethod error:&cmisError]) {
-            self.completionBlock(httpResponse, nil);
-        } else {
-            self.completionBlock(nil, cmisError);
-        }
-    }
-    
-    self.completionBlock = nil;
-    self.connection = nil;
+    [self.authenticationProvider didReceiveChallenge:challenge completionHandler:completionHandler];
 }
 
 - (BOOL)checkStatusCodeForResponse:(CMISHttpResponse *)response httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod error:(NSError **)error
diff --git a/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m b/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m
index a8a9e14..2dbcab9 100644
--- a/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m
+++ b/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m
@@ -42,7 +42,7 @@
 
 /**
  A category that extends the NSStream class in order to pair an inputstream with an outputstream.
- The input stream will be used by NSURLConnection via the HTTPBodyStream property of the URL request.
+ The input stream will be used by NSURLSession via the HTTPBodyStream property of the URL request.
  The paired output stream will buffer base64 encoded as well as XML data.
  
  NOTE: the original sample code also provides a method for backward compatibility w.r.t  iOS versions below 5.0
@@ -207,7 +207,8 @@
     return startSuccess;
 }
 
-#pragma CMISCancellableRequest method
+#pragma mark CMISCancellableRequest method
+
 - (void)cancel
 {
     self.progressBlock = nil;
@@ -218,68 +219,52 @@
     }
 }
 
-#pragma NSURLConnectionDataDelegate methods
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
+#pragma mark Session delegate methods
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
 {
-    [super connection:connection didReceiveResponse:response];
+    [super URLSession:session task:task didCompleteWithError:error];
     
-    self.bytesUploaded = 0;
+    if (self.useCombinedInputStream) {
+        if (error) {
+            [self stopSendWithStatus:@"connection is being terminated with error."];
+        } else {
+            [self stopSendWithStatus:@"Connection finished as expected."];
+        }
+    }
+    
+    self.progressBlock = nil;
 }
 
-- (void)connection:(NSURLConnection *)connection
-   didSendBodyData:(NSInteger)bytesWritten
- totalBytesWritten:(NSInteger)totalBytesWritten
-totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
+{
+    self.bytesUploaded = 0;
+    
+    [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
 {
     if (self.progressBlock) {
         if (self.useCombinedInputStream && self.base64Encoding) {
             // Show the actual transmitted raw data size to the user, not the base64 encoded size
-            totalBytesWritten = [CMISHttpUploadRequest rawEncodedLength:totalBytesWritten];
-            if (totalBytesWritten > totalBytesExpectedToWrite) {
-                totalBytesWritten = totalBytesExpectedToWrite;
+            totalBytesSent = [CMISHttpUploadRequest rawEncodedLength:totalBytesSent];
+            if (totalBytesSent > totalBytesExpectedToSend) {
+                totalBytesSent = totalBytesExpectedToSend;
             }
         }
         
         if (self.bytesExpected == 0) {
-            self.progressBlock((unsigned long long)totalBytesWritten, (unsigned long long)totalBytesExpectedToWrite);
+            self.progressBlock((unsigned long long)totalBytesSent, (unsigned long long)totalBytesExpectedToSend);
         } else {
-            self.progressBlock((unsigned long long)totalBytesWritten, self.bytesExpected);
+            self.progressBlock((unsigned long long)totalBytesSent, self.bytesExpected);
         }
     }
 }
 
+#pragma mark NSStreamDelegate method
 
 /**
- In addition to closing the connection we also have to close/reset all streams used in this class.
- This is for base64 encoding only
- */
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
-{
-    [super connection:connection didFailWithError:error];
-    
-    if (self.useCombinedInputStream) {
-        [self stopSendWithStatus:@"connection is being terminated with error."];
-    }
-    self.progressBlock = nil;
-}
-
-
-/**
- In addition to closing the connection we also have to close/reset all streams used in this class.
- This is for base64 encoding only
- */
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
-    [super connectionDidFinishLoading:connection];
-    if (self.useCombinedInputStream) {
-        [self stopSendWithStatus:@"Connection finished as expected."];
-    }
-    
-    self.progressBlock = nil;
-}
-
-#pragma NSStreamDelegate method
-/**
  For encoding base64 data - this is the meat of this class.
  The action is in the case where the eventCode == NSStreamEventHasSpaceAvailable
  
@@ -393,7 +378,7 @@
                 if (bytesWritten <= 0) {
                     [self stopSendWithStatus:@"Network write error"];
                     NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Network write error"];
-                    [self connection:nil didFailWithError:cmisError];
+                    [self URLSession:nil task:nil didCompleteWithError:cmisError];
                 } else {
                     self.bufferOffset += bytesWritten;
                 }
@@ -417,7 +402,7 @@
 }
 
 
-#pragma private methods
+#pragma mark Private methods
 
 - (void)prepareStreams
 {
@@ -468,13 +453,13 @@
     return 4 * adjustedThirdPartOfSize;
 }
 
-+ (NSUInteger)rawEncodedLength:(NSUInteger)base64EncodedSize
++ (unsigned long long)rawEncodedLength:(unsigned long long)base64EncodedSize
 {
     if (0 == base64EncodedSize) {
         return 0;
     }
     
-    NSUInteger adjustedFourthPartOfSize = (base64EncodedSize / 4) + ( (0 == base64EncodedSize % 4 ) ? 0 : 1 );
+    unsigned long long adjustedFourthPartOfSize = (base64EncodedSize / 4) + ( (0 == base64EncodedSize % 4 ) ? 0 : 1 );
     return 3 * adjustedFourthPartOfSize;
 }
 
@@ -486,9 +471,9 @@
     self.bufferOffset = 0;
     self.bufferLimit  = 0;
     self.dataBuffer = nil;
-    if (self.connection != nil) {
-        [self.connection cancel];
-        self.connection = nil;
+    if (self.urlSession != nil) {
+        [self.urlSession invalidateAndCancel];
+        self.urlSession = nil;
     }
     if (self.encoderStream != nil) {
         self.encoderStream.delegate = nil;