# CURLHandle

> Inherits From:
>   NSURLHandle

> Declared In:
>   CURLHandle.h

## Class Description

CURLHandle is a wrapper around a CURL. This is in the public domain, but please report any improvements back to the author (dwood_karelia_com). Be sure to be familiar with CURL and how it works; see http://curl.haxx.se/

The idea is to have it handle http and possibly other schemes too. At this time we don't support writing data (via HTTP PUT) and special situations such as HTTPS and firewall proxies haven't been tried out yet.

This class maintains only basic functionality, any "bells and whistles" should be defined in a category to keep this file as simple as possible.

Each instance is created to be associated with a URL. But we can change the URL and use the previous connection, as the CURL documentation says.

A URL cache is maintained as described in the NSURLHandle documentation. It's just a mutable dictionary, which is apparently about as sophisticated as Apple does. In the future, it might be nice to have some sort of LRU scheme....

Notifications are posted when the cache changes; it's possible for the client to track this cache for debugging or other nefarious purposes.

Note: Comments in methods with this formatting indicate quotes from the headers and documentation for NSURLHandle and are provided to help prove "correctness." Some come from an another document -- perhaps an earlier version of the documentation or release notes, but I can't find the original source. These are marked "(?source)"

The comment "DO NOT INVOKE SUPERCLASS" indicates that NSURLHandle does not provide an implementation available for overriding.

## Functions

> #### Synopsis:

> `size_t curlBodyFunction(void *ptr, size_t size, size_t nmemb, void *inSelf);`
> `size_t curlHeaderFunction(void *ptr, size_t size, size_t nmemb, void *inSelf);`

> #### Description:

> Callbacks from reading a chunk of data. Since we pass "self" in as the "data pointer", we can use that to get back into Objective C and do the work with the class.

## Global Variables

> #### Synopsis:

> `NSString *CURLHandleCacheDeleteNotification;`

> #### Description:

> The cache has been removed

> #### Synopsis:

> `NSString *CURLHandleCacheCreateNotification;`

> #### Description:

> The cache has been created

> #### Synopsis:

> `NSString *CURLHandleCacheChangeNotification;`

> #### Description:

> The cache has been changed

> #### Synopsis:

> `NSString *CURLHandleCreatedNotification;`

> #### Description:

> A handle has been created; the object is the handle itself.

> #### Synopsis:

> `NSMutableDictionary *sCurlCache;`

> #### Description:

> Cache of URL contents, keyed by URL

> #### Synopsis:

> `NSMutableSet *sAcceptedURLs;`

> #### Description:

> Set of URLs that CURLHandle will process

> #### Synopsis:

> `BOOL sAcceptAllHTTP;`

> #### Description:

> YES if CURLHandle will accept all HTTP

> #### Synopsis:

> `BOOL sAllowsProxy;`

> #### Description:

> YES if CURLHandle will allow use of a proxy server

> #### Synopsis:

> `NSString *sProxyUserIDAndPassword;`

> #### Description:

> Proxy User ID:Password combo for all uses of CURL.

## Instance Variables

> `NSThread *mMainThread;`
> `CURL *mCURL;`
> `char mErrorBuffer[CURL_ERROR_SIZE];`
> `int mResult;`
> `NSURL *mNSURL;`
> `NSMutableData *mHeaderBuffer;`
> `NSString *mHeaderString;`
> `NSMutableDictionary *mStringOptions;`
> `NSDictionary *mProxies;`
> `NSMutableDictionary *mHTTPHeaders;`
> `id mProgressIndicator;`
> `NSPort *mPort;`
> `BOOL mAbortBackground;`
> `FILE *mPutFile;`

> mMainThread | Reference to main thread so thread can determine if it's a background thread or not
> mCURL | Pointer to the actual CURL object that does all the hard work
> mErrorBuffer | Buffer to hold string generated by CURL; this is then converted to an NSString.
> mResult | Result after performing a CURL operation; it is displayed as an error code in case there was no error string generated.
> mNSURL | The instance of NSURL that is the URL to load
> mHeaderBuffer | The buffer that is filled with data from the header as the download progresses; it's appended to one line at a time.
> mHeaderString | The header buffer, converted into a string, upon demand.
> mStringOptions | Dictionary of keys(ints) & values (NSStrings) for performing curl_easy_setopt. We store the options in a dictionary and then invoke curl_easy_setopt on each option right before the curl_easy_perform so that we can retain their memory until it is needed.
> mProxies | Dictionary of proxy information; it's released when the handle is deallocated since it's needed for the transfer.
> mHTTPHeaders | Dictionary of & values (NSStrings) for additional HTTP headers. We store the options in a dictionary and then make use of them right before the curl_easy_perform so that we can retain their memory until it is needed.
> mProgressIndicator | A progress indicator, to animate during foreground loads. This will help give some indication of loading progress, though of course you're better off loading in the background.
> mPort | A port for communicating between the background thread and the foreground thread.
> mAbortBackground | A flag that is set by the foreground thread and read by the background thread; it's an indicator that the user has cancelled.
> mPutFile | The FILE stream if putFile: is used. It's only saved so it can be closed after perform

## Method Types

> CURLHandle-specific interfaces.
>   + curlGoodbye
>   + curlHelloSignature:acceptAll:
>   + curlAcceptURL:
>   + curlFlushEntireCache
>   - curl
>   - setString:forKey:
>   - setStringOrNumberObject:forKey:
>   - setURL:
>   - url
>   - setHTTPHeaders:
>   + setProxyUserIDAndPassword:
>   + setAllowsProxy:
>   - setPutFile:
>   - setPutFileOffset:
>   - getResponseCookies
>   + curlVersion
> NSURLHandle overrides
>   + canInitWithURL:
>   + cachedHandleForURL:
>   - loadInForeground
>   - curlError
>   - initWithURL:cached:
>   - propertyForKey:
>   - propertyForKeyIfAvailable:
>   - dealloc
>   - beginLoadInBackground
>   - cancelLoadInBackground
>   - endLoadInBackground
> Support Methods
>   - curlWritePtr:size:number:message:
>   - curlThreadBackgroundLoad:
>   - prepareAndPerformCurl
>   - handlePortMessage:
>   - headerString

## Class Methods

### cachedHandleForURL:

`+ (NSURLHandle *)cachedHandleForURL:(NSURL *)anURL`

Returns the URL handle from the cache that has serviced anURL or another identical URL. Subclasses of NSURLHandle must override this method. Returns nil if there is no such handle.

cachedHandleForURL: should look in the cache (maintained by your subclass) for an existing handle that services an URL identical to the one passed. If so, the cached handle should be returned. If not, a new handle should be created for the URL, stored in the cache, then returned. (?source) We have super cache the handle as well, though it's only to cause the data not to be flushed. Because the superclass is actually abstract (using the whole class cluster mechanism), it's not like we're caching the URL and so is the parent.

### canInitWithURL:

`+ (BOOL)canInitWithURL:(NSURL *)anURL`

Returns whether an URL handle can be initialized with anURL. If anURL uses an unsupported scheme, this method returns NO. Subclasses of NSURLHandle must override this method. to identify which URLs they can service.

Success if either the "all HTTP" switch is on and the URL is an HTTP url, or if it's a member of the set of URLs accepted explicitly.

DO NOT INVOKE SUPERCLASS.

### curlAcceptURL:

`+ (void)curlAcceptURL:(NSURL *)url`

Add an individual URL to the set of URLs that CURLHandle will accept. This is useful when you want to accept only certain URLs but not others. If you want to have CURLHandle handle all HTTPs (which seems to work just fine), just invoke curlHelloSignature:acceptAll: with YES instead of registering individual URLs.

### curlFlushEntireCache

`+ (void)curlFlushEntireCache`

Flush the entire cache of URLs. There doesn't seem to be an NSURLHandle API to do this, so we provide our own.

### curlGoodbye

`+ (void)curlGoodbye`

You must call curlGoodbye at end of program, for example in [NSApplication applicationWillTerminate:].

### curlHelloSignature:acceptAll:

`+ (void)curlHelloSignature:(NSString *)inSignature acceptAll:(BOOL)inAcceptAllHTTP`

Initialize CURLHandle and the underlying CURL. This can be invoked when the program is launched or before any loading is needed. Parameter is YES if all http URLs should be handled by this; NO if only ones registered with curlAcceptURL:

Now all that remains is to inform NSURLHandle of your new subclass; you do this by sending the NSURLHandle class the registerURLHandleClass: message, passing your subclass as the argument. Once this message has been sent, as NSURLHandle is asked to create handles for a given URL, it will in turn ask your subclass if it wants to handle the URL. If your subclass responds YES, NSURLHandle will instantiate your subclass for the URL. (?source)

### curlVersion

`+ (NSString *)curlVersion`

No method description.

### setAllowsProxy:

`+ (void)setAllowsProxy:(BOOL)inBool`

Set whether proxies are allowed or not. Default value is YES. If no, the proxy settings are ignored.

### setProxyUserIDAndPassword:

`+ (void)setProxyUserIDAndPassword:(NSString *)inString`

Set a proxy user id and password, used by all CURLHandle. This should be done before any transfers are made.

## Instance Methods

### beginLoadInBackground

`- (void)beginLoadInBackground`

beginLoadInBackground should start a background load of the data, then return. (?source) Called from -loadInBackground, above. Called when a background load begins. This method is provided mainly for subclasses that wish to take advantage of the superclass' failure reporting mechanism.

DO NOT INVOKE SUPERCLASS.

We just set a couple of status flags and then detach the background thread at this point, as long as it's not already happening.

### cancelLoadInBackground

`- (void)cancelLoadInBackground`

Called to cancel a load currently in progress. You should call super's implementation at the end of your implementation of this method.

Finally, your subclass should override cancelLoadInBackground to stop a background load in progress. Once a handle has received a cancelLoadInBackground message, it must not send any further didLoadBytes:loadComplete: or backgroundLoadDidFailWithReason: messages. (?source) This just sets a flag so that the next time the background thread is about to send a message, it will not. However, all current operations will still execute. But we just won't do anything with the results.

### curl

`- (CURL *)curl`

Return the CURL object assocated with this, so categories can have other methods that do curl-specific stuff like curl_easy_getinfo

### curlError

`- (NSString *)curlError`

Convert curl's error buffer into an NSString if possible, or return the result code number as a string. This is pass into backgroundLoadDidFailWithReason to set the failureReason string.

### curlThreadBackgroundLoad:

`- (void)curlThreadBackgroundLoad:(id)inParam`

Method executed in new background thread to load the URL. It sets up an autorelease pool, does the load (which has callbacks), and then sends a port message to indicate that the load is done. The CURLHandle is retained for the duration of this thread, so it won't be deallocated until the thread is done executing.

### curlWritePtr:size:number:message:

`- (size_t)curlWritePtr:(void *)inPtr size:(size_t)inSize number:(size_t)inNumber message:(int)inMessageID`

Continue the writing callback in Objective C; now we have our instance variables.

### dealloc

`- (void)dealloc`

Make the CURLHandle go away.

This will only be invoked after the background thread has completed, since the target of a thread detachment is retained.

### endLoadInBackground

`- (void)endLoadInBackground`

Called by cancelLoadInBackground to halt any background loading. You should call super's implementation at the end of your implementation of this method. DO NOT INVOKE SUPERCLASS

### getResponseCookies

`- (NSArray *)getResponseCookies`

Return the cookie array from the latest request. Equivalent to getting a property of COOKIES.

### handlePortMessage:

`- (void)handlePortMessage:(NSPortMessage *)portMessage`

NSPortDelegate method gets called in the foreground thread. Now we're ready to call our data-processor, which is called from both head and body.

### headerString

`- (NSString *)headerString`

Return the current header, as a string. This is meant to be invoked after all the headers are read; the entire header is cached into a string after converting from raw data.

### initWithURL:cached:

`- (id)initWithURL:(NSURL *)anURL cached:(BOOL)willCache`

initWithURL:cached: is the designated initializer for NSURLHandle; the second argument specifies whether the handle will be placed in the cache. (?source)

Initializes a newly created URL handle with the URL anURL. willCache controls whether the URL handle will cache its data and respond to requests from equivalent URLs for the cached data. Subclasses of NSURLHandle must override this method.

TODO: initWithURL ought to clean up better if init failed; release what was allocated. Note that this will not actually look up a URL in the cache if you specify YES. If you want to get an existing URL from the cache, use cachedHandleForURL:.

### loadInForeground

`- (NSData *)loadInForeground`

The last three methods, loadInForeground, beginLoadInBackground, and endLoadInBackground do the meaty work of your subclass. They are called from resourceData, loadInBackground, and cancelLoadInBackground respectively, after checking the status of the handle. (For instance, resourceData will not call loadInForeground if the handle has already been loaded; it will simply return the existing data.) (?source)

Loads the receiver's data in the synchronously. Called by resourceData. Subclasses of NSURLHandle must override this method.

DO NOT INVOKE SUPERCLASS.

### prepareAndPerformCurl

`- (void)prepareAndPerformCurl`

Actually set up for loading and do the perform. This happens in either the foreground or background thread. Before doing the perform, we collect up all the saved-up string-valued options, and set them right before the perform. This is because we create temporary (autoreleased) c-strings.

### propertyForKey:

`- (id)propertyForKey:(NSString *)propertyKey`

Returns the property for key propertyKey; returns nil if there is no such key. Subclasses of NSURLHandle must override this method.

DO NOT INVOKE SUPERCLASS.

### propertyForKeyIfAvailable:

`- (id)propertyForKeyIfAvailable:(NSString *)propertyKey`

Returns the property for key propertyKey only if the value is already available, i.e., the client doesn't need to do any work.

DO NOT INVOKE SUPERCLASS.

TODO: We can't assume any encoding for header. Perhaps we could look for the encoding value in the header, and try again if it doesn't match?

TODO: Apple defines some keys, but what the heck are they? "Description Forthcoming"....

This first attempts to handle the Apple-defined NSHTTPProperty... keys. Then if it's HEADER we just return the whole header string. If it's COOKIES, we return the cookie as an array; this can be further parsed with parsedCookies:.

Otherwise, we try to get it by just getting a header with that property name (case-insensitive).

### setHTTPHeaders:

`- (void)setHTTPHeaders:(NSDictionary *)inDict`

Add these to the list of HTTP headers (besides cookie, user agent, referer -- see CURLOPT_HTTPHEADER).

### setPutFile:

`- (void)setPutFile:(NSString *)path`

Set the file to be PUT

### setPutFileOffset:

`- (void)setPutFileOffset:(int)offset`

Set the file offset for performing the PUT.

### setString:forKey:

`- (void)setString:(NSString *)inString forKey:(CURLoption)inCurlOption`

Set an option given a `CURLoption` key. Before transfer, the string will be used to invoke curl_easy_setopt. Categories with convenient APIs can make use of this.

### setStringOrNumberObject:forKey:

`- (void)setStringOrNumberObject:(id)inString forKey:(CURLoption)inCurlOption`

Set an option given a `CURLoption` key. Before transfer, the object, which must be an NSString or an integer NSNumber will be used to invoke curl_easy_setopt. Categories with convenient APIs can make use of this.

### setURL:

`- (void)setURL:(NSURL *)inURL`

Set the URL related to this CURLHandle. This can actually be changed so the same CURL is used for different URLs, though they must be done sequentially. (See libcurl documentation.) Note that doing so will confuse the cache, since cache is still keyed by original URL.

### url

`- (NSURL *)url`

return the NSURL associated with this CURLHandle