Merge changes for NSURLSession from EngageHub (formerly Brainstorm).

This commit is contained in:
Richard Frith-Macdonald 2020-11-29 06:57:47 -05:00
parent d015cebbf3
commit 1b7bf26bea
70 changed files with 7980 additions and 192 deletions

View file

@ -1,6 +1,6 @@
---
language: cpp
dist: xenial
dist: focal
jobs:
include:
- name: "Linux GCC"
@ -44,7 +44,7 @@ before_install: |
if [ $CC = 'gcc' ]; then
sudo apt-get install -y gobjc
fi
sudo apt-get install -y libobjc-4.8-dev libblocksruntime-dev
sudo apt-get install -y libobjc-9-dev libblocksruntime-dev
;;
ng-gnu-gnu)
curl -s -o - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
@ -115,7 +115,7 @@ before_script: |
export LD_LIBRARY_PATH=$DEP_ROOT/lib:$DEP_ROOT/lib64:$LD_LIBRARY_PATH
case $LIBRARY_COMBO in
gnu-gnu-gnu)
export CPATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/include
export CPATH=/usr/lib/gcc/x86_64-linux-gnu/9/include
;;
ng-gnu-gnu)
export CPATH=$DEP_ROOT/include

View file

@ -1,5 +1,5 @@
/* Interface to debugging utilities for GNUStep and OpenStep
Copyright (C) 1997,1999 Free Software Foundation, Inc.
Copyright (C) 1997-2020 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: August 1997
@ -44,6 +44,35 @@
extern "C" {
#endif
/** Protocol for a delegate, set as an extension in some classes, to handle
* debug logging of low level I/O. The rationale for this protocol is that
* on occasion debug logging may be required, but the data being logged may
* contain sensitive information which should not be writtent to file. In
* that situation, the delegate may filter/mask the sensitive information
* from the logs by taking over the simpel writing to stderr that the inbuilt
* debug logging provides.
*/
@protocol GSLogDelegate <NSObject>
/** Method sent to the delegate to ask it to log a chunk of data that
* has been read. The delegate should return YES if it has handled
* the logging, NO if it wants the default mechanism to be used.<br />
* The handle is the object which is performing the read operation.
*/
- (BOOL) getBytes: (const uint8_t*)bytes
ofLength: (NSUInteger)length
byHandle: (NSObject*)handle;
/** Method sent to the delegate to ask it to log a chunk of data that
* has been written (or is immediately going to be written).
* The delegate should return YES if it has handled the logging,
* NO if it wants the default logging mechanism to be used.<br />
* The handle is the object which is performing the write operation.
*/
- (BOOL) putBytes: (const uint8_t*)bytes
ofLength: (NSUInteger)length
byHandle: (NSObject*)handle;
@end
/*
* Functions for debugging object allocation/deallocation
*

View file

@ -196,6 +196,19 @@ typedef enum
@end
@class NSURLSessionDataTask;
@interface NSURLCache (NSURLSessionTaskAdditions)
- (void) storeCachedResponse: (NSCachedURLResponse*)cachedResponse
forDataTask: (NSURLSessionDataTask*)dataTask;
- (NSCachedURLResponse*) cachedResponseForDataTask: (NSURLSessionDataTask*)dataTask;
- (void) removeCachedResponseForDataTask: (NSURLSessionDataTask*)dataTask;
@end
#if defined(__cplusplus)
}
#endif

View file

@ -71,6 +71,7 @@ enum
NSURLErrorFileDoesNotExist = -1100,
NSURLErrorFileIsDirectory = -1101,
NSURLErrorNoPermissionsToReadFile = -1102,
NSURLErrorDataLengthExceedsMaximum = -1103,
NSURLErrorSecureConnectionFailed = -1200,
NSURLErrorServerCertificateHasBadDate = -1201,
NSURLErrorServerCertificateUntrusted = -1202,

View file

@ -44,6 +44,7 @@ extern "C" {
@class NSURLProtocol;
@class NSURLRequest;
@class NSURLResponse;
@class NSURLSessionTask;
/**
@ -178,11 +179,20 @@ extern "C" {
cachedResponse: (NSCachedURLResponse *)cachedResponse
client: (id <NSURLProtocolClient>)client;
- (instancetype) initWithTask: (NSURLSessionTask*)task
cachedResponse: (NSCachedURLResponse*)cachedResponse
client: (id<NSURLProtocolClient>)client;
/**
* Returns the request handled by the receiver.
*/
- (NSURLRequest *) request;
/**
* Returns the task handled by the receiver.
*/
- (NSURLSessionTask *) task;
@end
/**

View file

@ -283,6 +283,25 @@ typedef NSUInteger NSURLRequestCachePolicy;
@end
@protocol GSLogDelegate;
@interface NSMutableURLRequest (GNUstep)
/** Sets a flag to turn on low level debug logging for this request and the
* corresponding response. The previous vaue of the setting is returned.
*/
- (int) setDebug: (int)d;
/** Sets a delegate object to override logging of low level I/O of the
* request as it is sent and the corresponding response as it arrives.<br />
* The delegate object is not retained, so it is the responsibility of the
* caller to ensure that it persists until all I/O has completed.<br />
* This has no effect unless debug is turned on, but if debug is turned on
* it permits the delegate to override the default behavior of writing the
* data to stderr.
*/
- (id<GSLogDelegate>) setDebugLogDelegate: (id<GSLogDelegate>)d;
@end
#if defined(__cplusplus)
}
#endif

View file

@ -2,18 +2,240 @@
#define __NSURLSession_h_GNUSTEP_BASE_INCLUDE
#import <Foundation/NSObject.h>
#import <Foundation/NSURLRequest.h>
#import <Foundation/NSHTTPCookieStorage.h>
#if GS_HAVE_NSURLSESSION
#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
@protocol NSURLSessionDelegate;
@protocol NSURLSessionTaskDelegate;
@class GSMultiHandle;
@class GSURLSessionTaskBody;
@class NSError;
@class NSHTTPURLResponse;
@class NSOperationQueue;
@class NSURL;
@class NSURLAuthenticationChallenge;
@class NSURLCache;
@class NSURLCredential;
@class NSURLCredentialStorage;
@class NSURLRequest;
@class NSURLResponse;
@class NSURLSessionConfiguration;
@class NSURLSessionDataTask;
/**
* NSURLSession is a replacement API for NSURLConnection. It provides
* options that affect the policy of, and various aspects of the
* mechanism by which NSURLRequest objects are retrieved from the
* network.<br />
*
* An NSURLSession may be bound to a delegate object. The delegate is
* invoked for certain events during the lifetime of a session.
*
* NSURLSession instances are threadsafe.
*
* An NSURLSession creates NSURLSessionTask objects which represent the
* action of a resource being loaded.
*
* NSURLSessionTask objects are always created in a suspended state and
* must be sent the -resume message before they will execute.
*
* Subclasses of NSURLSessionTask are used to syntactically
* differentiate between data and file downloads.
*
* An NSURLSessionDataTask receives the resource as a series of calls to
* the URLSession:dataTask:didReceiveData: delegate method. This is type of
* task most commonly associated with retrieving objects for immediate parsing
* by the consumer.
*/
@interface NSURLSession : NSObject
{
NSOperationQueue *_delegateQueue;
id <NSURLSessionDelegate> _delegate;
NSURLSessionConfiguration *_configuration;
NSString *_sessionDescription;
GSMultiHandle *_multiHandle;
}
/*
* Customization of NSURLSession occurs during creation of a new session.
* If you do specify a delegate, the delegate will be retained until after
* the delegate has been sent the URLSession:didBecomeInvalidWithError: message.
*/
+ (NSURLSession*) sessionWithConfiguration: (NSURLSessionConfiguration*)configuration
delegate: (id <NSURLSessionDelegate>)delegate
delegateQueue: (NSOperationQueue*)queue;
- (NSOperationQueue*) delegateQueue;
- (id <NSURLSessionDelegate>) delegate;
- (NSURLSessionConfiguration*) configuration;
- (NSString*) sessionDescription;
- (void) setSessionDescription: (NSString*)sessionDescription;
/* -finishTasksAndInvalidate returns immediately and existing tasks will be
* allowed to run to completion. New tasks may not be created. The session
* will continue to make delegate callbacks until
* URLSession:didBecomeInvalidWithError: has been issued.
*
* When invalidating a background session, it is not safe to create another
* background session with the same identifier until
* URLSession:didBecomeInvalidWithError: has been issued.
*/
- (void) finishTasksAndInvalidate;
/* -invalidateAndCancel acts as -finishTasksAndInvalidate, but issues
* -cancel to all outstanding tasks for this session. Note task
* cancellation is subject to the state of the task, and some tasks may
* have already have completed at the time they are sent -cancel.
*/
- (void) invalidateAndCancel;
/*
* NSURLSessionTask objects are always created in a suspended state and
* must be sent the -resume message before they will execute.
*/
/* Creates a data task with the given request.
* The request may have a body stream. */
- (NSURLSessionDataTask*) dataTaskWithRequest: (NSURLRequest*)request;
/* Creates a data task to retrieve the contents of the given URL. */
- (NSURLSessionDataTask*) dataTaskWithURL: (NSURL*)url;
@end
@interface NSURLSessionConfiguration : NSObject <NSCopying>
@end
typedef NS_ENUM(NSUInteger, NSURLSessionTaskState) {
/* The task is currently being serviced by the session */
NSURLSessionTaskStateRunning = 0,
NSURLSessionTaskStateSuspended = 1,
/* The task has been told to cancel.
* The session will receive URLSession:task:didCompleteWithError:. */
NSURLSessionTaskStateCanceling = 2,
/* The task has completed and the session will receive no more
* delegate notifications */
NSURLSessionTaskStateCompleted = 3,
};
/*
* NSURLSessionTask - a cancelable object that refers to the lifetime
* of processing a given request.
*/
@interface NSURLSessionTask : NSObject <NSCopying>
{
/** An identifier for this task, assigned by and unique
* to the owning session
*/
NSUInteger _taskIdentifier;
/** The request this task was created to handle.
*/
NSURLRequest *_originalRequest;
/** The request this task is currently handling. This may differ from
* originalRequest due to http server redirection
*/
NSURLRequest *_currentRequest;
/** The response to the current request, which may be nil if no response
* has been received
*/
NSURLResponse *_response;
/** number of body bytes already received
*/
int64_t _countOfBytesReceived;
/** number of body bytes already sent
*/
int64_t _countOfBytesSent;
/** number of body bytes we expect to send, derived from
* the Content-Length of the HTTP request
*/
int64_t _countOfBytesExpectedToSend;
/** number of byte bytes we expect to receive, usually derived from the
* Content-Length header of an HTTP response.
*/
int64_t _countOfBytesExpectedToReceive;
/** a description of the current task for diagnostic purposes
*/
NSString *_taskDescription;
/** The current state of the task within the session.
*/
NSURLSessionTaskState _state;
/** The error, if any, delivered via -URLSession:task:didCompleteWithError:
* This is nil until an error has occured.
*/
NSError *_error;
/** The dispatch queue used to handle this request/response.
* This is actualy a libdispatch queue of type dispatch_queue_t, but on all
* known implementations this is a pointer, so void* is the correct size.
*/
void *_workQueue;
NSUInteger _suspendCount;
GSURLSessionTaskBody *_knownBody;
}
- (NSUInteger) taskIdentifier;
- (NSURLRequest*) originalRequest;
- (NSURLRequest*) currentRequest;
- (NSURLResponse*) response;
- (void) setResponse: (NSURLResponse*)response;
- (int64_t) countOfBytesReceived;
- (int64_t) countOfBytesSent;
- (int64_t) countOfBytesExpectedToSend;
- (int64_t) countOfBytesExpectedToReceive;
- (NSString*) taskDescription;
- (void) setTaskDescription: (NSString*)taskDescription;
- (NSURLSessionTaskState) state;
- (NSError*) error;
- (NSURLSession*) session;
/* -cancel returns immediately, but marks a task as being canceled.
* The task will signal -URLSession:task:didCompleteWithError: with an
* error value of { NSURLErrorDomain, NSURLErrorCancelled }. In some
* cases, the task may signal other work before it acknowledges the
* cancelation. -cancel may be sent to a task that has been suspended.
*/
- (void) cancel;
/*
* Suspending a task will prevent the NSURLSession from continuing to
* load data. There may still be delegate calls made on behalf of
* this task (for instance, to report data received while suspending)
* but no further transmissions will be made on behalf of the task
* until -resume is sent. The timeout timer associated with the task
* will be disabled while a task is suspended.
*/
- (void) suspend;
- (void) resume;
@end
@interface NSURLSessionDataTask : NSURLSessionTask
@ -30,11 +252,177 @@
@end
#endif
/*
* Configuration options for an NSURLSession. When a session is
* created, a copy of the configuration object is made - you cannot
* modify the configuration of a session after it has been created.
*/
@interface NSURLSessionConfiguration : NSObject <NSCopying>
{
NSURLCache *_URLCache;
NSURLRequestCachePolicy _requestCachePolicy;
NSArray *_protocolClasses;
NSInteger _HTTPMaximumConnectionLifetime;
NSInteger _HTTPMaximumConnectionsPerHost;
BOOL _HTTPShouldUsePipelining;
NSHTTPCookieAcceptPolicy _HTTPCookieAcceptPolicy;
NSHTTPCookieStorage *_HTTPCookieStorage;
NSURLCredentialStorage *_URLCredentialStorage;
BOOL _HTTPShouldSetCookies;
NSDictionary *_HTTPAdditionalHeaders;
}
- (NSURLRequest*) configureRequest: (NSURLRequest*)request;
@property (class, readonly, strong)
NSURLSessionConfiguration *defaultSessionConfiguration;
- (NSDictionary*) HTTPAdditionalHeaders;
- (NSHTTPCookieAcceptPolicy) HTTPCookieAcceptPolicy;
- (NSHTTPCookieStorage*) HTTPCookieStorage;
#if !NO_GNUSTEP
- (NSInteger) HTTPMaximumConnectionLifetime;
#endif
- (NSInteger) HTTPMaximumConnectionsPerHost;
- (BOOL) HTTPShouldSetCookies;
- (BOOL) HTTPShouldUsePipelining;
- (NSArray*) protocolClasses;
- (NSURLRequestCachePolicy) requestCachePolicy;
- (void) setHTTPAdditionalHeaders: (NSDictionary*)headers;
- (void) setHTTPCookieAcceptPolicy: (NSHTTPCookieAcceptPolicy)policy;
- (void) setHTTPCookieStorage: (NSHTTPCookieStorage*)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.
*/
- (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) {
NSURLSessionAuthChallengeUseCredential = 0,
NSURLSessionAuthChallengePerformDefaultHandling = 1,
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,
NSURLSessionAuthChallengeRejectProtectionSpace = 3
};
typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
NSURLSessionResponseCancel = 0,
NSURLSessionResponseAllow = 1,
NSURLSessionResponseBecomeDownload = 2,
NSURLSessionResponseBecomeStream = 3
};
@protocol NSURLSessionDelegate <NSObject>
@optional
/* The last message a session receives. A session will only become
* invalid because of a systemic error or when it has been
* explicitly invalidated, in which case the error parameter will be nil.
*/
- (void) URLSession: (NSURLSession*)session
didBecomeInvalidWithError: (NSError*)error;
/* Implementing this method permits a delegate to provide authentication
* credentials in response to a challenge from the remote server.
*/
- (void) URLSession: (NSURLSession*)session
didReceiveChallenge: (NSURLAuthenticationChallenge*)challenge
completionHandler: (void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))handler;
@end
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
@optional
/* Sent as the last message related to a specific task. Error may be
* nil, which implies that no error occurred and this task is complete.
*/
- (void ) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didCompleteWithError: (NSError*)error;
/* Called to request authentication credentials from the delegate when
* an authentication request is received from the server which is specific
* to this task.
*/
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didReceiveChallenge: (NSURLAuthenticationChallenge*)challenge
completionHandler: (void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))handler;
/* Periodically informs the delegate of the progress of sending body content
* to the server.
*/
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didSendBodyData: (int64_t)bytesSent
totalBytesSent: (int64_t)totalBytesSent
totalBytesExpectedToSend: (int64_t)totalBytesExpectedToSend;
/* An HTTP request is attempting to perform a redirection to a different
* URL. You must invoke the completion routine to allow the
* redirection, allow the redirection with a modified request, or
* pass nil to the completionHandler to cause the body of the redirection
* response to be delivered as the payload of this request. The default
* is to follow redirections.
*
*/
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
willPerformHTTPRedirection: (NSHTTPURLResponse*)response
newRequest: (NSURLRequest*)request
completionHandler: (void (^)(NSURLRequest*))completionHandler;
@end
@protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
@optional
/* Sent when data is available for the delegate to consume.
*/
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveData: (NSData*)data;
/** Informs the delegate of a response. This message is sent when all the
* response headers have arrived, before the body of the response arrives.
*/
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveResponse: (NSURLResponse*)response
completionHandler: (void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
@end
#endif
#endif
#endif

View file

@ -267,8 +267,10 @@ typedef struct {
#define GS_USE_AVAHI @HAVE_AVAHI@
#define GS_USE_MDNS @HAVE_MDNS@
#define GS_USE_ICU @HAVE_ICU@
#define GS_USE_LIBCURL @HAVE_LIBCURL@
#define GS_USE_LIBDISPATCH @HAVE_LIBDISPATCH@
#define GS_USE_LIBDISPATCH_RUNLOOP @HAVE_LIBDISPATCH_RUNLOOP@
#define GS_HAVE_NSURLSESSION @GS_HAVE_NSURLSESSION@
#define GS_HAVE_OBJC_ROOT_CLASS_ATTR @GS_HAVE_OBJC_ROOT_CLASS_ATTR@
#ifndef __has_include

View file

@ -189,6 +189,12 @@
/* Define to 1 if you have the `ctime' function. */
#undef HAVE_CTIME
/* Define to 1 if you have the <curl/curl.h> header file. */
#undef HAVE_CURL_CURL_H
/* Define to 1 if you have the `curl_global_sslset' function. */
#undef HAVE_CURL_GLOBAL_SSLSET
/* Define if you have currency_symbol field in struct lconv */
#undef HAVE_CURRENCY_SYMBOL_IN_LCONV
@ -219,6 +225,10 @@
/* Define to 1 if you have the <dispatch/private.h> header file. */
#undef HAVE_DISPATCH_PRIVATE_H
/* Define to 1 if you have the `dispatch_queue_create_with_target' function.
*/
#undef HAVE_DISPATCH_QUEUE_CREATE_WITH_TARGET
/* Define to 1 if you have the `dladdr' function. */
#undef HAVE_DLADDR

View file

@ -2091,3 +2091,253 @@ GSClassSwizzle(id instance, Class newClass)
}
}
void
GSObjCPrint(void *base, void *item)
{
FILE *fptr = stdout;
Class c;
id o;
if (NULL == base)
{
fprintf(fptr, "null\n");
return;
}
if (GSObjCIsClass((Class)base))
{
o = nil;
c = (Class)base;
if (NULL == item)
{
fprintf(fptr, "%p is class %s {\n", base, GSNameFromClass(c));
}
}
else if (GSObjCIsInstance((id)base))
{
o = (id)base;
c = GSObjCClass(o);
if (NULL == item)
{
fprintf(fptr, "%p is instance of class %s (%p) {\n",
base, GSNameFromClass(c), c);
}
}
else
{
fprintf(fptr, "%p is not a class or instance\n", base);
return;
}
while (c != Nil)
{
unsigned count;
Ivar *ivars = class_copyIvarList(c, &count);
while (count-- > 0)
{
Ivar ivar = ivars[count];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
ptrdiff_t offset = ivar_getOffset(ivar);
const char *t;
if (NULL == item)
{
fprintf(fptr, " (%ld) %s", (long)offset, name);
}
else if (strcmp(item, name) == 0)
{
continue; // not a match
}
else
{
fprintf(fptr, "(%ld) %s", (long)offset, name);
}
if (nil == o)
{
/* We have no instance ... display offset to ivar
*/
fprintf(fptr, "\n");
continue;
}
fprintf(fptr, " = ");
t = GSSkipTypeQualifierAndLayoutInfo(type);
switch (*t)
{
case _C_ID:
{
id v = *(id *)((char *)o + offset);
if (nil == v)
{
fprintf(fptr, "nil\n");
}
else
{
fprintf(fptr, "%s instance %p\n",
GSNameFromClass(GSObjCClass(v)), v);
}
}
break;
case _C_CLASS:
{
Class v = *(Class *)((char *)o + offset);
if (Nil == v)
{
fprintf(fptr, "Nil\n");
}
else
{
fprintf(fptr, "%s class %p\n", GSNameFromClass(v), v);
}
}
break;
case _C_CHR:
{
signed char v = *(char *)((char *)o + offset);
fprintf(fptr, "%c %d\n", v, (int)v);
}
break;
case _C_UCHR:
{
unsigned char v = *(unsigned char *)((char *)o + offset);
fprintf(fptr, "%c %u\n", v, (unsigned)v);
}
break;
#if __GNUC__ > 2 && defined(_C_BOOL)
case _C_BOOL:
{
_Bool v = *(_Bool *)((char *)o + offset);
fprintf(fptr, "%s %u\n", (v ? "YES" : "NO"), (unsigned)v);
}
break;
#endif
case _C_SHT:
{
short v = *(short *)((char *)o + offset);
fprintf(fptr, "%hd\n", v);
}
break;
case _C_USHT:
{
unsigned short v;
v = *(unsigned short *)((char *)o + offset);
fprintf(fptr, "%hu\n", v);
}
break;
case _C_INT:
{
int v = *(int *)((char *)o + offset);
fprintf(fptr, "%d\n", v);
}
break;
case _C_UINT:
{
unsigned int v = *(unsigned int *)((char *)o + offset);
fprintf(fptr, "%u\n", v);
}
break;
case _C_LNG:
{
long v = *(long *)((char *)o + offset);
fprintf(fptr, "%ld\n", v);
}
break;
case _C_ULNG:
{
unsigned long v = *(unsigned long *)((char *)o + offset);
fprintf(fptr, "%lu\n", v);
}
break;
#ifdef _C_LNG_LNG
case _C_LNG_LNG:
{
long long v = *(long long *)((char *)o + offset);
fprintf(fptr, "%lld\n", v);
}
break;
#endif
#ifdef _C_ULNG_LNG
case _C_ULNG_LNG:
{
unsigned long long v;
v = *(unsigned long long *)((char *)o + offset);
fprintf(fptr, "%llu\n", v);
}
break;
#endif
case _C_FLT:
{
float v = *(float *)((char *)o + offset);
fprintf(fptr, "%g\n", v);
}
break;
case _C_DBL:
{
double v = *(double *)((char *)o + offset);
fprintf(fptr, "%g\n", v);
}
break;
case _C_VOID:
{
fprintf(fptr, "void ???\n");
}
break;
case _C_STRUCT_B:
{
fprintf(fptr, "struct not supported\n");
}
break;
default:
{
fprintf(fptr, "type %s not supported\n", type);
}
break;
}
}
if (ivars != NULL)
{
free(ivars);
}
c = class_getSuperclass(c);
}
if (NULL == item)
{
fprintf(fptr, "}\n");
}
}

View file

@ -67,4 +67,4 @@ ifeq ($(FOUNDATION_LIB),gnu)
endif
# Additional LDFLAGS to pass to the linker
ADDITIONAL_LDFLAGS =
ADDITIONAL_LDFLAGS =

View file

@ -331,7 +331,6 @@ NSUbiquitousKeyValueStore.m \
NSUnarchiver.m \
NSUndoManager.m \
NSURL.m \
NSURLSession.m \
NSURLAuthenticationChallenge.m \
NSURLCache.m \
NSURLCredential.m \
@ -362,33 +361,53 @@ objc-load.m
ifneq ($(GNUSTEP_TARGET_OS), mingw32)
ifneq ($(GNUSTEP_TARGET_OS), mingw64)
BASE_MFILES += \
GSFileHandle.m \
NSMessagePort.m \
NSMessagePortNameServer.m
BASE_MFILES += \
GSFileHandle.m \
NSMessagePort.m \
NSMessagePortNameServer.m
endif
endif
ifeq ($(HAVE_BLOCKS), 1)
ifeq ($(GNUSTEP_BASE_HAVE_LIBDISPATCH), 1)
ifeq ($(GNUSTEP_BASE_HAVE_LIBCURL), 1)
BASE_MFILES += \
GSEasyHandle.m \
GSHTTPURLProtocol.m \
GSMultiHandle.m \
GSNativeProtocol.m \
GSTaskRegistry.m \
GSTimeoutSource.m \
GSTransferState.m \
GSURLSessionTaskBody.m \
GSURLSessionTaskBodySource.m \
NSURLSession.m
endif
endif
endif
ifeq ($(GNUSTEP_BASE_HAVE_MDNS), 1)
BASE_MFILES += NSNetServices.m \
GSMDNSNetServices.m
BASE_MFILES += \
GSMDNSNetServices.m \
NSNetServices.m
endif
ifeq ($(GNUSTEP_BASE_HAVE_AVAHI), 1)
BASE_MFILES += NSNetServices.m \
GSAvahiNetService.m \
GSAvahiNetServiceBrowser.m \
GSAvahiClient.m \
GSAvahiRunLoopIntegration.m
BASE_MFILES += \
GSAvahiNetService.m \
GSAvahiNetServiceBrowser.m \
GSAvahiClient.m \
GSAvahiRunLoopIntegration.m \
NSNetServices.m
endif
ifeq ($(WITH_FFI),libffi)
GNU_MFILES += cifframe.m
BASE_MFILES += GSFFIInvocation.m
GNU_MFILES += cifframe.m
BASE_MFILES += GSFFIInvocation.m
endif
ifeq ($(WITH_FFI),ffcall)
GNU_MFILES += callframe.m
BASE_MFILES += GSFFCallInvocation.m
GNU_MFILES += callframe.m
BASE_MFILES += GSFFCallInvocation.m
endif
BASE_OTHER_SRCFILES = \

236
Source/GSEasyHandle.h Normal file
View file

@ -0,0 +1,236 @@
#ifndef INCLUDED_GSEASYHANDLE_H
#define INCLUDED_GSEASYHANDLE_H
#import "common.h"
#import <curl/curl.h>
@class NSData;
@class NSError;
@class NSURL;
@class NSURLSessionConfiguration;
@class NSURLSessionTask;
@class GSTimeoutSource;
typedef NS_ENUM(NSUInteger, GSEasyHandleAction) {
GSEasyHandleActionAbort,
GSEasyHandleActionProceed,
GSEasyHandleActionPause,
};
typedef NS_ENUM(NSUInteger, GSEasyHandleWriteBufferResult) {
GSEasyHandleWriteBufferResultAbort,
GSEasyHandleWriteBufferResultPause,
GSEasyHandleWriteBufferResultBytes,
};
@protocol GSEasyHandleDelegate <NSObject>
/*
* Handle data read from the network.
* - returns: the action to be taken: abort, proceed, or pause.
*/
- (GSEasyHandleAction) didReceiveData: (NSData*)data;
/*
* Handle header data read from the network.
* - returns: the action to be taken: abort, proceed, or pause.
*/
- (GSEasyHandleAction) didReceiveHeaderData: (NSData*)data
contentLength: (int64_t)contentLength;
/*
* Fill a buffer with data to be sent.
* - parameter data: The buffer to fill
* - returns: the number of bytes written to the `data` buffer, or `nil`
* to stop the current transfer immediately.
*/
- (void) fillWriteBufferLength: (NSInteger)length
result: (void (^)(GSEasyHandleWriteBufferResult result, NSInteger length, NSData *data))result;
/*
* The transfer for this handle completed.
* - parameter errorCode: An NSURLError code, or `nil` if no error occurred.
*/
- (void) transferCompletedWithError: (NSError*)error;
/*
* Seek the input stream to the given position
*/
- (BOOL) seekInputStreamToPosition: (uint64_t)position;
/*
* Gets called during the transfer to update progress.
*/
- (void) updateProgressMeterWithTotalBytesSent: (int64_t)totalBytesSent
totalBytesExpectedToSend: (int64_t)totalBytesExpectedToSend
totalBytesReceived: (int64_t)totalBytesReceived
totalBytesExpectedToReceive: (int64_t)totalBytesExpectedToReceive;
@end
/*
* Minimal wrapper around the curl easy interface
* (https://curl.haxx.se/libcurl/c/)
*
* An *easy handle* manages the state of a transfer inside libcurl.
*
* As such the easy handle's responsibility is implementing the HTTP
* protocol while the *multi handle* is in charge of managing sockets and
* reading from / writing to these sockets.
*
* An easy handle is added to a multi handle in order to associate it with
* an actual socket. The multi handle will then feed bytes into the easy
* handle and read bytes from the easy handle. But this process is opaque
* to use. It is further worth noting, that with HTTP/1.1 persistent
* connections and with HTTP/2 there's a 1-to-many relationship between
* TCP streams and HTTP transfers / easy handles. A single TCP stream and
* its socket may be shared by multiple easy handles.
*
* A single HTTP request-response exchange (refered to here as a
* *transfer*) corresponds directly to an easy handle. Hence anything that
* needs to be configured for a specific transfer (e.g. the URL) will be
* configured on an easy handle.
*
* A single `NSURLSessionTask` may do multiple, consecutive transfers, and
* as a result it will have to reconfigure its easy handle between
* transfers. An easy handle can be re-used once its transfer has
* completed.
*
* Note: All code assumes that it is being called on a single thread,
* it is intentionally **not** thread safe.
*/
@interface GSEasyHandle : NSObject
{
CURL *_rawHandle;
char *_errorBuffer;
id<GSEasyHandleDelegate> _delegate;
GSTimeoutSource *_timeoutTimer;
NSURL *_URL;
}
- (CURL*) rawHandle;
- (char*) errorBuffer;
/*
* Set error buffer for error messages
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html
*/
- (void) setErrorBuffer: (char*)buffer;
- (GSTimeoutSource*) timeoutTimer;
- (void) setTimeoutTimer: (GSTimeoutSource*)timer;
- (NSURL*) URL;
/*
* URL to use in the request
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_URL.html
*/
- (void) setURL: (NSURL*)URL;
- (void) setPipeWait: (BOOL)flag;
- (instancetype) initWithDelegate: (id<GSEasyHandleDelegate>)delegate;
- (void) transferCompletedWithError: (NSError*)error;
- (int) urlErrorCodeWithEasyCode: (int)easyCode;
- (void) setVerboseMode: (BOOL)flag;
- (void) setDebugOutput: (BOOL)flag
task: (NSURLSessionTask*)task;
- (void) setPassHeadersToDataStream: (BOOL)flag;
/*
* Follow any Location: header that the server sends as part of a HTTP header
* in a 3xx response
*/
- (void) setFollowLocation: (BOOL)flag;
/*
* Switch off the progress meter.
*/
- (void) setProgressMeterOff: (BOOL)flag;
/*
* Skip all signal handling
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_NOSIGNAL.html
*/
- (void) setSkipAllSignalHandling: (BOOL)flag;
/*
* Request failure on HTTP response >= 400
*/
- (void) setFailOnHTTPErrorCode: (BOOL)flag;
- (void) setConnectToHost: (NSString*)host
port: (NSInteger)port;
- (void) setSessionConfig: (NSURLSessionConfiguration*)config;
- (void) setAllowedProtocolsToHTTPAndHTTPS;
/*
* set preferred receive buffer size
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_BUFFERSIZE.html
*/
- (void) setPreferredReceiveBufferSize: (NSInteger)size;
/*
* Set custom HTTP headers
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_HTTPHEADER.html
*/
- (void) setCustomHeaders: (NSArray*)headers;
/*
* Enable automatic decompression of HTTP downloads
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_CONTENT_DECODING.html
*/
- (void) setAutomaticBodyDecompression: (BOOL)flag;
/*
* Set request method
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_CUSTOMREQUEST.html
*/
- (void) setRequestMethod:(NSString*)method;
/*
* Download request without body
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_NOBODY.html
*/
- (void) setNoBody: (BOOL)flag;
/*
* Enable data upload
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_UPLOAD.html
*/
- (void) setUpload: (BOOL)flag;
/*
* Set size of the request body to send
* - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_INFILESIZE_LARGE.html
*/
- (void) setRequestBodyLength: (int64_t)length;
- (void) setTimeout: (NSInteger)timeout;
- (void) setProxy;
- (double) getTimeoutIntervalSpent;
- (void) pauseReceive;
- (void) unpauseReceive;
- (void) pauseSend;
- (void) unpauseSend;
@end
#endif

756
Source/GSEasyHandle.m Normal file
View file

@ -0,0 +1,756 @@
#import "GSURLPrivate.h"
#import "GSEasyHandle.h"
#import "GSTimeoutSource.h"
#import "Foundation/NSCharacterSet.h"
#import "Foundation/NSURLSession.h"
typedef NS_OPTIONS(NSUInteger, GSEasyHandlePauseState) {
GSEasyHandlePauseStateReceive = 1 << 0,
GSEasyHandlePauseStateSend = 1 << 1
};
@interface GSEasyHandle ()
- (void) resetTimer;
- (NSInteger) didReceiveData: (char*)data
size: (NSInteger)size
nmemb:(NSInteger)nmemb;
- (NSInteger) fillWriteBuffer: (char *)buffer
size: (NSInteger)size
nmemb: (NSInteger)nmemb;
- (NSInteger) didReceiveHeaderData: (char*)headerData
size: (NSInteger)size
nmemb: (NSInteger)nmemb
contentLength: (double)contentLength;
- (int) seekInputStreamWithOffset: (int64_t)offset
origin: (NSInteger)origin;
@end
static void
handleEasyCode(int code)
{
if (CURLE_OK != code)
{
NSString *reason;
NSException *e;
reason = [NSString stringWithFormat: @"An error occurred, CURLcode is %d",
code];
e = [NSException exceptionWithName: @"libcurl.easy"
reason: reason
userInfo: nil];
[e raise];
}
}
static size_t
curl_write_function(char *data, size_t size, size_t nmemb, void *userdata)
{
if (!userdata)
{
return 0;
}
GSEasyHandle *handle = (GSEasyHandle*)userdata;
[handle resetTimer]; //FIXME should be deffered after the function returns?
return [handle didReceiveData:data size:size nmemb:nmemb];
}
static size_t
curl_read_function(char *data, size_t size, size_t nmemb, void *userdata)
{
if (!userdata)
{
return 0;
}
GSEasyHandle *handle = (GSEasyHandle*)userdata;
[handle resetTimer]; //FIXME should be deffered after the function returns?
return [handle fillWriteBuffer: data size: size nmemb: nmemb];
}
size_t
curl_header_function(char *data, size_t size, size_t nmemb, void *userdata)
{
if (!userdata)
{
return 0;
}
GSEasyHandle *handle = (GSEasyHandle*)userdata;
double length;
[handle resetTimer]; //FIXME should be deffered after the function returns?
handleEasyCode(curl_easy_getinfo(handle.rawHandle,
CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length));
return [handle didReceiveHeaderData: data
size: size
nmemb: nmemb
contentLength: length];
}
static int
curl_seek_function(void *userdata, curl_off_t offset, int origin)
{
if (!userdata)
{
return CURL_SEEKFUNC_FAIL;
}
GSEasyHandle *handle = (GSEasyHandle*)userdata;
return [handle seekInputStreamWithOffset: offset origin: origin];
}
static int
curl_debug_function(CURL *handle, curl_infotype type, char *data,
size_t size, void *userptr)
{
if (!userptr)
{
return 0;
}
if (CURLINFO_SSL_DATA_OUT == type || CURLINFO_SSL_DATA_IN == type)
{
return 0; // Don't log encrypted data here
}
NSURLSessionTask *task = (NSURLSessionTask*)userptr;
NSString *text = @"";
NSURLRequest *o = [task originalRequest];
NSURLRequest *r = [task currentRequest];
id<GSLogDelegate> d = [(nil == r ? o : r) _debugLogDelegate];
if (d != nil)
{
if (CURLINFO_DATA_IN == type || CURLINFO_HEADER_IN == type)
{
if ([d getBytes: (const uint8_t *)data ofLength: size byHandle: o])
{
return 0; // Handled
}
}
if (CURLINFO_DATA_OUT == type || CURLINFO_HEADER_OUT == type)
{
if ([d putBytes: (const uint8_t *)data ofLength: size byHandle: o])
{
return 0; // Handled
}
}
}
if (data)
{
text = [NSString stringWithUTF8String: data];
}
NSLog(@"%p %lu %d %@", o, [task taskIdentifier], type, text);
return 0;
}
static int
curl_socket_function(void *userdata, curl_socket_t fd, curlsocktype type)
{
return 0;
}
@implementation GSEasyHandle
{
NSURLSessionConfiguration *_config;
GSEasyHandlePauseState _pauseState;
struct curl_slist *_headerList;
}
- (instancetype) initWithDelegate: (id<GSEasyHandleDelegate>)delegate
{
if (nil != (self = [super init]))
{
_rawHandle = curl_easy_init();
_delegate = delegate;
char *eb = (char *)malloc(sizeof(char) * (CURL_ERROR_SIZE + 1));
_errorBuffer = memset(eb, 0, sizeof(char) * (CURL_ERROR_SIZE + 1));
[self setupCallbacks];
}
return self;
}
- (void) dealloc
{
curl_easy_cleanup(_rawHandle);
curl_slist_free_all(_headerList);
free(_errorBuffer);
DESTROY(_config);
DESTROY(_timeoutTimer);
DESTROY(_URL);
[super dealloc];
}
- (CURL*) rawHandle
{
return _rawHandle;
}
- (char*) errorBuffer
{
return _errorBuffer;
}
- (GSTimeoutSource*) timeoutTimer
{
return _timeoutTimer;
}
- (void) setTimeoutTimer: (GSTimeoutSource*)timer
{
ASSIGN(_timeoutTimer, timer);
}
- (NSURL*) URL
{
return _URL;
}
- (void) transferCompletedWithError: (NSError*)error
{
[_delegate transferCompletedWithError: 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]];
}
- (void) setupCallbacks
{
// write
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_WRITEDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_WRITEFUNCTION,
curl_write_function));
// read
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_READDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_READFUNCTION,
curl_read_function));
// header
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_HEADERDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_HEADERFUNCTION,
curl_header_function));
// socket options
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_SOCKOPTDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_SOCKOPTFUNCTION,
curl_socket_function));
// seeking in input stream
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_SEEKDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_SEEKFUNCTION,
curl_seek_function));
}
- (int) urlErrorCodeWithEasyCode: (int)easyCode
{
int failureErrno = (int)[self connectFailureErrno];
if (easyCode == CURLE_OK)
{
return 0;
}
else if (failureErrno == ECONNREFUSED)
{
return NSURLErrorCannotConnectToHost;
}
else if (easyCode == CURLE_UNSUPPORTED_PROTOCOL)
{
return NSURLErrorUnsupportedURL;
}
else if (easyCode == CURLE_URL_MALFORMAT)
{
return NSURLErrorBadURL;
}
else if (easyCode == CURLE_COULDNT_RESOLVE_HOST)
{
return NSURLErrorCannotFindHost;
}
else if (easyCode == CURLE_RECV_ERROR && failureErrno == ECONNRESET)
{
return NSURLErrorNetworkConnectionLost;
}
else if (easyCode == CURLE_SEND_ERROR && failureErrno == ECONNRESET)
{
return NSURLErrorNetworkConnectionLost;
}
else if (easyCode == CURLE_GOT_NOTHING)
{
return NSURLErrorBadServerResponse;
}
else if (easyCode == CURLE_ABORTED_BY_CALLBACK)
{
return NSURLErrorUnknown;
}
else if (easyCode == CURLE_COULDNT_CONNECT && failureErrno == ETIMEDOUT)
{
return NSURLErrorTimedOut;
}
else if (easyCode == CURLE_OPERATION_TIMEDOUT)
{
return NSURLErrorTimedOut;
}
else
{
return NSURLErrorUnknown;
}
}
- (void) setVerboseMode: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_VERBOSE, flag ? 1 : 0));
}
- (void) setDebugOutput: (BOOL)flag
task: (NSURLSessionTask*)task
{
if (flag)
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGDATA, self));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGFUNCTION,
curl_debug_function));
}
else
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGDATA, NULL));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_DEBUGFUNCTION, NULL));
}
}
- (void) setPassHeadersToDataStream: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_HEADER,
flag ? 1 : 0));
}
- (void) setFollowLocation: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_FOLLOWLOCATION,
flag ? 1 : 0));
}
- (void) setProgressMeterOff: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_NOPROGRESS,
flag ? 1 : 0));
}
- (void) setSkipAllSignalHandling: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_NOSIGNAL,
flag ? 1 : 0));
}
- (void) setErrorBuffer: (char*)buffer
{
char *b = buffer ? buffer : _errorBuffer;
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_ERRORBUFFER, b));
}
- (void) setFailOnHTTPErrorCode: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_FAILONERROR,
flag ? 1 : 0));
}
- (void) setURL: (NSURL *)URL
{
ASSIGN(_URL, URL);
if (nil != [URL absoluteString])
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_URL,
[[URL absoluteString] UTF8String]));
}
}
- (void) setPipeWait: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_PIPEWAIT, flag ? 1 : 0));
}
- (void) setConnectToHost: (NSString*)host port: (NSInteger)port
{
if (nil != host)
{
NSString *originHost = [_URL host];
NSString *value = nil;
if (0 == port)
{
value = [NSString stringWithFormat: @"%@::%@", originHost, host];
}
else
{
value = [NSString stringWithFormat: @"%@:%lu:%@",
originHost, port, host];
}
struct curl_slist *connect_to = NULL;
connect_to = curl_slist_append(NULL, [value UTF8String]);
handleEasyCode(
curl_easy_setopt(_rawHandle, CURLOPT_CONNECT_TO, connect_to));
}
}
- (void) setSessionConfig: (NSURLSessionConfiguration*)config
{
ASSIGN(_config, config);
#if defined(CURLOPT_MAXAGE_CONN)
/* This specifies the maximum age of a connection if it is to be considered
* a candidate for re-use. By default curl currently uses 118 seconds, so
* this is what we will get if the configuration does not contain a positive
* number of seconds.
*/
if ([config HTTPMaximumConnectionLifetime] > 0)
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_MAXAGE_CONN,
(long)[config HTTPMaximumConnectionLifetime]));
}
#endif
}
- (void) setAllowedProtocolsToHTTPAndHTTPS
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_PROTOCOLS,
CURLPROTO_HTTP | CURLPROTO_HTTPS));
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_REDIR_PROTOCOLS,
CURLPROTO_HTTP | CURLPROTO_HTTPS));
}
- (void) setPreferredReceiveBufferSize: (NSInteger)size
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_BUFFERSIZE,
MIN(size, CURL_MAX_WRITE_SIZE)));
}
- (void) setCustomHeaders: (NSArray*)headers
{
NSEnumerator *e;
NSString *h;
e = [headers objectEnumerator];
while (nil != (h = [e nextObject]))
{
_headerList = curl_slist_append(_headerList, [h UTF8String]);
}
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_HTTPHEADER, _headerList));
}
- (void) setAutomaticBodyDecompression: (BOOL)flag
{
if (flag)
{
handleEasyCode(curl_easy_setopt(_rawHandle,
CURLOPT_ACCEPT_ENCODING, ""));
handleEasyCode(curl_easy_setopt(_rawHandle,
CURLOPT_HTTP_CONTENT_DECODING, 1));
}
else
{
handleEasyCode(curl_easy_setopt(_rawHandle,
CURLOPT_ACCEPT_ENCODING, NULL));
handleEasyCode(curl_easy_setopt(_rawHandle,
CURLOPT_HTTP_CONTENT_DECODING, 0));
}
}
- (void) setRequestMethod: (NSString*)method
{
if (nil == method)
{
return;
}
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_CUSTOMREQUEST,
[method UTF8String]));
}
- (void) setNoBody: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_NOBODY,
flag ? 1 : 0));
}
- (void) setUpload: (BOOL)flag
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_UPLOAD, flag ? 1 : 0));
}
- (void) setRequestBodyLength: (int64_t)length
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_INFILESIZE_LARGE,
length));
}
- (void) setTimeout: (NSInteger)timeout
{
handleEasyCode(curl_easy_setopt(_rawHandle, CURLOPT_TIMEOUT,
(long)timeout));
}
- (void) setProxy
{
//TODO setup proxy
}
- (void) updatePauseState: (GSEasyHandlePauseState)pauseState
{
NSUInteger send = pauseState & GSEasyHandlePauseStateSend;
NSUInteger receive = pauseState & GSEasyHandlePauseStateReceive;
int bitmask;
bitmask = 0
| (send ? CURLPAUSE_SEND : CURLPAUSE_SEND_CONT)
| (receive ? CURLPAUSE_RECV : CURLPAUSE_RECV_CONT);
handleEasyCode(curl_easy_pause(_rawHandle, bitmask));
}
- (double) getTimeoutIntervalSpent
{
double timeSpent;
curl_easy_getinfo(_rawHandle, CURLINFO_TOTAL_TIME, &timeSpent);
return timeSpent / 1000;
}
- (long) connectFailureErrno
{
long _errno;
handleEasyCode(curl_easy_getinfo(_rawHandle, CURLINFO_OS_ERRNO, &_errno));
return _errno;
}
- (void) pauseSend
{
if (_pauseState & GSEasyHandlePauseStateSend)
{
return;
}
_pauseState = _pauseState | GSEasyHandlePauseStateSend;
[self updatePauseState: _pauseState];
}
- (void) unpauseSend
{
if (!(_pauseState & GSEasyHandlePauseStateSend))
{
return;
}
_pauseState = _pauseState ^ GSEasyHandlePauseStateSend;
[self updatePauseState: _pauseState];
}
- (void) pauseReceive
{
if (_pauseState & GSEasyHandlePauseStateReceive)
{
return;
}
_pauseState = _pauseState | GSEasyHandlePauseStateReceive;
[self updatePauseState: _pauseState];
}
- (void) unpauseReceive
{
if (!(_pauseState & GSEasyHandlePauseStateReceive))
{
return;
}
_pauseState = _pauseState ^ GSEasyHandlePauseStateReceive;
[self updatePauseState: _pauseState];
}
- (NSInteger) didReceiveData: (char*)data
size: (NSInteger)size
nmemb: (NSInteger)nmemb
{
NSData *buffer;
GSEasyHandleAction action;
NSUInteger bytes;
if (![_delegate respondsToSelector: @selector(didReceiveData:)])
{
return 0;
}
bytes = size * nmemb;
buffer = AUTORELEASE([[NSData alloc] initWithBytes: data length: bytes]);
action = [_delegate didReceiveData: buffer];
switch (action)
{
case GSEasyHandleActionProceed:
return bytes;
case GSEasyHandleActionAbort:
return 0;
case GSEasyHandleActionPause:
_pauseState = _pauseState | GSEasyHandlePauseStateReceive;
return CURL_WRITEFUNC_PAUSE;
}
}
- (NSInteger) didReceiveHeaderData: (char*)headerData
size: (NSInteger)size
nmemb: (NSInteger)nmemb
contentLength: (double)contentLength
{
NSData *buffer;
GSEasyHandleAction action;
NSInteger bytes = size * nmemb;
buffer = [NSData dataWithBytes: headerData length: bytes];
[self setCookiesWithHeaderData: buffer];
if (![_delegate respondsToSelector:
@selector(didReceiveHeaderData:contentLength:)])
{
return 0;
}
action = [_delegate didReceiveHeaderData: buffer
contentLength: (int64_t)contentLength];
switch (action)
{
case GSEasyHandleActionProceed:
return bytes;
case GSEasyHandleActionAbort:
return 0;
case GSEasyHandleActionPause:
_pauseState = _pauseState | GSEasyHandlePauseStateReceive;
return CURL_WRITEFUNC_PAUSE;
}
}
- (NSInteger) fillWriteBuffer: (char*)buffer
size: (NSInteger)size
nmemb: (NSInteger)nmemb
{
__block NSInteger d;
if (![_delegate respondsToSelector: @selector(fillWriteBufferLength:result:)])
{
return CURL_READFUNC_ABORT;
}
[_delegate fillWriteBufferLength: size * nmemb
result: ^(GSEasyHandleWriteBufferResult result, NSInteger length, NSData *data)
{
switch (result)
{
case GSEasyHandleWriteBufferResultPause:
_pauseState = _pauseState | GSEasyHandlePauseStateSend;
d = CURL_READFUNC_PAUSE;
break;
case GSEasyHandleWriteBufferResultAbort:
d = CURL_READFUNC_ABORT;
break;
case GSEasyHandleWriteBufferResultBytes:
memcpy(buffer, [data bytes], length);
d = length;
break;
}
}];
return d;
}
- (int) seekInputStreamWithOffset: (int64_t)offset
origin: (NSInteger)origin
{
NSAssert(SEEK_SET == origin, @"Unexpected 'origin' in seek.");
if (![_delegate respondsToSelector: @selector(seekInputStreamToPosition:)])
{
return CURL_SEEKFUNC_CANTSEEK;
}
if ([_delegate seekInputStreamToPosition: offset])
{
return CURL_SEEKFUNC_OK;
}
else
{
return CURL_SEEKFUNC_CANTSEEK;
}
}
- (void) setCookiesWithHeaderData: (NSData*)data
{
NSString *headerLine;
NSRange r;
NSString *head;
NSString *tail;
NSCharacterSet *set;
NSString *key;
NSString *value;
NSArray *cookies;
NSDictionary *d;
if (nil != _config
&& NSHTTPCookieAcceptPolicyNever != [_config HTTPCookieAcceptPolicy]
&& nil != [_config HTTPCookieStorage])
{
headerLine = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
if (0 == [headerLine length])
{
RELEASE(headerLine);
return;
}
r = [headerLine rangeOfString: @":"];
if (NSNotFound != r.location)
{
head = [headerLine substringToIndex:r.location];
tail = [headerLine substringFromIndex:r.location + 1];
set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
key = [head stringByTrimmingCharactersInSet:set];
value = [tail stringByTrimmingCharactersInSet:set];
if (nil != key && nil != value)
{
d = [NSDictionary dictionaryWithObject: value forKey: key];
cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: d
forURL: _URL];
if ([cookies count] > 0)
{
[[_config HTTPCookieStorage] setCookies: cookies
forURL: _URL
mainDocumentURL: nil];
}
}
}
RELEASE(headerLine);
}
}
@end

View file

@ -108,6 +108,7 @@ static NSString *httpVersion = @"1.1";
BOOL debug;
BOOL keepalive;
BOOL returnAll;
id<GSLogDelegate> ioDelegate;
unsigned char challenged;
NSFileHandle *sock;
NSTimeInterval cacheAge;
@ -135,6 +136,7 @@ static NSString *httpVersion = @"1.1";
}
+ (void) setMaxCached: (NSUInteger)limit;
- (void) _tryLoadInBackground: (NSURL*)fromURL;
- (id<GSLogDelegate>) setDebugLogDelegate: (id<GSLogDelegate>)d;
@end
/**
@ -601,7 +603,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
/*
* Send request to server.
*/
if (YES == debug) debugWrite(self, buf);
if (debug)
{
if (NO == [ioDelegate putBytes: [buf bytes]
ofLength: [buf length]
byHandle: self])
{
debugWrite(self, buf);
}
}
[sock writeInBackgroundAndNotify: buf];
RELEASE(buf);
RELEASE(s);
@ -621,8 +631,16 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
if (debug)
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
d = [dict objectForKey: NSFileHandleNotificationDataItem];
if (YES == debug) debugRead(self, d);
readCount = [d length];
if (debug)
{
if (NO == [ioDelegate getBytes: [d bytes]
ofLength: readCount
byHandle: self])
{
debugRead(self, d);
}
}
if (connectionState == idle)
{
@ -631,11 +649,32 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
* it should just be the connection being closed by the other
* end because of a timeout etc.
*/
if (YES == debug && [d length] != 0)
if (debug)
{
NSLog(@"%@ %p %s Unexpected data (%*.*s) from remote!",
NSStringFromSelector(_cmd), self, keepalive?"K":"",
(int)[d length], (int)[d length], [d bytes]);
NSUInteger length = [d length];
if (length > 0)
{
if (nil == ioDelegate)
{
NSLog(@"%@ %p %s Unexpected data (%*.*s) from remote!",
NSStringFromSelector(_cmd), self, keepalive?"K":"",
(int)[d length], (int)[d length], (char*)[d bytes]);
}
else
{
NSLog(@"%@ %p %s Unexpected data from remote!",
NSStringFromSelector(_cmd), self, keepalive?"K":"");
if (NO == [ioDelegate getBytes: [d bytes]
ofLength: length
byHandle: self])
{
NSLog(@"%@ %p %s (%*.*s)",
NSStringFromSelector(_cmd), self, keepalive?"K":"",
(int)[d length], (int)[d length], (char*)[d bytes]);
}
}
}
}
[nc removeObserver: self name: nil object: sock];
[sock closeFile];
@ -643,7 +682,7 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
}
else if ([parser parse: d] == NO && [parser isComplete] == NO)
{
if (YES == debug)
if (debug)
{
NSLog(@"HTTP parse failure - %@", parser);
}
@ -874,7 +913,7 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
* lost in the network or the remote end received it and
* the response was lost.
*/
if (YES == debug)
if (debug)
{
NSLog(@"HTTP response not received - %@", parser);
}
@ -906,7 +945,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
}
d = [dict objectForKey: NSFileHandleNotificationDataItem];
if (YES == debug) debugRead(self, d);
if (debug)
{
if (NO == [ioDelegate getBytes: [d bytes]
ofLength: [d length]
byHandle: self])
{
debugRead(self, d);
}
}
if ([d length] > 0)
{
@ -1071,7 +1118,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
object: sock];
buf = [cmd dataUsingEncoding: NSASCIIStringEncoding];
if (YES == debug) debugWrite(self, buf);
if (debug)
{
if (NO == [ioDelegate putBytes: [buf bytes]
ofLength: [buf length]
byHandle: self])
{
debugWrite(self, buf);
}
}
[sock writeInBackgroundAndNotify: buf];
when = [NSDate alloc];
@ -1365,6 +1420,16 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
return old;
}
- (id<GSLogDelegate>) setDebugLogDelegate: (id<GSLogDelegate>)d
{
id<GSLogDelegate> old = ioDelegate;
NSAssert(nil == d || [d conformsToProtocol: @protocol(GSLogDelegate)],
NSInvalidArgumentException);
ioDelegate = d;
return old;
}
- (void) setReturnAll: (BOOL)flag
{
returnAll = flag;

View file

@ -0,0 +1,9 @@
#ifndef INCLUDED_GSHTTPURLPROTOCOL_H
#define INCLUDED_GSHTTPURLPROTOCOL_H
#import "GSNativeProtocol.h"
@interface GSHTTPURLProtocol : GSNativeProtocol
@end
#endif

1064
Source/GSHTTPURLProtocol.m Normal file

File diff suppressed because it is too large Load diff

91
Source/GSMultiHandle.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef INCLUDED_GSMULTIHANDLE_H
#define INCLUDED_GSMULTIHANDLE_H
#import "common.h"
#import <curl/curl.h>
#import "GSDispatch.h"
@class NSURLSessionConfiguration;
@class GSEasyHandle;
/*
* Minimal wrapper around curl multi interface
* (https://curl.haxx.se/libcurl/c/libcurl-multi.html).
*
* The the *multi handle* manages the sockets for easy handles
* (`GSEasyHandle`), and this implementation uses
* libdispatch to listen for sockets being read / write ready.
*
* Using `dispatch_source_t` allows this implementation to be
* non-blocking and all code to run on the same thread
* thus keeping is simple.
*
* - SeeAlso: GSEasyHandle
*/
@interface GSMultiHandle : NSObject
{
CURLM *_rawHandle;
}
- (CURLM*) rawHandle;
- (instancetype) initWithConfiguration: (NSURLSessionConfiguration*)configuration
workQueue: (dispatch_queue_t)workQueque;
- (void) addHandle: (GSEasyHandle*)easyHandle;
- (void) removeHandle: (GSEasyHandle*)easyHandle;
- (void) updateTimeoutTimerToValue: (NSInteger)value;
@end
// What read / write ready event to register / unregister.
typedef NS_ENUM(NSUInteger, GSSocketRegisterActionType) {
GSSocketRegisterActionTypeNone = 0,
GSSocketRegisterActionTypeRegisterRead,
GSSocketRegisterActionTypeRegisterWrite,
GSSocketRegisterActionTypeRegisterReadAndWrite,
GSSocketRegisterActionTypeUnregister,
};
@interface GSSocketRegisterAction : NSObject
{
GSSocketRegisterActionType _type;
}
- (instancetype) initWithRawValue: (int)rawValue;
- (GSSocketRegisterActionType) type;
- (BOOL) needsReadSource;
- (BOOL) needsWriteSource;
- (BOOL) needsSource;
@end
/*
* Read and write libdispatch sources for a specific socket.
*
* A simple helper that combines two sources -- both being optional.
*
* This info is stored into the socket using `curl_multi_assign()`.
*
* - SeeAlso: GSSocketRegisterAction
*/
@interface GSSocketSources : NSObject
{
dispatch_source_t _readSource;
dispatch_source_t _writeSource;
}
- (void) createSourcesWithAction: (GSSocketRegisterAction *)action
socket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
- (void) createReadSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
- (void) createWriteSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler;
+ (instancetype) from: (void*)socketSourcePtr;
@end
#endif

514
Source/GSMultiHandle.m Normal file
View file

@ -0,0 +1,514 @@
#import "GSMultiHandle.h"
#import "GSTimeoutSource.h"
#import "GSEasyHandle.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSError.h"
#import "Foundation/NSException.h"
#import "Foundation/NSURLError.h"
#import "Foundation/NSURLSession.h"
@interface GSMultiHandle ()
- (void) performActionForSocket: (int)socket;
- (void) readAndWriteAvailableDataOnSocket: (int)socket;
- (void) readMessages;
- (void) completedTransferForEasyHandle: (CURL*)rawEasyHandle
easyCode: (int)easyCode;
- (int32_t) registerWithSocket: (curl_socket_t)socket
what: (int)what
socketSourcePtr: (void *)socketSourcePtr;
@end
static void handleEasyCode(int code)
{
if (CURLE_OK != code)
{
NSString *reason;
NSException *e;
reason = [NSString stringWithFormat: @"An error occurred, CURLcode is %d",
code];
e = [NSException exceptionWithName: @"libcurl.easy"
reason: reason
userInfo: nil];
[e raise];
}
}
static void handleMultiCode(int code)
{
if (CURLM_OK != code)
{
NSString *reason;
NSException *e;
reason = [NSString stringWithFormat: @"An error occurred, CURLcode is %d",
code];
e = [NSException exceptionWithName: @"libcurl.multi"
reason: reason
userInfo: nil];
[e raise];
}
}
static int curl_socket_function(CURL *easyHandle, curl_socket_t socket, int what, void *userdata, void *socketptr)
{
GSMultiHandle *handle = (GSMultiHandle*)userdata;
return [handle registerWithSocket: socket
what: what
socketSourcePtr: socketptr];
}
static int curl_timer_function(CURL *easyHandle, int timeout, void *userdata) {
GSMultiHandle *handle = (GSMultiHandle*)userdata;
[handle updateTimeoutTimerToValue: timeout];
return 0;
}
@implementation GSMultiHandle
{
NSMutableArray *_easyHandles;
dispatch_queue_t _queue;
GSTimeoutSource *_timeoutSource;
}
- (CURLM*) rawHandle
{
return _rawHandle;
}
- (instancetype) initWithConfiguration: (NSURLSessionConfiguration*)conf
workQueue: (dispatch_queue_t)aQueue
{
if (nil != (self = [super init]))
{
_rawHandle = curl_multi_init();
_easyHandles = [[NSMutableArray alloc] init];
#if HAVE_DISPATCH_QUEUE_CREATE_WITH_TARGET
_queue = dispatch_queue_create_with_target("GSMultiHandle.isolation",
DISPATCH_QUEUE_SERIAL, aQueue);
#else
_queue = dispatch_queue_create("GSMultiHandle.isolation",
DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(_queue, aQueue);
#endif
[self setupCallbacks];
[self configureWithConfiguration: conf];
}
return self;
}
- (void) dealloc
{
NSEnumerator *e;
GSEasyHandle *handle;
DESTROY(_timeoutSource);
e = [_easyHandles objectEnumerator];
while (nil != (handle = [e nextObject]))
{
curl_multi_remove_handle([handle rawHandle], _rawHandle);
}
DESTROY(_easyHandles);
curl_multi_cleanup(_rawHandle);
[super dealloc];
}
- (void) configureWithConfiguration: (NSURLSessionConfiguration*)configuration
{
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_MAX_HOST_CONNECTIONS, [configuration HTTPMaximumConnectionsPerHost]));
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_PIPELINING, [configuration HTTPShouldUsePipelining] ? CURLPIPE_MULTIPLEX : CURLPIPE_NOTHING));
}
- (void)setupCallbacks
{
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_SOCKETDATA, (void*)self));
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_SOCKETFUNCTION, curl_socket_function));
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_TIMERDATA, (__bridge void *)self));
handleEasyCode(curl_multi_setopt(_rawHandle, CURLMOPT_TIMERFUNCTION, curl_timer_function));
}
- (void) addHandle: (GSEasyHandle*)easyHandle
{
// If this is the first handle being added, we need to `kick` the
// underlying multi handle by calling `timeoutTimerFired` as
// described in
// <https://curl.haxx.se/libcurl/c/curl_multi_socket_action.html>.
// That will initiate the registration for timeout timer and socket
// readiness.
BOOL needsTimeout = false;
if ([_easyHandles count] == 0)
{
needsTimeout = YES;
}
[_easyHandles addObject: easyHandle];
handleMultiCode(curl_multi_add_handle(_rawHandle, [easyHandle rawHandle]));
if (needsTimeout)
{
[self timeoutTimerFired];
}
}
- (void) removeHandle: (GSEasyHandle*)easyHandle
{
NSEnumerator *e;
int idx = 0;
BOOL found = NO;
GSEasyHandle *h;
e = [_easyHandles objectEnumerator];
while (nil != (h = [e nextObject]))
{
if ([h rawHandle] == [easyHandle rawHandle])
{
found = YES;
break;
}
idx++;
}
NSAssert(found, @"Handle not in list.");
handleMultiCode(curl_multi_remove_handle(_rawHandle, [easyHandle rawHandle]));
[_easyHandles removeObjectAtIndex: idx];
}
- (void) updateTimeoutTimerToValue: (NSInteger)value
{
// A timeout_ms value of -1 passed to this callback means you should delete
// the timer. All other values are valid expire times in number
// of milliseconds.
if (-1 == value)
{
DESTROY(_timeoutSource);
}
else if (0 == value)
{
DESTROY(_timeoutSource);
dispatch_async(_queue,
^{
[self timeoutTimerFired];
});
}
else
{
if (nil == _timeoutSource || value != [_timeoutSource milliseconds])
{
DESTROY(_timeoutSource);
_timeoutSource = [[GSTimeoutSource alloc] initWithQueue: _queue
milliseconds: value
handler: ^{
[self timeoutTimerFired];
}];
}
}
}
- (void) performActionForSocket: (int)socket
{
[self readAndWriteAvailableDataOnSocket: socket];
}
- (void)timeoutTimerFired
{
[self readAndWriteAvailableDataOnSocket: CURL_SOCKET_TIMEOUT];
}
- (void) readAndWriteAvailableDataOnSocket: (int)socket
{
int runningHandlesCount = 0;
handleMultiCode(curl_multi_socket_action(_rawHandle, socket, 0, &runningHandlesCount));
[self readMessages];
}
/// Check the status of all individual transfers.
///
/// libcurl refers to this as read multi stack informationals.
/// Check for transfers that completed.
- (void) readMessages
{
while (true)
{
int count = 0;
CURLMsg *msg;
CURL *easyHandle;
int code;
msg = curl_multi_info_read(_rawHandle, &count);
if (NULL == msg || CURLMSG_DONE != msg->msg || !msg->easy_handle) break;
easyHandle = msg->easy_handle;
code = msg->data.result;
[self completedTransferForEasyHandle: easyHandle easyCode: code];
}
}
- (void) completedTransferForEasyHandle: (CURL*)rawEasyHandle
easyCode: (int)easyCode
{
NSEnumerator *e;
GSEasyHandle *h;
GSEasyHandle *handle = nil;
NSError *err = nil;
int errCode;
e = [_easyHandles objectEnumerator];
while (nil != (h = [e nextObject]))
{
if ([h rawHandle] == rawEasyHandle)
{
handle = h;
break;
}
}
NSAssert(nil != handle, @"Transfer completed for easy handle"
@", but it is not in the list of added handles.");
errCode = [handle urlErrorCodeWithEasyCode: easyCode];
if (0 != errCode)
{
NSString *d = nil;
if ([handle errorBuffer][0] == 0)
{
const char *description = curl_easy_strerror(errCode);
d = [[NSString alloc] initWithCString: description
encoding: NSUTF8StringEncoding];
}
else
{
d = [[NSString alloc] initWithCString: [handle errorBuffer]
encoding: NSUTF8StringEncoding];
}
err = [NSError errorWithDomain: NSURLErrorDomain
code: errCode
userInfo: @{NSLocalizedDescriptionKey : d}];
RELEASE(d);
}
[handle transferCompletedWithError: err];
}
- (int32_t) registerWithSocket: (curl_socket_t)socket
what: (int)what
socketSourcePtr: (void *)socketSourcePtr
{
// We get this callback whenever we need to register or unregister a
// given socket with libdispatch.
// The `action` / `what` defines if we should register or unregister
// that we're interested in read and/or write readiness. We will do so
// through libdispatch (DispatchSource) and store the source(s) inside
// a `SocketSources` which we in turn store inside libcurl's multi handle
// by means of curl_multi_assign() -- we retain the object first.
GSSocketRegisterAction *action;
GSSocketSources *socketSources;
action = [[GSSocketRegisterAction alloc] initWithRawValue: what];
socketSources = [GSSocketSources from: socketSourcePtr];
if (nil == socketSources && [action needsSource])
{
GSSocketSources *s;
s = [[GSSocketSources alloc] init];
curl_multi_assign(_rawHandle, socket, (void*)s);
socketSources = s;
}
else if (nil != socketSources
&& GSSocketRegisterActionTypeUnregister == [action type])
{
DESTROY(socketSources);
}
if (nil != socketSources)
{
[socketSources createSourcesWithAction: action
socket: socket
queue: _queue
handler: ^{
[self performActionForSocket: socket];
}];
}
RELEASE(action);
return 0;
}
@end
@implementation GSSocketRegisterAction
- (instancetype) initWithRawValue: (int)rawValue
{
if (nil != (self = [super init]))
{
switch (rawValue) {
case CURL_POLL_NONE:
_type = GSSocketRegisterActionTypeNone;
break;
case CURL_POLL_IN:
_type = GSSocketRegisterActionTypeRegisterRead;
break;
case CURL_POLL_OUT:
_type = GSSocketRegisterActionTypeRegisterWrite;
break;
case CURL_POLL_INOUT:
_type = GSSocketRegisterActionTypeRegisterReadAndWrite;
break;
case CURL_POLL_REMOVE:
_type = GSSocketRegisterActionTypeUnregister;
break;
default:
NSAssert(NO, @"Invalid CURL_POLL value");
}
}
return self;
}
- (GSSocketRegisterActionType) type
{
return _type;
}
- (BOOL) needsReadSource
{
switch (self.type)
{
case GSSocketRegisterActionTypeNone:
return false;
case GSSocketRegisterActionTypeRegisterRead:
return true;
case GSSocketRegisterActionTypeRegisterWrite:
return false;
case GSSocketRegisterActionTypeRegisterReadAndWrite:
return true;
case GSSocketRegisterActionTypeUnregister:
return false;
}
}
- (BOOL) needsWriteSource
{
switch (self.type)
{
case GSSocketRegisterActionTypeNone:
return false;
case GSSocketRegisterActionTypeRegisterRead:
return false;
case GSSocketRegisterActionTypeRegisterWrite:
return true;
case GSSocketRegisterActionTypeRegisterReadAndWrite:
return true;
case GSSocketRegisterActionTypeUnregister:
return false;
}
}
- (BOOL)needsSource
{
return [self needsReadSource] || [self needsWriteSource];
}
@end
@implementation GSSocketSources
- (void) dealloc
{
if (_readSource)
{
dispatch_source_cancel(_readSource);
}
_readSource = NULL;
if (_writeSource)
{
dispatch_source_cancel(_writeSource);
}
_writeSource = NULL;
[super dealloc];
}
- (void) createSourcesWithAction: (GSSocketRegisterAction*)action
socket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
if ([action needsReadSource])
{
[self createReadSourceWithSocket: socket queue: queue handler: handler];
}
if ([action needsWriteSource])
{
[self createWriteSourceWithSocket: socket queue: queue handler: handler];
}
}
- (void) createReadSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
dispatch_source_t s;
if (_readSource)
{
return;
}
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket, 0, queue);
dispatch_source_set_event_handler(s, handler);
_readSource = s;
dispatch_resume(s);
}
- (void) createWriteSourceWithSocket: (curl_socket_t)socket
queue: (dispatch_queue_t)queue
handler: (dispatch_block_t)handler
{
dispatch_source_t s;
if (_writeSource)
{
return;
}
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket, 0, queue);
dispatch_source_set_event_handler(s, handler);
_writeSource = s;
dispatch_resume(s);
}
+ (instancetype) from: (void*)socketSourcePtr
{
if (!socketSourcePtr)
{
return nil;
}
else
{
return (GSSocketSources*)socketSourcePtr;
}
}
@end

91
Source/GSNativeProtocol.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef INCLUDED_GSNATIVEPROTOCOL_H
#define INCLUDED_GSNATIVEPROTOCOL_H
#import "GSEasyHandle.h"
#import "Foundation/NSURLProtocol.h"
@class GSTransferState;
typedef NS_ENUM(NSUInteger, GSCompletionActionType) {
GSCompletionActionTypeCompleteTask,
GSCompletionActionTypeFailWithError,
GSCompletionActionTypeRedirectWithRequest,
};
// Action to be taken after a transfer completes
@interface GSCompletionAction : NSObject
{
GSCompletionActionType _type;
int _errorCode;
NSURLRequest *_redirectRequest;
}
- (GSCompletionActionType) type;
- (void) setType: (GSCompletionActionType) type;
- (int) errorCode;
- (void) setErrorCode: (int)code;
- (NSURLRequest*) redirectRequest;
- (void) setRedirectRequest: (NSURLRequest*)request;
@end
typedef NS_ENUM(NSUInteger, GSNativeProtocolInternalState) {
// Task has been created, but nothing has been done, yet
GSNativeProtocolInternalStateInitial,
// The task is being fulfilled from the cache rather than the network.
GSNativeProtocolInternalStateFulfillingFromCache,
// The easy handle has been fully configured. But it is not added to
// the multi handle.
GSNativeProtocolInternalStateTransferReady,
// The easy handle is currently added to the multi handle
GSNativeProtocolInternalStateTransferInProgress,
// The transfer completed.
// The easy handle has been removed from the multi handle. This does
// not necessarily mean the task completed. A task that gets
// redirected will do multiple transfers.
GSNativeProtocolInternalStateTransferCompleted,
// The transfer failed.
// Same as `GSNativeProtocolInternalStateTransferCompleted`,
// but without response / body data
GSNativeProtocolInternalStateTransferFailed,
// Waiting for the completion handler of the HTTP redirect callback.
// When we tell the delegate that we're about to perform an HTTP
// redirect, we need to wait for the delegate to let us know what
// action to take.
GSNativeProtocolInternalStateWaitingForRedirectCompletionHandler,
// Waiting for the completion handler of the 'did receive response' callback.
// When we tell the delegate that we received a response (i.e. when
// we received a complete header), we need to wait for the delegate to
// let us know what action to take. In this state the easy handle is
// paused in order to suspend delegate callbacks.
GSNativeProtocolInternalStateWaitingForResponseCompletionHandler,
// The task is completed
// Contrast this with `GSNativeProtocolInternalStateTransferCompleted`.
GSNativeProtocolInternalStateTaskCompleted,
};
// This abstract class has the common implementation of Native protocols like
// HTTP, FTP, etc.
// These are libcurl helpers for the URLSession API code.
@interface GSNativeProtocol : NSURLProtocol <GSEasyHandleDelegate>
{
GSEasyHandle *_easyHandle;
GSNativeProtocolInternalState _internalState;
GSTransferState *_transferState;
}
- (void) setInternalState: (GSNativeProtocolInternalState)newState;
- (void) failWithError: (NSError*)error request: (NSURLRequest*)request;
- (void) completeTaskWithError: (NSError*)error;
- (void) completeTask;
- (void) startNewTransferWithRequest: (NSURLRequest*)request;
@end
#endif

768
Source/GSNativeProtocol.m Normal file
View file

@ -0,0 +1,768 @@
#import "GSNativeProtocol.h"
#import "GSTransferState.h"
#import "GSURLSessionTaskBody.h"
#import "Foundation/NSData.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSError.h"
#import "Foundation/NSException.h"
#import "Foundation/NSOperation.h"
#import "Foundation/NSURL.h"
#import "Foundation/NSURLError.h"
#import "Foundation/NSURLSession.h"
static BOOL isEasyHandlePaused(GSNativeProtocolInternalState state)
{
switch (state)
{
case GSNativeProtocolInternalStateInitial:
return NO;
case GSNativeProtocolInternalStateFulfillingFromCache:
return NO;
case GSNativeProtocolInternalStateTransferReady:
return NO;
case GSNativeProtocolInternalStateTransferInProgress:
return NO;
case GSNativeProtocolInternalStateTransferCompleted:
return NO;
case GSNativeProtocolInternalStateTransferFailed:
return NO;
case GSNativeProtocolInternalStateWaitingForRedirectCompletionHandler:
return NO;
case GSNativeProtocolInternalStateWaitingForResponseCompletionHandler:
return YES;
case GSNativeProtocolInternalStateTaskCompleted:
return NO;
}
}
static BOOL isEasyHandleAddedToMultiHandle(GSNativeProtocolInternalState state)
{
switch (state)
{
case GSNativeProtocolInternalStateInitial:
return NO;
case GSNativeProtocolInternalStateFulfillingFromCache:
return NO;
case GSNativeProtocolInternalStateTransferReady:
return NO;
case GSNativeProtocolInternalStateTransferInProgress:
return YES;
case GSNativeProtocolInternalStateTransferCompleted:
return NO;
case GSNativeProtocolInternalStateTransferFailed:
return NO;
case GSNativeProtocolInternalStateWaitingForRedirectCompletionHandler:
return NO;
case GSNativeProtocolInternalStateWaitingForResponseCompletionHandler:
return YES;
case GSNativeProtocolInternalStateTaskCompleted:
return NO;
}
}
@interface NSURLSession (Internal)
- (void) removeHandle: (GSEasyHandle*)handle;
- (void) addHandle: (GSEasyHandle*)handle;
@end
@implementation NSURLSession (Internal)
- (void) removeHandle: (GSEasyHandle*)handle
{
[_multiHandle removeHandle: handle];
}
- (void) addHandle: (GSEasyHandle*)handle
{
[_multiHandle addHandle: handle];
}
@end
@interface NSURLSessionTask (Internal)
- (void) setCurrentRequest: (NSURLRequest*)request;
- (dispatch_queue_t) workQueue;
- (NSUInteger) suspendCount;
- (void) getBodyWithCompletion: (void (^)(GSURLSessionTaskBody *body))completion;
- (void) setKnownBody: (GSURLSessionTaskBody*)body;
- (void) setError: (NSError*)error;
@end
@implementation NSURLSessionTask (Internal)
- (void) setCurrentRequest: (NSURLRequest*)request
{
ASSIGN(_currentRequest, request);
}
- (dispatch_queue_t) workQueue
{
return _workQueue;
}
- (NSUInteger) suspendCount
{
return _suspendCount;
}
- (void) getBodyWithCompletion: (void (^)(GSURLSessionTaskBody *body))completion
{
if (nil != _knownBody)
{
completion(_knownBody);
return;
};
GSURLSessionTaskBody *body = AUTORELEASE([[GSURLSessionTaskBody alloc] init]);
completion(body);
}
- (void) setKnownBody: (GSURLSessionTaskBody*)body
{
ASSIGN(_knownBody, body);
}
- (void) setError: (NSError*)error
{
ASSIGN(_error, error);
}
@end
@implementation GSCompletionAction
- (void) dealloc
{
DESTROY(_redirectRequest);
[super dealloc];
}
- (GSCompletionActionType) type
{
return _type;
}
- (void) setType: (GSCompletionActionType) type
{
_type = type;
}
- (int) errorCode
{
return _errorCode;
}
- (void) setErrorCode: (int)code
{
_errorCode = code;
}
- (NSURLRequest*) redirectRequest
{
return _redirectRequest;
}
- (void) setRedirectRequest: (NSURLRequest*)request
{
ASSIGN(_redirectRequest, request);
}
@end
@implementation GSNativeProtocol
- (instancetype) initWithTask: (NSURLSessionTask*)_task
cachedResponse: (NSCachedURLResponse*)_cachedResponse
client: (id<NSURLProtocolClient>)_client
{
if (nil != (self = [super initWithTask: _task
cachedResponse: _cachedResponse
client: _client]))
{
_internalState = GSNativeProtocolInternalStateInitial;
_easyHandle = [[GSEasyHandle alloc] initWithDelegate: self];
}
return self;
}
- (void) dealloc
{
DESTROY(_easyHandle);
DESTROY(_transferState);
[super dealloc];
}
+ (NSURLRequest*) canonicalRequestForRequest: (NSURLRequest*)request
{
return request;
}
- (void) startLoading
{
[self resume];
}
- (void) stopLoading
{
NSURLSessionTask *task;
if (nil != (task = [self task])
&& NSURLSessionTaskStateSuspended == [task state])
{
[self suspend];
}
else
{
[self setInternalState: GSNativeProtocolInternalStateTransferFailed];
NSAssert(nil != [task error], @"Missing error for failed task");
[self completeTaskWithError: [task error]];
}
}
- (void) setInternalState: (GSNativeProtocolInternalState)newState
{
NSURLSessionTask *task;
GSNativeProtocolInternalState oldState;
if (!isEasyHandlePaused(_internalState) && isEasyHandlePaused(newState))
{
NSAssert(NO, @"Need to solve pausing receive.");
}
if (isEasyHandleAddedToMultiHandle(_internalState)
&& !isEasyHandleAddedToMultiHandle(newState))
{
if (nil != (task = [self task]))
{
[[task session] removeHandle: _easyHandle];
}
}
oldState = _internalState;
_internalState = newState;
if (!isEasyHandleAddedToMultiHandle(oldState)
&& isEasyHandleAddedToMultiHandle(_internalState))
{
if (nil != (task = [self task]))
{
[[task session] addHandle: _easyHandle];
}
}
if (isEasyHandlePaused(oldState) && !isEasyHandlePaused(_internalState))
{
NSAssert(NO, @"Need to solve pausing receive.");
}
}
- (void) startNewTransferWithRequest: (NSURLRequest*)request
{
NSURLSessionTask *task = [self task];
[task setCurrentRequest: request];
NSAssert(nil != [request URL], @"No URL in request.");
[task getBodyWithCompletion: ^(GSURLSessionTaskBody *body)
{
[task setKnownBody: body];
[self setInternalState: GSNativeProtocolInternalStateTransferReady];
ASSIGN(_transferState,
[self createTransferStateWithURL: [request URL]
body: body
workQueue: [task workQueue]]);
[self configureEasyHandleForRequest: request body: body];
if ([task suspendCount] < 1)
{
[self resume];
}
}];
}
- (void) configureEasyHandleForRequest: (NSURLRequest*)request
body: (GSURLSessionTaskBody*)body
{
NSAssert(NO, @"Requires concrete implementation");
}
- (GSTransferState*) createTransferStateWithURL: (NSURL*)url
body: (GSURLSessionTaskBody*)body
workQueue: (dispatch_queue_t)workQueue
{
GSDataDrain *drain = [self createTransferBodyDataDrain];
switch ([body type])
{
case GSURLSessionTaskBodyTypeNone:
return AUTORELEASE([[GSTransferState alloc] initWithURL: url
bodyDataDrain: drain]);
case GSURLSessionTaskBodyTypeData:
{
GSBodyDataSource *source;
source = AUTORELEASE([[GSBodyDataSource alloc] initWithData: [body data]]);
return AUTORELEASE([[GSTransferState alloc] initWithURL: url
bodyDataDrain: drain
bodySource: source]);
}
case GSURLSessionTaskBodyTypeFile:
{
GSBodyFileSource *source;
source = AUTORELEASE([[GSBodyFileSource alloc] initWithFileURL: [body fileURL]
workQueue: workQueue
dataAvailableHandler: ^{
[_easyHandle unpauseSend];
}]);
return AUTORELEASE([[GSTransferState alloc] initWithURL: url
bodyDataDrain: drain
bodySource: source]);
}
case GSURLSessionTaskBodyTypeStream:
{
GSBodyStreamSource *source;
source = AUTORELEASE([[GSBodyStreamSource alloc] initWithInputStream: [body inputStream]]);
return AUTORELEASE([[GSTransferState alloc] initWithURL: url
bodyDataDrain: drain
bodySource: source]);
}
}
}
// The data drain.
// This depends on what the delegate need.
- (GSDataDrain*) createTransferBodyDataDrain
{
NSURLSession *s = [[self task] session];
GSDataDrain *dd = AUTORELEASE([[GSDataDrain alloc] init]);
if (nil != [s delegate])
{
// Data will be forwarded to the delegate as we receive it, we don't
// need to do anything about it.
[dd setType: GSDataDrainTypeIgnore];
return dd;
}
else
{
[dd setType: GSDataDrainTypeIgnore];
return dd;
}
}
- (void) resume
{
NSURLSessionTask *task;
task = [self task];
if (_internalState == GSNativeProtocolInternalStateInitial)
{
NSAssert(nil != [task originalRequest], @"Task has no original request.");
// Check if the cached response is good to use:
if (nil != [self cachedResponse]
&& [self canRespondFromCacheUsing: [self cachedResponse]])
{
[self setInternalState:
GSNativeProtocolInternalStateFulfillingFromCache];
dispatch_async([task workQueue],
^{
id<NSURLProtocolClient> client;
client = [self client];
[client URLProtocol: self
cachedResponseIsValid: [self cachedResponse]];
[client URLProtocol: self
didReceiveResponse: [[self cachedResponse] response]
cacheStoragePolicy: NSURLCacheStorageNotAllowed];
if ([[[self cachedResponse] data] length] > 0)
{
if ([client respondsToSelector:
@selector(URLProtocol:didLoad:)])
{
[client URLProtocol: self
didLoadData: [[self cachedResponse] data]];
}
}
if ([client respondsToSelector:
@selector(URLProtocolDidFinishLoading:)])
{
[client URLProtocolDidFinishLoading: self];
}
[self setInternalState:
GSNativeProtocolInternalStateTaskCompleted];
});
}
else
{
[self startNewTransferWithRequest: [task originalRequest]];
}
}
if (_internalState == GSNativeProtocolInternalStateTransferReady
&& nil != _transferState)
{
[self setInternalState: GSNativeProtocolInternalStateTransferInProgress];
}
}
- (BOOL) canRespondFromCacheUsing: (NSCachedURLResponse*)response
{
// Allows a native protocol to process a cached response.
// If `YES` is returned, the protocol will replay the cached response
// instead of starting a new transfer. The default implementation invalidates
// the response in the cache and returns `NO`.
NSURLCache *cache;
NSURLSessionTask *task;
task = [self task];
cache = [[[task session] configuration] URLCache];
if (nil != cache && [task isKindOfClass: [NSURLSessionDataTask class]])
{
[cache removeCachedResponseForDataTask: (NSURLSessionDataTask*)task];
}
return NO;
}
- (void) suspend
{
if (_internalState == GSNativeProtocolInternalStateTransferInProgress)
{
[self setInternalState: GSNativeProtocolInternalStateTransferReady];
}
}
- (void) completeTaskWithError: (NSError*)error
{
[[self task] setError: error];
NSAssert(_internalState == GSNativeProtocolInternalStateTransferFailed,
@"Trying to complete the task, but its transfer isn't complete / failed.");
// We don't want a timeout to be triggered after this.
// The timeout timer needs to be cancelled.
[_easyHandle setTimeoutTimer: nil];
[self setInternalState: GSNativeProtocolInternalStateTaskCompleted];
}
- (GSEasyHandleAction) didReceiveData: (NSData*)data
{
NSURLResponse *response;
NSAssert(GSNativeProtocolInternalStateTransferInProgress == _internalState,
@"Received body data, but no transfer in progress.");
response = [self validateHeaderCompleteTransferState: _transferState];
if (nil != response)
{
[_transferState setResponse: response];
}
[self notifyDelegateAboutReceivedData: data];
_internalState = GSNativeProtocolInternalStateTransferInProgress;
ASSIGN(_transferState, [_transferState byAppendingBodyData: data]);
return GSEasyHandleActionProceed;
}
- (NSURLResponse*) validateHeaderCompleteTransferState: (GSTransferState*)ts
{
if (![ts isHeaderComplete])
{
NSAssert(NO, @"Received body data, but the header is not complete, yet.");
}
return nil;
}
- (void) notifyDelegateAboutReceivedData: (NSData*)data
{
NSURLSessionTask *task;
id<NSURLSessionDelegate> delegate;
task = [self task];
NSAssert(nil != task, @"Cannot notify");
delegate = [[task session] delegate];
if (nil != delegate
&& [task isKindOfClass: [NSURLSessionDataTask class]]
&& [delegate respondsToSelector: @selector(URLSession:dataTask:didReceiveData:)])
{
id<NSURLSessionDataDelegate> dataDelegate;
NSURLSessionDataTask *dataTask;
NSURLSession *session;
session = [task session];
NSAssert(nil != session, @"Missing session");
dataDelegate = (id<NSURLSessionDataDelegate>)delegate;
dataTask = (NSURLSessionDataTask*)task;
[[session delegateQueue] addOperationWithBlock:
^{
[dataDelegate URLSession: session
dataTask: dataTask
didReceiveData: data];
}];
}
}
- (void) notifyDelegateAboutUploadedDataCount: (int64_t)count
{
NSURLSessionTask *task;
id<NSURLSessionDelegate> delegate;
task = [self task];
NSAssert(nil != task, @"Cannot notify");
delegate = [[task session] delegate];
if (nil != delegate
&& [task isKindOfClass: [NSURLSessionUploadTask class]]
&& [delegate respondsToSelector: @selector(URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)])
{
id<NSURLSessionTaskDelegate> taskDelegate;
NSURLSession *session;
session = [task session];
NSAssert(nil != session, @"Missing session");
taskDelegate = (id<NSURLSessionTaskDelegate>)delegate;
[[session delegateQueue] addOperationWithBlock:
^{
[taskDelegate URLSession: session
task: task
didSendBodyData: count
totalBytesSent: [task countOfBytesSent]
totalBytesExpectedToSend: [task countOfBytesExpectedToSend]];
}];
}
}
- (GSEasyHandleAction) didReceiveHeaderData: (NSData*)data
contentLength: (int64_t)contentLength
{
NSAssert(NO, @"Require concrete implementation");
return GSEasyHandleActionAbort;
}
- (void) fillWriteBufferLength: (NSInteger)length
result: (void (^)(GSEasyHandleWriteBufferResult result, NSInteger length, NSData *data))result
{
id<GSURLSessionTaskBodySource> source;
NSAssert(GSNativeProtocolInternalStateTransferInProgress == _internalState,
@"Requested to fill write buffer, but transfer isn't in progress.");
source = [_transferState requestBodySource];
NSAssert(nil != source,
@"Requested to fill write buffer, but transfer state has no body source.");
if (nil == result)
{
return;
}
[source getNextChunkWithLength: length
completionHandler: ^(GSBodySourceDataChunk chunk, NSData *_Nullable data)
{
switch (chunk)
{
case GSBodySourceDataChunkData:
{
NSUInteger count = [data length];
[self notifyDelegateAboutUploadedDataCount: (int64_t)count];
result(GSEasyHandleWriteBufferResultBytes, count, data);
break;
}
case GSBodySourceDataChunkDone:
result(GSEasyHandleWriteBufferResultBytes, 0, nil);
break;
case GSBodySourceDataChunkRetryLater:
// At this point we'll try to pause the easy handle. The body
// source is responsible for un-pausing the handle once data
// becomes available.
result(GSEasyHandleWriteBufferResultPause, -1, nil);
break;
case GSBodySourceDataChunkError:
result(GSEasyHandleWriteBufferResultAbort, -1, nil);
break;
}
}];
}
- (void) transferCompletedWithError: (NSError*)error
{
/* At this point the transfer is complete and we can decide what to do.
* If everything went well, we will simply forward the resulting data
* to the delegate. But in case of redirects etc. we might send another
* request.
*/
NSURLRequest *request;
NSURLResponse *response;
GSCompletionAction *action;
if (nil != error)
{
[self setInternalState: GSNativeProtocolInternalStateTransferFailed];
[self failWithError: error request: [self request]];
return;
}
NSAssert(_internalState == GSNativeProtocolInternalStateTransferInProgress,
@"Transfer completed, but it wasn't in progress.");
request = [[self task] currentRequest];
NSAssert(nil != request,
@"Transfer completed, but there's no current request.");
if (nil != [[self task] response])
{
[_transferState setResponse: [[self task] response]];
}
response = [_transferState response];
NSAssert(nil != response, @"Transfer completed, but there's no response.");
[self setInternalState: GSNativeProtocolInternalStateTransferCompleted];
action = [self completeActionForCompletedRequest: request response: response];
switch ([action type])
{
case GSCompletionActionTypeCompleteTask:
[self completeTask];
break;
case GSCompletionActionTypeFailWithError:
[self setInternalState: GSNativeProtocolInternalStateTransferFailed];
error = [NSError errorWithDomain: NSURLErrorDomain
code: [action errorCode]
userInfo: nil];
[self failWithError: error request: request];
break;
case GSCompletionActionTypeRedirectWithRequest:
[self redirectForRequest: [action redirectRequest]];
break;
}
}
- (GSCompletionAction*) completeActionForCompletedRequest: (NSURLRequest*)request
response: (NSURLResponse*)response
{
GSCompletionAction *action;
action = AUTORELEASE([[GSCompletionAction alloc] init]);
[action setType: GSCompletionActionTypeCompleteTask];
return action;
}
- (void) completeTask
{
NSURLSessionTask *task;
GSDataDrain *bodyDataDrain;
id<NSURLProtocolClient> client;
NSAssert(_internalState == GSNativeProtocolInternalStateTransferCompleted,
@"Trying to complete the task, but its transfer isn't complete.");
task = [self task];
[task setResponse: [_transferState response]];
client = [self client];
// We don't want a timeout to be triggered after this. The timeout timer
// needs to be cancelled.
[_easyHandle setTimeoutTimer: nil];
// because we deregister the task with the session on internalState being set
// to taskCompleted we need to do the latter after the delegate/handler was
// notified/invoked
bodyDataDrain = [_transferState bodyDataDrain];
if (GSDataDrainInMemory == [bodyDataDrain type])
{
NSData *data;
if (nil != [bodyDataDrain data])
{
data = [NSData dataWithData: [bodyDataDrain data]];
}
else
{
data = [NSData data];
}
if ([client respondsToSelector: @selector(URLProtocol:didLoadData:)])
{
[client URLProtocol: self didLoadData: data];
}
[self setInternalState: GSNativeProtocolInternalStateTaskCompleted];
}
if ([client respondsToSelector: @selector(URLProtocolDidFinishLoading:)])
{
[client URLProtocolDidFinishLoading: self];
}
}
- (void) redirectForRequest: (NSURLRequest*)request
{
NSAssert(NO, @"Require concrete implementation");
}
- (void) failWithError: (NSError*)error request: (NSURLRequest*)request
{
NSDictionary *info;
NSError *urlError;
id<NSURLProtocolClient> client;
info = [NSDictionary dictionaryWithObjectsAndKeys:
error, NSUnderlyingErrorKey,
[request URL], NSURLErrorFailingURLErrorKey,
[[request URL] absoluteString], NSURLErrorFailingURLStringErrorKey,
[error localizedDescription], NSLocalizedDescriptionKey, nil];
urlError = [NSError errorWithDomain: NSURLErrorDomain
code: [error code]
userInfo: info];
[self completeTaskWithError: urlError];
client = [self client];
if ([client respondsToSelector: @selector(URLProtocol:didFailWithError:)])
{
[client URLProtocol: self didFailWithError: urlError];
}
}
- (BOOL) seekInputStreamToPosition: (uint64_t)position
{
//TODO implement seek for NSURLSessionUploadTask
return NO;
}
- (void) updateProgressMeterWithTotalBytesSent: (int64_t)totalBytesSent
totalBytesExpectedToSend: (int64_t)totalBytesExpectedToSend
totalBytesReceived: (int64_t)totalBytesReceived
totalBytesExpectedToReceive: (int64_t)totalBytesExpectedToReceive
{
// TODO: Update progress. Note that a single NSURLSessionTask might
// perform multiple transfers. The values in `progress` are only for
// the current transfer.
}
@end

View file

@ -2180,11 +2180,6 @@ retrieve_callback(gnutls_session_t session,
[str appendFormat: _(@"- Certificate Type: %s\n"), tmp];
}
/* print the compression algorithm (if any)
*/
tmp = gnutls_compression_get_name(gnutls_compression_get(session));
[str appendFormat: _(@"- Compression: %s\n"), tmp];
/* print the name of the cipher used.
* eg 3DES.
*/

30
Source/GSTaskRegistry.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef INCLUDED_GSTASKREGISTRY_H
#define INCLUDED_GSTASKREGISTRY_H
#import "common.h"
@class NSArray;
@class NSURLSessionTask;
/*
* This helper class keeps track of all tasks.
*
* Each `NSURLSession` has a `GSTaskRegistry` for its running tasks.
*
* - Note: This must **only** be accessed on the owning session's work queue.
*/
@interface GSTaskRegistry : NSObject
- (void ) addTask: (NSURLSessionTask*)task;
- (void) removeTask: (NSURLSessionTask*)task;
- (void) notifyOnTasksCompletion: (void (^)(void))tasksCompletion;
- (NSArray*) allTasks;
- (BOOL) isEmpty;
@end
#endif

98
Source/GSTaskRegistry.m Normal file
View file

@ -0,0 +1,98 @@
#import "GSTaskRegistry.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSException.h"
#import "Foundation/NSURLSession.h"
@implementation GSTaskRegistry
{
NSMutableDictionary *_tasks;
void (^_tasksCompletion)(void);
}
- (instancetype) init
{
if (nil != (self = [super init]))
{
_tasks = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) dealloc
{
DESTROY(_tasks);
[super dealloc];
}
- (NSArray*) allTasks
{
return [_tasks allValues];
}
- (BOOL) isEmpty
{
return [_tasks count] == 0;
}
- (void) notifyOnTasksCompletion: (void (^)(void))tasksCompletion
{
_tasksCompletion = tasksCompletion;
}
- (void) addTask: (NSURLSessionTask*)task
{
NSString *identifier;
NSUInteger taskIdentifier;
NSURLSessionTask *t;
taskIdentifier = [task taskIdentifier];
NSAssert(taskIdentifier != 0, @"Invalid task identifier");
identifier = [NSString stringWithFormat: @"%lu", taskIdentifier];
if (nil != (t = [_tasks objectForKey: identifier]))
{
if ([t isEqual: task])
{
NSAssert(NO,
@"Trying to re-insert a task that's already in the registry.");
}
else
{
NSAssert(NO,
@"Trying to insert a task, but a different task with the same"
@" identifier is already in the registry.");
}
}
[_tasks setObject: task forKey: identifier];
}
- (void) removeTask: (NSURLSessionTask*)task
{
NSString *identifier;
NSUInteger taskIdentifier;
taskIdentifier = [task taskIdentifier];
NSAssert(taskIdentifier != 0, @"Invalid task identifier");
identifier = [NSString stringWithFormat: @"%lu", taskIdentifier];
if (nil == [_tasks objectForKey: identifier])
{
NSAssert(NO, @"Trying to remove task, but it's not in the registry.");
}
[_tasks removeObjectForKey: identifier];
if (nil != _tasksCompletion && [self isEmpty])
{
_tasksCompletion();
}
}
@end

32
Source/GSTimeoutSource.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef INCLUDED_GSTIMEOUTSOURCE_H
#define INCLUDED_GSTIMEOUTSOURCE_H
#import "common.h"
#import "GSDispatch.h"
/*
* A helper class that wraps a libdispatch timer.
*
* Used to implement the timeout of `GSMultiHandle` and `GSEasyHandle`
*/
@interface GSTimeoutSource : NSObject
{
dispatch_source_t _rawSource;
NSInteger _milliseconds;
dispatch_queue_t _queue;
dispatch_block_t _handler;
}
- (NSInteger) milliseconds;
- (dispatch_queue_t) queue;
- (dispatch_block_t) handler;
- (instancetype) initWithQueue: (dispatch_queue_t)queue
milliseconds: (NSInteger)milliseconds
handler: (dispatch_block_t)handler;
@end
#endif

48
Source/GSTimeoutSource.m Normal file
View file

@ -0,0 +1,48 @@
#import "GSTimeoutSource.h"
@implementation GSTimeoutSource
- (instancetype) initWithQueue: (dispatch_queue_t)queue
milliseconds: (NSInteger)milliseconds
handler: (dispatch_block_t)handler
{
if (nil != (self = [super init]))
{
_queue = queue;
_handler = handler;
_milliseconds = milliseconds;
_rawSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
uint64_t delay = MAX(1, milliseconds - 1);
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC);
dispatch_source_set_timer(_rawSource, start, delay * NSEC_PER_MSEC, _milliseconds == 1 ? 1 * NSEC_PER_USEC : 1 * NSEC_PER_MSEC);
dispatch_source_set_event_handler(_rawSource, _handler);
dispatch_resume(_rawSource);
}
return self;
}
- (void) dealloc
{
dispatch_source_cancel(_rawSource);
[super dealloc];
}
- (NSInteger) milliseconds
{
return _milliseconds;
}
- (dispatch_queue_t) queue
{
return _queue;
}
- (dispatch_block_t) handler
{
return _handler;
}
@end

142
Source/GSTransferState.h Normal file
View file

@ -0,0 +1,142 @@
#ifndef INCLUDED_GSTRANSFERTSTATE_H
#define INCLUDED_GSTRANSFERTSTATE_H
#import "GSURLSessionTaskBodySource.h"
@class GSURLSessionTaskBodySource;
@class NSArray;
@class NSData;
@class NSFileHandle;
@class NSHTTPURLResponse;
@class NSURL;
@class NSURLResponse;
typedef NS_ENUM(NSUInteger, GSParsedResponseHeaderType) {
GSParsedResponseHeaderTypePartial,
GSParsedResponseHeaderTypeComplete
};
/*
* A native protocol like HTTP header being parsed.
*
* It can either be complete (i.e. the final CR LF CR LF has been
* received), or partial.
*/
@interface GSParsedResponseHeader: NSObject
{
NSArray *_lines;
GSParsedResponseHeaderType _type;
}
- (GSParsedResponseHeaderType) type;
/*
* Parse a header line passed by libcurl.
*
* These contain the <CRLF> ending and the final line contains nothing but
* that ending.
* - Returns: Returning nil indicates failure. Otherwise returns a new
* `GSParsedResponseHeader` with the given line added.
*/
- (instancetype) byAppendingHeaderLine: (NSData*)data;
- (NSHTTPURLResponse*) createHTTPURLResponseForURL: (NSURL*)URL;
@end
typedef NS_ENUM(NSUInteger, GSDataDrainType) {
// Concatenate in-memory
GSDataDrainInMemory,
// Write to file
GSDataDrainTypeToFile,
// Do nothing. Might be forwarded to delegate
GSDataDrainTypeIgnore,
};
@interface GSDataDrain: NSObject
{
GSDataDrainType _type;
NSData *_data;
NSURL *_fileURL;
NSFileHandle *_fileHandle;
}
- (GSDataDrainType) type;
- (void) setType: (GSDataDrainType)type;
- (NSData*) data;
- (void) setData: (NSData*)data;
- (NSURL*) fileURL;
- (void) setFileURL: (NSURL*)url;
- (NSFileHandle*) fileHandle;
- (void) setFileHandle: (NSFileHandle*)handle;
@end
/*
* State related to an ongoing transfer.
*
* This contains headers received so far, body data received so far, etc.
*
* There's a strict 1-to-1 relationship between a `GSEasyHandle` and a
* `GSTransferState`.
*/
@interface GSTransferState: NSObject
{
NSURL *_url; // The URL that's being requested
GSParsedResponseHeader *_parsedResponseHeader; // Raw headers received.
NSURLResponse *_response; // Once the headers is complete, this will contain the response
id<GSURLSessionTaskBodySource> _requestBodySource; // The body data to be sent in the request
GSDataDrain *_bodyDataDrain; // Body data received
BOOL _isHeaderComplete;
}
// Transfer state that can receive body data, but will not send body data.
- (instancetype) initWithURL: (NSURL*)url
bodyDataDrain: (GSDataDrain*)bodyDataDrain;
// Transfer state that sends body data and can receive body data.
- (instancetype) initWithURL: (NSURL*)url
bodyDataDrain: (GSDataDrain*)bodyDataDrain
bodySource: (id<GSURLSessionTaskBodySource>)bodySource;
- (instancetype) initWithURL: (NSURL*)url
parsedResponseHeader: (GSParsedResponseHeader*)parsedResponseHeader
response: (NSURLResponse*)response
bodySource: (id<GSURLSessionTaskBodySource>)bodySource
bodyDataDrain: (GSDataDrain*)bodyDataDrain;
/*
* Append body data
*/
- (instancetype) byAppendingBodyData: (NSData*)bodyData;
/*
* Appends a header line
*
* Will set the complete response once the header is complete, i.e. the
* return value's `isHeaderComplete` will then by `YES`.
*
* When a parsing error occurs `error` will be set.
*/
- (instancetype) byAppendingHTTPHeaderLineData: (NSData*)data
error: (NSError**)error;
- (NSURLResponse*) response;
- (void) setResponse: (NSURLResponse*)response;
- (BOOL) isHeaderComplete;
- (id<GSURLSessionTaskBodySource>) requestBodySource;
- (GSDataDrain*) bodyDataDrain;
- (NSURL*) URL;
@end
#endif

487
Source/GSTransferState.m Normal file
View file

@ -0,0 +1,487 @@
#import "GSTransferState.h"
#import "GSURLSessionTaskBodySource.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSCharacterSet.h"
#import "Foundation/NSData.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSFileHandle.h"
#import "Foundation/NSError.h"
#import "Foundation/NSURL.h"
#import "Foundation/NSURLError.h"
#import "Foundation/NSURLResponse.h"
#import "Foundation/NSURLSession.h"
#define GS_DELIMITERS_CR 0x0d
#define GS_DELIMITERS_LR 0x0a
@implementation GSParsedResponseHeader
- (instancetype) init
{
if (nil != (self = [super init]))
{
_lines = [[NSMutableArray alloc] init];
_type = GSParsedResponseHeaderTypePartial;
}
return self;
}
- (void) dealloc
{
DESTROY(_lines);
[super dealloc];
}
- (GSParsedResponseHeaderType) type
{
return _type;
}
- (void) setType: (GSParsedResponseHeaderType)type
{
_type = type;
}
- (void) setLines: (NSArray*)lines
{
ASSIGN(_lines, lines);
}
- (instancetype) byAppendingHeaderLine: (NSData*)data
{
NSUInteger length = [data length];
if (length >= 2)
{
uint8_t last2;
uint8_t last1;
[data getBytes: &last2 range: NSMakeRange(length - 2, 1)];
[data getBytes: &last1 range: NSMakeRange(length - 1, 1)];
if (GS_DELIMITERS_CR == last2 && GS_DELIMITERS_LR == last1)
{
NSData *lineBuffer;
NSString *line;
lineBuffer = [data subdataWithRange: NSMakeRange(0, length - 2)];
line = AUTORELEASE([[NSString alloc] initWithData: lineBuffer
encoding: NSUTF8StringEncoding]);
if (nil == line)
{
return nil;
}
return [self _byAppendingHeaderLine: line];
}
}
return nil;
}
- (NSHTTPURLResponse*) createHTTPURLResponseForURL: (NSURL*)URL
{
NSArray *tail;
NSArray *startLine;
NSDictionary *headerFields;
NSString *head;
NSString *s, *v;
head = [_lines firstObject];
if (nil == head)
{
return nil;
}
if ([_lines count] == 0)
{
return nil;
}
tail = [_lines subarrayWithRange: NSMakeRange(1, [_lines count] - 1)];
startLine = [self statusLineFromLine: head];
if (nil == startLine)
{
return nil;
}
headerFields = [self createHeaderFieldsFromLines: tail];
v = [startLine objectAtIndex: 0];
s = [startLine objectAtIndex: 1];
return AUTORELEASE([[NSHTTPURLResponse alloc] initWithURL: URL
statusCode: [s integerValue]
HTTPVersion: v
headerFields: headerFields]);
}
- (NSArray*) statusLineFromLine: (NSString*)line
{
NSArray *a;
NSString *s;
NSInteger status;
a = [line componentsSeparatedByString: @" "];
if ([a count] < 3)
{
return nil;
}
s = [a objectAtIndex: 1];
status = [s integerValue];
if (status >= 100 && status <= 999)
{
return a;
}
else
{
return nil;
}
}
- (NSDictionary *) createHeaderFieldsFromLines: (NSArray *)lines
{
NSMutableDictionary *headerFields = nil;
NSEnumerator *e;
NSString *line;
e = [_lines objectEnumerator];
while (nil != (line = [e nextObject]))
{
NSRange r;
NSString *head;
NSString *tail;
NSCharacterSet *set;
NSString *key;
NSString *value;
NSString *v;
r = [line rangeOfString: @":"];
if (r.location != NSNotFound)
{
head = [line substringToIndex: r.location];
tail = [line substringFromIndex: r.location + 1];
set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
key = [head stringByTrimmingCharactersInSet: set];
value = [tail stringByTrimmingCharactersInSet: set];
if (nil != key && nil != value)
{
if (nil == headerFields)
{
headerFields = [NSMutableDictionary dictionary];
}
if (nil != [headerFields objectForKey: key])
{
v = [NSString stringWithFormat:@"%@, %@",
[headerFields objectForKey: key], value];
[headerFields setObject: v forKey: key];
}
else
{
[headerFields setObject: value forKey: key];
}
}
}
else
{
continue;
}
}
return AUTORELEASE([headerFields copy]);
}
- (instancetype) _byAppendingHeaderLine: (NSString*)line
{
GSParsedResponseHeader *header;
header = AUTORELEASE([[GSParsedResponseHeader alloc] init]);
if ([line length] == 0)
{
switch (_type)
{
case GSParsedResponseHeaderTypePartial:
{
[header setType: GSParsedResponseHeaderTypeComplete];
[header setLines: _lines];
return header;
}
case GSParsedResponseHeaderTypeComplete:
return header;
}
}
else
{
NSMutableArray *lines = [[self partialResponseHeader] mutableCopy];
[lines addObject:line];
[header setType: GSParsedResponseHeaderTypePartial];
[header setLines: lines];
RELEASE(lines);
return header;
}
}
- (NSArray*) partialResponseHeader
{
switch (_type)
{
case GSParsedResponseHeaderTypeComplete:
return [NSArray array];
case GSParsedResponseHeaderTypePartial:
return _lines;
}
}
@end
@implementation GSDataDrain
- (void) dealloc
{
DESTROY(_data);
DESTROY(_fileURL);
DESTROY(_fileHandle);
[super dealloc];
}
- (GSDataDrainType) type
{
return _type;
}
- (void) setType: (GSDataDrainType)type
{
_type = type;
}
- (NSData*) data
{
return _data;
}
- (void) setData: (NSData*)data
{
ASSIGN(_data, data);
}
- (NSURL*) fileURL
{
return _fileURL;
}
- (void) setFileURL: (NSURL*)url
{
ASSIGN(_fileURL, url);
}
- (NSFileHandle*) fileHandle
{
return _fileHandle;
}
- (void) setFileHandle: (NSFileHandle*)handle
{
ASSIGN(_fileHandle, handle);
}
@end
@implementation GSTransferState
- (instancetype) initWithURL: (NSURL*)url
bodyDataDrain: (GSDataDrain*)bodyDataDrain
{
return [self initWithURL: url
parsedResponseHeader: nil
response: nil
bodySource: nil
bodyDataDrain: bodyDataDrain];
}
- (instancetype) initWithURL: (NSURL*)url
bodyDataDrain: (GSDataDrain*)bodyDataDrain
bodySource: (id<GSURLSessionTaskBodySource>)bodySource
{
return [self initWithURL: url
parsedResponseHeader: nil
response: nil
bodySource: bodySource
bodyDataDrain: bodyDataDrain];
}
- (instancetype) initWithURL: (NSURL*)url
parsedResponseHeader: (GSParsedResponseHeader*)parsedResponseHeader
response: (NSURLResponse*)response
bodySource: (id<GSURLSessionTaskBodySource>)bodySource
bodyDataDrain: (GSDataDrain*)bodyDataDrain
{
if (nil != (self = [super init]))
{
ASSIGN(_url, url);
if (nil != parsedResponseHeader)
{
ASSIGN(_parsedResponseHeader, parsedResponseHeader);
}
else
{
_parsedResponseHeader = [[GSParsedResponseHeader alloc] init];
}
ASSIGN(_response, response);
ASSIGN(_requestBodySource, bodySource);
ASSIGN(_bodyDataDrain, bodyDataDrain);
}
return self;
}
- (void) dealloc
{
DESTROY(_url);
DESTROY(_parsedResponseHeader);
DESTROY(_response);
DESTROY(_requestBodySource);
DESTROY(_bodyDataDrain);
[super dealloc];
}
- (instancetype) byAppendingBodyData: (NSData*)bodyData
{
switch ([_bodyDataDrain type])
{
case GSDataDrainInMemory:
{
NSMutableData *data;
GSDataDrain *dataDrain;
GSTransferState *ts;
data = [_bodyDataDrain data] ?
AUTORELEASE([[_bodyDataDrain data] mutableCopy])
: [NSMutableData data];
[data appendData: bodyData];
dataDrain = AUTORELEASE([[GSDataDrain alloc] init]);
[dataDrain setType: GSDataDrainInMemory];
[dataDrain setData: data];
ts = [[GSTransferState alloc] initWithURL: _url
parsedResponseHeader: _parsedResponseHeader
response: _response
bodySource: _requestBodySource
bodyDataDrain: dataDrain];
return AUTORELEASE(ts);
}
case GSDataDrainTypeToFile:
{
NSFileHandle *fileHandle;
fileHandle = [_bodyDataDrain fileHandle];
[fileHandle seekToEndOfFile];
[fileHandle writeData: bodyData];
return self;
}
case GSDataDrainTypeIgnore:
return self;
}
}
- (instancetype) byAppendingHTTPHeaderLineData: (NSData*)data
error: (NSError**)error
{
GSParsedResponseHeader *h;
h = [_parsedResponseHeader byAppendingHeaderLine: data];
if (nil == h)
{
if (error != NULL)
{
*error = [NSError errorWithDomain: NSURLErrorDomain
code: -1
userInfo: nil];
}
return nil;
}
if ([h type] == GSParsedResponseHeaderTypeComplete)
{
NSHTTPURLResponse *response;
GSParsedResponseHeader *ph;
GSTransferState *ts;
response = [h createHTTPURLResponseForURL: _url];
if (nil == response)
{
if (error != NULL)
{
*error = [NSError errorWithDomain: NSURLErrorDomain
code: -1
userInfo: nil];
}
return nil;
}
ph = AUTORELEASE([[GSParsedResponseHeader alloc] init]);
ts = [[GSTransferState alloc] initWithURL: _url
parsedResponseHeader: ph
response: response
bodySource: _requestBodySource
bodyDataDrain: _bodyDataDrain];
return AUTORELEASE(ts);
}
else
{
GSTransferState *ts;
ts = [[GSTransferState alloc] initWithURL: _url
parsedResponseHeader: h
response: nil
bodySource: _requestBodySource
bodyDataDrain: _bodyDataDrain];
return AUTORELEASE(ts);
}
}
- (BOOL) isHeaderComplete
{
return _response != nil;
}
- (NSURLResponse*) response
{
return _response;
}
- (void) setResponse: (NSURLResponse*)response
{
ASSIGN(_response, response);
}
- (id<GSURLSessionTaskBodySource>) requestBodySource
{
return _requestBodySource;
}
- (GSDataDrain*) bodyDataDrain
{
return _bodyDataDrain;
}
- (NSURL*) URL
{
return _url;
}
@end

View file

@ -58,6 +58,7 @@
@interface NSURLRequest (Private)
- (BOOL) _debug;
- (id<GSLogDelegate>) _debugLogDelegate;
- (id) _propertyForKey: (NSString*)key;
- (void) _setProperty: (id)value forKey: (NSString*)key;
@end
@ -72,11 +73,23 @@
@interface NSURLProtocol (Private)
+ (Class) _classToHandleRequest:(NSURLRequest *)request;
+ (Class) _classToHandleRequest: (NSURLRequest *)request;
+ (id<NSURLProtocolClient>) _ProtocolClient;
@end
/*
* Internal class for handling HTTP authentication
/* Internal class for handling HTTP protocol client messages
*/
@interface _NSURLProtocolClient : NSObject <NSURLProtocolClient>
{
NSURLRequestCachePolicy _cachePolicy;
NSMutableArray *_cacheableData;
NSURLResponse *_cacheableResponse;
}
@end
/* Internal class for handling HTTP authentication
*/
@class NSLock;
@interface GSHTTPAuthentication : NSObject

View file

@ -0,0 +1,46 @@
#ifndef INCLUDED_GSURLSESSIONTASKBODY_H
#define INCLUDED_GSURLSESSIONTASKBODY_H
#import "common.h"
@class NSData;
@class NSError;
@class NSInputStream;
@class NSNumber;
@class NSURL;
typedef NS_ENUM(NSUInteger, GSURLSessionTaskBodyType) {
GSURLSessionTaskBodyTypeNone,
GSURLSessionTaskBodyTypeData,
// Body data is read from the given file URL
GSURLSessionTaskBodyTypeFile,
// Body data is read from the given input stream
GSURLSessionTaskBodyTypeStream,
};
@interface GSURLSessionTaskBody : NSObject
{
GSURLSessionTaskBodyType _type;
NSData *_data;
NSURL *_fileURL;
NSInputStream *_inputStream;
}
- (instancetype) init;
- (instancetype) initWithData: (NSData*)data;
- (instancetype) initWithFileURL: (NSURL*)fileURL;
- (instancetype) initWithInputStream: (NSInputStream*)inputStream;
- (GSURLSessionTaskBodyType) type;
- (NSData*) data;
- (NSURL*) fileURL;
- (NSInputStream*) inputStream;
- (NSNumber*) getBodyLengthWithError: (NSError**)error;
@end
#endif

View file

@ -0,0 +1,111 @@
#import "GSURLSessionTaskBody.h"
#import "Foundation/NSData.h"
#import "Foundation/NSFileManager.h"
#import "Foundation/NSStream.h"
#import "Foundation/NSURL.h"
#import "Foundation/NSValue.h"
@implementation GSURLSessionTaskBody
- (instancetype) init
{
if (nil != (self = [super init]))
{
_type = GSURLSessionTaskBodyTypeNone;
}
return self;
}
- (instancetype) initWithData: (NSData*)data
{
if (nil != (self = [super init]))
{
_type = GSURLSessionTaskBodyTypeData;
ASSIGN(_data, data);
}
return self;
}
- (instancetype) initWithFileURL: (NSURL*)fileURL
{
if (nil != (self = [super init]))
{
_type = GSURLSessionTaskBodyTypeFile;
ASSIGN(_fileURL, fileURL);
}
return self;
}
- (instancetype) initWithInputStream: (NSInputStream*)inputStream
{
if (nil != (self = [super init]))
{
_type = GSURLSessionTaskBodyTypeStream;
ASSIGN(_inputStream, inputStream);
}
return self;
}
- (void) dealloc
{
DESTROY(_data);
DESTROY(_fileURL);
DESTROY(_inputStream);
[super dealloc];
}
- (GSURLSessionTaskBodyType) type
{
return _type;
}
- (NSData*) data
{
return _data;
}
- (NSURL*) fileURL
{
return _fileURL;
}
- (NSInputStream*) inputStream
{
return _inputStream;
}
- (NSNumber*) getBodyLengthWithError: (NSError**)error
{
switch (_type)
{
case GSURLSessionTaskBodyTypeNone:
return [NSNumber numberWithInt: 0];
case GSURLSessionTaskBodyTypeData:
return [NSNumber numberWithUnsignedInteger: [_data length]];
case GSURLSessionTaskBodyTypeFile:
{
NSDictionary *attributes;
attributes = [[NSFileManager defaultManager]
attributesOfItemAtPath: [_fileURL path]
error: error];
if (!error)
{
NSNumber *size = [attributes objectForKey: NSFileSize];
return size;
}
else
{
return nil;
}
}
case GSURLSessionTaskBodyTypeStream:
return nil;
}
}
@end

View file

@ -0,0 +1,52 @@
#ifndef INCLUDED_GSURLSESSIONTASKBODYSOURCE_H
#define INCLUDED_GSURLSESSIONTASKBODYSOURCE_H
#import "common.h"
#import "GSDispatch.h"
@class NSFileHandle;
@class NSInputStream;
typedef NS_ENUM(NSUInteger, GSBodySourceDataChunk) {
GSBodySourceDataChunkData,
// The source is depleted.
GSBodySourceDataChunkDone,
// Retry later to get more data.
GSBodySourceDataChunkRetryLater,
GSBodySourceDataChunkError
};
/*
* A (non-blocking) source for body data.
*/
@protocol GSURLSessionTaskBodySource <NSObject>
/*
* Get the next chunck of data.
*/
- (void) getNextChunkWithLength: (NSInteger)length
completionHandler: (void (^)(GSBodySourceDataChunk chunk, NSData *data))completionHandler;
@end
@interface GSBodyStreamSource : NSObject <GSURLSessionTaskBodySource>
- (instancetype) initWithInputStream: (NSInputStream*)inputStream;
@end
@interface GSBodyDataSource : NSObject <GSURLSessionTaskBodySource>
- (instancetype)initWithData:(NSData *)data;
@end
@interface GSBodyFileSource : NSObject <GSURLSessionTaskBodySource>
- (instancetype) initWithFileURL: (NSURL*)fileURL
workQueue: (dispatch_queue_t)workQueue
dataAvailableHandler: (void (^)(void))dataAvailableHandler;
@end
#endif

View file

@ -0,0 +1,152 @@
#import "GSURLSessionTaskBodySource.h"
#import "Foundation/NSData.h"
#import "Foundation/NSStream.h"
@implementation GSBodyStreamSource
{
NSInputStream *_inputStream;
}
- (instancetype) initWithInputStream: (NSInputStream*)inputStream
{
if (nil != (self = [super init]))
{
ASSIGN(_inputStream, inputStream);
if ([_inputStream streamStatus] == NSStreamStatusNotOpen)
{
[_inputStream open];
}
}
return self;
}
- (void) dealloc
{
DESTROY(_inputStream);
[super dealloc];
}
- (void) getNextChunkWithLength: (NSInteger)length
completionHandler: (void (^)(GSBodySourceDataChunk, NSData*))completionHandler
{
if (nil == completionHandler)
{
return;
}
if (![_inputStream hasBytesAvailable])
{
completionHandler(GSBodySourceDataChunkDone, nil);
return;
}
uint8_t buffer[length];
NSInteger readBytes;
NSData *data;
readBytes = [_inputStream read: buffer maxLength: length];
if (readBytes > 0)
{
data = AUTORELEASE([[NSData alloc] initWithBytes: buffer
length: readBytes]);
completionHandler(GSBodySourceDataChunkData, data);
}
else if (readBytes == 0)
{
completionHandler(GSBodySourceDataChunkDone, nil);
}
else
{
completionHandler(GSBodySourceDataChunkError, nil);
}
}
@end
@implementation GSBodyDataSource
{
NSData *_data;
}
- (instancetype) initWithData: (NSData*)data
{
if (nil != (self = [super init]))
{
ASSIGN(_data, data);
}
return self;
}
- (void) dealloc
{
DESTROY(_data);
[super dealloc];
}
- (void) getNextChunkWithLength: (NSInteger)length
completionHandler: (void (^)(GSBodySourceDataChunk, NSData*))completionHandler
{
if (nil == completionHandler)
{
return;
}
NSUInteger remaining = [_data length];
if (remaining == 0)
{
completionHandler(GSBodySourceDataChunkDone, nil);
}
else if (remaining <= length)
{
NSData *r = AUTORELEASE([[NSData alloc] initWithData: _data]);
DESTROY(_data);
completionHandler(GSBodySourceDataChunkData, r);
}
else
{
NSData *chunk = [_data subdataWithRange: NSMakeRange(0, length)];
NSData *remainder = [_data subdataWithRange:
NSMakeRange(length - 1, [_data length] - length)];
ASSIGN(_data, remainder);
completionHandler(GSBodySourceDataChunkData, chunk);
}
}
@end
@implementation GSBodyFileSource
{
NSURL *_fileURL;
dispatch_queue_t _workQueue;
void (^_dataAvailableHandler)(void);
}
- (instancetype) initWithFileURL: (NSURL*)fileURL
workQueue: (dispatch_queue_t)workQueue
dataAvailableHandler: (void (^)(void))dataAvailableHandler
{
if (nil != (self = [super init]))
{
ASSIGN(_fileURL, fileURL);
_workQueue = workQueue;
_dataAvailableHandler = dataAvailableHandler;
}
return self;
}
- (void) dealloc
{
DESTROY(_fileURL);
[super dealloc];
}
- (void) getNextChunkWithLength: (NSInteger)length
completionHandler: (void (^)(GSBodySourceDataChunk chunk, NSData *data))completionHandler
{
//FIXME
}
@end

View file

@ -62,7 +62,7 @@ acquireExistingMemory(const void *item,
static NSString*
describeString(const void *item)
{
return [NSString stringWithFormat: @"%s", item];
return [NSString stringWithFormat: @"%s", (char*)item];
}
static NSString*

View file

@ -687,6 +687,14 @@ recover(int sig)
siglongjmp(jbuf()->buf, 1);
}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wframe-address"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wframe-address"
#endif
void *
NSFrameAddress(NSUInteger offset)
{
@ -873,6 +881,12 @@ NSReturnAddress(NSUInteger offset)
return env->addr;
}
#ifdef __clang__
#pragma clang diagnostic pop
#else
#pragma GCC diagnostic pop
#endif
unsigned
GSPrivateReturnAddresses(NSUInteger **returns)
{

View file

@ -1412,7 +1412,7 @@ typedef struct {
desc = [NSString stringWithFormat:
@"<%s %p file name %s>",
GSClassNameFromObject(self), self, [name bytes]];
GSClassNameFromObject(self), self, (char*)[name bytes]];
return desc;
}

View file

@ -1469,7 +1469,7 @@ static Class tcpPortClass;
* Look up an existing NSSocketPort given a host and number
*/
+ (NSSocketPort*) existingPortWithNumber: (uint16_t)number
onHost: (NSHost*)aHost
onHost: (NSHost*)aHost
{
NSSocketPort *port = nil;
NSMapTable *thePorts;
@ -1763,6 +1763,9 @@ static Class tcpPortClass;
{
NSDebugMLLog(@"NSPort",
@"NSSocketPort 0x%"PRIxPTR" finalized", (NSUInteger)self);
M_LOCK(tcpPortLock);
NSMapRemove(tcpPortMap, (void*)(uintptr_t)portNum);
M_UNLOCK(tcpPortLock);
[self invalidate];
if (handles != 0)
{

View file

@ -24,6 +24,10 @@
#import "common.h"
#if GS_HAVE_NSURLSESSION
#import <Foundation/NSURLSession.h>
#endif
#define EXPOSE_NSURLCache_IVARS 1
#import "GSURLPrivate.h"
@ -215,3 +219,27 @@ static NSURLCache *shared = nil;
@end
#if GS_HAVE_NSURLSESSION
@implementation NSURLCache (NSURLSessionTaskAdditions)
- (void) storeCachedResponse: (NSCachedURLResponse*)cachedResponse
forDataTask: (NSURLSessionDataTask*)dataTask
{
[self storeCachedResponse: cachedResponse
forRequest: [dataTask currentRequest]];
}
- (NSCachedURLResponse*) cachedResponseForDataTask:
(NSURLSessionDataTask*)dataTask
{
return [self cachedResponseForRequest: [dataTask currentRequest]];
}
- (void) removeCachedResponseForDataTask: (NSURLSessionDataTask*)dataTask
{
[self removeCachedResponseForRequest: [dataTask currentRequest]];
}
@end
#endif

View file

@ -169,6 +169,11 @@ typedef struct
DESTROY(this->_delegate);
}
- (id) delegate
{
return this->_delegate;
}
- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop
forMode: (NSRunLoopMode)mode
{
@ -209,7 +214,9 @@ typedef struct
}
}
- (id) initWithRequest: (NSURLRequest *)request delegate: (id)delegate startImmediately: (BOOL)startImmediately
- (id) initWithRequest: (NSURLRequest *)request
delegate: (id)delegate
startImmediately: (BOOL)startImmediately
{
if ((self = [super init]) != nil)
{

View file

@ -25,13 +25,19 @@
#import "common.h"
#define EXPOSE_NSURLProtocol_IVARS 1
//#define EXPOSE_NSURLProtocol_IVARS 1
#import "Foundation/NSError.h"
#import "Foundation/NSHost.h"
#import "Foundation/NSNotification.h"
#import "Foundation/NSRunLoop.h"
#import "Foundation/NSValue.h"
#if GS_HAVE_NSURLSESSION
#import "Foundation/NSURLSession.h"
#else
@class NSURLSessionTask;
#endif
#import "GSPrivate.h"
#import "GSURLPrivate.h"
#import "GNUstepBase/GSMime.h"
@ -358,6 +364,7 @@ static NSLock *pairLock = nil;
NSURLAuthenticationChallenge *_challenge;
NSURLCredential *_credential;
NSHTTPURLResponse *_response;
id<GSLogDelegate> _logDelegate;
}
@end
@ -367,27 +374,6 @@ static NSLock *pairLock = nil;
@interface _NSDataURLProtocol : NSURLProtocol
@end
// Internal data storage
typedef struct {
NSInputStream *input;
NSOutputStream *output;
NSCachedURLResponse *cachedResponse;
id <NSURLProtocolClient> client;
NSURLRequest *request;
NSString *in;
NSString *out;
#if USE_ZLIB
z_stream z; // context for decompress
BOOL compressing; // are we compressing?
BOOL decompressing; // are we decompressing?
NSData *compressed; // only partially decompressed
#endif
} Internal;
#define this ((Internal*)(self->_NSURLProtocolInternal))
#define inst ((Internal*)(o->_NSURLProtocolInternal))
static NSMutableArray *registered = nil;
static NSLock *regLock = nil;
static Class abstractClass = nil;
@ -419,8 +405,44 @@ static NSURLProtocol *placeholder = nil;
}
@end
@implementation NSURLProtocol
#if GS_NONFRAGILE
{
@protected
#else
// Internal data storage
typedef struct {
#endif
NSInputStream *input;
NSOutputStream *output;
NSCachedURLResponse *cachedResponse;
id <NSURLProtocolClient> client;
NSURLRequest *request;
NSURLSessionTask *task;
NSString *in;
NSString *out;
#if USE_ZLIB
z_stream z; // context for decompress
BOOL compressing; // are we compressing?
BOOL decompressing; // are we decompressing?
NSData *compressed; // only partially decompressed
#endif
#if GS_NONFRAGILE
}
#define this self
#define inst o
#else
} Internal;
#define this ((Internal*)(self->_NSURLProtocolInternal))
#define inst ((Internal*)(o->_NSURLProtocolInternal))
#endif
+ (id) allocWithZone: (NSZone*)z
{
NSURLProtocol *o;
@ -508,7 +530,6 @@ static NSURLProtocol *placeholder = nil;
{
if (this != 0)
{
[self stopLoading];
if (this->input != nil)
{
[this->input setDelegate: nil];
@ -526,7 +547,11 @@ static NSURLProtocol *placeholder = nil;
}
DESTROY(this->cachedResponse);
DESTROY(this->request);
#if GS_HAVE_NSURLSESSION
DESTROY(this->task);
#endif
DESTROY(this->client);
#if USE_ZLIB
if (this->compressing == YES)
{
@ -538,8 +563,10 @@ static NSURLProtocol *placeholder = nil;
}
DESTROY(this->compressed);
#endif
#if !GS_NONFRAGILE
NSZoneFree([self zone], this);
_NSURLProtocolInternal = 0;
#endif
}
[super dealloc];
}
@ -554,6 +581,7 @@ static NSURLProtocol *placeholder = nil;
{
if ((self = [super init]) != nil)
{
#if !GS_NONFRAGILE
Class c = object_getClass(self);
if (c != abstractClass && c != placeholderClass)
@ -561,13 +589,14 @@ static NSURLProtocol *placeholder = nil;
_NSURLProtocolInternal = NSZoneCalloc([self zone],
1, sizeof(Internal));
}
#endif
}
return self;
}
- (id) initWithRequest: (NSURLRequest *)request
cachedResponse: (NSCachedURLResponse *)cachedResponse
client: (id <NSURLProtocolClient>)client
- (id) initWithRequest: (NSURLRequest*)_request
cachedResponse: (NSCachedURLResponse*)_cachedResponse
client: (id <NSURLProtocolClient>)_client
{
Class c = object_getClass(self);
@ -582,31 +611,57 @@ static NSURLProtocol *placeholder = nil;
{
Class proto = [registered objectAtIndex: count];
if ([proto canInitWithRequest: request] == YES)
if ([proto canInitWithRequest: _request] == YES)
{
self = [proto alloc];
break;
}
}
[regLock unlock];
return [self initWithRequest: request
cachedResponse: cachedResponse
client: client];
return [self initWithRequest: _request
cachedResponse: _cachedResponse
client: _client];
}
if ((self = [self init]) != nil)
{
this->request = [request copy];
this->cachedResponse = RETAIN(cachedResponse);
this->client = RETAIN(client);
this->request = [_request copy];
this->cachedResponse = RETAIN(_cachedResponse);
if (nil == _client)
{
_client = [[self class] _ProtocolClient];
}
this->client = RETAIN(_client);
}
return self;
}
- (instancetype) initWithTask: (NSURLSessionTask*)_task
cachedResponse: (NSCachedURLResponse*)_cachedResponse
client: (id<NSURLProtocolClient>)_client
{
#if GS_HAVE_NSURLSESSION
if (nil != (self = [self initWithRequest: [_task currentRequest]
cachedResponse: _cachedResponse
client: _client]))
{
ASSIGN(this->task, _task);
}
#else
DESTROY(self);
#endif
return self;
}
- (NSURLRequest *) request
{
return this->request;
}
- (NSURLSessionTask*) task
{
return this->task;
}
@end
@implementation NSURLProtocol (Debug)
@ -643,6 +698,13 @@ static NSURLProtocol *placeholder = nil;
return protoClass;
}
/* Internal method to return a client to handle callbacks if the protocol
* is initialised without one.
*/
+ (id<NSURLProtocolClient>) _ProtocolClient
{
return nil;
}
@end
@implementation NSURLProtocol (Subclassing)
@ -726,7 +788,14 @@ static NSURLProtocol *placeholder = nil;
static NSDictionary *methods = nil;
_debug = GSDebugSet(@"NSURLProtocol");
if (YES == [this->request _debug]) _debug = YES;
if (YES == [this->request _debug])
{
_debug = YES;
}
if (_debug)
{
_logDelegate = [this->request _debugLogDelegate];
}
if (methods == nil)
{
@ -795,12 +864,12 @@ static NSURLProtocol *placeholder = nil;
}
else
{
NSMutableURLRequest *request;
NSMutableURLRequest *r;
request = [[this->request mutableCopy] autorelease];
[request setURL: url];
r = [[this->request mutableCopy] autorelease];
[r setURL: url];
[this->client URLProtocol: self
wasRedirectedToRequest: request
wasRedirectedToRequest: r
redirectResponse: nil];
}
if (NO == _isLoading)
@ -979,7 +1048,12 @@ static NSURLProtocol *placeholder = nil;
}
if (_debug)
{
debugRead(self, readCount, buffer);
if (NO == [_logDelegate getBytes: buffer
ofLength: readCount
byHandle: self])
{
debugRead(self, readCount, buffer);
}
}
if (_parser == nil)
@ -991,7 +1065,7 @@ static NSURLProtocol *placeholder = nil;
d = [NSData dataWithBytes: buffer length: readCount];
if ([_parser parse: d] == NO && (_complete = [_parser isComplete]) == NO)
{
if (_debug == YES)
if (_debug)
{
NSLog(@"%@ HTTP parse failure - %@", self, _parser);
}
@ -1113,12 +1187,12 @@ static NSURLProtocol *placeholder = nil;
}
else
{
NSMutableURLRequest *request;
NSMutableURLRequest *r;
request = [[this->request mutableCopy] autorelease];
[request setURL: url];
r = AUTORELEASE([this->request mutableCopy]);
[r setURL: url];
[this->client URLProtocol: self
wasRedirectedToRequest: request
wasRedirectedToRequest: r
redirectResponse: _response];
}
}
@ -1335,18 +1409,18 @@ static NSURLProtocol *placeholder = nil;
}
else
{
NSMutableURLRequest *request;
NSMutableURLRequest *r;
/* To answer the authentication challenge,
* we must retry with a modified request and
* with the cached response cleared.
*/
request = [this->request mutableCopy];
[request setValue: auth
r = [this->request mutableCopy];
[r setValue: auth
forHTTPHeaderField: @"Authorization"];
[self stopLoading];
[this->request release];
this->request = request;
RELEASE(this->request);
this->request = r;
DESTROY(this->cachedResponse);
[self startLoading];
return;
@ -1426,7 +1500,7 @@ static NSURLProtocol *placeholder = nil;
* lost in the network or the remote end received it and
* the response was lost.
*/
if (_debug == YES)
if (_debug)
{
NSLog(@"%@ HTTP response not received - %@", self, _parser);
}
@ -1460,7 +1534,7 @@ static NSURLProtocol *placeholder = nil;
return;
case NSStreamEventOpenCompleted:
if (_debug == YES)
if (_debug)
{
NSLog(@"%@ HTTP input stream opened", self);
}
@ -1483,7 +1557,7 @@ static NSURLProtocol *placeholder = nil;
NSURL *u;
int l;
if (_debug == YES)
if (_debug)
{
NSLog(@"%@ HTTP output stream opened", self);
}
@ -1624,9 +1698,14 @@ static NSURLProtocol *placeholder = nil;
maxLength: len - _writeOffset];
if (written > 0)
{
if (_debug == YES)
if (_debug)
{
debugWrite(self, written, bytes + _writeOffset);
if (NO == [_logDelegate putBytes: bytes + _writeOffset
ofLength: written
byHandle: self])
{
debugWrite(self, written, bytes + _writeOffset);
}
}
_writeOffset += written;
if (_writeOffset >= len)
@ -1664,7 +1743,7 @@ static NSURLProtocol *placeholder = nil;
len = [_body read: buffer maxLength: sizeof(buffer)];
if (len < 0)
{
if (_debug == YES)
if (_debug)
{
NSLog(@"%@ error reading from HTTPBody stream %@",
self, [NSError _last]);
@ -1681,9 +1760,14 @@ static NSURLProtocol *placeholder = nil;
written = [this->output write: buffer maxLength: len];
if (written > 0)
{
if (_debug == YES)
if (_debug)
{
debugWrite(self, written, buffer);
if (NO == [_logDelegate putBytes: buffer
ofLength: written
byHandle: self])
{
debugWrite(self, written, buffer);
}
}
len -= written;
if (len > 0)

View file

@ -39,6 +39,7 @@ typedef struct {
NSMutableDictionary *headers;
BOOL shouldHandleCookies;
BOOL debug;
id<GSLogDelegate> ioDelegate;
NSURL *URL;
NSURL *mainDocumentURL;
NSURLRequestCachePolicy cachePolicy;
@ -116,6 +117,7 @@ typedef struct {
ASSIGN(inst->method, this->method);
inst->shouldHandleCookies = this->shouldHandleCookies;
inst->debug = this->debug;
inst->ioDelegate = this->ioDelegate;
inst->headers = [this->headers mutableCopy];
}
}
@ -284,6 +286,16 @@ typedef struct {
return old;
}
- (id<GSLogDelegate>) setDebugLogDelegate: (id<GSLogDelegate>)d
{
id<GSLogDelegate> old = this->ioDelegate;
NSAssert(nil == d || [d conformsToProtocol: @protocol(GSLogDelegate)],
NSInvalidArgumentException);
this->ioDelegate = d;
return old;
}
- (NSTimeInterval) timeoutInterval
{
return this->timeoutInterval;
@ -444,6 +456,11 @@ typedef struct {
return this->debug;
}
- (id<GSLogDelegate>) _debugLogDelegate
{
return this->ioDelegate;
}
- (id) _propertyForKey: (NSString*)key
{
return [this->properties objectForKey: key];

View file

@ -221,6 +221,11 @@ typedef struct {
[super dealloc];
}
- (NSString*) description
{
return [NSString stringWithFormat: @"%@ { URL: %@ } { Status Code: %d, Headers %@ }", [super description], this->URL, this->statusCode, this->headers];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
// FIXME

File diff suppressed because it is too large Load diff

View file

@ -146,15 +146,15 @@
}
- (pid_t) processIdentifier
{
return (pid_t)[self notImplemented: _cmd];
return (pid_t)(uintptr_t)[self notImplemented: _cmd];
}
- (uid_t) effectiveUserIdentifier
{
return (uid_t)[self notImplemented: _cmd];
return (uid_t)(uintptr_t)[self notImplemented: _cmd];
}
- (gid_t) effectiveGroupIdentifier
{
return (gid_t)[self notImplemented: _cmd];
return (gid_t)(uintptr_t)[self notImplemented: _cmd];
}
@end

View file

@ -36,7 +36,7 @@
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSString *file,
*lengthHeader;
NSHost *host = [NSHost hostWithAddress: @"127.0.0.1"];
NSHost *host = [NSHost hostWithName: @"localhost"];
NSStream *serverStream;
int port = [[defs stringForKey: @"Port"] intValue];
int lifetime = [[defs stringForKey: @"Lifetime"] intValue];

View file

@ -48,7 +48,7 @@ int main()
data = [u resourceDataUsingCache: NO];
// Get status code
str = [u propertyForKey: NSHTTPPropertyStatusCodeKey];
PASS([data isEqual: cont], "NSURL chunked test OK");
PASS_EQUAL(data, cont, "NSURL chunked test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];

View file

@ -13,7 +13,7 @@
* test.
*
* The test case which the NSURLConnectionTest implements by default is connecting
* to the http://127.0.0.1:1234/ whith the HTTP method 'GET'. You can change variable
* to the http://localhost:1234/ whith the HTTP method 'GET'. You can change variable
* parts of process by supplying a custom dictionary to the method -[setUpTest:]
* before the test case is started by a call of the -[startTest:]. The use pattern:
* --------------------------------------------------------------------------
@ -57,7 +57,7 @@
* (the instance will be run within a detached thread).
* Default: NO
* 'Address' - the address of the remote side.
* Default: 127.0.0.1
* Default: localhost
* 'Port' - the port of the remote side.
* Default: 1234
* 'AuxPort' - the port of the auxilliary remote side (where the connection

View file

@ -590,7 +590,7 @@ didReceiveResponse:(NSURLResponse *)response
}
if (nil == address)
{
address = @"127.0.0.1";
address = @"localhost";
}
if (nil == port)
{

View file

@ -6,9 +6,9 @@
@implementation TestCase
- (id)init
- (id) init
{
if((self = [super init]) != nil)
if ((self = [super init]) != nil)
{
[self resetFlags];
[self setReferenceFlags: NORESULTS];
@ -19,7 +19,7 @@
return self;
}
- (void)dealloc
- (void) dealloc
{
[self tearDownTest: _extra];
@ -27,47 +27,47 @@
}
/* TestProgress */
- (void)resetFlags
- (void) resetFlags
{
_flags = NORESULTS;
}
- (void)setFlags:(NSUInteger)mask
- (void) setFlags:(NSUInteger)mask
{
_flags = _flags | mask;
}
- (void)unsetFlags:(NSUInteger)mask
- (void) unsetFlags:(NSUInteger)mask
{
_flags = _flags & ~mask;
}
- (BOOL)isFlagSet:(NSUInteger)mask
- (BOOL) isFlagSet:(NSUInteger)mask
{
return ((_flags & mask) == mask);
}
- (void)resetReferenceFlags
- (void) resetReferenceFlags
{
_refFlags = NORESULTS;
}
- (void)setReferenceFlags:(NSUInteger)mask
- (void) setReferenceFlags:(NSUInteger)mask
{
_refFlags = _refFlags | mask;
}
- (void)unsetReferenceFlags:(NSUInteger)mask
- (void) unsetReferenceFlags:(NSUInteger)mask
{
_refFlags = _refFlags & ~mask;
}
- (BOOL)isReferenceFlagSet:(NSUInteger)mask
- (BOOL) isReferenceFlagSet:(NSUInteger)mask
{
return ((_refFlags & mask) == mask);
}
- (BOOL)isSuccess
- (BOOL) isSuccess
{
if(!_failed && (_flags == _refFlags))
{

View file

@ -13,7 +13,7 @@
* It is designed to call the delegate's callbacks (if implemented) of
* the TestWebServerDelegate protocol for every request made to
* the SimpleWebServer's assigned address on the SimpleWebServer's assigned port
* (by default 127.0.0.1 and 1234 respectively). However it currently doesn't
* (by default localhost and 1234 respectively). However it currently doesn't
* handle any request on it's own. Instead the class uses a collection of
* handlers. Every handler makes a custom response. So the TestWebServer only
* has to dispatch a request to a corresponding handler and then to send back
@ -29,7 +29,7 @@
* NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys:
* @"https", @"Protocol",
* nil];
* TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"127.0.0.1"
* TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"localhost"
* port: @"1234"
* mode: NO
* extra: extra];
@ -72,7 +72,7 @@
* 301 "Redirect to <URL>"
* Returns in the header 'Location' a new <URL> which by default
* has the same protocol, address but the port is incremented by
* 1 (e.g. http://127.0.0.1:1235/ with other parameters set to
* 1 (e.g. http://localhost:1235/ with other parameters set to
* their default values).
* 400 "You have issued a request with invalid data"
* 401 "Invalid login or password"

View file

@ -9,7 +9,7 @@
/**
* Starts the detached thread. It is the entry point of the thread.
*/
*/
- (void)_startDetached:(id)extra;
/**
@ -23,10 +23,10 @@
- (void)_stopHTTPServer;
@end
@end
/* default 'constants' */
#define DEFAULTADDRESS @"127.0.0.1"
#define DEFAULTADDRESS @"localhost"
#define DEFAULTPORT @"1234"
#define DEFAULTMODE NO
#define DEFAULTLOGIN @"login"
@ -45,7 +45,7 @@
@implementation TestWebServer
- (id)init
- (id) init
{
return [self initWithAddress: DEFAULTADDRESS
port: DEFAULTPORT
@ -68,7 +68,7 @@
_password = nil;
_login = nil;
_isSecure = NO;
if ([extra isKindOfClass: [NSDictionary class]])
{
NSDictionary *d = extra;
@ -123,7 +123,7 @@
selector: @selector(_startDetached:)
object: extra];
}
else
else
{
_serverThread = nil;
}
@ -145,36 +145,40 @@
[super dealloc];
}
- (void)start:(id)extra
- (void) start: (id)extra
{
if ([_server port] != nil)
{
if (YES == _debug)
if ([_server port] != nil)
{
if (_debug)
{
NSWarnMLog(@"SimpleWebServer already started");
}
return;
return;
}
if (nil != _serverThread)
{
if (_debug)
NSLog(@"Waiting for startup");
if (![_serverThread isExecuting])
{
NSTimeInterval duration = 0.0;
[_serverThread start];
// wait for the SimpleWebServer is started
while(![_lock tryLockWhenCondition: STARTED] &&
duration < MAXDURATION)
while (![_lock tryLockWhenCondition: STARTED]
&& duration < MAXDURATION)
{
[[NSRunLoop currentRunLoop]
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
duration += TIMING;
}
[_lock unlock];
if (duration >= MAXDURATION &&
nil != _delegate &&
[_delegate respondsToSelector: @selector(timeoutExceededByHandler:)])
if (duration >= MAXDURATION
&& nil != _delegate
&& [_delegate respondsToSelector:
@selector(timeoutExceededByHandler:)])
{
[_delegate timeoutExceededByHandler: self];
[self stop];
@ -191,7 +195,7 @@
}
}
- (void)stop
- (void) stop
{
if ([_server port] == nil)
{
@ -283,18 +287,18 @@
_isSecure ? @"https" : @"http",
_address,
_port];
[(HandlerIndex *)handler setURLString: urlString];
}
else if ([handler isKindOfClass: [Handler301 class]])
{
// by default http://127.0.0.1:1235/
// by default http://localhost:1235/
NSString *port = [NSString stringWithFormat: @"%u", [_port intValue] + 1]; // the TestWebServer's port + 1
NSString *urlString = [NSString stringWithFormat: @"%@://%@:%@/",
_isSecure ? @"https" : @"http",
_address,
port];
[(Handler301 *)handler setURLString: urlString];
}
}
@ -314,7 +318,7 @@
[handler posthandleRequest: request
response: response
for: self];
return ret;
}
@ -369,7 +373,7 @@
@implementation TestWebServer (Private)
- (void)_startHTTPServer:(id)extra
- (void) _startHTTPServer: (id)extra
{
NSString *certPath;
NSString *keyPath;
@ -381,8 +385,10 @@
[_server setDelegate: self];
if (_isSecure)
{
if ([_address isEqualToString: @"127.0.0.1"] ||
[_address isEqualToString: @"localhost"])
NSHost *h = [NSHost hostWithAddress: _address];
NSHost *l = [NSHost hostWithName: @"localhost"];
if ([h isEqual: l])
{
certPath = [[NSBundle bundleForClass: [self class]]
pathForResource: @"testCert"
@ -397,22 +403,31 @@
}
else
{
[NSException raise: NSInternalInconsistencyException
format: @"The server hasn't run. Address %@ is not localhost (%@)",
_address, l];
// NOTE: generate corresponding certificates for any address differing
// from 127.0.0.1
// from localhost
}
}
status = [_server setAddress: _address port: _port secure: secure]; // run the server
if (!status)
if (_debug)
{
NSLog(@"Starting web server with address %@, port %@ %@",
_address, _port, secure ? @" with TLS" : @"");
}
status = [_server setAddress: _address port: _port secure: secure];
if (!status)
{
[NSException raise: NSInternalInconsistencyException
format: @"The server hasn't run. Perhaps the port %@ is invalid", DEFAULTPORT];
format: @"The server hasn't run. Perhaps the port %@ is invalid",
DEFAULTPORT];
}
}
- (void)_stopHTTPServer
{
if (nil != _server && [_server port] != nil)
if (nil != _server && [_server port] != nil)
{
[_server stop]; // shut down the server
if (YES == _debug)

View file

@ -3,15 +3,15 @@
* Author: Sergei Golovin <Golovin.SV@gmail.com>
*
* Runs two TestWebServer instances to check how the class TestWebServer
* behaves. Visit http://127.0.0.1:1234/index to see all supported resources.
* behaves. Visit http://localhost:1234/index to see all supported resources.
*
* If you visit the main TestWebServer instance with the following command:
*
* wget -O - --user=login --password=password http://127.0.0.1:1234/301 2>&1
* wget -O - --user=login --password=password http://localhost:1234/301 2>&1
*
* you should get a session log like this:
*
* --2014-08-13 12:08:01-- http://127.0.0.1:1234/301
* --2014-08-13 12:08:01-- http://localhost:1234/301
* Resolving 127.0.0.1 (localhost)... 127.0.0.1
* Connecting to 127.0.0.1 (localhost)|127.0.0.1|:1234... connected.
* HTTP request sent, awaiting response... 401 Unauthorized
@ -64,20 +64,20 @@ int main(int argc, char **argv, char **env)
// @"https", @"Protocol",
nil];
server1 = [[[testClass testWebServerClass] alloc]
initWithAddress: @"127.0.0.1"
initWithAddress: @"localhost"
port: @"1234"
mode: NO
extra: d];
[server1 setDebug: debug];
[server1 start: d]; // 127.0.0.1:1234 HTTP
[server1 start: d]; // localhost:1234 HTTP
server2 = [[[testClass testWebServerClass] alloc]
initWithAddress: @"127.0.0.1"
initWithAddress: @"localhost"
port: @"1235"
mode: NO
extra: d];
[server2 setDebug: debug];
[server2 start: d]; // 127.0.0.1:1235 HTTP
[server2 start: d]; // localhost:1235 HTTP
while (YES)
{

View file

@ -59,7 +59,7 @@ int main(int argc, char **argv, char **env)
duration = 0.0;
timing = 0.1;
urlString = @"http://127.0.0.1:19750";
urlString = @"http://localhost:19750";
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
del = [[Delegate new] autorelease];
[NSURLConnection connectionWithRequest: req
@ -76,7 +76,7 @@ int main(int argc, char **argv, char **env)
[del reset];
duration = 0.0;
urlString = @"https://127.0.0.1:19750";
urlString = @"https://localhost:19750";
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
[NSURLConnection connectionWithRequest: req
delegate: del];

View file

@ -35,7 +35,7 @@ int main(int argc, char **argv, char **env)
// create a shared TestWebServer instance for performance
server = [[testClass testWebServerClass] new];
[server setDebug: debug];
[server start: nil]; // 127.0.0.1:1234 HTTP
[server start: nil]; // localhost:1234 HTTP
/*
* Simple GET via HTTP with empty response's body and

View file

@ -40,12 +40,12 @@ int main(int argc, char **argv, char **env)
nil];
// create a shared TestWebServer instance for performance
server = [[[testClass testWebServerClass] alloc]
initWithAddress: @"127.0.0.1"
initWithAddress: @"localhost"
port: @"1234"
mode: NO
extra: d];
[server setDebug: debug];
[server start: d]; // 127.0.0.1:1234 HTTPS
[server start: d]; // localhost:1234 HTTPS
/*
* Simple GET via HTTPS with empty response's body and
@ -59,7 +59,7 @@ int main(int argc, char **argv, char **env)
nil];
[testCase setUpTest: d];
[testCase startTest: d];
PASS([testCase isSuccess], "HTTPS... GET https://127.0.0.1:1234/");
PASS([testCase isSuccess], "HTTPS... GET https://localhost:1234/");
[testCase tearDownTest: d];
DESTROY(testCase);

View file

@ -35,7 +35,7 @@ int main(int argc, char **argv, char **env)
// create a shared TestWebServer instance for performance
server = [[testClass testWebServerClass] new];
[server setDebug: debug];
[server start: nil]; // 127.0.0.1:1234 HTTP
[server start: nil]; // localhost:1234 HTTP
/*
* Simple GET via HTTP without authorization with empty response's body and

View file

@ -44,7 +44,7 @@ int main(int argc, char **argv, char **env)
mode: NO
extra: d];
[server setDebug: debug];
[server start: d]; // 127.0.0.1:1234 HTTPS
[server start: d]; // localhost:1234 HTTPS
/* Simple GET via HTTPS without authorization with empty response's
* body and the response's status code 204 (by default)

View file

@ -39,7 +39,7 @@ int main(int argc, char **argv, char **env)
// login:password
server = [[testClass testWebServerClass] new];
[server setDebug: debug];
[server start: nil]; // 127.0.0.1:1234 HTTP
[server start: nil]; // localhost:1234 HTTP
/*
* Simple GET via HTTP with some response's body and

View file

@ -45,7 +45,7 @@ int main(int argc, char **argv, char **env)
mode: NO
extra: d];
[server setDebug: debug];
[server start: d]; // 127.0.0.1:1234 HTTPS
[server start: d]; // localhost:1234 HTTPS
/* Simple POST via HTTPS with the response's status code 400 and
* non-empty response's body

View file

@ -0,0 +1,5 @@
SUBPROJECTS = ../NSURLConnection/Helpers
include $(GNUSTEP_MAKEFILES)/aggregate.make

View file

View file

@ -0,0 +1,116 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
@interface MyDelegate : NSObject <NSURLSessionDelegate>
{
BOOL _finished;
NSMutableArray *_order;
NSURLResponse *_response;
NSData *_taskData;
NSString *_taskText;
NSError *_taskError;
}
- (BOOL) finished;
- (void) reset;
@end
@implementation MyDelegate
- (void) dealloc
{
[self reset];
[super dealloc];
}
- (BOOL) finished
{
return _finished;
}
- (id) init
{
if (nil != (self = [super init]))
{
_order = [NSMutableArray new];
}
return self;
}
- (NSMutableArray*) order
{
return _order;
}
- (void) reset
{
DESTROY(_order);
DESTROY(_response);
DESTROY(_taskData);
DESTROY(_taskError);
DESTROY(_taskText);
_finished = NO;
}
- (NSURLResponse*) response
{
return _response;
}
- (NSData*) taskData
{
return _taskData;
}
- (NSError*) taskError
{
return _taskError;
}
- (NSString*) taskText
{
return _taskText;
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveResponse: (NSURLResponse*)response
completionHandler: (void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
[_order addObject: NSStringFromSelector(_cmd)];
ASSIGN(_response, response);
completionHandler(NSURLSessionResponseAllow);
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveData: (NSData*)data
{
[_order addObject: NSStringFromSelector(_cmd)];
NSString *text;
ASSIGN(_taskData, data);
text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if (nil != text)
{
ASSIGN(_taskText, text);
}
RELEASE(text);
}
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didCompleteWithError: (NSError*)error
{
[_order addObject: NSStringFromSelector(_cmd)];
_finished = YES;
if (error == nil)
{
NSLog(@"Download is Succesfull");
}
else
{
NSLog(@"Error %@", [error userInfo]);
}
ASSIGN(_taskError, error);
}
@end

View file

@ -0,0 +1,79 @@
def print_ivar
set $adr = malloc(1000)
call strcpy(($adr),($arg1))
call GSObjCPrint(($arg0),($adr))
call free($adr)
end
def print_ivars
if ((($arg0)->isa->info & 0x01) || (($arg0)->isa->info == 2816))
# arg0 is a pointer to an object
set $cls = ($arg0)->isa
else
if (($arg0)->isa->info & 0x02)
# arg0 is a pointer to a class
set $cls = ($arg0)
else
# arg0 is something else
set $cls = 0
end
end
while (($cls) != 0)
set $ivars = ($cls)->ivars
if (($ivars) != 0)
set $i = 0
while ($i < ($ivars)->count)
output ($ivars)->ivar_list[$i]
echo \n
set $i = $i + 1
end
end
set $cls = ($cls)->super_class
end
end
def pivi
print *(int *)((char *)($arg0) + ($arg1)))
end
def pivl
print *(long *)((char *)($arg0) + ($arg1)))
end
def pivp
print *(void *)((char *)($arg0) + ($arg1)))
end
def pivo
po *((id *)((char *)($arg0) + ($arg1)))
end
document print_ivars
Recursively print the instance varibles of the object or a class given
as first (and only) argument.
end
document pivi
Print the value of the an instance variable as an int.
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivl
Print the value of the an instance variable as a long.
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivp
Print the value of the an instance variable as a pointer (to void).
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivo
Ask an instance variable to print itself (using po).
The first argument is the pointer to the object and the second the
offset of the instance variable.
end

View file

@ -0,0 +1,132 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
#if GS_HAVE_NSURLSESSION
@interface MyDelegate : NSObject <NSURLSessionDelegate>
{
@public
BOOL responseCompletion;
BOOL didComplete;
NSString *taskText;
NSError *taskError;
}
@end
@implementation MyDelegate
- (void) dealloc
{
RELEASE(taskText);
RELEASE(taskError);
[super dealloc];
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveResponse: (NSURLResponse*)response
completionHandler: (void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
responseCompletion = YES;
if (NO == didComplete)
{
NSLog(@"### handler 1 before didComplete...");
}
else
{
NSLog(@"### handler 1 after didComplete...");
}
completionHandler(NSURLSessionResponseAllow);
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveData: (NSData*)data
{
NSString *text;
text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if (nil == text)
{
NSLog(@"Received non-utf8 %@", data);
}
else
{
ASSIGN(taskText, text);
NSLog(@"Received String %@", text);
}
RELEASE(text);
}
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didCompleteWithError: (NSError*)error
{
if (error == nil)
{
NSLog(@"Download is Succesfull");
}
else
{
NSLog(@"Error %@", [error userInfo]);
}
didComplete = YES;
ASSIGN(taskError, error);
}
@end
#endif
int main()
{
START_SET("NSURLSession test01")
#if !GS_HAVE_NSURLSESSION
SKIP("library built without NSURLSession support")
#else
NSURLSessionConfiguration *defaultConfigObject;
NSURLSession *defaultSession;
NSURLSessionDataTask *dataTask;
NSMutableURLRequest *urlRequest;
NSURL *url;
NSOperationQueue *mainQueue;
NSString *params;
MyDelegate *object;
object = AUTORELEASE([MyDelegate new]);
mainQueue = [NSOperationQueue mainQueue];
defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
delegate: object
delegateQueue: mainQueue];
url = [NSURL URLWithString:
@"http://localhost:12345/not-here"];
urlRequest = [NSMutableURLRequest requestWithURL: url];
[urlRequest setHTTPMethod: @"POST"];
params = @"name=Ravi&loc=India&age=31&submit=true";
[urlRequest setHTTPBody: [params dataUsingEncoding: NSUTF8StringEncoding]];
if ([urlRequest respondsToSelector: @selector(setDebug:)])
{
[urlRequest setDebug: YES];
}
dataTask = [defaultSession dataTaskWithRequest: urlRequest];
[dataTask resume];
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 60.0];
while (object->didComplete == NO
&& [limit timeIntervalSinceNow] > 0.0)
{
ENTER_POOL
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: 0.1];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
LEAVE_POOL
}
PASS(YES == object->didComplete, "request completed")
PASS([object->taskError code] == NSURLErrorCannotConnectToHost,
"unable to connect to host")
#endif
END_SET("NSURLSession test01")
return 0;
}

View file

@ -0,0 +1,89 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
#import "../NSURLConnection/Helpers/TestWebServer.h"
#if GS_HAVE_NSURLSESSION
#import "delegate.g"
#endif
int main()
{
START_SET("NSURLSession http")
#if !GS_HAVE_NSURLSESSION
SKIP("library built without NSURLSession support")
#else
NSFileManager *fm;
NSBundle *bundle;
NSString *helperPath;
// load the test suite's classes
fm = [NSFileManager defaultManager];
helperPath = [[fm currentDirectoryPath] stringByAppendingPathComponent:
@"../NSURLConnection/Helpers/TestConnection.bundle"];
bundle = [NSBundle bundleWithPath: helperPath];
NSCAssert([bundle load], NSInternalInconsistencyException);
TestWebServer *server;
Class c;
BOOL debug = YES;
// create a shared TestWebServer instance for performance
c = [bundle principalClass];
server = [[c testWebServerClass] new];
NSCAssert(server != nil, NSInternalInconsistencyException);
[server setDebug: debug];
[server start: nil]; // localhost:1234 HTTP
NSURLSessionConfiguration *defaultConfigObject;
NSURLSession *defaultSession;
NSURLSessionDataTask *dataTask;
NSMutableURLRequest *urlRequest;
NSURL *url;
NSOperationQueue *mainQueue;
NSString *params;
MyDelegate *object;
object = AUTORELEASE([MyDelegate new]);
mainQueue = [NSOperationQueue mainQueue];
defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
delegate: object
delegateQueue: mainQueue];
url = [NSURL URLWithString: @"http://localhost:1234/xxx"];
params = @"dummy=true";
urlRequest = [NSMutableURLRequest requestWithURL: url];
[urlRequest setHTTPMethod: @"POST"];
[urlRequest setHTTPBody: [params dataUsingEncoding: NSUTF8StringEncoding]];
if ([urlRequest respondsToSelector: @selector(setDebug:)])
{
[urlRequest setDebug: YES];
}
dataTask = [defaultSession dataTaskWithRequest: urlRequest];
[dataTask resume];
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 60.0];
while ([object finished] == NO
&& [limit timeIntervalSinceNow] > 0.0)
{
ENTER_POOL
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: 0.1];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
LEAVE_POOL
}
PASS(YES == [object finished], "request completed")
PASS_EQUAL([object taskError], nil, "request did not error")
NSString *expect = @"Please give login and password";
PASS_EQUAL([object taskText], expect, "request returned text")
#endif
END_SET("NSURLSession http")
return 0;
}

View file

@ -61,7 +61,7 @@ ifeq ($(BASE_MAKE_LOADED),)
endif
# Now we have definitions to show whether important dependencies have
# been met ... if thse are 0 then some core functi0nailyt is missing.
# been met ... if thse are 0 then some core functionality is missing.
# Has GNUTLS been found (for TLS/SSL support throughout)?
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@

View file

@ -35,13 +35,14 @@ endif
GNUSTEP_INSTALL_GDOMAP_AS_SETUID=@GNUSTEP_INSTALL_GDOMAP_AS_SETUID@
GNUSTEP_GDOMAP_PORT_OVERRIDE=@GNUSTEP_GDOMAP_PORT_OVERRIDE@
GNUSTEP_BASE_HAVE_LIBXML=@HAVE_LIBXML@
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
GNUSTEP_BASE_HAVE_MDNS=@HAVE_MDNS@
GNUSTEP_BASE_HAVE_AVAHI=@HAVE_AVAHI@
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
GNUSTEP_BASE_HAVE_ICU=@HAVE_ICU@
GNUSTEP_BASE_HAVE_LIBCURL=@HAVE_LIBCURL@
GNUSTEP_BASE_HAVE_LIBDISPATCH=@HAVE_LIBDISPATCH@
GNUSTEP_BASE_HAVE_LIBDISPATCH_RUNLOOP=@HAVE_LIBDISPATCH_RUNLOOP@
GNUSTEP_BASE_HAVE_LIBXML=@HAVE_LIBXML@
GNUSTEP_BASE_HAVE_MDNS=@HAVE_MDNS@
# Default to building only -baseadd
# on non *-gnu-* library combos

View file

@ -6,15 +6,16 @@ dnl notice and this notice are preserved.
dnl
dnl Written by Andrew Ruder
dnl GS_ADD_LIBRARY_PATH
dnl Adds -L$1 -Wl,-R$1 on netbsd and -L$1 elsewhere to LDFLAGS and LDIR_FLAGS
dnl Adds -L$1 -Wl,-R$1 on netbsd and -Wl,-rpath,$1 -L$1 elsewhere
dnl to LDFLAGS and LDIR_FLAGS
AC_DEFUN([GS_ADD_LIBRARY_PATH], [
case "$target_os" in
netbsd*)
LDFLAGS="$LDFLAGS -L$1 -Wl,-R$1"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R$1 -L$1";;
*)
LDFLAGS="$LDFLAGS -L$1"
LDIR_FLAGS="$LDIR_FLAGS -L$1";;
LDFLAGS="$LDFLAGS -Wl,-rpath,$1 -L$1"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,$1 -L$1";;
esac
])

263
configure vendored
View file

@ -632,6 +632,8 @@ WARN_FLAGS
LDIR_FLAGS
INCLUDE_FLAGS
USE_GMP
GS_HAVE_NSURLSESSION
HAVE_LIBCURL
HAVE_LIBDISPATCH_RUNLOOP
HAVE_LIBDISPATCH
HAVE_ICU
@ -820,6 +822,10 @@ enable_zeroconf
with_zeroconf_api
enable_icu
enable_libdispatch
with_dispatch_include
with_dispatch_library
with_curl
enable_nsurlsession
with_gmp_include
with_gmp_library
with_gdomap_port
@ -1500,6 +1506,7 @@ Optional Features:
--disable-zeroconf Disable NSNetServices support
--disable-icu Disable International Components for Unicode
--disable-libdispatch Disable dispatching blocks via libdispatch
--disable-nsurlsession Disable support for NSURLSession
--enable-setuid-gdomap Enable installing gdomap as a setuid
executable. By default, it is installed
@ -1571,6 +1578,9 @@ Optional Packages:
--with-xml-prefix=PFX Prefix where libxml is installed (optional)
--with-tls-prefix=PFX Prefix where libgnutls is installed (optional)
--with-zeroconf-api=API force use of a specific zeroconf API (mdns or avahi)
--with-dispatch-include=PATH Include path for dispatch header
--with-dispatch-library=PATH Library path for dispatch lib
--with-curl=<DIR> use curl installed in directory <DIR>
--with-gmp-include=PATH include path for gmp headers
--with-gmp-library=PATH library path for gmp libraries
--with-gdomap-port=PORT alternative port for gdomap
@ -10762,8 +10772,8 @@ case "$target_os" in
LDFLAGS="$LDFLAGS -L${ffi_libdir} -Wl,-R${ffi_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${ffi_libdir} -L${ffi_libdir}";;
*)
LDFLAGS="$LDFLAGS -L${ffi_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -L${ffi_libdir}";;
LDFLAGS="$LDFLAGS -Wl,-rpath,${ffi_libdir} -L${ffi_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${ffi_libdir} -L${ffi_libdir}";;
esac
fi
@ -11093,8 +11103,8 @@ case "$target_os" in
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
*)
LDFLAGS="$LDFLAGS -L${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -L${libiconv_libdir}";;
LDFLAGS="$LDFLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}";;
esac
fi
@ -11251,8 +11261,8 @@ case "$target_os" in
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
*)
LDFLAGS="$LDFLAGS -L${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -L${libiconv_libdir}";;
LDFLAGS="$LDFLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}";;
esac
fi
@ -12885,6 +12895,7 @@ fi
# Check for libdispatch
# See DEPENDENCIES POLICY at the start of this file.
#--------------------------------------------------------------------
HAVE_LIBDISPATCH=0
# Check whether --enable-libdispatch was given.
if test "${enable_libdispatch+set}" = set; then :
@ -12895,6 +12906,40 @@ fi
if test $enable_libdispatch = yes; then
# Check whether --with-dispatch-include was given.
if test "${with_dispatch_include+set}" = set; then :
withval=$with_dispatch_include; dispatch_incdir="$withval"
else
dispatch_incdir="no"
fi
if test ${dispatch_incdir} != "no"; then
CPPFLAGS="$CPPFLAGS -I${dispatch_incdir}"
INCLUDE_FLAGS="$INCLUDE_FLAGS -I${dispatch_incdir}"
fi
# Check whether --with-dispatch-library was given.
if test "${with_dispatch_library+set}" = set; then :
withval=$with_dispatch_library; dispatch_libdir="$withval"
else
dispatch_libdir="no"
fi
if test ${dispatch_libdir} != "no"; then
case "$target_os" in
netbsd*)
LDFLAGS="$LDFLAGS -L${dispatch_libdir} -Wl,-R${dispatch_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${dispatch_libdir} -L${dispatch_libdir}";;
*)
LDFLAGS="$LDFLAGS -Wl,-rpath,${dispatch_libdir} -L${dispatch_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${dispatch_libdir} -L${dispatch_libdir}";;
esac
fi
for ac_header in dispatch.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "dispatch.h" "ac_cv_header_dispatch_h" "$ac_includes_default"
@ -13019,8 +13064,8 @@ $as_echo "no" >&6; };
fi
fi
else
HAVE_LIBDISPATCH=0;
# just ignore libdispatch if it's not there
HAVE_LIBDISPATCH=0;
# just ignore libdispatch if it's not there
fi
fi
@ -13064,6 +13109,204 @@ done
fi
#--------------------------------------------------------------------
# Check for libcurl
# See DEPENDENCIES POLICY at the start of this file.
#--------------------------------------------------------------------
CURL_CONFIG="curl-config"
# Check whether --with-curl was given.
if test "${with_curl+set}" = set; then :
withval=$with_curl;
fi
if test "$with_curl" != ""; then
CURL_CONFIG="$with_curl/bin/curl-config"
fi
HAVE_LIBCURL=0
curl_all=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl" >&5
$as_echo_n "checking for libcurl... " >&6; }
if eval $CURL_CONFIG --version 2>/dev/null >/dev/null; then
curl_ver=`$CURL_CONFIG --version | sed -e "s/libcurl //g"`
curl_maj=`echo $curl_ver | sed -e "s/^\(.*\)\.\(.*\)\.\(.*\)$/\1/"`
curl_min=`echo $curl_ver | sed -e "s/^\(.*\)\.\(.*\)\.\(.*\)$/\2/"`
if test $curl_maj -lt 7 -o \( $curl_maj -eq 7 -a $curl_min -lt 49 \); then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: FAILED (version too old to use" >&5
$as_echo "FAILED (version too old to use" >&6; }
else
CURLCFLAGS=`$CURL_CONFIG --cflags`
CURLLIBS=`$CURL_CONFIG --libs`
CFLAGS="$CFLAGS $CURLCFLAGS"
LIBS="$LIBS $CURLLIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ... version $curl_ver" >&5
$as_echo "yes ... version $curl_ver" >&6; }
HAVE_LIBCURL=1
curl_all=yes
if test \( $curl_maj -eq 7 -a $curl_min -lt 56 \); then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: LOSS OF FUNCTIONALITY (version too old for SSL backend control)" >&5
$as_echo "$as_me: WARNING: LOSS OF FUNCTIONALITY (version too old for SSL backend control)" >&2;}
curl_all=no
elif test \( $curl_maj -eq 7 -a $curl_min -lt 65 \); then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: LOSS OF FUNCTIONALITY (version too old for connection lifetime control)" >&5
$as_echo "$as_me: WARNING: LOSS OF FUNCTIONALITY (version too old for connection lifetime control)" >&2;}
curl_all=no
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: FAILED (curl-config not found)" >&5
$as_echo "FAILED (curl-config not found)" >&6; }
fi
if test $HAVE_LIBCURL = 1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl TLS support with gnutls" >&5
$as_echo_n "checking for libcurl TLS support with gnutls... " >&6; }
if $CURL_CONFIG --configure | grep -q with-gnutls; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
curl_all=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: LOSS OF FUNCTIONALITY (curl lacks gnutls support)" >&5
$as_echo "$as_me: WARNING: LOSS OF FUNCTIONALITY (curl lacks gnutls support)" >&2;}
fi
fi
if test $HAVE_LIBCURL = 1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for curl_multi_perform in -lcurl" >&5
$as_echo_n "checking for curl_multi_perform in -lcurl... " >&6; }
if ${ac_cv_lib_curl_curl_multi_perform+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lcurl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char curl_multi_perform ();
int
main ()
{
return curl_multi_perform ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_curl_curl_multi_perform=yes
else
ac_cv_lib_curl_curl_multi_perform=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curl_curl_multi_perform" >&5
$as_echo "$ac_cv_lib_curl_curl_multi_perform" >&6; }
if test "x$ac_cv_lib_curl_curl_multi_perform" = xyes; then :
curl_ok=yes
else
curl_ok=no
fi
fi
if test $HAVE_LIBCURL = 1; then
for ac_header in curl/curl.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "curl/curl.h" "ac_cv_header_curl_curl_h" "$ac_includes_default"
if test "x$ac_cv_header_curl_curl_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_CURL_CURL_H 1
_ACEOF
curl_ok=yes
else
curl_ok=no
fi
done
fi
if test $HAVE_LIBCURL = 1; then
ac_fn_c_check_decl "$LINENO" "CURLOPT_CONNECT_TO" "ac_cv_have_decl_CURLOPT_CONNECT_TO" "#include <curl/curl.h>
"
if test "x$ac_cv_have_decl_CURLOPT_CONNECT_TO" = xyes; then :
curl_ok=yes
else
curl_ok=no
fi
fi
if test $HAVE_LIBCURL = 1; then
# If curl supports runtime selection of the TLS module we will need
# to use curl_global_sslset to pick GNUTLS
for ac_func in curl_global_sslset
do :
ac_fn_c_check_func "$LINENO" "curl_global_sslset" "ac_cv_func_curl_global_sslset"
if test "x$ac_cv_func_curl_global_sslset" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_CURL_GLOBAL_SSLSET 1
_ACEOF
fi
done
fi
#--------------------------------------------------------------------
# Check dependencies of NSURLSession API
#--------------------------------------------------------------------
nsurlsessiondefault=no
if test "$OBJC_RUNTIME_LIB" = "ng" -a $HAVE_BLOCKS = 1 -a $HAVE_LIBDISPATCH = 1 -a $HAVE_LIBCURL = 1; then
nsurlsessiondefault=yes
fi
GS_HAVE_NSURLSESSION=0
# Check whether --enable-nsurlsession was given.
if test "${enable_nsurlsession+set}" = set; then :
enableval=$enable_nsurlsession;
else
enable_nsurlsession=$nsurlsessiondefault
fi
if test $enable_nsurlsession = yes; then
if test "$OBJC_RUNTIME_LIB" != "ng"; then
as_fn_error $? "Missing ng runtime (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option." "$LINENO" 5
fi
if test $HAVE_BLOCKS = 0; then
as_fn_error $? "Missing blocks support (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option." "$LINENO" 5
fi
if test $HAVE_LIBDISPATCH = 0; then
as_fn_error $? "Missing libdispatch (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option." "$LINENO" 5
fi
# Check for dispatch_queue_create_with_target needed for NSURLSession
for ac_func in dispatch_queue_create_with_target
do :
ac_fn_c_check_func "$LINENO" "dispatch_queue_create_with_target" "ac_cv_func_dispatch_queue_create_with_target"
if test "x$ac_cv_func_dispatch_queue_create_with_target" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DISPATCH_QUEUE_CREATE_WITH_TARGET 1
_ACEOF
fi
done
if test $HAVE_LIBCURL = 0; then
as_fn_error $? "Missing libcurl (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option." "$LINENO" 5
fi
GS_HAVE_NSURLSESSION=1
fi
#--------------------------------------------------------------------
# Check GMP for NSDecimal
#--------------------------------------------------------------------
@ -13098,8 +13341,8 @@ case "$target_os" in
LDFLAGS="$LDFLAGS -L${gmp_libdir} -Wl,-R${gmp_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${gmp_libdir} -L${gmp_libdir}";;
*)
LDFLAGS="$LDFLAGS -L${gmp_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -L${gmp_libdir}";;
LDFLAGS="$LDFLAGS -Wl,-rpath,${gmp_libdir} -L${gmp_libdir}"
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${gmp_libdir} -L${gmp_libdir}";;
esac
fi

View file

@ -2837,7 +2837,7 @@ AC_ARG_WITH(ffi-library,
[ --with-ffi-library=PATH Library path for ffi libs],
ffi_libdir="$withval", ffi_libdir="no")
if test ${ffi_libdir} != "no"; then
GS_ADD_LIBRARY_PATH([${ffi_libdir}])
GS_ADD_LIBRARY_PATH([${ffi_libdir}])
fi
if test "$do_broken_libffi" = "no"; then
@ -3369,6 +3369,7 @@ AC_SUBST(HAVE_ICU)
# Check for libdispatch
# See DEPENDENCIES POLICY at the start of this file.
#--------------------------------------------------------------------
HAVE_LIBDISPATCH=0
AC_ARG_ENABLE(libdispatch,
[ --disable-libdispatch Disable dispatching blocks via libdispatch],
@ -3376,6 +3377,21 @@ AC_ARG_ENABLE(libdispatch,
enable_libdispatch=yes)
if test $enable_libdispatch = yes; then
AC_ARG_WITH(dispatch-include,
[ --with-dispatch-include=PATH Include path for dispatch header],
dispatch_incdir="$withval", dispatch_incdir="no")
if test ${dispatch_incdir} != "no"; then
CPPFLAGS="$CPPFLAGS -I${dispatch_incdir}"
INCLUDE_FLAGS="$INCLUDE_FLAGS -I${dispatch_incdir}"
fi
AC_ARG_WITH(dispatch-library,
[ --with-dispatch-library=PATH Library path for dispatch lib],
dispatch_libdir="$withval", dispatch_libdir="no")
if test ${dispatch_libdir} != "no"; then
GS_ADD_LIBRARY_PATH([${dispatch_libdir}])
fi
AC_CHECK_HEADERS(dispatch.h, have_dispatch=yes, have_dispatch=no)
if test "$have_dispatch" = "no"; then
AC_CHECK_HEADERS(dispatch/dispatch.h, have_dispatch=yes, have_dispatch=no)
@ -3402,8 +3418,8 @@ if test $enable_libdispatch = yes; then
fi
fi
else
HAVE_LIBDISPATCH=0;
# just ignore libdispatch if it's not there
HAVE_LIBDISPATCH=0;
# just ignore libdispatch if it's not there
fi
fi
AC_SUBST(HAVE_LIBDISPATCH)
@ -3425,6 +3441,106 @@ if test $HAVE_LIBDISPATCH = 1; then
fi
AC_SUBST(HAVE_LIBDISPATCH_RUNLOOP)
#--------------------------------------------------------------------
# Check for libcurl
# See DEPENDENCIES POLICY at the start of this file.
#--------------------------------------------------------------------
CURL_CONFIG="curl-config"
AC_ARG_WITH(curl, AS_HELP_STRING([--with-curl=<DIR>],
[use curl installed in directory <DIR>]))
if test "$with_curl" != ""; then
CURL_CONFIG="$with_curl/bin/curl-config"
fi
HAVE_LIBCURL=0
curl_all=no
AC_MSG_CHECKING([for libcurl])
if eval $CURL_CONFIG --version 2>/dev/null >/dev/null; then
curl_ver=`$CURL_CONFIG --version | sed -e "s/libcurl //g"`
curl_maj=`echo $curl_ver | sed -e "s/^\(.*\)\.\(.*\)\.\(.*\)$/\1/"`
curl_min=`echo $curl_ver | sed -e "s/^\(.*\)\.\(.*\)\.\(.*\)$/\2/"`
if test $curl_maj -lt 7 -o \( $curl_maj -eq 7 -a $curl_min -lt 49 \); then
AC_MSG_RESULT([FAILED (version too old to use])
else
CURLCFLAGS=`$CURL_CONFIG --cflags`
CURLLIBS=`$CURL_CONFIG --libs`
CFLAGS="$CFLAGS $CURLCFLAGS"
LIBS="$LIBS $CURLLIBS"
AC_MSG_RESULT(yes ... version $curl_ver)
HAVE_LIBCURL=1
curl_all=yes
if test \( $curl_maj -eq 7 -a $curl_min -lt 56 \); then
AC_MSG_WARN([LOSS OF FUNCTIONALITY (version too old for SSL backend control)])
curl_all=no
elif test \( $curl_maj -eq 7 -a $curl_min -lt 65 \); then
AC_MSG_WARN([LOSS OF FUNCTIONALITY (version too old for connection lifetime control)])
curl_all=no
fi
fi
else
AC_MSG_RESULT([FAILED (curl-config not found)])
fi
if test $HAVE_LIBCURL = 1; then
AC_MSG_CHECKING([for libcurl TLS support with gnutls])
if $CURL_CONFIG --configure | grep -q with-gnutls; then
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
curl_all=no
AC_MSG_WARN([LOSS OF FUNCTIONALITY (curl lacks gnutls support)])
fi
fi
if test $HAVE_LIBCURL = 1; then
AC_CHECK_LIB(curl, curl_multi_perform, curl_ok=yes, curl_ok=no)
fi
if test $HAVE_LIBCURL = 1; then
AC_CHECK_HEADERS(curl/curl.h, curl_ok=yes, curl_ok=no)
fi
if test $HAVE_LIBCURL = 1; then
AC_CHECK_DECL(CURLOPT_CONNECT_TO, curl_ok=yes, curl_ok=no,
[#include <curl/curl.h>])
fi
if test $HAVE_LIBCURL = 1; then
# If curl supports runtime selection of the TLS module we will need
# to use curl_global_sslset to pick GNUTLS
AC_CHECK_FUNCS(curl_global_sslset)
fi
AC_SUBST(HAVE_LIBCURL)
#--------------------------------------------------------------------
# Check dependencies of NSURLSession API
#--------------------------------------------------------------------
nsurlsessiondefault=no
if test "$OBJC_RUNTIME_LIB" = "ng" -a $HAVE_BLOCKS = 1 -a $HAVE_LIBDISPATCH = 1 -a $HAVE_LIBCURL = 1; then
nsurlsessiondefault=yes
fi
GS_HAVE_NSURLSESSION=0
AC_ARG_ENABLE(nsurlsession,
[ --disable-nsurlsession Disable support for NSURLSession],,
enable_nsurlsession=$nsurlsessiondefault)
if test $enable_nsurlsession = yes; then
if test "$OBJC_RUNTIME_LIB" != "ng"; then
AC_MSG_ERROR([Missing ng runtime (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option.])
fi
if test $HAVE_BLOCKS = 0; then
AC_MSG_ERROR([Missing blocks support (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option.])
fi
if test $HAVE_LIBDISPATCH = 0; then
AC_MSG_ERROR([Missing libdispatch (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option.])
fi
# Check for dispatch_queue_create_with_target needed for NSURLSession
AC_CHECK_FUNCS(dispatch_queue_create_with_target)
if test $HAVE_LIBCURL = 0; then
AC_MSG_ERROR([Missing libcurl (needed for NSURLSession). To build without NSURLSession support, please run configure again with the --disable-nsurlsession option.])
fi
GS_HAVE_NSURLSESSION=1
fi
AC_SUBST(GS_HAVE_NSURLSESSION)
#--------------------------------------------------------------------
# Check GMP for NSDecimal
#--------------------------------------------------------------------