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