aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMHTTPFetcher.h
diff options
context:
space:
mode:
Diffstat (limited to 'Foundation/GTMHTTPFetcher.h')
-rw-r--r--Foundation/GTMHTTPFetcher.h500
1 files changed, 500 insertions, 0 deletions
diff --git a/Foundation/GTMHTTPFetcher.h b/Foundation/GTMHTTPFetcher.h
new file mode 100644
index 0000000..181283a
--- /dev/null
+++ b/Foundation/GTMHTTPFetcher.h
@@ -0,0 +1,500 @@
+//
+// GTMHTTPFetcher.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// This is essentially a wrapper around NSURLConnection for POSTs and GETs.
+// If setPostData: is called, then POST is assumed.
+//
+// When would you use this instead of NSURLConnection?
+//
+// - When you just want the result from a GET or POST
+// - When you want the "standard" behavior for connections (redirection handling
+// an so on)
+// - When you want to avoid cookie collisions with Safari and other applications
+// - When you want to provide if-modified-since headers
+// - When you need to set a credential for the http
+// - When you want to avoid changing WebKit's cookies
+//
+// This is assumed to be a one-shot fetch request; don't reuse the object
+// for a second fetch.
+//
+// The fetcher may be created auto-released, in which case it will release
+// itself after the fetch completion callback. The fetcher
+// is implicitly retained as long as a connection is pending.
+//
+// But if you may need to cancel the fetcher, allocate it with initWithRequest:
+// and have the delegate release the fetcher in the callbacks.
+//
+// Sample usage:
+//
+// NSURLRequest *request = [NSURLRequest requestWithURL:myURL];
+// GTMHTTPFetcher* myFetcher = [GTMHTTPFetcher httpFetcherWithRequest:request];
+//
+// [myFetcher setPostData:[postString dataUsingEncoding:NSUTF8StringEncoding]]; // for POSTs
+//
+// [myFetcher setCredential:[NSURLCredential authCredentialWithUsername:@"foo"
+// password:@"bar"]]; // optional http credential
+//
+// [myFetcher setFetchHistory:myMutableDictionary]; // optional, for persisting modified-dates
+//
+// [myFetcher beginFetchWithDelegate:self
+// didFinishSelector:@selector(myFetcher:finishedWithData:)
+// didFailSelector:@selector(myFetcher:failedWithError:)];
+//
+// Upon fetch completion, the callback selectors are invoked; they should have
+// these signatures (you can use any callback method names you want so long as
+// the signatures match these):
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)retrievedData;
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error;
+//
+// NOTE: Fetches may retrieve data from the server even though the server
+// returned an error. The failWithError selector is called when the server
+// status is >= 300 (along with any server-supplied data, usually
+// some html explaining the error).
+// Status codes are at <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>
+//
+//
+// Proxies:
+//
+// Proxy handling is invisible so long as the system has a valid credential in
+// the keychain, which is normally true (else most NSURL-based apps would have
+// difficulty.) But when there is a proxy authetication error, the the fetcher
+// will call the failedWithError: method with the NSURLChallenge in the error's
+// userInfo. The error method can get the challenge info like this:
+//
+// NSURLAuthenticationChallenge *challenge
+// = [[error userInfo] objectForKey:kGTMHTTPFetcherErrorChallengeKey];
+// BOOL isProxyChallenge = [[challenge protectionSpace] isProxy];
+//
+// If a proxy error occurs, you can ask the user for the proxy username/password
+// and call fetcher's setProxyCredential: to provide those for the
+// next attempt to fetch.
+//
+//
+// Cookies:
+//
+// There are three supported mechanisms for remembering cookies between fetches.
+//
+// By default, GTMHTTPFetcher uses a mutable array held statically to track
+// cookies for all instantiated fetchers. This avoids server cookies being set
+// by servers for the application from interfering with Safari cookie settings,
+// and vice versa. The fetcher cookies are lost when the application quits.
+//
+// To rely instead on WebKit's global NSHTTPCookieStorage, call
+// setCookieStorageMethod: with kGTMHTTPFetcherCookieStorageMethodSystemDefault.
+//
+// If you provide a fetch history (such as for periodic checks, described
+// below) then the cookie storage mechanism is set to use the fetch
+// history rather than the static storage.
+//
+//
+// Fetching for periodic checks:
+//
+// The fetcher object can track "Last-modified" dates on returned data and
+// provide an "If-modified-since" header. This allows the server to save
+// bandwidth by providing a "Nothing changed" status message instead of response
+// data.
+//
+// To get this behavior, provide a persistent mutable dictionary to setFetchHistory:,
+// and look for the failedWithError: callback with code 304
+// (kGTMHTTPFetcherStatusNotModified) like this:
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error {
+// if ([[error domain] isEqual:kGTMHTTPFetcherStatusDomain] &&
+// ([error code] == kGTMHTTPFetcherStatusNotModified)) {
+// // [[error userInfo] objectForKey:kGTMHTTPFetcherStatusDataKey] is
+// // empty; use the data from the previous finishedWithData: for this URL
+// } else {
+// // handle other server status code
+// }
+// }
+//
+// The fetchHistory mutable dictionary should be maintained by the client between
+// fetches and given to each fetcher intended to have the If-modified-since header
+// or the same cookie storage.
+//
+//
+// Monitoring received data
+//
+// The optional received data selector should have the signature
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher receivedData:(NSData *)dataReceivedSoFar;
+//
+// The bytes received so far are [dataReceivedSoFar length]. This number may go down
+// if a redirect causes the download to begin again from a new server.
+// If supplied by the server, the anticipated total download size is available as
+// [[myFetcher response] expectedContentLength] (may be -1 for unknown
+// download sizes.)
+//
+//
+// Automatic retrying of fetches
+//
+// The fetcher can optionally create a timer and reattempt certain kinds of
+// fetch failures (status codes 408, request timeout; 503, service unavailable;
+// 504, gateway timeout; networking errors NSURLErrorTimedOut and
+// NSURLErrorNetworkConnectionLost.) The user may set a retry selector to
+// customize the type of errors which will be retried.
+//
+// Retries are done in an exponential-backoff fashion (that is, after 1 second,
+// 2, 4, 8, and so on.)
+//
+// Enabling automatic retries looks like this:
+// [myFetcher setIsRetryEnabled:YES];
+//
+// With retries enabled, the success or failure callbacks are called only
+// when no more retries will be attempted. Calling the fetcher's stopFetching
+// method will terminate the retry timer, without the finished or failure
+// selectors being invoked.
+//
+// Optionally, the client may set the maximum retry interval:
+// [myFetcher setMaxRetryInterval:60.]; // in seconds; default is 600 seconds
+//
+// Also optionally, the client may provide a callback selector to determine
+// if a status code or other error should be retried.
+// [myFetcher setRetrySelector:@selector(myFetcher:willRetry:forError:)];
+//
+// If set, the retry selector should have the signature:
+// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error
+// and return YES to set the retry timer or NO to fail without additional
+// fetch attempts.
+//
+// The retry method may return the |suggestedWillRetry| argument to get the
+// default retry behavior. Server status codes are present in the error
+// argument, and have the domain kGTMHTTPFetcherStatusDomain. The user's method
+// may look something like this:
+//
+// -(BOOL)myFetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error {
+//
+// // perhaps examine [error domain] and [error code], or [fetcher retryCount]
+// //
+// // return YES to start the retry timer, NO to proceed to the failure
+// // callback, or |suggestedWillRetry| to get default behavior for the
+// // current error domain and code values.
+// return suggestedWillRetry;
+// }
+
+
+
+#pragma once
+
+#import <Foundation/Foundation.h>
+
+#import "GTMDefines.h"
+
+#undef _EXTERN
+#undef _INITIALIZE_AS
+#ifdef GTMHTTPFETCHER_DEFINE_GLOBALS
+#define _EXTERN
+#define _INITIALIZE_AS(x) =x
+#else
+#define _EXTERN extern
+#define _INITIALIZE_AS(x)
+#endif
+
+// notifications & errors
+_EXTERN NSString* const kGTMHTTPFetcherErrorDomain _INITIALIZE_AS(@"com.google.GTMHTTPFetcher");
+_EXTERN NSString* const kGTMHTTPFetcherStatusDomain _INITIALIZE_AS(@"com.google.HTTPStatus");
+_EXTERN NSString* const kGTMHTTPFetcherErrorChallengeKey _INITIALIZE_AS(@"challenge");
+_EXTERN NSString* const kGTMHTTPFetcherStatusDataKey _INITIALIZE_AS(@"data"); // any data returns w/ a kGTMHTTPFetcherStatusDomain error
+
+
+// fetch history mutable dictionary keys
+_EXTERN NSString* const kGTMHTTPFetcherHistoryLastModifiedKey _INITIALIZE_AS(@"FetchHistoryLastModified");
+_EXTERN NSString* const kGTMHTTPFetcherHistoryDatedDataKey _INITIALIZE_AS(@"FetchHistoryDatedDataCache");
+_EXTERN NSString* const kGTMHTTPFetcherHistoryCookiesKey _INITIALIZE_AS(@"FetchHistoryCookies");
+
+enum {
+ kGTMHTTPFetcherErrorDownloadFailed = -1,
+ kGTMHTTPFetcherErrorAuthenticationChallengeFailed = -2,
+
+ kGTMHTTPFetcherStatusNotModified = 304
+};
+
+enum {
+ kGTMHTTPFetcherCookieStorageMethodStatic = 0,
+ kGTMHTTPFetcherCookieStorageMethodFetchHistory = 1,
+ kGTMHTTPFetcherCookieStorageMethodSystemDefault = 2
+};
+typedef NSUInteger GTMHTTPFetcherCookieStorageMethod;
+
+/// async retrieval of an http get or post
+@interface GTMHTTPFetcher : NSObject {
+ NSMutableURLRequest *request_;
+ NSURLConnection *connection_; // while connection_ is non-nil, delegate_ is retained
+ NSMutableData *downloadedData_;
+ NSURLCredential *credential_; // username & password
+ NSURLCredential *proxyCredential_; // credential supplied to proxy servers
+ NSData *postData_;
+ NSInputStream *postStream_;
+ NSMutableData *loggedStreamData_;
+ NSURLResponse *response_; // set in connection:didReceiveResponse:
+ id delegate_; // WEAK (though retained during an open connection)
+ SEL finishedSEL_; // should by implemented by delegate
+ SEL failedSEL_; // should be implemented by delegate
+ SEL receivedDataSEL_; // optional, set with setReceivedDataSelector
+ id userData_; // retained, if set by caller
+ NSArray *runLoopModes_; // optional, for 10.5 and later
+ NSMutableDictionary *fetchHistory_; // if supplied by the caller, used for Last-Modified-Since checks and cookies
+ BOOL shouldCacheDatedData_; // if true, remembers and returns data marked with a last-modified date
+ GTMHTTPFetcherCookieStorageMethod cookieStorageMethod_; // constant from above
+
+ BOOL isRetryEnabled_; // user wants auto-retry
+ SEL retrySEL_; // optional; set with setRetrySelector
+ NSTimer *retryTimer_;
+ unsigned int retryCount_;
+ NSTimeInterval maxRetryInterval_; // default 600 seconds
+ NSTimeInterval minRetryInterval_; // random between 1 and 2 seconds
+ NSTimeInterval retryFactor_; // default interval multiplier is 2
+ NSTimeInterval lastRetryInterval_;
+}
+
+/// create a fetcher
+//
+// httpFetcherWithRequest will return an autoreleased fetcher, but if
+// the connection is successfully created, the connection should retain the
+// fetcher for the life of the connection as well. So the caller doesn't have
+// to retain the fetcher explicitly unless they want to be able to cancel it.
++ (GTMHTTPFetcher *)httpFetcherWithRequest:(NSURLRequest *)request;
+
+// designated initializer
+- (id)initWithRequest:(NSURLRequest *)request;
+
+- (NSMutableURLRequest *)request;
+- (void)setRequest:(NSURLRequest *)theRequest;
+
+// setting the credential is optional; it is used if the connection receives
+// an authentication challenge
+- (NSURLCredential *)credential;
+- (void)setCredential:(NSURLCredential *)theCredential;
+
+// setting the proxy credential is optional; it is used if the connection
+// receives an authentication challenge from a proxy
+- (NSURLCredential *)proxyCredential;
+- (void)setProxyCredential:(NSURLCredential *)theCredential;
+
+
+// if post data or stream is not set, then a GET retrieval method is assumed
+- (NSData *)postData;
+- (void)setPostData:(NSData *)theData;
+
+// beware: In 10.4, NSInputStream fails to copy or retain
+// the data it was initialized with, contrary to docs.
+// NOTE: if logging is enabled and GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING is
+// 1, postStream will return a GTMProgressMonitorInputStream that wraps your
+// stream (so the upload can be logged).
+- (NSInputStream *)postStream;
+- (void)setPostStream:(NSInputStream *)theStream;
+
+- (GTMHTTPFetcherCookieStorageMethod)cookieStorageMethod;
+- (void)setCookieStorageMethod:(GTMHTTPFetcherCookieStorageMethod)method;
+
+// returns cookies from the currently appropriate cookie storage
+- (NSArray *)cookiesForURL:(NSURL *)theURL;
+
+// the delegate is not retained except during the connection
+- (id)delegate;
+- (void)setDelegate:(id)theDelegate;
+
+// the delegate's optional receivedData selector has a signature like:
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher receivedData:(NSData *)dataReceivedSoFar;
+- (SEL)receivedDataSelector;
+- (void)setReceivedDataSelector:(SEL)theSelector;
+
+
+// retrying; see comments at the top of the file. Calling
+// setIsRetryEnabled(YES) resets the min and max retry intervals.
+- (BOOL)isRetryEnabled;
+- (void)setIsRetryEnabled:(BOOL)flag;
+
+// retry selector is optional for retries.
+//
+// If present, it should have the signature:
+// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error
+// and return YES to cause a retry. See comments at the top of this file.
+- (SEL)retrySelector;
+- (void)setRetrySelector:(SEL)theSel;
+
+// retry intervals must be strictly less than maxRetryInterval, else
+// they will be limited to maxRetryInterval and no further retries will
+// be attempted. Setting maxRetryInterval to 0.0 will reset it to the
+// default value, 600 seconds.
+- (NSTimeInterval)maxRetryInterval;
+- (void)setMaxRetryInterval:(NSTimeInterval)secs;
+
+// Starting retry interval. Setting minRetryInterval to 0.0 will reset it
+// to a random value between 1.0 and 2.0 seconds. Clients should normally not
+// call this except for unit testing.
+- (NSTimeInterval)minRetryInterval;
+- (void)setMinRetryInterval:(NSTimeInterval)secs;
+
+// Multiplier used to increase the interval between retries, typically 2.0.
+// Clients should not need to call this.
+- (double)retryFactor;
+- (void)setRetryFactor:(double)multiplier;
+
+// number of retries attempted
+- (unsigned int)retryCount;
+
+// interval delay to precede next retry
+- (NSTimeInterval)nextRetryInterval;
+
+/// Begin fetching the request.
+//
+/// |delegate| can optionally implement the two selectors |finishedSEL| and
+/// |networkFailedSEL| or pass nil for them.
+/// Returns YES if the fetch is initiated. Delegate is retained between
+/// the beginFetch call until after the finish/fail callbacks.
+//
+// finishedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data
+// failedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error
+//
+
+- (BOOL)beginFetchWithDelegate:(id)delegate
+ didFinishSelector:(SEL)finishedSEL
+ didFailSelector:(SEL)networkFailedSEL;
+
+// Returns YES if this is in the process of fetching a URL
+- (BOOL)isFetching;
+
+/// Cancel the fetch of the request that's currently in progress
+- (void)stopFetching;
+
+/// return the status code from the server response
+- (NSInteger)statusCode;
+
+/// the response, once it's been received
+- (NSURLResponse *)response;
+- (void)setResponse:(NSURLResponse *)response;
+
+// Fetch History is a useful, but a little complex at times...
+//
+// The caller should provide a mutable dictionary that can be used for storing
+// Last-Modified-Since checks and cookie storage (setFetchHistory implicity
+// calls setCookieStorageMethod w/
+// kGTMHTTPFetcherCookieStorageMethodFetchHistory if you passed a dictionary,
+// kGTMHTTPFetcherCookieStorageMethodStatic if you passed nil.
+//
+// The caller can hold onto the dictionary to reuse the modification dates and
+// cookies across multiple fetcher instances.
+//
+// With a fetch history dictionary setup, the http fetcher cache has the
+// modification dates returned by the servers cached, and future fetches will
+// return 304 to indicate the data hasn't changed since then (the data in the
+// NSError object will be of length zero to show nothing was fetched). This
+// reduces load on the server when the response data has not changed. See
+// shouldCacheDatedData below for additional 304 support.
+//
+// Side effect: setFetchHistory: implicitly calls setCookieStorageMethod:
+- (NSMutableDictionary *)fetchHistory;
+- (void)setFetchHistory:(NSMutableDictionary *)fetchHistory;
+
+// For fetched data with a last-modified date, the fetcher can optionally cache
+// the response data in the fetch history and return cached data instead of a
+// 304 error. Set this to NO if you want to manually handle last-modified and
+// status 304 (Not changed) rather than be delivered cached data from previous
+// fetches. Default is NO. When a cache result is returned, the didFinish
+// selector is called with the data, and [fetcher status] returns 200.
+//
+// If the caller has already provided a fetchHistory dictionary, they can also
+// enable fetcher handling of 304 (not changed) status responses. By setting
+// shouldCacheDatedData to YES, the fetcher will save any response that has a
+// last modifed reply header into the fetchHistory. Then any future fetches
+// using that same fetchHistory will automatically load the cached response and
+// return it to the caller (with a status of 200) in place of the 304 server
+// reply.
+- (BOOL)shouldCacheDatedData;
+- (void)setShouldCacheDatedData:(BOOL)flag;
+
+// Delete the last-modified dates and cached data from the fetch history.
+- (void)clearDatedDataHistory;
+
+/// userData is retained for the convenience of the caller
+- (id)userData;
+- (void)setUserData:(id)theObj;
+
+// using the fetcher while a modal dialog is displayed requires setting the
+// run-loop modes to include NSModalPanelRunLoopMode
+//
+// setting run loop modes does nothing if they are not supported,
+// such as on 10.4
+- (NSArray *)runLoopModes;
+- (void)setRunLoopModes:(NSArray *)modes;
+
++ (BOOL)doesSupportRunLoopModes;
++ (NSArray *)defaultRunLoopModes;
++ (void)setDefaultRunLoopModes:(NSArray *)modes;
+
+// users who wish to replace GTMHTTPFetcher's use of NSURLConnection
+// can do so globally here. The replacement should be a subclass of
+// NSURLConnection.
++ (Class)connectionClass;
++ (void)setConnectionClass:(Class)theClass;
+
+@end
+
+// GTM HTTP Logging
+//
+// All traffic using GTMHTTPFetcher can be easily logged. Call
+//
+// [GTMHTTPFetcher setIsLoggingEnabled:YES];
+//
+// to begin generating log files.
+//
+// Log files are put into a folder on the desktop called "GTMHTTPDebugLogs"
+// unless another directory is specified with +setLoggingDirectory.
+//
+// Each run of an application gets a separate set of log files. An html
+// file is generated to simplify browsing the run's http transactions.
+// The html file includes javascript links for inline viewing of uploaded
+// and downloaded data.
+//
+// A symlink is created in the logs folder to simplify finding the html file
+// for the latest run of the application; the symlink is called
+//
+// AppName_http_log_newest.html
+//
+// For better viewing of XML logs, use Camino or Firefox rather than Safari.
+//
+// Projects may define GTM_HTTPFETCHER_ENABLE_LOGGING to 0 to remove all of the
+// logging code (it defaults to 1). By default, any data uploaded via PUT/POST
+// w/ and NSInputStream will not be logged. You can enable this logging by
+// defining GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING to 1 (it defaults to 0).
+//
+
+@interface GTMHTTPFetcher (GTMHTTPFetcherLogging)
+
+// Note: the default logs directory is ~/Desktop/GTMHTTPDebugLogs; it will be
+// created as needed. If a custom directory is set, the directory should
+// already exist.
++ (void)setLoggingDirectory:(NSString *)path;
++ (NSString *)loggingDirectory;
+
+// client apps can turn logging on and off
++ (void)setIsLoggingEnabled:(BOOL)flag;
++ (BOOL)isLoggingEnabled;
+
+// client apps can optionally specify process name and date string used in
+// log file names
++ (void)setLoggingProcessName:(NSString *)str;
++ (NSString *)loggingProcessName;
+
++ (void)setLoggingDateStamp:(NSString *)str;
++ (NSString *)loggingDateStamp;
+@end