package org.cocoa4android.util.asihttp;
import org.cocoa4android.cf.CFHTTPAuthenticationRef;
import org.cocoa4android.cf.CFHTTPMessageRef;
import org.cocoa4android.ns.NSArray;
import org.cocoa4android.ns.NSData;
import org.cocoa4android.ns.NSDate;
import org.cocoa4android.ns.NSDictionary;
import org.cocoa4android.ns.NSError;
import org.cocoa4android.ns.NSInputStream;
import org.cocoa4android.ns.NSLock;
import org.cocoa4android.ns.NSMutableArray;
import org.cocoa4android.ns.NSMutableData;
import org.cocoa4android.ns.NSMutableDictionary;
import org.cocoa4android.ns.NSNumber;
import org.cocoa4android.ns.NSOperation;
import org.cocoa4android.ns.NSOperationQueue;
import org.cocoa4android.ns.NSOutputStream;
import org.cocoa4android.ns.NSRecursiveLock;
import org.cocoa4android.ns.NSString.NSStringEncoding;
import org.cocoa4android.ns.NSThread;
import org.cocoa4android.ns.NSTimer;
import org.cocoa4android.ns.NSURL;
import org.cocoa4android.sec.SecIdentityRef;
import org.cocoa4android.ui.UIBackgroundTaskIdentifier;
import org.cocoa4android.ui.UIDevice;
public class ASIHTTPRequest extends NSOperation {
// Automatically set on build
String ASIHTTPRequestVersion = "v1.8-21 2010-12-04";
final String NetworkRequestErrorDomain = "ASIHTTPRequestErrorDomain";
static String ASIHTTPRequestRunLoopMode = "ASIHTTPRequestRunLoopMode";
//static final CFOptionFlags kNetworkEvents = kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred;
// In memory caches of credentials, used on when useSessionPersistence is YES
static NSMutableArray sessionCredentialsStore = null;
static NSMutableArray sessionProxyCredentialsStore = null;
// This lock mediates access to session credentials
static NSRecursiveLock sessionCredentialsLock = null;
// We keep track of cookies we have received here so we can remove them from the sharedHTTPCookieStorage later
static NSMutableArray sessionCookies = null;
// The number of times we will allow requests to redirect before we fail with a redirection error
final int RedirectionLimit = 5;
// The default number of seconds to use for a timeout
static double defaultTimeOutSeconds = 10;
// This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
static NSRecursiveLock progressLock;
static NSError ASIRequestCancelledError;
static NSError ASIRequestTimedOutError;
static NSError ASIAuthenticationError;
static NSError ASIUnableToCreateRequestError;
static NSError ASITooMuchRedirectionError;
static NSMutableArray bandwidthUsageTracker = null;
static long averageBandwidthUsedPerSecond = 0;
// These are used for queuing persistent connections on the same connection
// Incremented every time we specify we want a new connection
static int nextConnectionNumberToCreate = 0;
// An array of connectionInfo dictionaries.
// When attempting a persistent connection, we look here to try to find an existing connection to the same server that is currently not in use
static NSMutableArray persistentConnectionsPool = null;
// Mediates access to the persistent connections pool
static NSRecursiveLock connectionsLock = null;
// Each request gets a new id, we store this rather than a ref to the request itself in the connectionInfo dictionary.
// We do this so we don't have to keep the request around while we wait for the connection to expire
static int nextRequestID = 0;
// Records how much bandwidth all requests combined have used in the last second
static long bandwidthUsedInLastSecond = 0;
// A date one second in the future from the time it was created
static NSDate bandwidthMeasurementDate = null;
// Since throttling variables are shared among all requests, we'll use a lock to mediate access
static NSLock bandwidthThrottlingLock = null;
// the maximum number of bytes that can be transmitted in one second
static long maxBandwidthPerSecond = 0;
// A default figure for throttling bandwidth on mobile devices
final long ASIWWANBandwidthThrottleAmount = 14800;
// YES when bandwidth throttling is active
// This flag does not denote whether throttling is turned on - rather whether it is currently in use
// It will be set to NO when throttling was turned on with setShouldThrottleBandwidthForWWAN, but a WI-FI connection is active
static boolean isBandwidthThrottled = NO;
// When YES, bandwidth will be automatically throttled when using WWAN (3G/Edge/GPRS)
// Wifi will not be throttled
static boolean shouldThrottleBandwithForWWANOnly = NO;
// Mediates access to the session cookies so requests
static NSRecursiveLock sessionCookiesLock = null;
// This lock ensures delegates only receive one notification that authentication is required at once
// When using ASIAuthenticationDialogs, it also ensures only one dialog is shown at once
// If a request can't acquire the lock immediately, it means a dialog is being shown or a delegate is handling the authentication challenge
// Once it gets the lock, it will try to look for existing credentials again rather than showing the dialog / notifying the delegate
// This is so it can make use of any credentials supplied for the other request, if they are appropriate
static NSRecursiveLock delegateAuthenticationLock = null;
// When throttling bandwidth, Set to a date in future that we will allow all requests to wake up and reschedule their streams
static NSDate throttleWakeUpTime = null;
static ASICacheDelegate defaultCache = null;
// Used for tracking when requests are using the network
static int runningRequestCount = 0;
// You can use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself
// Alternatively, override showNetworkActivityIndicator / hideNetworkActivityIndicator
// By default this does nothing on Mac OS X, but again override the above methods for a different behaviour
static boolean shouldUpdateNetworkActivityIndicator = YES;
//**Queue stuff**/
// The thread all requests will run on
// Hangs around forever, but will be blocked unless there are requests underway
static NSThread networkThread = null;
static NSOperationQueue sharedQueue = null;
static{
persistentConnectionsPool = new NSMutableArray();
connectionsLock = new NSRecursiveLock();
progressLock = new NSRecursiveLock();
bandwidthThrottlingLock = new NSLock();
sessionCookiesLock = new NSRecursiveLock();
sessionCredentialsLock = new NSRecursiveLock();
delegateAuthenticationLock = new NSRecursiveLock();
bandwidthUsageTracker = NSMutableArray.arrayWithCapacity(5);
//FIXME no errors
sharedQueue = new NSOperationQueue();
sharedQueue.setMaxConcurrentOperationCount(4);
}
public static double defaultTimeOutSeconds(){
return defaultTimeOutSeconds;
}
public static boolean isMultitaskingSupported()
{
boolean multiTaskingSupported = NO;
if (UIDevice.currentDevice()!=null) {
multiTaskingSupported =UIDevice.currentDevice().isMultitaskingSupported();
}
return multiTaskingSupported;
}
// The url for this operation, should include GET params in the query string where appropriate
private NSURL url;
public NSURL URL() {
return url;
}
public void setURL(NSURL url) {
this.url = url;
}
// Will always contain the original url used for making the request (the value of url can change when a request is redirected)
private NSURL originalURL;
public NSURL originalURL() {
return originalURL;
}
public void setOriginalURL(NSURL originalURL) {
this.originalURL = originalURL;
}
// Temporarily stores the url we are about to redirect to. Will be nil again when we do redirect
private NSURL redirectURL;
public NSURL redirectURL() {
return redirectURL;
}
public void setRedirectURL(NSURL redirectURL) {
this.redirectURL = redirectURL;
}
// The delegate, you need to manage setting and talking to your delegate in your subclasses
private ASIHTTPRequestDelegate delegate;
// HTTP method to use (GET / POST / PUT / DELETE / HEAD). Defaults to GET
private String requestMethod;
public String requestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
// Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false)
private NSMutableData postBody;
// gzipped request body used when shouldCompressRequestBody is YES
private NSData compressedPostBody;
// When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads)
// Automatically set to true in ASIFormDataRequests when using setFile:forKey:
boolean shouldStreamPostDataFromDisk;
// Path to file used to store post body (when shouldStreamPostDataFromDisk is true)
// You can set this yourself - useful if you want to PUT a file from local disk
String postBodyFilePath;
// Path to a temporary file used to store a deflated post body (when shouldCompressPostBody is YES)
String compressedPostBodyFilePath;
// Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request)
boolean didCreateTemporaryPostDataFile;
// Used when writing to the post body when shouldStreamPostDataFromDisk is true (via appendPostData: or appendPostDataFromFile:)
NSOutputStream postBodyWriteStream;
// Used for reading from the post body when sending the request
NSInputStream postBodyReadStream;
// Dictionary for custom HTTP request headers
NSMutableDictionary requestHeaders;
// Set to YES when the request header dictionary has been populated, used to prevent this happening more than once
boolean haveBuiltRequestHeaders;
// Will be populated with HTTP response headers from the server
NSDictionary responseHeaders;
public NSDictionary responseHeaders() {
return responseHeaders;
}
public void setResponseHeaders(NSDictionary responseHeaders) {
this.responseHeaders = responseHeaders;
}
// Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you
NSMutableArray requestCookies;
public NSMutableArray requestCookies() {
return requestCookies;
}
public void setRequestCookies(NSMutableArray requestCookies) {
this.requestCookies = requestCookies;
}
// Will be populated with cookies
NSArray responseCookies;
// If use useCookiePersistence is true, network requests will present valid cookies from previous requests
boolean useCookiePersistence;
public boolean useCookiePersistence() {
return useCookiePersistence;
}
public void setUseCookiePersistence(boolean useCookiePersistence) {
this.useCookiePersistence = useCookiePersistence;
}
// If useKeychainPersistence is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented
boolean useKeychainPersistence;
// If useSessionPersistence is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called)
boolean useSessionPersistence;
public boolean useSessionPersistence() {
return useSessionPersistence;
}
public void setUseSessionPersistence(boolean useSessionPersistence) {
this.useSessionPersistence = useSessionPersistence;
}
// If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true.
boolean allowCompressedResponse;
public boolean allowCompressedResponse() {
return allowCompressedResponse;
}
public void setAllowCompressedResponse(boolean allowCompressedResponse) {
this.allowCompressedResponse = allowCompressedResponse;
}
// If shouldCompressRequestBody is true, the request body will be gzipped. Default is false.
// You will probably need to enable this feature on your webserver to make this work. Tested with apache only.
boolean shouldCompressRequestBody;
// When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location
// If downloadDestinationPath is not set, download data will be stored in memory
String downloadDestinationPath;
public String downloadDestinationPath() {
return downloadDestinationPath;
}
public void setDownloadDestinationPath(String downloadDestinationPath) {
this.downloadDestinationPath = downloadDestinationPath;
}
// The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath
String temporaryFileDownloadPath;
// If the response is gzipped and shouldWaitToInflateCompressedResponses is NO, a file will be created at this path containing the inflated response as it comes in
String temporaryUncompressedDataDownloadPath;
// Used for writing data to a file when downloadDestinationPath is set
NSOutputStream fileDownloadOutputStream;
NSOutputStream inflatedFileDownloadOutputStream;
// When the request fails or completes successfully, complete will be true
boolean complete;
public boolean complete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
// external "finished" indicator, subject of KVO notifications; updates after 'complete'
boolean finished;
// True if our 'cancel' selector has been called
boolean cancelled;
// If an error occurs, error will contain an NSError
// If error code is = ASIConnectionFailureErrorType (1, Connection failure occurred) - inspect [[error userInfo] objectForKey:NSUnderlyingErrorKey] for more information
NSError error;
public NSError error() {
return error;
}
public void setError(NSError error) {
this.error = error;
}
// Username and password used for authentication
String username;
String password;
// Domain used for NTLM authentication
String domain;
// Username and password used for proxy authentication
String proxyUsername;
String proxyPassword;
// Domain used for NTLM proxy authentication
String proxyDomain;
// Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
ASIProgressDelegate uploadProgressDelegate;
// Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
ASIProgressDelegate downloadProgressDelegate;
// Whether we've seen the headers of the response yet
boolean haveExaminedHeaders;
// Data we receive will be stored here. Data may be compressed unless allowCompressedResponse is false - you should use [request responseData] instead in most cases
NSMutableData rawResponseData;
public NSMutableData rawResponseData() {
return rawResponseData;
}
public void setRawResponseData(NSMutableData rawResponseData) {
this.rawResponseData = rawResponseData;
}
CFHTTPMessageRef request;
NSInputStream readStream;
// Used for authentication
CFHTTPAuthenticationRef requestAuthentication;
NSDictionary requestCredentials;
// Used during NTLM authentication
int authenticationRetryCount;
// Authentication scheme (Basic, Digest, NTLM)
String authenticationScheme;
// Realm for authentication when credentials are required
String authenticationRealm;
// When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a server that requires authentication
// The dialog will not be shown if your delegate responds to authenticationNeededForRequest:
// Default is NO.
boolean shouldPresentAuthenticationDialog;
// When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a proxy server that requires authentication
// The dialog will not be shown if your delegate responds to proxyAuthenticationNeededForRequest:
// Default is YES (basically, because most people won't want the hassle of adding support for authenticating proxies to their apps)
boolean shouldPresentProxyAuthenticationDialog;
public boolean shouldPresentProxyAuthenticationDialog() {
return shouldPresentProxyAuthenticationDialog;
}
public void setShouldPresentProxyAuthenticationDialog(
boolean shouldPresentProxyAuthenticationDialog) {
this.shouldPresentProxyAuthenticationDialog = shouldPresentProxyAuthenticationDialog;
}
// Used for proxy authentication
CFHTTPAuthenticationRef proxyAuthentication;
NSDictionary proxyCredentials;
// Used during authentication with an NTLM proxy
int proxyAuthenticationRetryCount;
// Authentication scheme for the proxy (Basic, Digest, NTLM)
String proxyAuthenticationScheme;
// Realm for proxy authentication when credentials are required
String proxyAuthenticationRealm;
// HTTP status code, eg: 200 = OK, 404 = Not found etc
int responseStatusCode;
// Description of the HTTP status code
String responseStatusMessage;
// Size of the response
long contentLength;
public long contentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
// Size of the partially downloaded content
long partialDownloadSize;
// Size of the POST payload
long postLength;
// The total amount of downloaded data
long totalBytesRead;
public long totalBytesRead() {
return totalBytesRead;
}
public void setTotalBytesRead(long totalBytesRead) {
this.totalBytesRead = totalBytesRead;
}
// The total amount of uploaded data
long totalBytesSent;
// Last amount of data read (used for incrementing progress)
long lastBytesRead;
public long lastBytesRead() {
return lastBytesRead;
}
public void setLastBytesRead(long lastBytesRead) {
this.lastBytesRead = lastBytesRead;
}
// Last amount of data sent (used for incrementing progress)
long lastBytesSent;
public long lastBytesSent() {
return lastBytesSent;
}
public void setLastBytesSent(long lastBytesSent) {
this.lastBytesSent = lastBytesSent;
}
// This lock prevents the operation from being cancelled at an inopportune moment
NSRecursiveLock cancelledLock;
public NSRecursiveLock cancelledLock() {
return cancelledLock;
}
public void setCancelledLock(NSRecursiveLock cancelledLock) {
this.cancelledLock = cancelledLock;
}
// Called on the delegate (if implemented) when the request starts. Default is requestStarted:
String didStartSelector;
// Called on the delegate (if implemented) when the request receives response headers. Default is requestDidReceiveResponseHeaders:
String didReceiveResponseHeadersSelector;
// Called on the delegate (if implemented) when the request receives a Location header and shouldRedirect is YES
// The delegate can then change the url if needed, and can restart the request by calling [request resume], or simply cancel it
String willRedirectSelector;
// Called on the delegate (if implemented) when the request completes successfully. Default is requestFinished:
String didFinishSelector;
// Called on the delegate (if implemented) when the request fails. Default is requestFailed:
String didFailSelector;
// Called on the delegate (if implemented) when the request receives data. Default is request:didReceiveData:
// If you set this and implement the method in your delegate, you must handle the data yourself - ASIHTTPRequest will not populate responseData or write the data to downloadDestinationPath
String didReceiveDataSelector;
// Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate
NSDate lastActivityTime;
// Number of seconds to wait before timing out - default is 10
double timeOutSeconds;
public double timeOutSeconds() {
return timeOutSeconds;
}
public void setTimeOutSeconds(double timeOutSeconds) {
this.timeOutSeconds = timeOutSeconds;
}
//block to execute when request starts
ASIBasicBlock startedBlock;
public ASIBasicBlock startedBlock() {
return startedBlock;
}
public void setStartedBlock(ASIBasicBlock startedBlock) {
this.startedBlock = startedBlock;
}
//block to execute when headers are received
ASIHeadersBlock headersReceivedBlock;
//block to execute when request completes successfully
ASIBasicBlock completionBlock;
public ASIBasicBlock completionBlock() {
return completionBlock;
}
public void setCompletionBlock(ASIBasicBlock completionBlock) {
this.completionBlock = completionBlock;
}
//block to execute when request fails
ASIBasicBlock failureBlock;
public ASIBasicBlock failureBlock() {
return failureBlock;
}
public void setFailureBlock(ASIBasicBlock failureBlock) {
this.failureBlock = failureBlock;
}
//block for when bytes are received
ASIProgressBlock bytesReceivedBlock;
//block for when bytes are sent
ASIProgressBlock bytesSentBlock;
//block for when download size is incremented
ASISizeBlock downloadSizeIncrementedBlock;
//block for when upload size is incremented
ASISizeBlock uploadSizeIncrementedBlock;
//block for handling raw bytes received
ASIDataBlock dataReceivedBlock;
//block for handling authentication
ASIBasicBlock authenticationNeededBlock;
//block for handling proxy authentication
ASIBasicBlock proxyAuthenticationNeededBlock;
//block for handling redirections, if you want to
ASIBasicBlock requestRedirectedBlock;
public interface ASIBasicBlock{
public abstract void function();
}
public interface ASIHeadersBlock{
public abstract void function(NSDictionary responseHeaders);
}
public interface ASISizeBlock{
public abstract void function(long size);
}
public interface ASIProgressBlock{
public abstract void function(long size,long total);
}
public interface ASIDataBlock{
public abstract void function(NSData data);
}
// Will be YES when a HEAD request will handle the content-length before this request starts
boolean shouldResetUploadProgress;
public boolean shouldResetUploadProgress() {
return shouldResetUploadProgress;
}
public void setShouldResetUploadProgress(boolean shouldResetUploadProgress) {
this.shouldResetUploadProgress = shouldResetUploadProgress;
}
boolean shouldResetDownloadProgress;
public boolean shouldResetDownloadProgress() {
return shouldResetDownloadProgress;
}
public void setShouldResetDownloadProgress(boolean shouldResetDownloadProgress) {
this.shouldResetDownloadProgress = shouldResetDownloadProgress;
}
// Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request
ASIHTTPRequest mainRequest;
public ASIHTTPRequest mainRequest() {
return mainRequest;
}
public void setMainRequest(ASIHTTPRequest mainRequest) {
this.mainRequest = mainRequest;
}
// When NO, this request will only update the progress indicator when it completes
// When YES, this request will update the progress indicator according to how much data it has received so far
// The default for requests is YES
// Also see the comments in ASINetworkQueue.h
boolean showAccurateProgress;
public boolean showAccurateProgress() {
return showAccurateProgress;
}
public void setShowAccurateProgress(boolean showAccurateProgress) {
this.showAccurateProgress = showAccurateProgress;
}
// Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO
boolean updatedProgress;
// Prevents the body of the post being built more than once (largely for subclasses)
boolean haveBuiltPostBody;
// Used internally, may reflect the size of the internal buffer used by CFNetwork
// POST / PUT operations with body sizes greater than uploadBufferSize will not timeout unless more than uploadBufferSize bytes have been sent
// Likely to be 32KB on iPhone 3.0, 128KB on Mac OS X Leopard and iPhone 2.2.x
long uploadBufferSize;
// Text encoding for responses that do not send a Content-Type with a charset value. Defaults to NSISOLatin1StringEncoding
int defaultResponseEncoding;
public int defaultResponseEncoding() {
return defaultResponseEncoding;
}
public void setDefaultResponseEncoding(int defaultResponseEncoding) {
this.defaultResponseEncoding = defaultResponseEncoding;
}
// The text encoding of the response, will be defaultResponseEncoding if the server didn't specify. Can't be set.
int responseEncoding;
// Tells ASIHTTPRequest not to delete partial downloads, and allows it to use an existing file to resume a download. Defaults to NO.
boolean allowResumeForFileDownloads;
// Custom user information associated with the request
NSDictionary userInfo;
// Use HTTP 1.0 rather than 1.1 (defaults to false)
boolean useHTTPVersionOne;
// When YES, requests will automatically redirect when they get a HTTP 30x header (defaults to YES)
boolean shouldRedirect;
public boolean shouldRedirect() {
return shouldRedirect;
}
public void setShouldRedirect(boolean shouldRedirect) {
this.shouldRedirect = shouldRedirect;
}
// Used internally to tell the main loop we need to stop and retry with a new url
boolean needsRedirect;
// Incremented every time this request redirects. When it reaches 5, we give up
int redirectCount;
public int redirectCount() {
return redirectCount;
}
public void setRedirectCount(int redirectCount) {
this.redirectCount = redirectCount;
}
// When NO, requests will not check the secure certificate is valid (use for self-signed certificates during development, DO NOT USE IN PRODUCTION) Default is YES
boolean validatesSecureCertificate;
public boolean validatesSecureCertificate() {
return validatesSecureCertificate;
}
public void setValidatesSecureCertificate(boolean validatesSecureCertificate) {
this.validatesSecureCertificate = validatesSecureCertificate;
}
// If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate
SecIdentityRef clientCertificateIdentity;
NSArray clientCertificates;
// Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
String proxyHost;
int proxyPort;
// ASIHTTPRequest will assume kCFProxyTypeHTTP if the proxy type could not be automatically determined
// Set to kCFProxyTypeSOCKS if you are manually configuring a SOCKS proxy
String proxyType;
// URL for a PAC (Proxy Auto Configuration) file. If you want to set this yourself, it's probably best if you use a local file
NSURL PACurl;
// See ASIAuthenticationState values above. 0 == default == No authentication needed yet
ASIAuthenticationState authenticationNeeded;
// When YES, ASIHTTPRequests will present credentials from the session store for requests to the same server before being asked for them
// This avoids an extra round trip for requests after authentication has succeeded, which is much for efficient for authenticated requests with large bodies, or on slower connections
// Set to NO to only present credentials when explicitly asked for them
// This only affects credentials stored in the session cache when useSessionPersistence is YES. Credentials from the keychain are never presented unless the server asks for them
// Default is YES
boolean shouldPresentCredentialsBeforeChallenge;
public boolean shouldPresentCredentialsBeforeChallenge() {
return shouldPresentCredentialsBeforeChallenge;
}
public void setShouldPresentCredentialsBeforeChallenge(
boolean shouldPresentCredentialsBeforeChallenge) {
this.shouldPresentCredentialsBeforeChallenge = shouldPresentCredentialsBeforeChallenge;
}
// YES when the request hasn't finished yet. Will still be YES even if the request isn't doing anything (eg it's waiting for delegate authentication). READ-ONLY
boolean inProgress;
// Used internally to track whether the stream is scheduled on the run loop or not
// Bandwidth throttling can unschedule the stream to slow things down while a request is in progress
boolean readStreamIsScheduled;
// Set to allow a request to automatically retry itself on timeout
// Default is zero - timeout will stop the request
int numberOfTimesToRetryOnTimeout;
// The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0)
int retryCount;
// When YES, requests will keep the connection to the server alive for a while to allow subsequent requests to re-use it for a substantial speed-boost
// Persistent connections will not be used if the server explicitly closes the connection
// Default is YES
boolean shouldAttemptPersistentConnection;
public boolean shouldAttemptPersistentConnection() {
return shouldAttemptPersistentConnection;
}
public void setShouldAttemptPersistentConnection(
boolean shouldAttemptPersistentConnection) {
this.shouldAttemptPersistentConnection = shouldAttemptPersistentConnection;
}
// Number of seconds to keep an inactive persistent connection open on the client side
// Default is 60
// If we get a keep-alive header, this is this value is replaced with how long the server told us to keep the connection around
// A future date is created from this and used for expiring the connection, this is stored in connectionInfo's expires value
double persistentConnectionTimeoutSeconds;
public double persistentConnectionTimeoutSeconds() {
return persistentConnectionTimeoutSeconds;
}
public void setPersistentConnectionTimeoutSeconds(
double persistentConnectionTimeoutSeconds) {
this.persistentConnectionTimeoutSeconds = persistentConnectionTimeoutSeconds;
}
// Set to yes when an appropriate keep-alive header is found
boolean connectionCanBeReused;
// Stores information about the persistent connection that is currently in use.
// It may contain:
// * The id we set for a particular connection, incremented every time we want to specify that we need a new connection
// * The date that connection should expire
// * A host, port and scheme for the connection. These are used to determine whether that connection can be reused by a subsequent request (all must match the new request)
// * An id for the request that is currently using the connection. This is used for determining if a connection is available or not (we store a number rather than a reference to the request so we don't need to hang onto a request until the connection expires)
// * A reference to the stream that is currently using the connection. This is necessary because we need to keep the old stream open until we've opened a new one.
// The stream will be closed + released either when another request comes to use the connection, or when the timer fires to tell the connection to expire
NSMutableDictionary connectionInfo;
// When set to YES, 301 and 302 automatic redirects will use the original method and and body, according to the HTTP 1.1 standard
// Default is NO (to follow the behaviour of most browsers)
boolean shouldUseRFC2616RedirectBehaviour;
// Used internally to record when a request has finished downloading data
boolean downloadComplete;
public boolean downloadComplete() {
return downloadComplete;
}
public void setDownloadComplete(boolean downloadComplete) {
this.downloadComplete = downloadComplete;
}
// An ID that uniquely identifies this request - primarily used for debugging persistent connections
NSNumber requestID;
// Will be ASIHTTPRequestRunLoopMode for synchronous requests, NSDefaultRunLoopMode for all other requests
String runLoopMode;
public String runLoopMode() {
return runLoopMode;
}
public void setRunLoopMode(String runLoopMode) {
this.runLoopMode = runLoopMode;
}
// This timer checks up on the request every 0.25 seconds, and updates progress
NSTimer statusTimer;
// The download cache that will be used for this request (use [ASIHTTPRequest setDefaultCache:cache] to configure a default cache
ASICacheDelegate downloadCache;
public ASICacheDelegate downloadCache() {
return downloadCache;
}
public void setDownloadCache(ASICacheDelegate downloadCache) {
this.downloadCache = downloadCache;
}
// The cache policy that will be used for this request - See ASICacheDelegate.h for possible values
int cachePolicy;
// The cache storage policy that will be used for this request - See ASICacheDelegate.h for possible values
int cacheStoragePolicy;
// Will be true when the response was pulled from the cache rather than downloaded
boolean didUseCachedResponse;
public boolean didUseCachedResponse() {
return didUseCachedResponse;
}
public void setDidUseCachedResponse(boolean didUseCachedResponse) {
this.didUseCachedResponse = didUseCachedResponse;
}
// Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache
int secondsToCache;
boolean shouldContinueWhenAppEntersBackground;
UIBackgroundTaskIdentifier backgroundTask;
// When downloading a gzipped response, the request will use this helper object to inflate the response
ASIDataDecompressor dataDecompressor;
// Controls how responses with a gzipped encoding are inflated (decompressed)
// When set to YES (This is the default):
// * gzipped responses for requests without a downloadDestinationPath will be inflated only when [request responseData] / [request responseString] is called
// * gzipped responses for requests with a downloadDestinationPath set will be inflated only when the request completes
//
// When set to NO
// All requests will inflate the response as it comes in
// * If the request has no downloadDestinationPath set, the raw (compressed) response is discarded and rawResponseData will contain the decompressed response
// * If the request has a downloadDestinationPath, the raw response will be stored in temporaryFileDownloadPath as normal, the inflated response will be stored in temporaryUncompressedDataDownloadPath
// Once the request completes successfully, the contents of temporaryUncompressedDataDownloadPath are moved into downloadDestinationPath
//
// Setting this to NO may be especially useful for users using ASIHTTPRequest in conjunction with a streaming parser, as it will allow partial gzipped responses to be inflated and passed on to the parser while the request is still running
boolean shouldWaitToInflateCompressedResponses;
public boolean shouldWaitToInflateCompressedResponses() {
return shouldWaitToInflateCompressedResponses;
}
public void setShouldWaitToInflateCompressedResponses(
boolean shouldWaitToInflateCompressedResponses) {
this.shouldWaitToInflateCompressedResponses = shouldWaitToInflateCompressedResponses;
}
public ASIHTTPRequest(NSURL newURL){
this.setRequestMethod("GET");
this.setRunLoopMode(NSDefaultRunLoopMode);
this.setShouldAttemptPersistentConnection(YES);
this.setPersistentConnectionTimeoutSeconds(60.0);
this.setShouldPresentCredentialsBeforeChallenge(YES);
this.setShouldRedirect(YES);
this.setShowAccurateProgress(YES);
this.setShouldResetDownloadProgress(YES);
this.setShouldResetUploadProgress(YES);
this.setAllowCompressedResponse(YES);
this.setShouldWaitToInflateCompressedResponses(YES);
this.setDefaultResponseEncoding(NSStringEncoding.NSISOLatin1StringEncoding);
this.setShouldPresentProxyAuthenticationDialog(YES);
this.setTimeOutSeconds(ASIHTTPRequest.defaultTimeOutSeconds());
this.setUseSessionPersistence(YES);
this.setUseSessionPersistence(YES);
this.setValidatesSecureCertificate(YES);
this.setRequestCookies(new NSMutableArray());
this.setURL(newURL);
this.setCancelledLock(new NSRecursiveLock());
this.setDownloadCache(defaultCache);
}
public static ASIHTTPRequest requestWithURL(NSURL newURL){
return new ASIHTTPRequest(newURL);
}
public void startAsynchronous(){
sharedQueue.addOperation(this);
}
public class ASIAuthenticationState{
public static final int ASINoAuthenticationNeededYet = 0;
public static final int ASIHTTPAuthenticationNeeded = 1;
public static final int ASIProxyAuthenticationNeeded = 2;
}
public class ASINetworkErrorType{
public static final int ASIConnectionFailureErrorType = 1;
public static final int ASIRequestTimedOutErrorType = 2;
public static final int ASIAuthenticationErrorType = 3;
public static final int ASIRequestCancelledErrorType = 4;
public static final int ASIUnableToCreateRequestErrorType = 5;
public static final int ASIInternalErrorWhileBuildingRequestType = 6;
public static final int ASIInternalErrorWhileApplyingCredentialsType = 7;
public static final int ASIFileManagementError = 8;
public static final int ASITooMuchRedirectionErrorType = 9;
public static final int ASIUnhandledExceptionError = 10;
public static final int ASICompressionError = 11;
}
@Override
public void main() {
this.setComplete(NO);
this.setDidUseCachedResponse(NO);
if (this.url==null) {
//TODO error
return;
}
// Must call before we create the request so that the request method can be set if needs be
if (this.mainRequest!=null) {
this.buildPostBody();
}
if (this.requestMethod().equals("GET")) {
this.setDownloadCache(null);
}
// If we're redirecting, we'll already have a CFHTTPMessageRef
if (request!=null) {
}
//If this is a HEAD request generated by an ASINetworkQueue, we need to let the main request generate its headers first so we can use them
if (this.mainRequest()!=null) {
this.mainRequest().buildRequestHeaders();
}
// Even if this is a HEAD request with a mainRequest, we still need to call to give subclasses a chance to add their own to HEAD requests (ASIS3Request does this)
this.buildRequestHeaders();
if (this.downloadCache()!=null) {
}
this.applyAuthorizationHeader();
String header;
for (int i = 0; i < requestHeaders.cout(); i++) {
}
this.startRequest();
}
public void requestStarted(){
if (startedBlock!=null) {
startedBlock.function();
}
}
public void startRequest(){
this.performSelectorOnMainThread(selector("requestStarted"), null, NSThread.isMainThread());
this.setDownloadComplete(NO);
this.setComplete(NO);
this.setTotalBytesRead(0);
this.setLastBytesRead(0);
if (this.redirectCount()==0) {
this.setOriginalURL(this.url);
}
if (this.lastBytesSent()>0) {
this.removeUploadProgressSoFar();
}
this.setLastBytesSent(0);
this.setContentLength(0);
this.setResponseHeaders(null);
if (this.downloadDestinationPath()!=null) {
this.setRawResponseData(new NSMutableData());
}
}
public void removeUploadProgressSoFar(){}
public void buildPostBody(){
}
public void buildRequestHeaders(){
}
public void applyAuthorizationHeader(){}
}