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