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. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
http -> HTTP
(lots more below).
floitsch
2016/07/02 01:53:55
Done.
| |
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>`. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
by -> from.
It's created by the `create` function
floitsch
2016/07/02 01:53:55
Done.
| |
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 method used to do the request. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
"method" here needs to be put in context. Maybe "r
floitsch
2016/07/02 01:53:55
changed to '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"`), | |
Lasse Reichstein Nielsen
2016/07/01 12:41:30
It seems a little odd to have `""` be equal to `"t
floitsch
2016/07/02 01:53:55
not my choice. This is just documentation copied f
| |
37 /// - `"arraybuffer"`, | |
38 /// - `"blob"`, | |
39 /// - `"document"`, | |
40 /// - `"json"`, | |
41 /// - `"text"` | |
42 /// | |
43 /// By default (when `null`) the desired format is `""`. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:30
This says that if the desired response format is n
floitsch
2016/07/02 01:53:55
Changed to: "When no value is provided (when equal
| |
44 final String responseType; | |
45 | |
46 /// The desired mime type. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
mime -> MIME
I think "media type" or "content typ
floitsch
2016/07/02 01:53:55
I'm just copying/documenting the values from the `
| |
47 /// | |
48 /// This overrides the default mime-type which is set up to transfer textual | |
Lasse Reichstein Nielsen
2016/07/01 12:41:30
which is set up to transfer textual data -> which
Lasse Reichstein Nielsen
2016/07/01 12:41:30
no dash in "MIME type".
floitsch
2016/07/02 01:53:55
I even went into the spec, but couldn't find the M
floitsch
2016/07/02 01:53:55
Done.
| |
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 provided (not `null`) must be a [ByteBuffer], | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
When data is provided (the value is not `null`), i
floitsch
2016/07/02 01:53:55
Done.
| |
58 /// [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. | |
Lasse Reichstein Nielsen
2016/07/01 12:41:29
Doesn't that make it a non-task? If a task is some
floitsch
2016/07/02 01:53:55
Somehow yes.
I added it:
- because it makes http-
| |
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 | |
7 /** | 110 /** |
8 * A client-side XHR request for getting data from a URL, | 111 * A client-side XHR request for getting data from a URL, |
9 * formally known as XMLHttpRequest. | 112 * formally known as XMLHttpRequest. |
10 * | 113 * |
11 * HttpRequest can be used to obtain data from HTTP and FTP protocols, | 114 * HttpRequest can be used to obtain data from HTTP and FTP protocols, |
12 * and is useful for AJAX-style page updates. | 115 * and is useful for AJAX-style page updates. |
13 * | 116 * |
14 * The simplest way to get the contents of a text file, such as a | 117 * The simplest way to get the contents of a text file, such as a |
15 * JSON-formatted file, is with [getString]. | 118 * JSON-formatted file, is with [getString]. |
16 * For example, the following code gets the contents of a JSON file | 119 * 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 | 286 * with appropriate permissions in their manifest. Requests to file:// URIs |
184 * will also never fail- the Future will always complete successfully, even | 287 * will also never fail- the Future will always complete successfully, even |
185 * when the file cannot be found. | 288 * when the file cannot be found. |
186 * | 289 * |
187 * See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access _authentication). | 290 * See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access _authentication). |
188 */ | 291 */ |
189 static Future<HttpRequest> request(String url, | 292 static Future<HttpRequest> request(String url, |
190 {String method, bool withCredentials, String responseType, | 293 {String method, bool withCredentials, String responseType, |
191 String mimeType, Map<String, String> requestHeaders, sendData, | 294 String mimeType, Map<String, String> requestHeaders, sendData, |
192 void onProgress(ProgressEvent e)}) { | 295 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 | |
193 var completer = new Completer<HttpRequest>(); | 322 var completer = new Completer<HttpRequest>(); |
323 var task = completer.future; | |
194 | 324 |
195 var xhr = new HttpRequest(); | 325 var xhr = new HttpRequest(); |
196 if (method == null) { | 326 if (method == null) { |
197 method = 'GET'; | 327 method = 'GET'; |
198 } | 328 } |
199 xhr.open(method, url, async: true); | 329 xhr.open(method, url, async: true); |
200 | 330 |
201 if (withCredentials != null) { | 331 if (withCredentials != null) { |
202 xhr.withCredentials = withCredentials; | 332 xhr.withCredentials = withCredentials; |
203 } | 333 } |
(...skipping 19 matching lines...) Expand all Loading... | |
223 xhr.onLoad.listen((e) { | 353 xhr.onLoad.listen((e) { |
224 var accepted = xhr.status >= 200 && xhr.status < 300; | 354 var accepted = xhr.status >= 200 && xhr.status < 300; |
225 var fileUri = xhr.status == 0; // file:// URIs have status of 0. | 355 var fileUri = xhr.status == 0; // file:// URIs have status of 0. |
226 var notModified = xhr.status == 304; | 356 var notModified = xhr.status == 304; |
227 // Redirect status is specified up to 307, but others have been used in | 357 // Redirect status is specified up to 307, but others have been used in |
228 // practice. Notably Google Drive uses 308 Resume Incomplete for | 358 // practice. Notably Google Drive uses 308 Resume Incomplete for |
229 // resumable uploads, and it's also been used as a redirect. The | 359 // 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, | 360 // 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. | 361 // so if we see it we should pass it through to the user. |
232 var unknownRedirect = xhr.status > 307 && xhr.status < 400; | 362 var unknownRedirect = xhr.status > 307 && xhr.status < 400; |
233 | 363 |
234 if (accepted || fileUri || notModified || unknownRedirect) { | 364 var isSuccessful = accepted || fileUri || notModified || unknownRedirect; |
365 | |
366 if (zone == null && isSuccessful) { | |
235 completer.complete(xhr); | 367 completer.complete(xhr); |
368 } else if (zone == null) { | |
369 completer.completeError(e); | |
370 } else if (isSuccessful) { | |
371 zone.runTask((task, value) { | |
372 completer.complete(value); | |
373 }, task, xhr); | |
236 } else { | 374 } else { |
237 completer.completeError(e); | 375 zone.runTask((task, error) { |
376 completer.completeError(error); | |
377 }, task, e); | |
238 } | 378 } |
239 }); | 379 }); |
240 | 380 |
241 xhr.onError.listen(completer.completeError); | 381 if (zone == null) { |
382 xhr.onError.listen(completer.completeError); | |
383 } else { | |
384 xhr.onError.listen((error) { | |
385 zone.runTask((task, error) { | |
386 completer.completeError(error); | |
387 }, task, error); | |
388 }); | |
389 } | |
242 | 390 |
243 if (sendData != null) { | 391 if (sendData != null) { |
244 xhr.send(sendData); | 392 // TODO(floitsch): should we go through 'send()' and have nested tasks? |
393 xhr._send(sendData); | |
245 } else { | 394 } else { |
246 xhr.send(); | 395 xhr._send(); |
247 } | 396 } |
248 | 397 |
249 return completer.future; | 398 return task; |
250 } | 399 } |
251 | 400 |
252 /** | 401 /** |
253 * Checks to see if the Progress event is supported on the current platform. | 402 * Checks to see if the Progress event is supported on the current platform. |
254 */ | 403 */ |
255 static bool get supportsProgressEvent { | 404 static bool get supportsProgressEvent { |
256 $if DART2JS | 405 $if DART2JS |
257 var xhr = new HttpRequest(); | 406 var xhr = new HttpRequest(); |
258 return JS('bool', '("onprogress" in #)', xhr); | 407 return JS('bool', '("onprogress" in #)', xhr); |
259 $else | 408 $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. | 458 * cross-origin support is not required then [request] should be used instead. |
310 */ | 459 */ |
311 @Experimental() | 460 @Experimental() |
312 static Future<String> requestCrossOrigin(String url, | 461 static Future<String> requestCrossOrigin(String url, |
313 {String method, String sendData}) { | 462 {String method, String sendData}) { |
314 if (supportsCrossOrigin) { | 463 if (supportsCrossOrigin) { |
315 return request(url, method: method, sendData: sendData).then((xhr) { | 464 return request(url, method: method, sendData: sendData).then((xhr) { |
316 return xhr.responseText; | 465 return xhr.responseText; |
317 }); | 466 }); |
318 } | 467 } |
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. | |
319 $if DART2JS | 471 $if DART2JS |
320 var completer = new Completer<String>(); | 472 var completer = new Completer<String>(); |
321 if (method == null) { | 473 if (method == null) { |
322 method = 'GET'; | 474 method = 'GET'; |
323 } | 475 } |
324 var xhr = JS('var', 'new XDomainRequest()'); | 476 var xhr = JS('var', 'new XDomainRequest()'); |
325 JS('', '#.open(#, #)', xhr, method, url); | 477 JS('', '#.open(#, #)', xhr, method, url); |
326 JS('', '#.onload = #', xhr, convertDartClosureToJS((e) { | 478 JS('', '#.onload = #', xhr, convertDartClosureToJS((e) { |
327 var response = JS('String', '#.responseText', xhr); | 479 var response = JS('String', '#.responseText', xhr); |
328 completer.complete(response); | 480 completer.complete(response); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
389 * | 541 * |
390 * By default the request is done asyncronously, with no user or password | 542 * By default the request is done asyncronously, with no user or password |
391 * authentication information. If `async` is false, the request will be send | 543 * authentication information. If `async` is false, the request will be send |
392 * synchronously. | 544 * synchronously. |
393 * | 545 * |
394 * Calling `open` again on a currently active request is equivalent to | 546 * Calling `open` again on a currently active request is equivalent to |
395 * calling `abort`. | 547 * calling `abort`. |
396 * | 548 * |
397 * Note: Most simple HTTP requests can be accomplished using the [getString], | 549 * Note: Most simple HTTP requests can be accomplished using the [getString], |
398 * [request], [requestCrossOrigin], or [postFormData] methods. Use of this | 550 * [request], [requestCrossOrigin], or [postFormData] methods. Use of this |
399 * `open` method is intended only for more complext HTTP requests where | 551 * `open` method is intended only for more complex HTTP requests where |
400 * finer-grained control is needed. | 552 * finer-grained control is needed. |
401 */ | 553 */ |
402 @DomName('XMLHttpRequest.open') | 554 @DomName('XMLHttpRequest.open') |
403 @DocsEditable() | 555 @DocsEditable() |
404 $if JSINTEROP | 556 $if JSINTEROP |
405 void open(String method, String url, {bool async, String user, String password }) { | 557 void open(String method, String url, {bool async, String user, String password }) { |
406 if (async == null && user == null && password == null) { | 558 if (async == null && user == null && password == null) { |
407 _blink.BlinkXMLHttpRequest.instance.open_Callback_2_(this, method, url); | 559 _blink.BlinkXMLHttpRequest.instance.open_Callback_2_(this, method, url); |
408 } else { | 560 } else { |
409 _blink.BlinkXMLHttpRequest.instance.open_Callback_5_(this, method, url, as ync, user, password); | 561 _blink.BlinkXMLHttpRequest.instance.open_Callback_5_(this, method, url, as ync, user, password); |
410 } | 562 } |
411 } | 563 } |
412 $else | 564 $else |
413 void open(String method, String url, {bool async, String user, String password }) native; | 565 void open(String method, String url, {bool async, String user, String password }) native; |
414 $endif | 566 $endif |
415 | 567 |
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 | |
416 $!MEMBERS | 598 $!MEMBERS |
417 } | 599 } |
OLD | NEW |