aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Foundation/GTMHTTPFetcher.h494
-rw-r--r--Foundation/GTMHTTPFetcher.m1978
-rw-r--r--Foundation/GTMHTTPFetcherTest.m516
-rw-r--r--Foundation/GTMProgressMonitorInputStream.h59
-rw-r--r--Foundation/GTMProgressMonitorInputStream.m217
-rw-r--r--Foundation/GTMProgressMonitorInputStreamTest.m262
-rw-r--r--Foundation/TestData/GTMHTTPFetcherTestPage.html9
-rw-r--r--GTM.xcodeproj/project.pbxproj36
-rw-r--r--GTMDefines.h11
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj32
-rw-r--r--ReleaseNotes.txt6
11 files changed, 14 insertions, 3606 deletions
diff --git a/Foundation/GTMHTTPFetcher.h b/Foundation/GTMHTTPFetcher.h
index 5abbe1a..7377d1c 100644
--- a/Foundation/GTMHTTPFetcher.h
+++ b/Foundation/GTMHTTPFetcher.h
@@ -16,493 +16,7 @@
// 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 GTM_EXTERN
-#define _INITIALIZE_AS(x)
-#endif
-
-// notifications & errors
-_EXTERN NSString* const kGTMHTTPFetcherErrorDomain _INITIALIZE_AS(@"com.google.mactoolbox.HTTPFetcher");
-_EXTERN NSString* const kGTMHTTPFetcherStatusDomain _INITIALIZE_AS(@"com.google.mactoolbox.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
- NSMutableDictionary *properties_; // more data retained for 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;
-
-// properties are retained for the convenience of the caller
-- (void)setProperties:(NSDictionary *)dict;
-- (NSDictionary *)properties;
-
-- (void)setProperty:(id)obj forKey:(NSString *)key; // pass nil obj to remove property
-- (id)propertyForKey:(NSString *)key;
-
-// 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
+// This class is no more. If you want something like it's functionality, look
+// at using the version in the Objective-C GData Client
+// (http://code.google.com/p/gdata-objectivec-client/). It provides the same
+// functionality and will continue to be maintained.
diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m
deleted file mode 100644
index 1c6720a..0000000
--- a/Foundation/GTMHTTPFetcher.m
+++ /dev/null
@@ -1,1978 +0,0 @@
-//
-// GTMHTTPFetcher.m
-//
-// 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.
-//
-
-#define GTMHTTPFETCHER_DEFINE_GLOBALS 1
-
-#import "GTMHTTPFetcher.h"
-#import "GTMDebugSelectorValidation.h"
-#import "GTMGarbageCollection.h"
-#import "GTMSystemVersion.h"
-
-@interface GTMHTTPFetcher (GTMHTTPFetcherLoggingInternal)
-- (void)logFetchWithError:(NSError *)error;
-- (void)logCapturePostStream;
-@end
-
-// Make sure that if logging is disabled, the InputStream logging is also
-// diabled.
-#if !GTM_HTTPFETCHER_ENABLE_LOGGING
-# undef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-# define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
-#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
-
-#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-#import "GTMProgressMonitorInputStream.h"
-@interface GTMInputStreamLogger : GTMProgressMonitorInputStream
-// GTMInputStreamLogger wraps any NSInputStream used for uploading so we can
-// capture a copy of the data for the log
-@end
-#endif // !GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
-@interface NSURLConnection (LeopardMethodsOnTigerBuilds)
-- (id)initWithRequest:(NSURLRequest *)request
- delegate:(id)delegate
- startImmediately:(BOOL)startImmediately;
-- (void)start;
-- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
-@end
-#endif
-
-NSString* const kGTMLastModifiedHeader = @"Last-Modified";
-NSString* const kGTMIfModifiedSinceHeader = @"If-Modified-Since";
-
-
-static NSMutableArray* gGTMFetcherStaticCookies = nil;
-static Class gGTMFetcherConnectionClass = nil;
-static NSArray *gGTMFetcherDefaultRunLoopModes = nil;
-
-const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes
-
-@interface GTMHTTPFetcher (PrivateMethods)
-- (void)setCookies:(NSArray *)newCookies
- inArray:(NSMutableArray *)cookieStorageArray;
-- (NSArray *)cookiesForURL:(NSURL *)theURL
- inArray:(NSMutableArray *)cookieStorageArray;
-- (void)handleCookiesForResponse:(NSURLResponse *)response;
-- (BOOL)shouldRetryNowForStatus:(NSInteger)status error:(NSError *)error;
-- (void)retryTimerFired:(NSTimer *)timer;
-- (void)destroyRetryTimer;
-- (void)beginRetryTimer;
-- (void)primeTimerWithNewTimeInterval:(NSTimeInterval)secs;
-- (void)retryFetch;
-@end
-
-@implementation GTMHTTPFetcher
-
-+ (GTMHTTPFetcher *)httpFetcherWithRequest:(NSURLRequest *)request {
- return [[[GTMHTTPFetcher alloc] initWithRequest:request] autorelease];
-}
-
-+ (void)initialize {
- if (!gGTMFetcherStaticCookies) {
- gGTMFetcherStaticCookies = [[NSMutableArray alloc] init];
- GTMNSMakeUncollectable(gGTMFetcherStaticCookies);
- }
-}
-
-- (id)init {
- return [self initWithRequest:nil];
-}
-
-- (id)initWithRequest:(NSURLRequest *)request {
- if ((self = [super init]) != nil) {
-
- request_ = [request mutableCopy];
-
- [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic];
- }
- return self;
-}
-
-// TODO: do we need finalize to call stopFetching?
-
-- (void)dealloc {
- [self stopFetching]; // releases connection_
-
- [request_ release];
- [downloadedData_ release];
- [credential_ release];
- [proxyCredential_ release];
- [postData_ release];
- [postStream_ release];
- [loggedStreamData_ release];
- [response_ release];
- [userData_ release];
- [properties_ release];
- [runLoopModes_ release];
- [fetchHistory_ release];
- [self destroyRetryTimer];
-
- [super dealloc];
-}
-
-#pragma mark -
-
-// Begin fetching the URL. |delegate| is not retained
-// The delegate must provide and implement the finished and failed selectors.
-//
-// 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)failedSEL {
-
- GTMAssertSelectorNilOrImplementedWithArguments(delegate, finishedSEL,
- @encode(GTMHTTPFetcher *),
- @encode(NSData *), NULL);
- GTMAssertSelectorNilOrImplementedWithArguments(delegate, failedSEL,
- @encode(GTMHTTPFetcher *),
- @encode(NSError *), NULL);
- GTMAssertSelectorNilOrImplementedWithArguments(delegate, receivedDataSEL_,
- @encode(GTMHTTPFetcher *),
- @encode(NSData *), NULL);
- GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(delegate, retrySEL_,
- @encode(BOOL),
- @encode(GTMHTTPFetcher *),
- @encode(BOOL),
- @encode(NSError *),
- NULL);
-
- if (connection_ != nil) {
- // COV_NF_START - since we want the assert, we can't really test this
- _GTMDevAssert(connection_ != nil,
- @"fetch object %@ being reused; this should never happen",
- self);
- goto CannotBeginFetch;
- // COV_NF_END
- }
-
- if (request_ == nil) {
- _GTMDevLog(@"beginFetchWithDelegate requires a request");
- goto CannotBeginFetch;
- }
-
- [downloadedData_ release];
- downloadedData_ = nil;
-
- [self setDelegate:delegate];
- finishedSEL_ = finishedSEL;
- failedSEL_ = failedSEL;
-
- if (postData_ || postStream_) {
- if ([request_ HTTPMethod] == nil ||
- [[request_ HTTPMethod] isEqual:@"GET"]) {
- [request_ setHTTPMethod:@"POST"];
- }
-
- if (postData_) {
- [request_ setHTTPBody:postData_];
- } else {
-
- // if logging is enabled, it needs a buffer to accumulate data from any
- // NSInputStream used for uploading. Logging will wrap the input
- // stream with a stream that lets us keep a copy the data being read.
- if ([GTMHTTPFetcher isLoggingEnabled] && postStream_ != nil) {
- loggedStreamData_ = [[NSMutableData alloc] init];
- [self logCapturePostStream];
- }
-
- [request_ setHTTPBodyStream:postStream_];
- }
- }
-
- if (fetchHistory_) {
-
- // If this URL is in the history, set the Last-Modified header field
-
- // if we have a history, we're tracking across fetches, so we don't
- // want to pull results from a cache
- [request_ setCachePolicy:NSURLRequestReloadIgnoringCacheData];
-
- NSDictionary* lastModifiedDict
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
- NSString* urlString = [[request_ URL] absoluteString];
- NSString* lastModifiedStr = [lastModifiedDict objectForKey:urlString];
-
- // servers don't want last-modified-ifs on POSTs, so check for a body
- if (lastModifiedStr
- && [request_ HTTPBody] == nil
- && [request_ HTTPBodyStream] == nil) {
-
- [request_ addValue:lastModifiedStr
- forHTTPHeaderField:kGTMIfModifiedSinceHeader];
- }
- }
-
- // get cookies for this URL from our storage array, if
- // we have a storage array
- if (cookieStorageMethod_ != kGTMHTTPFetcherCookieStorageMethodSystemDefault) {
-
- NSArray *cookies = [self cookiesForURL:[request_ URL]];
-
- if ([cookies count]) {
-
- NSDictionary *headerFields
- = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
- NSString *cookieHeader
- = [headerFields objectForKey:@"Cookie"]; // key used in header dict
- if (cookieHeader) {
- [request_ addValue:cookieHeader
- forHTTPHeaderField:@"Cookie"]; // header name
- }
- }
- }
-
- // finally, start the connection
-
- Class connectionClass = [[self class] connectionClass];
-
- NSArray *runLoopModes = nil;
-
- if ([[self class] doesSupportRunLoopModes]) {
-
- // use the connection-specific run loop modes, if they were provided,
- // or else use the GTMHTTPFetcher default run loop modes, if any
- if (runLoopModes_) {
- runLoopModes = runLoopModes_;
- } else {
- runLoopModes = gGTMFetcherDefaultRunLoopModes;
- }
- }
-
- if ([runLoopModes count] == 0) {
-
- // if no run loop modes were specified, then we'll start the connection
- // on the current run loop in the current mode
- connection_ = [[connectionClass connectionWithRequest:request_
- delegate:self] retain];
- } else {
-
- // schedule on current run loop in the specified modes
- connection_ = [[connectionClass alloc] initWithRequest:request_
- delegate:self
- startImmediately:NO];
-
- for (NSUInteger idx = 0; idx < [runLoopModes count]; idx++) {
- NSString *mode = [runLoopModes objectAtIndex:idx];
- [connection_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
- }
- [connection_ start];
- }
-
- if (!connection_) {
- // COV_NF_START - can't really create this case
- _GTMDevLog(@"beginFetchWithDelegate could not create a connection");
- goto CannotBeginFetch;
- // COV_NF_END
- }
-
- // we'll retain the delegate only during the outstanding connection (similar
- // to what Cocoa does with performSelectorOnMainThread:) since we'd crash
- // if the delegate was released in the interim. We don't retain the selector
- // at other times, to avoid vicious retain loops. This retain is balanced in
- // the -stopFetch method.
- [delegate_ retain];
-
- downloadedData_ = [[NSMutableData alloc] init];
- return YES;
-
-CannotBeginFetch:
-
- if (failedSEL) {
-
- NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain
- code:kGTMHTTPFetcherErrorDownloadFailed
- userInfo:nil];
-
- [[self retain] autorelease]; // in case the callback releases us
-
- [delegate performSelector:failedSEL
- withObject:self
- withObject:error];
- }
-
- return NO;
-}
-
-// Returns YES if this is in the process of fetching a URL, or waiting to
-// retry
-- (BOOL)isFetching {
- return (connection_ != nil || retryTimer_ != nil);
-}
-
-// Returns the status code set in connection:didReceiveResponse:
-- (NSInteger)statusCode {
-
- NSInteger statusCode;
-
- if (response_ != nil
- && [response_ respondsToSelector:@selector(statusCode)]) {
-
- statusCode = [(NSHTTPURLResponse *)response_ statusCode];
- } else {
- // Default to zero, in hopes of hinting "Unknown" (we can't be
- // sure that things are OK enough to use 200).
- statusCode = 0;
- }
- return statusCode;
-}
-
-// Cancel the fetch of the URL that's currently in progress.
-- (void)stopFetching {
- [self destroyRetryTimer];
-
- if (connection_) {
- // in case cancelling the connection calls this recursively, we want
- // to ensure that we'll only release the connection and delegate once,
- // so first set connection_ to nil
-
- NSURLConnection* oldConnection = connection_;
- connection_ = nil;
-
- // this may be called in a callback from the connection, so use autorelease
- [oldConnection cancel];
- [oldConnection autorelease];
-
- // balance the retain done when the connection was opened
- [delegate_ release];
- }
-}
-
-- (void)retryFetch {
-
- id holdDelegate = [[delegate_ retain] autorelease];
-
- [self stopFetching];
-
- [self beginFetchWithDelegate:holdDelegate
- didFinishSelector:finishedSEL_
- didFailSelector:failedSEL_];
-}
-
-#pragma mark NSURLConnection Delegate Methods
-
-//
-// NSURLConnection Delegate Methods
-//
-
-// This method just says "follow all redirects", which _should_ be the default behavior,
-// According to file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/URLLoadingSystem
-// but the redirects were not being followed until I added this method. May be
-// a bug in the NSURLConnection code, or the documentation.
-//
-// In OS X 10.4.8 and earlier, the redirect request doesn't
-// get the original's headers and body. This causes POSTs to fail.
-// So we construct a new request, a copy of the original, with overrides from the
-// redirect.
-//
-// Docs say that if redirectResponse is nil, just return the redirectRequest.
-
-- (NSURLRequest *)connection:(NSURLConnection *)connection
- willSendRequest:(NSURLRequest *)redirectRequest
- redirectResponse:(NSURLResponse *)redirectResponse {
-
- if (redirectRequest && redirectResponse) {
- NSMutableURLRequest *newRequest = [[request_ mutableCopy] autorelease];
- // copy the URL
- NSURL *redirectURL = [redirectRequest URL];
- NSURL *url = [newRequest URL];
-
- // disallow scheme changes (say, from https to http)
- NSString *redirectScheme = [url scheme];
- NSString *newScheme = [redirectURL scheme];
- NSString *newResourceSpecifier = [redirectURL resourceSpecifier];
-
- if ([redirectScheme caseInsensitiveCompare:@"http"] == NSOrderedSame
- && newScheme != nil
- && [newScheme caseInsensitiveCompare:@"https"] == NSOrderedSame) {
-
- // allow the change from http to https
- redirectScheme = newScheme;
- }
-
- NSString *newUrlString = [NSString stringWithFormat:@"%@:%@",
- redirectScheme, newResourceSpecifier];
-
- NSURL *newURL = [NSURL URLWithString:newUrlString];
- [newRequest setURL:newURL];
-
- // any headers in the redirect override headers in the original.
- NSDictionary *redirectHeaders = [redirectRequest allHTTPHeaderFields];
- if (redirectHeaders) {
- NSString *key;
- GTM_FOREACH_KEY(key, redirectHeaders) {
- NSString *value = [redirectHeaders objectForKey:key];
- [newRequest setValue:value forHTTPHeaderField:key];
- }
- }
- redirectRequest = newRequest;
-
- // save cookies from the response
- [self handleCookiesForResponse:redirectResponse];
-
- // log the response we just received
- [self setResponse:redirectResponse];
- [self logFetchWithError:nil];
-
- // update the request for future logging
- [self setRequest:redirectRequest];
- }
- return redirectRequest;
-}
-
-- (void)connection:(NSURLConnection *)connection
- didReceiveResponse:(NSURLResponse *)response {
-
- // this method is called when the server has determined that it
- // has enough information to create the NSURLResponse
- // it can be called multiple times, for example in the case of a
- // redirect, so each time we reset the data.
- [downloadedData_ setLength:0];
-
- [self setResponse:response];
-
- // save cookies from the response
- [self handleCookiesForResponse:response];
-}
-
-
-// handleCookiesForResponse: handles storage of cookies for responses passed to
-// connection:willSendRequest:redirectResponse: and
-// connection:didReceiveResponse:
-- (void)handleCookiesForResponse:(NSURLResponse *)response {
-
- if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodSystemDefault) {
-
- // do nothing special for NSURLConnection's default storage mechanism
-
- } else if ([response respondsToSelector:@selector(allHeaderFields)]) {
-
- // grab the cookies from the header as NSHTTPCookies and store them either
- // into our static array or into the fetchHistory
-
- NSDictionary *responseHeaderFields
- = [(NSHTTPURLResponse *)response allHeaderFields];
- if (responseHeaderFields) {
-
- NSArray *cookies
- = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaderFields
- forURL:[response URL]];
- if ([cookies count] > 0) {
-
- NSMutableArray *cookieArray = nil;
-
- // static cookies are stored in gGTMFetcherStaticCookies; fetchHistory
- // cookies are stored in fetchHistory_'s kGTMHTTPFetcherHistoryCookiesKey
-
- if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodStatic) {
-
- cookieArray = gGTMFetcherStaticCookies;
-
- } else if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodFetchHistory
- && fetchHistory_ != nil) {
-
- cookieArray
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryCookiesKey];
- if (cookieArray == nil) {
- cookieArray = [NSMutableArray array];
- [fetchHistory_ setObject:cookieArray
- forKey:kGTMHTTPFetcherHistoryCookiesKey];
- }
- }
-
- if (cookieArray) {
- @synchronized(cookieArray) {
- [self setCookies:cookies inArray:cookieArray];
- }
- }
- }
- }
- }
-}
-
--(void)connection:(NSURLConnection *)connection
- didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
-
- if ([challenge previousFailureCount] <= 2) {
-
- NSURLCredential *credential = credential_;
-
- if ([[challenge protectionSpace] isProxy] && proxyCredential_ != nil) {
- credential = proxyCredential_;
- }
-
- // Here, if credential is still nil, then we *could* try to get it from
- // NSURLCredentialStorage's defaultCredentialForProtectionSpace:.
- // We don't, because we're assuming:
- //
- // - for server credentials, we only want ones supplied by the program
- // calling http fetcher
- // - for proxy credentials, if one were necessary and available in the
- // keychain, it would've been found automatically by NSURLConnection
- // and this challenge delegate method never would've been called
- // anyway
-
- if (credential) {
- // try the credential
- [[challenge sender] useCredential:credential
- forAuthenticationChallenge:challenge];
- return;
- }
- }
-
- // If we don't have credentials, or we've already failed auth 3x, give up and
- // report the error, putting the challenge as a value in the userInfo
- // dictionary
- // Store the challenge first to ensure that it lives past being cancelled.
- NSDictionary *userInfo
- = [NSDictionary dictionaryWithObject:challenge
- forKey:kGTMHTTPFetcherErrorChallengeKey];
- [[challenge sender] cancelAuthenticationChallenge:challenge];
-
- NSError *error
- = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain
- code:kGTMHTTPFetcherErrorAuthenticationChallengeFailed
- userInfo:userInfo];
-
- [self connection:connection didFailWithError:error];
-}
-
-
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
- [downloadedData_ appendData:data];
-
- if (receivedDataSEL_) {
- [delegate_ performSelector:receivedDataSEL_
- withObject:self
- withObject:downloadedData_];
- }
-}
-
-- (void)updateFetchHistory {
-
- if (fetchHistory_) {
-
- NSString* urlString = [[request_ URL] absoluteString];
- if ([response_ respondsToSelector:@selector(allHeaderFields)]) {
- NSDictionary *headers = [(NSHTTPURLResponse *)response_ allHeaderFields];
- NSString* lastModifiedStr = [headers objectForKey:kGTMLastModifiedHeader];
-
- // get the dictionary mapping URLs to last-modified dates
- NSMutableDictionary* lastModifiedDict
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
- if (!lastModifiedDict) {
- lastModifiedDict = [NSMutableDictionary dictionary];
- [fetchHistory_ setObject:lastModifiedDict
- forKey:kGTMHTTPFetcherHistoryLastModifiedKey];
- }
-
- NSMutableDictionary* datedDataCache = nil;
- if (shouldCacheDatedData_) {
- // get the dictionary mapping URLs to cached, dated data
- datedDataCache
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
- if (!datedDataCache) {
- datedDataCache = [NSMutableDictionary dictionary];
- [fetchHistory_ setObject:datedDataCache
- forKey:kGTMHTTPFetcherHistoryDatedDataKey];
- }
- }
-
- NSInteger statusCode = [self statusCode];
- if (statusCode != kGTMHTTPFetcherStatusNotModified) {
-
- // save this last modified date string for successful results (<300)
- // If there's no last modified string, clear the dictionary
- // entry for this URL. Also cache or delete the data, if appropriate
- // (when datedDataCache is non-nil.)
- if (lastModifiedStr && statusCode < 300) {
- [lastModifiedDict setObject:lastModifiedStr forKey:urlString];
- if (downloadedData_) {
- [datedDataCache setObject:downloadedData_ forKey:urlString];
- } else {
- [datedDataCache removeObjectForKey:urlString];
- }
- } else {
- [lastModifiedDict removeObjectForKey:urlString];
- [datedDataCache removeObjectForKey:urlString];
- }
- }
- }
- }
-}
-
-// for error 304's ("Not Modified") where we've cached the data, return status
-// 200 ("OK") to the caller (but leave the fetcher status as 304)
-// and copy the cached data to downloadedData_.
-// For other errors or if there's no cached data, just return the actual status.
-- (NSInteger)statusAfterHandlingNotModifiedError {
-
- NSInteger status = [self statusCode];
- if (status == kGTMHTTPFetcherStatusNotModified && shouldCacheDatedData_) {
-
- // get the dictionary of URLs and data
- NSString* urlString = [[request_ URL] absoluteString];
-
- NSDictionary* datedDataCache
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
- NSData* cachedData = [datedDataCache objectForKey:urlString];
-
- if (cachedData) {
- // copy our stored data, and forge the status to pass on to the delegate
- [downloadedData_ setData:cachedData];
- status = 200;
- }
- }
- return status;
-}
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
-
- [self updateFetchHistory];
-
- [[self retain] autorelease]; // in case the callback releases us
-
- [self logFetchWithError:nil];
-
- NSInteger status = [self statusAfterHandlingNotModifiedError];
-
- if (status >= 300) {
-
- if ([self shouldRetryNowForStatus:status error:nil]) {
-
- [self beginRetryTimer];
-
- } else {
- // not retrying
-
- // did they want failure notifications?
- if (failedSEL_) {
-
- NSDictionary *userInfo =
- [NSDictionary dictionaryWithObject:downloadedData_
- forKey:kGTMHTTPFetcherStatusDataKey];
- NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain
- code:status
- userInfo:userInfo];
-
- [delegate_ performSelector:failedSEL_
- withObject:self
- withObject:error];
- }
- // we're done fetching
- [self stopFetching];
- }
-
- } else if (finishedSEL_) {
-
- // successful http status (under 300)
- [delegate_ performSelector:finishedSEL_
- withObject:self
- withObject:downloadedData_];
- [self stopFetching];
- }
-
-}
-
-- (void)connection:(NSURLConnection *)connection
- didFailWithError:(NSError *)error {
-
- [self logFetchWithError:error];
-
- if ([self shouldRetryNowForStatus:0 error:error]) {
-
- [self beginRetryTimer];
-
- } else {
-
- if (failedSEL_) {
- [[self retain] autorelease]; // in case the callback releases us
-
- [delegate_ performSelector:failedSEL_
- withObject:self
- withObject:error];
- }
-
- [self stopFetching];
- }
-}
-
-#pragma mark Retries
-
-- (BOOL)isRetryError:(NSError *)error {
-
- struct retryRecord {
- NSString *const domain;
- int code;
- };
-
- struct retryRecord retries[] = {
- { kGTMHTTPFetcherStatusDomain, 408 }, // request timeout
- { kGTMHTTPFetcherStatusDomain, 503 }, // service unavailable
- { kGTMHTTPFetcherStatusDomain, 504 }, // request timeout
- { NSURLErrorDomain, NSURLErrorTimedOut },
- { NSURLErrorDomain, NSURLErrorNetworkConnectionLost },
- { nil, 0 }
- };
-
- BOOL isGood = NO;
- // NSError's isEqual always returns false for equal but distinct instances
- // of NSError, so we have to compare the domain and code values explicitly
-
- for (int idx = 0; retries[idx].domain != nil; idx++) {
-
- if ([[error domain] isEqual:retries[idx].domain]
- && [error code] == retries[idx].code) {
-
- isGood = YES;
- break;
- }
- }
- return isGood;
-}
-
-
-// shouldRetryNowForStatus:error: returns YES if the user has enabled retries
-// and the status or error is one that is suitable for retrying. "Suitable"
-// means either the isRetryError:'s list contains the status or error, or the
-// user's retrySelector: is present and returns YES when called.
-- (BOOL)shouldRetryNowForStatus:(NSInteger)status
- error:(NSError *)error {
-
- if ([self isRetryEnabled]) {
-
- if ([self nextRetryInterval] < [self maxRetryInterval]) {
-
- if (error == nil) {
- // make an error for the status
- error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain
- code:status
- userInfo:nil];
- }
-
- BOOL willRetry = [self isRetryError:error];
-
- if (retrySEL_) {
- NSMethodSignature *signature = [delegate_ methodSignatureForSelector:retrySEL_];
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
- [invocation setSelector:retrySEL_];
- [invocation setTarget:delegate_];
- [invocation setArgument:&self atIndex:2];
- [invocation setArgument:&willRetry atIndex:3];
- [invocation setArgument:&error atIndex:4];
- [invocation invoke];
-
- [invocation getReturnValue:&willRetry];
- }
-
- return willRetry;
- }
- }
-
- return NO;
-}
-
-- (void)beginRetryTimer {
-
- NSTimeInterval nextInterval = [self nextRetryInterval];
- NSTimeInterval maxInterval = [self maxRetryInterval];
-
- NSTimeInterval newInterval = MIN(nextInterval, maxInterval);
-
- [self primeTimerWithNewTimeInterval:newInterval];
-}
-
-- (void)primeTimerWithNewTimeInterval:(NSTimeInterval)secs {
-
- [self destroyRetryTimer];
-
- lastRetryInterval_ = secs;
-
- retryTimer_ = [NSTimer scheduledTimerWithTimeInterval:secs
- target:self
- selector:@selector(retryTimerFired:)
- userInfo:nil
- repeats:NO];
- [retryTimer_ retain];
-}
-
-- (void)retryTimerFired:(NSTimer *)timer {
-
- [self destroyRetryTimer];
-
- retryCount_++;
-
- [self retryFetch];
-}
-
-- (void)destroyRetryTimer {
-
- [retryTimer_ invalidate];
- [retryTimer_ autorelease];
- retryTimer_ = nil;
-}
-
-- (unsigned int)retryCount {
- return retryCount_;
-}
-
-- (NSTimeInterval)nextRetryInterval {
- // the next wait interval is the factor (2.0) times the last interval,
- // but never less than the minimum interval
- NSTimeInterval secs = lastRetryInterval_ * retryFactor_;
- secs = MIN(secs, maxRetryInterval_);
- secs = MAX(secs, minRetryInterval_);
-
- return secs;
-}
-
-- (BOOL)isRetryEnabled {
- return isRetryEnabled_;
-}
-
-- (void)setIsRetryEnabled:(BOOL)flag {
-
- if (flag && !isRetryEnabled_) {
- // We defer initializing these until the user calls setIsRetryEnabled
- // to avoid seeding the random number generator if it's not needed.
- // However, it means min and max intervals for this fetcher are reset
- // as a side effect of calling setIsRetryEnabled.
- //
- // seed the random value, and make an initial retry interval
- // random between 1.0 and 2.0 seconds
- srandomdev();
- [self setMinRetryInterval:0.0];
- [self setMaxRetryInterval:kDefaultMaxRetryInterval];
- [self setRetryFactor:2.0];
- lastRetryInterval_ = 0.0;
- }
- isRetryEnabled_ = flag;
-};
-
-- (SEL)retrySelector {
- return retrySEL_;
-}
-
-- (void)setRetrySelector:(SEL)theSelector {
- retrySEL_ = theSelector;
-}
-
-- (NSTimeInterval)maxRetryInterval {
- return maxRetryInterval_;
-}
-
-- (void)setMaxRetryInterval:(NSTimeInterval)secs {
- if (secs > 0) {
- maxRetryInterval_ = secs;
- } else {
- maxRetryInterval_ = kDefaultMaxRetryInterval;
- }
-}
-
-- (double)minRetryInterval {
- return minRetryInterval_;
-}
-
-- (void)setMinRetryInterval:(NSTimeInterval)secs {
- if (secs > 0) {
- minRetryInterval_ = secs;
- } else {
- // set min interval to a random value between 1.0 and 2.0 seconds
- // so that if multiple clients start retrying at the same time, they'll
- // repeat at different times and avoid overloading the server
- minRetryInterval_ = 1.0 + ((double)(random() & 0x0FFFF) / (double) 0x0FFFF);
- }
-}
-
-- (double)retryFactor {
- return retryFactor_;
-}
-
-- (void)setRetryFactor:(double)multiplier {
- retryFactor_ = multiplier;
-}
-
-#pragma mark Getters and Setters
-
-- (NSMutableURLRequest *)request {
- return request_;
-}
-
-- (void)setRequest:(NSURLRequest *)theRequest {
- [request_ autorelease];
- request_ = [theRequest mutableCopy];
-}
-
-- (NSURLCredential *)credential {
- return credential_;
-}
-
-- (void)setCredential:(NSURLCredential *)theCredential {
- [credential_ autorelease];
- credential_ = [theCredential retain];
-}
-
-- (NSURLCredential *)proxyCredential {
- return proxyCredential_;
-}
-
-- (void)setProxyCredential:(NSURLCredential *)theCredential {
- [proxyCredential_ autorelease];
- proxyCredential_ = [theCredential retain];
-}
-
-- (NSData *)postData {
- return postData_;
-}
-
-- (void)setPostData:(NSData *)theData {
- [postData_ autorelease];
- postData_ = [theData retain];
-}
-
-- (NSInputStream *)postStream {
- return postStream_;
-}
-
-- (void)setPostStream:(NSInputStream *)theStream {
- [postStream_ autorelease];
- postStream_ = [theStream retain];
-}
-
-- (GTMHTTPFetcherCookieStorageMethod)cookieStorageMethod {
- return cookieStorageMethod_;
-}
-
-- (void)setCookieStorageMethod:(GTMHTTPFetcherCookieStorageMethod)method {
-
- cookieStorageMethod_ = method;
-
- BOOL handleCookies
- = (method == kGTMHTTPFetcherCookieStorageMethodSystemDefault) ? YES : NO;
- [request_ setHTTPShouldHandleCookies:handleCookies];
-}
-
-- (id)delegate {
- return delegate_;
-}
-
-- (void)setDelegate:(id)theDelegate {
-
- // we retain delegate_ only during the life of the connection
- if (connection_) {
- [delegate_ autorelease];
- delegate_ = [theDelegate retain];
- } else {
- delegate_ = theDelegate;
- }
-}
-
-- (SEL)receivedDataSelector {
- return receivedDataSEL_;
-}
-
-- (void)setReceivedDataSelector:(SEL)theSelector {
- receivedDataSEL_ = theSelector;
-}
-
-- (NSURLResponse *)response {
- return response_;
-}
-
-- (void)setResponse:(NSURLResponse *)response {
- [response_ autorelease];
- response_ = [response retain];
-}
-
-- (NSMutableDictionary *)fetchHistory {
- return fetchHistory_;
-}
-
-- (void)setFetchHistory:(NSMutableDictionary *)fetchHistory {
- [fetchHistory_ autorelease];
- fetchHistory_ = [fetchHistory retain];
- GTMHTTPFetcherCookieStorageMethod method
- = fetchHistory_ ? kGTMHTTPFetcherCookieStorageMethodFetchHistory
- : kGTMHTTPFetcherCookieStorageMethodStatic;
- [self setCookieStorageMethod:method];
-}
-
-- (void)setShouldCacheDatedData:(BOOL)flag {
- shouldCacheDatedData_ = flag;
- if (!flag) {
- [self clearDatedDataHistory];
- }
-}
-
-- (BOOL)shouldCacheDatedData {
- return shouldCacheDatedData_;
-}
-
-// delete last-modified dates and cached data from the fetch history
-- (void)clearDatedDataHistory {
- [fetchHistory_ removeObjectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
- [fetchHistory_ removeObjectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
-}
-
-- (id)userData {
- return userData_;
-}
-
-- (void)setUserData:(id)theObj {
- [userData_ autorelease];
- userData_ = [theObj retain];
-}
-
-- (void)setProperties:(NSDictionary *)dict {
- [properties_ autorelease];
- properties_ = [dict mutableCopy];
-}
-
-- (NSDictionary *)properties {
- return properties_;
-}
-
-- (void)setProperty:(id)obj forKey:(NSString *)key {
-
- if (properties_ == nil && obj != nil) {
- properties_ = [[NSMutableDictionary alloc] init];
- }
-
- [properties_ setValue:obj forKey:key];
-}
-
-- (id)propertyForKey:(NSString *)key {
- return [properties_ objectForKey:key];
-}
-
-- (NSArray *)runLoopModes {
- return runLoopModes_;
-}
-
-- (void)setRunLoopModes:(NSArray *)modes {
- [runLoopModes_ autorelease];
- runLoopModes_ = [modes retain];
-}
-
-+ (BOOL)doesSupportRunLoopModes {
- SEL sel = @selector(initWithRequest:delegate:startImmediately:);
- return [NSURLConnection instancesRespondToSelector:sel];
-}
-
-+ (NSArray *)defaultRunLoopModes {
- return gGTMFetcherDefaultRunLoopModes;
-}
-
-+ (void)setDefaultRunLoopModes:(NSArray *)modes {
- [gGTMFetcherDefaultRunLoopModes autorelease];
- gGTMFetcherDefaultRunLoopModes = [modes retain];
- GTMNSMakeUncollectable(gGTMFetcherDefaultRunLoopModes);
-}
-
-+ (Class)connectionClass {
- if (gGTMFetcherConnectionClass == nil) {
- gGTMFetcherConnectionClass = [NSURLConnection class];
- }
- return gGTMFetcherConnectionClass;
-}
-
-+ (void)setConnectionClass:(Class)theClass {
- gGTMFetcherConnectionClass = theClass;
-}
-
-#pragma mark Cookies
-
-// return a cookie from the array with the same name, domain, and path as the
-// given cookie, or else return nil if none found
-//
-// Both the cookie being tested and all cookies in cookieStorageArray should
-// be valid (non-nil name, domains, paths)
-- (NSHTTPCookie *)cookieMatchingCookie:(NSHTTPCookie *)cookie
- inArray:(NSArray *)cookieStorageArray {
-
- NSUInteger numberOfCookies = [cookieStorageArray count];
- NSString *name = [cookie name];
- NSString *domain = [cookie domain];
- NSString *path = [cookie path];
-
- _GTMDevAssert(name && domain && path,
- @"Invalid cookie (name:%@ domain:%@ path:%@)",
- name, domain, path);
-
- for (NSUInteger idx = 0; idx < numberOfCookies; idx++) {
-
- NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
-
- if ([[storedCookie name] isEqual:name]
- && [[storedCookie domain] isEqual:domain]
- && [[storedCookie path] isEqual:path]) {
-
- return storedCookie;
- }
- }
- return nil;
-}
-
-// remove any expired cookies from the array, excluding cookies with nil
-// expirations
-- (void)removeExpiredCookiesInArray:(NSMutableArray *)cookieStorageArray {
-
- // count backwards since we're deleting items from the array
- for (NSInteger idx = [cookieStorageArray count] - 1; idx >= 0; idx--) {
-
- NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
-
- NSDate *expiresDate = [storedCookie expiresDate];
- if (expiresDate && [expiresDate timeIntervalSinceNow] < 0) {
- [cookieStorageArray removeObjectAtIndex:idx];
- }
- }
-}
-
-
-// retrieve all cookies appropriate for the given URL, considering
-// domain, path, cookie name, expiration, security setting.
-// Side effect: removed expired cookies from the storage array
-- (NSArray *)cookiesForURL:(NSURL *)theURL
- inArray:(NSMutableArray *)cookieStorageArray {
-
- [self removeExpiredCookiesInArray:cookieStorageArray];
-
- NSMutableArray *foundCookies = [NSMutableArray array];
-
- // we'll prepend "." to the desired domain, since we want the
- // actual domain "nytimes.com" to still match the cookie domain ".nytimes.com"
- // when we check it below with hasSuffix
- NSString *host = [theURL host];
- NSString *path = [theURL path];
- NSString *scheme = [theURL scheme];
-
- NSString *domain = nil;
-
- if (host) {
- domain = [[@"." stringByAppendingString:host] lowercaseString];
- }
-
- NSUInteger numberOfCookies = [cookieStorageArray count];
- for (NSUInteger idx = 0; idx < numberOfCookies; idx++) {
-
- NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
-
- NSString *cookieDomain = [[storedCookie domain] lowercaseString];
- NSString *cookiePath = [storedCookie path];
- BOOL cookieIsSecure = [storedCookie isSecure];
-
- BOOL domainIsOK = [domain hasSuffix:cookieDomain];
- if (!domainIsOK && [domain hasSuffix:@"localhost"]) {
- // On Leopard and below, localhost Cookies always come back
- // with a domain of localhost.local. On SnowLeopard they come
- // back as just localhost.
- domainIsOK = [@".localhost.local" hasSuffix:cookieDomain];
- }
- BOOL pathIsOK = [cookiePath isEqual:@"/"] || [path hasPrefix:cookiePath];
- BOOL secureIsOK = (!cookieIsSecure) || [scheme isEqual:@"https"];
-
- if (domainIsOK && pathIsOK && secureIsOK) {
- [foundCookies addObject:storedCookie];
- }
- }
- return foundCookies;
-}
-
-// return cookies for the given URL using the current cookie storage method
-- (NSArray *)cookiesForURL:(NSURL *)theURL {
-
- NSArray *cookies = nil;
- NSMutableArray *cookieStorageArray = nil;
-
- if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodStatic) {
- cookieStorageArray = gGTMFetcherStaticCookies;
- } else if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodFetchHistory) {
- cookieStorageArray
- = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryCookiesKey];
- } else {
- // kGTMHTTPFetcherCookieStorageMethodSystemDefault
- NSHTTPCookieStorage *storage
- = [NSHTTPCookieStorage sharedHTTPCookieStorage];
- cookies = [storage cookiesForURL:theURL];
- }
-
- if (cookieStorageArray) {
-
- @synchronized(cookieStorageArray) {
-
- // cookiesForURL returns a new array of immutable NSCookie objects
- // from cookieStorageArray
- cookies = [self cookiesForURL:theURL
- inArray:cookieStorageArray];
- }
- }
- return cookies;
-}
-
-
-// add all cookies in the array |newCookies| to the storage array,
-// replacing cookies in the storage array as appropriate
-// Side effect: removes expired cookies from the storage array
-- (void)setCookies:(NSArray *)newCookies
- inArray:(NSMutableArray *)cookieStorageArray {
-
- [self removeExpiredCookiesInArray:cookieStorageArray];
-
- NSHTTPCookie *newCookie;
-
- GTM_FOREACH_OBJECT(newCookie, newCookies) {
-
- if ([[newCookie name] length] > 0
- && [[newCookie domain] length] > 0
- && [[newCookie path] length] > 0) {
-
- // remove the cookie if it's currently in the array
- NSHTTPCookie *oldCookie = [self cookieMatchingCookie:newCookie
- inArray:cookieStorageArray];
- if (oldCookie) {
- [cookieStorageArray removeObject:oldCookie];
- }
-
- // make sure the cookie hasn't already expired
- NSDate *expiresDate = [newCookie expiresDate];
- if ((!expiresDate) || [expiresDate timeIntervalSinceNow] > 0) {
- [cookieStorageArray addObject:newCookie];
- }
-
- } else {
- _GTMDevAssert(NO, @"Cookie incomplete: %@", newCookie); // COV_NF_LINE
- }
- }
-}
-@end
-
-#pragma mark Logging
-
-// NOTE: Threads and Logging
-//
-// All the NSURLConnection callbacks happen on one thread, so we don't have
-// to put any synchronization into the logging code. Yes, the state around
-// logging (it's directory, etc.) could use it, but for now, that's punted.
-
-
-// We don't invoke Leopard methods on 10.4, because we check if the methods are
-// implemented before invoking it, but we need to be able to compile without
-// warnings.
-// This declaration means if you target <=10.4, this method will compile
-// without complaint in this source, so you must test with
-// -respondsToSelector:, too.
-#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
-@interface NSFileManager (LeopardMethodsOnTigerBuilds)
-- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;
-@end
-#endif
-// The iPhone Foundation removes the deprecated removeFileAtPath:handler:
-#if GTM_IPHONE_SDK
-@interface NSFileManager (TigerMethodsOniPhoneBuilds)
-- (BOOL)removeFileAtPath:(NSString *)path handler:(id)handler;
-@end
-#endif
-
-@implementation GTMHTTPFetcher (GTMHTTPFetcherLogging)
-
-// if GTM_HTTPFETCHER_ENABLE_LOGGING is defined by the user's project then
-// logging code will be compiled into the framework
-
-#if !GTM_HTTPFETCHER_ENABLE_LOGGING
-- (void)logFetchWithError:(NSError *)error {}
-
-+ (void)setLoggingDirectory:(NSString *)path {}
-+ (NSString *)loggingDirectory {return nil;}
-
-+ (void)setIsLoggingEnabled:(BOOL)flag {}
-+ (BOOL)isLoggingEnabled {return NO;}
-
-+ (void)setLoggingProcessName:(NSString *)str {}
-+ (NSString *)loggingProcessName {return nil;}
-
-+ (void)setLoggingDateStamp:(NSString *)str {}
-+ (NSString *)loggingDateStamp {return nil;}
-
-- (void)appendLoggedStreamData:(NSData *)newData {}
-- (void)logCapturePostStream {}
-#else // GTM_HTTPFETCHER_ENABLE_LOGGING
-
-// fetchers come and fetchers go, but statics are forever
-static BOOL gIsLoggingEnabled = NO;
-static NSString *gLoggingDirectoryPath = nil;
-static NSString *gLoggingDateStamp = nil;
-static NSString* gLoggingProcessName = nil;
-
-+ (void)setLoggingDirectory:(NSString *)path {
- [gLoggingDirectoryPath autorelease];
- gLoggingDirectoryPath = [path copy];
-}
-
-+ (NSString *)loggingDirectory {
-
- if (!gLoggingDirectoryPath) {
-
-#if GTM_IPHONE_SDK
- // default to a directory called GTMHTTPDebugLogs into a sandbox-safe
- // directory that a devloper can find easily, the application home
- NSArray *arr = [NSArray arrayWithObject:NSHomeDirectory()];
-#else
- // default to a directory called GTMHTTPDebugLogs in the desktop folder
- NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory,
- NSUserDomainMask, YES);
-#endif
-
- if ([arr count] > 0) {
- NSString *const kGTMLogFolderName = @"GTMHTTPDebugLogs";
-
- NSString *desktopPath = [arr objectAtIndex:0];
- NSString *logsFolderPath
- = [desktopPath stringByAppendingPathComponent:kGTMLogFolderName];
-
- BOOL doesFolderExist;
- BOOL isDir = NO;
- NSFileManager *fileManager = [NSFileManager defaultManager];
- doesFolderExist = [fileManager fileExistsAtPath:logsFolderPath
- isDirectory:&isDir];
-
- if (!doesFolderExist) {
- // make the directory
- doesFolderExist = [fileManager createDirectoryAtPath:logsFolderPath
- attributes:nil];
- }
-
- if (doesFolderExist) {
- // it's there; store it in the global
- gLoggingDirectoryPath = [logsFolderPath copy];
- }
- }
- }
- return gLoggingDirectoryPath;
-}
-
-+ (void)setIsLoggingEnabled:(BOOL)flag {
- gIsLoggingEnabled = flag;
-}
-
-+ (BOOL)isLoggingEnabled {
- return gIsLoggingEnabled;
-}
-
-+ (void)setLoggingProcessName:(NSString *)str {
- [gLoggingProcessName release];
- gLoggingProcessName = [str copy];
-}
-
-+ (NSString *)loggingProcessName {
-
- // get the process name (once per run) replacing spaces with underscores
- if (!gLoggingProcessName) {
-
- NSString *procName = [[NSProcessInfo processInfo] processName];
- NSMutableString *loggingProcessName;
- loggingProcessName = [[NSMutableString alloc] initWithString:procName];
- NSRange nameRange = NSMakeRange(0, [gLoggingProcessName length]);
- [loggingProcessName replaceOccurrencesOfString:@" "
- withString:@"_"
- options:0
- range:nameRange];
- gLoggingProcessName = loggingProcessName;
- }
- return gLoggingProcessName;
-}
-
-+ (void)setLoggingDateStamp:(NSString *)str {
- [gLoggingDateStamp release];
- gLoggingDateStamp = [str copy];
-}
-
-+ (NSString *)loggingDateStamp {
- // we'll pick one date stamp per run, so a run that starts at a later second
- // will get a unique results html file
- if (!gLoggingDateStamp) {
- // produce a string like 08-21_01-41-23PM
-
- NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
- [formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
- [formatter setDateFormat:@"M-dd_hh-mm-ssa"];
-
- gLoggingDateStamp = [[formatter stringFromDate:[NSDate date]] retain] ;
- }
- return gLoggingDateStamp;
-}
-
-- (NSString *)cleanParameterFollowing:(NSString *)paramName
- fromString:(NSString *)originalStr {
- // We don't want the password written to disk
- //
- // find "&Passwd=" in the string, and replace it and the stuff that
- // follows it with "Passwd=_snip_"
-
- NSRange passwdRange = [originalStr rangeOfString:@"&Passwd="];
- if (passwdRange.location != NSNotFound) {
-
- // we found Passwd=; find the & that follows the parameter
- NSUInteger origLength = [originalStr length];
- NSRange restOfString = NSMakeRange(passwdRange.location+1,
- origLength - passwdRange.location - 1);
- NSRange rangeOfFollowingAmp = [originalStr rangeOfString:@"&"
- options:0
- range:restOfString];
- NSRange replaceRange;
- if (rangeOfFollowingAmp.location == NSNotFound) {
- // found no other & so replace to end of string
- replaceRange = NSMakeRange(passwdRange.location,
- rangeOfFollowingAmp.location - passwdRange.location);
- } else {
- // another parameter after &Passwd=foo
- replaceRange = NSMakeRange(passwdRange.location,
- rangeOfFollowingAmp.location - passwdRange.location);
- }
-
- NSMutableString *result = [NSMutableString stringWithString:originalStr];
- NSString *replacement = [NSString stringWithFormat:@"%@_snip_", paramName];
-
- [result replaceCharactersInRange:replaceRange withString:replacement];
- return result;
- }
- return originalStr;
-}
-
-// stringFromStreamData creates a string given the supplied data
-//
-// If NSString can create a UTF-8 string from the data, then that is returned.
-//
-// Otherwise, this routine tries to find a MIME boundary at the beginning of
-// the data block, and uses that to break up the data into parts. Each part
-// will be used to try to make a UTF-8 string. For parts that fail, a
-// replacement string showing the part header and <<n bytes>> is supplied
-// in place of the binary data.
-
-- (NSString *)stringFromStreamData:(NSData *)data {
-
- if (data == nil) return nil;
-
- // optimistically, see if the whole data block is UTF-8
- NSString *streamDataStr
- = [[[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding] autorelease];
- if (streamDataStr) return streamDataStr;
-
- // Munge a buffer by replacing non-ASCII bytes with underscores,
- // and turn that munged buffer an NSString. That gives us a string
- // we can use with NSScanner.
- NSMutableData *mutableData = [NSMutableData dataWithData:data];
- unsigned char *bytes = [mutableData mutableBytes];
-
- for (NSUInteger idx = 0; idx < [mutableData length]; idx++) {
- if (bytes[idx] > 0x7F || bytes[idx] == 0) {
- bytes[idx] = '_';
- }
- }
-
- NSString *mungedStr
- = [[[NSString alloc] initWithData:mutableData
- encoding:NSUTF8StringEncoding] autorelease];
- if (mungedStr != nil) {
-
- // scan for the boundary string
- NSString *boundary = nil;
- NSScanner *scanner = [NSScanner scannerWithString:mungedStr];
-
- if ([scanner scanUpToString:@"\r\n" intoString:&boundary]
- && [boundary hasPrefix:@"--"]) {
-
- // we found a boundary string; use it to divide the string into parts
- NSArray *mungedParts = [mungedStr componentsSeparatedByString:boundary];
-
- // look at each of the munged parts in the original string, and try to
- // convert those into UTF-8
- NSMutableArray *origParts = [NSMutableArray array];
- NSUInteger offset = 0;
- for (NSUInteger partIdx = 0; partIdx < [mungedParts count]; partIdx++) {
-
- NSString *mungedPart = [mungedParts objectAtIndex:partIdx];
- NSUInteger partSize = [mungedPart length];
-
- NSRange range = NSMakeRange(offset, partSize);
- NSData *origPartData = [data subdataWithRange:range];
-
- NSString *origPartStr
- = [[[NSString alloc] initWithData:origPartData
- encoding:NSUTF8StringEncoding] autorelease];
- if (origPartStr) {
- // we could make this original part into UTF-8; use the string
- [origParts addObject:origPartStr];
- } else {
- // this part can't be made into UTF-8; scan the header, if we can
- NSString *header = nil;
- NSScanner *headerScanner = [NSScanner scannerWithString:mungedPart];
- if (![headerScanner scanUpToString:@"\r\n\r\n" intoString:&header]) {
- // we couldn't find a header
- header = @"";
- }
-
- // make a part string with the header and <<n bytes>>
- NSString *binStr = [NSString stringWithFormat:@"\r%@\r<<%u bytes>>\r",
- header, partSize - [header length]];
- [origParts addObject:binStr];
- }
- offset += partSize + [boundary length];
- }
-
- // rejoin the original parts
- streamDataStr = [origParts componentsJoinedByString:boundary];
- }
- }
-
- if (!streamDataStr) {
- // give up; just make a string showing the uploaded bytes
- streamDataStr = [NSString stringWithFormat:@"<<%u bytes>>", [data length]];
- }
- return streamDataStr;
-}
-
-// logFetchWithError is called following a successful or failed fetch attempt
-//
-// This method does all the work for appending to and creating log files
-
-- (void)logFetchWithError:(NSError *)error {
-
- if (![[self class] isLoggingEnabled]) return;
-
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- // TODO: add Javascript to display response data formatted in hex
-
- NSString *logDirectory = [[self class] loggingDirectory];
- NSString *processName = [[self class] loggingProcessName];
- NSString *dateStamp = [[self class] loggingDateStamp];
-
- // each response's NSData goes into its own xml or txt file, though all
- // responses for this run of the app share a main html file. This
- // counter tracks all fetch responses for this run of the app.
- static int zResponseCounter = 0;
- zResponseCounter++;
-
- // file name for the html file containing plain text in a <textarea>
- NSString *responseDataUnformattedFileName = nil;
-
- // file name for the "formatted" (raw) data file
- NSString *responseDataFormattedFileName = nil;
- NSUInteger responseDataLength = [downloadedData_ length];
-
- NSURLResponse *response = [self response];
- NSString *responseBaseName = nil;
-
- // if there's response data, decide what kind of file to put it in based
- // on the first bytes of the file or on the mime type supplied by the server
- if (responseDataLength) {
- NSString *responseDataExtn = nil;
-
- // generate a response file base name like
- // SyncProto_http_response_10-16_01-56-58PM_3
- responseBaseName = [NSString stringWithFormat:@"%@_http_response_%@_%d",
- processName, dateStamp, zResponseCounter];
-
- NSString *dataStr
- = [[[NSString alloc] initWithData:downloadedData_
- encoding:NSUTF8StringEncoding] autorelease];
- if (dataStr) {
- // we were able to make a UTF-8 string from the response data
-
- NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
- dataStr = [dataStr stringByTrimmingCharactersInSet:whitespaceSet];
-
- // save a plain-text version of the response data in an html cile
- // containing a wrapped, scrollable <textarea>
- //
- // we'll use <textarea rows="33" cols="108" readonly=true wrap=soft>
- // </textarea> to fit inside our iframe
- responseDataUnformattedFileName
- = [responseBaseName stringByAppendingPathExtension:@"html"];
- NSString *textFilePath
- = [logDirectory stringByAppendingPathComponent:responseDataUnformattedFileName];
-
- NSString* wrapFmt = @"<textarea rows=\"33\" cols=\"108\" readonly=true"
- " wrap=soft>\n%@\n</textarea>";
- NSString* wrappedStr = [NSString stringWithFormat:wrapFmt, dataStr];
- [wrappedStr writeToFile:textFilePath
- atomically:NO
- encoding:NSUTF8StringEncoding
- error:nil];
-
- // now determine the extension for the "formatted" file, which is really
- // the raw data written with an appropriate extension
-
- // for known file types, we'll write the data to a file with the
- // appropriate extension
- if ([dataStr hasPrefix:@"<?xml"]) {
- responseDataExtn = @"xml";
- } else if ([dataStr hasPrefix:@"<html"]) {
- responseDataExtn = @"html";
- } else {
- // add more types of identifiable text here
- }
-
- } else if ([[response MIMEType] isEqual:@"image/jpeg"]) {
- responseDataExtn = @"jpg";
- } else if ([[response MIMEType] isEqual:@"image/gif"]) {
- responseDataExtn = @"gif";
- } else if ([[response MIMEType] isEqual:@"image/png"]) {
- responseDataExtn = @"png";
- } else {
- // add more non-text types here
- }
-
- // if we have an extension, save the raw data in a file with that
- // extension to be our "formatted" display file
- if (responseDataExtn) {
- responseDataFormattedFileName
- = [responseBaseName stringByAppendingPathExtension:responseDataExtn];
- NSString *formattedFilePath
- = [logDirectory stringByAppendingPathComponent:responseDataFormattedFileName];
-
- [downloadedData_ writeToFile:formattedFilePath atomically:NO];
- }
- }
-
- // we'll have one main html file per run of the app
- NSString *htmlName = [NSString stringWithFormat:@"%@_http_log_%@.html",
- processName, dateStamp];
- NSString *htmlPath =[logDirectory stringByAppendingPathComponent:htmlName];
-
- // if the html file exists (from logging previous fetches) we don't need
- // to re-write the header or the scripts
- BOOL didFileExist = [fileManager fileExistsAtPath:htmlPath];
-
- NSMutableString* outputHTML = [NSMutableString string];
- NSURLRequest *request = [self request];
-
- // we need file names for the various div's that we're going to show and hide,
- // names unique to this response's bundle of data, so we format our div
- // names with the counter that we incremented earlier
- NSString *requestHeadersName
- = [NSString stringWithFormat:@"RequestHeaders%d", zResponseCounter];
- NSString *postDataName
- = [NSString stringWithFormat:@"PostData%d", zResponseCounter];
-
- NSString *responseHeadersName
- = [NSString stringWithFormat:@"ResponseHeaders%d", zResponseCounter];
- NSString *responseDataDivName
- = [NSString stringWithFormat:@"ResponseData%d", zResponseCounter];
- NSString *dataIFrameID
- = [NSString stringWithFormat:@"DataIFrame%d", zResponseCounter];
-
- // we need a header to say we'll have UTF-8 text
- if (!didFileExist) {
- [outputHTML appendFormat:@"<html><head><meta http-equiv=\"content-type\" "
- @"content=\"text/html; charset=UTF-8\"><title>%@ HTTP fetch log %@</title>",
- processName, dateStamp];
- }
-
- // write style sheets for each hideable element; each style sheet is
- // customized with our current response number, since they'll share
- // the html page with other responses
- NSString *styleFormat = @"<style type=\"text/css\">div#%@ "
- @"{ margin: 0px 20px 0px 20px; display: none; }</style>\n";
-
- [outputHTML appendFormat:styleFormat, requestHeadersName];
- [outputHTML appendFormat:styleFormat, postDataName];
- [outputHTML appendFormat:styleFormat, responseHeadersName];
- [outputHTML appendFormat:styleFormat, responseDataDivName];
-
- if (!didFileExist) {
- // write javascript functions. The first one shows/hides the layer
- // containing the iframe.
- NSString *scriptFormat = @"<script type=\"text/javascript\"> "
- @"function toggleLayer(whichLayer){ "
- @"var style2 = document.getElementById(whichLayer).style; "
- @"style2.display = style2.display ? \"\":\"block\";}</script>\n";
- [outputHTML appendFormat:scriptFormat];
-
- // the second function is passed the src file; if it's what's shown, it
- // toggles the iframe's visibility. If some other src is shown, it shows
- // the iframe and loads the new source. Note we want to load the source
- // whenever we show the iframe too since Firefox seems to format it wrong
- // when showing it if we don't reload it.
- NSString *toggleIFScriptFormat = @"<script type=\"text/javascript\"> "
- @"function toggleIFrame(whichLayer,iFrameID,newsrc)"
- @"{ \n var iFrameElem=document.getElementById(iFrameID); "
- @"if (iFrameElem.src.indexOf(newsrc) != -1) { toggleLayer(whichLayer); } "
- @"else { document.getElementById(whichLayer).style.display=\"block\"; } "
- @"iFrameElem.src=newsrc; }</script>\n</head>\n<body>\n";
- [outputHTML appendFormat:toggleIFScriptFormat];
- }
-
- // now write the visible html elements
-
- // write the date & time
- [outputHTML appendFormat:@"<b>%@</b><br>", [[NSDate date] description]];
-
- // write the request URL
- [outputHTML appendFormat:@"<b>request:</b> %@ <i>URL:</i> <code>%@</code><br>\n",
- [request HTTPMethod], [request URL]];
-
- // write the request headers, toggleable
- NSDictionary *requestHeaders = [request allHTTPHeaderFields];
- if ([requestHeaders count]) {
- NSString *requestHeadersFormat = @"<a href=\"javascript:toggleLayer('%@');\">"
- @"request headers (%d)</a><div id=\"%@\"><pre>%@</pre></div><br>\n";
- [outputHTML appendFormat:requestHeadersFormat,
- requestHeadersName, // layer name
- [requestHeaders count],
- requestHeadersName,
- [requestHeaders description]]; // description gives a human-readable dump
- } else {
- [outputHTML appendString:@"<i>Request headers: none</i><br>"];
- }
-
- // write the request post data, toggleable
- NSData *postData = postData_;
- if (loggedStreamData_) {
- postData = loggedStreamData_;
- }
-
- if ([postData length]) {
- NSString *postDataFormat = @"<a href=\"javascript:toggleLayer('%@');\">"
- "posted data (%d bytes)</a><div id=\"%@\">%@</div><br>\n";
- NSString *postDataStr = [self stringFromStreamData:postData];
- if (postDataStr) {
- NSString *postDataTextAreaFmt = @"<pre>%@</pre>";
- if ([postDataStr rangeOfString:@"<"].location != NSNotFound) {
- postDataTextAreaFmt = @"<textarea rows=\"15\" cols=\"100\""
- " readonly=true wrap=soft>\n%@\n</textarea>";
- }
- NSString *cleanedPostData = [self cleanParameterFollowing:@"&Passwd="
- fromString:postDataStr];
- NSString *postDataTextArea = [NSString stringWithFormat:
- postDataTextAreaFmt, cleanedPostData];
-
- [outputHTML appendFormat:postDataFormat,
- postDataName, // layer name
- [postData length],
- postDataName,
- postDataTextArea];
- }
- } else {
- // no post data
- }
-
- // write the response status, MIME type, URL
- if (response) {
- NSString *statusString = @"";
- if ([response respondsToSelector:@selector(statusCode)]) {
- NSInteger status = [(NSHTTPURLResponse *)response statusCode];
- statusString = @"200";
- if (status != 200) {
- // purple for errors
- statusString
- = [NSString stringWithFormat:@"<FONT COLOR=\"#FF00FF\">%d</FONT>",
- status];
- }
- }
-
- // show the response URL only if it's different from the request URL
- NSString *responseURLStr = @"";
- NSURL *responseURL = [response URL];
-
- if (responseURL && ![responseURL isEqual:[request URL]]) {
- NSString *responseURLFormat = @"<br><FONT COLOR=\"#FF00FF\">response URL:"
- @"</FONT> <code>%@</code>";
- responseURLStr = [NSString stringWithFormat:responseURLFormat,
- [responseURL absoluteString]];
- }
-
- NSDictionary *responseHeaders = nil;
- if ([response respondsToSelector:@selector(allHeaderFields)]) {
- responseHeaders = [(NSHTTPURLResponse *)response allHeaderFields];
- }
- [outputHTML appendFormat:@"<b>response:</b> <i>status:</i> %@ <i> "
- @"&nbsp;&nbsp;&nbsp;MIMEType:</i><code> %@</code>%@<br>\n",
- statusString,
- [response MIMEType],
- responseURLStr,
- responseHeaders ? [responseHeaders description] : @""];
-
- // write the response headers, toggleable
- if ([responseHeaders count]) {
-
- NSString *cookiesSet = [responseHeaders objectForKey:@"Set-Cookie"];
-
- NSString *responseHeadersFormat = @"<a href=\"javascript:toggleLayer("
- @"'%@');\">response headers (%d) %@</a><div id=\"%@\"><pre>%@</pre>"
- @"</div><br>\n";
- [outputHTML appendFormat:responseHeadersFormat,
- responseHeadersName,
- [responseHeaders count],
- (cookiesSet ? @"<i>sets cookies</i>" : @""),
- responseHeadersName,
- [responseHeaders description]];
-
- } else {
- [outputHTML appendString:@"<i>Response headers: none</i><br>\n"];
- }
- }
-
- // error
- if (error) {
- [outputHTML appendFormat:@"<b>error:</b> %@ <br>\n", [error description]];
- }
-
- // write the response data. We have links to show formatted and text
- // versions, but they both show it in the same iframe, and both
- // links also toggle visible/hidden
- if (responseDataFormattedFileName || responseDataUnformattedFileName) {
-
- // response data, toggleable links -- formatted and text versions
- if (responseDataFormattedFileName) {
- [outputHTML appendFormat:@"response data (%d bytes) formatted <b>%@</b> ",
- responseDataLength,
- [responseDataFormattedFileName pathExtension]];
-
- // inline (iframe) link
- NSString *responseInlineFormattedDataNameFormat = @"&nbsp;&nbsp;<a "
- @"href=\"javascript:toggleIFrame('%@','%@','%@');\">inline</a>\n";
- [outputHTML appendFormat:responseInlineFormattedDataNameFormat,
- responseDataDivName, // div ID
- dataIFrameID, // iframe ID (for reloading)
- responseDataFormattedFileName]; // src to reload
-
- // plain link (so the user can command-click it into another tab)
- [outputHTML appendFormat:@"&nbsp;&nbsp;<a href=\"%@\">stand-alone</a><br>\n",
- [responseDataFormattedFileName
- stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
- }
- if (responseDataUnformattedFileName) {
- [outputHTML appendFormat:@"response data (%d bytes) plain text ",
- responseDataLength];
-
- // inline (iframe) link
- NSString *responseInlineDataNameFormat = @"&nbsp;&nbsp;<a href=\""
- @"javascript:toggleIFrame('%@','%@','%@');\">inline</a> \n";
- [outputHTML appendFormat:responseInlineDataNameFormat,
- responseDataDivName, // div ID
- dataIFrameID, // iframe ID (for reloading)
- responseDataUnformattedFileName]; // src to reload
-
- // plain link (so the user can command-click it into another tab)
- [outputHTML appendFormat:@"&nbsp;&nbsp;<a href=\"%@\">stand-alone</a><br>\n",
- [responseDataUnformattedFileName
- stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
- }
-
- // make the iframe
- NSString *divHTMLFormat = @"<div id=\"%@\">%@</div><br>\n";
- NSString *src = responseDataFormattedFileName ?
- responseDataFormattedFileName : responseDataUnformattedFileName;
- NSString *escapedSrc
- = [src stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSString *iframeFmt = @" <iframe src=\"%@\" id=\"%@\" width=800 height=400>"
- @"\n<a href=\"%@\">%@</a>\n </iframe>\n";
- NSString *dataIFrameHTML = [NSString stringWithFormat:iframeFmt,
- escapedSrc, dataIFrameID, escapedSrc, src];
- [outputHTML appendFormat:divHTMLFormat,
- responseDataDivName, dataIFrameHTML];
- } else {
- // could not parse response data; just show the length of it
- [outputHTML appendFormat:@"<i>Response data: %d bytes </i>\n",
- responseDataLength];
- }
-
- [outputHTML appendString:@"<br><hr><p>"];
-
- // append the HTML to the main output file
- const char* htmlBytes = [outputHTML UTF8String];
- NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:htmlPath
- append:YES];
- [stream open];
- [stream write:(const uint8_t *) htmlBytes maxLength:strlen(htmlBytes)];
- [stream close];
-
- // make a symlink to the latest html
- NSString *symlinkName = [NSString stringWithFormat:@"%@_http_log_newest.html",
- processName];
- NSString *symlinkPath
- = [logDirectory stringByAppendingPathComponent:symlinkName];
-
- // removeFileAtPath might be going away, but removeItemAtPath does not exist
- // in 10.4
- if ([fileManager respondsToSelector:@selector(removeFileAtPath:handler:)]) {
- [fileManager removeFileAtPath:symlinkPath handler:nil];
- } else if ([fileManager respondsToSelector:@selector(removeItemAtPath:error:)]) {
- // To make the next line compile when targeting 10.4, we declare
- // removeItemAtPath:error: in an @interface above
- [fileManager removeItemAtPath:symlinkPath error:NULL];
- }
-
- [fileManager createSymbolicLinkAtPath:symlinkPath pathContent:htmlPath];
-}
-
-- (void)logCapturePostStream {
-
-#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
- // This is called when beginning a fetch. The caller should have already
- // verified that logging is enabled, and should have allocated
- // loggedStreamData_ as a mutable object.
-
- // If we're logging, we need to wrap the upload stream with our monitor
- // stream subclass that will call us back with the bytes being read from the
- // stream
-
- // our wrapper will retain the old post stream
- [postStream_ autorelease];
-
- // length can be
- postStream_ = [GTMInputStreamLogger inputStreamWithStream:postStream_
- length:0];
- [postStream_ retain];
-
- // we don't really want monitoring callbacks; our subclass will be
- // calling our appendLoggedStreamData: method at every read instead
- [(GTMInputStreamLogger *)postStream_ setMonitorDelegate:self
- selector:nil];
-#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-}
-
-- (void)appendLoggedStreamData:(NSData *)newData {
- [loggedStreamData_ appendData:newData];
-}
-
-#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
-@end
-
-#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-@implementation GTMInputStreamLogger
-- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
-
- // capture the read stream data, and pass it to the delegate to append to
- NSInteger result = [super read:buffer maxLength:len];
- if (result >= 0) {
- NSData *data = [NSData dataWithBytes:buffer length:result];
- [monitorDelegate_ appendLoggedStreamData:data];
- }
- return result;
-}
-@end
-#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
diff --git a/Foundation/GTMHTTPFetcherTest.m b/Foundation/GTMHTTPFetcherTest.m
deleted file mode 100644
index a541d1f..0000000
--- a/Foundation/GTMHTTPFetcherTest.m
+++ /dev/null
@@ -1,516 +0,0 @@
-//
-// GTMHTTPFetcherTest.m
-//
-// 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.
-//
-
-#import "GTMSenTestCase.h"
-#import "GTMHTTPFetcher.h"
-#import "GTMTestHTTPServer.h"
-#import "GTMUnitTestDevLog.h"
-
-@interface GTMHTTPFetcherTest : GTMTestCase {
- // these ivars are checked after fetches, and are reset by resetFetchResponse
- NSData *fetchedData_;
- NSError *fetcherError_;
- NSInteger fetchedStatus_;
- NSURLResponse *fetchedResponse_;
- NSMutableURLRequest *fetchedRequest_;
-
- // setup/teardown ivars
- NSMutableDictionary *fetchHistory_;
- GTMTestHTTPServer *testServer_;
-}
-@end
-
-@interface GTMHTTPFetcherTest (PrivateMethods)
-- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
- cachingDatedData:(BOOL)doCaching;
-
-- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
- cachingDatedData:(BOOL)doCaching
- retrySelector:(SEL)retrySel
- maxRetryInterval:(NSTimeInterval)maxRetryInterval
- userData:(id)userData;
-
-- (NSString *)fileURLStringToTestFileName:(NSString *)name;
-- (BOOL)countRetriesFetcher:(GTMHTTPFetcher *)fetcher
- willRetry:(BOOL)suggestedWillRetry
- forError:(NSError *)error;
-- (BOOL)fixRequestFetcher:(GTMHTTPFetcher *)fetcher
- willRetry:(BOOL)suggestedWillRetry
- forError:(NSError *)error;
-- (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data;
-- (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error;
-@end
-
-@implementation GTMHTTPFetcherTest
-
-static const NSTimeInterval kRunLoopInterval = 0.01;
-// The bogus-fetch test can take >10s to pass. Pick something way higher
-// to avoid failing.
-static const NSTimeInterval kGiveUpInterval = 60.0; // bail on the test if 60 seconds elapse
-
-static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
-
-- (void)setUp {
- fetchHistory_ = [[NSMutableDictionary alloc] init];
-
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- STAssertNotNil(testBundle, nil);
- NSString *docRoot = [testBundle pathForResource:@"GTMHTTPFetcherTestPage"
- ofType:@"html"];
- docRoot = [docRoot stringByDeletingLastPathComponent];
- STAssertNotNil(docRoot, nil);
-
- testServer_ = [[GTMTestHTTPServer alloc] initWithDocRoot:docRoot];
- STAssertNotNil(testServer_, @"failed to create a testing server");
-}
-
-- (void)resetFetchResponse {
- [fetchedData_ release];
- fetchedData_ = nil;
-
- [fetcherError_ release];
- fetcherError_ = nil;
-
- [fetchedRequest_ release];
- fetchedRequest_ = nil;
-
- [fetchedResponse_ release];
- fetchedResponse_ = nil;
-
- fetchedStatus_ = 0;
-}
-
-- (void)tearDown {
- [testServer_ release];
- testServer_ = nil;
-
- [self resetFetchResponse];
-
- [fetchHistory_ release];
- fetchHistory_ = nil;
-}
-
-- (void)testValidFetch {
- NSString *urlString = [self fileURLStringToTestFileName:kValidFileName];
-
- GTMHTTPFetcher *fetcher =
- [self doFetchWithURLString:urlString cachingDatedData:YES];
-
- STAssertNotNil(fetchedData_,
- @"failed to fetch data, status:%ld error:%@, URL:%@",
- (long)fetchedStatus_, fetcherError_, urlString);
- STAssertNotNil(fetchedResponse_,
- @"failed to get fetch response, status:%ld error:%@",
- (long)fetchedStatus_, fetcherError_);
- STAssertNotNil(fetchedRequest_, @"failed to get fetch request, URL %@",
- urlString);
- STAssertNil(fetcherError_, @"fetching data gave error: %@", fetcherError_);
- STAssertEquals(fetchedStatus_, (NSInteger)200,
- @"fetching data expected status 200, instead got %ld, for URL %@",
- (long)fetchedStatus_, urlString);
-
- // no cookies should be sent with our first request
- NSDictionary *headers = [fetchedRequest_ allHTTPHeaderFields];
- NSString *cookiesSent = [headers objectForKey:@"Cookie"];
- STAssertNil(cookiesSent, @"Cookies sent unexpectedly: %@", cookiesSent);
-
-
- // cookies should have been set by the response; specifically, TestCookie
- // should be set to the name of the file requested
- NSDictionary *responseHeaders;
-
- responseHeaders = [(NSHTTPURLResponse *)fetchedResponse_ allHeaderFields];
- NSString *cookiesSetString = [responseHeaders objectForKey:@"Set-Cookie"];
- NSString *cookieExpected = [NSString stringWithFormat:@"TestCookie=%@",
- kValidFileName];
- STAssertEqualObjects(cookiesSetString, cookieExpected, @"Unexpected cookie");
-
- // test properties
- NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
- @"val1", @"key1", @"val2", @"key2", nil];
- [fetcher setProperties:dict];
- STAssertEqualObjects([fetcher properties], dict, @"properties as dictionary");
- STAssertEqualObjects([fetcher propertyForKey:@"key2"], @"val2",
- @"single property");
-
- NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
- @"valx1", @"key1", @"val3", @"key3", nil];
- [fetcher setProperty:@"valx1" forKey:@"key1"];
- [fetcher setProperty:nil forKey:@"key2"];
- [fetcher setProperty:@"val3" forKey:@"key3"];
- STAssertEqualObjects([fetcher properties], dict2, @"property changes");
-
- // make a copy of the fetched data to compare with our next fetch from the
- // cache
- NSData *originalFetchedData = [[fetchedData_ copy] autorelease];
-
- // Now fetch again so the "If modified since" header will be set (because
- // we're calling setFetchHistory: below) and caching ON, and verify that we
- // got a good data from the cache, along with a "Not modified" status
-
- [self resetFetchResponse];
-
- [self doFetchWithURLString:urlString cachingDatedData:YES];
-
- STAssertEqualObjects(fetchedData_, originalFetchedData,
- @"cache data mismatch");
-
- STAssertNotNil(fetchedData_,
- @"failed to fetch data, status:%ld error:%@, URL:%@",
- (long)fetchedStatus_, fetcherError_, urlString);
- STAssertNotNil(fetchedResponse_,
- @"failed to get fetch response, status:%;d error:%@",
- (long)fetchedStatus_, fetcherError_);
- STAssertNotNil(fetchedRequest_, @"failed to get fetch request, URL %@",
- urlString);
- STAssertNil(fetcherError_, @"fetching data gave error: %@", fetcherError_);
-
- STAssertEquals(fetchedStatus_, (NSInteger)kGTMHTTPFetcherStatusNotModified, // 304
- @"fetching data expected status 304, instead got %ld, for URL %@",
- (long)fetchedStatus_, urlString);
-
- // the TestCookie set previously should be sent with this request
- cookiesSent = [[fetchedRequest_ allHTTPHeaderFields] objectForKey:@"Cookie"];
- STAssertEqualObjects(cookiesSent, cookieExpected, @"Cookie not sent");
-
- // Now fetch twice without caching enabled, and verify that we got a
- // "Not modified" status, along with a non-nil but empty NSData (which
- // is normal for that status code)
-
- [self resetFetchResponse];
-
- [fetchHistory_ removeAllObjects];
-
- [self doFetchWithURLString:urlString cachingDatedData:NO];
-
- STAssertEqualObjects(fetchedData_, originalFetchedData,
- @"cache data mismatch");
-
- [self resetFetchResponse];
- [self doFetchWithURLString:urlString cachingDatedData:NO];
-
- STAssertNotNil(fetchedData_, @"");
- STAssertEquals([fetchedData_ length], (NSUInteger)0, @"unexpected data");
- STAssertEquals(fetchedStatus_, (NSInteger)kGTMHTTPFetcherStatusNotModified,
- @"fetching data expected status 304, instead got %d", fetchedStatus_);
- STAssertNil(fetcherError_, @"unexpected error: %@", fetcherError_);
-
-}
-
-- (void)testBogusFetch {
- // fetch a live, invalid URL
- NSString *badURLString = @"http://localhost:86/";
- [self doFetchWithURLString:badURLString cachingDatedData:NO];
-
- const int kServiceUnavailableStatus = 503;
-
- if (fetchedStatus_ == kServiceUnavailableStatus) {
- // some proxies give a "service unavailable" error for bogus fetches
- } else {
-
- if (fetchedData_) {
- NSString *str = [[[NSString alloc] initWithData:fetchedData_
- encoding:NSUTF8StringEncoding] autorelease];
- STAssertNil(fetchedData_, @"fetched unexpected data: %@", str);
- }
-
- STAssertNotNil(fetcherError_, @"failed to receive fetching error");
- STAssertEquals(fetchedStatus_, (NSInteger)0,
- @"fetching data expected no status from no response, instead got %d",
- fetchedStatus_);
- }
-
- // fetch with a specific status code from our http server
- [self resetFetchResponse];
-
- NSString *invalidWebPageFile =
- [kValidFileName stringByAppendingString:@"?status=400"];
- NSString *statusUrlString =
- [self fileURLStringToTestFileName:invalidWebPageFile];
-
- [self doFetchWithURLString:statusUrlString cachingDatedData:NO];
-
- STAssertNotNil(fetchedData_, @"fetch lacked data with error info");
- STAssertNil(fetcherError_, @"expected bad status but got an error");
- STAssertEquals(fetchedStatus_, (NSInteger)400,
- @"unexpected status, error=%@", fetcherError_);
-}
-
-- (void)testRetryFetches {
- GTMHTTPFetcher *fetcher;
-
- NSString *invalidFile = [kValidFileName stringByAppendingString:@"?status=503"];
- NSString *urlString = [self fileURLStringToTestFileName:invalidFile];
-
- SEL countRetriesSel = @selector(countRetriesFetcher:willRetry:forError:);
- SEL fixRequestSel = @selector(fixRequestFetcher:willRetry:forError:);
-
- //
- // test: retry until timeout, then expect failure with status message
- //
-
- NSNumber *lotsOfRetriesNumber = [NSNumber numberWithInt:1000];
-
- fetcher= [self doFetchWithURLString:urlString
- cachingDatedData:NO
- retrySelector:countRetriesSel
- maxRetryInterval:5.0 // retry intervals of 1, 2, 4
- userData:lotsOfRetriesNumber];
-
- STAssertNotNil(fetchedData_, @"error data is expected");
- STAssertEquals(fetchedStatus_, (NSInteger)503, nil);
- STAssertEquals([fetcher retryCount], 3U, @"retry count unexpected");
-
- //
- // test: retry twice, then give up
- //
- [self resetFetchResponse];
-
- NSNumber *twoRetriesNumber = [NSNumber numberWithInt:2];
-
- fetcher= [self doFetchWithURLString:urlString
- cachingDatedData:NO
- retrySelector:countRetriesSel
- maxRetryInterval:10.0 // retry intervals of 1, 2, 4, 8
- userData:twoRetriesNumber];
-
- STAssertNotNil(fetchedData_, @"error data is expected");
- STAssertEquals(fetchedStatus_, (NSInteger)503, nil);
- STAssertEquals([fetcher retryCount], 2U, @"retry count unexpected");
-
-
- //
- // test: retry, making the request succeed on the first retry
- // by fixing the URL
- //
- [self resetFetchResponse];
-
- fetcher= [self doFetchWithURLString:urlString
- cachingDatedData:NO
- retrySelector:fixRequestSel
- maxRetryInterval:30.0 // should only retry once due to selector
- userData:lotsOfRetriesNumber];
-
- STAssertNotNil(fetchedData_, @"data is expected");
- STAssertEquals(fetchedStatus_, (NSInteger)200, nil);
- STAssertEquals([fetcher retryCount], 1U, @"retry count unexpected");
-}
-
-- (void)testNilFetch {
- GTMHTTPFetcher *fetcher = [[GTMHTTPFetcher alloc] init];
- [GTMUnitTestDevLog expectString:@"beginFetchWithDelegate requires a request"];
- BOOL wasGood = [fetcher beginFetchWithDelegate:nil
- didFinishSelector:NULL
- didFailSelector:NULL];
- STAssertFalse(wasGood, nil);
-}
-
-- (void)testCookies {
- // This is checking part one of
- // rdar://6293862 NSHTTPCookie cookieWithProperties doesn't work with
- // NSHTTPCookieOriginURL key
- NSString *urlString = @"http://www.apple.com/index.html";
- NSURL *url = [NSURL URLWithString:@"http://www.apple.com/index.html"];
-
- NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
- url, NSHTTPCookieOriginURL,
- @"testCookies", NSHTTPCookieName,
- @"1", NSHTTPCookieValue,
- nil];
- NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:properties];
-
- STAssertNil(cookie, nil);
-
- // This is checking part two of
- // rdar://6293862 NSHTTPCookie cookieWithProperties doesn't work with
- // NSHTTPCookieOriginURL key
- properties = [NSDictionary dictionaryWithObjectsAndKeys:
- urlString, NSHTTPCookieOriginURL,
- @"testCookies", NSHTTPCookieName,
- @"1", NSHTTPCookieValue,
- nil];
- cookie = [NSHTTPCookie cookieWithProperties:properties];
-
- STAssertNil(cookie, nil);
-}
-
-#pragma mark -
-
-- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
- cachingDatedData:(BOOL)doCaching {
-
- return [self doFetchWithURLString:(NSString *)urlString
- cachingDatedData:(BOOL)doCaching
- retrySelector:nil
- maxRetryInterval:0
- userData:nil];
-}
-
-- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
- cachingDatedData:(BOOL)doCaching
- retrySelector:(SEL)retrySel
- maxRetryInterval:(NSTimeInterval)maxRetryInterval
- userData:(id)userData {
-
- NSURL *url = [NSURL URLWithString:urlString];
- NSURLRequest *req = [NSURLRequest requestWithURL:url
- cachePolicy:NSURLRequestReloadIgnoringCacheData
- timeoutInterval:kGiveUpInterval];
- GTMHTTPFetcher *fetcher = [GTMHTTPFetcher httpFetcherWithRequest:req];
-
- STAssertNotNil(fetcher, @"Failed to allocate fetcher");
-
- // setting the fetch history will add the "If-modified-since" header
- // to repeat requests
- [fetcher setFetchHistory:fetchHistory_];
- if (doCaching != [fetcher shouldCacheDatedData]) {
- // only set the value when it changes since setting it to nil clears out
- // some of the state and our tests need the state between some non caching
- // fetches.
- [fetcher setShouldCacheDatedData:doCaching];
- }
-
- if (retrySel) {
- [fetcher setIsRetryEnabled:YES];
- [fetcher setRetrySelector:retrySel];
- [fetcher setMaxRetryInterval:maxRetryInterval];
- [fetcher setUserData:userData];
-
- // we force a minimum retry interval for unit testing; otherwise,
- // we'd have no idea how many retries will occur before the max
- // retry interval occurs, since the minimum would be random
- [fetcher setMinRetryInterval:1.0];
- }
-
- BOOL isFetching =
- [fetcher beginFetchWithDelegate:self
- didFinishSelector:@selector(fetcher:finishedWithData:)
- didFailSelector:@selector(fetcher:failedWithError:)];
- STAssertTrue(isFetching, @"Begin fetch failed");
-
- if (isFetching) {
-
- // Give time for the fetch to happen, but give up if 10 seconds elapse with
- // no response
- NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval];
- while ((!fetchedData_ && !fetcherError_) &&
- [giveUpDate timeIntervalSinceNow] > 0) {
- NSDate* loopIntervalDate =
- [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
- [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
- }
- }
-
- return fetcher;
-}
-
-- (NSString *)fileURLStringToTestFileName:(NSString *)name {
-
- // we need to create http URLs referring to the desired
- // resource to be found by the python http server running locally
-
- // return a localhost:port URL for the test file
- NSString *urlString = [NSString stringWithFormat:@"http://localhost:%d/%@",
- [testServer_ port], name];
-
- // we exclude the "?status=" that would indicate that the URL
- // should cause a retryable error
- NSRange range = [name rangeOfString:@"?status="];
- if (range.length > 0) {
- name = [name substringToIndex:range.location];
- }
-
- // we exclude the ".auth" extension that would indicate that the URL
- // should be tested with authentication
- if ([[name pathExtension] isEqual:@"auth"]) {
- name = [name stringByDeletingPathExtension];
- }
-
- // just for sanity, let's make sure we see the file locally, so
- // we can expect the Python http server to find it too
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- STAssertNotNil(testBundle, nil);
-
- NSString *filePath =
- [testBundle pathForResource:[name stringByDeletingPathExtension]
- ofType:[name pathExtension]];
- STAssertNotNil(filePath, nil);
-
- return urlString;
-}
-
-
-
-- (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data {
- fetchedData_ = [data copy];
- fetchedStatus_ = [fetcher statusCode]; // this implicitly tests that the fetcher has kept the response
- fetchedRequest_ = [[fetcher request] retain];
- fetchedResponse_ = [[fetcher response] retain];
-}
-
-- (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error {
- // if it's a status error, don't hang onto the error, just the status/data
- if ([[error domain] isEqual:kGTMHTTPFetcherStatusDomain]) {
- fetchedData_ = [[[error userInfo] objectForKey:kGTMHTTPFetcherStatusDataKey] copy];
- fetchedStatus_ = [error code]; // this implicitly tests that the fetcher has kept the response
- } else {
- fetcherError_ = [error retain];
- fetchedStatus_ = [fetcher statusCode];
- }
-}
-
-
-// Selector for allowing up to N retries, where N is an NSNumber in the
-// fetcher's userData
-- (BOOL)countRetriesFetcher:(GTMHTTPFetcher *)fetcher
- willRetry:(BOOL)suggestedWillRetry
- forError:(NSError *)error {
-
- int count = [fetcher retryCount];
- int allowedRetryCount = [[fetcher userData] intValue];
-
- BOOL shouldRetry = (count < allowedRetryCount);
-
- STAssertEquals([fetcher nextRetryInterval], pow(2.0, [fetcher retryCount]),
- @"unexpected next retry interval (expected %f, was %f)",
- pow(2.0, [fetcher retryCount]),
- [fetcher nextRetryInterval]);
-
- return shouldRetry;
-}
-
-// Selector for retrying and changing the request to one that will succeed
-- (BOOL)fixRequestFetcher:(GTMHTTPFetcher *)fetcher
- willRetry:(BOOL)suggestedWillRetry
- forError:(NSError *)error {
-
- STAssertEquals([fetcher nextRetryInterval], pow(2.0, [fetcher retryCount]),
- @"unexpected next retry interval (expected %f, was %f)",
- pow(2.0, [fetcher retryCount]),
- [fetcher nextRetryInterval]);
-
- // fix it - change the request to a URL which does not have a status value
- NSString *urlString = [self fileURLStringToTestFileName:kValidFileName];
-
- NSURL *url = [NSURL URLWithString:urlString];
- [fetcher setRequest:[NSURLRequest requestWithURL:url]];
-
- return YES; // do the retry fetch; it should succeed now
-}
-
-@end
diff --git a/Foundation/GTMProgressMonitorInputStream.h b/Foundation/GTMProgressMonitorInputStream.h
index d3da5cd..5f779bc 100644
--- a/Foundation/GTMProgressMonitorInputStream.h
+++ b/Foundation/GTMProgressMonitorInputStream.h
@@ -16,58 +16,7 @@
// the License.
//
-#import <Foundation/Foundation.h>
-
-// The monitored input stream calls back into the monitor delegate
-// with the number of bytes and total size
-//
-// - (void)inputStream:(GTMProgressMonitorInputStream *)stream
-// hasDeliveredByteCount:(unsigned long long)numberOfBytesRead
-// ofTotalByteCount:(unsigned long long)dataLength;
-
-@interface GTMProgressMonitorInputStream : NSInputStream {
-
- NSInputStream *inputStream_; // encapsulated stream that does the work
-
- unsigned long long dataSize_; // size of data in the source
- unsigned long long numBytesRead_; // bytes read from the input stream so far
-
- __weak id monitorDelegate_; // WEAK, not retained
- SEL monitorSelector_;
-
- __weak id monitorSource_; // WEAK, not retained
-}
-
-// Length is passed to the progress callback; it may be zero if the progress
-// callback can handle that (mainly meant so the monitor delegate can update the
-// bounds/position for a progress indicator.
-+ (id)inputStreamWithStream:(NSInputStream *)input
- length:(unsigned long long)length;
-
-- (id)initWithStream:(NSInputStream *)input
- length:(unsigned long long)length;
-
-// The monitor is called when bytes have been read
-//
-// monitorDelegate should respond to a selector with a signature matching:
-//
-// - (void)inputStream:(GTMProgressMonitorInputStream *)stream
-// hasDeliveredBytes:(unsigned long long)numReadSoFar
-// ofTotalBytes:(unsigned long long)total
-//
-// |total| will be the length passed when this GTMProgressMonitorInputStream was
-// created.
-
-- (void)setMonitorDelegate:(id)monitorDelegate // not retained
- selector:(SEL)monitorSelector;
-- (id)monitorDelegate;
-- (SEL)monitorSelector;
-
-// The source argument lets the delegate know the source of this input stream.
-// this class does nothing w/ this, it's just here to provide context to your
-// monitorDelegate.
-- (void)setMonitorSource:(id)source; // not retained
-- (id)monitorSource;
-
-@end
-
+// This class is no more. If you want something like it's functionality, look
+// at using the version in the Objective-C GData Client
+// (http://code.google.com/p/gdata-objectivec-client/). It provides the same
+// functionality and will continue to be maintained.
diff --git a/Foundation/GTMProgressMonitorInputStream.m b/Foundation/GTMProgressMonitorInputStream.m
deleted file mode 100644
index 94212dd..0000000
--- a/Foundation/GTMProgressMonitorInputStream.m
+++ /dev/null
@@ -1,217 +0,0 @@
-//
-// GTMProgressMonitorInputStream.m
-//
-// 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.
-//
-
-#import "GTMProgressMonitorInputStream.h"
-#import "GTMDefines.h"
-#import "GTMDebugSelectorValidation.h"
-
-@implementation GTMProgressMonitorInputStream
-
-// we'll forward all unhandled messages to the NSInputStream class
-// or to the encapsulated input stream. This is needed
-// for all messages sent to NSInputStream which aren't
-// handled by our superclass; that includes various private run
-// loop calls.
-+ (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
- return [NSInputStream methodSignatureForSelector:selector];
-}
-
-+ (void)forwardInvocation:(NSInvocation*)invocation {
- [invocation invokeWithTarget:[NSInputStream class]];
-}
-
-- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
- return [inputStream_ methodSignatureForSelector:selector];
-}
-
-- (void)forwardInvocation:(NSInvocation*)invocation {
- [invocation invokeWithTarget:inputStream_];
-}
-
-#pragma mark -
-
-+ (id)inputStreamWithStream:(NSInputStream *)input
- length:(unsigned long long)length {
-
- return [[[self alloc] initWithStream:input
- length:length] autorelease];
-}
-
-- (id)initWithStream:(NSInputStream *)input
- length:(unsigned long long)length {
-
- if ((self = [super init]) != nil) {
-
- inputStream_ = [input retain];
- dataSize_ = length;
-
- if (!inputStream_) {
- [self release];
- self = nil;
- }
- }
- return self;
-}
-
-#pragma mark -
-
-- (id)init {
- return [self initWithStream:nil length:0];
-}
-
-- (id)initWithData:(NSData *)data {
- unsigned long long dataLength = [data length];
- NSInputStream *inputStream = nil;
- if (data) {
- inputStream = [NSInputStream inputStreamWithData:data];
- }
- return [self initWithStream:inputStream length:dataLength];
-}
-
-- (id)initWithFileAtPath:(NSString *)path {
- NSDictionary *fileAttrs =
- [[NSFileManager defaultManager] fileAttributesAtPath:path
- traverseLink:YES];
- unsigned long long fileSize = [fileAttrs fileSize];
- NSInputStream *inputStream = nil;
- if (fileSize) {
- inputStream = [NSInputStream inputStreamWithFileAtPath:path];
- }
- return [self initWithStream:inputStream length:fileSize];
-}
-
-- (void)dealloc {
- [inputStream_ release];
- [super dealloc];
-}
-
-#pragma mark -
-
-- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
-
- NSInteger numRead = [inputStream_ read:buffer maxLength:len];
-
- if (numRead > 0) {
-
- numBytesRead_ += numRead;
-
- if (monitorDelegate_ && monitorSelector_) {
-
- // call the monitor delegate with the number of bytes read and the
- // total bytes read
-
- NSMethodSignature *signature = [monitorDelegate_ methodSignatureForSelector:monitorSelector_];
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
- [invocation setSelector:monitorSelector_];
- [invocation setTarget:monitorDelegate_];
- [invocation setArgument:&self atIndex:2];
- [invocation setArgument:&numBytesRead_ atIndex:3];
- [invocation setArgument:&dataSize_ atIndex:4];
- [invocation invoke];
- }
- }
- return numRead;
-}
-
-- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
- // TODO: doesn't this advance the stream so we should warn the progress
- // callback? NSInputStream w/ a file and NSData both seem to return NO for
- // this so I'm not sure how to test it.
- return [inputStream_ getBuffer:buffer length:len];
-}
-
-- (BOOL)hasBytesAvailable {
- return [inputStream_ hasBytesAvailable];
-}
-
-#pragma mark Standard messages
-
-// Pass expected messages to our encapsulated stream.
-//
-// We want our encapsulated NSInputStream to handle the standard messages;
-// we don't want the superclass to handle them.
-- (void)open {
- [inputStream_ open];
-}
-
-- (void)close {
- [inputStream_ close];
-}
-
-- (id)delegate {
- return [inputStream_ delegate];
-}
-
-- (void)setDelegate:(id)delegate {
- [inputStream_ setDelegate:delegate];
-}
-
-- (id)propertyForKey:(NSString *)key {
- return [inputStream_ propertyForKey:key];
-}
-
-- (BOOL)setProperty:(id)property forKey:(NSString *)key {
- return [inputStream_ setProperty:property forKey:key];
-}
-
-- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
- [inputStream_ scheduleInRunLoop:aRunLoop forMode:mode];
-}
-- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
- [inputStream_ removeFromRunLoop:aRunLoop forMode:mode];
-}
-
-- (NSStreamStatus)streamStatus {
- return [inputStream_ streamStatus];
-}
-
-- (NSError *)streamError {
- return [inputStream_ streamError];
-}
-
-#pragma mark Setters and getters
-
-- (void)setMonitorDelegate:(id)monitorDelegate
- selector:(SEL)monitorSelector {
- monitorDelegate_ = monitorDelegate; // non-retained
- monitorSelector_ = monitorSelector;
- GTMAssertSelectorNilOrImplementedWithArguments(monitorDelegate,
- monitorSelector,
- @encode(GTMProgressMonitorInputStream *),
- @encode(unsigned long long),
- @encode(unsigned long long),
- NULL);
-}
-
-- (id)monitorDelegate {
- return monitorDelegate_;
-}
-
-- (SEL)monitorSelector {
- return monitorSelector_;
-}
-
-- (void)setMonitorSource:(id)source {
- monitorSource_ = source; // non-retained
-}
-
-- (id)monitorSource {
- return monitorSource_;
-}
-
-@end
diff --git a/Foundation/GTMProgressMonitorInputStreamTest.m b/Foundation/GTMProgressMonitorInputStreamTest.m
deleted file mode 100644
index 69d331a..0000000
--- a/Foundation/GTMProgressMonitorInputStreamTest.m
+++ /dev/null
@@ -1,262 +0,0 @@
-//
-// GTMProgressMonitorInputStreamTest.m
-//
-// Copyright 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.
-//
-
-#import "GTMSenTestCase.h"
-#import "GTMProgressMonitorInputStream.h"
-#import "GTMUnitTestDevLog.h"
-#import "GTMSystemVersion.h"
-
-@interface GTMProgressMonitorInputStreamTest : GTMTestCase
-@end
-
-@interface TestStreamMonitor : NSObject {
- @private
- NSMutableArray *reportedDeliverySizesArray_;
- NSMutableSet *reportedTotalSizesSet_;
-}
-- (NSArray *)reportedSizes;
-- (NSSet *)reportedTotals;
-- (void)inputStream:(GTMProgressMonitorInputStream *)stream
- hasDeliveredBytes:(unsigned long long)numRead
- ofTotalBytes:(unsigned long long)total;
-@end
-
-@implementation GTMProgressMonitorInputStreamTest
-
-static const unsigned long long kSourceDataByteCount = (10000*10);
-
-- (void)testInit {
-
- // bad inputs
-
- // init
- STAssertNil([[GTMProgressMonitorInputStream alloc] init], nil);
- STAssertNil([[GTMProgressMonitorInputStream alloc] initWithStream:nil length:0], nil);
- STAssertNil([[GTMProgressMonitorInputStream alloc] initWithData:nil], nil);
- STAssertNil([[GTMProgressMonitorInputStream alloc] initWithFileAtPath:nil], nil);
-
- // class helpers
- STAssertNil([GTMProgressMonitorInputStream inputStreamWithStream:nil length:0], nil);
- STAssertNil([GTMProgressMonitorInputStream inputStreamWithData:nil], nil);
- STAssertNil([GTMProgressMonitorInputStream inputStreamWithFileAtPath:nil], nil);
-
- // some data for next round
- NSData *data = [@"some data" dataUsingEncoding:NSUTF8StringEncoding];
- STAssertNotNil(data, nil);
- GTMProgressMonitorInputStream *monStream;
-
- // good inputs
-
- NSInputStream *inputStream = [NSInputStream inputStreamWithData:data];
- STAssertNotNil(inputStream, nil);
- monStream =
- [GTMProgressMonitorInputStream inputStreamWithStream:inputStream
- length:[data length]];
- STAssertNotNil(monStream, nil);
-
- monStream = [GTMProgressMonitorInputStream inputStreamWithData:data];
- STAssertNotNil(monStream, nil);
-
- monStream =
- [GTMProgressMonitorInputStream inputStreamWithFileAtPath:@"/etc/services"];
- STAssertNotNil(monStream, nil);
-
-}
-
-- (void)testMonitorAccessors {
-
- NSData *data = [@"some data" dataUsingEncoding:NSUTF8StringEncoding];
- STAssertNotNil(data, nil);
- GTMProgressMonitorInputStream *monStream =
- [GTMProgressMonitorInputStream inputStreamWithData:data];
- STAssertNotNil(monStream, nil);
-
- TestStreamMonitor *monitor = [[[TestStreamMonitor alloc] init] autorelease];
- STAssertNotNil(monitor, nil);
-
- SEL monSel = @selector(inputStream:hasDeliveredBytes:ofTotalBytes:);
- [monStream setMonitorDelegate:monitor selector:monSel];
- STAssertEquals([monStream monitorDelegate], monitor, nil);
- STAssertEquals([monStream monitorSelector], monSel, nil);
-
- [monStream setMonitorSource:data];
- STAssertEquals([monStream monitorSource], data, nil);
-}
-
-- (void)testInputStreamAccessors {
-
- GTMProgressMonitorInputStream *monStream =
- [GTMProgressMonitorInputStream inputStreamWithFileAtPath:@"/etc/services"];
- STAssertNotNil(monStream, nil);
-
- // delegate
-
- [monStream setDelegate:self];
- STAssertEquals([monStream delegate], self, nil);
- [monStream setDelegate:nil];
- STAssertNil([monStream delegate], nil);
-
- if (![GTMSystemVersion isBuildEqualTo:kGTMSystemBuild10_6_0_WWDC]) {
- // error (we get unknown error before we open things)
- // This was changed on SnowLeopard.
- // rdar://689714 Calling streamError on unopened stream no longer returns
- // error
- // was filed to check this behaviour.
- NSError *err = [monStream streamError];
- STAssertEqualObjects([err domain], @"NSUnknownErrorDomain", nil);
- }
-
- // status and properties
-
- // pre open
- STAssertEquals([monStream streamStatus],
- (NSStreamStatus)NSStreamStatusNotOpen, nil);
- [monStream open];
- // post open
- STAssertEquals([monStream streamStatus],
- (NSStreamStatus)NSStreamStatusOpen, nil);
- STAssertEqualObjects([monStream propertyForKey:NSStreamFileCurrentOffsetKey],
- [NSNumber numberWithInt:0], nil);
- // read some
- uint8_t buf[8];
- long bytesRead = [monStream read:buf maxLength:sizeof(buf)];
- STAssertGreaterThanOrEqual(bytesRead, (long)sizeof(buf), nil);
- // post read
- STAssertEqualObjects([monStream propertyForKey:NSStreamFileCurrentOffsetKey],
- [NSNumber numberWithLong:bytesRead], nil);
- [monStream close];
- // post close
- STAssertEquals([monStream streamStatus],
- (NSStreamStatus)NSStreamStatusClosed, nil);
-
-}
-
-- (void)testProgressMessagesViaRead {
-
- // make a big data buffer (sourceData)
- NSMutableData *sourceData =
- [NSMutableData dataWithCapacity:kSourceDataByteCount];
- for (int idx = 0; idx < 10000; idx++) {
- [sourceData appendBytes:"0123456789" length:10];
- }
- STAssertEquals([sourceData length], (NSUInteger)kSourceDataByteCount, nil);
-
- // make a buffer to hold the data as read from the stream, and an array
- // to hold the size of each read
- NSMutableData *resultData = [NSMutableData data];
- NSMutableArray *deliverySizesArray = [NSMutableArray array];
-
- TestStreamMonitor *monitor = [[[TestStreamMonitor alloc] init] autorelease];
- STAssertNotNil(monitor, nil);
-
- // create the stream; set self as the monitor
- GTMProgressMonitorInputStream* monStream =
- [GTMProgressMonitorInputStream inputStreamWithData:sourceData];
- [monStream setMonitorDelegate:monitor
- selector:@selector(inputStream:hasDeliveredBytes:ofTotalBytes:)];
- [monStream open];
-
- // we'll read random-sized chunks of data from our stream, adding the chunk
- // size to deliverySizesArray and the data itself to resultData
- srandomdev();
-
- NSUInteger bytesReadSoFar = 0;
- uint8_t readBuffer[2048];
- while (1) {
- NSStreamStatus status = [monStream streamStatus];
- if (bytesReadSoFar < kSourceDataByteCount) {
- STAssertTrue([monStream hasBytesAvailable], nil);
- STAssertEquals(status, (NSStreamStatus)NSStreamStatusOpen, nil);
- } else {
- STAssertFalse([monStream hasBytesAvailable], nil);
- STAssertEquals(status, (NSStreamStatus)NSStreamStatusAtEnd, nil);
- }
-
- // read a random block size between 1 and 2048 bytes
- NSUInteger bytesToRead = (random() % sizeof(readBuffer)) + 1;
- NSInteger bytesRead = [monStream read:readBuffer maxLength:bytesToRead];
-
- // done?
- if (bytesRead <= 0) {
- break;
- }
-
- // save the data we just read, and the size of the read
- [resultData appendBytes:readBuffer length:bytesRead];
- bytesReadSoFar += bytesRead;
- NSNumber *bytesReadSoFarNumber =
- [NSNumber numberWithUnsignedLongLong:(unsigned long long)bytesReadSoFar];
- [deliverySizesArray addObject:bytesReadSoFarNumber];
- }
-
- [monStream close];
-
- // compare deliverySizesArray to the array built by our callback, and
- // resultData to the sourceData
- STAssertEqualObjects(deliverySizesArray, [monitor reportedSizes],
- @"unexpected size deliveries");
- NSNumber *sourceNumber =
- [NSNumber numberWithUnsignedLongLong:kSourceDataByteCount];
- STAssertEqualObjects([NSSet setWithObject:sourceNumber],
- [monitor reportedTotals],
- @"unexpected total sizes");
-
- // STAssertEqualObjects on the actual NSDatas is hanging when they are unequal
- // here so I'll just assert True
- STAssertTrue([sourceData isEqualToData:resultData],
- @"unexpected data read");
-}
-
-@end
-
-@implementation TestStreamMonitor
-
-- (id)init {
- self = [super init];
- if (self) {
- reportedDeliverySizesArray_ = [[NSMutableArray alloc] init];
- reportedTotalSizesSet_ = [[NSMutableSet alloc] init];
- }
- return self;
-}
-
-- (void) dealloc {
- [reportedDeliverySizesArray_ release];
- [reportedTotalSizesSet_ release];
- [super dealloc];
-}
-
-- (NSArray *)reportedSizes {
- return reportedDeliverySizesArray_;
-}
-
-- (NSSet *)reportedTotals {
- return reportedTotalSizesSet_;
-}
-
-- (void)inputStream:(GTMProgressMonitorInputStream *)stream
- hasDeliveredBytes:(unsigned long long)numRead
- ofTotalBytes:(unsigned long long)total {
- // add the number read so far to the array
- [reportedDeliverySizesArray_ addObject:
- [NSNumber numberWithUnsignedLongLong:numRead]];
- [reportedTotalSizesSet_ addObject:
- [NSNumber numberWithUnsignedLongLong:total]];
-}
-
-@end
diff --git a/Foundation/TestData/GTMHTTPFetcherTestPage.html b/Foundation/TestData/GTMHTTPFetcherTestPage.html
deleted file mode 100644
index 1f44469..0000000
--- a/Foundation/TestData/GTMHTTPFetcherTestPage.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
- <title>Test Page</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-</head>
-<body>
-This is a simple test page
-</body>
-</html>
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 52ae20a..b76fa17 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -138,7 +138,6 @@
8B7DCE1A0DFF39850017E983 /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */; };
8B7DCE1B0DFF39850017E983 /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */; };
8B7DCEF10E002C210017E983 /* GTMDevLogUnitTestingBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBE10DFF18720017E983 /* GTMDevLogUnitTestingBridge.m */; };
- 8B7E35750E048E2D00EF70C8 /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */; };
8B8B10290EEB8B1600E543D0 /* GTMHotKeyTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A420EE0EDDF8E000397A11 /* GTMHotKeyTextFieldTest.m */; };
8B8B102A0EEB8B2900E543D0 /* GTMCarbonEventTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3E29290EEB53F3000681D8 /* GTMCarbonEventTest.m */; };
8B8B10F90EEB8B9E00E543D0 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */; };
@@ -183,7 +182,6 @@
F42597790E23FE3A003BEA3E /* GTMNSString+FindFolder.h in Headers */ = {isa = PBXBuildFile; fileRef = F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */; settings = {ATTRIBUTES = (Public, ); }; };
F425977A0E23FE3A003BEA3E /* GTMNSString+FindFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */; };
F425977F0E23FE43003BEA3E /* GTMNSString+FindFolderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */; };
- F427EFC40E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */; };
F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
F428FF040D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */; };
F428FF090D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */; };
@@ -216,11 +214,6 @@
F435DE7C0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */; };
F435DE7D0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE7B0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff */; };
F435DE8B0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */; };
- F435E0890DC63F6D0069CDE8 /* GTMHTTPFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
- F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */; };
- F435E27F0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
- F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */; };
- F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */ = {isa = PBXBuildFile; fileRef = F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */; };
F437F55D0D50BC0A00F5C3A4 /* GTMRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */; settings = {ATTRIBUTES = (Public, ); }; };
F437F55E0D50BC0A00F5C3A4 /* GTMRegex.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */; };
F437F5620D50BC1D00F5C3A4 /* GTMRegexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */; };
@@ -475,7 +468,6 @@
F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+FindFolder.h"; sourceTree = "<group>"; };
F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+FindFolder.m"; sourceTree = "<group>"; };
F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+FindFolderTest.m"; sourceTree = "<group>"; };
- F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStreamTest.m; sourceTree = "<group>"; };
F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+CGPath.h"; sourceTree = "<group>"; };
F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPath.m"; sourceTree = "<group>"; };
F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPathTest.m"; sourceTree = "<group>"; };
@@ -489,12 +481,6 @@
F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.ppc64.tiff"; sourceTree = "<group>"; };
F435DE7B0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.x86_64.tiff"; sourceTree = "<group>"; };
F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.ppc64.tiff"; sourceTree = "<group>"; };
- F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPFetcher.h; sourceTree = "<group>"; };
- F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcher.m; sourceTree = "<group>"; };
- F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMProgressMonitorInputStream.h; sourceTree = "<group>"; };
- F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStream.m; sourceTree = "<group>"; };
- F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcherTest.m; sourceTree = "<group>"; };
- F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GTMHTTPFetcherTestPage.html; sourceTree = "<group>"; };
F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMRegex.h; sourceTree = "<group>"; };
F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegex.m; sourceTree = "<group>"; };
F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegexTest.m; sourceTree = "<group>"; };
@@ -754,14 +740,6 @@
path = TestData;
sourceTree = "<group>";
};
- F435E4B50DC903E20069CDE8 /* TestData */ = {
- isa = PBXGroup;
- children = (
- F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */,
- );
- path = TestData;
- sourceTree = "<group>";
- };
F48B91030D94485500D45044 /* TigerGcov */ = {
isa = PBXGroup;
children = (
@@ -861,9 +839,6 @@
F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */,
F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */,
F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */,
- F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */,
- F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */,
- F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */,
F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */,
F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */,
F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */,
@@ -922,9 +897,6 @@
F9FD945E0E1D30F80005867E /* GTMPath.h */,
F9FD945C0E1D30F80005867E /* GTMPath.m */,
F9FD945D0E1D30F80005867E /* GTMPathTest.m */,
- F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */,
- F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */,
- F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */,
F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */,
F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */,
F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */,
@@ -949,7 +921,6 @@
8B3AA9EF0E033E23007E31B5 /* GTMValidatingContainers.h */,
8B3AA9F00E033E23007E31B5 /* GTMValidatingContainers.m */,
8B3AA9F70E033E5F007E31B5 /* GTMValidatingContainersTest.m */,
- F435E4B50DC903E20069CDE8 /* TestData */,
);
path = Foundation;
sourceTree = "<group>";
@@ -1060,8 +1031,6 @@
F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */,
8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */,
F41D258B0DBD21A300774EEB /* GTMBase64.h in Headers */,
- F435E0890DC63F6D0069CDE8 /* GTMHTTPFetcher.h in Headers */,
- F435E27F0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h in Headers */,
F431221D0DD4E3B800F45252 /* GTMStackTrace.h in Headers */,
33C372A60DD8A88500E97817 /* GTMNSString+URLArguments.h in Headers */,
33C374380DD8D44800E97817 /* GTMNSDictionary+URLArguments.h in Headers */,
@@ -1284,7 +1253,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1422,14 +1390,12 @@
8B7DCE1A0DFF39850017E983 /* GTMSenTestCase.m in Sources */,
F41A6F850E02EC4D00788A6C /* GTMSignalHandlerTest.m in Sources */,
8B3AA9F80E033E5F007E31B5 /* GTMValidatingContainersTest.m in Sources */,
- 8B7E35750E048E2D00EF70C8 /* GTMHTTPFetcherTest.m in Sources */,
F9FD94640E1D312E0005867E /* GTMPathTest.m in Sources */,
F425974B0E23AA94003BEA3E /* GTMNSString+ReplaceTest.m in Sources */,
F425977F0E23FE43003BEA3E /* GTMNSString+FindFolderTest.m in Sources */,
F98680C40E2C164300CEE8BF /* GTMLoggerTest.m in Sources */,
F98681960E2C20C100CEE8BF /* GTMLogger+ASLTest.m in Sources */,
F95803FB0E2FB0A80049A088 /* GTMLoggerRingBufferWriterTest.m in Sources */,
- F427EFC40E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m in Sources */,
8B1B49260E5F97C800A08972 /* GTMExceptionalInlinesTest.m in Sources */,
8BE839AA0E8AF72E00C611B0 /* GTMDebugThreadValidationTest.m in Sources */,
F41711380ECDFBE100B9B276 /* GTMLightweightProxyTest.m in Sources */,
@@ -1467,8 +1433,6 @@
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */,
8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */,
F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */,
- F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */,
- F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */,
33C372A70DD8A88500E97817 /* GTMNSString+URLArguments.m in Sources */,
33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */,
F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */,
diff --git a/GTMDefines.h b/GTMDefines.h
index 5fbd773..5012e49 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -34,17 +34,6 @@
// ----------------------------------------------------------------------------
-// GTMHTTPFetcher will support logging by default but only hook its input
-// stream support for logging when requested. You can control the inclusion of
-// the code by providing your own definitions for these w/in a prefix header.
-//
-#ifndef GTM_HTTPFETCHER_ENABLE_LOGGING
- #define GTM_HTTPFETCHER_ENABLE_LOGGING 1
-#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
-#ifndef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
- #define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
-#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
-
// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
// when a validation fails. If you implement your own validators, you may want
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index 4221f3f..ffd1edc 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -30,11 +30,9 @@
8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */; };
8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */; };
8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */; };
- 8B3AA9220E033624007E31B5 /* GTMHTTPFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA91D0E033624007E31B5 /* GTMHTTPFetcher.m */; };
8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */; };
8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */; };
8B3AA9340E0336AC007E31B5 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */; };
- 8B3AA96A0E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html in Resources */ = {isa = PBXBuildFile; fileRef = 8B3AA9690E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html */; };
8B41EC0F0E0711D40040CF9F /* GTMValidatingContainersTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */; };
8B41EC100E0711D40040CF9F /* GTMValidatingContainers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */; };
8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */; };
@@ -72,7 +70,6 @@
8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; };
8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; };
8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; };
- 8BDA25120E759A6300C9769D /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA91E0E033624007E31B5 /* GTMHTTPFetcherTest.m */; };
8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; };
8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; };
8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */; };
@@ -89,8 +86,6 @@
F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFCC0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m */; };
F418AFD70E755D44004FB565 /* GTMPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD50E755D44004FB565 /* GTMPath.m */; };
F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD60E755D44004FB565 /* GTMPathTest.m */; };
- F418AFEB0E755F21004FB565 /* GTMProgressMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */; };
- F418AFEC0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */; };
F439ADEB0DBD3C0000BE9B91 /* GTMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */; };
F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */; };
F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */; };
@@ -126,16 +121,12 @@
8B3AA8F00E032FC7007E31B5 /* GTMNSString+URLArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+URLArguments.h"; sourceTree = "<group>"; };
8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArguments.m"; sourceTree = "<group>"; };
8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArgumentsTest.m"; sourceTree = "<group>"; };
- 8B3AA91C0E033624007E31B5 /* GTMHTTPFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPFetcher.h; sourceTree = "<group>"; };
- 8B3AA91D0E033624007E31B5 /* GTMHTTPFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcher.m; sourceTree = "<group>"; };
- 8B3AA91E0E033624007E31B5 /* GTMHTTPFetcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcherTest.m; sourceTree = "<group>"; };
8B3AA91F0E033624007E31B5 /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = "<group>"; };
8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = "<group>"; };
8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServerTest.m; sourceTree = "<group>"; };
8B3AA9270E033647007E31B5 /* GTMTestHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTestHTTPServer.h; sourceTree = "<group>"; };
8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTestHTTPServer.m; sourceTree = "<group>"; };
8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
- 8B3AA9690E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GTMHTTPFetcherTestPage.html; sourceTree = "<group>"; };
8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMValidatingContainersTest.m; sourceTree = "<group>"; };
8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMValidatingContainers.m; sourceTree = "<group>"; };
8B41EC0E0E0711D40040CF9F /* GTMValidatingContainers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMValidatingContainers.h; sourceTree = "<group>"; };
@@ -223,9 +214,6 @@
F418AFD40E755D44004FB565 /* GTMPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMPath.h; sourceTree = "<group>"; };
F418AFD50E755D44004FB565 /* GTMPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPath.m; sourceTree = "<group>"; };
F418AFD60E755D44004FB565 /* GTMPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPathTest.m; sourceTree = "<group>"; };
- F418AFE80E755F21004FB565 /* GTMProgressMonitorInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMProgressMonitorInputStream.h; sourceTree = "<group>"; };
- F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStream.m; sourceTree = "<group>"; };
- F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStreamTest.m; sourceTree = "<group>"; };
F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMBase64.h; sourceTree = "<group>"; };
F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64.m; sourceTree = "<group>"; };
F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64Test.m; sourceTree = "<group>"; };
@@ -298,14 +286,6 @@
name = Frameworks;
sourceTree = "<group>";
};
- 8B3AA9680E0337E4007E31B5 /* TestData */ = {
- isa = PBXGroup;
- children = (
- 8B3AA9690E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html */,
- );
- path = TestData;
- sourceTree = "<group>";
- };
8BA5F4060E75669000798036 /* iPhone */ = {
isa = PBXGroup;
children = (
@@ -344,9 +324,6 @@
F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */,
F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */,
F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */,
- 8B3AA91C0E033624007E31B5 /* GTMHTTPFetcher.h */,
- 8B3AA91D0E033624007E31B5 /* GTMHTTPFetcher.m */,
- 8B3AA91E0E033624007E31B5 /* GTMHTTPFetcherTest.m */,
8B3AA91F0E033624007E31B5 /* GTMHTTPServer.h */,
8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */,
8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */,
@@ -391,9 +368,6 @@
F418AFD40E755D44004FB565 /* GTMPath.h */,
F418AFD50E755D44004FB565 /* GTMPath.m */,
F418AFD60E755D44004FB565 /* GTMPathTest.m */,
- F418AFE80E755F21004FB565 /* GTMProgressMonitorInputStream.h */,
- F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */,
- F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */,
8BC047910DAE928A00C2D1CA /* GTMRegex.h */,
8BC047920DAE928A00C2D1CA /* GTMRegex.m */,
8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */,
@@ -406,7 +380,6 @@
8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */,
8B41EC0E0E0711D40040CF9F /* GTMValidatingContainers.h */,
8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */,
- 8B3AA9680E0337E4007E31B5 /* TestData */,
);
path = Foundation;
sourceTree = "<group>";
@@ -552,7 +525,6 @@
8BC0486B0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState in Resources */,
8BC0486C0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png in Resources */,
8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */,
- 8B3AA96A0E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html in Resources */,
8BA5F40C0E75669000798036 /* phone.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -612,7 +584,6 @@
67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */,
8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */,
8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */,
- 8B3AA9220E033624007E31B5 /* GTMHTTPFetcher.m in Sources */,
8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */,
8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */,
8B41EC0F0E0711D40040CF9F /* GTMValidatingContainersTest.m in Sources */,
@@ -627,11 +598,8 @@
F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */,
F418AFD70E755D44004FB565 /* GTMPath.m in Sources */,
F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */,
- F418AFEB0E755F21004FB565 /* GTMProgressMonitorInputStream.m in Sources */,
- F418AFEC0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m in Sources */,
8BA5F40B0E75669000798036 /* GTMABAddressBook.m in Sources */,
8BA5F52C0E7567AB00798036 /* GTMABAddressBookTest.m in Sources */,
- 8BDA25120E759A6300C9769D /* GTMHTTPFetcherTest.m in Sources */,
8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */,
8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */,
8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */,
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 8b368e6..4b87e72 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -238,6 +238,12 @@ Changes since 1.5.1
connections from any type of port. GTMTransientRootProxy is designed for
NSMachPorts while GTMTransientRootSocketProxy is for NSSocketPorts.
+- Finally dropped GTMHTTPFetcher and GTMProgressMonitorInputStream, GData
+ versions now pretty much line up w/ these, so rather then both projects
+ maintaing them, we've dropped them and point folks at the gdata versions
+ which can be used independent of the rest of GData.
+
+
Release 1.5.1
Changes since 1.5.0
16-June-2008