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

Side by Side Diff: pkg/appengine/lib/src/server/http_wrapper.dart

Issue 804973002: Add appengine/gcloud/mustache dependencies. (Closed) Base URL: git@github.com:dart-lang/pub-dartlang-dart.git@master
Patch Set: Added AUTHORS/LICENSE/PATENTS files Created 6 years 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
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
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.
4
5 library http_wrapper;
6
7 import 'dart:async';
8 import 'dart:io';
9 import 'dart:convert';
10
11 class AppengineHttpRequest implements HttpRequest {
12 final HttpRequest _realRequest;
13 AppengineHttpResponse _response;
14 bool _hasSubscriber = false;
15
16 AppengineHttpRequest(this._realRequest) {
17 _response = new AppengineHttpResponse(this, _realRequest.response);
18 }
19
20 AppengineHttpResponse get response => _response;
21
22 Future<bool> any(bool test(List<int> element)) {
23 _hasSubscriber = true;
24 return _realRequest.any(test);
25 }
26
27 Stream<List<int>> asBroadcastStream(
28 {void onListen(StreamSubscription<List<int>> subscription),
29 void onCancel(StreamSubscription<List<int>> subscription)}) {
30 _hasSubscriber = true;
31 return _realRequest.asBroadcastStream(onListen: onListen,
32 onCancel: onCancel);
33 }
34
35 Stream asyncExpand(Stream convert(List<int> event)) {
36 _hasSubscriber = true;
37 return _realRequest.asyncExpand(convert);
38 }
39
40 Stream asyncMap(convert(List<int> event)) {
41 _hasSubscriber = true;
42 return _realRequest.asyncMap(convert);
43 }
44
45 Future<bool> contains(Object needle) {
46 _hasSubscriber = true;
47 return _realRequest.contains(needle);
48 }
49
50 Stream<List<int>> distinct(
51 [bool equals(List<int> previous, List<int> next)]) {
52 _hasSubscriber = true;
53 return _realRequest.distinct(equals);
54 }
55
56 Future drain([futureValue]) {
57 _hasSubscriber = true;
58 return _realRequest.drain(futureValue);
59 }
60
61 Future<List<int>> elementAt(int index) {
62 _hasSubscriber = true;
63 return _realRequest.elementAt(index);
64 }
65
66 Future<bool> every(bool test(List<int> element)) {
67 _hasSubscriber = true;
68 return _realRequest.every(test);
69 }
70
71 Stream expand(Iterable convert(List<int> value)) {
72 _hasSubscriber = true;
73 return _realRequest.expand(convert);
74 }
75
76 Future<List<int>> get first {
77 _hasSubscriber = true;
78 return _realRequest.first;
79 }
80
81 Future firstWhere(bool test(List<int> element), {Object defaultValue()}) {
82 _hasSubscriber = true;
83 return _realRequest.firstWhere(test, defaultValue: defaultValue);
84 }
85
86 Future fold(initialValue, combine(previous, List<int> element)) {
87 _hasSubscriber = true;
88 return _realRequest.fold(initialValue, combine);
89 }
90
91 Future forEach(void action(List<int> element)) {
92 _hasSubscriber = true;
93 return _realRequest.forEach(action);
94 }
95
96 Stream<List<int>> handleError(Function onError, {bool test(error)}) {
97 _hasSubscriber = true;
98 return _realRequest.handleError(onError, test: test);
99 }
100
101 Future<bool> get isEmpty {
102 _hasSubscriber = true;
103 return _realRequest.isEmpty;
104 }
105
106 Future<String> join([String separator = ""]) {
107 _hasSubscriber = true;
108 return _realRequest.join(separator);
109 }
110
111 Future<List<int>> get last {
112 _hasSubscriber = true;
113 return _realRequest.last;
114 }
115
116 Future lastWhere(bool test(List<int> element), {Object defaultValue()}) {
117 _hasSubscriber = true;
118 return _realRequest.lastWhere(test, defaultValue: defaultValue);
119 }
120
121 Future<int> get length {
122 _hasSubscriber = true;
123 return _realRequest.length;
124 }
125
126 StreamSubscription<List<int>> listen(void onData(List<int> event),
127 {Function onError, void onDone(), bool cancelOnError}) {
128 _hasSubscriber = true;
129 return _realRequest.listen(onData,
130 onError: onError,
131 onDone: onDone,
132 cancelOnError: cancelOnError);
133 }
134
135 Stream map(convert(List<int> event)) {
136 _hasSubscriber = true;
137 return _realRequest.map(convert);
138 }
139
140 Future<List<int>> get single {
141 _hasSubscriber = true;
142 return _realRequest.single;
143 }
144
145 Future<List<int>> singleWhere(bool test(List<int> element)) {
146 _hasSubscriber = true;
147 return _realRequest.singleWhere(test);
148 }
149
150 Stream<List<int>> skip(int count) {
151 _hasSubscriber = true;
152 return _realRequest.skip(count);
153 }
154
155 Stream<List<int>> skipWhile(bool test(List<int> element)) {
156 _hasSubscriber = true;
157 return _realRequest.skipWhile(test);
158 }
159
160 Stream<List<int>> take(int count) {
161 _hasSubscriber = true;
162 return _realRequest.take(count);
163 }
164
165 Stream<List<int>> takeWhile(bool test(List<int> element)) {
166 _hasSubscriber = true;
167 return _realRequest.takeWhile(test);
168 }
169
170 Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) {
171 _hasSubscriber = true;
172 return _realRequest.timeout(timeLimit, onTimeout: onTimeout);
173 }
174
175 Future<List<List<int>>> toList() {
176 _hasSubscriber = true;
177 return _realRequest.toList();
178 }
179
180 Future<Set<List<int>>> toSet() {
181 _hasSubscriber = true;
182 return _realRequest.toSet();
183 }
184
185 Stream transform(StreamTransformer<List<int>, dynamic> streamTransformer) {
186 _hasSubscriber = true;
187 return _realRequest.transform(streamTransformer);
188 }
189
190 Stream<List<int>> where(bool test(List<int> event)) {
191 _hasSubscriber = true;
192 return _realRequest.where(test);
193 }
194
195 Future<List<int>> reduce(
196 List<int> combine(List<int> previous, List<int> element)) {
197 _hasSubscriber = true;
198 return _realRequest.reduce(combine);
199 }
200
201 Future pipe(StreamConsumer<List<int>> streamConsumer) {
202 _hasSubscriber = true;
203 return _realRequest.pipe(streamConsumer);
204 }
205
206 Uri get uri => _realRequest.uri;
207
208 X509Certificate get certificate => _realRequest.certificate;
209
210 HttpConnectionInfo get connectionInfo => _realRequest.connectionInfo;
211
212 int get contentLength => _realRequest.contentLength;
213
214 List<Cookie> get cookies => _realRequest.cookies;
215
216 HttpHeaders get headers => _realRequest.headers;
217
218 bool get isBroadcast => _realRequest.isBroadcast;
219
220 String get method => _realRequest.method;
221
222 bool get persistentConnection => _realRequest.persistentConnection;
223
224 String get protocolVersion => _realRequest.protocolVersion;
225
226 Uri get requestedUri => _realRequest.requestedUri;
227
228 HttpSession get session => _realRequest.session;
229 }
230
231 abstract class AppengineIOSinkMixin {
232 void writeAll(Iterable objects, [String separator = ""]) {
233 Iterator iterator = objects.iterator;
234 if (!iterator.moveNext()) return;
235 if (separator.isEmpty) {
236 do {
237 write(iterator.current);
238 } while (iterator.moveNext());
239 } else {
240 write(iterator.current);
241 while (iterator.moveNext()) {
242 write(separator);
243 write(iterator.current);
244 }
245 }
246 }
247
248 void writeln([Object obj = ""]) {
249 write(obj);
250 write('\n');
251 }
252
253 void writeCharCode(int charCode) {
254 write(new String.fromCharCode(charCode));
255 }
256
257 void write(Object obj) {
258 add(encoding.encode('$obj'));
259 }
260
261 void add(List<int> data);
262
263 Encoding get encoding;
264 }
265
266 class AppengineHttpResponse extends Object
267 with AppengineIOSinkMixin
268 implements HttpResponse {
269 final AppengineHttpRequest _request;
270 final HttpResponse _realResponse;
271 AppengineHttpHeaders _headers;
272
273 // Buffer mechanism + state
274 static const int _STATE_BUILDING_HEADER = 0;
275 static const int _STATE_BUILDING_RESPONSE = 1;
276 static const int _STATE_ADDING_STREAM = 2;
277 static const int _STATE_FINISHED = 3;
278
279 final BytesBuilder _data = new BytesBuilder();
280 final List<Function> _hooksToRunBeforeEnd = [];
281 final Completer _hooksAndResponseComplete = new Completer();
282 int _state = _STATE_BUILDING_HEADER;
283 Future _drainFuture = null;
284
285 AppengineHttpResponse(this._request, this._realResponse) {
286 _headers = new AppengineHttpHeaders(this);
287 }
288
289 void registerHook(Function function) => _hooksToRunBeforeEnd.add(function);
290
291 void add(List<int> data) {
292 if (_state == _STATE_BUILDING_HEADER) {
293 _state = _STATE_BUILDING_RESPONSE;
294 } else if (_state == _STATE_ADDING_STREAM) {
295 throw new StateError(
296 'Cannot add data while addStream() has not finished');
297 }
298 _enqueueData(data);
299 }
300
301 void addError(error, [StackTrace stackTrace]) {
302 if (_state == _STATE_BUILDING_HEADER) {
303 _state = _STATE_BUILDING_RESPONSE;
304 } else if (_state == _STATE_ADDING_STREAM) {
305 throw new StateError(
306 'Cannot add data while addStream() has not finished');
307 }
308 _submitData(error, stackTrace);
309 }
310
311 Future addStream(Stream<List<int>> stream) {
312 if (_state == _STATE_ADDING_STREAM) {
313 throw new StateError(
314 'Cannot call addStream() before previous addStream() is done.');
315 }
316 _state = _STATE_ADDING_STREAM;
317 var completer = new Completer();
318
319 stream.listen((List<int> data) {
320 _enqueueData(data);
321 }, onError: (error, stack) {
322 // NOTE: The error will be reported on the returned Future of addStream().
323 // The close()/done future will complete without an error.
324 _submitData();
325 completer.completeError(error, stack);
326 }, onDone: () {
327 _state = _STATE_BUILDING_RESPONSE;
328 completer.complete(this);
329 }, cancelOnError: true);
330
331 return completer.future;
332 }
333
334 Future flush() {
335 // We have to collect all data before sending it back, so we do not support
336 // flushing the output stream.
337 return new Future.value();
338 }
339
340 Future close() {
341 _submitData();
342 return done;
343 }
344
345 Future get done => _hooksAndResponseComplete.future;
346
347 HttpConnectionInfo get connectionInfo => _realResponse.connectionInfo;
348
349 void set deadline(Duration deadline) {
350 _realResponse.deadline = deadline;
351 }
352
353 Duration get deadline => _realResponse.deadline;
354
355 void set bufferOutput(bool bufferOutput) {
356 _realResponse.bufferOutput = bufferOutput;
357 }
358
359 bool get bufferOutput => _realResponse.bufferOutput;
360
361 Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY}) {
362 _ensureInHeaderBuildingState();
363 return _submitRedirect(location, status);
364 }
365
366 void set contentLength(int contentLength) {
367 // NOTE: The state checking will be handled by [_headers].
368 _headers.contentLength = contentLength;
369 }
370
371 int get contentLength => _headers.contentLength;
372
373 // NOTE: We have custom headers here, to override state checking, since the
374 // underlying [_realResponse], doesn't know when we start buffering data.
375 HttpHeaders get headers => _headers;
376
377 // NOTE: The 'dart:io' implementation allows you to modify cookies after
378 // writing data, so we just forward.
379 List<Cookie> get cookies => _realResponse.cookies;
380
381 void set statusCode(int statusCode) {
382 _ensureInHeaderBuildingState(stateError: true);
383 _realResponse.statusCode = statusCode;
384 }
385
386 int get statusCode => _realResponse.statusCode;
387
388 void set persistentConnection(bool persistentConnection) {
389 _ensureInHeaderBuildingState();
390 _realResponse.persistentConnection = persistentConnection;
391 }
392
393 bool get persistentConnection => _realResponse.persistentConnection;
394
395 void set reasonPhrase(String reasonPhrase) {
396 _ensureInHeaderBuildingState(stateError: true);
397 _realResponse.reasonPhrase = reasonPhrase;
398 }
399
400 String get reasonPhrase => _realResponse.reasonPhrase;
401
402
403 void set encoding(Encoding _encoding) {
404 throw new StateError('HttpResponse encoding is not mutable.');
405 }
406
407 Encoding get encoding => _realResponse.encoding;
408
409 Future<Socket> detachSocket({bool writeHeaders: true}) {
410 throw new UnsupportedError('You cannot detach the socket '
411 'from AppengineHttpResponse implementation.');
412 }
413
414 Future _drain() {
415 // Asynchronously detect whether we need to drain and if so drain it.
416 return new Future(() {
417 // If someone listens to the data, we will not drain it.
418 if (_request._hasSubscriber) {
419 return new Future.value();
420 }
421 _request._hasSubscriber = true;
422 return _request.drain().catchError((_) {});
423 });
424 }
425
426 _enqueueData(List<int> data) {
427 if (_state == _STATE_FINISHED) return;
428
429 if (_drainFuture == null) {
430 _drainFuture = _drain();
431 }
432
433 _data.add(data);
434 }
435
436 _submitData([error, stack]) {
437 if (_state == _STATE_FINISHED) return;
438 _state = _STATE_FINISHED;
439
440 if (_drainFuture == null) {
441 _drainFuture = _drain();
442 }
443
444 // Run all hooks before sending the data and closing.
445 _drainFuture.then((_) {
446 _runHooks().then((_) {
447 if (_request.method != 'HEAD' &&_realResponse.contentLength == -1) {
448 _realResponse.contentLength = _data.length;
449 }
450 _realResponse.add(_data.takeBytes());
451 _data.clear();
452 _realResponse.close().then((_){
453 _hooksAndResponseComplete.complete(this);
454 }).catchError((error, stack) {
455 _hooksAndResponseComplete.completeError(error, stack);
456 });
457 });
458 });
459 }
460
461 Future _submitRedirect(Uri location, int status) {
462 _state = _STATE_FINISHED;
463
464 if (_drainFuture == null) {
465 _drainFuture = _drain();
466 }
467
468 // Run all hooks before sending the redirect.
469 return _drainFuture.then((_) {
470 return _runHooks().then((_) {
471 return _realResponse.redirect(location, status: status);
472 });
473 });
474 }
475
476 void _ensureInHeaderBuildingState({bool stateError: false}) {
477 if (_state != _STATE_BUILDING_HEADER) {
478 if (stateError) {
479 throw new StateError('HTTP headers were already sent.');
480 } else {
481 throw new HttpException('HTTP headers were already sent.');
482 }
483 }
484 }
485
486 Future _runHooks() {
487 // TODO: We swallow errors from the hooks here. Having an internal error
488 // mechanism would be beneficial where we could report these kinds of
489 // errors.
490 var futures = _hooksToRunBeforeEnd.map((hook) => hook());
491 return Future.wait(futures).catchError((_) {});
492 }
493 }
494
495 class AppengineHttpHeaders implements HttpHeaders {
496 final AppengineHttpResponse _response;
497 final HttpHeaders _realHeaders;
498
499 AppengineHttpHeaders(AppengineHttpResponse response)
500 : _response = response, _realHeaders = response._realResponse.headers;
501
502 List<String> operator [](String name) {
503 // NOTE: The underlying HttpResponse from dart:io doesn't do checks, so we
504 // don't do checks either here.
505 return _realHeaders[name];
506 }
507
508 void add(String name, Object value) {
509 _response._ensureInHeaderBuildingState();
510 _realHeaders.add(name, value);
511 }
512
513 void set chunkedTransferEncoding(bool chunkedTransferEncoding) {
514 _response._ensureInHeaderBuildingState();
515 _realHeaders.chunkedTransferEncoding = chunkedTransferEncoding;
516 }
517
518 bool get chunkedTransferEncoding => _realHeaders.chunkedTransferEncoding;
519
520 void set contentLength(int contentLength) {
521 _response._ensureInHeaderBuildingState();
522 _realHeaders.contentLength = contentLength;
523 }
524
525 int get contentLength => _realHeaders.contentLength;
526
527 void set contentType(ContentType contentType) {
528 _response._ensureInHeaderBuildingState();
529 _realHeaders.contentType = contentType;
530 }
531
532 ContentType get contentType => _realHeaders.contentType;
533
534 void set date(DateTime date) {
535 _response._ensureInHeaderBuildingState();
536 _realHeaders.date = date;
537 }
538
539 DateTime get date => _realHeaders.date;
540
541 void set expires(DateTime expires) {
542 _response._ensureInHeaderBuildingState();
543 _realHeaders.expires = expires;
544 }
545
546 DateTime get expires => _realHeaders.expires;
547
548 void forEach(void f(String name, List<String> values)) {
549 // NOTE: The underlying HttpResponse from dart:io leaks the List (which is
550 // modifiable even after writing data), so we don't change that.
551 _realHeaders.forEach(f);
552 }
553
554 void set host(String host) {
555 _response._ensureInHeaderBuildingState();
556 _realHeaders.host = host;
557 }
558
559 String get host => _realHeaders.host;
560
561 void set ifModifiedSince(DateTime ifModifiedSince) {
562 _response._ensureInHeaderBuildingState();
563 _realHeaders.ifModifiedSince = ifModifiedSince;
564 }
565
566 DateTime get ifModifiedSince => _realHeaders.ifModifiedSince;
567
568 void noFolding(String name) {
569 // NOTE: The underlying HttpResponse from dart:io doesn't do checks, so we
570 // don't do checks either here.
571 _realHeaders.noFolding(name);
572 }
573
574 void set persistentConnection(bool persistentConnection) {
575 _response._ensureInHeaderBuildingState();
576 _realHeaders.persistentConnection = persistentConnection;
577 }
578
579 bool get persistentConnection => _realHeaders.persistentConnection;
580
581 void set port(int port) {
582 _response._ensureInHeaderBuildingState();
583 _realHeaders.port = port;
584 }
585
586 int get port => _realHeaders.port;
587
588 void remove(String name, Object value) {
589 _response._ensureInHeaderBuildingState();
590 _realHeaders.remove(name, value);
591 }
592
593 void removeAll(String name) {
594 _response._ensureInHeaderBuildingState();
595 _realHeaders.removeAll(name);
596 }
597
598 void set(String name, Object value) {
599 _response._ensureInHeaderBuildingState();
600 _realHeaders.set(name, value);
601 }
602
603 String value(String name) => _realHeaders.value(name);
604
605 void clear() {
606 _response._ensureInHeaderBuildingState();
607 _realHeaders.clear();
608 }
609 }
OLDNEW
« no previous file with comments | « pkg/appengine/lib/src/server/context_registry.dart ('k') | pkg/appengine/lib/src/server/server.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698