2024-08-16 12:08:41 +00:00
|
|
|
|
/**
|
2024-10-28 11:21:47 +00:00
|
|
|
|
* NSURLSessionTask.m
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2017-2024 Free Software Foundation, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Written by: Hugo Melder <hugo@algoriddim.com>
|
|
|
|
|
* Date: May 2024
|
|
|
|
|
*
|
|
|
|
|
* This file is part of GNUStep-base
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* If you are interested in a warranty or support for this source code,
|
|
|
|
|
* contact Scott Christley <scottc@net-community.com> for more information.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free
|
2024-11-07 13:37:59 +00:00
|
|
|
|
* Software Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
|
2024-10-28 11:21:47 +00:00
|
|
|
|
*/
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
#import "NSURLSessionPrivate.h"
|
|
|
|
|
#include <curl/curl.h>
|
|
|
|
|
#include <dispatch/dispatch.h>
|
|
|
|
|
#import "NSURLSessionTaskPrivate.h"
|
|
|
|
|
|
|
|
|
|
#import "Foundation/NSOperation.h"
|
|
|
|
|
#import "Foundation/NSPathUtilities.h"
|
|
|
|
|
#import "Foundation/NSFileManager.h"
|
|
|
|
|
#import "Foundation/NSFileHandle.h"
|
|
|
|
|
#import "Foundation/NSCharacterSet.h"
|
|
|
|
|
#import "Foundation/NSDictionary.h"
|
|
|
|
|
#import "Foundation/NSError.h"
|
|
|
|
|
#import "Foundation/NSData.h"
|
|
|
|
|
#import "Foundation/NSUUID.h"
|
|
|
|
|
#import "Foundation/NSValue.h"
|
|
|
|
|
#import "Foundation/NSURL.h"
|
|
|
|
|
#import "Foundation/NSURLError.h"
|
|
|
|
|
#import "Foundation/NSURLResponse.h"
|
|
|
|
|
#import "Foundation/NSHTTPCookie.h"
|
|
|
|
|
#import "Foundation/NSStream.h"
|
|
|
|
|
|
|
|
|
|
#import "GNUstepBase/NSDebug+GNUstepBase.h" /* For NSDebugMLLog */
|
|
|
|
|
#import "GNUstepBase/NSObject+GNUstepBase.h" /* For -[NSObject notImplemented] */
|
|
|
|
|
|
|
|
|
|
#import "GSURLPrivate.h"
|
|
|
|
|
|
|
|
|
|
@interface _GSInsensitiveDictionary : NSDictionary
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@interface _GSMutableInsensitiveDictionary : NSMutableDictionary
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
GS_DECLARE const float NSURLSessionTaskPriorityDefault = 0.5;
|
|
|
|
|
GS_DECLARE const float NSURLSessionTaskPriorityLow = 0.0;
|
|
|
|
|
GS_DECLARE const float NSURLSessionTaskPriorityHigh = 1.0;
|
|
|
|
|
|
|
|
|
|
GS_DECLARE const int64_t NSURLSessionTransferSizeUnknown = -1;
|
|
|
|
|
|
|
|
|
|
/* Initialised in +[NSURLSessionTask initialize] */
|
|
|
|
|
static Class dataTaskClass;
|
|
|
|
|
static Class downloadTaskClass;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
static SEL didReceiveDataSel;
|
|
|
|
|
static SEL didReceiveResponseSel;
|
|
|
|
|
static SEL didCompleteWithErrorSel;
|
|
|
|
|
static SEL didFinishDownloadingToURLSel;
|
|
|
|
|
static SEL didWriteDataSel;
|
|
|
|
|
static SEL needNewBodyStreamSel;
|
|
|
|
|
static SEL willPerformHTTPRedirectionSel;
|
|
|
|
|
|
|
|
|
|
static NSString * taskTransferDataKey = @"transferData";
|
|
|
|
|
static NSString * taskTemporaryFileLocationKey = @"tempFileLocation";
|
|
|
|
|
static NSString * taskTemporaryFileHandleKey = @"tempFileHandle";
|
|
|
|
|
static NSString * taskInputStreamKey = @"inputStream";
|
|
|
|
|
static NSString * taskUploadData = @"uploadData";
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Translate WinSock2 Error Codes */
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
static inline NSInteger
|
|
|
|
|
translateWinSockToPOSIXError(NSInteger err)
|
|
|
|
|
{
|
|
|
|
|
switch (err)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
case WSAEADDRINUSE:
|
|
|
|
|
err = EADDRINUSE;
|
|
|
|
|
break;
|
|
|
|
|
case WSAEADDRNOTAVAIL:
|
|
|
|
|
err = EADDRNOTAVAIL;
|
|
|
|
|
break;
|
|
|
|
|
case WSAEINPROGRESS:
|
|
|
|
|
err = EINPROGRESS;
|
|
|
|
|
break;
|
|
|
|
|
case WSAECONNRESET:
|
|
|
|
|
err = ECONNRESET;
|
|
|
|
|
break;
|
|
|
|
|
case WSAECONNABORTED:
|
|
|
|
|
err = ECONNABORTED;
|
|
|
|
|
break;
|
|
|
|
|
case WSAECONNREFUSED:
|
|
|
|
|
err = ECONNREFUSED;
|
|
|
|
|
break;
|
|
|
|
|
case WSAEHOSTUNREACH:
|
|
|
|
|
err = EHOSTUNREACH;
|
|
|
|
|
break;
|
|
|
|
|
case WSAENETUNREACH:
|
|
|
|
|
err = ENETUNREACH;
|
|
|
|
|
break;
|
|
|
|
|
case WSAETIMEDOUT:
|
|
|
|
|
err = ETIMEDOUT;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
} /* switch */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* translateWinSockToPOSIXError */
|
|
|
|
|
#endif /* ifdef _WIN32 */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
static inline NSError *
|
2024-10-28 11:21:47 +00:00
|
|
|
|
errorForCURLcode(CURL * handle, CURLcode code,
|
|
|
|
|
char errorBuffer[CURL_ERROR_SIZE])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * curlErrorString;
|
|
|
|
|
NSString * errorString;
|
|
|
|
|
NSDictionary * userInfo;
|
|
|
|
|
NSError * error;
|
|
|
|
|
NSInteger urlError = NSURLErrorUnknown;
|
|
|
|
|
NSInteger posixError;
|
|
|
|
|
NSInteger osError = 0;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
if (NULL == handle || CURLE_OK == code)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
errorString = [[NSString alloc] initWithCString: errorBuffer];
|
|
|
|
|
curlErrorString =
|
|
|
|
|
[[NSString alloc] initWithCString: curl_easy_strerror(code)];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Get errno number from the last connect failure.
|
|
|
|
|
*
|
|
|
|
|
* libcurl errors that may have saved errno are:
|
|
|
|
|
* - CURLE_COULDNT_CONNECT
|
|
|
|
|
* - CURLE_FAILED_INIT
|
|
|
|
|
* - CURLE_INTERFACE_FAILED
|
|
|
|
|
* - CURLE_OPERATION_TIMEDOUT
|
|
|
|
|
* - CURLE_RECV_ERROR
|
|
|
|
|
* - CURLE_SEND_ERROR
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_getinfo(handle, CURLINFO_OS_ERRNO, &osError);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
posixError = translateWinSockToPOSIXError(osError);
|
|
|
|
|
#else
|
|
|
|
|
posixError = osError;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Translate libcurl to NSURLError codes */
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
case CURLE_UNSUPPORTED_PROTOCOL:
|
|
|
|
|
urlError = NSURLErrorUnsupportedURL;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_URL_MALFORMAT:
|
|
|
|
|
urlError = NSURLErrorBadURL;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Connection Errors */
|
|
|
|
|
case CURLE_COULDNT_RESOLVE_PROXY:
|
|
|
|
|
case CURLE_COULDNT_RESOLVE_HOST:
|
|
|
|
|
urlError = NSURLErrorDNSLookupFailed;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_QUIC_CONNECT_ERROR:
|
|
|
|
|
case CURLE_COULDNT_CONNECT:
|
|
|
|
|
urlError = NSURLErrorCannotConnectToHost;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_OPERATION_TIMEDOUT:
|
|
|
|
|
urlError = NSURLErrorTimedOut;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_FILESIZE_EXCEEDED:
|
|
|
|
|
urlError = NSURLErrorDataLengthExceedsMaximum;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_LOGIN_DENIED:
|
|
|
|
|
urlError = NSURLErrorUserAuthenticationRequired;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Response Errors */
|
|
|
|
|
case CURLE_WEIRD_SERVER_REPLY:
|
|
|
|
|
urlError = NSURLErrorBadServerResponse;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_REMOTE_ACCESS_DENIED:
|
|
|
|
|
urlError = NSURLErrorNoPermissionsToReadFile;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_GOT_NOTHING:
|
|
|
|
|
urlError = NSURLErrorZeroByteResource;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_RECV_ERROR:
|
|
|
|
|
urlError = NSURLErrorResourceUnavailable;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Callback Errors */
|
|
|
|
|
case CURLE_ABORTED_BY_CALLBACK:
|
|
|
|
|
case CURLE_WRITE_ERROR:
|
|
|
|
|
errorString = @"Transfer aborted by user";
|
|
|
|
|
urlError = NSURLErrorCancelled;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* SSL Errors */
|
|
|
|
|
case CURLE_SSL_CACERT_BADFILE:
|
|
|
|
|
case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
|
|
|
|
|
case CURLE_SSL_CONNECT_ERROR:
|
|
|
|
|
urlError = NSURLErrorSecureConnectionFailed;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_SSL_CERTPROBLEM:
|
|
|
|
|
urlError = NSURLErrorClientCertificateRejected;
|
|
|
|
|
break;
|
|
|
|
|
case CURLE_SSL_INVALIDCERTSTATUS:
|
|
|
|
|
case CURLE_SSL_ISSUER_ERROR:
|
|
|
|
|
urlError = NSURLErrorServerCertificateUntrusted;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
urlError = NSURLErrorUnknown;
|
|
|
|
|
break;
|
|
|
|
|
} /* switch */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Adjust error based on underlying OS error if available */
|
|
|
|
|
if (code == CURLE_COULDNT_CONNECT || code == CURLE_RECV_ERROR
|
|
|
|
|
|| code == CURLE_SEND_ERROR)
|
|
|
|
|
{
|
|
|
|
|
switch (posixError)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
case EADDRINUSE:
|
|
|
|
|
urlError = NSURLErrorCannotConnectToHost;
|
|
|
|
|
break;
|
|
|
|
|
case EADDRNOTAVAIL:
|
|
|
|
|
urlError = NSURLErrorCannotFindHost;
|
|
|
|
|
break;
|
|
|
|
|
case ECONNREFUSED:
|
|
|
|
|
urlError = NSURLErrorCannotConnectToHost;
|
|
|
|
|
break;
|
|
|
|
|
case ENETUNREACH:
|
|
|
|
|
urlError = NSURLErrorDNSLookupFailed;
|
|
|
|
|
break;
|
|
|
|
|
case ETIMEDOUT:
|
|
|
|
|
urlError = NSURLErrorTimedOut;
|
|
|
|
|
break;
|
|
|
|
|
default: /* Do not alter urlError if we have no match */
|
|
|
|
|
break;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userInfo = @{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
@"_curlErrorCode": [NSNumber numberWithInteger: code],
|
|
|
|
|
@"_curlErrorString": curlErrorString,
|
2024-08-16 12:08:41 +00:00
|
|
|
|
/* This is the raw POSIX error or WinSock2 Error Code depending on OS */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
@"_errno": [NSNumber numberWithInteger: osError],
|
|
|
|
|
NSLocalizedDescriptionKey: errorString
|
2024-08-16 12:08:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
error = [NSError errorWithDomain: NSURLErrorDomain
|
|
|
|
|
code: urlError
|
|
|
|
|
userInfo: userInfo];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[curlErrorString release];
|
|
|
|
|
[errorString release];
|
|
|
|
|
|
|
|
|
|
return error;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* errorForCURLcode */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* CURLOPT_PROGRESSFUNCTION: progress reports by libcurl */
|
|
|
|
|
static int
|
2024-10-28 11:21:47 +00:00
|
|
|
|
progress_callback(void * clientp, curl_off_t dltotal, curl_off_t dlnow,
|
2024-08-16 12:08:41 +00:00
|
|
|
|
curl_off_t ultotal, curl_off_t ulnow)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionTask * task = clientp;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Returning -1 from this callback makes libcurl abort the transfer and return
|
|
|
|
|
* CURLE_ABORTED_BY_CALLBACK.
|
|
|
|
|
*/
|
|
|
|
|
if (YES == [task _shouldStopTransfer])
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setCountOfBytesReceived: dlnow];
|
|
|
|
|
[task _setCountOfBytesSent: ulnow];
|
|
|
|
|
[task _setCountOfBytesExpectedToSend: ultotal];
|
|
|
|
|
[task _setCountOfBytesExpectedToReceive: dltotal];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CURLOPT_HEADERFUNCTION: callback for received headers
|
|
|
|
|
*
|
|
|
|
|
* This function is called for each header line and is called
|
|
|
|
|
* again when a redirect or authentication occurs.
|
|
|
|
|
*
|
|
|
|
|
* libcurl does not unfold HTTP "folded headers" (deprecated since RFC 7230).
|
|
|
|
|
*/
|
|
|
|
|
size_t
|
2024-10-28 11:21:47 +00:00
|
|
|
|
header_callback(char * ptr, size_t size, size_t nitems, void * userdata)
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionTask * task;
|
|
|
|
|
NSMutableDictionary * taskData;
|
|
|
|
|
NSMutableDictionary * headerFields;
|
|
|
|
|
NSString * headerLine;
|
|
|
|
|
NSInteger headerCallbackCount;
|
|
|
|
|
NSRange range;
|
|
|
|
|
NSCharacterSet * set;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
task = (NSURLSessionTask *)userdata;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
taskData = [task _taskData];
|
2024-10-28 11:21:47 +00:00
|
|
|
|
headerFields = [taskData objectForKey: @"headers"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
headerCallbackCount = [task _headerCallbackCount] + 1;
|
|
|
|
|
set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setHeaderCallbackCount: headerCallbackCount];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
if (nil == headerFields)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ Could not find 'headers' key in taskData",
|
|
|
|
|
task);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
headerLine = [[NSString alloc] initWithBytes: ptr
|
|
|
|
|
length: nitems
|
|
|
|
|
encoding: NSUTF8StringEncoding];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
// First line is the HTTP Version
|
|
|
|
|
if (1 == headerCallbackCount)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: headerLine forKey: @"version"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[headerLine release];
|
|
|
|
|
return size * nitems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Header fields can be extended over multiple lines by preceding
|
|
|
|
|
* each extra line with at least one SP or HT (RFC 2616).
|
|
|
|
|
*
|
|
|
|
|
* This is known as line folding. We append the value to the
|
|
|
|
|
* previous header's value.
|
|
|
|
|
*/
|
|
|
|
|
if ((ptr[0] == ' ') || (ptr[0] == '\t'))
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * key;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if (nil != (key = [taskData objectForKey: @"lastHeaderKey"]))
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * value;
|
|
|
|
|
NSString * trimmedLine;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
value = [headerFields objectForKey: key];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if (!value)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSError * error;
|
|
|
|
|
NSString * errorDescription;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
errorDescription = [NSString
|
2024-10-28 11:21:47 +00:00
|
|
|
|
stringWithFormat:
|
|
|
|
|
@"Header is line folded but previous header "
|
|
|
|
|
@"key '%@' does not have an entry",
|
|
|
|
|
key];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
error =
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[NSError errorWithDomain: NSURLErrorDomain
|
|
|
|
|
code: NSURLErrorCancelled
|
|
|
|
|
userInfo: @{
|
|
|
|
|
NSLocalizedDescriptionKey: errorDescription
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: error forKey: NSUnderlyingErrorKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[headerLine release];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
trimmedLine = [headerLine stringByTrimmingCharactersInSet: set];
|
|
|
|
|
value = [value stringByAppendingString: trimmedLine];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[headerFields setObject: value forKey: key];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[headerLine release];
|
|
|
|
|
return size * nitems;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
range = [headerLine rangeOfString: @":"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if (NSNotFound != range.location)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * key;
|
|
|
|
|
NSString * value;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
key = [headerLine substringToIndex: range.location];
|
|
|
|
|
value = [headerLine substringFromIndex: range.location + 1];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Remove LWS from key and value */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
key = [key stringByTrimmingCharactersInSet: set];
|
|
|
|
|
value = [value stringByTrimmingCharactersInSet: set];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[headerFields setObject: value forKey: key];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
/* Used for line unfolding */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: key forKey: @"lastHeaderKey"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[headerLine release];
|
|
|
|
|
return size * nitems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[headerLine release];
|
|
|
|
|
|
|
|
|
|
/* Final Header Line:
|
|
|
|
|
*
|
|
|
|
|
* If this is the initial request (not a redirect) and delegate updates are
|
|
|
|
|
* enabled, notify the delegate about the initial response.
|
|
|
|
|
*/
|
|
|
|
|
if (nitems > 1 && (ptr[0] == '\r') && (ptr[1] == '\n'))
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSession * session;
|
|
|
|
|
id delegate;
|
|
|
|
|
NSHTTPURLResponse * response;
|
|
|
|
|
NSString * version;
|
|
|
|
|
NSString * urlString;
|
|
|
|
|
NSURL * url;
|
|
|
|
|
CURL * handle;
|
|
|
|
|
char * effURL;
|
|
|
|
|
NSInteger numberOfRedirects = 0;
|
|
|
|
|
NSInteger statusCode = 0;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
session = [task _session];
|
|
|
|
|
delegate = [task delegate];
|
|
|
|
|
handle = [task _easyHandle];
|
|
|
|
|
numberOfRedirects = [task _numberOfRedirects] + 1;
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setNumberOfRedirects: numberOfRedirects];
|
|
|
|
|
[task _setHeaderCallbackCount: 0];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &statusCode);
|
|
|
|
|
curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &effURL);
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if (nil == (version = [taskData objectForKey: @"version"]))
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
/* Default to HTTP/1.0 if no data is available */
|
|
|
|
|
version = @"HTTP/1.0";
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ version=%@ status=%ld found %ld headers",
|
|
|
|
|
task,
|
|
|
|
|
version,
|
|
|
|
|
statusCode,
|
|
|
|
|
[headerFields count]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
urlString = [[NSString alloc] initWithCString: effURL];
|
|
|
|
|
url = [NSURL URLWithString: urlString];
|
|
|
|
|
response = [[NSHTTPURLResponse alloc] initWithURL: url
|
|
|
|
|
statusCode: statusCode
|
|
|
|
|
HTTPVersion: version
|
|
|
|
|
headerFields: [headerFields copy]];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setCookiesFromHeaders: headerFields];
|
|
|
|
|
[task _setResponse: response];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* URL redirection handling for 3xx status codes, if delegate updates are
|
|
|
|
|
* enabled.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: The URLSession API does not provide a way to limit redirection
|
|
|
|
|
* attempts.
|
|
|
|
|
*/
|
|
|
|
|
if ([task _properties] & GSURLSessionUpdatesDelegate && statusCode >= 300
|
|
|
|
|
&& statusCode < 400)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * location;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* RFC 7231: 7.1.2 Location [Header]
|
|
|
|
|
* Location = URI-reference
|
|
|
|
|
*
|
|
|
|
|
* The field value consists of a single URI-reference. When it has
|
|
|
|
|
* the form of a relative reference ([RFC3986], Section 4.2), the
|
|
|
|
|
* final value is computed by resolving it against the effective
|
|
|
|
|
* request URI
|
|
|
|
|
* ([RFC3986], Section 5).
|
|
|
|
|
*/
|
2024-10-28 11:21:47 +00:00
|
|
|
|
location = [headerFields objectForKey: @"Location"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if (nil != location)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURL * redirectURL;
|
|
|
|
|
NSMutableURLRequest * newRequest;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* baseURL is only used, if location is a relative reference */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
redirectURL = [NSURL URLWithString: location relativeToURL: url];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
newRequest = [[task originalRequest] mutableCopy];
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[newRequest setURL: redirectURL];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ status=%ld has Location header. Prepare "
|
|
|
|
|
@"for redirection with url=%@",
|
|
|
|
|
task,
|
|
|
|
|
statusCode,
|
|
|
|
|
redirectURL);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([delegate respondsToSelector: willPerformHTTPRedirectionSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ ask delegate for redirection "
|
|
|
|
|
@"permission. Pausing handle.",
|
|
|
|
|
task);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
curl_easy_pause(handle, CURLPAUSE_ALL);
|
|
|
|
|
|
|
|
|
|
[[session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
void (^completionHandler)(NSURLRequest *) = ^(
|
|
|
|
|
NSURLRequest * userRequest) {
|
|
|
|
|
/* Changes are dispatched onto workqueue */
|
|
|
|
|
dispatch_async(
|
|
|
|
|
[session _workQueue],
|
|
|
|
|
^{
|
|
|
|
|
if (NULL == userRequest)
|
|
|
|
|
{
|
|
|
|
|
curl_easy_pause(handle, CURLPAUSE_CONT);
|
|
|
|
|
[task _setShouldStopTransfer: YES];
|
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ willPerformHTTPRedirection "
|
|
|
|
|
@"completionHandler called with nil "
|
|
|
|
|
@"request",
|
|
|
|
|
task);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSString * newURLString;
|
|
|
|
|
|
|
|
|
|
newURLString = [[userRequest URL] absoluteString];
|
|
|
|
|
|
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ willPerformHTTPRedirection "
|
|
|
|
|
@"delegate completionHandler called "
|
|
|
|
|
@"with new URL %@",
|
|
|
|
|
task,
|
|
|
|
|
newURLString);
|
|
|
|
|
|
|
|
|
|
/* Remove handle for reconfiguration */
|
|
|
|
|
[session _removeHandle: handle];
|
|
|
|
|
|
|
|
|
|
/* Reset statistics */
|
|
|
|
|
[task _setCountOfBytesReceived: 0];
|
|
|
|
|
[task _setCountOfBytesSent: 0];
|
|
|
|
|
[task _setCountOfBytesExpectedToReceive: 0];
|
|
|
|
|
[task _setCountOfBytesExpectedToSend: 0];
|
|
|
|
|
|
|
|
|
|
[task _setCurrentRequest: userRequest];
|
|
|
|
|
|
|
|
|
|
/* Update URL in easy handle */
|
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
handle,
|
|
|
|
|
CURLOPT_URL,
|
|
|
|
|
[newURLString UTF8String]);
|
|
|
|
|
curl_easy_pause(handle, CURLPAUSE_CONT);
|
|
|
|
|
|
|
|
|
|
[session _addHandle: handle];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
[delegate URLSession: session
|
|
|
|
|
task: task
|
|
|
|
|
willPerformHTTPRedirection: response
|
|
|
|
|
newRequest: newRequest
|
|
|
|
|
completionHandler: completionHandler];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[headerFields removeAllObjects];
|
|
|
|
|
return size * nitems;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ status=%ld has Location header but "
|
|
|
|
|
@"delegate does not respond to "
|
|
|
|
|
@"willPerformHTTPRedirection:. Redirecting to Location %@",
|
2024-10-28 11:21:47 +00:00
|
|
|
|
task,
|
|
|
|
|
statusCode,
|
|
|
|
|
redirectURL);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Remove handle for reconfiguration */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[session _removeHandle: handle];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
handle,
|
|
|
|
|
CURLOPT_URL,
|
|
|
|
|
[[redirectURL absoluteString] UTF8String]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Reset statistics */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setCountOfBytesReceived: 0];
|
|
|
|
|
[task _setCountOfBytesSent: 0];
|
|
|
|
|
[task _setCountOfBytesExpectedToReceive: 0];
|
|
|
|
|
[task _setCountOfBytesExpectedToSend: 0];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task _setCurrentRequest: newRequest];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Re-add handle to session */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[session _addHandle: handle];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[headerFields removeAllObjects];
|
|
|
|
|
return size * nitems;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSError * error;
|
|
|
|
|
NSString * errorString;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
errorString = [NSString
|
2024-10-28 11:21:47 +00:00
|
|
|
|
stringWithFormat:
|
|
|
|
|
@"task=%@ status=%ld has no Location header",
|
|
|
|
|
task, statusCode];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
error = [NSError
|
2024-10-28 11:21:47 +00:00
|
|
|
|
errorWithDomain: NSURLErrorDomain
|
|
|
|
|
code: NSURLErrorBadServerResponse
|
|
|
|
|
userInfo: @{ NSLocalizedDescriptionKey:
|
|
|
|
|
errorString }];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
NSDebugLLog(GS_NSURLSESSION_DEBUG_KEY, @"%@", errorString);
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: error forKey: NSUnderlyingErrorKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[headerFields removeAllObjects];
|
|
|
|
|
|
|
|
|
|
/* URLSession:dataTask:didReceiveResponse:completionHandler:
|
|
|
|
|
* is called *after* all potential redirections are handled.
|
|
|
|
|
*
|
|
|
|
|
* FIXME: Enforce this and implement a custom redirect system
|
|
|
|
|
*/
|
|
|
|
|
if ([task _properties] & GSURLSessionUpdatesDelegate &&
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[task isKindOfClass: dataTaskClass] &&
|
|
|
|
|
[delegate respondsToSelector: didReceiveResponseSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
dispatch_queue_t queue;
|
|
|
|
|
|
|
|
|
|
queue = [session _workQueue];
|
|
|
|
|
/* Pause until the completion handler is called */
|
|
|
|
|
curl_easy_pause(handle, CURLPAUSE_ALL);
|
|
|
|
|
|
|
|
|
|
[[session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[delegate URLSession: session
|
|
|
|
|
dataTask: (NSURLSessionDataTask *)task
|
|
|
|
|
didReceiveResponse: response
|
|
|
|
|
completionHandler:^(
|
|
|
|
|
NSURLSessionResponseDisposition disposition) {
|
|
|
|
|
/* FIXME: Implement NSURLSessionResponseBecomeDownload */
|
|
|
|
|
if (disposition == NSURLSessionResponseCancel)
|
|
|
|
|
{
|
|
|
|
|
[task _setShouldStopTransfer: YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unpause easy handle */
|
|
|
|
|
dispatch_async(
|
|
|
|
|
queue,
|
|
|
|
|
^{
|
|
|
|
|
curl_easy_pause(handle, CURLPAUSE_CONT);
|
|
|
|
|
});
|
|
|
|
|
}];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[urlString release];
|
|
|
|
|
[response release];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return size * nitems;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* header_callback */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* CURLOPT_READFUNCTION: read callback for data uploads */
|
|
|
|
|
size_t
|
2024-10-28 11:21:47 +00:00
|
|
|
|
read_callback(char * buffer, size_t size, size_t nitems, void * userdata)
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSession * session;
|
|
|
|
|
NSURLSessionTask * task;
|
|
|
|
|
NSMutableDictionary * taskData;
|
|
|
|
|
NSInputStream * stream;
|
|
|
|
|
NSInteger bytesWritten;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
task = (NSURLSessionTask *)userdata;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
session = [task _session];
|
|
|
|
|
taskData = [task _taskData];
|
2024-10-28 11:21:47 +00:00
|
|
|
|
stream = [taskData objectForKey: taskInputStreamKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
if (nil == stream)
|
|
|
|
|
{
|
|
|
|
|
id<NSURLSessionTaskDelegate> delegate = [task delegate];
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ requesting new body stream from delegate",
|
|
|
|
|
task);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([delegate respondsToSelector: needNewBodyStreamSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
[[[task _session] delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[delegate URLSession: session
|
|
|
|
|
task: task
|
|
|
|
|
needNewBodyStream:^(NSInputStream * bodyStream) {
|
|
|
|
|
/* Add input stream to task data */
|
|
|
|
|
[taskData setObject: bodyStream forKey: taskInputStreamKey];
|
|
|
|
|
/* Continue with the transfer */
|
|
|
|
|
curl_easy_pause([task _easyHandle], CURLPAUSE_CONT);
|
|
|
|
|
}];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return CURL_READFUNC_PAUSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDebugLLog(
|
|
|
|
|
GS_NSURLSESSION_DEBUG_KEY,
|
|
|
|
|
@"task=%@ no input stream was given and delegate does "
|
|
|
|
|
@"not respond to URLSession:task:needNewBodyStream:",
|
|
|
|
|
task);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return CURL_READFUNC_ABORT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
bytesWritten = [stream read: (uint8_t *)buffer maxLength: (size * nitems)];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
/* An error occured while reading from the inputStream */
|
|
|
|
|
if (bytesWritten < 0)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSError * error;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
error = [NSError
|
2024-10-28 11:21:47 +00:00
|
|
|
|
errorWithDomain: NSURLErrorDomain
|
|
|
|
|
code: NSURLErrorCancelled
|
|
|
|
|
userInfo: @{
|
|
|
|
|
NSLocalizedDescriptionKey:
|
|
|
|
|
@"An error occured while reading from the body stream",
|
|
|
|
|
NSUnderlyingErrorKey: [stream streamError]
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}];
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: error forKey: NSUnderlyingErrorKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
return CURL_READFUNC_ABORT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bytesWritten;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* read_callback */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* CURLOPT_WRITEFUNCTION: callback for writing received data from easy handle */
|
|
|
|
|
static size_t
|
2024-10-28 11:21:47 +00:00
|
|
|
|
write_callback(char * ptr, size_t size, size_t nmemb, void * userdata)
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionTask * task;
|
|
|
|
|
NSURLSession * session;
|
|
|
|
|
NSMutableDictionary * taskData;
|
|
|
|
|
NSData * dataFragment;
|
|
|
|
|
NSInteger properties;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
task = (NSURLSessionTask *)userdata;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
session = [task _session];
|
|
|
|
|
taskData = [task _taskData];
|
2024-10-28 11:21:47 +00:00
|
|
|
|
dataFragment = [[NSData alloc] initWithBytes: ptr length: (size * nmemb)];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
properties = [task _properties];
|
|
|
|
|
|
|
|
|
|
if (properties & GSURLSessionStoresDataInMemory)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSMutableData * data;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
data = [taskData objectForKey: taskTransferDataKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if (!data)
|
|
|
|
|
{
|
|
|
|
|
data = [[NSMutableData alloc] init];
|
|
|
|
|
/* Strong reference maintained by taskData */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: data forKey: taskTransferDataKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
[data release];
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[data appendData: dataFragment];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
else if (properties & GSURLSessionWritesDataToFile)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSFileHandle * handle;
|
|
|
|
|
NSError * error = NULL;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
// Get a temporary file path and create a file handle
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if (nil == (handle = [taskData objectForKey: taskTemporaryFileHandleKey]))
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
handle = [task _createTemporaryFileHandleWithError: &error];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* We add the error to taskData as an underlying error */
|
|
|
|
|
if (NULL != error)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[taskData setObject: error forKey: NSUnderlyingErrorKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
[dataFragment release];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[handle writeData: dataFragment];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Notify delegate */
|
|
|
|
|
if (properties & GSURLSessionUpdatesDelegate)
|
|
|
|
|
{
|
|
|
|
|
id delegate = [task delegate];
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([task isKindOfClass: dataTaskClass] &&
|
|
|
|
|
[delegate respondsToSelector: didReceiveDataSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
[[session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[delegate URLSession: session
|
|
|
|
|
dataTask: (NSURLSessionDataTask *)task
|
|
|
|
|
didReceiveData: dataFragment];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Notify delegate about the download process */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([task isKindOfClass: downloadTaskClass] &&
|
|
|
|
|
[delegate respondsToSelector: didWriteDataSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionDownloadTask * downloadTask;
|
|
|
|
|
int64_t bytesWritten;
|
|
|
|
|
int64_t totalBytesWritten;
|
|
|
|
|
int64_t totalBytesExpectedToReceive;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
downloadTask = (NSURLSessionDownloadTask *)task;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
bytesWritten = [dataFragment length];
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[downloadTask _updateCountOfBytesWritten: bytesWritten];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
totalBytesWritten = [downloadTask _countOfBytesWritten];
|
|
|
|
|
totalBytesExpectedToReceive =
|
|
|
|
|
[downloadTask countOfBytesExpectedToReceive];
|
|
|
|
|
|
|
|
|
|
[[session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[delegate URLSession: session
|
|
|
|
|
downloadTask: downloadTask
|
|
|
|
|
didWriteData: bytesWritten
|
|
|
|
|
totalBytesWritten: totalBytesWritten
|
|
|
|
|
totalBytesExpectedToWrite: totalBytesExpectedToReceive];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[dataFragment release];
|
|
|
|
|
return size * nmemb;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* write_callback */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSURLSessionTask
|
|
|
|
|
{
|
|
|
|
|
_Atomic(BOOL) _shouldStopTransfer;
|
|
|
|
|
|
|
|
|
|
/* Opaque value for storing task specific properties */
|
|
|
|
|
NSInteger _properties;
|
|
|
|
|
|
|
|
|
|
/* Internal task data */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSMutableDictionary * _taskData;
|
|
|
|
|
NSInteger _numberOfRedirects;
|
|
|
|
|
NSInteger _headerCallbackCount;
|
|
|
|
|
NSUInteger _suspendCount;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
char _curlErrorBuffer[CURL_ERROR_SIZE];
|
|
|
|
|
struct curl_slist * _headerList;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
CURL * _easyHandle;
|
|
|
|
|
NSURLSession * _session;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
+ (void) initialize
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
dataTaskClass = [NSURLSessionDataTask class];
|
|
|
|
|
downloadTaskClass = [NSURLSessionDownloadTask class];
|
|
|
|
|
didReceiveDataSel = @selector(URLSession:dataTask:didReceiveData:);
|
|
|
|
|
didReceiveResponseSel =
|
|
|
|
|
@selector(URLSession:dataTask:didReceiveResponse:completionHandler:);
|
|
|
|
|
didCompleteWithErrorSel = @selector(URLSession:task:didCompleteWithError:);
|
|
|
|
|
didFinishDownloadingToURLSel =
|
|
|
|
|
@selector(URLSession:downloadTask:didFinishDownloadingToURL:);
|
|
|
|
|
didWriteDataSel = @selector
|
|
|
|
|
(URLSession:
|
2024-10-28 11:21:47 +00:00
|
|
|
|
downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
needNewBodyStreamSel = @selector(URLSession:task:needNewBodyStream:);
|
|
|
|
|
willPerformHTTPRedirectionSel = @selector
|
|
|
|
|
(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (instancetype) initWithSession: (NSURLSession *)session
|
|
|
|
|
request: (NSURLRequest *)request
|
|
|
|
|
taskIdentifier: (NSUInteger)identifier
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * httpMethod;
|
|
|
|
|
NSData * certificateBlob;
|
|
|
|
|
NSURL * url;
|
|
|
|
|
NSDictionary * immConfigHeaders;
|
|
|
|
|
NSURLSessionConfiguration * configuration;
|
|
|
|
|
NSHTTPCookieStorage * storage;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
_GSMutableInsensitiveDictionary * requestHeaders = nil;
|
|
|
|
|
_GSMutableInsensitiveDictionary * configHeaders = nil;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
_taskIdentifier = identifier;
|
|
|
|
|
_taskData = [[NSMutableDictionary alloc] init];
|
|
|
|
|
_shouldStopTransfer = NO;
|
|
|
|
|
_numberOfRedirects = -1;
|
|
|
|
|
_headerCallbackCount = 0;
|
|
|
|
|
|
|
|
|
|
ASSIGNCOPY(_originalRequest, request);
|
|
|
|
|
ASSIGNCOPY(_currentRequest, request);
|
|
|
|
|
|
|
|
|
|
httpMethod = [[_originalRequest HTTPMethod] lowercaseString];
|
|
|
|
|
url = [_originalRequest URL];
|
|
|
|
|
requestHeaders = [[_originalRequest _insensitiveHeaders] mutableCopy];
|
|
|
|
|
configuration = [session configuration];
|
|
|
|
|
|
|
|
|
|
/* Only retain the session once the -resume method is called
|
|
|
|
|
* and release the session as the last thing done once the
|
|
|
|
|
* task has completed. This avoids a retain loop causing
|
|
|
|
|
* session and tasks to be leaked.
|
|
|
|
|
*/
|
|
|
|
|
_session = session;
|
|
|
|
|
_suspendCount = 0;
|
|
|
|
|
_state = NSURLSessionTaskStateSuspended;
|
|
|
|
|
_curlErrorBuffer[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/* Configure initial task data
|
|
|
|
|
*/
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_taskData setObject: [NSMutableDictionary new] forKey: @"headers"];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Easy Handle Configuration
|
|
|
|
|
*/
|
|
|
|
|
_easyHandle = curl_easy_init();
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([@"head" isEqualToString: httpMethod])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_NOBODY, 1L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup upload data if a HTTPBody or HTTPBodyStream is present in the
|
|
|
|
|
* URLRequest
|
|
|
|
|
*/
|
|
|
|
|
if (nil != [_originalRequest HTTPBody])
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSData * body = [_originalRequest HTTPBody];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_UPLOAD, 1L);
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_POSTFIELDSIZE_LARGE,
|
|
|
|
|
[body length]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDS, [body bytes]);
|
|
|
|
|
}
|
|
|
|
|
else if (nil != [_originalRequest HTTPBodyStream])
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSInputStream * stream = [_originalRequest HTTPBodyStream];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_taskData setObject: stream forKey: taskInputStreamKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_READFUNCTION, read_callback);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_READDATA, self);
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_UPLOAD, 1L);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDSIZE, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Configure HTTP method and URL */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_CUSTOMREQUEST,
|
|
|
|
|
[[_originalRequest HTTPMethod] UTF8String]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_URL,
|
|
|
|
|
[[url absoluteString] UTF8String]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* This callback function gets called by libcurl as soon as there is data
|
|
|
|
|
* received that needs to be saved. For most transfers, this callback gets
|
|
|
|
|
* called many times and each invoke delivers another chunk of data.
|
|
|
|
|
*
|
|
|
|
|
* This is directly mapped to -[NSURLSessionDataDelegate
|
|
|
|
|
* URLSession:dataTask:didReceiveData:].
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_WRITEFUNCTION, write_callback);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_WRITEDATA, self);
|
|
|
|
|
|
|
|
|
|
/* Retrieve the header data
|
|
|
|
|
*
|
|
|
|
|
* If the delegate conforms to the NSURLSessionDataDelegate
|
|
|
|
|
* - URLSession:dataTask:didReceiveResponse:completionHandler:
|
|
|
|
|
* we can notify it about the header response.
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_HEADERFUNCTION, header_callback);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_HEADERDATA, self);
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_ERRORBUFFER, _curlErrorBuffer);
|
|
|
|
|
|
|
|
|
|
/* The task is now associated with the easy handle and can be accessed
|
|
|
|
|
* using curl_easy_getinfo with CURLINFO_PRIVATE.
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_PRIVATE, self);
|
|
|
|
|
|
|
|
|
|
/* Disable libcurl's build-in progress reporting */
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_NOPROGRESS, 0L);
|
|
|
|
|
/* Specifiy our own progress function with the user pointer being the
|
|
|
|
|
* current object
|
|
|
|
|
*/
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_XFERINFOFUNCTION,
|
|
|
|
|
progress_callback);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_XFERINFODATA, self);
|
|
|
|
|
|
|
|
|
|
/* Do not Follow redirects by default
|
|
|
|
|
*
|
|
|
|
|
* libcurl does not provide a direct interface
|
|
|
|
|
* for redirect notification. We have implemented our own redirection
|
|
|
|
|
* system in header_callback.
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_FOLLOWLOCATION, 0L);
|
|
|
|
|
|
|
|
|
|
/* Set timeout in connect phase */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_CONNECTTIMEOUT,
|
|
|
|
|
(NSInteger)[request timeoutInterval]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Set overall timeout */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_TIMEOUT,
|
|
|
|
|
[configuration timeoutIntervalForResource]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Set to HTTP/3 if requested */
|
|
|
|
|
if ([request assumesHTTP3Capable])
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_HTTP_VERSION,
|
|
|
|
|
CURL_HTTP_VERSION_3);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Configure the custom CA certificate if available */
|
|
|
|
|
if (nil != (certificateBlob = [_session _certificateBlob]))
|
|
|
|
|
{
|
|
|
|
|
// CURLOPT_CAINFO_BLOB was added in 7.77.0
|
|
|
|
|
#if LIBCURL_VERSION_NUM >= 0x074D00
|
|
|
|
|
struct curl_blob blob;
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
blob.data = (void *)[certificateBlob bytes];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
blob.len = [certificateBlob length];
|
|
|
|
|
/* Session becomes a strong reference when task is resumed until the
|
|
|
|
|
* end of transfer. */
|
|
|
|
|
blob.flags = CURL_BLOB_NOCOPY;
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_CAINFO_BLOB, &blob);
|
|
|
|
|
#else
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_CAINFO,
|
|
|
|
|
[_session _certificatePath]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process config headers */
|
|
|
|
|
immConfigHeaders = [configuration HTTPAdditionalHeaders];
|
|
|
|
|
if (nil != immConfigHeaders)
|
|
|
|
|
{
|
|
|
|
|
configHeaders = [[_GSMutableInsensitiveDictionary alloc]
|
2024-10-28 11:21:47 +00:00
|
|
|
|
initWithDictionary: immConfigHeaders
|
|
|
|
|
copyItems: NO];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Merge Headers.
|
|
|
|
|
*
|
|
|
|
|
* If the same header appears in both the configuration's
|
|
|
|
|
* HTTPAdditionalHeaders and the request object (where applicable),
|
|
|
|
|
* the request object’s value takes precedence.
|
|
|
|
|
*/
|
|
|
|
|
[configHeaders
|
2024-10-28 11:21:47 +00:00
|
|
|
|
addEntriesFromDictionary: (NSDictionary *)requestHeaders];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
requestHeaders = configHeaders;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use stored cookies is instructed to do so
|
|
|
|
|
*/
|
|
|
|
|
storage = [configuration HTTPCookieStorage];
|
|
|
|
|
if (nil != storage && [configuration HTTPShouldSetCookies])
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDictionary * cookieHeaders;
|
|
|
|
|
NSArray<NSHTTPCookie *> * cookies;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* No headers were set */
|
|
|
|
|
if (nil == requestHeaders)
|
|
|
|
|
{
|
|
|
|
|
requestHeaders = [_GSMutableInsensitiveDictionary new];
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
cookies = [storage cookiesForURL: url];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if ([cookies count] > 0)
|
|
|
|
|
{
|
|
|
|
|
cookieHeaders =
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[NSHTTPCookie requestHeaderFieldsWithCookies: cookies];
|
|
|
|
|
[requestHeaders addEntriesFromDictionary: cookieHeaders];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append Headers to the libcurl header list
|
|
|
|
|
*/
|
|
|
|
|
[requestHeaders
|
2024-10-28 11:21:47 +00:00
|
|
|
|
enumerateKeysAndObjectsUsingBlock:^(id<NSCopying> key, id object,
|
|
|
|
|
BOOL * stop) {
|
|
|
|
|
NSString * headerLine;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
headerLine = [NSString stringWithFormat: @"%@: %@", key, object];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
/* We have removed all reserved headers in NSURLRequest */
|
|
|
|
|
_headerList = curl_slist_append(_headerList, [headerLine UTF8String]);
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_HTTPHEADER, _headerList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* initWithSession */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _enableAutomaticRedirects: (BOOL)flag
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_FOLLOWLOCATION, flag ? 1L : 0L);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _enableUploadWithData: (NSData *)data
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_UPLOAD, 1L);
|
|
|
|
|
|
|
|
|
|
/* Retain data */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_taskData setObject: data forKey: taskUploadData];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDSIZE_LARGE, [data length]);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDS, [data bytes]);
|
|
|
|
|
|
|
|
|
|
/* The method is overwritten by CURLOPT_UPLOAD. Change it back. */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_CUSTOMREQUEST,
|
|
|
|
|
[[_originalRequest HTTPMethod] UTF8String]);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _enableUploadWithSize: (NSInteger)size
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_UPLOAD, 1L);
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_READFUNCTION, read_callback);
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_READDATA, self);
|
|
|
|
|
|
|
|
|
|
if (size > 0)
|
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDSIZE_LARGE, size);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_POSTFIELDSIZE, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The method is overwritten by CURLOPT_UPLOAD. Change it back. */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
curl_easy_setopt(
|
|
|
|
|
_easyHandle,
|
|
|
|
|
CURLOPT_CUSTOMREQUEST,
|
|
|
|
|
[[_originalRequest HTTPMethod] UTF8String]);
|
|
|
|
|
} /* _enableUploadWithSize */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (CURL *) _easyHandle
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _easyHandle;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setVerbose: (BOOL)flag
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
dispatch_async(
|
|
|
|
|
[_session _workQueue],
|
|
|
|
|
^{
|
2024-08-16 12:08:41 +00:00
|
|
|
|
curl_easy_setopt(_easyHandle, CURLOPT_VERBOSE, flag ? 1L : 0L);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setBodyStream: (NSInputStream *)stream
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_taskData setObject: stream forKey: taskInputStreamKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setOriginalRequest: (NSURLRequest *)request
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
ASSIGNCOPY(_originalRequest, request);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCurrentRequest: (NSURLRequest *)request
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
ASSIGNCOPY(_currentRequest, request);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setResponse: (NSURLResponse *)response
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLResponse * oldResponse = _response;
|
|
|
|
|
|
2024-08-16 12:08:41 +00:00
|
|
|
|
_response = [response retain];
|
|
|
|
|
[oldResponse release];
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCountOfBytesSent: (int64_t)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_countOfBytesSent = count;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCountOfBytesReceived: (int64_t)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_countOfBytesReceived = count;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCountOfBytesExpectedToSend: (int64_t)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_countOfBytesExpectedToSend = count;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCountOfBytesExpectedToReceive: (int64_t)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_countOfBytesExpectedToReceive = count;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSMutableDictionary *) _taskData
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _taskData;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSInteger) _properties
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _properties;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setProperties: (NSInteger)properties
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_properties = properties;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSURLSession *) _session
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _session;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (BOOL) _shouldStopTransfer
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _shouldStopTransfer;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setShouldStopTransfer: (BOOL)flag
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_shouldStopTransfer = flag;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSInteger) _numberOfRedirects
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _numberOfRedirects;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setNumberOfRedirects: (NSInteger)redirects
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_numberOfRedirects = redirects;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSInteger) _headerCallbackCount
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _headerCallbackCount;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setHeaderCallbackCount: (NSInteger)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_headerCallbackCount = count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates a temporary file and opens a file handle for writing */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSFileHandle *) _createTemporaryFileHandleWithError: (NSError **)error
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSFileManager * mgr;
|
|
|
|
|
NSFileHandle * handle;
|
|
|
|
|
NSString * path;
|
|
|
|
|
NSURL * url;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
mgr = [NSFileManager defaultManager];
|
|
|
|
|
path = NSTemporaryDirectory();
|
2024-10-28 11:21:47 +00:00
|
|
|
|
path = [path stringByAppendingPathComponent: [[NSUUID UUID] UUIDString]];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
url = [NSURL fileURLWithPath: path];
|
|
|
|
|
[_taskData setObject: url forKey: taskTemporaryFileLocationKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if (![mgr createFileAtPath: path contents: nil attributes: nil])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * errorDescription = [NSString
|
|
|
|
|
stringWithFormat:
|
|
|
|
|
@"Failed to create temporary file at path %@",
|
|
|
|
|
path];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
*error = [NSError
|
2024-10-28 11:21:47 +00:00
|
|
|
|
errorWithDomain: NSCocoaErrorDomain
|
|
|
|
|
code: NSURLErrorCannotCreateFile
|
|
|
|
|
userInfo: @{ NSLocalizedDescriptionKey:
|
|
|
|
|
errorDescription }];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
handle = [NSFileHandle fileHandleForWritingAtPath: path];
|
|
|
|
|
[_taskData setObject: handle forKey: taskTemporaryFileHandleKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
return handle;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* _createTemporaryFileHandleWithError */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Called in _checkForCompletion */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _transferFinishedWithCode: (CURLcode)code
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSError * error = errorForCURLcode(_easyHandle, code, _curlErrorBuffer);
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
if (_properties & GSURLSessionWritesDataToFile)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSFileHandle * handle;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if (nil !=
|
|
|
|
|
(handle = [_taskData objectForKey: taskTemporaryFileHandleKey]))
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
[handle closeFile];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_properties & GSURLSessionUpdatesDelegate)
|
|
|
|
|
{
|
|
|
|
|
if (_properties & GSURLSessionWritesDataToFile &&
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_delegate respondsToSelector: didFinishDownloadingToURLSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURL * url = [_taskData objectForKey: taskTemporaryFileLocationKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[[_session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[(id<NSURLSessionDownloadDelegate>) _delegate
|
|
|
|
|
URLSession: _session
|
|
|
|
|
downloadTask: (NSURLSessionDownloadTask *)self
|
|
|
|
|
didFinishDownloadingToURL: url];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
if ([_delegate respondsToSelector: didCompleteWithErrorSel])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
[[_session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_delegate URLSession: _session
|
|
|
|
|
task: self
|
|
|
|
|
didCompleteWithError: error];
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* NSURLSessionUploadTask is a subclass of a NSURLSessionDataTask with the
|
|
|
|
|
* same completion handler signature. It thus follows the same code path.
|
|
|
|
|
*/
|
|
|
|
|
if ((_properties & GSURLSessionStoresDataInMemory)
|
|
|
|
|
&& (_properties & GSURLSessionHasCompletionHandler) &&
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[self isKindOfClass: dataTaskClass])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionDataTask * dataTask;
|
|
|
|
|
NSData * data;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
dataTask = (NSURLSessionDataTask *)self;
|
|
|
|
|
data = [_taskData objectForKey: taskTransferDataKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[[_session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[dataTask _completionHandler](data, _response, error);
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
else if ((_properties & GSURLSessionWritesDataToFile)
|
|
|
|
|
&& (_properties & GSURLSessionHasCompletionHandler) &&
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[self isKindOfClass: downloadTaskClass])
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionDownloadTask * downloadTask;
|
|
|
|
|
NSURL * tempFile;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
downloadTask = (NSURLSessionDownloadTask *)self;
|
|
|
|
|
tempFile = [_taskData objectForKey: taskTemporaryFileLocationKey];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
[[_session delegateQueue] addOperationWithBlock:^{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[downloadTask _completionHandler](tempFile, _response, error);
|
|
|
|
|
}];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RELEASE(_session);
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* _transferFinishedWithCode */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
/* Called in header_callback */
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCookiesFromHeaders: (NSDictionary *)headers
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURL * url;
|
|
|
|
|
NSArray * cookies;
|
|
|
|
|
NSURLSessionConfiguration * config;
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
config = [_session configuration];
|
|
|
|
|
url = [_currentRequest URL];
|
|
|
|
|
|
|
|
|
|
/* FIXME: Implement NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain */
|
|
|
|
|
if (NSHTTPCookieAcceptPolicyNever != [config HTTPCookieAcceptPolicy]
|
|
|
|
|
&& nil != [config HTTPCookieStorage])
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers
|
|
|
|
|
forURL: url];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
if ([cookies count] > 0)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[[config HTTPCookieStorage] setCookies: cookies
|
|
|
|
|
forURL: url
|
|
|
|
|
mainDocumentURL: nil];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
} /* _setCookiesFromHeaders */
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
#pragma mark - Public Methods
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) suspend
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_suspendCount += 1;
|
|
|
|
|
if (_suspendCount == 1)
|
|
|
|
|
{
|
|
|
|
|
/* If there is an active transfer associated with this task, it will be
|
|
|
|
|
* aborted in the next libcurl progress_callback.
|
|
|
|
|
*
|
|
|
|
|
* TODO: Pause the easy handle put do not abort the full transfer!
|
|
|
|
|
* . What if the handle is currently paused?
|
|
|
|
|
*/
|
|
|
|
|
_shouldStopTransfer = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) resume
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
/* Only resume a transfer if the task is not suspended and in suspended state
|
|
|
|
|
*/
|
|
|
|
|
if (_suspendCount == 0 && [self state] == NSURLSessionTaskStateSuspended)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Properly retain the session to keep a reference
|
|
|
|
|
* to the task. This ensures correct API behaviour.
|
|
|
|
|
*/
|
|
|
|
|
RETAIN(_session);
|
|
|
|
|
|
|
|
|
|
_state = NSURLSessionTaskStateRunning;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
[_session _resumeTask: self];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_suspendCount -= 1;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) cancel
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
/* Transfer is aborted in the next libcurl progress_callback
|
|
|
|
|
*
|
|
|
|
|
* If a NSURLSessionTask delegate is set and this is not a convenience task,
|
|
|
|
|
* URLSession:task:didCompleteWithError: is called after receiving
|
|
|
|
|
* CURLMSG_DONE in -[NSURLSessionTask _checkForCompletion].
|
|
|
|
|
*/
|
2024-10-28 11:21:47 +00:00
|
|
|
|
dispatch_async(
|
|
|
|
|
[_session _workQueue],
|
|
|
|
|
^{
|
2024-08-16 12:08:41 +00:00
|
|
|
|
/* Unpause the easy handle if previously paused */
|
|
|
|
|
curl_easy_pause(_easyHandle, CURLPAUSE_CONT);
|
|
|
|
|
|
|
|
|
|
_shouldStopTransfer = YES;
|
|
|
|
|
_state = NSURLSessionTaskStateCanceling;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (float) priority
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _priority;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) setPriority: (float)priority
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_priority = priority;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (id) copyWithZone: (NSZone *)zone
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSURLSessionTask * copy = [[[self class] alloc] init];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
|
|
|
|
|
if (copy)
|
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
copy->_originalRequest = [_originalRequest copyWithZone: zone];
|
|
|
|
|
copy->_currentRequest = [_currentRequest copyWithZone: zone];
|
|
|
|
|
copy->_response = [_response copyWithZone: zone];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
/* FIXME: Seems like copyWithZone: is not implemented for NSProgress */
|
|
|
|
|
copy->_progress = [_progress copy];
|
2024-10-28 11:21:47 +00:00
|
|
|
|
copy->_earliestBeginDate = [_earliestBeginDate copyWithZone: zone];
|
|
|
|
|
copy->_taskDescription = [_taskDescription copyWithZone: zone];
|
|
|
|
|
copy->_taskData = [_taskData copyWithZone: zone];
|
2024-08-16 12:08:41 +00:00
|
|
|
|
copy->_easyHandle = curl_easy_duphandle(_easyHandle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Getter and Setter
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSUInteger) taskIdentifier
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _taskIdentifier;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSURLRequest *) originalRequest
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return AUTORELEASE([_originalRequest copy]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSURLRequest *) currentRequest
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return AUTORELEASE([_currentRequest copy]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSURLResponse *) response
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return AUTORELEASE([_response copy]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSURLSessionTaskState) state
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _state;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSProgress *) progress
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _progress;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSError *) error
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _error;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (id<NSURLSessionTaskDelegate>) delegate
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _delegate;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) setDelegate: (id<NSURLSessionTaskDelegate>)delegate
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
id<NSURLSessionTaskDelegate> oldDelegate = _delegate;
|
2024-10-28 11:21:47 +00:00
|
|
|
|
|
2024-08-16 12:08:41 +00:00
|
|
|
|
_delegate = RETAIN(delegate);
|
|
|
|
|
RELEASE(oldDelegate);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSDate *) earliestBeginDate
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _earliestBeginDate;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) setEarliestBeginDate: (NSDate *)date
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSDate * oldDate = _earliestBeginDate;
|
|
|
|
|
|
2024-08-16 12:08:41 +00:00
|
|
|
|
_earliestBeginDate = RETAIN(date);
|
|
|
|
|
RELEASE(oldDate);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesClientExpectsToSend
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesClientExpectsToSend;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesClientExpectsToReceive
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesClientExpectsToReceive;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesSent
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesSent;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesReceived
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesReceived;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesExpectedToSend
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesExpectedToSend;
|
|
|
|
|
}
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) countOfBytesExpectedToReceive
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesExpectedToReceive;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (NSString *) taskDescription
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _taskDescription;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) setTaskDescription: (NSString *)description
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
2024-10-28 11:21:47 +00:00
|
|
|
|
NSString * oldDescription = _taskDescription;
|
|
|
|
|
|
2024-08-16 12:08:41 +00:00
|
|
|
|
_taskDescription = [description copy];
|
|
|
|
|
RELEASE(oldDescription);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) dealloc
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
/* The session retains this task until the transfer is complete and the easy
|
|
|
|
|
* handle removed from the multi handle.
|
|
|
|
|
*
|
|
|
|
|
* It is save to release the curl handle here.
|
|
|
|
|
*/
|
|
|
|
|
curl_easy_cleanup(_easyHandle);
|
|
|
|
|
curl_slist_free_all(_headerList);
|
|
|
|
|
|
|
|
|
|
RELEASE(_originalRequest);
|
|
|
|
|
RELEASE(_currentRequest);
|
|
|
|
|
RELEASE(_response);
|
|
|
|
|
RELEASE(_progress);
|
|
|
|
|
RELEASE(_earliestBeginDate);
|
|
|
|
|
RELEASE(_taskDescription);
|
|
|
|
|
RELEASE(_taskData);
|
|
|
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end /* NSURLSessionTask */
|
|
|
|
|
|
|
|
|
|
@implementation NSURLSessionDataTask
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (GSNSURLSessionDataCompletionHandler) _completionHandler
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _completionHandler;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCompletionHandler: (GSNSURLSessionDataCompletionHandler)handler
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_completionHandler = _Block_copy(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) dealloc
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_Block_release(_completionHandler);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSURLSessionUploadTask
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSURLSessionDownloadTask
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (GSNSURLSessionDownloadCompletionHandler) _completionHandler
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _completionHandler;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _setCompletionHandler: (GSNSURLSessionDownloadCompletionHandler)handler
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_completionHandler = _Block_copy(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (int64_t) _countOfBytesWritten
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
return _countOfBytesWritten;
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) _updateCountOfBytesWritten: (int64_t)count
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_countOfBytesWritten += count;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 11:21:47 +00:00
|
|
|
|
- (void) dealloc
|
2024-08-16 12:08:41 +00:00
|
|
|
|
{
|
|
|
|
|
_Block_release(_completionHandler);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSURLSessionStreamTask
|
|
|
|
|
@end
|