Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NSURLSession fixes and improvements #286

Merged
merged 14 commits into from
Mar 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
name: CI
run-name: >-
${{
join(fromJSON(format('["{0}", "{1}", "{2}"]',
((inputs.tools_make_branch != '' && inputs.tools_make_branch != 'master') || inputs.tools_windows_msvc_branch != '') && github.workflow || '',
(inputs.tools_make_branch != '' && inputs.tools_make_branch != 'master') && format('tools-make: {0}', inputs.tools_make_branch) || '',
inputs.tools_windows_msvc_branch != '' && format('tools-windows-msvc: {0}', inputs.tools_windows_msvc_branch) || ''
)), ' ')
}}

on:
push:
Expand Down Expand Up @@ -228,14 +236,19 @@ jobs:
if: env.IS_WINDOWS_MSVC == 'true'
with:
msystem: MSYS
install: make autoconf automake libtool
install: make autoconf automake libtool pkg-config
# make Windows packages like Clang available in MSYS
path-type: inherit

- name: Delete MinGW gmake (MSVC)
- name: Remove Perl Strawberry installation and MinGW gmake (MSVC)
if: env.IS_WINDOWS_MSVC == 'true'
# delete /c/Strawberry/c/bin/gmake built for MinGW that is found on runners, because we must use make built for MSYS
run: if GMAKE_PATH=`which gmake`; then rm -f "$GMAKE_PATH"; fi
# C:\Strawberry contains various MinGW libraries and binaries like pkg-config
# that can get picked up by configure/CMake and don't necessarily behave
# correctly when not using a MinGW environment, and more specifically we cannot
# use MinGW gmake but must use MSYS make for correctly handling of Windows paths,
# so we delete everything that could mess up our builds
run: rmdir /S /Q C:\Strawberry
shell: cmd

- name: Install Windows packages (MSVC)
if: env.IS_WINDOWS_MSVC == 'true'
Expand Down
25 changes: 25 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
2023-01-13 Frederik Seiffert <[email protected]>

* Source/Additions/GSInsensitiveDictionary.m:
* Source/NSURLRequest.m:
* Source/NSURLResponse.m:
Fix NSURLSession header fields not always being matched case
insensitive.

2023-01-13 Frederik Seiffert <[email protected]>

* Source/GSEasyHandle.m:
* Source/GSMultiHandle.m:
* Source/GSTimeoutSource.h:
* Source/GSTimeoutSource.m:
* Source/NSURLSession.m:
Fix NSURLSession memory management of libdispatch objects.
* Source/GSHTTPURLProtocol.m:
Fix overrelease.

2023-01-13 Frederik Seiffert <[email protected]>

* Headers/Foundation/NSURLSession.h:
* Source/NSURLSession.m:
Add missing NSURLSession APIs.

2022-02-09 Richard Frith-Macdonald <[email protected]>

* Source/Additions/GSMime.m: ([GSMimeHeader setValue:]) do not set
Expand Down
135 changes: 107 additions & 28 deletions Headers/Foundation/NSURLSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
@class NSURLRequest;
@class NSURLResponse;
@class NSURLSessionConfiguration;
@class NSURLSessionTask;
@class NSURLSessionDataTask;
@class NSURLSessionUploadTask;
@class NSURLSessionDownloadTask;


Expand Down Expand Up @@ -62,6 +64,10 @@ GS_EXPORT_CLASS
GSMultiHandle *_multiHandle;
}

+ (NSURLSession*) sharedSession;

+ (NSURLSession*) sessionWithConfiguration: (NSURLSessionConfiguration*)configuration;

/*
* Customization of NSURLSession occurs during creation of a new session.
* If you do specify a delegate, the delegate will be retained until after
Expand Down Expand Up @@ -111,11 +117,80 @@ GS_EXPORT_CLASS
/* Creates a data task to retrieve the contents of the given URL. */
- (NSURLSessionDataTask*) dataTaskWithURL: (NSURL*)url;

/* Not implemented */
- (NSURLSessionUploadTask*) uploadTaskWithRequest: (NSURLRequest*)request
fromFile: (NSURL*)fileURL;

/* Not implemented */
- (NSURLSessionUploadTask*) uploadTaskWithRequest: (NSURLRequest*)request
fromData: (NSData*)bodyData;

/* Not implemented */
- (NSURLSessionUploadTask*) uploadTaskWithStreamedRequest: (NSURLRequest*)request;

/* Creates a download task with the given request. */
- (NSURLSessionDownloadTask *) downloadTaskWithRequest: (NSURLRequest *)request;
- (NSURLSessionDownloadTask*) downloadTaskWithRequest: (NSURLRequest*)request;

/* Creates a download task to download the contents of the given URL. */
- (NSURLSessionDownloadTask *) downloadTaskWithURL: (NSURL *)url;
- (NSURLSessionDownloadTask*) downloadTaskWithURL: (NSURL*)url;

/* Not implemented */
- (NSURLSessionDownloadTask *) downloadTaskWithResumeData: (NSData *)resumeData;

- (void) getTasksWithCompletionHandler: (void (^)(GS_GENERIC_CLASS(NSArray, NSURLSessionDataTask*) *dataTasks, GS_GENERIC_CLASS(NSArray, NSURLSessionUploadTask*) *uploadTasks, GS_GENERIC_CLASS(NSArray, NSURLSessionDownloadTask*) *downloadTasks))completionHandler;

- (void) getAllTasksWithCompletionHandler: (void (^)(GS_GENERIC_CLASS(NSArray, __kindof NSURLSessionTask*) *tasks))completionHandler;

@end

/*
* NSURLSession convenience routines deliver results to
* a completion handler block. These convenience routines
* are not available to NSURLSessions that are configured
* as background sessions.
*
* Task objects are always created in a suspended state and
* must be sent the -resume message before they will execute.
*/
@interface NSURLSession (NSURLSessionAsynchronousConvenience)
/*
* data task convenience methods. These methods create tasks that
* bypass the normal delegate calls for response and data delivery,
* and provide a simple cancelable asynchronous interface to receiving
* data. Errors will be returned in the NSURLErrorDomain,
* see <Foundation/NSURLError.h>. The delegate, if any, will still be
* called for authentication challenges.
*/
- (NSURLSessionDataTask*) dataTaskWithRequest: (NSURLRequest*)request
completionHandler: (void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDataTask*) dataTaskWithURL: (NSURL*)url
completionHandler: (void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;


/* Not implemented */
- (NSURLSessionUploadTask*) uploadTaskWithRequest: (NSURLRequest*)request
fromFile: (NSURL*)fileURL
completionHandler: (void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

/* Not implemented */
- (NSURLSessionUploadTask*) uploadTaskWithRequest: (NSURLRequest*)request
fromData: (NSData*)bodyData
completionHandler: (void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

/*
* download task convenience methods. When a download successfully
* completes, the NSURL will point to a file that must be read or
* copied during the invocation of the completion routine. The file
* will be removed automatically.
*/
- (NSURLSessionDownloadTask*) downloadTaskWithRequest: (NSURLRequest*)request
completionHandler: (void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDownloadTask*) downloadTaskWithURL: (NSURL *)url
completionHandler: (void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

/* Not implemented */
- (NSURLSessionDownloadTask*) downloadTaskWithResumeData: (NSData*)resumeData
completionHandler: (void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

@end

Expand All @@ -131,6 +206,12 @@ typedef NS_ENUM(NSUInteger, NSURLSessionTaskState) {
NSURLSessionTaskStateCompleted = 3,
};

GS_EXPORT const float NSURLSessionTaskPriorityDefault;
GS_EXPORT const float NSURLSessionTaskPriorityLow;
GS_EXPORT const float NSURLSessionTaskPriorityHigh;

GS_EXPORT const int64_t NSURLSessionTransferSizeUnknown;

/*
* NSURLSessionTask - a cancelable object that refers to the lifetime
* of processing a given request.
Expand Down Expand Up @@ -197,6 +278,9 @@ GS_EXPORT_CLASS
NSUInteger _suspendCount;

GSURLSessionTaskBody *_knownBody;

void (^_dataCompletionHandler)(NSData *data, NSURLResponse *response, NSError *error);
void (^_downloadCompletionHandler)(NSURL *location, NSURLResponse *response, NSError *error);
}

- (NSUInteger) taskIdentifier;
Expand Down Expand Up @@ -245,6 +329,9 @@ GS_EXPORT_CLASS
- (void) suspend;
- (void) resume;

- (float) priority;
- (void) setPriority: (float)priority;

@end

GS_EXPORT_CLASS
Expand Down Expand Up @@ -273,6 +360,7 @@ GS_EXPORT_CLASS
GS_EXPORT_CLASS
@interface NSURLSessionConfiguration : NSObject <NSCopying>
{
NSString *_identifier;
NSURLCache *_URLCache;
NSURLRequestCachePolicy _requestCachePolicy;
NSArray *_protocolClasses;
Expand All @@ -288,60 +376,51 @@ GS_EXPORT_CLASS

- (NSURLRequest*) configureRequest: (NSURLRequest*)request;

@property (class, readonly, strong)
NSURLSessionConfiguration *defaultSessionConfiguration;
+ (NSURLSessionConfiguration*) defaultSessionConfiguration;
+ (NSURLSessionConfiguration*) ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration*) backgroundSessionConfigurationWithIdentifier:(NSString*)identifier;

- (NSDictionary*) HTTPAdditionalHeaders;
- (void) setHTTPAdditionalHeaders: (NSDictionary*)headers;

- (NSHTTPCookieAcceptPolicy) HTTPCookieAcceptPolicy;
- (void) setHTTPCookieAcceptPolicy: (NSHTTPCookieAcceptPolicy)policy;

- (NSHTTPCookieStorage*) HTTPCookieStorage;

#if !NO_GNUSTEP
- (NSInteger) HTTPMaximumConnectionLifetime;
#endif
- (void) setHTTPCookieStorage: (NSHTTPCookieStorage*)storage;

- (NSInteger) HTTPMaximumConnectionsPerHost;
- (void) setHTTPMaximumConnectionsPerHost: (NSInteger)n;

- (BOOL) HTTPShouldSetCookies;
- (void) setHTTPShouldSetCookies: (BOOL)flag;

- (BOOL) HTTPShouldUsePipelining;
- (void) setHTTPShouldUsePipelining: (BOOL)flag;

- (NSString*) identifier;

- (NSArray*) protocolClasses;

- (NSURLRequestCachePolicy) requestCachePolicy;
- (void) setRequestCachePolicy: (NSURLRequestCachePolicy)policy;

- (void) setHTTPAdditionalHeaders: (NSDictionary*)headers;

- (void) setHTTPCookieAcceptPolicy: (NSHTTPCookieAcceptPolicy)policy;
- (NSURLCache*) URLCache;
- (void) setURLCache: (NSURLCache*)cache;

- (void) setHTTPCookieStorage: (NSHTTPCookieStorage*)storage;
- (NSURLCredentialStorage*) URLCredentialStorage;
- (void) setURLCredentialStorage: (NSURLCredentialStorage*)storage;

#if !NO_GNUSTEP
/** Permits a session to be configured so that older connections are reused.
* A value of zero or less uses the default behavior where connections are
* reused as long as they are not older than 118 seconds, which is reasonable
* for the vast majority if situations.
*/
- (NSInteger) HTTPMaximumConnectionLifetime;
- (void) setHTTPMaximumConnectionLifetime: (NSInteger)n;
#endif

- (void) setHTTPMaximumConnectionsPerHost: (NSInteger)n;

- (void) setHTTPShouldSetCookies: (BOOL)flag;

- (void) setHTTPShouldUsePipelining: (BOOL)flag;

- (void) setRequestCachePolicy: (NSURLRequestCachePolicy)policy;

- (void) setURLCache: (NSURLCache*)cache;

- (void) setURLCredentialStorage: (NSURLCredentialStorage*)storage;

- (NSURLCache*) URLCache;

- (NSURLCredentialStorage*) URLCredentialStorage;

@end

typedef NS_ENUM(NSInteger, NSURLSessionAuthChallengeDisposition) {
Expand Down
7 changes: 7 additions & 0 deletions Source/Additions/GSInsensitiveDictionary.m
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ - (id) copyWithZone: (NSZone*)zone
return [copy initWithDictionary: self copyItems: NO];
}

- (id) mutableCopyWithZone: (NSZone*)z
{
NSMutableDictionary *copy = [_GSMutableInsensitiveDictionary allocWithZone: z];

return [copy initWithDictionary: self copyItems: NO];
}

- (id) init
{
return [self initWithCapacity: 0];
Expand Down
1 change: 0 additions & 1 deletion Source/Additions/GSMime.m
Original file line number Diff line number Diff line change
Expand Up @@ -3345,7 +3345,6 @@ - (BOOL) _scanHeaderParameters: (NSScanner*)scanner into: (GSMimeHeader*)info

@end



@interface _GSMutableInsensitiveDictionary : NSMutableDictionary
@end
Expand Down
15 changes: 6 additions & 9 deletions Source/GSEasyHandle.m
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ - (int) seekInputStreamWithOffset: (int64_t)offset
text = [NSString stringWithUTF8String: data];
}

NSLog(@"%p %lu %d %@", o, [task taskIdentifier], type, text);
NSLog(@"%p %lu %d %@", o, (unsigned long)[task taskIdentifier], type, text);

return 0;
}
Expand Down Expand Up @@ -195,6 +195,7 @@ - (void) dealloc
curl_slist_free_all(_headerList);
free(_errorBuffer);
DESTROY(_config);
[_timeoutTimer cancel];
DESTROY(_timeoutTimer);
DESTROY(_URL);
[super dealloc];
Expand All @@ -217,6 +218,7 @@ - (GSTimeoutSource*) timeoutTimer

- (void) setTimeoutTimer: (GSTimeoutSource*)timer
{
[_timeoutTimer cancel];
ASSIGN(_timeoutTimer, timer);
}

Expand All @@ -232,12 +234,7 @@ - (void) transferCompletedWithError: (NSError*)error

- (void) resetTimer
{
// simply create a new timer with the same queue, timeout and handler
// this must cancel the old handler and reset the timer
DESTROY(_timeoutTimer);
_timeoutTimer = [[GSTimeoutSource alloc] initWithQueue: [_timeoutTimer queue]
milliseconds: [_timeoutTimer milliseconds]
handler: [_timeoutTimer handler]];
[_timeoutTimer setTimeout: [_timeoutTimer timeout]];
}

- (void) setupCallbacks
Expand Down Expand Up @@ -332,7 +329,7 @@ - (void) setDebugOutput: (BOOL)flag
{
if (flag)
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGDATA, task));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGFUNCTION,
curl_debug_function));
}
Expand Down Expand Up @@ -408,7 +405,7 @@ - (void) setConnectToHost: (NSString*)host port: (NSInteger)port
else
{
value = [NSString stringWithFormat: @"%@:%lu:%@",
originHost, port, host];
originHost, (unsigned long)port, host];
}

struct curl_slist *connect_to = NULL;
Expand Down
Loading