New: added timeout and cache feature in CPURLRequest

This PR adds the possibility to set the timeout and the cache policy of a CPURLRequest.
For that, there are new things in the framework :

- CFHTTPRequest has now the functions setTimeout(), getTimeout() and isTimeoutRequest
- CPURLRequest has now the methods +requestWithURL:cachePolicy:timeoutInterval: and -initWithURL:cachePolicy:timeoutInterval:
- CPURLConnection does not call the delegate didReceiveData: when a request has timed out. I will call the delegate didFailWithError:
- Cache is now possible for a CPURLRequest, only CPURLRequestReturnCacheDataElseLoad, CPURLRequestReturnCacheDataDontLoad and CPURLRequestReloadIgnoringLocalCacheData are supported. By default CPURLRequestReloadIgnoringLocalCacheData.
- Default timeout has been set to 60sec as in cocoa.
- A request is considered as timeout when there isn't any response (text/xml/type) and when the status of the response is 0.
This commit is contained in:
Alexandre Wilhelm
2015-08-13 17:08:59 -07:00
parent 3d79799450
commit bd0bd2e087
3 changed files with 132 additions and 14 deletions
+26 -4
View File
@@ -112,6 +112,7 @@ var CPURLConnectionDelegate = nil;
try
{
var aCFHTTPRequest = new CFHTTPRequest();
aCFHTTPRequest.setTimeout([aRequest timeoutInterval] * 1000);
aCFHTTPRequest.setWithCredentials([aRequest withCredentials]);
aCFHTTPRequest.open([aRequest HTTPMethod], [[aRequest URL] absoluteString], NO);
@@ -125,7 +126,7 @@ var CPURLConnectionDelegate = nil;
aCFHTTPRequest.send([aRequest HTTPBody]);
if (!aCFHTTPRequest.success())
if (!aCFHTTPRequest.success() || aCFHTTPRequest.isTimeoutRequest())
return nil;
return [CPData dataWithRawString:aCFHTTPRequest.responseText()];
@@ -176,6 +177,7 @@ var CPURLConnectionDelegate = nil;
(window.location.protocol === "file:" || window.location.protocol === "app:"));
_HTTPRequest = new CFHTTPRequest();
_HTTPRequest.setTimeout([aRequest timeoutInterval] * 1000);
_HTTPRequest.setWithCredentials([aRequest withCredentials]);
if (shouldStartImmediately)
@@ -210,6 +212,7 @@ var CPURLConnectionDelegate = nil;
_HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES);
_HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; };
_HTTPRequest.ontimeout = function() { [self _timeout]; };
var fields = [_request allHTTPHeaderFields],
key = nil,
@@ -222,11 +225,16 @@ var CPURLConnectionDelegate = nil;
}
catch (anException)
{
if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
[_delegate connection:self didFailWithError:anException];
[self _sendDelegateDidFailWithError:anException];
}
}
- (void)_sendDelegateDidFailWithError:(CPException)anException
{
if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
[_delegate connection:self didFailWithError:anException];
}
/*
Cancels the current request.
*/
@@ -249,10 +257,24 @@ var CPURLConnectionDelegate = nil;
return _isLocalFileConnection;
}
/*!
@ignore
*/
- (void)_timeout
{
var exception = [CPException exceptionWithName:@"Timeout exception"
reason:"The request timed out."
userInfo:@{}];
[self _sendDelegateDidFailWithError:exception];
}
/* @ignore */
- (void)_readyStateDidChange
{
if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState)
var isTimeoutRequest = !_HTTPRequest.status() && !_HTTPRequest.responseText();
if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState && !_HTTPRequest.isTimeoutRequest())
{
var statusCode = _HTTPRequest.status(),
URL = [_request URL];
+72 -6
View File
@@ -25,6 +25,12 @@
@import "CPString.j"
@import "CPURL.j"
@typedef CPURLRequestCachePolicy
CPURLRequestUseProtocolCachePolicy = 0;
CPURLRequestReloadIgnoringLocalCacheData = 1;
CPURLRequestReturnCacheDataElseLoad = 2;
CPURLRequestReturnCacheDataDontLoad = 3;
/*!
@class CPURLRequest
@ingroup foundation
@@ -35,14 +41,16 @@
*/
@implementation CPURLRequest : CPObject
{
CPURL _URL @accessors(property=URL);
CPURL _URL @accessors(property=URL);
// FIXME: this should be CPData
CPString _HTTPBody @accessors(property=HTTPBody);
CPString _HTTPMethod @accessors(property=HTTPMethod);
BOOL _withCredentials @accessors(property=withCredentials);
CPString _HTTPBody @accessors(property=HTTPBody);
CPString _HTTPMethod @accessors(property=HTTPMethod);
BOOL _withCredentials @accessors(property=withCredentials);
CPDictionary _HTTPHeaderFields @accessors(readonly, getter=allHTTPHeaderFields);
CPDictionary _HTTPHeaderFields @accessors(readonly, getter=allHTTPHeaderFields);
CPTimeInterval _timeoutInterval @accessors(readonly, getter=timeoutInterval);
CPURLRequestCachePolicy _cachePolicy @accessors(readonly, getter=cachePolicy);
}
/*!
@@ -55,6 +63,18 @@
return [[CPURLRequest alloc] initWithURL:aURL];
}
/*!
Creates a request with a specified URL, cachePolicy and timeoutInterval
@param aURL the URL of the request
@param aCachePolicy the cache policy of the request
@param aTimeoutInterval the timeoutInterval of the request
@return a CPURLRequest
*/
+ (id)requestWithURL:(CPURL)anURL cachePolicy:(CPURLRequestCachePolicy)aCachePolicy timeoutInterval:(CPTimeInterval)aTimeoutInterval
{
return [[CPURLRequest alloc] initWithURL:anURL cachePolicy:aCachePolicy timeoutInterval:aTimeoutInterval];
}
/*!
Equal to `[receiver initWithURL:nil]`.
*/
@@ -63,6 +83,27 @@
return [self initWithURL:nil];
}
/*!
Initializes the request with a URL. This is the designated initializer.
@param aURL the url to set
@param aCachePolicy the cache policy of the request
@param aTimeoutInterval the timeoutInterval of the request
@return the initialized CPURLRequest
*/
- (id)initWithURL:(CPURL)anURL cachePolicy:(CPURLRequestCachePolicy)aCachePolicy timeoutInterval:(CPTimeInterval)aTimeoutInterval
{
self = [self initWithURL:anURL];
if (self)
{
_cachePolicy = aCachePolicy;
_timeoutInterval = aTimeoutInterval;
}
return self;
}
/*!
Initializes the request with a URL. This is the designated initializer.
@@ -81,9 +122,34 @@
_HTTPMethod = @"GET";
_HTTPHeaderFields = @{};
_withCredentials = NO;
_timeoutInterval = 60.0;
_cachePolicy = CPURLRequestUseProtocolCachePolicy;
[self setValue:"Thu, 01 Jan 1970 00:00:00 GMT" forHTTPHeaderField:"If-Modified-Since"];
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
switch (_cachePolicy)
{
case CPURLRequestUseProtocolCachePolicy:
// TODO: implement everything about cache...
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReturnCacheDataElseLoad:
[self setValue:"max-stale=31536000" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReturnCacheDataDontLoad:
[self setValue:"only-if-cached" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReloadIgnoringLocalCacheData:
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
break;
default:
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
}
[self setValue:"XMLHttpRequest" forHTTPHeaderField:"X-Requested-With"];
}
+34 -4
View File
@@ -105,6 +105,7 @@ GLOBAL(CFHTTPRequest) = function()
// by default, all requests will assume that credentials should not be sent.
this._nativeRequest.withCredentials = false;
this._nativeRequest.timeout = 60000;
var self = this;
this._stateChangeHandler = function()
@@ -112,7 +113,13 @@ GLOBAL(CFHTTPRequest) = function()
determineAndDispatchHTTPRequestEvents(self);
};
this._timeoutHandler = function()
{
dispatchTimeoutHTTPRequestEvents(self);
};
this._nativeRequest.onreadystatechange = this._stateChangeHandler;
this._nativeRequest.ontimeout = this._timeoutHandler;
if (CFHTTPRequest.AuthenticationDelegate !== nil)
this._eventDispatcher.addEventListener("HTTP403", function()
@@ -209,6 +216,16 @@ CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader)
return this._nativeRequest.getResponseHeader(aHeader);
};
CFHTTPRequest.prototype.setTimeout = function(/*int*/ aTimeout)
{
this._nativeRequest.timeout = aTimeout;
};
CFHTTPRequest.prototype.getTimeout = function(/*int*/ aTimeout)
{
return this._nativeRequest.timeout
};
CFHTTPRequest.prototype.getAllResponseHeaders = function()
{
return this._nativeRequest.getAllResponseHeaders();
@@ -235,7 +252,10 @@ CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
if (!this._isOpen)
{
delete this._nativeRequest.onreadystatechange;
delete this._nativeRequest.ontimeout;
this._nativeRequest.open(this._method, this._URL, this._async, this._user, this._password);
this._nativeRequest.ontimeout = this._timeoutHandler;
this._nativeRequest.onreadystatechange = this._stateChangeHandler;
}
@@ -287,15 +307,25 @@ CFHTTPRequest.prototype.withCredentials = function()
return this._nativeRequest.withCredentials;
};
CFHTTPRequest.prototype.isTimeoutRequest = function()
{
// Can we consider that as a timeout ?
return !this.success() && !this._nativeRequest.response && !this._nativeRequest.responseText && !this._nativeRequest.responseType && !this._nativeRequest.responseURL && !this._nativeRequest.responseXML;
};
function dispatchTimeoutHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
{
aRequest._eventDispatcher.dispatchEvent({ type:"timeout", request:aRequest});
}
function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
{
var eventDispatcher = aRequest._eventDispatcher;
var eventDispatcher = aRequest._eventDispatcher,
nativeRequest = aRequest._nativeRequest,
readyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"];
eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest});
var nativeRequest = aRequest._nativeRequest,
readyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"];
if (readyStates[aRequest.readyState()] === "complete")
{
var status = "HTTP" + aRequest.status();