| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of $LIBRARYNAME; | 5 part of $LIBRARYNAME; |
| 6 | 6 |
| 7 /** |
| 8 * A task specification for http requests. |
| 9 * |
| 10 * This specification is not available when an http request is sent through |
| 11 * direct use of [HttpRequest.send]. See [HttpRequestSendTaskSpecification]. |
| 12 * |
| 13 * A task created by this specification is a `Future<HttpRequest>`. |
| 14 */ |
| 15 class HttpRequestTaskSpecification extends TaskSpecification { |
| 16 /// The URL of the request. |
| 17 final String url; |
| 18 |
| 19 /// The method used to do the request. |
| 20 /// |
| 21 /// By default (when `null`) this is a `"GET"` request. Alternatively, the |
| 22 /// method can be `"POST"`, `"PUT"`, `"DELETE"`, etc. |
| 23 final String method; |
| 24 |
| 25 /// Whether the request should send credentials. Credentials are only useful |
| 26 /// for cross-origin requests. |
| 27 /// |
| 28 /// See [HttpRequest.request] for more information. |
| 29 final bool withCredentials; |
| 30 |
| 31 /// The desired response format. |
| 32 /// |
| 33 /// Supported types are: |
| 34 /// - `""`: (same as `"text"`), |
| 35 /// - `"arraybuffer"`, |
| 36 /// - `"blob"`, |
| 37 /// - `"document"`, |
| 38 /// - `"json"`, |
| 39 /// - `"text"` |
| 40 /// |
| 41 /// By default (when `null`) the desired format is `""`. |
| 42 final String responseType; |
| 43 |
| 44 /// The desired mime type. |
| 45 /// |
| 46 /// This overrides the default mime-type which is set up to transfer textual |
| 47 /// data. |
| 48 final String mimeType; |
| 49 |
| 50 /// The request headers that should be sent with the request. |
| 51 final Map<String, String> requestHeaders; |
| 52 |
| 53 /// The data that is sent with the request. |
| 54 /// |
| 55 /// When provided (not `null`) must be a [ByteBuffer], |
| 56 /// [Blob], [Document], [String], or [FormData]. |
| 57 final dynamic sendData; |
| 58 |
| 59 /// The function that is invoked on progress updates. This function is |
| 60 /// registered as an event listener on the created [HttpRequest] object, and |
| 61 /// thus has its own task. Further invocations of the progress function do |
| 62 /// *not* use the http-request task as task object. |
| 63 /// |
| 64 /// Creating an HTTP request automatically registers the on-progress listener. |
| 65 final ZoneUnaryCallback<dynamic, ProgressEvent> onProgress; |
| 66 |
| 67 HttpRequestTaskSpecification(this.url, |
| 68 {String this.method, bool this.withCredentials, String this.responseType, |
| 69 String this.mimeType, Map<String, String> this.requestHeaders, |
| 70 this.sendData, |
| 71 void this.onProgress(ProgressEvent e)}); |
| 72 |
| 73 String get name => "dart.html.http-request"; |
| 74 bool get isOneShot => true; |
| 75 } |
| 76 |
| 77 /** |
| 78 * A task specification for http requests that are initiated through a direct |
| 79 * invocation of [HttpRequest.send]. |
| 80 * |
| 81 * This specification serves as signal to zones that an http request has been |
| 82 * initiated. The created task is the [request] object itself, and |
| 83 * no callback is ever executed in this task. |
| 84 * |
| 85 * Note that event listeners on the http request are also registered in the |
| 86 * zone (although with their own task creations), and that a zone can thus |
| 87 * detect when the http-request returns. |
| 88 * |
| 89 * HTTP requests that are initiated through `request` methods don't use |
| 90 * this class but use [HttpRequestTaskSpecification]. |
| 91 */ |
| 92 class HttpRequestSendTaskSpecification extends TaskSpecification { |
| 93 final HttpRequest request; |
| 94 final dynamic sendData; |
| 95 |
| 96 HttpRequestSendTaskSpecification(this.request, this.sendData); |
| 97 |
| 98 String get name => "dart.html.http-request-send"; |
| 99 |
| 100 /** |
| 101 * No callback is ever executed in an http-request send task. |
| 102 */ |
| 103 bool get isOneShot => false; |
| 104 } |
| 105 |
| 7 /** | 106 /** |
| 8 * A client-side XHR request for getting data from a URL, | 107 * A client-side XHR request for getting data from a URL, |
| 9 * formally known as XMLHttpRequest. | 108 * formally known as XMLHttpRequest. |
| 10 * | 109 * |
| 11 * HttpRequest can be used to obtain data from HTTP and FTP protocols, | 110 * HttpRequest can be used to obtain data from HTTP and FTP protocols, |
| 12 * and is useful for AJAX-style page updates. | 111 * and is useful for AJAX-style page updates. |
| 13 * | 112 * |
| 14 * The simplest way to get the contents of a text file, such as a | 113 * The simplest way to get the contents of a text file, such as a |
| 15 * JSON-formatted file, is with [getString]. | 114 * JSON-formatted file, is with [getString]. |
| 16 * For example, the following code gets the contents of a JSON file | 115 * For example, the following code gets the contents of a JSON file |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 * with appropriate permissions in their manifest. Requests to file:// URIs | 282 * with appropriate permissions in their manifest. Requests to file:// URIs |
| 184 * will also never fail- the Future will always complete successfully, even | 283 * will also never fail- the Future will always complete successfully, even |
| 185 * when the file cannot be found. | 284 * when the file cannot be found. |
| 186 * | 285 * |
| 187 * See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access
_authentication). | 286 * See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access
_authentication). |
| 188 */ | 287 */ |
| 189 static Future<HttpRequest> request(String url, | 288 static Future<HttpRequest> request(String url, |
| 190 {String method, bool withCredentials, String responseType, | 289 {String method, bool withCredentials, String responseType, |
| 191 String mimeType, Map<String, String> requestHeaders, sendData, | 290 String mimeType, Map<String, String> requestHeaders, sendData, |
| 192 void onProgress(ProgressEvent e)}) { | 291 void onProgress(ProgressEvent e)}) { |
| 292 var spec = new HttpRequestTaskSpecification( |
| 293 url, method: method, |
| 294 withCredentials: withCredentials, |
| 295 responseType: responseType, |
| 296 mimeType: mimeType, |
| 297 requestHeaders: requestHeaders, |
| 298 sendData: sendData, |
| 299 onProgress: onProgress); |
| 300 |
| 301 if (identical(Zone.current, Zone.ROOT)) { |
| 302 return _createHttpRequestTask(spec, null); |
| 303 } |
| 304 return Zone.current.createTask(_createHttpRequestTask, spec); |
| 305 } |
| 306 |
| 307 static Future<HttpRequest> _createHttpRequestTask( |
| 308 HttpRequestTaskSpecification spec, Zone zone) { |
| 309 String url = spec.url; |
| 310 String method = spec.method; |
| 311 bool withCredentials = spec.withCredentials; |
| 312 String responseType = spec.responseType; |
| 313 String mimeType = spec.mimeType; |
| 314 Map<String, String> requestHeaders = spec.requestHeaders; |
| 315 var sendData = spec.sendData; |
| 316 var onProgress = spec.onProgress; |
| 317 |
| 193 var completer = new Completer<HttpRequest>(); | 318 var completer = new Completer<HttpRequest>(); |
| 319 var task = completer.future; |
| 194 | 320 |
| 195 var xhr = new HttpRequest(); | 321 var xhr = new HttpRequest(); |
| 196 if (method == null) { | 322 if (method == null) { |
| 197 method = 'GET'; | 323 method = 'GET'; |
| 198 } | 324 } |
| 199 xhr.open(method, url, async: true); | 325 xhr.open(method, url, async: true); |
| 200 | 326 |
| 201 if (withCredentials != null) { | 327 if (withCredentials != null) { |
| 202 xhr.withCredentials = withCredentials; | 328 xhr.withCredentials = withCredentials; |
| 203 } | 329 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 223 xhr.onLoad.listen((e) { | 349 xhr.onLoad.listen((e) { |
| 224 var accepted = xhr.status >= 200 && xhr.status < 300; | 350 var accepted = xhr.status >= 200 && xhr.status < 300; |
| 225 var fileUri = xhr.status == 0; // file:// URIs have status of 0. | 351 var fileUri = xhr.status == 0; // file:// URIs have status of 0. |
| 226 var notModified = xhr.status == 304; | 352 var notModified = xhr.status == 304; |
| 227 // Redirect status is specified up to 307, but others have been used in | 353 // Redirect status is specified up to 307, but others have been used in |
| 228 // practice. Notably Google Drive uses 308 Resume Incomplete for | 354 // practice. Notably Google Drive uses 308 Resume Incomplete for |
| 229 // resumable uploads, and it's also been used as a redirect. The | 355 // resumable uploads, and it's also been used as a redirect. The |
| 230 // redirect case will be handled by the browser before it gets to us, | 356 // redirect case will be handled by the browser before it gets to us, |
| 231 // so if we see it we should pass it through to the user. | 357 // so if we see it we should pass it through to the user. |
| 232 var unknownRedirect = xhr.status > 307 && xhr.status < 400; | 358 var unknownRedirect = xhr.status > 307 && xhr.status < 400; |
| 233 | 359 |
| 234 if (accepted || fileUri || notModified || unknownRedirect) { | 360 var isSuccessful = accepted || fileUri || notModified || unknownRedirect; |
| 361 |
| 362 if (zone == null && isSuccessful) { |
| 235 completer.complete(xhr); | 363 completer.complete(xhr); |
| 364 } else if (zone == null) { |
| 365 completer.completeError(e); |
| 366 } else if (isSuccessful) { |
| 367 zone.runTask((task, value) { |
| 368 completer.complete(value); |
| 369 }, task, xhr); |
| 236 } else { | 370 } else { |
| 237 completer.completeError(e); | 371 zone.runTask((task, error) { |
| 372 completer.completeError(error); |
| 373 }, task, e); |
| 238 } | 374 } |
| 239 }); | 375 }); |
| 240 | 376 |
| 241 xhr.onError.listen(completer.completeError); | 377 if (zone == null) { |
| 378 xhr.onError.listen(completer.completeError); |
| 379 } else { |
| 380 xhr.onError.listen((error) { |
| 381 zone.runTask((task, error) { |
| 382 completer.completeError(error); |
| 383 }, task, error); |
| 384 }); |
| 385 } |
| 242 | 386 |
| 243 if (sendData != null) { | 387 if (sendData != null) { |
| 244 xhr.send(sendData); | 388 // TODO(floitsch): should we go through 'send()' and have nested tasks? |
| 389 xhr._send(sendData); |
| 245 } else { | 390 } else { |
| 246 xhr.send(); | 391 xhr._send(); |
| 247 } | 392 } |
| 248 | 393 |
| 249 return completer.future; | 394 return task; |
| 250 } | 395 } |
| 251 | 396 |
| 252 /** | 397 /** |
| 253 * Checks to see if the Progress event is supported on the current platform. | 398 * Checks to see if the Progress event is supported on the current platform. |
| 254 */ | 399 */ |
| 255 static bool get supportsProgressEvent { | 400 static bool get supportsProgressEvent { |
| 256 $if DART2JS | 401 $if DART2JS |
| 257 var xhr = new HttpRequest(); | 402 var xhr = new HttpRequest(); |
| 258 return JS('bool', '("onprogress" in #)', xhr); | 403 return JS('bool', '("onprogress" in #)', xhr); |
| 259 $else | 404 $else |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 * cross-origin support is not required then [request] should be used instead. | 454 * cross-origin support is not required then [request] should be used instead. |
| 310 */ | 455 */ |
| 311 @Experimental() | 456 @Experimental() |
| 312 static Future<String> requestCrossOrigin(String url, | 457 static Future<String> requestCrossOrigin(String url, |
| 313 {String method, String sendData}) { | 458 {String method, String sendData}) { |
| 314 if (supportsCrossOrigin) { | 459 if (supportsCrossOrigin) { |
| 315 return request(url, method: method, sendData: sendData).then((xhr) { | 460 return request(url, method: method, sendData: sendData).then((xhr) { |
| 316 return xhr.responseText; | 461 return xhr.responseText; |
| 317 }); | 462 }); |
| 318 } | 463 } |
| 464 // TODO(floitsch): the following code doesn't go through task zones. |
| 465 // Since 'XDomainRequest' is an IE9 feature we should probably just remove |
| 466 // it. |
| 319 $if DART2JS | 467 $if DART2JS |
| 320 var completer = new Completer<String>(); | 468 var completer = new Completer<String>(); |
| 321 if (method == null) { | 469 if (method == null) { |
| 322 method = 'GET'; | 470 method = 'GET'; |
| 323 } | 471 } |
| 324 var xhr = JS('var', 'new XDomainRequest()'); | 472 var xhr = JS('var', 'new XDomainRequest()'); |
| 325 JS('', '#.open(#, #)', xhr, method, url); | 473 JS('', '#.open(#, #)', xhr, method, url); |
| 326 JS('', '#.onload = #', xhr, convertDartClosureToJS((e) { | 474 JS('', '#.onload = #', xhr, convertDartClosureToJS((e) { |
| 327 var response = JS('String', '#.responseText', xhr); | 475 var response = JS('String', '#.responseText', xhr); |
| 328 completer.complete(response); | 476 completer.complete(response); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 * | 537 * |
| 390 * By default the request is done asyncronously, with no user or password | 538 * By default the request is done asyncronously, with no user or password |
| 391 * authentication information. If `async` is false, the request will be send | 539 * authentication information. If `async` is false, the request will be send |
| 392 * synchronously. | 540 * synchronously. |
| 393 * | 541 * |
| 394 * Calling `open` again on a currently active request is equivalent to | 542 * Calling `open` again on a currently active request is equivalent to |
| 395 * calling `abort`. | 543 * calling `abort`. |
| 396 * | 544 * |
| 397 * Note: Most simple HTTP requests can be accomplished using the [getString], | 545 * Note: Most simple HTTP requests can be accomplished using the [getString], |
| 398 * [request], [requestCrossOrigin], or [postFormData] methods. Use of this | 546 * [request], [requestCrossOrigin], or [postFormData] methods. Use of this |
| 399 * `open` method is intended only for more complext HTTP requests where | 547 * `open` method is intended only for more complex HTTP requests where |
| 400 * finer-grained control is needed. | 548 * finer-grained control is needed. |
| 401 */ | 549 */ |
| 402 @DomName('XMLHttpRequest.open') | 550 @DomName('XMLHttpRequest.open') |
| 403 @DocsEditable() | 551 @DocsEditable() |
| 404 $if JSINTEROP | 552 $if JSINTEROP |
| 405 void open(String method, String url, {bool async, String user, String password
}) { | 553 void open(String method, String url, {bool async, String user, String password
}) { |
| 406 if (async == null && user == null && password == null) { | 554 if (async == null && user == null && password == null) { |
| 407 _blink.BlinkXMLHttpRequest.instance.open_Callback_2_(this, method, url); | 555 _blink.BlinkXMLHttpRequest.instance.open_Callback_2_(this, method, url); |
| 408 } else { | 556 } else { |
| 409 _blink.BlinkXMLHttpRequest.instance.open_Callback_5_(this, method, url, as
ync, user, password); | 557 _blink.BlinkXMLHttpRequest.instance.open_Callback_5_(this, method, url, as
ync, user, password); |
| 410 } | 558 } |
| 411 } | 559 } |
| 412 $else | 560 $else |
| 413 void open(String method, String url, {bool async, String user, String password
}) native; | 561 void open(String method, String url, {bool async, String user, String password
}) native; |
| 414 $endif | 562 $endif |
| 415 | 563 |
| 564 /** |
| 565 * Sends the request with any given `data`. |
| 566 * |
| 567 * Note: Most simple HTTP requests can be accomplished using the [getString], |
| 568 * [request], [requestCrossOrigin], or [postFormData] methods. Use of this |
| 569 * `send` method is intended only for more complex HTTP requests where |
| 570 * finer-grained control is needed. |
| 571 * |
| 572 * ## Other resources |
| 573 * |
| 574 * * [XMLHttpRequest.send](https://developer.mozilla.org/en-US/docs/DOM/XMLHtt
pRequest#send%28%29) |
| 575 * from MDN. |
| 576 */ |
| 577 @DomName('XMLHttpRequest.send') |
| 578 @DocsEditable() |
| 579 void send([body_OR_data]) { |
| 580 if (identical(Zone.current, Zone.ROOT)) { |
| 581 _send(body_OR_data); |
| 582 } else { |
| 583 Zone.current.createTask(_createHttpRequestSendTask, |
| 584 new HttpRequestSendTaskSpecification(this, body_OR_data)); |
| 585 } |
| 586 } |
| 587 |
| 588 static HttpRequest _createHttpRequestSendTask( |
| 589 HttpRequestSendTaskSpecification spec, Zone zone) { |
| 590 spec.request._send(spec.sendData); |
| 591 return spec.request; |
| 592 } |
| 593 |
| 416 $!MEMBERS | 594 $!MEMBERS |
| 417 } | 595 } |
| OLD | NEW |