| OLD | NEW |
| 1 part of angular.core.dom; | 1 part of angular.core.dom; |
| 2 | 2 |
| 3 @NgInjectableService() | 3 @NgInjectableService() |
| 4 class UrlRewriter { | 4 class UrlRewriter { |
| 5 String call(url) => url; | 5 String call(url) => url; |
| 6 } | 6 } |
| 7 | 7 |
| 8 /** | 8 /** |
| 9 * HTTP backend used by the [Http] service that delegates to dart:html's | 9 * HTTP backend used by the [Http] service that delegates to dart:html's |
| 10 * [HttpRequest] and deals with Dart bugs. | 10 * [HttpRequest] and deals with Dart bugs. |
| 11 * | 11 * |
| 12 * Never use this service directly, instead use the higher-level [Http]. | 12 * Never use this service directly, instead use the higher-level [Http]. |
| 13 * | 13 * |
| 14 * During testing this implementation is swapped with [MockHttpBackend] which | 14 * During testing this implementation is swapped with [MockHttpBackend] which |
| 15 * can be trained with responses. | 15 * can be trained with responses. |
| 16 */ | 16 */ |
| 17 @NgInjectableService() | 17 @NgInjectableService() |
| 18 class HttpBackend { | 18 class HttpBackend { |
| 19 /** | 19 /** |
| 20 * Wrapper around dart:html's [HttpRequest.request] | 20 * Wrapper around dart:html's [HttpRequest.request] |
| 21 */ | 21 */ |
| 22 async.Future request(String url, | 22 async.Future request(String url, |
| 23 {String method, bool withCredentials, String responseType, | 23 {String method, bool withCredentials, String responseType, |
| 24 String mimeType, Map<String, String> requestHeaders, sendData, | 24 String mimeType, Map<String, String> requestHeaders, sendData, |
| 25 void onProgress(dom.ProgressEvent e)}) => | 25 void onProgress(dom.ProgressEvent e)}) { |
| 26 dom.HttpRequest.request(url, method: method, | 26 // Complete inside a then to work-around dartbug.com/13051 |
| 27 withCredentials: withCredentials, responseType: responseType, | 27 var c = new async.Completer(); |
| 28 mimeType: mimeType, requestHeaders: requestHeaders, | 28 |
| 29 sendData: sendData, onProgress: onProgress); | 29 dom.HttpRequest.request(url, |
| 30 method: method, |
| 31 withCredentials: withCredentials, |
| 32 responseType: responseType, |
| 33 mimeType: mimeType, |
| 34 requestHeaders: requestHeaders, |
| 35 sendData: sendData, |
| 36 onProgress: onProgress).then((x) => c.complete(x), |
| 37 onError: (e, stackTrace) => c.completeError(e, stackTrace)); |
| 38 return c.future; |
| 39 } |
| 30 } | 40 } |
| 31 | 41 |
| 32 @NgInjectableService() | 42 @NgInjectableService() |
| 33 class LocationWrapper { | 43 class LocationWrapper { |
| 34 get location => dom.window.location; | 44 get location => dom.window.location; |
| 35 } | 45 } |
| 36 | 46 |
| 37 typedef RequestInterceptor(HttpResponseConfig); | 47 typedef RequestInterceptor(HttpResponseConfig); |
| 38 typedef RequestErrorInterceptor(dynamic); | 48 typedef RequestErrorInterceptor(dynamic); |
| 39 typedef Response(HttpResponse); | 49 typedef Response(HttpResponse); |
| 40 typedef ResponseError(dynamic); | 50 typedef ResponseError(dynamic); |
| 41 | 51 |
| 42 /** | 52 /** |
| 43 * HttpInterceptors are used to modify the Http request. They can be added to | 53 * HttpInterceptors are used to modify the Http request. They can be added to |
| 44 * [HttpInterceptors] or passed into [Http.call]. | 54 * [HttpInterceptors] or passed into [Http.call]. |
| 45 */ | 55 */ |
| 46 class HttpInterceptor { | 56 class HttpInterceptor { |
| 47 RequestInterceptor request; | 57 RequestInterceptor request; |
| 48 Response response; | 58 Response response; |
| 49 RequestErrorInterceptor requestError; | 59 RequestErrorInterceptor requestError; |
| 50 ResponseError responseError; | 60 ResponseError responseError; |
| 51 | 61 |
| 52 /** | 62 /** |
| 53 * All parameters are optional. | 63 * All parameters are optional. |
| 54 */ | 64 */ |
| 55 HttpInterceptor({this.request, this.response, this.requestError, | 65 HttpInterceptor({ |
| 56 this.responseError}); | 66 this.request, this.response, |
| 67 this.requestError, this.responseError}); |
| 57 } | 68 } |
| 58 | 69 |
| 59 | 70 |
| 60 /** | 71 /** |
| 61 * The default transform data interceptor.abstract | 72 * The default transform data interceptor.abstract |
| 62 * | 73 * |
| 63 * For requests, this interceptor will | 74 * For requests, this interceptor will |
| 64 * automatically stringify any non-string non-file objects. | 75 * automatically stringify any non-string non-file objects. |
| 65 * | 76 * |
| 66 * For responses, this interceptor will unwrap JSON objects and | 77 * For responses, this interceptor will unwrap JSON objects and |
| 67 * parse them into [Map]s. | 78 * parse them into [Map]s. |
| 68 */ | 79 */ |
| 69 class DefaultTransformDataHttpInterceptor implements HttpInterceptor { | 80 class DefaultTransformDataHttpInterceptor implements HttpInterceptor { |
| 70 Function request = (HttpResponseConfig config) { | 81 Function request = (HttpResponseConfig config) { |
| 71 if (config.data != null && config.data is! String && | 82 if (config.data != null && config.data is! String && config.data is! dom.Fil
e) { |
| 72 config.data is! dom.File) { | |
| 73 config.data = JSON.encode(config.data); | 83 config.data = JSON.encode(config.data); |
| 74 } | 84 } |
| 75 return config; | 85 return config; |
| 76 }; | 86 }; |
| 77 | 87 |
| 78 static var _JSON_START = new RegExp(r'^\s*(\[|\{[^\{])'); | 88 static var _JSON_START = new RegExp(r'^\s*(\[|\{[^\{])'); |
| 79 static var _JSON_END = new RegExp(r'[\}\]]\s*$'); | 89 static var _JSON_END = new RegExp(r'[\}\]]\s*$'); |
| 80 static var _PROTECTION_PREFIX = new RegExp('^\\)\\]\\}\',?\\n'); | 90 static var _PROTECTION_PREFIX = new RegExp('^\\)\\]\\}\',?\\n'); |
| 81 Function response = (HttpResponse r) { | 91 Function response = (HttpResponse r) { |
| 82 if (r.data is String) { | 92 if (r.data is String) { |
| 83 var d = r.data.replaceFirst(_PROTECTION_PREFIX, ''); | 93 var d = r.data; |
| 94 d = d.replaceFirst(_PROTECTION_PREFIX, ''); |
| 84 if (d.contains(_JSON_START) && d.contains(_JSON_END)) { | 95 if (d.contains(_JSON_START) && d.contains(_JSON_END)) { |
| 85 d = JSON.decode(d); | 96 d = JSON.decode(d); |
| 86 } | 97 } |
| 87 return new HttpResponse.copy(r, data: d); | 98 return new HttpResponse.copy(r, data: d); |
| 88 } | 99 } |
| 89 return r; | 100 return r; |
| 90 }; | 101 }; |
| 91 | 102 |
| 92 Function requestError, responseError; | 103 Function requestError, responseError; |
| 93 } | 104 } |
| 94 | 105 |
| 95 /** | 106 /** |
| 96 * A list of [HttpInterceptor]s. | 107 * A list of [HttpInterceptor]s. |
| 97 */ | 108 */ |
| 98 @NgInjectableService() | 109 @NgInjectableService() |
| 99 class HttpInterceptors { | 110 class HttpInterceptors { |
| 100 List<HttpInterceptor> _interceptors = | 111 List<HttpInterceptor> _interceptors = [new DefaultTransformDataHttpInterceptor
()]; |
| 101 [new DefaultTransformDataHttpInterceptor()]; | |
| 102 | 112 |
| 103 add(HttpInterceptor x) => _interceptors.add(x); | 113 add(HttpInterceptor x) => _interceptors.add(x); |
| 104 addAll(List<HttpInterceptor> x) => _interceptors.addAll(x); | 114 addAll(List<HttpInterceptor> x) => _interceptors.addAll(x); |
| 105 | 115 |
| 106 /** | 116 /** |
| 107 * Called from [Http] to construct a [Future] chain. | 117 * Called from [Http] to construct a [Future] chain. |
| 108 */ | 118 */ |
| 109 constructChain(List chain) { | 119 constructChain(List chain) { |
| 110 _interceptors.reversed.forEach((HttpInterceptor i) { | 120 _interceptors.reversed.forEach((HttpInterceptor i) { |
| 111 // AngularJS has an optimization of not including null interceptors. | 121 // AngularJS has an optimization of not including null interceptors. |
| 112 chain | 122 chain.insert(0, [ |
| 113 ..insert(0, [ | 123 i.request == null ? (x) => x : i.request, |
| 114 i.request == null ? (x) => x : i.request, | 124 i.requestError]); |
| 115 i.requestError]) | 125 chain.add([ |
| 116 ..add([ | 126 i.response == null ? (x) => x : i.response, |
| 117 i.response == null ? (x) => x : i.response, | 127 i.responseError]); |
| 118 i.responseError]); | |
| 119 }); | 128 }); |
| 120 } | 129 } |
| 121 | 130 |
| 122 /** | 131 /** |
| 123 * Default constructor. | 132 * Default constructor. |
| 124 */ | 133 */ |
| 125 HttpInterceptors() { | 134 HttpInterceptors() { |
| 126 _interceptors = [new DefaultTransformDataHttpInterceptor()]; | 135 _interceptors = [new DefaultTransformDataHttpInterceptor()]; |
| 127 } | 136 } |
| 128 | 137 |
| 129 /** | 138 /** |
| 130 * Creates a [HttpInterceptors] from a [List]. Does not include the default | 139 * Creates a [HttpInterceptors] from a [List]. Does not include the default i
nterceptors. |
| 131 * interceptors. | |
| 132 */ | 140 */ |
| 133 HttpInterceptors.of([List interceptors]) { | 141 HttpInterceptors.of([List interceptors]) { |
| 134 _interceptors = interceptors; | 142 _interceptors = interceptors; |
| 135 } | 143 } |
| 136 } | 144 } |
| 137 | 145 |
| 138 /** | 146 /** |
| 139 * The request configuration of the request associated with this response. | 147 * The request configuration of the request associated with this response. |
| 140 */ | 148 */ |
| 141 class HttpResponseConfig { | 149 class HttpResponseConfig { |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 config = r.config; | 221 config = r.config; |
| 214 } | 222 } |
| 215 | 223 |
| 216 /** | 224 /** |
| 217 * The response's data. Either a string or a transformed object. | 225 * The response's data. Either a string or a transformed object. |
| 218 */ | 226 */ |
| 219 get data => responseText; | 227 get data => responseText; |
| 220 | 228 |
| 221 /** | 229 /** |
| 222 * The response's headers. Without parameters, this method will return the | 230 * The response's headers. Without parameters, this method will return the |
| 223 * [Map] of headers. With [key] parameter, this method will return the | 231 * [Map] of headers. With [key] parameter, this method will return the specif
ic |
| 224 * specific header. | 232 * header. |
| 225 */ | 233 */ |
| 226 headers([String key]) => key == null ? _headers : _headers[key]; | 234 headers([String key]) { |
| 235 return key == null ? _headers : _headers[key]; |
| 236 } |
| 227 | 237 |
| 228 /** | 238 /** |
| 229 * Useful for debugging. | 239 * Useful for debugging. |
| 230 */ | 240 */ |
| 231 toString() => 'HTTP $status: $data'; | 241 toString() => 'HTTP $status: $data'; |
| 232 } | 242 } |
| 233 | 243 |
| 234 /** | 244 /** |
| 235 * Default header configuration. | 245 * Default header configuration. |
| 236 */ | 246 */ |
| 237 @NgInjectableService() | 247 @NgInjectableService() |
| 238 class HttpDefaultHeaders { | 248 class HttpDefaultHeaders { |
| 239 static var _defaultContentType = 'application/json;charset=utf-8'; | 249 static String _defaultContentType = 'application/json;charset=utf-8'; |
| 240 var _headers = { | 250 Map _headers = { |
| 241 'COMMON': {'Accept': 'application/json, text/plain, */*'}, | 251 'COMMON': { |
| 242 'POST' : {'Content-Type': _defaultContentType}, | 252 'Accept': 'application/json, text/plain, */*' |
| 243 'PUT' : {'Content-Type': _defaultContentType }, | 253 }, |
| 244 'PATCH' : {'Content-Type': _defaultContentType} | 254 'POST' : { |
| 255 'Content-Type': _defaultContentType |
| 256 }, |
| 257 'PUT' : { |
| 258 'Content-Type': _defaultContentType |
| 259 }, |
| 260 'PATCH' : { |
| 261 'Content-Type': _defaultContentType |
| 262 } |
| 245 }; | 263 }; |
| 246 | 264 |
| 247 _applyHeaders(method, ucHeaders, headers) { | 265 _applyHeaders(method, ucHeaders, headers) { |
| 248 if (!_headers.containsKey(method)) return; | 266 if (!_headers.containsKey(method)) return; |
| 249 _headers[method].forEach((k, v) { | 267 _headers[method].forEach((k, v) { |
| 250 if (!ucHeaders.contains(k.toUpperCase())) { | 268 if (!ucHeaders.contains(k.toUpperCase())) { |
| 251 headers[k] = v; | 269 headers[k] = v; |
| 252 } | 270 } |
| 253 }); | 271 }); |
| 254 } | 272 } |
| 255 | 273 |
| 256 /** | 274 /** |
| 257 * Called from [Http], this method sets default headers on [headers] | 275 * Called from [Http], this method sets default headers on [headers] |
| 258 */ | 276 */ |
| 259 setHeaders(Map<String, String> headers, String method) { | 277 setHeaders(Map<String, String> headers, String method) { |
| 260 assert(headers != null); | 278 assert(headers != null); |
| 261 var ucHeaders = headers.keys.map((x) => x.toUpperCase()).toSet(); | 279 var ucHeaders = headers.keys.map((x) => x.toUpperCase()).toSet(); |
| 262 _applyHeaders('COMMON', ucHeaders, headers); | 280 _applyHeaders('COMMON', ucHeaders, headers); |
| 263 _applyHeaders(method.toUpperCase(), ucHeaders, headers); | 281 _applyHeaders(method.toUpperCase(), ucHeaders, headers); |
| 264 } | 282 } |
| 265 | 283 |
| 266 /** | 284 /** |
| 267 * Returns the default header [Map] for a method. You can then modify | 285 * Returns the default header [Map] for a method. You can then modify |
| 268 * the map. | 286 * the map. |
| 269 * | 287 * |
| 270 * Passing 'common' as [method] will return a Map that contains headers | 288 * Passing 'common' as [method] will return a Map that contains headers |
| 271 * common to all operations. | 289 * common to all operations. |
| 272 */ | 290 */ |
| 273 operator[](method) => _headers[method.toUpperCase()]; | 291 operator[](method) { |
| 292 return _headers[method.toUpperCase()]; |
| 293 } |
| 274 } | 294 } |
| 275 | 295 |
| 276 /** | 296 /** |
| 277 * Injected into the [Http] service. This class contains application-wide | 297 * Injected into the [Http] service. This class contains application-wide |
| 278 * HTTP defaults. | 298 * HTTP defaults. |
| 279 * | 299 * |
| 280 * The default implementation provides headers which the | 300 * The default implementation provides headers which the |
| 281 * Angular team believes to be useful. | 301 * Angular team believes to be useful. |
| 282 */ | 302 */ |
| 283 @NgInjectableService() | 303 @NgInjectableService() |
| (...skipping 20 matching lines...) Expand all Loading... |
| 304 */ | 324 */ |
| 305 String xsrfHeaderName = 'X-XSRF-TOKEN'; | 325 String xsrfHeaderName = 'X-XSRF-TOKEN'; |
| 306 | 326 |
| 307 /** | 327 /** |
| 308 * Constructor intended for DI. | 328 * Constructor intended for DI. |
| 309 */ | 329 */ |
| 310 HttpDefaults(this.headers); | 330 HttpDefaults(this.headers); |
| 311 } | 331 } |
| 312 | 332 |
| 313 /** | 333 /** |
| 314 * The [Http] service facilitates communication with the remote HTTP servers. | 334 * The [Http] service facilitates communication with the remote HTTP servers. I
t |
| 315 * It uses dart:html's [HttpRequest] and provides a number of features on top | 335 * uses dart:html's [HttpRequest] and provides a number of features on top |
| 316 * of the core Dart library. | 336 * of the core Dart library. |
| 317 * | 337 * |
| 318 * For unit testing, applications should use the [MockHttpBackend] service. | 338 * For unit testing, applications should use the [MockHttpBackend] service. |
| 319 * | 339 * |
| 320 * # General usage | 340 * # General usage |
| 321 * The [call] method takes a number of named parameters and returns a | 341 * The [call] method takes a number of named parameters and returns a |
| 322 * [Future<HttpResponse>]. | 342 * [Future<HttpResponse>]. |
| 323 * | 343 * |
| 324 * http(method: 'GET', url: '/someUrl') | 344 * http(method: 'GET', url: '/someUrl') |
| 325 * .then((HttpResponse response) { .. }, | 345 * .then((HttpResponse response) { .. }, |
| 326 * onError: (HttpRequest request) { .. }); | 346 * onError: (HttpRequest request) { .. }); |
| 327 * | 347 * |
| 328 * A response status code between 200 and 299 is considered a success status and | 348 * A response status code between 200 and 299 is considered a success status and |
| 329 * will result in the 'then' being called. Note that if the response is a | 349 * will result in the 'then' being called. Note that if the response is a redire
ct, |
| 330 * redirect, Dart's [HttpRequest] will transparently follow it, meaning that the | 350 * Dart's [HttpRequest] will transparently follow it, meaning that the error cal
lback will not be |
| 331 * error callback will not be called for such responses. | 351 * called for such responses. |
| 332 * | 352 * |
| 333 * # Shortcut methods | 353 * # Shortcut methods |
| 334 * | 354 * |
| 335 * The Http service also defines a number of shortcuts: | 355 * The Http service also defines a number of shortcuts: |
| 336 * | 356 * |
| 337 * http.get('/someUrl') is the same as http(method: 'GET', url: '/someUrl') | 357 * http.get('/someUrl') is the same as http(method: 'GET', url: '/someUrl') |
| 338 * | 358 * |
| 339 * See the method definitions below. | 359 * See the method definitions below. |
| 340 * | 360 * |
| 341 * # Setting HTTP Headers | 361 * # Setting HTTP Headers |
| (...skipping 20 matching lines...) Expand all Loading... |
| 362 * | 382 * |
| 363 * Http uses the interceptors from [HttpInterceptors]. You can also include | 383 * Http uses the interceptors from [HttpInterceptors]. You can also include |
| 364 * interceptors in the [call] method. | 384 * interceptors in the [call] method. |
| 365 * | 385 * |
| 366 * # Security Considerations | 386 * # Security Considerations |
| 367 * | 387 * |
| 368 * NOTE: < not yet documented > | 388 * NOTE: < not yet documented > |
| 369 */ | 389 */ |
| 370 @NgInjectableService() | 390 @NgInjectableService() |
| 371 class Http { | 391 class Http { |
| 372 var _pendingRequests = <String, async.Future<HttpResponse>>{}; | 392 Map<String, async.Future<HttpResponse>> _pendingRequests = <String, async.Futu
re<HttpResponse>>{}; |
| 373 BrowserCookies _cookies; | 393 BrowserCookies _cookies; |
| 374 LocationWrapper _location; | 394 LocationWrapper _location; |
| 375 UrlRewriter _rewriter; | 395 UrlRewriter _rewriter; |
| 376 HttpBackend _backend; | 396 HttpBackend _backend; |
| 377 HttpInterceptors _interceptors; | 397 HttpInterceptors _interceptors; |
| 378 | 398 |
| 379 /** | 399 /** |
| 380 * The defaults for [Http] | 400 * The defaults for [Http] |
| 381 */ | 401 */ |
| 382 HttpDefaults defaults; | 402 HttpDefaults defaults; |
| 383 | 403 |
| 384 /** | 404 /** |
| 385 * Constructor, useful for DI. | 405 * Constructor, useful for DI. |
| 386 */ | 406 */ |
| 387 Http(this._cookies, this._location, this._rewriter, this._backend, | 407 Http(this._cookies, this._location, this._rewriter, this._backend, this.defaul
ts, this._interceptors); |
| 388 this.defaults, this._interceptors); | |
| 389 | 408 |
| 390 /** | 409 /** |
| 391 * DEPRECATED | 410 * DEPRECATED |
| 392 */ | 411 */ |
| 393 async.Future<String> getString(String url, {bool withCredentials, | 412 async.Future<String> getString(String url, |
| 394 void onProgress(dom.ProgressEvent e), Cache cache}) => | 413 {bool withCredentials, void onProgress(dom.ProgressEvent e), Cache cache})
{ |
| 395 request(url, | 414 return request(url, |
| 396 withCredentials: withCredentials, | 415 withCredentials: withCredentials, |
| 397 onProgress: onProgress, | 416 onProgress: onProgress, |
| 398 cache: cache).then((HttpResponse xhr) => xhr.responseText); | 417 cache: cache).then((HttpResponse xhr) => xhr.responseText); |
| 418 } |
| 399 | 419 |
| 400 /** | 420 /** |
| 401 * Parse a [requestUrl] and determine whether this is a same-origin request as | 421 * Parse a request URL and determine whether this is a same-origin request as
the application document. |
| 402 * the application document. | 422 * |
| 423 * @param {string|Uri} requestUrl The url of the request as a string that will
be resolved |
| 424 * or a parsed URL object. |
| 425 * @returns {boolean} Whether the request is for the same origin as the applic
ation document. |
| 403 */ | 426 */ |
| 404 bool _urlIsSameOrigin(String requestUrl) { | 427 _urlIsSameOrigin(String requestUrl) { |
| 405 Uri originUrl = Uri.parse(_location.location.toString()); | 428 Uri originUrl = Uri.parse(_location.location.toString()); |
| 406 Uri parsed = originUrl.resolve(requestUrl); | 429 Uri parsed = originUrl.resolve(requestUrl); |
| 407 return (parsed.scheme == originUrl.scheme && parsed.host == originUrl.host); | 430 return (parsed.scheme == originUrl.scheme && |
| 431 parsed.host == originUrl.host); |
| 408 } | 432 } |
| 409 | 433 |
| 410 /** | 434 /** |
| 411 * Returns a [Future<HttpResponse>] when the request is fulfilled. | 435 * Returns a [Future<HttpResponse>] when the request is fulfilled. |
| 412 * | 436 * |
| 413 * Named Parameters: | 437 * Named Parameters: |
| 414 * - method: HTTP method (e.g. 'GET', 'POST', etc) | 438 * - method: HTTP method (e.g. 'GET', 'POST', etc) |
| 415 * - url: Absolute or relative URL of the resource being requested. | 439 * - url: Absolute or relative URL of the resource being requested. |
| 416 * - data: Data to be sent as the request message data. | 440 * - data: Data to be sent as the request message data. |
| 417 * - params: Map of strings or objects which will be turned to | 441 * - params: Map of strings or objects which will be turned to |
| (...skipping 19 matching lines...) Expand all Loading... |
| 437 interceptors, | 461 interceptors, |
| 438 cache, | 462 cache, |
| 439 timeout | 463 timeout |
| 440 }) { | 464 }) { |
| 441 if (timeout != null) { | 465 if (timeout != null) { |
| 442 throw ['timeout not implemented']; | 466 throw ['timeout not implemented']; |
| 443 } | 467 } |
| 444 | 468 |
| 445 method = method.toUpperCase(); | 469 method = method.toUpperCase(); |
| 446 | 470 |
| 447 if (headers == null) headers = {}; | 471 if (headers == null) { headers = {}; } |
| 448 defaults.headers.setHeaders(headers, method); | 472 defaults.headers.setHeaders(headers, method); |
| 449 | 473 |
| 450 var xsrfValue = _urlIsSameOrigin(url) ? | 474 var xsrfValue = _urlIsSameOrigin(url) ? |
| 451 _cookies[xsrfCookieName != null ? xsrfCookieName : defaults.xsrfCookieNa
me] : | 475 _cookies[xsrfCookieName != null ? xsrfCookieName : defaults.xsrfCookieNa
me] : null; |
| 452 null; | |
| 453 if (xsrfValue != null) { | 476 if (xsrfValue != null) { |
| 454 headers[xsrfHeaderName != null ? xsrfHeaderName : defaults.xsrfHeaderName] | 477 headers[xsrfHeaderName != null ? xsrfHeaderName : defaults.xsrfHeaderName]
= xsrfValue; |
| 455 = xsrfValue; | |
| 456 } | 478 } |
| 457 | 479 |
| 458 // Check for functions in headers | 480 // Check for functions in headers |
| 459 headers.forEach((k, v) { | 481 headers.forEach((k,v) { |
| 460 if (v is Function) headers[k] = v(); | 482 if (v is Function) { |
| 483 headers[k] = v(); |
| 484 } |
| 461 }); | 485 }); |
| 462 | 486 |
| 463 var serverRequest = (HttpResponseConfig config) { | 487 var serverRequest = (HttpResponseConfig config) { |
| 464 assert(config.data == null || config.data is String || | 488 assert(config.data == null || config.data is String || config.data is dom.
File); |
| 465 config.data is dom.File); | |
| 466 | 489 |
| 467 // Strip content-type if data is undefined | 490 // Strip content-type if data is undefined |
| 468 if (config.data == null) { | 491 if (config.data == null) { |
| 469 new List.from(headers.keys) | 492 new List.from(headers.keys) |
| 470 .where((h) => h.toUpperCase() == 'CONTENT-TYPE') | 493 .where((h) => h.toUpperCase() == 'CONTENT-TYPE') |
| 471 .forEach((h) => headers.remove(h)); | 494 .forEach((h) => headers.remove(h)); |
| 472 } | 495 } |
| 473 | 496 |
| 474 return request(null, | 497 return request( |
| 475 config: config, | 498 null, |
| 476 method: method, | 499 config: config, |
| 477 sendData: config.data, | 500 method: method, |
| 478 requestHeaders: config.headers, | 501 sendData: config.data, |
| 479 cache: cache); | 502 requestHeaders: config.headers, |
| 503 cache: cache); |
| 480 }; | 504 }; |
| 481 | 505 |
| 482 var chain = [[serverRequest, null]]; | 506 var chain = [[serverRequest, null]]; |
| 483 | 507 |
| 484 var future = new async.Future.value(new HttpResponseConfig( | 508 var future = new async.Future.value(new HttpResponseConfig( |
| 485 url: url, | 509 url: url, |
| 486 params: params, | 510 params: params, |
| 487 headers: headers, | 511 headers: headers, |
| 488 data: data)); | 512 data: data)); |
| 489 | 513 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 510 */ | 534 */ |
| 511 async.Future<HttpResponse> get(String url, { | 535 async.Future<HttpResponse> get(String url, { |
| 512 String data, | 536 String data, |
| 513 Map<String, dynamic> params, | 537 Map<String, dynamic> params, |
| 514 Map<String, String> headers, | 538 Map<String, String> headers, |
| 515 xsrfHeaderName, | 539 xsrfHeaderName, |
| 516 xsrfCookieName, | 540 xsrfCookieName, |
| 517 interceptors, | 541 interceptors, |
| 518 cache, | 542 cache, |
| 519 timeout | 543 timeout |
| 520 }) => call(method: 'GET', url: url, data: data, params: params, | 544 }) => call(method: 'GET', url: url, data: data, params: params, headers: heade
rs, |
| 521 headers: headers, xsrfHeaderName: xsrfHeaderName, | 545 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 522 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 546 interceptors: interceptors, |
| 523 cache: cache, timeout: timeout); | 547 cache: cache, timeout: timeout); |
| 524 | 548 |
| 525 /** | 549 /** |
| 526 * Shortcut method for DELETE requests. See [call] for a complete description | 550 * Shortcut method for DELETE requests. See [call] for a complete description |
| 527 * of parameters. | 551 * of parameters. |
| 528 */ | 552 */ |
| 529 async.Future<HttpResponse> delete(String url, { | 553 async.Future<HttpResponse> delete(String url, { |
| 530 String data, | 554 String data, |
| 531 Map<String, dynamic> params, | 555 Map<String, dynamic> params, |
| 532 Map<String, String> headers, | 556 Map<String, String> headers, |
| 533 xsrfHeaderName, | 557 xsrfHeaderName, |
| 534 xsrfCookieName, | 558 xsrfCookieName, |
| 535 interceptors, | 559 interceptors, |
| 536 cache, | 560 cache, |
| 537 timeout | 561 timeout |
| 538 }) => call(method: 'DELETE', url: url, data: data, params: params, | 562 }) => call(method: 'DELETE', url: url, data: data, params: params, headers: he
aders, |
| 539 headers: headers, xsrfHeaderName: xsrfHeaderName, | 563 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 540 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 564 interceptors: interceptors, |
| 541 cache: cache, timeout: timeout); | 565 cache: cache, timeout: timeout); |
| 542 | 566 |
| 543 /** | 567 /** |
| 544 * Shortcut method for HEAD requests. See [call] for a complete description | 568 * Shortcut method for HEAD requests. See [call] for a complete description |
| 545 * of parameters. | 569 * of parameters. |
| 546 */ | 570 */ |
| 547 async.Future<HttpResponse> head(String url, { | 571 async.Future<HttpResponse> head(String url, { |
| 548 String data, | 572 String data, |
| 549 Map<String, dynamic> params, | 573 Map<String, dynamic> params, |
| 550 Map<String, String> headers, | 574 Map<String, String> headers, |
| 551 xsrfHeaderName, | 575 xsrfHeaderName, |
| 552 xsrfCookieName, | 576 xsrfCookieName, |
| 553 interceptors, | 577 interceptors, |
| 554 cache, | 578 cache, |
| 555 timeout | 579 timeout |
| 556 }) => call(method: 'HEAD', url: url, data: data, params: params, | 580 }) => call(method: 'HEAD', url: url, data: data, params: params, headers: head
ers, |
| 557 headers: headers, xsrfHeaderName: xsrfHeaderName, | 581 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 558 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 582 interceptors: interceptors, |
| 559 cache: cache, timeout: timeout); | 583 cache: cache, timeout: timeout); |
| 560 | 584 |
| 561 /** | 585 /** |
| 562 * Shortcut method for PUT requests. See [call] for a complete description | 586 * Shortcut method for PUT requests. See [call] for a complete description |
| 563 * of parameters. | 587 * of parameters. |
| 564 */ | 588 */ |
| 565 async.Future<HttpResponse> put(String url, String data, { | 589 async.Future<HttpResponse> put(String url, String data, { |
| 566 Map<String, dynamic> params, | 590 Map<String, dynamic> params, |
| 567 Map<String, String> headers, | 591 Map<String, String> headers, |
| 568 xsrfHeaderName, | 592 xsrfHeaderName, |
| 569 xsrfCookieName, | 593 xsrfCookieName, |
| 570 interceptors, | 594 interceptors, |
| 571 cache, | 595 cache, |
| 572 timeout | 596 timeout |
| 573 }) => call(method: 'PUT', url: url, data: data, params: params, | 597 }) => call(method: 'PUT', url: url, data: data, params: params, headers: heade
rs, |
| 574 headers: headers, xsrfHeaderName: xsrfHeaderName, | 598 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 575 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 599 interceptors: interceptors, |
| 576 cache: cache, timeout: timeout); | 600 cache: cache, timeout: timeout); |
| 577 | 601 |
| 578 /** | 602 /** |
| 579 * Shortcut method for POST requests. See [call] for a complete description | 603 * Shortcut method for POST requests. See [call] for a complete description |
| 580 * of parameters. | 604 * of parameters. |
| 581 */ | 605 */ |
| 582 async.Future<HttpResponse> post(String url, String data, { | 606 async.Future<HttpResponse> post(String url, String data, { |
| 583 Map<String, dynamic> params, | 607 Map<String, dynamic> params, |
| 584 Map<String, String> headers, | 608 Map<String, String> headers, |
| 585 xsrfHeaderName, | 609 xsrfHeaderName, |
| 586 xsrfCookieName, | 610 xsrfCookieName, |
| 587 interceptors, | 611 interceptors, |
| 588 cache, | 612 cache, |
| 589 timeout | 613 timeout |
| 590 }) => call(method: 'POST', url: url, data: data, params: params, | 614 }) => call(method: 'POST', url: url, data: data, params: params, headers: head
ers, |
| 591 headers: headers, xsrfHeaderName: xsrfHeaderName, | 615 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 592 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 616 interceptors: interceptors, |
| 593 cache: cache, timeout: timeout); | 617 cache: cache, timeout: timeout); |
| 594 | 618 |
| 595 /** | 619 /** |
| 596 * Shortcut method for JSONP requests. See [call] for a complete description | 620 * Shortcut method for JSONP requests. See [call] for a complete description |
| 597 * of parameters. | 621 * of parameters. |
| 598 */ | 622 */ |
| 599 async.Future<HttpResponse> jsonp(String url, { | 623 async.Future<HttpResponse> jsonp(String url, { |
| 600 String data, | 624 String data, |
| 601 Map<String, dynamic> params, | 625 Map<String, dynamic> params, |
| 602 Map<String, String> headers, | 626 Map<String, String> headers, |
| 603 xsrfHeaderName, | 627 xsrfHeaderName, |
| 604 xsrfCookieName, | 628 xsrfCookieName, |
| 605 interceptors, | 629 interceptors, |
| 606 cache, | 630 cache, |
| 607 timeout | 631 timeout |
| 608 }) => call(method: 'JSONP', url: url, data: data, params: params, | 632 }) => call(method: 'JSONP', url: url, data: data, params: params, headers: hea
ders, |
| 609 headers: headers, xsrfHeaderName: xsrfHeaderName, | 633 xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName, |
| 610 xsrfCookieName: xsrfCookieName, interceptors: interceptors, | 634 interceptors: interceptors, |
| 611 cache: cache, timeout: timeout); | 635 cache: cache, timeout: timeout); |
| 612 | 636 |
| 613 /** | 637 /** |
| 614 * Parse raw headers into key-value object | 638 * Parse raw headers into key-value object |
| 615 */ | 639 */ |
| 616 static Map<String, String> parseHeaders(dom.HttpRequest value) { | 640 static Map<String, String> parseHeaders(dom.HttpRequest value) { |
| 617 var headers = value.getAllResponseHeaders(); | 641 var headers = value.getAllResponseHeaders(); |
| 618 | 642 |
| 619 var parsed = {}; | 643 var parsed = {}; |
| 620 | 644 |
| 621 if (headers == null) return parsed; | 645 if (headers == null) return parsed; |
| 622 | 646 |
| 623 headers.split('\n').forEach((line) { | 647 headers.split('\n').forEach((line) { |
| 624 var i = line.indexOf(':'); | 648 var i = line.indexOf(':'); |
| 625 if (i == -1) return; | 649 if (i == -1) return; |
| 626 var key = line.substring(0, i).trim().toLowerCase(); | 650 var key = line.substring(0, i).trim().toLowerCase(); |
| 651 var val = line.substring(i + 1).trim(); |
| 627 | 652 |
| 628 if (key.isNotEmpty) { | 653 if (key != '') { |
| 629 var val = line.substring(i + 1).trim(); | 654 if (parsed.containsKey(key)) { |
| 630 parsed[key] = parsed.containsKey(key) ? "${parsed[key]}, $val" : val; | 655 parsed[key] += ', ' + val; |
| 656 } else { |
| 657 parsed[key] = val; |
| 658 } |
| 631 } | 659 } |
| 632 }); | 660 }); |
| 633 return parsed; | 661 return parsed; |
| 634 } | 662 } |
| 635 | 663 |
| 636 /** | 664 /** |
| 637 * Returns an [Iterable] of [Future] [HttpResponse]s for the requests | 665 * Returns an [Iterable] of [Future] [HttpResponse]s for the requests |
| 638 * that the [Http] service is currently waiting for. | 666 * that the [Http] service is currently waiting for. |
| 639 */ | 667 */ |
| 640 Iterable<async.Future<HttpResponse> > get pendingRequests => | 668 Iterable<async.Future<HttpResponse> > get pendingRequests => |
| 641 _pendingRequests.values; | 669 _pendingRequests.values; |
| 642 | 670 |
| 643 /** | 671 /** |
| 644 * DEPRECATED | 672 * DEPRECATED |
| 645 */ | 673 */ |
| 646 async.Future<HttpResponse> request(String rawUrl, | 674 async.Future<HttpResponse> request(String rawUrl, |
| 647 { HttpResponseConfig config, | 675 { HttpResponseConfig config, |
| 648 String method: 'GET', | 676 String method: 'GET', |
| 649 bool withCredentials: false, | 677 bool withCredentials: false, |
| 650 String responseType, | 678 String responseType, |
| 651 String mimeType, | 679 String mimeType, |
| 652 Map<String, String> requestHeaders, | 680 Map<String, String> requestHeaders, |
| 653 sendData, | 681 sendData, |
| 654 void onProgress(dom.ProgressEvent e), | 682 void onProgress(dom.ProgressEvent e), |
| 655 /*Cache<String, HttpResponse> or false*/ cache }) { | 683 /*Cache<String, HttpResponse> or false*/ cache }) { |
| 656 String url; | 684 String url; |
| 657 | 685 |
| 658 if (config == null) { | 686 if (config == null) { |
| 659 url = _rewriter(rawUrl); | 687 url = _rewriter(rawUrl); |
| 660 config = new HttpResponseConfig(url: url); | 688 config = new HttpResponseConfig(url: url); |
| 661 } else { | 689 } else { |
| 662 url = _buildUrl(config.url, config.params); | 690 url = _buildUrl(config.url, config.params); |
| 663 } | 691 } |
| 664 | 692 |
| 665 if (cache == false) { | 693 if (cache is bool && cache == false) { |
| 666 cache = null; | 694 cache = null; |
| 667 } else if (cache == null) { | 695 } else if (cache == null) { |
| 668 cache = defaults.cache; | 696 cache = defaults.cache; |
| 669 } | 697 } |
| 670 // We return a pending request only if caching is enabled. | 698 // We return a pending request only if caching is enabled. |
| 671 if (cache != null && _pendingRequests.containsKey(url)) { | 699 if (cache != null && _pendingRequests.containsKey(url)) { |
| 672 return _pendingRequests[url]; | 700 return _pendingRequests[url]; |
| 673 } | 701 } |
| 674 var cachedResponse = (cache != null && method == 'GET') | 702 var cachedValue = (cache != null && method == 'GET') ? cache.get(url) : null
; |
| 675 ? cache.get(url) | 703 if (cachedValue != null) { |
| 676 : null; | 704 return new async.Future.value(new HttpResponse.copy(cachedValue)); |
| 677 if (cachedResponse != null) { | |
| 678 return new async.Future.value(new HttpResponse.copy(cachedResponse)); | |
| 679 } | 705 } |
| 680 | 706 |
| 681 var result = _backend.request(url, | 707 var result = _backend.request(url, |
| 682 method: method, | 708 method: method, |
| 683 withCredentials: withCredentials, | 709 withCredentials: withCredentials, |
| 684 responseType: responseType, | 710 responseType: responseType, |
| 685 mimeType: mimeType, | 711 mimeType: mimeType, |
| 686 requestHeaders: requestHeaders, | 712 requestHeaders: requestHeaders, |
| 687 sendData: sendData, | 713 sendData: sendData, |
| 688 onProgress: onProgress).then((dom.HttpRequest value) { | 714 onProgress: onProgress).then((dom.HttpRequest value) { |
| 689 // TODO: Uncomment after apps migrate off of this class. | 715 // TODO: Uncomment after apps migrate off of this class. |
| 690 // assert(value.status >= 200 && value.status < 300); | 716 // assert(value.status >= 200 && value.status < 300); |
| 691 | 717 |
| 692 var response = new HttpResponse(value.status, value.responseText, | 718 var response = new HttpResponse( |
| 693 parseHeaders(value), config); | 719 value.status, value.responseText, parseHeaders(value), |
| 720 config); |
| 694 | 721 |
| 695 if (cache != null) cache.put(url, response); | 722 if (cache != null) { |
| 723 cache.put(url, response); |
| 724 } |
| 696 _pendingRequests.remove(url); | 725 _pendingRequests.remove(url); |
| 697 return response; | 726 return response; |
| 698 }, onError: (error) { | 727 }, onError: (error) { |
| 699 if (error is! dom.ProgressEvent) throw error; | 728 if (error is! dom.ProgressEvent) { |
| 729 throw error; |
| 730 } |
| 700 dom.ProgressEvent event = error; | 731 dom.ProgressEvent event = error; |
| 701 _pendingRequests.remove(url); | 732 _pendingRequests.remove(url); |
| 702 dom.HttpRequest request = event.currentTarget; | 733 dom.HttpRequest request = event.currentTarget; |
| 703 return new async.Future.error( | 734 return new async.Future.error( |
| 704 new HttpResponse(request.status, request.response, | 735 new HttpResponse(request.status, request.response, |
| 705 parseHeaders(request), config)); | 736 parseHeaders(request), config)); |
| 706 }); | 737 }); |
| 707 return _pendingRequests[url] = result; | 738 _pendingRequests[url] = result; |
| 739 return result; |
| 708 } | 740 } |
| 709 | 741 |
| 710 _buildUrl(String url, Map<String, dynamic> params) { | 742 _buildUrl(String url, Map<String, dynamic> params) { |
| 711 if (params == null) return url; | 743 if (params == null) return url; |
| 712 var parts = []; | 744 var parts = []; |
| 713 | 745 |
| 714 new List.from(params.keys)..sort()..forEach((String key) { | 746 new List.from(params.keys)..sort()..forEach((String key) { |
| 715 var value = params[key]; | 747 var value = params[key]; |
| 716 if (value == null) return; | 748 if (value == null) return; |
| 717 if (value is! List) value = [value]; | 749 if (value is! List) value = [value]; |
| 718 | 750 |
| 719 value.forEach((v) { | 751 value.forEach((v) { |
| 720 if (v is Map) v = JSON.encode(v); | 752 if (v is Map) { |
| 721 parts.add(_encodeUriQuery(key) + '=' + _encodeUriQuery("$v")); | 753 v = JSON.encode(v); |
| 754 } |
| 755 parts.add(_encodeUriQuery(key) + '=' + |
| 756 _encodeUriQuery("$v")); |
| 722 }); | 757 }); |
| 723 }); | 758 }); |
| 724 return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); | 759 return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); |
| 725 } | 760 } |
| 726 | 761 |
| 727 _encodeUriQuery(val, {bool pctEncodeSpaces: false}) => | 762 _encodeUriQuery(val, {bool pctEncodeSpaces: false}) => |
| 728 Uri.encodeComponent(val) | 763 Uri.encodeComponent(val) |
| 729 .replaceAll('%40', '@') | 764 .replaceAll('%40', '@') |
| 730 .replaceAll('%3A', ':') | 765 .replaceAll('%3A', ':') |
| 731 .replaceAll('%24', r'$') | 766 .replaceAll('%24', r'$') |
| 732 .replaceAll('%2C', ',') | 767 .replaceAll('%2C', ',') |
| 733 .replaceAll('%20', pctEncodeSpaces ? '%20' : '+'); | 768 .replaceAll('%20', pctEncodeSpaces ? '%20' : '+'); |
| 734 } | 769 } |
| OLD | NEW |