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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/appengine/lib/src/server/http_wrapper.dart
diff --git a/pkg/appengine/lib/src/server/http_wrapper.dart b/pkg/appengine/lib/src/server/http_wrapper.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6908c56fc8b57889b461ae89a80d32b85b08c925
--- /dev/null
+++ b/pkg/appengine/lib/src/server/http_wrapper.dart
@@ -0,0 +1,609 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library http_wrapper;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:convert';
+
+class AppengineHttpRequest implements HttpRequest {
+ final HttpRequest _realRequest;
+ AppengineHttpResponse _response;
+ bool _hasSubscriber = false;
+
+ AppengineHttpRequest(this._realRequest) {
+ _response = new AppengineHttpResponse(this, _realRequest.response);
+ }
+
+ AppengineHttpResponse get response => _response;
+
+ Future<bool> any(bool test(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.any(test);
+ }
+
+ Stream<List<int>> asBroadcastStream(
+ {void onListen(StreamSubscription<List<int>> subscription),
+ void onCancel(StreamSubscription<List<int>> subscription)}) {
+ _hasSubscriber = true;
+ return _realRequest.asBroadcastStream(onListen: onListen,
+ onCancel: onCancel);
+ }
+
+ Stream asyncExpand(Stream convert(List<int> event)) {
+ _hasSubscriber = true;
+ return _realRequest.asyncExpand(convert);
+ }
+
+ Stream asyncMap(convert(List<int> event)) {
+ _hasSubscriber = true;
+ return _realRequest.asyncMap(convert);
+ }
+
+ Future<bool> contains(Object needle) {
+ _hasSubscriber = true;
+ return _realRequest.contains(needle);
+ }
+
+ Stream<List<int>> distinct(
+ [bool equals(List<int> previous, List<int> next)]) {
+ _hasSubscriber = true;
+ return _realRequest.distinct(equals);
+ }
+
+ Future drain([futureValue]) {
+ _hasSubscriber = true;
+ return _realRequest.drain(futureValue);
+ }
+
+ Future<List<int>> elementAt(int index) {
+ _hasSubscriber = true;
+ return _realRequest.elementAt(index);
+ }
+
+ Future<bool> every(bool test(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.every(test);
+ }
+
+ Stream expand(Iterable convert(List<int> value)) {
+ _hasSubscriber = true;
+ return _realRequest.expand(convert);
+ }
+
+ Future<List<int>> get first {
+ _hasSubscriber = true;
+ return _realRequest.first;
+ }
+
+ Future firstWhere(bool test(List<int> element), {Object defaultValue()}) {
+ _hasSubscriber = true;
+ return _realRequest.firstWhere(test, defaultValue: defaultValue);
+ }
+
+ Future fold(initialValue, combine(previous, List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.fold(initialValue, combine);
+ }
+
+ Future forEach(void action(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.forEach(action);
+ }
+
+ Stream<List<int>> handleError(Function onError, {bool test(error)}) {
+ _hasSubscriber = true;
+ return _realRequest.handleError(onError, test: test);
+ }
+
+ Future<bool> get isEmpty {
+ _hasSubscriber = true;
+ return _realRequest.isEmpty;
+ }
+
+ Future<String> join([String separator = ""]) {
+ _hasSubscriber = true;
+ return _realRequest.join(separator);
+ }
+
+ Future<List<int>> get last {
+ _hasSubscriber = true;
+ return _realRequest.last;
+ }
+
+ Future lastWhere(bool test(List<int> element), {Object defaultValue()}) {
+ _hasSubscriber = true;
+ return _realRequest.lastWhere(test, defaultValue: defaultValue);
+ }
+
+ Future<int> get length {
+ _hasSubscriber = true;
+ return _realRequest.length;
+ }
+
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {Function onError, void onDone(), bool cancelOnError}) {
+ _hasSubscriber = true;
+ return _realRequest.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ cancelOnError: cancelOnError);
+ }
+
+ Stream map(convert(List<int> event)) {
+ _hasSubscriber = true;
+ return _realRequest.map(convert);
+ }
+
+ Future<List<int>> get single {
+ _hasSubscriber = true;
+ return _realRequest.single;
+ }
+
+ Future<List<int>> singleWhere(bool test(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.singleWhere(test);
+ }
+
+ Stream<List<int>> skip(int count) {
+ _hasSubscriber = true;
+ return _realRequest.skip(count);
+ }
+
+ Stream<List<int>> skipWhile(bool test(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.skipWhile(test);
+ }
+
+ Stream<List<int>> take(int count) {
+ _hasSubscriber = true;
+ return _realRequest.take(count);
+ }
+
+ Stream<List<int>> takeWhile(bool test(List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.takeWhile(test);
+ }
+
+ Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) {
+ _hasSubscriber = true;
+ return _realRequest.timeout(timeLimit, onTimeout: onTimeout);
+ }
+
+ Future<List<List<int>>> toList() {
+ _hasSubscriber = true;
+ return _realRequest.toList();
+ }
+
+ Future<Set<List<int>>> toSet() {
+ _hasSubscriber = true;
+ return _realRequest.toSet();
+ }
+
+ Stream transform(StreamTransformer<List<int>, dynamic> streamTransformer) {
+ _hasSubscriber = true;
+ return _realRequest.transform(streamTransformer);
+ }
+
+ Stream<List<int>> where(bool test(List<int> event)) {
+ _hasSubscriber = true;
+ return _realRequest.where(test);
+ }
+
+ Future<List<int>> reduce(
+ List<int> combine(List<int> previous, List<int> element)) {
+ _hasSubscriber = true;
+ return _realRequest.reduce(combine);
+ }
+
+ Future pipe(StreamConsumer<List<int>> streamConsumer) {
+ _hasSubscriber = true;
+ return _realRequest.pipe(streamConsumer);
+ }
+
+ Uri get uri => _realRequest.uri;
+
+ X509Certificate get certificate => _realRequest.certificate;
+
+ HttpConnectionInfo get connectionInfo => _realRequest.connectionInfo;
+
+ int get contentLength => _realRequest.contentLength;
+
+ List<Cookie> get cookies => _realRequest.cookies;
+
+ HttpHeaders get headers => _realRequest.headers;
+
+ bool get isBroadcast => _realRequest.isBroadcast;
+
+ String get method => _realRequest.method;
+
+ bool get persistentConnection => _realRequest.persistentConnection;
+
+ String get protocolVersion => _realRequest.protocolVersion;
+
+ Uri get requestedUri => _realRequest.requestedUri;
+
+ HttpSession get session => _realRequest.session;
+}
+
+abstract class AppengineIOSinkMixin {
+ void writeAll(Iterable objects, [String separator = ""]) {
+ Iterator iterator = objects.iterator;
+ if (!iterator.moveNext()) return;
+ if (separator.isEmpty) {
+ do {
+ write(iterator.current);
+ } while (iterator.moveNext());
+ } else {
+ write(iterator.current);
+ while (iterator.moveNext()) {
+ write(separator);
+ write(iterator.current);
+ }
+ }
+ }
+
+ void writeln([Object obj = ""]) {
+ write(obj);
+ write('\n');
+ }
+
+ void writeCharCode(int charCode) {
+ write(new String.fromCharCode(charCode));
+ }
+
+ void write(Object obj) {
+ add(encoding.encode('$obj'));
+ }
+
+ void add(List<int> data);
+
+ Encoding get encoding;
+}
+
+class AppengineHttpResponse extends Object
+ with AppengineIOSinkMixin
+ implements HttpResponse {
+ final AppengineHttpRequest _request;
+ final HttpResponse _realResponse;
+ AppengineHttpHeaders _headers;
+
+ // Buffer mechanism + state
+ static const int _STATE_BUILDING_HEADER = 0;
+ static const int _STATE_BUILDING_RESPONSE = 1;
+ static const int _STATE_ADDING_STREAM = 2;
+ static const int _STATE_FINISHED = 3;
+
+ final BytesBuilder _data = new BytesBuilder();
+ final List<Function> _hooksToRunBeforeEnd = [];
+ final Completer _hooksAndResponseComplete = new Completer();
+ int _state = _STATE_BUILDING_HEADER;
+ Future _drainFuture = null;
+
+ AppengineHttpResponse(this._request, this._realResponse) {
+ _headers = new AppengineHttpHeaders(this);
+ }
+
+ void registerHook(Function function) => _hooksToRunBeforeEnd.add(function);
+
+ void add(List<int> data) {
+ if (_state == _STATE_BUILDING_HEADER) {
+ _state = _STATE_BUILDING_RESPONSE;
+ } else if (_state == _STATE_ADDING_STREAM) {
+ throw new StateError(
+ 'Cannot add data while addStream() has not finished');
+ }
+ _enqueueData(data);
+ }
+
+ void addError(error, [StackTrace stackTrace]) {
+ if (_state == _STATE_BUILDING_HEADER) {
+ _state = _STATE_BUILDING_RESPONSE;
+ } else if (_state == _STATE_ADDING_STREAM) {
+ throw new StateError(
+ 'Cannot add data while addStream() has not finished');
+ }
+ _submitData(error, stackTrace);
+ }
+
+ Future addStream(Stream<List<int>> stream) {
+ if (_state == _STATE_ADDING_STREAM) {
+ throw new StateError(
+ 'Cannot call addStream() before previous addStream() is done.');
+ }
+ _state = _STATE_ADDING_STREAM;
+ var completer = new Completer();
+
+ stream.listen((List<int> data) {
+ _enqueueData(data);
+ }, onError: (error, stack) {
+ // NOTE: The error will be reported on the returned Future of addStream().
+ // The close()/done future will complete without an error.
+ _submitData();
+ completer.completeError(error, stack);
+ }, onDone: () {
+ _state = _STATE_BUILDING_RESPONSE;
+ completer.complete(this);
+ }, cancelOnError: true);
+
+ return completer.future;
+ }
+
+ Future flush() {
+ // We have to collect all data before sending it back, so we do not support
+ // flushing the output stream.
+ return new Future.value();
+ }
+
+ Future close() {
+ _submitData();
+ return done;
+ }
+
+ Future get done => _hooksAndResponseComplete.future;
+
+ HttpConnectionInfo get connectionInfo => _realResponse.connectionInfo;
+
+ void set deadline(Duration deadline) {
+ _realResponse.deadline = deadline;
+ }
+
+ Duration get deadline => _realResponse.deadline;
+
+ void set bufferOutput(bool bufferOutput) {
+ _realResponse.bufferOutput = bufferOutput;
+ }
+
+ bool get bufferOutput => _realResponse.bufferOutput;
+
+ Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY}) {
+ _ensureInHeaderBuildingState();
+ return _submitRedirect(location, status);
+ }
+
+ void set contentLength(int contentLength) {
+ // NOTE: The state checking will be handled by [_headers].
+ _headers.contentLength = contentLength;
+ }
+
+ int get contentLength => _headers.contentLength;
+
+ // NOTE: We have custom headers here, to override state checking, since the
+ // underlying [_realResponse], doesn't know when we start buffering data.
+ HttpHeaders get headers => _headers;
+
+ // NOTE: The 'dart:io' implementation allows you to modify cookies after
+ // writing data, so we just forward.
+ List<Cookie> get cookies => _realResponse.cookies;
+
+ void set statusCode(int statusCode) {
+ _ensureInHeaderBuildingState(stateError: true);
+ _realResponse.statusCode = statusCode;
+ }
+
+ int get statusCode => _realResponse.statusCode;
+
+ void set persistentConnection(bool persistentConnection) {
+ _ensureInHeaderBuildingState();
+ _realResponse.persistentConnection = persistentConnection;
+ }
+
+ bool get persistentConnection => _realResponse.persistentConnection;
+
+ void set reasonPhrase(String reasonPhrase) {
+ _ensureInHeaderBuildingState(stateError: true);
+ _realResponse.reasonPhrase = reasonPhrase;
+ }
+
+ String get reasonPhrase => _realResponse.reasonPhrase;
+
+
+ void set encoding(Encoding _encoding) {
+ throw new StateError('HttpResponse encoding is not mutable.');
+ }
+
+ Encoding get encoding => _realResponse.encoding;
+
+ Future<Socket> detachSocket({bool writeHeaders: true}) {
+ throw new UnsupportedError('You cannot detach the socket '
+ 'from AppengineHttpResponse implementation.');
+ }
+
+ Future _drain() {
+ // Asynchronously detect whether we need to drain and if so drain it.
+ return new Future(() {
+ // If someone listens to the data, we will not drain it.
+ if (_request._hasSubscriber) {
+ return new Future.value();
+ }
+ _request._hasSubscriber = true;
+ return _request.drain().catchError((_) {});
+ });
+ }
+
+ _enqueueData(List<int> data) {
+ if (_state == _STATE_FINISHED) return;
+
+ if (_drainFuture == null) {
+ _drainFuture = _drain();
+ }
+
+ _data.add(data);
+ }
+
+ _submitData([error, stack]) {
+ if (_state == _STATE_FINISHED) return;
+ _state = _STATE_FINISHED;
+
+ if (_drainFuture == null) {
+ _drainFuture = _drain();
+ }
+
+ // Run all hooks before sending the data and closing.
+ _drainFuture.then((_) {
+ _runHooks().then((_) {
+ if (_request.method != 'HEAD' &&_realResponse.contentLength == -1) {
+ _realResponse.contentLength = _data.length;
+ }
+ _realResponse.add(_data.takeBytes());
+ _data.clear();
+ _realResponse.close().then((_){
+ _hooksAndResponseComplete.complete(this);
+ }).catchError((error, stack) {
+ _hooksAndResponseComplete.completeError(error, stack);
+ });
+ });
+ });
+ }
+
+ Future _submitRedirect(Uri location, int status) {
+ _state = _STATE_FINISHED;
+
+ if (_drainFuture == null) {
+ _drainFuture = _drain();
+ }
+
+ // Run all hooks before sending the redirect.
+ return _drainFuture.then((_) {
+ return _runHooks().then((_) {
+ return _realResponse.redirect(location, status: status);
+ });
+ });
+ }
+
+ void _ensureInHeaderBuildingState({bool stateError: false}) {
+ if (_state != _STATE_BUILDING_HEADER) {
+ if (stateError) {
+ throw new StateError('HTTP headers were already sent.');
+ } else {
+ throw new HttpException('HTTP headers were already sent.');
+ }
+ }
+ }
+
+ Future _runHooks() {
+ // TODO: We swallow errors from the hooks here. Having an internal error
+ // mechanism would be beneficial where we could report these kinds of
+ // errors.
+ var futures = _hooksToRunBeforeEnd.map((hook) => hook());
+ return Future.wait(futures).catchError((_) {});
+ }
+}
+
+class AppengineHttpHeaders implements HttpHeaders {
+ final AppengineHttpResponse _response;
+ final HttpHeaders _realHeaders;
+
+ AppengineHttpHeaders(AppengineHttpResponse response)
+ : _response = response, _realHeaders = response._realResponse.headers;
+
+ List<String> operator [](String name) {
+ // NOTE: The underlying HttpResponse from dart:io doesn't do checks, so we
+ // don't do checks either here.
+ return _realHeaders[name];
+ }
+
+ void add(String name, Object value) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.add(name, value);
+ }
+
+ void set chunkedTransferEncoding(bool chunkedTransferEncoding) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.chunkedTransferEncoding = chunkedTransferEncoding;
+ }
+
+ bool get chunkedTransferEncoding => _realHeaders.chunkedTransferEncoding;
+
+ void set contentLength(int contentLength) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.contentLength = contentLength;
+ }
+
+ int get contentLength => _realHeaders.contentLength;
+
+ void set contentType(ContentType contentType) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.contentType = contentType;
+ }
+
+ ContentType get contentType => _realHeaders.contentType;
+
+ void set date(DateTime date) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.date = date;
+ }
+
+ DateTime get date => _realHeaders.date;
+
+ void set expires(DateTime expires) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.expires = expires;
+ }
+
+ DateTime get expires => _realHeaders.expires;
+
+ void forEach(void f(String name, List<String> values)) {
+ // NOTE: The underlying HttpResponse from dart:io leaks the List (which is
+ // modifiable even after writing data), so we don't change that.
+ _realHeaders.forEach(f);
+ }
+
+ void set host(String host) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.host = host;
+ }
+
+ String get host => _realHeaders.host;
+
+ void set ifModifiedSince(DateTime ifModifiedSince) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.ifModifiedSince = ifModifiedSince;
+ }
+
+ DateTime get ifModifiedSince => _realHeaders.ifModifiedSince;
+
+ void noFolding(String name) {
+ // NOTE: The underlying HttpResponse from dart:io doesn't do checks, so we
+ // don't do checks either here.
+ _realHeaders.noFolding(name);
+ }
+
+ void set persistentConnection(bool persistentConnection) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.persistentConnection = persistentConnection;
+ }
+
+ bool get persistentConnection => _realHeaders.persistentConnection;
+
+ void set port(int port) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.port = port;
+ }
+
+ int get port => _realHeaders.port;
+
+ void remove(String name, Object value) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.remove(name, value);
+ }
+
+ void removeAll(String name) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.removeAll(name);
+ }
+
+ void set(String name, Object value) {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.set(name, value);
+ }
+
+ String value(String name) => _realHeaders.value(name);
+
+ void clear() {
+ _response._ensureInHeaderBuildingState();
+ _realHeaders.clear();
+ }
+}
« 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