|
|
@ -18,10 +18,16 @@ import 'dart:async'; |
|
|
|
import 'dart:io'; |
|
|
|
import 'dart:convert'; |
|
|
|
|
|
|
|
/// If one or more interceptors are set, they will be called before the request |
|
|
|
/// is made in order to allow for modifications of the [HttpClientRequest] |
|
|
|
/// See [SimpleHttpClient.addInterceptor] |
|
|
|
typedef Future<HttpClientRequest> Interceptor(HttpClientRequest request); |
|
|
|
/// If one or more request interceptors are set, they will be called before the |
|
|
|
/// request is made in order to allow for modifications of the [HttpClientRequest] |
|
|
|
/// See [SimpleHttpClient.addRequestInterceptor] |
|
|
|
typedef Future<HttpClientRequest> RequestInterceptor(HttpClientRequest request); |
|
|
|
|
|
|
|
/// This type is used in order to allow the interception of the response, |
|
|
|
/// usually you will want to use this for logging or analytics. See |
|
|
|
/// [SimpleHttpClient.addResponseInterceptor] |
|
|
|
typedef Future<Null> ResponseInterceptor( |
|
|
|
HttpClientRequest request, Response response); |
|
|
|
|
|
|
|
// Defines a certificate by its bytes |
|
|
|
class SimpleCertificate { |
|
|
@ -42,7 +48,9 @@ class SimpleCertificate { |
|
|
|
/// Set an authenticator to have it called if a request receives a [:401:] response, |
|
|
|
/// or set an interceptor if you want to modify all the requests made with this |
|
|
|
/// client (e.g adding a header). See [SimpleHttpClient.setAuthenticator], |
|
|
|
/// [SimpleHttpClient.addInterceptor], and [Interceptor]. |
|
|
|
/// [SimpleHttpClient.addRequestInterceptor], and [RequestInterceptor]. For logging |
|
|
|
/// or analytics purposes, you may want to set a [ResponseInterceptor], see |
|
|
|
/// [SimpleHttpClient.addResponseInterceptor]. |
|
|
|
/// |
|
|
|
/// Set a timeout [Duration] when creating the instance |
|
|
|
/// `new SimpleHttpClient(timeout: new Duration(seconds: 7))`. |
|
|
@ -70,11 +78,10 @@ class SimpleHttpClient { |
|
|
|
/// Timeout for the requests. Default value is [:10:] seconds |
|
|
|
final Duration timeout; |
|
|
|
|
|
|
|
final bool debug; |
|
|
|
|
|
|
|
HttpClient _httpClient; |
|
|
|
List<Interceptor> _interceptors = new List(); |
|
|
|
Interceptor _authenticate; |
|
|
|
List<RequestInterceptor> _requestInterceptors = new List(); |
|
|
|
List<ResponseInterceptor> _responseInterceptors = new List(); |
|
|
|
RequestInterceptor _authenticate; |
|
|
|
final SecurityContext _securityContext; |
|
|
|
|
|
|
|
/// Creates an instance of [SimpleHttpClient]. |
|
|
@ -102,7 +109,6 @@ class SimpleHttpClient { |
|
|
|
this.followRedirects: true, |
|
|
|
this.maxRedirects: 5, |
|
|
|
this.maxAuthRetries: 1, |
|
|
|
this.debug: false, |
|
|
|
List<SimpleCertificate> trustedCertificates}) |
|
|
|
: _securityContext = |
|
|
|
(trustedCertificates != null) ? new SecurityContext() : null { |
|
|
@ -121,14 +127,22 @@ class SimpleHttpClient { |
|
|
|
request.headers.add("user-agent", userAgent); |
|
|
|
} |
|
|
|
|
|
|
|
_intercept(HttpClientRequest request) async { |
|
|
|
if (_interceptors.isNotEmpty) { |
|
|
|
for (Interceptor interceptor in _interceptors) { |
|
|
|
_interceptRequest(HttpClientRequest request) async { |
|
|
|
if (_requestInterceptors.isNotEmpty) { |
|
|
|
for (RequestInterceptor interceptor in _requestInterceptors) { |
|
|
|
await interceptor(request); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_interceptResponse(HttpClientRequest request, Response response) async { |
|
|
|
if (_responseInterceptors.isNotEmpty) { |
|
|
|
for (ResponseInterceptor interceptor in _responseInterceptors) { |
|
|
|
await interceptor(request, response); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_setContentType(HttpClientRequest request, {ContentType contentType}) { |
|
|
|
if (contentType == null) { |
|
|
|
contentType = ContentType.json; |
|
|
@ -155,7 +169,7 @@ class SimpleHttpClient { |
|
|
|
} |
|
|
|
|
|
|
|
if (authenticate) await _authenticate(request); |
|
|
|
await _intercept(request); |
|
|
|
await _interceptRequest(request); |
|
|
|
|
|
|
|
if (body != null) { |
|
|
|
request.write(body); |
|
|
@ -166,6 +180,9 @@ class SimpleHttpClient { |
|
|
|
throw new TimeOutException(); |
|
|
|
}); |
|
|
|
|
|
|
|
var simpleResponse = await _read(response); |
|
|
|
await _interceptResponse(request, simpleResponse); |
|
|
|
|
|
|
|
if (HttpStatus.unauthorized == response.statusCode && |
|
|
|
_authenticate != null && |
|
|
|
requestNumber <= maxAuthRetries) { |
|
|
@ -178,7 +195,7 @@ class SimpleHttpClient { |
|
|
|
throw new UnauthorizedException(); |
|
|
|
} |
|
|
|
|
|
|
|
return _read(response); |
|
|
|
return simpleResponse; |
|
|
|
} on SocketException catch (_) { |
|
|
|
throw new CantConnectException(); |
|
|
|
} on HandshakeException catch (_) { |
|
|
@ -231,7 +248,7 @@ class SimpleHttpClient { |
|
|
|
} |
|
|
|
|
|
|
|
/// Set the authenticator for the client. If an authenticator is set, |
|
|
|
/// the app will call it when a response has the HTTP Status Code [:401:]. |
|
|
|
/// the library will call it when a response has the HTTP Status Code [:401:]. |
|
|
|
/// The authenticator will be called with a request identical to the original |
|
|
|
/// one, and the authenticator has to modify that request in whichever way |
|
|
|
/// needed. After the authenticator is called, the request will be sent and |
|
|
@ -240,20 +257,31 @@ class SimpleHttpClient { |
|
|
|
/// not set, an [UnauthorizedException] will be thrown. |
|
|
|
/// |
|
|
|
/// The authenticator will be called before any interceptor. |
|
|
|
void setAuthenticator(Interceptor authenticator) { |
|
|
|
void setAuthenticator(RequestInterceptor authenticator) { |
|
|
|
this._authenticate = authenticator; |
|
|
|
} |
|
|
|
|
|
|
|
/// Add an [Interceptor]. Each interceptor will be called sequentially in the same |
|
|
|
/// Add a [RequestInterceptor]. Each interceptor will be called sequentially in the same |
|
|
|
/// order it was added, before executing the request, and after the authenticator |
|
|
|
/// if authentication was needed. |
|
|
|
void addInterceptor(Interceptor interceptor) { |
|
|
|
_interceptors.add(interceptor); |
|
|
|
void addRequestInterceptor(RequestInterceptor interceptor) { |
|
|
|
_requestInterceptors.add(interceptor); |
|
|
|
} |
|
|
|
|
|
|
|
/// Remove a request interceptor from the list |
|
|
|
void removeRequestInterceptor(RequestInterceptor interceptor) { |
|
|
|
_requestInterceptors.remove(interceptor); |
|
|
|
} |
|
|
|
|
|
|
|
/// Remove an interceptor from the list |
|
|
|
void removeInterceptor(Interceptor interceptor) { |
|
|
|
_interceptors.remove(interceptor); |
|
|
|
/// ResponseInterceptor are called as soon as a response is obtained. Usually |
|
|
|
/// this type of interceptor is used for analytics or logging |
|
|
|
void addResponseInterceptor(ResponseInterceptor interceptor) { |
|
|
|
_responseInterceptors.add(interceptor); |
|
|
|
} |
|
|
|
|
|
|
|
/// Remove a response interceptor from the list |
|
|
|
void removeResponseInterceptor(ResponseInterceptor interceptor) { |
|
|
|
_requestInterceptors.remove(interceptor); |
|
|
|
} |
|
|
|
|
|
|
|
/// Execute a POST call. |
|
|
@ -270,9 +298,6 @@ class SimpleHttpClient { |
|
|
|
{ContentType contentType, Map<String, String> headers}) async { |
|
|
|
var response = await _execute(() => _post(url, contentType: contentType), |
|
|
|
body: body, headers: headers); |
|
|
|
if (debug) { |
|
|
|
print("POST $url [${response.statusCode}]"); |
|
|
|
} |
|
|
|
return response; |
|
|
|
} |
|
|
|
|
|
|
@ -290,10 +315,6 @@ class SimpleHttpClient { |
|
|
|
{ContentType contentType, Map<String, String> headers}) async { |
|
|
|
var response = await _execute(() => _put(url, contentType: contentType), |
|
|
|
body: body, headers: headers); |
|
|
|
|
|
|
|
if (debug) { |
|
|
|
print("PUT $url [${response.statusCode}]"); |
|
|
|
} |
|
|
|
return response; |
|
|
|
} |
|
|
|
|
|
|
@ -306,10 +327,6 @@ class SimpleHttpClient { |
|
|
|
/// Will return a [Response] |
|
|
|
Future<Response> get(String url, {Map<String, String> headers}) async { |
|
|
|
var response = await _execute(() => _get(url), headers: headers); |
|
|
|
|
|
|
|
if (debug) { |
|
|
|
print("GET $url [${response.statusCode}]"); |
|
|
|
} |
|
|
|
return response; |
|
|
|
} |
|
|
|
|
|
|
@ -322,10 +339,6 @@ class SimpleHttpClient { |
|
|
|
/// Will return a [Response] |
|
|
|
Future<Response> delete(String url, {Map<String, String> headers}) async { |
|
|
|
var response = await _execute(() => _delete(url), headers: headers); |
|
|
|
|
|
|
|
if (debug) { |
|
|
|
print("DELETE $url [${response.statusCode}]"); |
|
|
|
} |
|
|
|
return response; |
|
|
|
} |
|
|
|
} |
|
|
|