mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-30 08:21:25 +00:00
Merge changes for NSURLSession from EngageHub (formerly Brainstorm).
This commit is contained in:
parent
d015cebbf3
commit
1b7bf26bea
70 changed files with 7980 additions and 192 deletions
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
language: cpp
|
language: cpp
|
||||||
dist: xenial
|
dist: focal
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- name: "Linux GCC"
|
- name: "Linux GCC"
|
||||||
|
@ -44,7 +44,7 @@ before_install: |
|
||||||
if [ $CC = 'gcc' ]; then
|
if [ $CC = 'gcc' ]; then
|
||||||
sudo apt-get install -y gobjc
|
sudo apt-get install -y gobjc
|
||||||
fi
|
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)
|
ng-gnu-gnu)
|
||||||
curl -s -o - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
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
|
export LD_LIBRARY_PATH=$DEP_ROOT/lib:$DEP_ROOT/lib64:$LD_LIBRARY_PATH
|
||||||
case $LIBRARY_COMBO in
|
case $LIBRARY_COMBO in
|
||||||
gnu-gnu-gnu)
|
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)
|
ng-gnu-gnu)
|
||||||
export CPATH=$DEP_ROOT/include
|
export CPATH=$DEP_ROOT/include
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Interface to debugging utilities for GNUStep and OpenStep
|
/* 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>
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||||
Date: August 1997
|
Date: August 1997
|
||||||
|
@ -44,6 +44,35 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
* Functions for debugging object allocation/deallocation
|
||||||
*
|
*
|
||||||
|
|
|
@ -196,6 +196,19 @@ typedef enum
|
||||||
|
|
||||||
@end
|
@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)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -71,6 +71,7 @@ enum
|
||||||
NSURLErrorFileDoesNotExist = -1100,
|
NSURLErrorFileDoesNotExist = -1100,
|
||||||
NSURLErrorFileIsDirectory = -1101,
|
NSURLErrorFileIsDirectory = -1101,
|
||||||
NSURLErrorNoPermissionsToReadFile = -1102,
|
NSURLErrorNoPermissionsToReadFile = -1102,
|
||||||
|
NSURLErrorDataLengthExceedsMaximum = -1103,
|
||||||
NSURLErrorSecureConnectionFailed = -1200,
|
NSURLErrorSecureConnectionFailed = -1200,
|
||||||
NSURLErrorServerCertificateHasBadDate = -1201,
|
NSURLErrorServerCertificateHasBadDate = -1201,
|
||||||
NSURLErrorServerCertificateUntrusted = -1202,
|
NSURLErrorServerCertificateUntrusted = -1202,
|
||||||
|
|
|
@ -44,6 +44,7 @@ extern "C" {
|
||||||
@class NSURLProtocol;
|
@class NSURLProtocol;
|
||||||
@class NSURLRequest;
|
@class NSURLRequest;
|
||||||
@class NSURLResponse;
|
@class NSURLResponse;
|
||||||
|
@class NSURLSessionTask;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,11 +179,20 @@ extern "C" {
|
||||||
cachedResponse: (NSCachedURLResponse *)cachedResponse
|
cachedResponse: (NSCachedURLResponse *)cachedResponse
|
||||||
client: (id <NSURLProtocolClient>)client;
|
client: (id <NSURLProtocolClient>)client;
|
||||||
|
|
||||||
|
- (instancetype) initWithTask: (NSURLSessionTask*)task
|
||||||
|
cachedResponse: (NSCachedURLResponse*)cachedResponse
|
||||||
|
client: (id<NSURLProtocolClient>)client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request handled by the receiver.
|
* Returns the request handled by the receiver.
|
||||||
*/
|
*/
|
||||||
- (NSURLRequest *) request;
|
- (NSURLRequest *) request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the task handled by the receiver.
|
||||||
|
*/
|
||||||
|
- (NSURLSessionTask *) task;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -283,6 +283,25 @@ typedef NSUInteger NSURLRequestCachePolicy;
|
||||||
|
|
||||||
@end
|
@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)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,18 +2,240 @@
|
||||||
#define __NSURLSession_h_GNUSTEP_BASE_INCLUDE
|
#define __NSURLSession_h_GNUSTEP_BASE_INCLUDE
|
||||||
|
|
||||||
#import <Foundation/NSObject.h>
|
#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)
|
#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
|
||||||
@protocol NSURLSessionDelegate;
|
@protocol NSURLSessionDelegate;
|
||||||
@protocol NSURLSessionTaskDelegate;
|
@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
|
@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
|
@end
|
||||||
|
|
||||||
@interface NSURLSessionConfiguration : NSObject <NSCopying>
|
typedef NS_ENUM(NSUInteger, NSURLSessionTaskState) {
|
||||||
@end
|
/* 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>
|
@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
|
@end
|
||||||
|
|
||||||
@interface NSURLSessionDataTask : NSURLSessionTask
|
@interface NSURLSessionDataTask : NSURLSessionTask
|
||||||
|
@ -30,11 +252,177 @@
|
||||||
@end
|
@end
|
||||||
#endif
|
#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>
|
@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
|
@end
|
||||||
|
|
||||||
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
|
@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
|
@end
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
|
@ -267,8 +267,10 @@ typedef struct {
|
||||||
#define GS_USE_AVAHI @HAVE_AVAHI@
|
#define GS_USE_AVAHI @HAVE_AVAHI@
|
||||||
#define GS_USE_MDNS @HAVE_MDNS@
|
#define GS_USE_MDNS @HAVE_MDNS@
|
||||||
#define GS_USE_ICU @HAVE_ICU@
|
#define GS_USE_ICU @HAVE_ICU@
|
||||||
|
#define GS_USE_LIBCURL @HAVE_LIBCURL@
|
||||||
#define GS_USE_LIBDISPATCH @HAVE_LIBDISPATCH@
|
#define GS_USE_LIBDISPATCH @HAVE_LIBDISPATCH@
|
||||||
#define GS_USE_LIBDISPATCH_RUNLOOP @HAVE_LIBDISPATCH_RUNLOOP@
|
#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@
|
#define GS_HAVE_OBJC_ROOT_CLASS_ATTR @GS_HAVE_OBJC_ROOT_CLASS_ATTR@
|
||||||
|
|
||||||
#ifndef __has_include
|
#ifndef __has_include
|
||||||
|
|
|
@ -189,6 +189,12 @@
|
||||||
/* Define to 1 if you have the `ctime' function. */
|
/* Define to 1 if you have the `ctime' function. */
|
||||||
#undef HAVE_CTIME
|
#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 */
|
/* Define if you have currency_symbol field in struct lconv */
|
||||||
#undef HAVE_CURRENCY_SYMBOL_IN_LCONV
|
#undef HAVE_CURRENCY_SYMBOL_IN_LCONV
|
||||||
|
|
||||||
|
@ -219,6 +225,10 @@
|
||||||
/* Define to 1 if you have the <dispatch/private.h> header file. */
|
/* Define to 1 if you have the <dispatch/private.h> header file. */
|
||||||
#undef HAVE_DISPATCH_PRIVATE_H
|
#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. */
|
/* Define to 1 if you have the `dladdr' function. */
|
||||||
#undef HAVE_DLADDR
|
#undef HAVE_DLADDR
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,4 +67,4 @@ ifeq ($(FOUNDATION_LIB),gnu)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Additional LDFLAGS to pass to the linker
|
# Additional LDFLAGS to pass to the linker
|
||||||
ADDITIONAL_LDFLAGS =
|
ADDITIONAL_LDFLAGS =
|
||||||
|
|
|
@ -331,7 +331,6 @@ NSUbiquitousKeyValueStore.m \
|
||||||
NSUnarchiver.m \
|
NSUnarchiver.m \
|
||||||
NSUndoManager.m \
|
NSUndoManager.m \
|
||||||
NSURL.m \
|
NSURL.m \
|
||||||
NSURLSession.m \
|
|
||||||
NSURLAuthenticationChallenge.m \
|
NSURLAuthenticationChallenge.m \
|
||||||
NSURLCache.m \
|
NSURLCache.m \
|
||||||
NSURLCredential.m \
|
NSURLCredential.m \
|
||||||
|
@ -362,33 +361,53 @@ objc-load.m
|
||||||
|
|
||||||
ifneq ($(GNUSTEP_TARGET_OS), mingw32)
|
ifneq ($(GNUSTEP_TARGET_OS), mingw32)
|
||||||
ifneq ($(GNUSTEP_TARGET_OS), mingw64)
|
ifneq ($(GNUSTEP_TARGET_OS), mingw64)
|
||||||
BASE_MFILES += \
|
BASE_MFILES += \
|
||||||
GSFileHandle.m \
|
GSFileHandle.m \
|
||||||
NSMessagePort.m \
|
NSMessagePort.m \
|
||||||
NSMessagePortNameServer.m
|
NSMessagePortNameServer.m
|
||||||
endif
|
endif
|
||||||
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)
|
ifeq ($(GNUSTEP_BASE_HAVE_MDNS), 1)
|
||||||
BASE_MFILES += NSNetServices.m \
|
BASE_MFILES += \
|
||||||
GSMDNSNetServices.m
|
GSMDNSNetServices.m \
|
||||||
|
NSNetServices.m
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(GNUSTEP_BASE_HAVE_AVAHI), 1)
|
ifeq ($(GNUSTEP_BASE_HAVE_AVAHI), 1)
|
||||||
BASE_MFILES += NSNetServices.m \
|
BASE_MFILES += \
|
||||||
GSAvahiNetService.m \
|
GSAvahiNetService.m \
|
||||||
GSAvahiNetServiceBrowser.m \
|
GSAvahiNetServiceBrowser.m \
|
||||||
GSAvahiClient.m \
|
GSAvahiClient.m \
|
||||||
GSAvahiRunLoopIntegration.m
|
GSAvahiRunLoopIntegration.m \
|
||||||
|
NSNetServices.m
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(WITH_FFI),libffi)
|
ifeq ($(WITH_FFI),libffi)
|
||||||
GNU_MFILES += cifframe.m
|
GNU_MFILES += cifframe.m
|
||||||
BASE_MFILES += GSFFIInvocation.m
|
BASE_MFILES += GSFFIInvocation.m
|
||||||
endif
|
endif
|
||||||
ifeq ($(WITH_FFI),ffcall)
|
ifeq ($(WITH_FFI),ffcall)
|
||||||
GNU_MFILES += callframe.m
|
GNU_MFILES += callframe.m
|
||||||
BASE_MFILES += GSFFCallInvocation.m
|
BASE_MFILES += GSFFCallInvocation.m
|
||||||
endif
|
endif
|
||||||
|
|
||||||
BASE_OTHER_SRCFILES = \
|
BASE_OTHER_SRCFILES = \
|
||||||
|
|
236
Source/GSEasyHandle.h
Normal file
236
Source/GSEasyHandle.h
Normal 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
756
Source/GSEasyHandle.m
Normal 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
|
|
@ -108,6 +108,7 @@ static NSString *httpVersion = @"1.1";
|
||||||
BOOL debug;
|
BOOL debug;
|
||||||
BOOL keepalive;
|
BOOL keepalive;
|
||||||
BOOL returnAll;
|
BOOL returnAll;
|
||||||
|
id<GSLogDelegate> ioDelegate;
|
||||||
unsigned char challenged;
|
unsigned char challenged;
|
||||||
NSFileHandle *sock;
|
NSFileHandle *sock;
|
||||||
NSTimeInterval cacheAge;
|
NSTimeInterval cacheAge;
|
||||||
|
@ -135,6 +136,7 @@ static NSString *httpVersion = @"1.1";
|
||||||
}
|
}
|
||||||
+ (void) setMaxCached: (NSUInteger)limit;
|
+ (void) setMaxCached: (NSUInteger)limit;
|
||||||
- (void) _tryLoadInBackground: (NSURL*)fromURL;
|
- (void) _tryLoadInBackground: (NSURL*)fromURL;
|
||||||
|
- (id<GSLogDelegate>) setDebugLogDelegate: (id<GSLogDelegate>)d;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -601,7 +603,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
/*
|
/*
|
||||||
* Send request to server.
|
* 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];
|
[sock writeInBackgroundAndNotify: buf];
|
||||||
RELEASE(buf);
|
RELEASE(buf);
|
||||||
RELEASE(s);
|
RELEASE(s);
|
||||||
|
@ -621,8 +631,16 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
if (debug)
|
if (debug)
|
||||||
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
|
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
|
||||||
d = [dict objectForKey: NSFileHandleNotificationDataItem];
|
d = [dict objectForKey: NSFileHandleNotificationDataItem];
|
||||||
if (YES == debug) debugRead(self, d);
|
|
||||||
readCount = [d length];
|
readCount = [d length];
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
if (NO == [ioDelegate getBytes: [d bytes]
|
||||||
|
ofLength: readCount
|
||||||
|
byHandle: self])
|
||||||
|
{
|
||||||
|
debugRead(self, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (connectionState == idle)
|
if (connectionState == idle)
|
||||||
{
|
{
|
||||||
|
@ -631,11 +649,32 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
* it should just be the connection being closed by the other
|
* it should just be the connection being closed by the other
|
||||||
* end because of a timeout etc.
|
* end because of a timeout etc.
|
||||||
*/
|
*/
|
||||||
if (YES == debug && [d length] != 0)
|
if (debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ %p %s Unexpected data (%*.*s) from remote!",
|
NSUInteger length = [d length];
|
||||||
NSStringFromSelector(_cmd), self, keepalive?"K":"",
|
|
||||||
(int)[d length], (int)[d length], [d bytes]);
|
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];
|
[nc removeObserver: self name: nil object: sock];
|
||||||
[sock closeFile];
|
[sock closeFile];
|
||||||
|
@ -643,7 +682,7 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
}
|
}
|
||||||
else if ([parser parse: d] == NO && [parser isComplete] == NO)
|
else if ([parser parse: d] == NO && [parser isComplete] == NO)
|
||||||
{
|
{
|
||||||
if (YES == debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
NSLog(@"HTTP parse failure - %@", parser);
|
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
|
* lost in the network or the remote end received it and
|
||||||
* the response was lost.
|
* the response was lost.
|
||||||
*/
|
*/
|
||||||
if (YES == debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
NSLog(@"HTTP response not received - %@", parser);
|
NSLog(@"HTTP response not received - %@", parser);
|
||||||
}
|
}
|
||||||
|
@ -906,7 +945,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
|
NSLog(@"%@ %p %s", NSStringFromSelector(_cmd), self, keepalive?"K":"");
|
||||||
}
|
}
|
||||||
d = [dict objectForKey: NSFileHandleNotificationDataItem];
|
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)
|
if ([d length] > 0)
|
||||||
{
|
{
|
||||||
|
@ -1071,7 +1118,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
object: sock];
|
object: sock];
|
||||||
|
|
||||||
buf = [cmd dataUsingEncoding: NSASCIIStringEncoding];
|
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];
|
[sock writeInBackgroundAndNotify: buf];
|
||||||
|
|
||||||
when = [NSDate alloc];
|
when = [NSDate alloc];
|
||||||
|
@ -1365,6 +1420,16 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
||||||
return old;
|
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
|
- (void) setReturnAll: (BOOL)flag
|
||||||
{
|
{
|
||||||
returnAll = flag;
|
returnAll = flag;
|
||||||
|
|
9
Source/GSHTTPURLProtocol.h
Normal file
9
Source/GSHTTPURLProtocol.h
Normal 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
1064
Source/GSHTTPURLProtocol.m
Normal file
File diff suppressed because it is too large
Load diff
91
Source/GSMultiHandle.h
Normal file
91
Source/GSMultiHandle.h
Normal 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
514
Source/GSMultiHandle.m
Normal 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
91
Source/GSNativeProtocol.h
Normal 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
768
Source/GSNativeProtocol.m
Normal 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
|
||||||
|
|
||||||
|
|
|
@ -2180,11 +2180,6 @@ retrieve_callback(gnutls_session_t session,
|
||||||
[str appendFormat: _(@"- Certificate Type: %s\n"), tmp];
|
[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.
|
/* print the name of the cipher used.
|
||||||
* eg 3DES.
|
* eg 3DES.
|
||||||
*/
|
*/
|
||||||
|
|
30
Source/GSTaskRegistry.h
Normal file
30
Source/GSTaskRegistry.h
Normal 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
98
Source/GSTaskRegistry.m
Normal 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
32
Source/GSTimeoutSource.h
Normal 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
48
Source/GSTimeoutSource.m
Normal 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
142
Source/GSTransferState.h
Normal 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
487
Source/GSTransferState.m
Normal 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
|
|
@ -58,6 +58,7 @@
|
||||||
|
|
||||||
@interface NSURLRequest (Private)
|
@interface NSURLRequest (Private)
|
||||||
- (BOOL) _debug;
|
- (BOOL) _debug;
|
||||||
|
- (id<GSLogDelegate>) _debugLogDelegate;
|
||||||
- (id) _propertyForKey: (NSString*)key;
|
- (id) _propertyForKey: (NSString*)key;
|
||||||
- (void) _setProperty: (id)value forKey: (NSString*)key;
|
- (void) _setProperty: (id)value forKey: (NSString*)key;
|
||||||
@end
|
@end
|
||||||
|
@ -72,11 +73,23 @@
|
||||||
|
|
||||||
|
|
||||||
@interface NSURLProtocol (Private)
|
@interface NSURLProtocol (Private)
|
||||||
+ (Class) _classToHandleRequest:(NSURLRequest *)request;
|
+ (Class) _classToHandleRequest: (NSURLRequest *)request;
|
||||||
|
+ (id<NSURLProtocolClient>) _ProtocolClient;
|
||||||
@end
|
@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;
|
@class NSLock;
|
||||||
@interface GSHTTPAuthentication : NSObject
|
@interface GSHTTPAuthentication : NSObject
|
||||||
|
|
46
Source/GSURLSessionTaskBody.h
Normal file
46
Source/GSURLSessionTaskBody.h
Normal 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
|
111
Source/GSURLSessionTaskBody.m
Normal file
111
Source/GSURLSessionTaskBody.m
Normal 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
|
52
Source/GSURLSessionTaskBodySource.h
Normal file
52
Source/GSURLSessionTaskBodySource.h
Normal 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
|
152
Source/GSURLSessionTaskBodySource.m
Normal file
152
Source/GSURLSessionTaskBodySource.m
Normal 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
|
|
@ -62,7 +62,7 @@ acquireExistingMemory(const void *item,
|
||||||
static NSString*
|
static NSString*
|
||||||
describeString(const void *item)
|
describeString(const void *item)
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat: @"%s", item];
|
return [NSString stringWithFormat: @"%s", (char*)item];
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSString*
|
static NSString*
|
||||||
|
|
|
@ -687,6 +687,14 @@ recover(int sig)
|
||||||
siglongjmp(jbuf()->buf, 1);
|
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 *
|
void *
|
||||||
NSFrameAddress(NSUInteger offset)
|
NSFrameAddress(NSUInteger offset)
|
||||||
{
|
{
|
||||||
|
@ -873,6 +881,12 @@ NSReturnAddress(NSUInteger offset)
|
||||||
return env->addr;
|
return env->addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#else
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
GSPrivateReturnAddresses(NSUInteger **returns)
|
GSPrivateReturnAddresses(NSUInteger **returns)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1412,7 +1412,7 @@ typedef struct {
|
||||||
|
|
||||||
desc = [NSString stringWithFormat:
|
desc = [NSString stringWithFormat:
|
||||||
@"<%s %p file name %s>",
|
@"<%s %p file name %s>",
|
||||||
GSClassNameFromObject(self), self, [name bytes]];
|
GSClassNameFromObject(self), self, (char*)[name bytes]];
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1469,7 +1469,7 @@ static Class tcpPortClass;
|
||||||
* Look up an existing NSSocketPort given a host and number
|
* Look up an existing NSSocketPort given a host and number
|
||||||
*/
|
*/
|
||||||
+ (NSSocketPort*) existingPortWithNumber: (uint16_t)number
|
+ (NSSocketPort*) existingPortWithNumber: (uint16_t)number
|
||||||
onHost: (NSHost*)aHost
|
onHost: (NSHost*)aHost
|
||||||
{
|
{
|
||||||
NSSocketPort *port = nil;
|
NSSocketPort *port = nil;
|
||||||
NSMapTable *thePorts;
|
NSMapTable *thePorts;
|
||||||
|
@ -1763,6 +1763,9 @@ static Class tcpPortClass;
|
||||||
{
|
{
|
||||||
NSDebugMLLog(@"NSPort",
|
NSDebugMLLog(@"NSPort",
|
||||||
@"NSSocketPort 0x%"PRIxPTR" finalized", (NSUInteger)self);
|
@"NSSocketPort 0x%"PRIxPTR" finalized", (NSUInteger)self);
|
||||||
|
M_LOCK(tcpPortLock);
|
||||||
|
NSMapRemove(tcpPortMap, (void*)(uintptr_t)portNum);
|
||||||
|
M_UNLOCK(tcpPortLock);
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
if (handles != 0)
|
if (handles != 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
|
|
||||||
#import "common.h"
|
#import "common.h"
|
||||||
|
|
||||||
|
#if GS_HAVE_NSURLSESSION
|
||||||
|
#import <Foundation/NSURLSession.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define EXPOSE_NSURLCache_IVARS 1
|
#define EXPOSE_NSURLCache_IVARS 1
|
||||||
#import "GSURLPrivate.h"
|
#import "GSURLPrivate.h"
|
||||||
|
|
||||||
|
@ -215,3 +219,27 @@ static NSURLCache *shared = nil;
|
||||||
|
|
||||||
@end
|
@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
|
||||||
|
|
||||||
|
|
|
@ -169,6 +169,11 @@ typedef struct
|
||||||
DESTROY(this->_delegate);
|
DESTROY(this->_delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id) delegate
|
||||||
|
{
|
||||||
|
return this->_delegate;
|
||||||
|
}
|
||||||
|
|
||||||
- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop
|
- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop
|
||||||
forMode: (NSRunLoopMode)mode
|
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)
|
if ((self = [super init]) != nil)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,13 +25,19 @@
|
||||||
|
|
||||||
#import "common.h"
|
#import "common.h"
|
||||||
|
|
||||||
#define EXPOSE_NSURLProtocol_IVARS 1
|
//#define EXPOSE_NSURLProtocol_IVARS 1
|
||||||
#import "Foundation/NSError.h"
|
#import "Foundation/NSError.h"
|
||||||
#import "Foundation/NSHost.h"
|
#import "Foundation/NSHost.h"
|
||||||
#import "Foundation/NSNotification.h"
|
#import "Foundation/NSNotification.h"
|
||||||
#import "Foundation/NSRunLoop.h"
|
#import "Foundation/NSRunLoop.h"
|
||||||
#import "Foundation/NSValue.h"
|
#import "Foundation/NSValue.h"
|
||||||
|
|
||||||
|
#if GS_HAVE_NSURLSESSION
|
||||||
|
#import "Foundation/NSURLSession.h"
|
||||||
|
#else
|
||||||
|
@class NSURLSessionTask;
|
||||||
|
#endif
|
||||||
|
|
||||||
#import "GSPrivate.h"
|
#import "GSPrivate.h"
|
||||||
#import "GSURLPrivate.h"
|
#import "GSURLPrivate.h"
|
||||||
#import "GNUstepBase/GSMime.h"
|
#import "GNUstepBase/GSMime.h"
|
||||||
|
@ -358,6 +364,7 @@ static NSLock *pairLock = nil;
|
||||||
NSURLAuthenticationChallenge *_challenge;
|
NSURLAuthenticationChallenge *_challenge;
|
||||||
NSURLCredential *_credential;
|
NSURLCredential *_credential;
|
||||||
NSHTTPURLResponse *_response;
|
NSHTTPURLResponse *_response;
|
||||||
|
id<GSLogDelegate> _logDelegate;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -367,27 +374,6 @@ static NSLock *pairLock = nil;
|
||||||
@interface _NSDataURLProtocol : NSURLProtocol
|
@interface _NSDataURLProtocol : NSURLProtocol
|
||||||
@end
|
@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 NSMutableArray *registered = nil;
|
||||||
static NSLock *regLock = nil;
|
static NSLock *regLock = nil;
|
||||||
static Class abstractClass = nil;
|
static Class abstractClass = nil;
|
||||||
|
@ -419,8 +405,44 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation NSURLProtocol
|
@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
|
+ (id) allocWithZone: (NSZone*)z
|
||||||
{
|
{
|
||||||
NSURLProtocol *o;
|
NSURLProtocol *o;
|
||||||
|
@ -508,7 +530,6 @@ static NSURLProtocol *placeholder = nil;
|
||||||
{
|
{
|
||||||
if (this != 0)
|
if (this != 0)
|
||||||
{
|
{
|
||||||
[self stopLoading];
|
|
||||||
if (this->input != nil)
|
if (this->input != nil)
|
||||||
{
|
{
|
||||||
[this->input setDelegate: nil];
|
[this->input setDelegate: nil];
|
||||||
|
@ -526,7 +547,11 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
DESTROY(this->cachedResponse);
|
DESTROY(this->cachedResponse);
|
||||||
DESTROY(this->request);
|
DESTROY(this->request);
|
||||||
|
#if GS_HAVE_NSURLSESSION
|
||||||
|
DESTROY(this->task);
|
||||||
|
#endif
|
||||||
DESTROY(this->client);
|
DESTROY(this->client);
|
||||||
|
|
||||||
#if USE_ZLIB
|
#if USE_ZLIB
|
||||||
if (this->compressing == YES)
|
if (this->compressing == YES)
|
||||||
{
|
{
|
||||||
|
@ -538,8 +563,10 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
DESTROY(this->compressed);
|
DESTROY(this->compressed);
|
||||||
#endif
|
#endif
|
||||||
|
#if !GS_NONFRAGILE
|
||||||
NSZoneFree([self zone], this);
|
NSZoneFree([self zone], this);
|
||||||
_NSURLProtocolInternal = 0;
|
_NSURLProtocolInternal = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
@ -554,6 +581,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
{
|
{
|
||||||
if ((self = [super init]) != nil)
|
if ((self = [super init]) != nil)
|
||||||
{
|
{
|
||||||
|
#if !GS_NONFRAGILE
|
||||||
Class c = object_getClass(self);
|
Class c = object_getClass(self);
|
||||||
|
|
||||||
if (c != abstractClass && c != placeholderClass)
|
if (c != abstractClass && c != placeholderClass)
|
||||||
|
@ -561,13 +589,14 @@ static NSURLProtocol *placeholder = nil;
|
||||||
_NSURLProtocolInternal = NSZoneCalloc([self zone],
|
_NSURLProtocolInternal = NSZoneCalloc([self zone],
|
||||||
1, sizeof(Internal));
|
1, sizeof(Internal));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) initWithRequest: (NSURLRequest *)request
|
- (id) initWithRequest: (NSURLRequest*)_request
|
||||||
cachedResponse: (NSCachedURLResponse *)cachedResponse
|
cachedResponse: (NSCachedURLResponse*)_cachedResponse
|
||||||
client: (id <NSURLProtocolClient>)client
|
client: (id <NSURLProtocolClient>)_client
|
||||||
{
|
{
|
||||||
Class c = object_getClass(self);
|
Class c = object_getClass(self);
|
||||||
|
|
||||||
|
@ -582,31 +611,57 @@ static NSURLProtocol *placeholder = nil;
|
||||||
{
|
{
|
||||||
Class proto = [registered objectAtIndex: count];
|
Class proto = [registered objectAtIndex: count];
|
||||||
|
|
||||||
if ([proto canInitWithRequest: request] == YES)
|
if ([proto canInitWithRequest: _request] == YES)
|
||||||
{
|
{
|
||||||
self = [proto alloc];
|
self = [proto alloc];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[regLock unlock];
|
[regLock unlock];
|
||||||
return [self initWithRequest: request
|
return [self initWithRequest: _request
|
||||||
cachedResponse: cachedResponse
|
cachedResponse: _cachedResponse
|
||||||
client: client];
|
client: _client];
|
||||||
}
|
}
|
||||||
if ((self = [self init]) != nil)
|
if ((self = [self init]) != nil)
|
||||||
{
|
{
|
||||||
this->request = [request copy];
|
this->request = [_request copy];
|
||||||
this->cachedResponse = RETAIN(cachedResponse);
|
this->cachedResponse = RETAIN(_cachedResponse);
|
||||||
this->client = RETAIN(client);
|
if (nil == _client)
|
||||||
|
{
|
||||||
|
_client = [[self class] _ProtocolClient];
|
||||||
|
}
|
||||||
|
this->client = RETAIN(_client);
|
||||||
}
|
}
|
||||||
return self;
|
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
|
- (NSURLRequest *) request
|
||||||
{
|
{
|
||||||
return this->request;
|
return this->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSURLSessionTask*) task
|
||||||
|
{
|
||||||
|
return this->task;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSURLProtocol (Debug)
|
@implementation NSURLProtocol (Debug)
|
||||||
|
@ -643,6 +698,13 @@ static NSURLProtocol *placeholder = nil;
|
||||||
return protoClass;
|
return protoClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Internal method to return a client to handle callbacks if the protocol
|
||||||
|
* is initialised without one.
|
||||||
|
*/
|
||||||
|
+ (id<NSURLProtocolClient>) _ProtocolClient
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSURLProtocol (Subclassing)
|
@implementation NSURLProtocol (Subclassing)
|
||||||
|
@ -726,7 +788,14 @@ static NSURLProtocol *placeholder = nil;
|
||||||
static NSDictionary *methods = nil;
|
static NSDictionary *methods = nil;
|
||||||
|
|
||||||
_debug = GSDebugSet(@"NSURLProtocol");
|
_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)
|
if (methods == nil)
|
||||||
{
|
{
|
||||||
|
@ -795,12 +864,12 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NSMutableURLRequest *request;
|
NSMutableURLRequest *r;
|
||||||
|
|
||||||
request = [[this->request mutableCopy] autorelease];
|
r = [[this->request mutableCopy] autorelease];
|
||||||
[request setURL: url];
|
[r setURL: url];
|
||||||
[this->client URLProtocol: self
|
[this->client URLProtocol: self
|
||||||
wasRedirectedToRequest: request
|
wasRedirectedToRequest: r
|
||||||
redirectResponse: nil];
|
redirectResponse: nil];
|
||||||
}
|
}
|
||||||
if (NO == _isLoading)
|
if (NO == _isLoading)
|
||||||
|
@ -979,7 +1048,12 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
if (_debug)
|
if (_debug)
|
||||||
{
|
{
|
||||||
debugRead(self, readCount, buffer);
|
if (NO == [_logDelegate getBytes: buffer
|
||||||
|
ofLength: readCount
|
||||||
|
byHandle: self])
|
||||||
|
{
|
||||||
|
debugRead(self, readCount, buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_parser == nil)
|
if (_parser == nil)
|
||||||
|
@ -991,7 +1065,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
d = [NSData dataWithBytes: buffer length: readCount];
|
d = [NSData dataWithBytes: buffer length: readCount];
|
||||||
if ([_parser parse: d] == NO && (_complete = [_parser isComplete]) == NO)
|
if ([_parser parse: d] == NO && (_complete = [_parser isComplete]) == NO)
|
||||||
{
|
{
|
||||||
if (_debug == YES)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ HTTP parse failure - %@", self, _parser);
|
NSLog(@"%@ HTTP parse failure - %@", self, _parser);
|
||||||
}
|
}
|
||||||
|
@ -1113,12 +1187,12 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NSMutableURLRequest *request;
|
NSMutableURLRequest *r;
|
||||||
|
|
||||||
request = [[this->request mutableCopy] autorelease];
|
r = AUTORELEASE([this->request mutableCopy]);
|
||||||
[request setURL: url];
|
[r setURL: url];
|
||||||
[this->client URLProtocol: self
|
[this->client URLProtocol: self
|
||||||
wasRedirectedToRequest: request
|
wasRedirectedToRequest: r
|
||||||
redirectResponse: _response];
|
redirectResponse: _response];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1335,18 +1409,18 @@ static NSURLProtocol *placeholder = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NSMutableURLRequest *request;
|
NSMutableURLRequest *r;
|
||||||
|
|
||||||
/* To answer the authentication challenge,
|
/* To answer the authentication challenge,
|
||||||
* we must retry with a modified request and
|
* we must retry with a modified request and
|
||||||
* with the cached response cleared.
|
* with the cached response cleared.
|
||||||
*/
|
*/
|
||||||
request = [this->request mutableCopy];
|
r = [this->request mutableCopy];
|
||||||
[request setValue: auth
|
[r setValue: auth
|
||||||
forHTTPHeaderField: @"Authorization"];
|
forHTTPHeaderField: @"Authorization"];
|
||||||
[self stopLoading];
|
[self stopLoading];
|
||||||
[this->request release];
|
RELEASE(this->request);
|
||||||
this->request = request;
|
this->request = r;
|
||||||
DESTROY(this->cachedResponse);
|
DESTROY(this->cachedResponse);
|
||||||
[self startLoading];
|
[self startLoading];
|
||||||
return;
|
return;
|
||||||
|
@ -1426,7 +1500,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
* lost in the network or the remote end received it and
|
* lost in the network or the remote end received it and
|
||||||
* the response was lost.
|
* the response was lost.
|
||||||
*/
|
*/
|
||||||
if (_debug == YES)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ HTTP response not received - %@", self, _parser);
|
NSLog(@"%@ HTTP response not received - %@", self, _parser);
|
||||||
}
|
}
|
||||||
|
@ -1460,7 +1534,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case NSStreamEventOpenCompleted:
|
case NSStreamEventOpenCompleted:
|
||||||
if (_debug == YES)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ HTTP input stream opened", self);
|
NSLog(@"%@ HTTP input stream opened", self);
|
||||||
}
|
}
|
||||||
|
@ -1483,7 +1557,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
NSURL *u;
|
NSURL *u;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
if (_debug == YES)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ HTTP output stream opened", self);
|
NSLog(@"%@ HTTP output stream opened", self);
|
||||||
}
|
}
|
||||||
|
@ -1624,9 +1698,14 @@ static NSURLProtocol *placeholder = nil;
|
||||||
maxLength: len - _writeOffset];
|
maxLength: len - _writeOffset];
|
||||||
if (written > 0)
|
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;
|
_writeOffset += written;
|
||||||
if (_writeOffset >= len)
|
if (_writeOffset >= len)
|
||||||
|
@ -1664,7 +1743,7 @@ static NSURLProtocol *placeholder = nil;
|
||||||
len = [_body read: buffer maxLength: sizeof(buffer)];
|
len = [_body read: buffer maxLength: sizeof(buffer)];
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
if (_debug == YES)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSLog(@"%@ error reading from HTTPBody stream %@",
|
NSLog(@"%@ error reading from HTTPBody stream %@",
|
||||||
self, [NSError _last]);
|
self, [NSError _last]);
|
||||||
|
@ -1681,9 +1760,14 @@ static NSURLProtocol *placeholder = nil;
|
||||||
written = [this->output write: buffer maxLength: len];
|
written = [this->output write: buffer maxLength: len];
|
||||||
if (written > 0)
|
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;
|
len -= written;
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
|
|
|
@ -39,6 +39,7 @@ typedef struct {
|
||||||
NSMutableDictionary *headers;
|
NSMutableDictionary *headers;
|
||||||
BOOL shouldHandleCookies;
|
BOOL shouldHandleCookies;
|
||||||
BOOL debug;
|
BOOL debug;
|
||||||
|
id<GSLogDelegate> ioDelegate;
|
||||||
NSURL *URL;
|
NSURL *URL;
|
||||||
NSURL *mainDocumentURL;
|
NSURL *mainDocumentURL;
|
||||||
NSURLRequestCachePolicy cachePolicy;
|
NSURLRequestCachePolicy cachePolicy;
|
||||||
|
@ -116,6 +117,7 @@ typedef struct {
|
||||||
ASSIGN(inst->method, this->method);
|
ASSIGN(inst->method, this->method);
|
||||||
inst->shouldHandleCookies = this->shouldHandleCookies;
|
inst->shouldHandleCookies = this->shouldHandleCookies;
|
||||||
inst->debug = this->debug;
|
inst->debug = this->debug;
|
||||||
|
inst->ioDelegate = this->ioDelegate;
|
||||||
inst->headers = [this->headers mutableCopy];
|
inst->headers = [this->headers mutableCopy];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,6 +286,16 @@ typedef struct {
|
||||||
return old;
|
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
|
- (NSTimeInterval) timeoutInterval
|
||||||
{
|
{
|
||||||
return this->timeoutInterval;
|
return this->timeoutInterval;
|
||||||
|
@ -444,6 +456,11 @@ typedef struct {
|
||||||
return this->debug;
|
return this->debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id<GSLogDelegate>) _debugLogDelegate
|
||||||
|
{
|
||||||
|
return this->ioDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
- (id) _propertyForKey: (NSString*)key
|
- (id) _propertyForKey: (NSString*)key
|
||||||
{
|
{
|
||||||
return [this->properties objectForKey: key];
|
return [this->properties objectForKey: key];
|
||||||
|
|
|
@ -221,6 +221,11 @@ typedef struct {
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString*) description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat: @"%@ { URL: %@ } { Status Code: %d, Headers %@ }", [super description], this->URL, this->statusCode, this->headers];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) encodeWithCoder: (NSCoder*)aCoder
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||||
{
|
{
|
||||||
// FIXME
|
// FIXME
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -146,15 +146,15 @@
|
||||||
}
|
}
|
||||||
- (pid_t) processIdentifier
|
- (pid_t) processIdentifier
|
||||||
{
|
{
|
||||||
return (pid_t)[self notImplemented: _cmd];
|
return (pid_t)(uintptr_t)[self notImplemented: _cmd];
|
||||||
}
|
}
|
||||||
- (uid_t) effectiveUserIdentifier
|
- (uid_t) effectiveUserIdentifier
|
||||||
{
|
{
|
||||||
return (uid_t)[self notImplemented: _cmd];
|
return (uid_t)(uintptr_t)[self notImplemented: _cmd];
|
||||||
}
|
}
|
||||||
- (gid_t) effectiveGroupIdentifier
|
- (gid_t) effectiveGroupIdentifier
|
||||||
{
|
{
|
||||||
return (gid_t)[self notImplemented: _cmd];
|
return (gid_t)(uintptr_t)[self notImplemented: _cmd];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
||||||
NSString *file,
|
NSString *file,
|
||||||
*lengthHeader;
|
*lengthHeader;
|
||||||
NSHost *host = [NSHost hostWithAddress: @"127.0.0.1"];
|
NSHost *host = [NSHost hostWithName: @"localhost"];
|
||||||
NSStream *serverStream;
|
NSStream *serverStream;
|
||||||
int port = [[defs stringForKey: @"Port"] intValue];
|
int port = [[defs stringForKey: @"Port"] intValue];
|
||||||
int lifetime = [[defs stringForKey: @"Lifetime"] intValue];
|
int lifetime = [[defs stringForKey: @"Lifetime"] intValue];
|
||||||
|
|
|
@ -48,7 +48,7 @@ int main()
|
||||||
data = [u resourceDataUsingCache: NO];
|
data = [u resourceDataUsingCache: NO];
|
||||||
// Get status code
|
// Get status code
|
||||||
str = [u propertyForKey: NSHTTPPropertyStatusCodeKey];
|
str = [u propertyForKey: NSHTTPPropertyStatusCodeKey];
|
||||||
PASS([data isEqual: cont], "NSURL chunked test OK");
|
PASS_EQUAL(data, cont, "NSURL chunked test OK");
|
||||||
// Wait for server termination
|
// Wait for server termination
|
||||||
[t terminate];
|
[t terminate];
|
||||||
[t waitUntilExit];
|
[t waitUntilExit];
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* test.
|
* test.
|
||||||
*
|
*
|
||||||
* The test case which the NSURLConnectionTest implements by default is connecting
|
* 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:]
|
* 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:
|
* 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).
|
* (the instance will be run within a detached thread).
|
||||||
* Default: NO
|
* Default: NO
|
||||||
* 'Address' - the address of the remote side.
|
* 'Address' - the address of the remote side.
|
||||||
* Default: 127.0.0.1
|
* Default: localhost
|
||||||
* 'Port' - the port of the remote side.
|
* 'Port' - the port of the remote side.
|
||||||
* Default: 1234
|
* Default: 1234
|
||||||
* 'AuxPort' - the port of the auxilliary remote side (where the connection
|
* 'AuxPort' - the port of the auxilliary remote side (where the connection
|
||||||
|
|
|
@ -590,7 +590,7 @@ didReceiveResponse:(NSURLResponse *)response
|
||||||
}
|
}
|
||||||
if (nil == address)
|
if (nil == address)
|
||||||
{
|
{
|
||||||
address = @"127.0.0.1";
|
address = @"localhost";
|
||||||
}
|
}
|
||||||
if (nil == port)
|
if (nil == port)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
@implementation TestCase
|
@implementation TestCase
|
||||||
|
|
||||||
- (id)init
|
- (id) init
|
||||||
{
|
{
|
||||||
if((self = [super init]) != nil)
|
if ((self = [super init]) != nil)
|
||||||
{
|
{
|
||||||
[self resetFlags];
|
[self resetFlags];
|
||||||
[self setReferenceFlags: NORESULTS];
|
[self setReferenceFlags: NORESULTS];
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
[self tearDownTest: _extra];
|
[self tearDownTest: _extra];
|
||||||
|
|
||||||
|
@ -27,47 +27,47 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TestProgress */
|
/* TestProgress */
|
||||||
- (void)resetFlags
|
- (void) resetFlags
|
||||||
{
|
{
|
||||||
_flags = NORESULTS;
|
_flags = NORESULTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFlags:(NSUInteger)mask
|
- (void) setFlags:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
_flags = _flags | mask;
|
_flags = _flags | mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unsetFlags:(NSUInteger)mask
|
- (void) unsetFlags:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
_flags = _flags & ~mask;
|
_flags = _flags & ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isFlagSet:(NSUInteger)mask
|
- (BOOL) isFlagSet:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
return ((_flags & mask) == mask);
|
return ((_flags & mask) == mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resetReferenceFlags
|
- (void) resetReferenceFlags
|
||||||
{
|
{
|
||||||
_refFlags = NORESULTS;
|
_refFlags = NORESULTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setReferenceFlags:(NSUInteger)mask
|
- (void) setReferenceFlags:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
_refFlags = _refFlags | mask;
|
_refFlags = _refFlags | mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unsetReferenceFlags:(NSUInteger)mask
|
- (void) unsetReferenceFlags:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
_refFlags = _refFlags & ~mask;
|
_refFlags = _refFlags & ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isReferenceFlagSet:(NSUInteger)mask
|
- (BOOL) isReferenceFlagSet:(NSUInteger)mask
|
||||||
{
|
{
|
||||||
return ((_refFlags & mask) == mask);
|
return ((_refFlags & mask) == mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSuccess
|
- (BOOL) isSuccess
|
||||||
{
|
{
|
||||||
if(!_failed && (_flags == _refFlags))
|
if(!_failed && (_flags == _refFlags))
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* It is designed to call the delegate's callbacks (if implemented) of
|
* It is designed to call the delegate's callbacks (if implemented) of
|
||||||
* the TestWebServerDelegate protocol for every request made to
|
* the TestWebServerDelegate protocol for every request made to
|
||||||
* the SimpleWebServer's assigned address on the SimpleWebServer's assigned port
|
* 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
|
* 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
|
* 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
|
* has to dispatch a request to a corresponding handler and then to send back
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
* NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys:
|
* NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
* @"https", @"Protocol",
|
* @"https", @"Protocol",
|
||||||
* nil];
|
* nil];
|
||||||
* TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"127.0.0.1"
|
* TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"localhost"
|
||||||
* port: @"1234"
|
* port: @"1234"
|
||||||
* mode: NO
|
* mode: NO
|
||||||
* extra: extra];
|
* extra: extra];
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
* 301 "Redirect to <URL>"
|
* 301 "Redirect to <URL>"
|
||||||
* Returns in the header 'Location' a new <URL> which by default
|
* Returns in the header 'Location' a new <URL> which by default
|
||||||
* has the same protocol, address but the port is incremented by
|
* 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).
|
* their default values).
|
||||||
* 400 "You have issued a request with invalid data"
|
* 400 "You have issued a request with invalid data"
|
||||||
* 401 "Invalid login or password"
|
* 401 "Invalid login or password"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the detached thread. It is the entry point of the thread.
|
* Starts the detached thread. It is the entry point of the thread.
|
||||||
*/
|
*/
|
||||||
- (void)_startDetached:(id)extra;
|
- (void)_startDetached:(id)extra;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,10 +23,10 @@
|
||||||
- (void)_stopHTTPServer;
|
- (void)_stopHTTPServer;
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/* default 'constants' */
|
/* default 'constants' */
|
||||||
#define DEFAULTADDRESS @"127.0.0.1"
|
#define DEFAULTADDRESS @"localhost"
|
||||||
#define DEFAULTPORT @"1234"
|
#define DEFAULTPORT @"1234"
|
||||||
#define DEFAULTMODE NO
|
#define DEFAULTMODE NO
|
||||||
#define DEFAULTLOGIN @"login"
|
#define DEFAULTLOGIN @"login"
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
@implementation TestWebServer
|
@implementation TestWebServer
|
||||||
|
|
||||||
- (id)init
|
- (id) init
|
||||||
{
|
{
|
||||||
return [self initWithAddress: DEFAULTADDRESS
|
return [self initWithAddress: DEFAULTADDRESS
|
||||||
port: DEFAULTPORT
|
port: DEFAULTPORT
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
_password = nil;
|
_password = nil;
|
||||||
_login = nil;
|
_login = nil;
|
||||||
_isSecure = NO;
|
_isSecure = NO;
|
||||||
|
|
||||||
if ([extra isKindOfClass: [NSDictionary class]])
|
if ([extra isKindOfClass: [NSDictionary class]])
|
||||||
{
|
{
|
||||||
NSDictionary *d = extra;
|
NSDictionary *d = extra;
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
selector: @selector(_startDetached:)
|
selector: @selector(_startDetached:)
|
||||||
object: extra];
|
object: extra];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_serverThread = nil;
|
_serverThread = nil;
|
||||||
}
|
}
|
||||||
|
@ -145,36 +145,40 @@
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)start:(id)extra
|
- (void) start: (id)extra
|
||||||
{
|
{
|
||||||
if ([_server port] != nil)
|
if ([_server port] != nil)
|
||||||
{
|
{
|
||||||
if (YES == _debug)
|
if (_debug)
|
||||||
{
|
{
|
||||||
NSWarnMLog(@"SimpleWebServer already started");
|
NSWarnMLog(@"SimpleWebServer already started");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nil != _serverThread)
|
if (nil != _serverThread)
|
||||||
{
|
{
|
||||||
|
if (_debug)
|
||||||
|
NSLog(@"Waiting for startup");
|
||||||
if (![_serverThread isExecuting])
|
if (![_serverThread isExecuting])
|
||||||
{
|
{
|
||||||
NSTimeInterval duration = 0.0;
|
NSTimeInterval duration = 0.0;
|
||||||
|
|
||||||
[_serverThread start];
|
[_serverThread start];
|
||||||
|
|
||||||
// wait for the SimpleWebServer is started
|
// wait for the SimpleWebServer is started
|
||||||
while(![_lock tryLockWhenCondition: STARTED] &&
|
while (![_lock tryLockWhenCondition: STARTED]
|
||||||
duration < MAXDURATION)
|
&& duration < MAXDURATION)
|
||||||
{
|
{
|
||||||
[[NSRunLoop currentRunLoop]
|
[[NSRunLoop currentRunLoop]
|
||||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||||
duration += TIMING;
|
duration += TIMING;
|
||||||
}
|
}
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
if (duration >= MAXDURATION &&
|
if (duration >= MAXDURATION
|
||||||
nil != _delegate &&
|
&& nil != _delegate
|
||||||
[_delegate respondsToSelector: @selector(timeoutExceededByHandler:)])
|
&& [_delegate respondsToSelector:
|
||||||
|
@selector(timeoutExceededByHandler:)])
|
||||||
{
|
{
|
||||||
[_delegate timeoutExceededByHandler: self];
|
[_delegate timeoutExceededByHandler: self];
|
||||||
[self stop];
|
[self stop];
|
||||||
|
@ -191,7 +195,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stop
|
- (void) stop
|
||||||
{
|
{
|
||||||
if ([_server port] == nil)
|
if ([_server port] == nil)
|
||||||
{
|
{
|
||||||
|
@ -283,18 +287,18 @@
|
||||||
_isSecure ? @"https" : @"http",
|
_isSecure ? @"https" : @"http",
|
||||||
_address,
|
_address,
|
||||||
_port];
|
_port];
|
||||||
|
|
||||||
[(HandlerIndex *)handler setURLString: urlString];
|
[(HandlerIndex *)handler setURLString: urlString];
|
||||||
}
|
}
|
||||||
else if ([handler isKindOfClass: [Handler301 class]])
|
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 *port = [NSString stringWithFormat: @"%u", [_port intValue] + 1]; // the TestWebServer's port + 1
|
||||||
NSString *urlString = [NSString stringWithFormat: @"%@://%@:%@/",
|
NSString *urlString = [NSString stringWithFormat: @"%@://%@:%@/",
|
||||||
_isSecure ? @"https" : @"http",
|
_isSecure ? @"https" : @"http",
|
||||||
_address,
|
_address,
|
||||||
port];
|
port];
|
||||||
|
|
||||||
[(Handler301 *)handler setURLString: urlString];
|
[(Handler301 *)handler setURLString: urlString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +318,7 @@
|
||||||
[handler posthandleRequest: request
|
[handler posthandleRequest: request
|
||||||
response: response
|
response: response
|
||||||
for: self];
|
for: self];
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -369,7 +373,7 @@
|
||||||
|
|
||||||
@implementation TestWebServer (Private)
|
@implementation TestWebServer (Private)
|
||||||
|
|
||||||
- (void)_startHTTPServer:(id)extra
|
- (void) _startHTTPServer: (id)extra
|
||||||
{
|
{
|
||||||
NSString *certPath;
|
NSString *certPath;
|
||||||
NSString *keyPath;
|
NSString *keyPath;
|
||||||
|
@ -381,8 +385,10 @@
|
||||||
[_server setDelegate: self];
|
[_server setDelegate: self];
|
||||||
if (_isSecure)
|
if (_isSecure)
|
||||||
{
|
{
|
||||||
if ([_address isEqualToString: @"127.0.0.1"] ||
|
NSHost *h = [NSHost hostWithAddress: _address];
|
||||||
[_address isEqualToString: @"localhost"])
|
NSHost *l = [NSHost hostWithName: @"localhost"];
|
||||||
|
|
||||||
|
if ([h isEqual: l])
|
||||||
{
|
{
|
||||||
certPath = [[NSBundle bundleForClass: [self class]]
|
certPath = [[NSBundle bundleForClass: [self class]]
|
||||||
pathForResource: @"testCert"
|
pathForResource: @"testCert"
|
||||||
|
@ -397,22 +403,31 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
[NSException raise: NSInternalInconsistencyException
|
||||||
|
format: @"The server hasn't run. Address %@ is not localhost (%@)",
|
||||||
|
_address, l];
|
||||||
// NOTE: generate corresponding certificates for any address differing
|
// 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 (_debug)
|
||||||
if (!status)
|
{
|
||||||
|
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
|
[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
|
- (void)_stopHTTPServer
|
||||||
{
|
{
|
||||||
if (nil != _server && [_server port] != nil)
|
if (nil != _server && [_server port] != nil)
|
||||||
{
|
{
|
||||||
[_server stop]; // shut down the server
|
[_server stop]; // shut down the server
|
||||||
if (YES == _debug)
|
if (YES == _debug)
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||||
*
|
*
|
||||||
* Runs two TestWebServer instances to check how the class TestWebServer
|
* 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:
|
* 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:
|
* 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
|
* Resolving 127.0.0.1 (localhost)... 127.0.0.1
|
||||||
* Connecting to 127.0.0.1 (localhost)|127.0.0.1|:1234... connected.
|
* Connecting to 127.0.0.1 (localhost)|127.0.0.1|:1234... connected.
|
||||||
* HTTP request sent, awaiting response... 401 Unauthorized
|
* HTTP request sent, awaiting response... 401 Unauthorized
|
||||||
|
@ -64,20 +64,20 @@ int main(int argc, char **argv, char **env)
|
||||||
// @"https", @"Protocol",
|
// @"https", @"Protocol",
|
||||||
nil];
|
nil];
|
||||||
server1 = [[[testClass testWebServerClass] alloc]
|
server1 = [[[testClass testWebServerClass] alloc]
|
||||||
initWithAddress: @"127.0.0.1"
|
initWithAddress: @"localhost"
|
||||||
port: @"1234"
|
port: @"1234"
|
||||||
mode: NO
|
mode: NO
|
||||||
extra: d];
|
extra: d];
|
||||||
[server1 setDebug: debug];
|
[server1 setDebug: debug];
|
||||||
[server1 start: d]; // 127.0.0.1:1234 HTTP
|
[server1 start: d]; // localhost:1234 HTTP
|
||||||
|
|
||||||
server2 = [[[testClass testWebServerClass] alloc]
|
server2 = [[[testClass testWebServerClass] alloc]
|
||||||
initWithAddress: @"127.0.0.1"
|
initWithAddress: @"localhost"
|
||||||
port: @"1235"
|
port: @"1235"
|
||||||
mode: NO
|
mode: NO
|
||||||
extra: d];
|
extra: d];
|
||||||
[server2 setDebug: debug];
|
[server2 setDebug: debug];
|
||||||
[server2 start: d]; // 127.0.0.1:1235 HTTP
|
[server2 start: d]; // localhost:1235 HTTP
|
||||||
|
|
||||||
while (YES)
|
while (YES)
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,7 +59,7 @@ int main(int argc, char **argv, char **env)
|
||||||
|
|
||||||
duration = 0.0;
|
duration = 0.0;
|
||||||
timing = 0.1;
|
timing = 0.1;
|
||||||
urlString = @"http://127.0.0.1:19750";
|
urlString = @"http://localhost:19750";
|
||||||
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
|
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
|
||||||
del = [[Delegate new] autorelease];
|
del = [[Delegate new] autorelease];
|
||||||
[NSURLConnection connectionWithRequest: req
|
[NSURLConnection connectionWithRequest: req
|
||||||
|
@ -76,7 +76,7 @@ int main(int argc, char **argv, char **env)
|
||||||
[del reset];
|
[del reset];
|
||||||
|
|
||||||
duration = 0.0;
|
duration = 0.0;
|
||||||
urlString = @"https://127.0.0.1:19750";
|
urlString = @"https://localhost:19750";
|
||||||
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
|
req = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]];
|
||||||
[NSURLConnection connectionWithRequest: req
|
[NSURLConnection connectionWithRequest: req
|
||||||
delegate: del];
|
delegate: del];
|
||||||
|
|
|
@ -35,7 +35,7 @@ int main(int argc, char **argv, char **env)
|
||||||
// create a shared TestWebServer instance for performance
|
// create a shared TestWebServer instance for performance
|
||||||
server = [[testClass testWebServerClass] new];
|
server = [[testClass testWebServerClass] new];
|
||||||
[server setDebug: debug];
|
[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
|
* Simple GET via HTTP with empty response's body and
|
||||||
|
|
|
@ -40,12 +40,12 @@ int main(int argc, char **argv, char **env)
|
||||||
nil];
|
nil];
|
||||||
// create a shared TestWebServer instance for performance
|
// create a shared TestWebServer instance for performance
|
||||||
server = [[[testClass testWebServerClass] alloc]
|
server = [[[testClass testWebServerClass] alloc]
|
||||||
initWithAddress: @"127.0.0.1"
|
initWithAddress: @"localhost"
|
||||||
port: @"1234"
|
port: @"1234"
|
||||||
mode: NO
|
mode: NO
|
||||||
extra: d];
|
extra: d];
|
||||||
[server setDebug: debug];
|
[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
|
* Simple GET via HTTPS with empty response's body and
|
||||||
|
@ -59,7 +59,7 @@ int main(int argc, char **argv, char **env)
|
||||||
nil];
|
nil];
|
||||||
[testCase setUpTest: d];
|
[testCase setUpTest: d];
|
||||||
[testCase startTest: 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];
|
[testCase tearDownTest: d];
|
||||||
DESTROY(testCase);
|
DESTROY(testCase);
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ int main(int argc, char **argv, char **env)
|
||||||
// create a shared TestWebServer instance for performance
|
// create a shared TestWebServer instance for performance
|
||||||
server = [[testClass testWebServerClass] new];
|
server = [[testClass testWebServerClass] new];
|
||||||
[server setDebug: debug];
|
[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
|
* Simple GET via HTTP without authorization with empty response's body and
|
||||||
|
|
|
@ -44,7 +44,7 @@ int main(int argc, char **argv, char **env)
|
||||||
mode: NO
|
mode: NO
|
||||||
extra: d];
|
extra: d];
|
||||||
[server setDebug: debug];
|
[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
|
/* Simple GET via HTTPS without authorization with empty response's
|
||||||
* body and the response's status code 204 (by default)
|
* body and the response's status code 204 (by default)
|
||||||
|
|
|
@ -39,7 +39,7 @@ int main(int argc, char **argv, char **env)
|
||||||
// login:password
|
// login:password
|
||||||
server = [[testClass testWebServerClass] new];
|
server = [[testClass testWebServerClass] new];
|
||||||
[server setDebug: debug];
|
[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
|
* Simple GET via HTTP with some response's body and
|
||||||
|
|
|
@ -45,7 +45,7 @@ int main(int argc, char **argv, char **env)
|
||||||
mode: NO
|
mode: NO
|
||||||
extra: d];
|
extra: d];
|
||||||
[server setDebug: debug];
|
[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
|
/* Simple POST via HTTPS with the response's status code 400 and
|
||||||
* non-empty response's body
|
* non-empty response's body
|
||||||
|
|
5
Tests/base/NSURLSession/GNUmakefile.preamble
Normal file
5
Tests/base/NSURLSession/GNUmakefile.preamble
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
SUBPROJECTS = ../NSURLConnection/Helpers
|
||||||
|
|
||||||
|
include $(GNUSTEP_MAKEFILES)/aggregate.make
|
||||||
|
|
0
Tests/base/NSURLSession/TestInfo
Normal file
0
Tests/base/NSURLSession/TestInfo
Normal file
116
Tests/base/NSURLSession/delegate.g
Normal file
116
Tests/base/NSURLSession/delegate.g
Normal 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
|
||||||
|
|
79
Tests/base/NSURLSession/gdbinit
Normal file
79
Tests/base/NSURLSession/gdbinit
Normal 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
|
132
Tests/base/NSURLSession/test01.m
Normal file
132
Tests/base/NSURLSession/test01.m
Normal 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;
|
||||||
|
}
|
89
Tests/base/NSURLSession/test02.m
Normal file
89
Tests/base/NSURLSession/test02.m
Normal 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;
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ ifeq ($(BASE_MAKE_LOADED),)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Now we have definitions to show whether important dependencies have
|
# 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)?
|
# Has GNUTLS been found (for TLS/SSL support throughout)?
|
||||||
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
|
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
|
||||||
|
|
|
@ -35,13 +35,14 @@ endif
|
||||||
GNUSTEP_INSTALL_GDOMAP_AS_SETUID=@GNUSTEP_INSTALL_GDOMAP_AS_SETUID@
|
GNUSTEP_INSTALL_GDOMAP_AS_SETUID=@GNUSTEP_INSTALL_GDOMAP_AS_SETUID@
|
||||||
GNUSTEP_GDOMAP_PORT_OVERRIDE=@GNUSTEP_GDOMAP_PORT_OVERRIDE@
|
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_AVAHI=@HAVE_AVAHI@
|
||||||
|
GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
|
||||||
GNUSTEP_BASE_HAVE_ICU=@HAVE_ICU@
|
GNUSTEP_BASE_HAVE_ICU=@HAVE_ICU@
|
||||||
|
GNUSTEP_BASE_HAVE_LIBCURL=@HAVE_LIBCURL@
|
||||||
GNUSTEP_BASE_HAVE_LIBDISPATCH=@HAVE_LIBDISPATCH@
|
GNUSTEP_BASE_HAVE_LIBDISPATCH=@HAVE_LIBDISPATCH@
|
||||||
GNUSTEP_BASE_HAVE_LIBDISPATCH_RUNLOOP=@HAVE_LIBDISPATCH_RUNLOOP@
|
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
|
# Default to building only -baseadd
|
||||||
# on non *-gnu-* library combos
|
# on non *-gnu-* library combos
|
||||||
|
|
|
@ -6,15 +6,16 @@ dnl notice and this notice are preserved.
|
||||||
dnl
|
dnl
|
||||||
dnl Written by Andrew Ruder
|
dnl Written by Andrew Ruder
|
||||||
dnl GS_ADD_LIBRARY_PATH
|
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], [
|
AC_DEFUN([GS_ADD_LIBRARY_PATH], [
|
||||||
case "$target_os" in
|
case "$target_os" in
|
||||||
netbsd*)
|
netbsd*)
|
||||||
LDFLAGS="$LDFLAGS -L$1 -Wl,-R$1"
|
LDFLAGS="$LDFLAGS -L$1 -Wl,-R$1"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R$1 -L$1";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R$1 -L$1";;
|
||||||
*)
|
*)
|
||||||
LDFLAGS="$LDFLAGS -L$1"
|
LDFLAGS="$LDFLAGS -Wl,-rpath,$1 -L$1"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -L$1";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,$1 -L$1";;
|
||||||
esac
|
esac
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
263
configure
vendored
263
configure
vendored
|
@ -632,6 +632,8 @@ WARN_FLAGS
|
||||||
LDIR_FLAGS
|
LDIR_FLAGS
|
||||||
INCLUDE_FLAGS
|
INCLUDE_FLAGS
|
||||||
USE_GMP
|
USE_GMP
|
||||||
|
GS_HAVE_NSURLSESSION
|
||||||
|
HAVE_LIBCURL
|
||||||
HAVE_LIBDISPATCH_RUNLOOP
|
HAVE_LIBDISPATCH_RUNLOOP
|
||||||
HAVE_LIBDISPATCH
|
HAVE_LIBDISPATCH
|
||||||
HAVE_ICU
|
HAVE_ICU
|
||||||
|
@ -820,6 +822,10 @@ enable_zeroconf
|
||||||
with_zeroconf_api
|
with_zeroconf_api
|
||||||
enable_icu
|
enable_icu
|
||||||
enable_libdispatch
|
enable_libdispatch
|
||||||
|
with_dispatch_include
|
||||||
|
with_dispatch_library
|
||||||
|
with_curl
|
||||||
|
enable_nsurlsession
|
||||||
with_gmp_include
|
with_gmp_include
|
||||||
with_gmp_library
|
with_gmp_library
|
||||||
with_gdomap_port
|
with_gdomap_port
|
||||||
|
@ -1500,6 +1506,7 @@ Optional Features:
|
||||||
--disable-zeroconf Disable NSNetServices support
|
--disable-zeroconf Disable NSNetServices support
|
||||||
--disable-icu Disable International Components for Unicode
|
--disable-icu Disable International Components for Unicode
|
||||||
--disable-libdispatch Disable dispatching blocks via libdispatch
|
--disable-libdispatch Disable dispatching blocks via libdispatch
|
||||||
|
--disable-nsurlsession Disable support for NSURLSession
|
||||||
|
|
||||||
--enable-setuid-gdomap Enable installing gdomap as a setuid
|
--enable-setuid-gdomap Enable installing gdomap as a setuid
|
||||||
executable. By default, it is installed
|
executable. By default, it is installed
|
||||||
|
@ -1571,6 +1578,9 @@ Optional Packages:
|
||||||
--with-xml-prefix=PFX Prefix where libxml is installed (optional)
|
--with-xml-prefix=PFX Prefix where libxml is installed (optional)
|
||||||
--with-tls-prefix=PFX Prefix where libgnutls 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-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-include=PATH include path for gmp headers
|
||||||
--with-gmp-library=PATH library path for gmp libraries
|
--with-gmp-library=PATH library path for gmp libraries
|
||||||
--with-gdomap-port=PORT alternative port for gdomap
|
--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}"
|
LDFLAGS="$LDFLAGS -L${ffi_libdir} -Wl,-R${ffi_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${ffi_libdir} -L${ffi_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${ffi_libdir} -L${ffi_libdir}";;
|
||||||
*)
|
*)
|
||||||
LDFLAGS="$LDFLAGS -L${ffi_libdir}"
|
LDFLAGS="$LDFLAGS -Wl,-rpath,${ffi_libdir} -L${ffi_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -L${ffi_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${ffi_libdir} -L${ffi_libdir}";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@ -11093,8 +11103,8 @@ case "$target_os" in
|
||||||
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
|
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
|
||||||
*)
|
*)
|
||||||
LDFLAGS="$LDFLAGS -L${libiconv_libdir}"
|
LDFLAGS="$LDFLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -L${libiconv_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@ -11251,8 +11261,8 @@ case "$target_os" in
|
||||||
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
|
LDFLAGS="$LDFLAGS -L${libiconv_libdir} -Wl,-R${libiconv_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${libiconv_libdir} -L${libiconv_libdir}";;
|
||||||
*)
|
*)
|
||||||
LDFLAGS="$LDFLAGS -L${libiconv_libdir}"
|
LDFLAGS="$LDFLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -L${libiconv_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${libiconv_libdir} -L${libiconv_libdir}";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@ -12885,6 +12895,7 @@ fi
|
||||||
# Check for libdispatch
|
# Check for libdispatch
|
||||||
# See DEPENDENCIES POLICY at the start of this file.
|
# See DEPENDENCIES POLICY at the start of this file.
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
||||||
HAVE_LIBDISPATCH=0
|
HAVE_LIBDISPATCH=0
|
||||||
# Check whether --enable-libdispatch was given.
|
# Check whether --enable-libdispatch was given.
|
||||||
if test "${enable_libdispatch+set}" = set; then :
|
if test "${enable_libdispatch+set}" = set; then :
|
||||||
|
@ -12895,6 +12906,40 @@ fi
|
||||||
|
|
||||||
|
|
||||||
if test $enable_libdispatch = yes; then
|
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
|
for ac_header in dispatch.h
|
||||||
do :
|
do :
|
||||||
ac_fn_c_check_header_mongrel "$LINENO" "dispatch.h" "ac_cv_header_dispatch_h" "$ac_includes_default"
|
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
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
HAVE_LIBDISPATCH=0;
|
HAVE_LIBDISPATCH=0;
|
||||||
# just ignore libdispatch if it's not there
|
# just ignore libdispatch if it's not there
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -13064,6 +13109,204 @@ done
|
||||||
fi
|
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
|
# Check GMP for NSDecimal
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
@ -13098,8 +13341,8 @@ case "$target_os" in
|
||||||
LDFLAGS="$LDFLAGS -L${gmp_libdir} -Wl,-R${gmp_libdir}"
|
LDFLAGS="$LDFLAGS -L${gmp_libdir} -Wl,-R${gmp_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${gmp_libdir} -L${gmp_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-R${gmp_libdir} -L${gmp_libdir}";;
|
||||||
*)
|
*)
|
||||||
LDFLAGS="$LDFLAGS -L${gmp_libdir}"
|
LDFLAGS="$LDFLAGS -Wl,-rpath,${gmp_libdir} -L${gmp_libdir}"
|
||||||
LDIR_FLAGS="$LDIR_FLAGS -L${gmp_libdir}";;
|
LDIR_FLAGS="$LDIR_FLAGS -Wl,-rpath,${gmp_libdir} -L${gmp_libdir}";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
122
configure.ac
122
configure.ac
|
@ -2837,7 +2837,7 @@ AC_ARG_WITH(ffi-library,
|
||||||
[ --with-ffi-library=PATH Library path for ffi libs],
|
[ --with-ffi-library=PATH Library path for ffi libs],
|
||||||
ffi_libdir="$withval", ffi_libdir="no")
|
ffi_libdir="$withval", ffi_libdir="no")
|
||||||
if test ${ffi_libdir} != "no"; then
|
if test ${ffi_libdir} != "no"; then
|
||||||
GS_ADD_LIBRARY_PATH([${ffi_libdir}])
|
GS_ADD_LIBRARY_PATH([${ffi_libdir}])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$do_broken_libffi" = "no"; then
|
if test "$do_broken_libffi" = "no"; then
|
||||||
|
@ -3369,6 +3369,7 @@ AC_SUBST(HAVE_ICU)
|
||||||
# Check for libdispatch
|
# Check for libdispatch
|
||||||
# See DEPENDENCIES POLICY at the start of this file.
|
# See DEPENDENCIES POLICY at the start of this file.
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
||||||
HAVE_LIBDISPATCH=0
|
HAVE_LIBDISPATCH=0
|
||||||
AC_ARG_ENABLE(libdispatch,
|
AC_ARG_ENABLE(libdispatch,
|
||||||
[ --disable-libdispatch Disable dispatching blocks via libdispatch],
|
[ --disable-libdispatch Disable dispatching blocks via libdispatch],
|
||||||
|
@ -3376,6 +3377,21 @@ AC_ARG_ENABLE(libdispatch,
|
||||||
enable_libdispatch=yes)
|
enable_libdispatch=yes)
|
||||||
|
|
||||||
if test $enable_libdispatch = yes; then
|
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)
|
AC_CHECK_HEADERS(dispatch.h, have_dispatch=yes, have_dispatch=no)
|
||||||
if test "$have_dispatch" = "no"; then
|
if test "$have_dispatch" = "no"; then
|
||||||
AC_CHECK_HEADERS(dispatch/dispatch.h, have_dispatch=yes, have_dispatch=no)
|
AC_CHECK_HEADERS(dispatch/dispatch.h, have_dispatch=yes, have_dispatch=no)
|
||||||
|
@ -3402,8 +3418,8 @@ if test $enable_libdispatch = yes; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
HAVE_LIBDISPATCH=0;
|
HAVE_LIBDISPATCH=0;
|
||||||
# just ignore libdispatch if it's not there
|
# just ignore libdispatch if it's not there
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
AC_SUBST(HAVE_LIBDISPATCH)
|
AC_SUBST(HAVE_LIBDISPATCH)
|
||||||
|
@ -3425,6 +3441,106 @@ if test $HAVE_LIBDISPATCH = 1; then
|
||||||
fi
|
fi
|
||||||
AC_SUBST(HAVE_LIBDISPATCH_RUNLOOP)
|
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
|
# Check GMP for NSDecimal
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue