Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(290)

Side by Side Diff: pkg/http/lib/src/utils.dart

Issue 11825010: Update pkg/http to use the new async APIs. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 library utils; 5 library utils;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:crypto'; 8 import 'dart:crypto';
9 import 'dart:io'; 9 import 'dart:io';
10 import 'dart:scalarlist'; 10 import 'dart:scalarlist';
11 import 'dart:uri'; 11 import 'dart:uri';
12 import 'dart:utf'; 12 import 'dart:utf';
13 13
14 import 'byte_stream.dart';
15
14 /// Converts a URL query string (or `application/x-www-form-urlencoded` body) 16 /// Converts a URL query string (or `application/x-www-form-urlencoded` body)
15 /// into a [Map] from parameter names to values. 17 /// into a [Map] from parameter names to values.
16 /// 18 ///
17 /// queryToMap("foo=bar&baz=bang&qux"); 19 /// queryToMap("foo=bar&baz=bang&qux");
18 /// //=> {"foo": "bar", "baz": "bang", "qux": ""} 20 /// //=> {"foo": "bar", "baz": "bang", "qux": ""}
19 Map<String, String> queryToMap(String queryList) { 21 Map<String, String> queryToMap(String queryList) {
20 var map = <String>{}; 22 var map = <String>{};
21 for (var pair in queryList.split("&")) { 23 for (var pair in queryList.split("&")) {
22 var split = split1(pair, "="); 24 var split = split1(pair, "=");
23 if (split.isEmpty) continue; 25 if (split.isEmpty) continue;
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 /// [ByteArrayViewable], this just returns a view on [input]. 126 /// [ByteArrayViewable], this just returns a view on [input].
125 Uint8List toUint8List(List<int> input) { 127 Uint8List toUint8List(List<int> input) {
126 if (input is Uint8List) return input; 128 if (input is Uint8List) return input;
127 if (input is ByteArrayViewable) input = input.asByteArray(); 129 if (input is ByteArrayViewable) input = input.asByteArray();
128 if (input is ByteArray) return new Uint8List.view(input); 130 if (input is ByteArray) return new Uint8List.view(input);
129 var output = new Uint8List(input.length); 131 var output = new Uint8List(input.length);
130 output.setRange(0, input.length, input); 132 output.setRange(0, input.length, input);
131 return output; 133 return output;
132 } 134 }
133 135
134 /// Buffers all input from an InputStream and returns it as a future. 136 /// If [stream] is already a [ByteStream], returns it. Otherwise, wraps it in a
135 Future<List<int>> consumeInputStream(InputStream stream) { 137 /// [ByteStream].
Bob Nystrom 2013/01/08 23:50:49 How about making this a (potentially factory) cons
nweiz 2013/01/09 00:52:11 It feels too similar to the default ByteStream con
136 if (stream.closed) return new Future<List<int>>.immediate(<int>[]); 138 ByteStream toByteStream(Stream<List<int>> stream) {
139 if (stream is ByteStream) return stream;
140 return new ByteStream(stream);
141 }
137 142
138 var completer = new Completer<List<int>>(); 143 /// Calls [onDone] once [stream] (a single-subscription [Stream]) is finished.
139 /// TODO(nweiz): use BufferList when issue 6409 is fixed 144 /// The return value, also a single-subscription [Stream] should be used in
140 var buffer = <int>[]; 145 /// place of [stream] after calling this method.
141 stream.onClosed = () => completer.complete(buffer); 146 Stream onDone(Stream stream, void onDone()) {
142 stream.onData = () => buffer.addAll(stream.read()); 147 var pair = tee(stream);
143 stream.onError = completer.completeError; 148 pair.first.listen((_) {}, onError: (_) {}, onDone: onDone);
149 return pair.last;
150 }
151
152 /// Wraps [stream] in a single-subscription [ByteStream] that emits the same
153 /// data.
Bob Nystrom 2013/01/08 23:50:49 Is this just for compatibility with the existing d
nweiz 2013/01/09 00:52:11 Done.
154 ByteStream wrapInputStream(InputStream stream) {
155 if (stream.closed) return emptyStream;
156
157 var controller = new StreamController.singleSubscription();
158 stream.onClosed = controller.close;
159 stream.onData = () => controller.add(stream.read());
160 stream.onError = (e) => controller.signalError(new AsyncError(e));
Bob Nystrom 2013/01/08 23:50:49 Having to manually create the AsyncError here is l
nweiz 2013/01/09 00:52:11 I'm not sure what a better API would be.
161 return new ByteStream(controller);
162 }
163
164 /// Wraps [stream] in a [StreamConsumer] so that [Stream]s can by piped into it
165 /// using [Stream.pipe].
166 StreamConsumer<List<int>, dynamic> wrapOutputStream(OutputStream stream) =>
167 new _OutputStreamConsumer(stream);
168
169 /// A [StreamConsumer] that pipes data into an [OutputStream].
170 class _OutputStreamConsumer implements StreamConsumer<List<int>, dynamic> {
171 final OutputStream _outputStream;
172
173 _OutputStreamConsumer(this._outputStream)
174 : super();
175
176 Future consume(Stream<List<int>> stream) {
177 // TODO(nweiz): we have to manually keep track of whether or not the
178 // completer has completed since the output stream could signal an error
179 // after close() has been called but before it has shut down internally. See
180 // the following TODO.
181 var completed = false;
182 var completer = new Completer();
183 stream.listen((data) => _outputStream.write(data), onDone: () {
184 _outputStream.close();
185 // TODO(nweiz): wait until _outputStream.onClosed is called once issue
186 // 7761 is fixed.
187 if (!completed) completer.complete(null);
188 completed = true;
189 });
190
191 _outputStream.onError = (e) {
192 if (!completed) completer.completeError(e);
193 completed = true;
194 };
195
196 return completer.future;
197 }
198 }
199
200 // TODO(nweiz): remove this when it's added to the Stream API.
Bob Nystrom 2013/01/08 23:50:49 Is there a bug# for this?
nweiz 2013/01/09 00:52:11 Filed one.
201
202 /// Pipes all data and errors from [stream] into [sink]. When [stream] is done,
203 /// [sink] is closed and the returned [Future] is completed.
204 Future store(Stream stream, StreamSink sink) {
205 var completer = new Completer();
206 stream.listen(sink.add,
207 onError: sink.signalError,
208 onDone: () {
209 sink.close();
210 completer.complete(null);
Bob Nystrom 2013/01/08 23:50:49 The arg is optional here now. Just do: completer.
nweiz 2013/01/09 00:52:11 Done.
211 });
144 return completer.future; 212 return completer.future;
145 } 213 }
146 214
147 /// Takes all input from [source] and writes it to [sink], then closes [sink]. 215 /// Pipes all data and errors from [stream] into [sink]. Completes [Future] once
148 /// Returns a [Future] that completes when [source] is exhausted. 216 /// [stream] is done. Unlike [store], [sink] remains open after [stream] is
149 void pipeInputToInput(InputStream source, ListInputStream sink) { 217 /// done.
150 source.onClosed = sink.markEndOfStream; 218 Future writeStreamToSink(Stream stream, StreamSink sink) {
151 source.onData = () => sink.write(source.read());
152 // TODO(nweiz): propagate source errors to the sink. See issue 3657.
153 // TODO(nweiz): we need to use async here to avoid issue 4974.
154 source.onError = (e) => async.then((_) {
155 throw e;
156 });
157 }
158
159 /// Takes all input from [source] and writes it to [sink], but does not close
160 /// [sink] when [source] is closed. Returns a [Future] that completes when
161 /// [source] is closed.
162 Future writeInputToInput(InputStream source, ListInputStream sink) {
163 var completer = new Completer(); 219 var completer = new Completer();
164 source.onClosed = () => completer.complete(null); 220 stream.listen(sink.add,
165 source.onData = () => sink.write(source.read()); 221 onError: sink.signalError,
166 // TODO(nweiz): propagate source errors to the sink. See issue 3657. 222 onDone: () => completer.complete(null));
Bob Nystrom 2013/01/08 23:50:49 complete()
nweiz 2013/01/09 00:52:11 Done.
167 return completer.future; 223 return completer.future;
168 } 224 }
169 225
170 /// Returns a [Future] that asynchronously completes to `null`. 226 /// Returns a [Future] that asynchronously completes to `null`.
171 Future get async { 227 Future get async => new Future.immediate(null);
172 var completer = new Completer(); 228
173 new Timer(0, (_) => completer.complete(null)); 229 /// Returns a closed [Stream] with no elements.
174 return completer.future; 230 Stream get emptyStream => streamFromIterable([]);
231
232 /// Creates a single-subscription stream that emits the items in [iter] and then
233 /// ends.
234 Stream streamFromIterable(Iterable iter) {
235 var stream = new StreamController.singleSubscription();
236 iter.forEach(stream.add);
237 stream.close();
238 return stream.stream;
239 }
240
241 /// Creates two single-subscription [Stream]s that each emit all values and
242 /// errors from [stream]. This is useful if [stream] is single-subscription but
243 /// multiple subscribers are necessary.
244 Pair<Stream, Stream> tee(Stream stream) {
Bob Nystrom 2013/01/08 23:50:49 If single-subscriber streams are going to stick ar
nweiz 2013/01/09 00:52:11 Done.
245 var controller1 = new StreamController.singleSubscription();
246 var controller2 = new StreamController.singleSubscription();
247 stream.listen((value) {
248 controller1.add(value);
249 controller2.add(value);
250 }, onError: (error) {
251 controller1.signalError(error);
252 controller2.signalError(error);
253 }, onDone: () {
254 controller1.close();
255 controller2.close();
256 });
257 return new Pair<Stream, Stream>(controller1.stream, controller2.stream);
258 }
259
260 /// A pair of values.
261 class Pair<E, F> {
262 E first;
263 F last;
264
265 Pair(this.first, this.last);
266
267 String toString() => '($first, $last)';
268
269 bool operator==(other) {
270 if (other is! Pair) return false;
271 return other.first == first && other.last == last;
272 }
273
274 int get hashCode => first.hashCode ^ last.hashCode;
275 }
276
277 /// Configures [future] so that its result (success or exception) is passed on
278 /// to [completer].
279 void chainToCompleter(Future future, Completer completer) {
280 future.then((v) => completer.complete(v)).catchError((e) {
281 completer.completeError(e.error, e.stackTrace);
282 });
Bob Nystrom 2013/01/08 23:50:49 Use onError: future.then((v) => completer.complet
nweiz 2013/01/09 00:52:11 Why? The documentation says (and I agree) that cat
Bob Nystrom 2013/01/09 03:00:43 <shrug> I figured one method call with two args w
nweiz 2013/01/09 03:07:40 I vote chaining.
175 } 283 }
176 284
177 // TOOD(nweiz): Get rid of this once https://codereview.chromium.org/11293132/ 285 // TOOD(nweiz): Get rid of this once https://codereview.chromium.org/11293132/
178 // is in. 286 // is in.
179 /// Runs [fn] for each element in [input] in order, moving to the next element 287 /// Runs [fn] for each element in [input] in order, moving to the next element
180 /// only when the [Future] returned by [fn] completes. Returns a [Future] that 288 /// only when the [Future] returned by [fn] completes. Returns a [Future] that
181 /// completes when all elements have been processed. 289 /// completes when all elements have been processed.
182 /// 290 ///
183 /// The return values of all [Future]s are discarded. Any errors will cause the 291 /// The return values of all [Future]s are discarded. Any errors will cause the
184 /// iteration to stop and will be piped through the return value. 292 /// iteration to stop and will be piped through the return value.
185 Future forEachFuture(Iterable input, Future fn(element)) { 293 Future forEachFuture(Iterable input, Future fn(element)) {
186 var iterator = input.iterator; 294 var iterator = input.iterator;
187 Future nextElement(_) { 295 Future nextElement(_) {
188 if (!iterator.moveNext()) return new Future.immediate(null); 296 if (!iterator.moveNext()) return new Future.immediate(null);
189 return fn(iterator.current).then(nextElement); 297 return fn(iterator.current).then(nextElement);
190 } 298 }
191 return nextElement(null); 299 return nextElement(null);
192 } 300 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698