[roll] Update third-party dart packages

Updated:
Change-Id: I2b875e2128e808e14357321b2c1bed8236c93072
diff --git a/_discoveryapis_commons/.gitignore b/_discoveryapis_commons/.gitignore
index d69d97c..813c59a 100644
--- a/_discoveryapis_commons/.gitignore
+++ b/_discoveryapis_commons/.gitignore
@@ -1,11 +1,3 @@
-.buildlog
-.DS_Store
-.idea
 .packages
-.project
-.pub/
 .dart_tool/
-.settings/
-build
-packages
 pubspec.lock
diff --git a/_discoveryapis_commons/.status b/_discoveryapis_commons/.status
deleted file mode 100644
index 364ca4b..0000000
--- a/_discoveryapis_commons/.status
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright (c) 2015, 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.
-
diff --git a/_discoveryapis_commons/.travis.yml b/_discoveryapis_commons/.travis.yml
index dece76c..a26ab6a 100644
--- a/_discoveryapis_commons/.travis.yml
+++ b/_discoveryapis_commons/.travis.yml
@@ -1,19 +1,23 @@
 language: dart
 
 dart:
-  - dev
-  - stable
+- dev
+- 2.0.0
 
 dart_task:
-  - test
-  - test: --platform firefox -j 1
-  - dartanalyzer: --fatal-infos --fatal-warnings .
-  - dartfmt
+- test
+- test: --platform firefox -j 1
+- dartanalyzer: --fatal-infos --fatal-warnings .
+
+matrix:
+  include:
+  - dart: dev
+    dart_task: dartfmt
 
 # Only building master means that we don't run two builds for each pull request.
 branches:
   only: [master]
 
 cache:
- directories:
-   - $HOME/.pub-cache
+  directories:
+  - $HOME/.pub-cache
diff --git a/_discoveryapis_commons/BUILD.gn b/_discoveryapis_commons/BUILD.gn
index 2255734..45eca6b 100644
--- a/_discoveryapis_commons/BUILD.gn
+++ b/_discoveryapis_commons/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for _discoveryapis_commons-0.1.8+1
+# This file is generated by importer.py for _discoveryapis_commons-0.1.9
 
 import("//build/dart/dart_library.gni")
 
diff --git a/_discoveryapis_commons/CHANGELOG.md b/_discoveryapis_commons/CHANGELOG.md
index 30b87eb..8397d80 100644
--- a/_discoveryapis_commons/CHANGELOG.md
+++ b/_discoveryapis_commons/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.1.9
+
+ - Added a `x-goog-api-client` header for client library identification.
+
+## 0.1.8+2
+
+- Filter out headers we're not allowed to send when operating in a browser.
+
 ## 0.1.8+1
 
 - Fix `content-length` bug introduced in `0.1.8`.
diff --git a/_discoveryapis_commons/analysis_options.yaml b/_discoveryapis_commons/analysis_options.yaml
index 4ef8ee7..958f92a 100644
--- a/_discoveryapis_commons/analysis_options.yaml
+++ b/_discoveryapis_commons/analysis_options.yaml
@@ -11,22 +11,20 @@
 linter:
   rules:
     - always_declare_return_types
-    #- annotate_overrides
     - avoid_empty_else
     - avoid_function_literals_in_foreach_calls
     - avoid_init_to_null
     - avoid_null_checks_in_equality_operators
+    - avoid_relative_lib_imports
     - avoid_renaming_method_parameters
     - avoid_return_types_on_setters
     - avoid_returning_null
+    - avoid_shadowing_type_parameters
     - avoid_types_as_parameter_names
     - avoid_unused_constructor_parameters
     - await_only_futures
     - camel_case_types
     - cancel_subscriptions
-    #- cascade_invocations
-    #- comment_references
-    #- constant_identifier_names
     - control_flow_in_finally
     - directives_ordering
     - empty_catches
@@ -36,17 +34,15 @@
     - implementation_imports
     - invariant_booleans
     - iterable_contains_unrelated_type
-    #- library_names
     - library_prefixes
     - list_remove_unrelated_type
     - literal_only_boolean_expressions
     - no_adjacent_strings_in_list
     - no_duplicate_case_values
-    #- non_constant_identifier_names
+    - null_closures
     - omit_local_variable_types
     - only_throw_errors
     - overridden_fields
-    #- package_api_docs
     - package_names
     - package_prefixed_library_names
     - prefer_adjacent_string_concatenation
@@ -56,22 +52,23 @@
     - prefer_contains
     - prefer_equal_for_default_values
     - prefer_final_fields
+    - prefer_generic_function_type_aliases
     - prefer_initializing_formals
-    #- prefer_interpolation_to_compose_strings
     - prefer_is_empty
     - prefer_is_not_empty
     - prefer_single_quotes
     - prefer_typing_uninitialized_variables
     - recursive_getters
-    #- slash_for_doc_comments
-    - super_goes_last
+    - slash_for_doc_comments
     - test_types_in_equals
     - throw_in_finally
     - type_init_formals
     - unawaited_futures
     - unnecessary_brace_in_string_interps
+    - unnecessary_const
     - unnecessary_getters_setters
     - unnecessary_lambdas
+    - unnecessary_new
     - unnecessary_null_aware_assignments
     - unnecessary_statements
     - unnecessary_this
diff --git a/_discoveryapis_commons/codereview.settings b/_discoveryapis_commons/codereview.settings
deleted file mode 100644
index 5272cec..0000000
--- a/_discoveryapis_commons/codereview.settings
+++ /dev/null
@@ -1,3 +0,0 @@
-CODE_REVIEW_SERVER: https://codereview.chromium.org
-VIEW_VC: https://github.com/dart-lang/discoveryapis_commons/commit/
-CC_LIST: reviews@dartlang.org
diff --git a/_discoveryapis_commons/lib/src/clients.dart b/_discoveryapis_commons/lib/src/clients.dart
index 66cb257..42210c6 100644
--- a/_discoveryapis_commons/lib/src/clients.dart
+++ b/_discoveryapis_commons/lib/src/clients.dart
@@ -10,13 +10,20 @@
 import 'package:http/http.dart' as http;
 
 import 'requests.dart' as client_requests;
+import 'version_fallback.dart' if (dart.library.io) 'version_io.dart';
 
 const CONTENT_TYPE_JSON_UTF8 = 'application/json; charset=utf-8';
 
-/**
- * Base class for all API clients, offering generic methods for
- * HTTP Requests to the API
- */
+/// List of headers that is forbidden in current execution context.
+///
+/// In a browser context we're not allowed to set `user-agent` and
+/// `content-length` headers.
+const _forbiddenHeaders = bool.fromEnvironment('dart.library.html')
+    ? <String>['user-agent', 'content-length']
+    : <String>[];
+
+/// Base class for all API clients, offering generic methods for
+/// HTTP Requests to the API
 class ApiRequester {
   final http.Client _httpClient;
   final String _rootUrl;
@@ -28,22 +35,20 @@
     assert(_rootUrl.endsWith('/'));
   }
 
-  /**
-   * Sends a HTTPRequest using [method] (usually GET or POST) to [requestUrl]
-   * using the specified [urlParams] and [queryParams]. Optionally include a
-   * [body] and/or [uploadMedia] in the request.
-   *
-   * If [uploadMedia] was specified [downloadOptions] must be
-   * [DownloadOptions.Metadata] or `null`.
-   *
-   * If [downloadOptions] is [DownloadOptions.Metadata] the result will be
-   * decoded as JSON.
-   *
-   * If [downloadOptions] is `null` the result will be a Future completing with
-   * `null`.
-   *
-   * Otherwise the result will be downloaded as a [client_requests.Media]
-   */
+  /// Sends a HTTPRequest using [method] (usually GET or POST) to [requestUrl]
+  /// using the specified [urlParams] and [queryParams]. Optionally include a
+  /// [body] and/or [uploadMedia] in the request.
+  ///
+  /// If [uploadMedia] was specified [downloadOptions] must be
+  /// [DownloadOptions.Metadata] or `null`.
+  ///
+  /// If [downloadOptions] is [DownloadOptions.Metadata] the result will be
+  /// decoded as JSON.
+  ///
+  /// If [downloadOptions] is `null` the result will be a Future completing with
+  /// `null`.
+  ///
+  /// Otherwise the result will be downloaded as a [client_requests.Media]
   Future request(String requestUrl, String method,
       {String body,
       Map<String, List<String>> queryParams,
@@ -202,14 +207,20 @@
           'content-type': CONTENT_TYPE_JSON_UTF8,
           'content-length': '$length',
           'range': 'bytes=${downloadRange.start}-${downloadRange.end}',
+          'x-goog-api-client': 'gl-dart/$dartVersion',
         };
       } else {
         headers = {
           'user-agent': _userAgent,
           'content-type': CONTENT_TYPE_JSON_UTF8,
           'content-length': '$length',
+          'x-goog-api-client': 'gl-dart/$dartVersion',
         };
       }
+      // Filter out headers forbidden in the browser (in calling in browser).
+      // If we don't do this, the browser will complain that we're attempting
+      // to set a header that we're not allowed to set.
+      headers.removeWhere((key, value) => _forbiddenHeaders.contains(key));
 
       var request = _RequestImpl(method, uri, bodyController.stream);
       request.headers.addAll(headers);
@@ -218,7 +229,7 @@
 
     if (uploadMedia != null) {
       // Three upload types:
-      // 1. Resumable: Upload of data + metdata with multiple requests.
+      // 1. Resumable: Upload of data + metadata with multiple requests.
       // 2. Simple: Upload of media.
       // 3. Multipart: Upload of data + metadata.
 
@@ -246,9 +257,7 @@
   }
 }
 
-/**
- * Does media uploads using the multipart upload protocol.
- */
+/// Does media uploads using the multipart upload protocol.
 class MultipartMediaUploader {
   static final _boundary = '314159265358979323846';
   static final _base64Encoder = Base64Encoder();
@@ -273,11 +282,11 @@
     // This guarantees us that [_body] cannot contain a valid multipart
     // boundary.
     var bodyHead = '--$_boundary\r\n'
-        'Content-Type: $CONTENT_TYPE_JSON_UTF8\r\n\r\n' +
+            'Content-Type: $CONTENT_TYPE_JSON_UTF8\r\n\r\n' +
         _body +
         '\r\n--$_boundary\r\n'
-        'Content-Type: ${_uploadMedia.contentType}\r\n'
-        'Content-Transfer-Encoding: base64\r\n\r\n';
+            'Content-Type: ${_uploadMedia.contentType}\r\n'
+            'Content-Transfer-Encoding: base64\r\n\r\n';
     var bodyTail = '\r\n--$_boundary--';
 
     var totalLength =
@@ -305,9 +314,7 @@
   }
 }
 
-/**
- * Base64 encodes a stream of bytes.
- */
+/// Base64 encodes a stream of bytes.
 class Base64Encoder extends StreamTransformerBase<List<int>, String> {
   static int lengthOfBase64Stream(int lengthOfByteStream) {
     return ((lengthOfByteStream + 2) ~/ 3) * 4;
@@ -347,7 +354,7 @@
 
       // Convert & Send main bytes.
       if (start == 0 && end == bytes.length) {
-        // Fast path if [bytes] are devisible by 3.
+        // Fast path if [bytes] are divisible by 3.
         controller.add(base64.encode(bytes));
       } else {
         controller.add(base64.encode(bytes.sublist(start, end)));
@@ -386,9 +393,7 @@
 }
 
 // TODO: Buffer less if we know the content length in advance.
-/**
- * Does media uploads using the resumable upload protocol.
- */
+/// Does media uploads using the resumable upload protocol.
 class ResumableMediaUploader {
   final http.Client _httpClient;
   final client_requests.Media _uploadMedia;
@@ -401,12 +406,10 @@
   ResumableMediaUploader(this._httpClient, this._uploadMedia, this._body,
       this._uri, this._method, this._options, this._userAgent);
 
-  /**
-   * Returns the final [http.StreamedResponse] if the upload succeded and
-   * completes with an error otherwise.
-   *
-   * The returned response stream has not been listened to.
-   */
+  /// Returns the final [http.StreamedResponse] if the upload succeed and
+  /// completes with an error otherwise.
+  ///
+  /// The returned response stream has not been listened to.
   Future<http.StreamedResponse> upload() {
     return _startSession().then((Uri uploadUri) {
       StreamSubscription subscription;
@@ -494,11 +497,9 @@
     });
   }
 
-  /**
-   * Starts a resumable upload.
-   *
-   * Returns the [Uri] which should be used for uploading all content.
-   */
+  /// Starts a resumable upload.
+  ///
+  /// Returns the [Uri] which should be used for uploading all content.
   Future<Uri> _startSession() {
     var length = 0;
     List<int> bytes;
@@ -530,19 +531,15 @@
     });
   }
 
-  /**
-   * Uploads [chunk], retries upon server errors. The response stream will be
-   * drained.
-   */
+  /// Uploads [chunk], retries upon server errors. The response stream will be
+  /// drained.
   Future _uploadChunkDrained(Uri uri, ResumableChunk chunk) {
     return _uploadChunkResumable(uri, chunk).then((response) {
       return response.stream.drain();
     });
   }
 
-  /**
-   * Does repeated attempts to upload [chunk].
-   */
+  /// Does repeated attempts to upload [chunk].
   Future<http.StreamedResponse> _uploadChunkResumable(
       Uri uri, ResumableChunk chunk,
       {bool lastChunk = false}) {
@@ -554,8 +551,8 @@
             (status == 500 || (502 <= status && status < 504))) {
           return response.stream.drain().then((_) {
             // Delay the next attempt. Default backoff function is exponential.
-            var failedAttemts = _options.numberOfAttempts - attemptsLeft;
-            Duration duration = _options.backoffFunction(failedAttemts);
+            var failedAttempts = _options.numberOfAttempts - attemptsLeft;
+            var duration = _options.backoffFunction(failedAttempts) as Duration;
             if (duration == null) {
               throw client_requests.DetailedApiRequestError(
                   status,
@@ -590,15 +587,13 @@
     return tryUpload(_options.numberOfAttempts - 1);
   }
 
-  /**
-   * Uploads [length] bytes in [byteArrays] and ensures the upload was
-   * successful.
-   *
-   * Content-Range: [start ... (start + length)[
-   *
-   * Returns the returned [http.StreamedResponse] or completes with an error if
-   * the upload did not succeed. The response stream will not be listened to.
-   */
+  /// Uploads [length] bytes in [byteArrays] and ensures the upload was
+  /// successful.
+  ///
+  /// Content-Range: [start ... (start + length)[
+  ///
+  /// Returns the returned [http.StreamedResponse] or completes with an error if
+  /// the upload did not succeed. The response stream will not be listened to.
   Future<http.StreamedResponse> _uploadChunk(Uri uri, ResumableChunk chunk,
       {bool lastChunk = false}) {
     // If [uploadMedia.length] is null, we do not know the length.
@@ -645,9 +640,7 @@
   }
 }
 
-/**
- * Represents a stack of [ResumableChunk]s.
- */
+/// Represents a stack of [ResumableChunk]s.
 class ChunkStack {
   final int _chunkSize;
   final List<ResumableChunk> _chunkStack = [];
@@ -661,21 +654,15 @@
 
   ChunkStack(this._chunkSize);
 
-  /**
-   * Whether data for a not-yet-finished [ResumableChunk] is present. A call to
-   * `finalize` will create a [ResumableChunk] of this data.
-   */
+  /// Whether data for a not-yet-finished [ResumableChunk] is present. A call to
+  /// `finalize` will create a [ResumableChunk] of this data.
   bool get hasPartialChunk => _length > 0;
 
-  /**
-   * The number of chunks in this [ChunkStack].
-   */
+  /// The number of chunks in this [ChunkStack].
   int get length => _chunkStack.length;
 
-  /**
-   * The total number of bytes which have been converted to [ResumableChunk]s.
-   * Can only be called once this [ChunkStack] has been finalized.
-   */
+  /// The total number of bytes which have been converted to [ResumableChunk]s.
+  /// Can only be called once this [ChunkStack] has been finalized.
   int get totalByteLength {
     if (!_finalized) {
       throw StateError('ChunkStack has not been finalized yet.');
@@ -684,19 +671,15 @@
     return _offset;
   }
 
-  /**
-   * Returns the chunks [from] ... [to] and deletes it from the stack.
-   */
+  /// Returns the chunks [from] ... [to] and deletes it from the stack.
   List<ResumableChunk> removeSublist(int from, int to) {
     var sublist = _chunkStack.sublist(from, to);
     _chunkStack.removeRange(from, to);
     return sublist;
   }
 
-  /**
-   * Adds [bytes] to the buffer. If the buffer is larger than the given chunk
-   * size a new [ResumableChunk] will be created.
-   */
+  /// Adds [bytes] to the buffer. If the buffer is larger than the given chunk
+  /// size a new [ResumableChunk] will be created.
   void addBytes(List<int> bytes) {
     if (_finalized) {
       throw StateError('ChunkStack has already been finalized.');
@@ -724,10 +707,8 @@
     }
   }
 
-  /**
-   * Finalizes this [ChunkStack] and creates the last chunk (may have less bytes
-   * than the chunk size, but not zero).
-   */
+  /// Finalizes this [ChunkStack] and creates the last chunk (may have less bytes
+  /// than the chunk size, but not zero).
   void finalize() {
     if (_finalized) {
       throw StateError('ChunkStack has already been finalized.');
@@ -741,17 +722,13 @@
   }
 }
 
-/**
- * Represents a chunk of data that will be transferred in one http request.
- */
+/// Represents a chunk of data that will be transferred in one http request.
 class ResumableChunk {
   final List<List<int>> byteArrays;
   final int offset;
   final int length;
 
-  /**
-   * Index of the next byte after this chunk.
-   */
+  /// Index of the next byte after this chunk.
   int get endOfChunk => offset + length;
 
   ResumableChunk(this.byteArrays, this.offset, this.length);
@@ -802,7 +779,7 @@
     // allowed characters being those in the set
     // (unreserved / reserved / pct-encoded)
 
-    // NOTE: The chracters [ and ] need (according to URI Template spec) not be
+    // NOTE: The characters [ and ] need (according to URI Template spec) not be
     // percent encoded. The dart implementation does percent-encode [ and ].
     // This gives us in effect a conservative encoding, since the server side
     // must interpret percent-encoded parts anyway due to arbitrary unicode.
@@ -858,7 +835,7 @@
     if (stringStream != null) {
       var jsonResponse = await stringStream.transform(json.decoder).first;
       if (jsonResponse is Map && jsonResponse['error'] is Map) {
-        final Map error = jsonResponse['error'];
+        final error = jsonResponse['error'] as Map;
         final codeValue = error['code'];
         final message = error['message'] as String;
 
diff --git a/_discoveryapis_commons/lib/src/requests.dart b/_discoveryapis_commons/lib/src/requests.dart
index 3443c1b..ff53d9f 100644
--- a/_discoveryapis_commons/lib/src/requests.dart
+++ b/_discoveryapis_commons/lib/src/requests.dart
@@ -7,22 +7,18 @@
 import 'dart:async' as async;
 import 'dart:core' as core;
 
-/**
- * Represents a media consisting of a stream of bytes, a content type and a
- * length.
- */
+/// Represents a media consisting of a stream of bytes, a content type and a
+/// length.
 class Media {
   final async.Stream<core.List<core.int>> stream;
   final core.String contentType;
   final core.int length;
 
-  /**
-   * Creates a new [Media] with a byte [stream] of length [length] with a
-   * [contentType].
-   *
-   * When uploading media, [length] can only be null if [ResumableUploadOptions]
-   * is used.
-   */
+  /// Creates a new [Media] with a byte [stream] of length [length] with a
+  /// [contentType].
+  ///
+  /// When uploading media, [length] can only be null if [ResumableUploadOptions]
+  /// is used.
   Media(this.stream, this.length,
       {this.contentType = 'application/octet-stream'}) {
     if (stream == null || contentType == null) {
@@ -35,22 +31,18 @@
   }
 }
 
-/**
- * Represents options for uploading a [Media].
- */
+/// Represents options for uploading a [Media].
 class UploadOptions {
-  /** Use either simple uploads (only media) or multipart for media+metadata */
+  /// Use either simple uploads (only media) or multipart for media+metadata
   static const UploadOptions Default = UploadOptions();
 
-  /** Make resumable uploads */
+  /// Make resumable uploads
   static final ResumableUploadOptions Resumable = ResumableUploadOptions();
 
   const UploadOptions();
 }
 
-/**
- * Specifies options for resumable uploads.
- */
+/// Specifies options for resumable uploads.
 class ResumableUploadOptions extends UploadOptions {
   static final core.Function ExponentialBackoff = (core.int failedAttempts) {
     // Do not retry more than 5 times.
@@ -61,23 +53,17 @@
     return core.Duration(seconds: 1 << (failedAttempts - 1));
   };
 
-  /**
-   * Maximum number of upload attempts per chunk.
-   */
+  /// Maximum number of upload attempts per chunk.
   final core.int numberOfAttempts;
 
-  /**
-   * Preferred size (in bytes) of a uploaded chunk.
-   * Must be a multiple of 256 KB.
-   *
-   * The default is 1 MB.
-   */
+  /// Preferred size (in bytes) of a uploaded chunk.
+  /// Must be a multiple of 256 KB.
+  ///
+  /// The default is 1 MB.
   final core.int chunkSize;
 
-  /**
-   * Function for determining the [core.Duration] to wait before making the
-   * next attempt. See [ExponentialBackoff] for an example.
-   */
+  /// Function for determining the [core.Duration] to wait before making the
+  /// next attempt. See [ExponentialBackoff] for an example.
   final core.Function backoffFunction;
 
   ResumableUploadOptions(
@@ -104,54 +90,46 @@
   }
 }
 
-/**
- * Represents options for downloading media.
- *
- * For partial downloads, see [PartialDownloadOptions].
- */
+/// Represents options for downloading media.
+///
+/// For partial downloads, see [PartialDownloadOptions].
 class DownloadOptions {
-  /** Download only metadata. */
+  /// Download only metadata.
   static const DownloadOptions Metadata = DownloadOptions();
 
-  /** Download full media. */
+  /// Download full media.
   static final PartialDownloadOptions FullMedia =
       PartialDownloadOptions(ByteRange(0, -1));
 
   const DownloadOptions();
 
-  /** Indicates whether metadata should be downloaded. */
+  /// Indicates whether metadata should be downloaded.
   core.bool get isMetadataDownload => true;
 }
 
-/**
- * Options for downloading a [Media].
- */
+/// Options for downloading a [Media].
 class PartialDownloadOptions extends DownloadOptions {
-  /** The range of bytes to be downloaded */
+  /// The range of bytes to be downloaded
   final ByteRange range;
 
   PartialDownloadOptions(this.range);
 
   core.bool get isMetadataDownload => false;
 
-  /**
-   * `true` if this is a full download and `false` if this is a partial
-   * download.
-   */
+  /// `true` if this is a full download and `false` if this is a partial
+  /// download.
   core.bool get isFullDownload => range.start == 0 && range.end == -1;
 }
 
-/**
- * Specifies a range of media.
- */
+/// Specifies a range of media.
 class ByteRange {
-  /** First byte of media. */
+  /// First byte of media.
   final core.int start;
 
-  /** Last byte of media (inclusive) */
+  /// Last byte of media (inclusive)
   final core.int end;
 
-  /** Length of this range (i.e. number of bytes) */
+  /// Length of this range (i.e. number of bytes)
   core.int get length => end - start + 1;
 
   ByteRange(this.start, this.end) {
@@ -161,9 +139,7 @@
   }
 }
 
-/**
- * Represents a general error reported by the API endpoint.
- */
+/// Represents a general error reported by the API endpoint.
 class ApiRequestError extends core.Error {
   final core.String message;
 
@@ -172,9 +148,7 @@
   core.String toString() => 'ApiRequestError(message: $message)';
 }
 
-/**
- * Represents a specific error reported by the API endpoint.
- */
+/// Represents a specific error reported by the API endpoint.
 class DetailedApiRequestError extends ApiRequestError {
   /// The error code. For some non-google services this can be `null`.
   final core.int status;
diff --git a/_discoveryapis_commons/lib/src/version_fallback.dart b/_discoveryapis_commons/lib/src/version_fallback.dart
new file mode 100644
index 0000000..1672372
--- /dev/null
+++ b/_discoveryapis_commons/lib/src/version_fallback.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2019, 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.
+
+/// Hardcoded dart version as `Platform` from `dart:io` is not available when
+/// targeting javascript.
+final dartVersion = '2.0.0';
diff --git a/_discoveryapis_commons/lib/src/version_io.dart b/_discoveryapis_commons/lib/src/version_io.dart
new file mode 100644
index 0000000..747fd6d
--- /dev/null
+++ b/_discoveryapis_commons/lib/src/version_io.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:io' show Platform;
+
+/// Major.minor.patch version of current dart version.
+final dartVersion = Platform.version.split(RegExp('[^0-9]')).take(3).join('.');
diff --git a/_discoveryapis_commons/pubspec.yaml b/_discoveryapis_commons/pubspec.yaml
index b57c512..102d765 100644
--- a/_discoveryapis_commons/pubspec.yaml
+++ b/_discoveryapis_commons/pubspec.yaml
@@ -1,5 +1,5 @@
 name: _discoveryapis_commons
-version: 0.1.8+1
+version: 0.1.9
 author: Dart Team <misc@dartlang.org>
 description: Library for use by client APIs generated from Discovery Documents.
 homepage: https://github.com/dart-lang/discoveryapis_commons
diff --git a/grpc/.github/ISSUE_TEMPLATE b/grpc/.github/ISSUE_TEMPLATE
new file mode 100644
index 0000000..f9478b2
--- /dev/null
+++ b/grpc/.github/ISSUE_TEMPLATE
@@ -0,0 +1,17 @@
+<short description>
+
+<version of the grpc-dart packages used; see your `pubspec.lock` file>
+
+## Repro steps
+
+1. step1
+1. step2
+1. step3
+
+Expected result: <describe what should have happened>
+
+Actual result: <describe what actually happened>
+
+## Details
+
+<Include any other relevant details, logs, etc.>
diff --git a/grpc/.gitignore b/grpc/.gitignore
new file mode 100644
index 0000000..e851cc5
--- /dev/null
+++ b/grpc/.gitignore
@@ -0,0 +1,12 @@
+# Files and directories created by pub
+.dart_tool/
+.packages
+.pub/
+build/
+
+# Remove the following pattern if you wish to check in your lock file
+pubspec.lock
+
+# Directory created by dartdoc
+doc/api/
+
diff --git a/grpc/.travis.yml b/grpc/.travis.yml
new file mode 100644
index 0000000..24dde5c
--- /dev/null
+++ b/grpc/.travis.yml
@@ -0,0 +1,38 @@
+language: dart
+sudo: false
+
+# Run against both the dev and channel.
+dart:
+  - stable
+  - dev
+
+# Define test tasks to run.
+dart_task:
+  - test: --platform vm
+
+# Only run one instance of the formatter and the analyzer, rather than running
+# them against each Dart version.
+matrix:
+  include:
+  # Wish we could exclude `example` in `analysis_options.yaml` but it seems
+  # blocked by https://github.com/dart-lang/sdk/issues/26212
+  - dart: dev
+    dart_task: dartfmt
+    script:
+    - dartanalyzer lib test
+    - for example in example/*; do (cd $example; echo [Analyzing $example]; pub get; dartanalyzer .); done
+    - (cd interop; echo [Analyzing interop]; pub get; dartanalyzer .)
+
+
+# Only building master means that we don't run two builds for each pull request.
+branches:
+  only: [master]
+
+os:
+  - linux
+  - osx
+  - windows
+
+cache:
+  directories:
+    - $HOME/.pub-cache
diff --git a/grpc/AUTHORS b/grpc/AUTHORS
new file mode 100644
index 0000000..7a6111c
--- /dev/null
+++ b/grpc/AUTHORS
@@ -0,0 +1,8 @@
+# Below is a list of people and organizations that have contributed
+# to the project. Names should be added to the list like so:
+#
+#   Name/Organization <email address>
+
+Google Inc.
+German Saprykin <saprykin.h@gmail.com>
+Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
diff --git a/grpc/BUILD.gn b/grpc/BUILD.gn
new file mode 100644
index 0000000..96f2fa0
--- /dev/null
+++ b/grpc/BUILD.gn
@@ -0,0 +1,21 @@
+# This file is generated by importer.py for grpc-1.0.3
+
+import("//build/dart/dart_library.gni")
+
+dart_library("grpc") {
+  package_name = "grpc"
+
+  # This parameter is left empty as we don't care about analysis or exporting
+  # these sources outside of the tree.
+  sources = []
+
+  disable_analysis = true
+
+  deps = [
+    "//third_party/dart-pkg/pub/async",
+    "//third_party/dart-pkg/pub/meta",
+    "//third_party/dart-pkg/pub/googleapis_auth",
+    "//third_party/dart-pkg/pub/http",
+    "//third_party/dart-pkg/pub/http2",
+  ]
+}
diff --git a/grpc/CHANGELOG.md b/grpc/CHANGELOG.md
new file mode 100644
index 0000000..361870f
--- /dev/null
+++ b/grpc/CHANGELOG.md
@@ -0,0 +1,122 @@
+## 1.0.3
+
+* Allow custom user agent with a `userAgent` argument for `ChannelOptions()`.
+* Allow specifying `authority` for `ChannelCredentials.insecure()`.
+* Add `userAgent` as an optional named argument for `clientConnection.createCallHeaders()`.
+
+## 1.0.2
+
+* Fix bug where the server would crash if the client would break the connection.
+
+## 1.0.1
+
+* Add `service_api.dart` that only contains the minimal imports needed by the code generated by
+  protoc_plugin.
+
+## 1.0.0+1
+
+* Support package:http2  1.0.0.
+
+## 1.0.0
+
+* Graduate package to 1.0.
+
+## 0.6.8+1
+
+* Removes stray files that where published by accident in version 0.6.8.
+
+## 0.6.8
+
+* Calling `terminate()` or `shutdown()` on a channel doesn't throw error if the
+channel is not yet open.
+
+## 0.6.7
+
+* Support package:test 1.5.
+
+## 0.6.6
+
+* Support `package:http` `>=0.11.3+17 <0.13.0`.
+* Update `package:googleapis_auth` to `^0.2.5+3`.
+
+## 0.6.5
+
+* Interceptors are now async.
+
+## 0.6.4
+
+* Update dependencies to be compatible with Dart 2.
+
+## 0.6.3
+
+* Make fields of `StatusCode` const rather than final.
+
+## 0.6.2
+
+* Allow for non-ascii header values.
+
+## 0.6.1
+
+* More fixes to update to Dart 2 core library APIs.
+
+## 0.6.0+1
+
+* Updated implementation to use new Dart 2 APIs using
+[dart2_fix](https://github.com/dart-lang/dart2_fix).
+
+## 0.6.0
+
+* Dart SDK upper constraint raised to declare compatability with Dart 2.0 stable.
+
+## 0.5.0
+
+* Breaking change: The package now exclusively supports Dart 2.
+* Fixed tests to pass in Dart 2.
+* Added support for Interceptors ([issue #79](https://github.com/grpc/grpc-dart/issues/79)); thanks to [@mogol](https://github.com/mogol) for contributing!
+
+## 0.4.1
+
+* Fixes for supporting Dart 2.
+
+## 0.4.0
+
+* Moved TLS credentials for server into a separate class.
+* Added support for specifying the address for the server, and support for
+  serving on an ephemeral port.
+
+## 0.3.1
+
+* Split out TLS credentials to a separate class.
+
+## 0.3.0
+
+* Added authentication metadata providers, optimized for use with Google Cloud.
+* Added service URI to metadata provider API, needed for Json Web Token generation.
+* Added authenticated cloud-to-prod interoperability tests.
+* Refactored connection logic to throw initial connection errors early.
+
+## 0.2.1
+
+* Updated generated code in examples using latest protoc compiler plugin.
+* Dart 2.0 fixes.
+* Changed license to Apache 2.0.
+
+## 0.2.0
+
+* Implemented support for per-RPC metadata providers. This can be used for
+  authentication providers which may need to obtain or refresh a token before
+  the RPC is sent.
+
+## 0.1.0
+
+* Core gRPC functionality is implemented and passes
+[gRPC compliance tests](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md).
+
+The API is shaping up, but may still change as more advanced features are implemented.
+
+## 0.0.1
+
+* Initial version.
+
+This package is in a very early and experimental state. We do not recommend
+using it for anything but experiments.
diff --git a/grpc/CODE-OF-CONDUCT.md b/grpc/CODE-OF-CONDUCT.md
new file mode 100644
index 0000000..9d4213e
--- /dev/null
+++ b/grpc/CODE-OF-CONDUCT.md
@@ -0,0 +1,3 @@
+## Community Code of Conduct
+
+gRPC follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
diff --git a/grpc/CONTRIBUTING.md b/grpc/CONTRIBUTING.md
new file mode 100644
index 0000000..b87c68f
--- /dev/null
+++ b/grpc/CONTRIBUTING.md
@@ -0,0 +1,65 @@
+# How to contribute
+
+We definitely welcome your patches and contributions to gRPC!
+
+If you are new to github, please start by reading [Pull Request
+howto](https://help.github.com/articles/about-pull-requests/)
+
+## Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License
+Agreement](https://identity.linuxfoundation.org/projects/cncf).
+
+## Code style
+
+We follow the [Effective
+Dart](https://www.dartlang.org/guides/language/effective-dart/style) code style.
+We rely on auto-formatting all whitespace -- this avoids having to discuss
+formatting during reviews. To format your code, run `dartfmt` from the root, or
+use the similar action in your [favorite Dart
+editor](https://www.dartlang.org/tools).
+
+All code must pass Dart analysis. If you are using an IDE or Dart-enabled editor
+it should raise analysis issues as you edit; alternatively validate from the
+Terminal:
+
+```
+dartanalyzer lib test
+```
+
+All analysis warnings and errors must be fixed; hints should be considered.
+
+## Running tests
+
+```
+pub get
+pub run test
+```
+
+## Guidelines for Pull Requests
+
+How to get your contributions merged smoothly and quickly.
+ 
+- Create **small PRs** that are narrowly focused on **addressing a single
+concern**. 
+
+- For speculative changes, consider opening an issue and discussing it first.
+ 
+- Provide a good **PR description** as a record of **what** change is being made
+and **why** it was made. Link to a github issue if it exists.
+ 
+- Unless your PR is trivial, you should expect there will be review comments
+that you'll need to address before merging. We expect you to be reasonably
+responsive to those comments, otherwise the PR will be closed after 2-3 weeks of
+inactivity.
+
+- Keep your PR up to date with upstream/master (if there are merge conflicts, we
+can't really merge your change).
+
+- **All tests need to be passing** before your change can be merged. We
+recommend you **run tests locally** before creating your PR to catch breakages
+early on.
+ 
+- Exceptions to the rules can be made if there's a compelling reason for doing
+so.
diff --git a/grpc/LICENSE b/grpc/LICENSE
new file mode 100644
index 0000000..c11903c
--- /dev/null
+++ b/grpc/LICENSE
@@ -0,0 +1,176 @@
+  Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/grpc/README.md b/grpc/README.md
new file mode 100644
index 0000000..1700aa8
--- /dev/null
+++ b/grpc/README.md
@@ -0,0 +1,22 @@
+The [Dart](https://www.dart.dev/) implementation of
+[gRPC](https://grpc.io/): A high performance, open source, general RPC framework that puts mobile and HTTP/2 first.
+
+[![Build Status](https://travis-ci.org/grpc/grpc-dart.svg?branch=master)](https://travis-ci.org/grpc/grpc-dart)
+[![pub package](https://img.shields.io/pub/v/grpc.svg)](https://pub.dev/packages/grpc)
+
+# Usage
+
+See the [Dart gRPC Quickstart](https://grpc.io/docs/quickstart/dart.html).
+
+# Status
+
+If you experience issues, or if you have feature requests,
+please [open an issue](https://github.com/dart-lang/grpc-dart/issues).
+
+# Notes
+
+This library requires Dart SDK version 2.0 or later.
+
+It currently supports the the [Flutter](https://flutter.dev/) and
+[Dart native](https://dart.dev/platforms) platforms. The potential
+addition of gRPC-Web is tracked in [issue 43](https://github.com/grpc/grpc-dart/issues/43).
diff --git a/grpc/analysis_options.yaml b/grpc/analysis_options.yaml
new file mode 100644
index 0000000..b2d4046
--- /dev/null
+++ b/grpc/analysis_options.yaml
@@ -0,0 +1,16 @@
+# Lint rules and documentation, see http://dart-lang.github.io/linter/lints
+linter:
+  rules:
+    - avoid_init_to_null
+    - cancel_subscriptions
+    - close_sinks
+    - directives_ordering
+    - hash_and_equals
+    - iterable_contains_unrelated_type
+    - list_remove_unrelated_type
+    - prefer_final_fields
+    - prefer_final_locals
+    - prefer_is_not_empty
+    - test_types_in_equals
+    - unrelated_type_equality_checks
+    - valid_regexps
diff --git a/grpc/example/README.md b/grpc/example/README.md
new file mode 100644
index 0000000..963d131
--- /dev/null
+++ b/grpc/example/README.md
@@ -0,0 +1,15 @@
+Four code examples are available:
+
+1. [`/helloworld/`](https://github.com/grpc/grpc-dart/tree/master/example/helloworld):
+   A demonstration of using the Dart gRPC library to perform unary RPs.
+
+1. [`/googleapis/`](https://github.com/grpc/grpc-dart/tree/master/example/googleapis):
+   A demonstration of using the Dart gRPC library to communicate with Google APIs.
+
+1. [`/metadata/`](https://github.com/grpc/grpc-dart/tree/master/example/metadata):
+   A demonstration of how to handle custom metadata, cancellation, and timeouts in Dart gRPC.
+
+1. [`/route_guide/`](https://github.com/grpc/grpc-dart/tree/master/example/route_guide):
+   A demonstration of how to perform unary, client streaming, server streaming and full duplex RPCs.
+
+For a complete, step-wise working example, see the [Dart gRPC Quickstart](https://grpc.io/docs/quickstart/dart.html).
diff --git a/grpc/example/googleapis/.gitignore b/grpc/example/googleapis/.gitignore
new file mode 100644
index 0000000..f67f1c8
--- /dev/null
+++ b/grpc/example/googleapis/.gitignore
@@ -0,0 +1 @@
+logging-service-account.json
diff --git a/grpc/example/googleapis/README.md b/grpc/example/googleapis/README.md
new file mode 100644
index 0000000..d78b5af
--- /dev/null
+++ b/grpc/example/googleapis/README.md
@@ -0,0 +1,62 @@
+# Description
+The googleapis client demonstrates how to use Dart gRPC libraries to communicate
+with Google APIs.
+
+# Set up Google Cloud Platform project
+This example uses the Stackdriver Logging API. Please follow the documentation on
+[Stackdriver Logging Documentation](https://cloud.google.com/logging/docs/) to create
+a project and enable the logging API.
+
+Then follow the documentation to
+[create a service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount).
+This example uses the Logging/Logs Writer role.
+
+Create a new service key, download the JSON file for it, and save it as
+`logging-service-account.json`.
+
+# Run the sample code
+To run the example, assuming you are in the root of the googleapis folder, i.e.,
+.../example/googleapis/, first get the dependencies by running:
+
+```sh
+$ pub get
+```
+
+Then, to run the logging client sample:
+
+```sh
+$ pub run googleapis:logging
+```
+
+# Regenerate the stubs
+The Dart gRPC stubs and message classes are generated based on protobuf definition
+files from [googleapis/googleapis](https://github.com/googleapis/googleapis).
+
+To regenerate them, you will need to check out both
+[googleapis/googleapis](https://github.com/googleapis/googleapis) and
+[google/protobuf](https://github.com/google/protobuf).
+
+You will also need to have protoc version 3.0.0 or higher and the Dart protoc
+plugin version 0.7.9 or higher on your PATH.
+
+To install protoc, see the instructions on
+[the Protocol Buffers website](https://developers.google.com/protocol-buffers/).
+
+The easiest way to get the Dart protoc plugin is by running
+
+```sh
+$ pub global activate protoc_plugin
+```
+
+and follow the directions to add `~/.pub-cache/bin` to your PATH, if you haven't
+already done so.
+
+You can now regenerate the Dart files. Set the `PROTOBUF` and `GOOGLEAPIS`
+environment variables to point to your clone of
+[google/protobuf](https://github.com/google/protobuf) and
+[googleapis/googleapis](https://github.com/googleapis/googleapis), respectively,
+and then run
+
+```sh
+$ tool/regenerate.sh
+```
diff --git a/grpc/example/googleapis/bin/logging.dart b/grpc/example/googleapis/bin/logging.dart
new file mode 100644
index 0000000..f505713
--- /dev/null
+++ b/grpc/example/googleapis/bin/logging.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:grpc/grpc.dart';
+
+import 'package:googleapis/src/generated/google/api/monitored_resource.pb.dart';
+import 'package:googleapis/src/generated/google/logging/type/log_severity.pb.dart';
+import 'package:googleapis/src/generated/google/logging/v2/log_entry.pb.dart';
+import 'package:googleapis/src/generated/google/logging/v2/logging.pbgrpc.dart';
+
+Future<void> main() async {
+  final serviceAccountFile = new File('logging-service-account.json');
+  if (!serviceAccountFile.existsSync()) {
+    print('File logging-service-account.json not found. Please follow the '
+        'steps in README.md to create it.');
+    exit(-1);
+  }
+
+  final scopes = [
+    'https://www.googleapis.com/auth/cloud-platform',
+    'https://www.googleapis.com/auth/logging.write',
+  ];
+
+  final authenticator = new ServiceAccountAuthenticator(
+      serviceAccountFile.readAsStringSync(), scopes);
+  final projectId = authenticator.projectId;
+
+  final channel = new ClientChannel('logging.googleapis.com');
+  final logging =
+      new LoggingServiceV2Client(channel, options: authenticator.toCallOptions);
+
+  final request = new WriteLogEntriesRequest()
+    ..entries.add(new LogEntry()
+      ..logName = 'projects/$projectId/logs/example'
+      ..severity = LogSeverity.INFO
+      ..resource = (new MonitoredResource()..type = 'global')
+      ..textPayload = 'This is a log entry!');
+  await logging.writeLogEntries(request);
+
+  await channel.shutdown();
+}
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/label.pb.dart b/grpc/example/googleapis/lib/src/generated/google/api/label.pb.dart
new file mode 100644
index 0000000..de8c072
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/label.pb.dart
@@ -0,0 +1,78 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_label;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import 'label.pbenum.dart';
+
+export 'label.pbenum.dart';
+
+class LabelDescriptor extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('LabelDescriptor')
+    ..aOS(1, 'key')
+    ..e<LabelDescriptor_ValueType>(
+        2,
+        'valueType',
+        PbFieldType.OE,
+        LabelDescriptor_ValueType.STRING,
+        LabelDescriptor_ValueType.valueOf,
+        LabelDescriptor_ValueType.values)
+    ..aOS(3, 'description')
+    ..hasRequiredFields = false;
+
+  LabelDescriptor() : super();
+  LabelDescriptor.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  LabelDescriptor.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  LabelDescriptor clone() => new LabelDescriptor()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static LabelDescriptor create() => new LabelDescriptor();
+  static PbList<LabelDescriptor> createRepeated() =>
+      new PbList<LabelDescriptor>();
+  static LabelDescriptor getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyLabelDescriptor();
+    return _defaultInstance;
+  }
+
+  static LabelDescriptor _defaultInstance;
+  static void $checkItem(LabelDescriptor v) {
+    if (v is! LabelDescriptor) checkItemFailed(v, 'LabelDescriptor');
+  }
+
+  String get key => $_getS(0, '');
+  set key(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  LabelDescriptor_ValueType get valueType => $_getN(1);
+  set valueType(LabelDescriptor_ValueType v) {
+    setField(2, v);
+  }
+
+  bool hasValueType() => $_has(1);
+  void clearValueType() => clearField(2);
+
+  String get description => $_getS(2, '');
+  set description(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasDescription() => $_has(2);
+  void clearDescription() => clearField(3);
+}
+
+class _ReadonlyLabelDescriptor extends LabelDescriptor
+    with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/label.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/api/label.pbenum.dart
new file mode 100644
index 0000000..3badf8c
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/label.pbenum.dart
@@ -0,0 +1,35 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_label_pbenum;
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
+import 'dart:core' show int, dynamic, String, List, Map;
+import 'package:protobuf/protobuf.dart';
+
+class LabelDescriptor_ValueType extends ProtobufEnum {
+  static const LabelDescriptor_ValueType STRING =
+      const LabelDescriptor_ValueType._(0, 'STRING');
+  static const LabelDescriptor_ValueType BOOL =
+      const LabelDescriptor_ValueType._(1, 'BOOL');
+  static const LabelDescriptor_ValueType INT64 =
+      const LabelDescriptor_ValueType._(2, 'INT64');
+
+  static const List<LabelDescriptor_ValueType> values =
+      const <LabelDescriptor_ValueType>[
+    STRING,
+    BOOL,
+    INT64,
+  ];
+
+  static final Map<int, dynamic> _byValue = ProtobufEnum.initByValue(values);
+  static LabelDescriptor_ValueType valueOf(int value) =>
+      _byValue[value] as LabelDescriptor_ValueType;
+  static void $checkItem(LabelDescriptor_ValueType v) {
+    if (v is! LabelDescriptor_ValueType)
+      checkItemFailed(v, 'LabelDescriptor_ValueType');
+  }
+
+  const LabelDescriptor_ValueType._(int v, String n) : super(v, n);
+}
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/label.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/api/label.pbjson.dart
new file mode 100644
index 0000000..36cc915
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/label.pbjson.dart
@@ -0,0 +1,31 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_label_pbjson;
+
+const LabelDescriptor$json = const {
+  '1': 'LabelDescriptor',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
+    const {
+      '1': 'value_type',
+      '3': 2,
+      '4': 1,
+      '5': 14,
+      '6': '.google.api.LabelDescriptor.ValueType',
+      '10': 'valueType'
+    },
+    const {'1': 'description', '3': 3, '4': 1, '5': 9, '10': 'description'},
+  ],
+  '4': const [LabelDescriptor_ValueType$json],
+};
+
+const LabelDescriptor_ValueType$json = const {
+  '1': 'ValueType',
+  '2': const [
+    const {'1': 'STRING', '2': 0},
+    const {'1': 'BOOL', '2': 1},
+    const {'1': 'INT64', '2': 2},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pb.dart b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pb.dart
new file mode 100644
index 0000000..c75ee71
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pb.dart
@@ -0,0 +1,186 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_monitored_resource;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import 'label.pb.dart';
+
+class MonitoredResourceDescriptor extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('MonitoredResourceDescriptor')
+    ..aOS(1, 'type')
+    ..aOS(2, 'displayName')
+    ..aOS(3, 'description')
+    ..pp<LabelDescriptor>(4, 'labels', PbFieldType.PM,
+        LabelDescriptor.$checkItem, LabelDescriptor.create)
+    ..aOS(5, 'name')
+    ..hasRequiredFields = false;
+
+  MonitoredResourceDescriptor() : super();
+  MonitoredResourceDescriptor.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  MonitoredResourceDescriptor.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  MonitoredResourceDescriptor clone() =>
+      new MonitoredResourceDescriptor()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static MonitoredResourceDescriptor create() =>
+      new MonitoredResourceDescriptor();
+  static PbList<MonitoredResourceDescriptor> createRepeated() =>
+      new PbList<MonitoredResourceDescriptor>();
+  static MonitoredResourceDescriptor getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyMonitoredResourceDescriptor();
+    return _defaultInstance;
+  }
+
+  static MonitoredResourceDescriptor _defaultInstance;
+  static void $checkItem(MonitoredResourceDescriptor v) {
+    if (v is! MonitoredResourceDescriptor)
+      checkItemFailed(v, 'MonitoredResourceDescriptor');
+  }
+
+  String get type => $_getS(0, '');
+  set type(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasType() => $_has(0);
+  void clearType() => clearField(1);
+
+  String get displayName => $_getS(1, '');
+  set displayName(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasDisplayName() => $_has(1);
+  void clearDisplayName() => clearField(2);
+
+  String get description => $_getS(2, '');
+  set description(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasDescription() => $_has(2);
+  void clearDescription() => clearField(3);
+
+  List<LabelDescriptor> get labels => $_getList(3);
+
+  String get name => $_getS(4, '');
+  set name(String v) {
+    $_setString(4, v);
+  }
+
+  bool hasName() => $_has(4);
+  void clearName() => clearField(5);
+}
+
+class _ReadonlyMonitoredResourceDescriptor extends MonitoredResourceDescriptor
+    with ReadonlyMessageMixin {}
+
+class MonitoredResource_LabelsEntry extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('MonitoredResource_LabelsEntry')
+    ..aOS(1, 'key')
+    ..aOS(2, 'value')
+    ..hasRequiredFields = false;
+
+  MonitoredResource_LabelsEntry() : super();
+  MonitoredResource_LabelsEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  MonitoredResource_LabelsEntry.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  MonitoredResource_LabelsEntry clone() =>
+      new MonitoredResource_LabelsEntry()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static MonitoredResource_LabelsEntry create() =>
+      new MonitoredResource_LabelsEntry();
+  static PbList<MonitoredResource_LabelsEntry> createRepeated() =>
+      new PbList<MonitoredResource_LabelsEntry>();
+  static MonitoredResource_LabelsEntry getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyMonitoredResource_LabelsEntry();
+    return _defaultInstance;
+  }
+
+  static MonitoredResource_LabelsEntry _defaultInstance;
+  static void $checkItem(MonitoredResource_LabelsEntry v) {
+    if (v is! MonitoredResource_LabelsEntry)
+      checkItemFailed(v, 'MonitoredResource_LabelsEntry');
+  }
+
+  String get key => $_getS(0, '');
+  set key(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  String get value => $_getS(1, '');
+  set value(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyMonitoredResource_LabelsEntry
+    extends MonitoredResource_LabelsEntry with ReadonlyMessageMixin {}
+
+class MonitoredResource extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('MonitoredResource')
+    ..aOS(1, 'type')
+    ..pp<MonitoredResource_LabelsEntry>(
+        2,
+        'labels',
+        PbFieldType.PM,
+        MonitoredResource_LabelsEntry.$checkItem,
+        MonitoredResource_LabelsEntry.create)
+    ..hasRequiredFields = false;
+
+  MonitoredResource() : super();
+  MonitoredResource.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  MonitoredResource.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  MonitoredResource clone() => new MonitoredResource()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static MonitoredResource create() => new MonitoredResource();
+  static PbList<MonitoredResource> createRepeated() =>
+      new PbList<MonitoredResource>();
+  static MonitoredResource getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyMonitoredResource();
+    return _defaultInstance;
+  }
+
+  static MonitoredResource _defaultInstance;
+  static void $checkItem(MonitoredResource v) {
+    if (v is! MonitoredResource) checkItemFailed(v, 'MonitoredResource');
+  }
+
+  String get type => $_getS(0, '');
+  set type(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasType() => $_has(0);
+  void clearType() => clearField(1);
+
+  List<MonitoredResource_LabelsEntry> get labels => $_getList(1);
+}
+
+class _ReadonlyMonitoredResource extends MonitoredResource
+    with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbenum.dart
new file mode 100644
index 0000000..ab8bdc9
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_monitored_resource_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbjson.dart
new file mode 100644
index 0000000..746965b
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/api/monitored_resource.pbjson.dart
@@ -0,0 +1,48 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.api_monitored_resource_pbjson;
+
+const MonitoredResourceDescriptor$json = const {
+  '1': 'MonitoredResourceDescriptor',
+  '2': const [
+    const {'1': 'name', '3': 5, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'type', '3': 1, '4': 1, '5': 9, '10': 'type'},
+    const {'1': 'display_name', '3': 2, '4': 1, '5': 9, '10': 'displayName'},
+    const {'1': 'description', '3': 3, '4': 1, '5': 9, '10': 'description'},
+    const {
+      '1': 'labels',
+      '3': 4,
+      '4': 3,
+      '5': 11,
+      '6': '.google.api.LabelDescriptor',
+      '10': 'labels'
+    },
+  ],
+};
+
+const MonitoredResource$json = const {
+  '1': 'MonitoredResource',
+  '2': const [
+    const {'1': 'type', '3': 1, '4': 1, '5': 9, '10': 'type'},
+    const {
+      '1': 'labels',
+      '3': 2,
+      '4': 3,
+      '5': 11,
+      '6': '.google.api.MonitoredResource.LabelsEntry',
+      '10': 'labels'
+    },
+  ],
+  '3': const [MonitoredResource_LabelsEntry$json],
+};
+
+const MonitoredResource_LabelsEntry$json = const {
+  '1': 'LabelsEntry',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
+    const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
+  ],
+  '7': const {'7': true},
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pb.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pb.dart
new file mode 100644
index 0000000..b90c01a
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pb.dart
@@ -0,0 +1,177 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_http_request;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:fixnum/fixnum.dart';
+import 'package:protobuf/protobuf.dart';
+
+import '../../protobuf/duration.pb.dart' as $google$protobuf;
+
+class HttpRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('HttpRequest')
+    ..aOS(1, 'requestMethod')
+    ..aOS(2, 'requestUrl')
+    ..aInt64(3, 'requestSize')
+    ..a<int>(4, 'status', PbFieldType.O3)
+    ..aInt64(5, 'responseSize')
+    ..aOS(6, 'userAgent')
+    ..aOS(7, 'remoteIp')
+    ..aOS(8, 'referer')
+    ..aOB(9, 'cacheHit')
+    ..aOB(10, 'cacheValidatedWithOriginServer')
+    ..aOB(11, 'cacheLookup')
+    ..aInt64(12, 'cacheFillBytes')
+    ..aOS(13, 'serverIp')
+    ..a<$google$protobuf.Duration>(14, 'latency', PbFieldType.OM,
+        $google$protobuf.Duration.getDefault, $google$protobuf.Duration.create)
+    ..aOS(15, 'protocol')
+    ..hasRequiredFields = false;
+
+  HttpRequest() : super();
+  HttpRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  HttpRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  HttpRequest clone() => new HttpRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static HttpRequest create() => new HttpRequest();
+  static PbList<HttpRequest> createRepeated() => new PbList<HttpRequest>();
+  static HttpRequest getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyHttpRequest();
+    return _defaultInstance;
+  }
+
+  static HttpRequest _defaultInstance;
+  static void $checkItem(HttpRequest v) {
+    if (v is! HttpRequest) checkItemFailed(v, 'HttpRequest');
+  }
+
+  String get requestMethod => $_getS(0, '');
+  set requestMethod(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasRequestMethod() => $_has(0);
+  void clearRequestMethod() => clearField(1);
+
+  String get requestUrl => $_getS(1, '');
+  set requestUrl(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasRequestUrl() => $_has(1);
+  void clearRequestUrl() => clearField(2);
+
+  Int64 get requestSize => $_getI64(2);
+  set requestSize(Int64 v) {
+    $_setInt64(2, v);
+  }
+
+  bool hasRequestSize() => $_has(2);
+  void clearRequestSize() => clearField(3);
+
+  int get status => $_get(3, 0);
+  set status(int v) {
+    $_setUnsignedInt32(3, v);
+  }
+
+  bool hasStatus() => $_has(3);
+  void clearStatus() => clearField(4);
+
+  Int64 get responseSize => $_getI64(4);
+  set responseSize(Int64 v) {
+    $_setInt64(4, v);
+  }
+
+  bool hasResponseSize() => $_has(4);
+  void clearResponseSize() => clearField(5);
+
+  String get userAgent => $_getS(5, '');
+  set userAgent(String v) {
+    $_setString(5, v);
+  }
+
+  bool hasUserAgent() => $_has(5);
+  void clearUserAgent() => clearField(6);
+
+  String get remoteIp => $_getS(6, '');
+  set remoteIp(String v) {
+    $_setString(6, v);
+  }
+
+  bool hasRemoteIp() => $_has(6);
+  void clearRemoteIp() => clearField(7);
+
+  String get referer => $_getS(7, '');
+  set referer(String v) {
+    $_setString(7, v);
+  }
+
+  bool hasReferer() => $_has(7);
+  void clearReferer() => clearField(8);
+
+  bool get cacheHit => $_get(8, false);
+  set cacheHit(bool v) {
+    $_setBool(8, v);
+  }
+
+  bool hasCacheHit() => $_has(8);
+  void clearCacheHit() => clearField(9);
+
+  bool get cacheValidatedWithOriginServer => $_get(9, false);
+  set cacheValidatedWithOriginServer(bool v) {
+    $_setBool(9, v);
+  }
+
+  bool hasCacheValidatedWithOriginServer() => $_has(9);
+  void clearCacheValidatedWithOriginServer() => clearField(10);
+
+  bool get cacheLookup => $_get(10, false);
+  set cacheLookup(bool v) {
+    $_setBool(10, v);
+  }
+
+  bool hasCacheLookup() => $_has(10);
+  void clearCacheLookup() => clearField(11);
+
+  Int64 get cacheFillBytes => $_getI64(11);
+  set cacheFillBytes(Int64 v) {
+    $_setInt64(11, v);
+  }
+
+  bool hasCacheFillBytes() => $_has(11);
+  void clearCacheFillBytes() => clearField(12);
+
+  String get serverIp => $_getS(12, '');
+  set serverIp(String v) {
+    $_setString(12, v);
+  }
+
+  bool hasServerIp() => $_has(12);
+  void clearServerIp() => clearField(13);
+
+  $google$protobuf.Duration get latency => $_getN(13);
+  set latency($google$protobuf.Duration v) {
+    setField(14, v);
+  }
+
+  bool hasLatency() => $_has(13);
+  void clearLatency() => clearField(14);
+
+  String get protocol => $_getS(14, '');
+  set protocol(String v) {
+    $_setString(14, v);
+  }
+
+  bool hasProtocol() => $_has(14);
+  void clearProtocol() => clearField(15);
+}
+
+class _ReadonlyHttpRequest extends HttpRequest with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbenum.dart
new file mode 100644
index 0000000..cb196dc
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_http_request_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbjson.dart
new file mode 100644
index 0000000..b9c176c
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/http_request.pbjson.dart
@@ -0,0 +1,51 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_http_request_pbjson;
+
+const HttpRequest$json = const {
+  '1': 'HttpRequest',
+  '2': const [
+    const {
+      '1': 'request_method',
+      '3': 1,
+      '4': 1,
+      '5': 9,
+      '10': 'requestMethod'
+    },
+    const {'1': 'request_url', '3': 2, '4': 1, '5': 9, '10': 'requestUrl'},
+    const {'1': 'request_size', '3': 3, '4': 1, '5': 3, '10': 'requestSize'},
+    const {'1': 'status', '3': 4, '4': 1, '5': 5, '10': 'status'},
+    const {'1': 'response_size', '3': 5, '4': 1, '5': 3, '10': 'responseSize'},
+    const {'1': 'user_agent', '3': 6, '4': 1, '5': 9, '10': 'userAgent'},
+    const {'1': 'remote_ip', '3': 7, '4': 1, '5': 9, '10': 'remoteIp'},
+    const {'1': 'server_ip', '3': 13, '4': 1, '5': 9, '10': 'serverIp'},
+    const {'1': 'referer', '3': 8, '4': 1, '5': 9, '10': 'referer'},
+    const {
+      '1': 'latency',
+      '3': 14,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Duration',
+      '10': 'latency'
+    },
+    const {'1': 'cache_lookup', '3': 11, '4': 1, '5': 8, '10': 'cacheLookup'},
+    const {'1': 'cache_hit', '3': 9, '4': 1, '5': 8, '10': 'cacheHit'},
+    const {
+      '1': 'cache_validated_with_origin_server',
+      '3': 10,
+      '4': 1,
+      '5': 8,
+      '10': 'cacheValidatedWithOriginServer'
+    },
+    const {
+      '1': 'cache_fill_bytes',
+      '3': 12,
+      '4': 1,
+      '5': 3,
+      '10': 'cacheFillBytes'
+    },
+    const {'1': 'protocol', '3': 15, '4': 1, '5': 9, '10': 'protocol'},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pb.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pb.dart
new file mode 100644
index 0000000..e215075
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pb.dart
@@ -0,0 +1,10 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_log_severity;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+export 'log_severity.pbenum.dart';
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbenum.dart
new file mode 100644
index 0000000..1141dd7
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbenum.dart
@@ -0,0 +1,41 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_log_severity_pbenum;
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
+import 'dart:core' show int, dynamic, String, List, Map;
+import 'package:protobuf/protobuf.dart';
+
+class LogSeverity extends ProtobufEnum {
+  static const LogSeverity DEFAULT = const LogSeverity._(0, 'DEFAULT');
+  static const LogSeverity DEBUG = const LogSeverity._(100, 'DEBUG');
+  static const LogSeverity INFO = const LogSeverity._(200, 'INFO');
+  static const LogSeverity NOTICE = const LogSeverity._(300, 'NOTICE');
+  static const LogSeverity WARNING = const LogSeverity._(400, 'WARNING');
+  static const LogSeverity ERROR = const LogSeverity._(500, 'ERROR');
+  static const LogSeverity CRITICAL = const LogSeverity._(600, 'CRITICAL');
+  static const LogSeverity ALERT = const LogSeverity._(700, 'ALERT');
+  static const LogSeverity EMERGENCY = const LogSeverity._(800, 'EMERGENCY');
+
+  static const List<LogSeverity> values = const <LogSeverity>[
+    DEFAULT,
+    DEBUG,
+    INFO,
+    NOTICE,
+    WARNING,
+    ERROR,
+    CRITICAL,
+    ALERT,
+    EMERGENCY,
+  ];
+
+  static final Map<int, dynamic> _byValue = ProtobufEnum.initByValue(values);
+  static LogSeverity valueOf(int value) => _byValue[value] as LogSeverity;
+  static void $checkItem(LogSeverity v) {
+    if (v is! LogSeverity) checkItemFailed(v, 'LogSeverity');
+  }
+
+  const LogSeverity._(int v, String n) : super(v, n);
+}
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbjson.dart
new file mode 100644
index 0000000..45b2646
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/type/log_severity.pbjson.dart
@@ -0,0 +1,20 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.type_log_severity_pbjson;
+
+const LogSeverity$json = const {
+  '1': 'LogSeverity',
+  '2': const [
+    const {'1': 'DEFAULT', '2': 0},
+    const {'1': 'DEBUG', '2': 100},
+    const {'1': 'INFO', '2': 200},
+    const {'1': 'NOTICE', '2': 300},
+    const {'1': 'WARNING', '2': 400},
+    const {'1': 'ERROR', '2': 500},
+    const {'1': 'CRITICAL', '2': 600},
+    const {'1': 'ALERT', '2': 700},
+    const {'1': 'EMERGENCY', '2': 800},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pb.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pb.dart
new file mode 100644
index 0000000..9b42389
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pb.dart
@@ -0,0 +1,383 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_log_entry;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:fixnum/fixnum.dart';
+import 'package:protobuf/protobuf.dart';
+
+import '../../protobuf/any.pb.dart' as $google$protobuf;
+import '../../protobuf/struct.pb.dart' as $google$protobuf;
+import '../type/http_request.pb.dart' as $google$logging$type;
+import '../../api/monitored_resource.pb.dart' as $google$api;
+import '../../protobuf/timestamp.pb.dart' as $google$protobuf;
+
+import '../type/log_severity.pbenum.dart' as $google$logging$type;
+
+class LogEntry_LabelsEntry extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('LogEntry_LabelsEntry')
+    ..aOS(1, 'key')
+    ..aOS(2, 'value')
+    ..hasRequiredFields = false;
+
+  LogEntry_LabelsEntry() : super();
+  LogEntry_LabelsEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  LogEntry_LabelsEntry.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  LogEntry_LabelsEntry clone() =>
+      new LogEntry_LabelsEntry()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static LogEntry_LabelsEntry create() => new LogEntry_LabelsEntry();
+  static PbList<LogEntry_LabelsEntry> createRepeated() =>
+      new PbList<LogEntry_LabelsEntry>();
+  static LogEntry_LabelsEntry getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyLogEntry_LabelsEntry();
+    return _defaultInstance;
+  }
+
+  static LogEntry_LabelsEntry _defaultInstance;
+  static void $checkItem(LogEntry_LabelsEntry v) {
+    if (v is! LogEntry_LabelsEntry) checkItemFailed(v, 'LogEntry_LabelsEntry');
+  }
+
+  String get key => $_getS(0, '');
+  set key(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  String get value => $_getS(1, '');
+  set value(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyLogEntry_LabelsEntry extends LogEntry_LabelsEntry
+    with ReadonlyMessageMixin {}
+
+class LogEntry extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('LogEntry')
+    ..a<$google$protobuf.Any>(2, 'protoPayload', PbFieldType.OM,
+        $google$protobuf.Any.getDefault, $google$protobuf.Any.create)
+    ..aOS(3, 'textPayload')
+    ..aOS(4, 'insertId')
+    ..a<$google$protobuf.Struct>(6, 'jsonPayload', PbFieldType.OM,
+        $google$protobuf.Struct.getDefault, $google$protobuf.Struct.create)
+    ..a<$google$logging$type.HttpRequest>(
+        7,
+        'httpRequest',
+        PbFieldType.OM,
+        $google$logging$type.HttpRequest.getDefault,
+        $google$logging$type.HttpRequest.create)
+    ..a<$google$api.MonitoredResource>(
+        8,
+        'resource',
+        PbFieldType.OM,
+        $google$api.MonitoredResource.getDefault,
+        $google$api.MonitoredResource.create)
+    ..a<$google$protobuf.Timestamp>(
+        9,
+        'timestamp',
+        PbFieldType.OM,
+        $google$protobuf.Timestamp.getDefault,
+        $google$protobuf.Timestamp.create)
+    ..e<$google$logging$type.LogSeverity>(
+        10,
+        'severity',
+        PbFieldType.OE,
+        $google$logging$type.LogSeverity.DEFAULT,
+        $google$logging$type.LogSeverity.valueOf,
+        $google$logging$type.LogSeverity.values)
+    ..pp<LogEntry_LabelsEntry>(11, 'labels', PbFieldType.PM,
+        LogEntry_LabelsEntry.$checkItem, LogEntry_LabelsEntry.create)
+    ..aOS(12, 'logName')
+    ..a<LogEntryOperation>(15, 'operation', PbFieldType.OM,
+        LogEntryOperation.getDefault, LogEntryOperation.create)
+    ..aOS(22, 'trace')
+    ..a<LogEntrySourceLocation>(23, 'sourceLocation', PbFieldType.OM,
+        LogEntrySourceLocation.getDefault, LogEntrySourceLocation.create)
+    ..a<$google$protobuf.Timestamp>(
+        24,
+        'receiveTimestamp',
+        PbFieldType.OM,
+        $google$protobuf.Timestamp.getDefault,
+        $google$protobuf.Timestamp.create)
+    ..aOS(27, 'spanId')
+    ..hasRequiredFields = false;
+
+  LogEntry() : super();
+  LogEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  LogEntry.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  LogEntry clone() => new LogEntry()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static LogEntry create() => new LogEntry();
+  static PbList<LogEntry> createRepeated() => new PbList<LogEntry>();
+  static LogEntry getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyLogEntry();
+    return _defaultInstance;
+  }
+
+  static LogEntry _defaultInstance;
+  static void $checkItem(LogEntry v) {
+    if (v is! LogEntry) checkItemFailed(v, 'LogEntry');
+  }
+
+  $google$protobuf.Any get protoPayload => $_getN(0);
+  set protoPayload($google$protobuf.Any v) {
+    setField(2, v);
+  }
+
+  bool hasProtoPayload() => $_has(0);
+  void clearProtoPayload() => clearField(2);
+
+  String get textPayload => $_getS(1, '');
+  set textPayload(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasTextPayload() => $_has(1);
+  void clearTextPayload() => clearField(3);
+
+  String get insertId => $_getS(2, '');
+  set insertId(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasInsertId() => $_has(2);
+  void clearInsertId() => clearField(4);
+
+  $google$protobuf.Struct get jsonPayload => $_getN(3);
+  set jsonPayload($google$protobuf.Struct v) {
+    setField(6, v);
+  }
+
+  bool hasJsonPayload() => $_has(3);
+  void clearJsonPayload() => clearField(6);
+
+  $google$logging$type.HttpRequest get httpRequest => $_getN(4);
+  set httpRequest($google$logging$type.HttpRequest v) {
+    setField(7, v);
+  }
+
+  bool hasHttpRequest() => $_has(4);
+  void clearHttpRequest() => clearField(7);
+
+  $google$api.MonitoredResource get resource => $_getN(5);
+  set resource($google$api.MonitoredResource v) {
+    setField(8, v);
+  }
+
+  bool hasResource() => $_has(5);
+  void clearResource() => clearField(8);
+
+  $google$protobuf.Timestamp get timestamp => $_getN(6);
+  set timestamp($google$protobuf.Timestamp v) {
+    setField(9, v);
+  }
+
+  bool hasTimestamp() => $_has(6);
+  void clearTimestamp() => clearField(9);
+
+  $google$logging$type.LogSeverity get severity => $_getN(7);
+  set severity($google$logging$type.LogSeverity v) {
+    setField(10, v);
+  }
+
+  bool hasSeverity() => $_has(7);
+  void clearSeverity() => clearField(10);
+
+  List<LogEntry_LabelsEntry> get labels => $_getList(8);
+
+  String get logName => $_getS(9, '');
+  set logName(String v) {
+    $_setString(9, v);
+  }
+
+  bool hasLogName() => $_has(9);
+  void clearLogName() => clearField(12);
+
+  LogEntryOperation get operation => $_getN(10);
+  set operation(LogEntryOperation v) {
+    setField(15, v);
+  }
+
+  bool hasOperation() => $_has(10);
+  void clearOperation() => clearField(15);
+
+  String get trace => $_getS(11, '');
+  set trace(String v) {
+    $_setString(11, v);
+  }
+
+  bool hasTrace() => $_has(11);
+  void clearTrace() => clearField(22);
+
+  LogEntrySourceLocation get sourceLocation => $_getN(12);
+  set sourceLocation(LogEntrySourceLocation v) {
+    setField(23, v);
+  }
+
+  bool hasSourceLocation() => $_has(12);
+  void clearSourceLocation() => clearField(23);
+
+  $google$protobuf.Timestamp get receiveTimestamp => $_getN(13);
+  set receiveTimestamp($google$protobuf.Timestamp v) {
+    setField(24, v);
+  }
+
+  bool hasReceiveTimestamp() => $_has(13);
+  void clearReceiveTimestamp() => clearField(24);
+
+  String get spanId => $_getS(14, '');
+  set spanId(String v) {
+    $_setString(14, v);
+  }
+
+  bool hasSpanId() => $_has(14);
+  void clearSpanId() => clearField(27);
+}
+
+class _ReadonlyLogEntry extends LogEntry with ReadonlyMessageMixin {}
+
+class LogEntryOperation extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('LogEntryOperation')
+    ..aOS(1, 'id')
+    ..aOS(2, 'producer')
+    ..aOB(3, 'first')
+    ..aOB(4, 'last')
+    ..hasRequiredFields = false;
+
+  LogEntryOperation() : super();
+  LogEntryOperation.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  LogEntryOperation.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  LogEntryOperation clone() => new LogEntryOperation()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static LogEntryOperation create() => new LogEntryOperation();
+  static PbList<LogEntryOperation> createRepeated() =>
+      new PbList<LogEntryOperation>();
+  static LogEntryOperation getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyLogEntryOperation();
+    return _defaultInstance;
+  }
+
+  static LogEntryOperation _defaultInstance;
+  static void $checkItem(LogEntryOperation v) {
+    if (v is! LogEntryOperation) checkItemFailed(v, 'LogEntryOperation');
+  }
+
+  String get id => $_getS(0, '');
+  set id(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasId() => $_has(0);
+  void clearId() => clearField(1);
+
+  String get producer => $_getS(1, '');
+  set producer(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasProducer() => $_has(1);
+  void clearProducer() => clearField(2);
+
+  bool get first => $_get(2, false);
+  set first(bool v) {
+    $_setBool(2, v);
+  }
+
+  bool hasFirst() => $_has(2);
+  void clearFirst() => clearField(3);
+
+  bool get last => $_get(3, false);
+  set last(bool v) {
+    $_setBool(3, v);
+  }
+
+  bool hasLast() => $_has(3);
+  void clearLast() => clearField(4);
+}
+
+class _ReadonlyLogEntryOperation extends LogEntryOperation
+    with ReadonlyMessageMixin {}
+
+class LogEntrySourceLocation extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('LogEntrySourceLocation')
+    ..aOS(1, 'file')
+    ..aInt64(2, 'line')
+    ..aOS(3, 'function')
+    ..hasRequiredFields = false;
+
+  LogEntrySourceLocation() : super();
+  LogEntrySourceLocation.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  LogEntrySourceLocation.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  LogEntrySourceLocation clone() =>
+      new LogEntrySourceLocation()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static LogEntrySourceLocation create() => new LogEntrySourceLocation();
+  static PbList<LogEntrySourceLocation> createRepeated() =>
+      new PbList<LogEntrySourceLocation>();
+  static LogEntrySourceLocation getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyLogEntrySourceLocation();
+    return _defaultInstance;
+  }
+
+  static LogEntrySourceLocation _defaultInstance;
+  static void $checkItem(LogEntrySourceLocation v) {
+    if (v is! LogEntrySourceLocation)
+      checkItemFailed(v, 'LogEntrySourceLocation');
+  }
+
+  String get file => $_getS(0, '');
+  set file(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasFile() => $_has(0);
+  void clearFile() => clearField(1);
+
+  Int64 get line => $_getI64(1);
+  set line(Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  bool hasLine() => $_has(1);
+  void clearLine() => clearField(2);
+
+  String get function => $_getS(2, '');
+  set function(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasFunction() => $_has(2);
+  void clearFunction() => clearField(3);
+}
+
+class _ReadonlyLogEntrySourceLocation extends LogEntrySourceLocation
+    with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbenum.dart
new file mode 100644
index 0000000..e8f1a7f
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_log_entry_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbjson.dart
new file mode 100644
index 0000000..117d400
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/log_entry.pbjson.dart
@@ -0,0 +1,137 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_log_entry_pbjson;
+
+const LogEntry$json = const {
+  '1': 'LogEntry',
+  '2': const [
+    const {'1': 'log_name', '3': 12, '4': 1, '5': 9, '10': 'logName'},
+    const {
+      '1': 'resource',
+      '3': 8,
+      '4': 1,
+      '5': 11,
+      '6': '.google.api.MonitoredResource',
+      '10': 'resource'
+    },
+    const {
+      '1': 'proto_payload',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Any',
+      '9': 0,
+      '10': 'protoPayload'
+    },
+    const {
+      '1': 'text_payload',
+      '3': 3,
+      '4': 1,
+      '5': 9,
+      '9': 0,
+      '10': 'textPayload'
+    },
+    const {
+      '1': 'json_payload',
+      '3': 6,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Struct',
+      '9': 0,
+      '10': 'jsonPayload'
+    },
+    const {
+      '1': 'timestamp',
+      '3': 9,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Timestamp',
+      '10': 'timestamp'
+    },
+    const {
+      '1': 'receive_timestamp',
+      '3': 24,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Timestamp',
+      '10': 'receiveTimestamp'
+    },
+    const {
+      '1': 'severity',
+      '3': 10,
+      '4': 1,
+      '5': 14,
+      '6': '.google.logging.type.LogSeverity',
+      '10': 'severity'
+    },
+    const {'1': 'insert_id', '3': 4, '4': 1, '5': 9, '10': 'insertId'},
+    const {
+      '1': 'http_request',
+      '3': 7,
+      '4': 1,
+      '5': 11,
+      '6': '.google.logging.type.HttpRequest',
+      '10': 'httpRequest'
+    },
+    const {
+      '1': 'labels',
+      '3': 11,
+      '4': 3,
+      '5': 11,
+      '6': '.google.logging.v2.LogEntry.LabelsEntry',
+      '10': 'labels'
+    },
+    const {
+      '1': 'operation',
+      '3': 15,
+      '4': 1,
+      '5': 11,
+      '6': '.google.logging.v2.LogEntryOperation',
+      '10': 'operation'
+    },
+    const {'1': 'trace', '3': 22, '4': 1, '5': 9, '10': 'trace'},
+    const {'1': 'span_id', '3': 27, '4': 1, '5': 9, '10': 'spanId'},
+    const {
+      '1': 'source_location',
+      '3': 23,
+      '4': 1,
+      '5': 11,
+      '6': '.google.logging.v2.LogEntrySourceLocation',
+      '10': 'sourceLocation'
+    },
+  ],
+  '3': const [LogEntry_LabelsEntry$json],
+  '8': const [
+    const {'1': 'payload'},
+  ],
+};
+
+const LogEntry_LabelsEntry$json = const {
+  '1': 'LabelsEntry',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
+    const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
+  ],
+  '7': const {'7': true},
+};
+
+const LogEntryOperation$json = const {
+  '1': 'LogEntryOperation',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'producer', '3': 2, '4': 1, '5': 9, '10': 'producer'},
+    const {'1': 'first', '3': 3, '4': 1, '5': 8, '10': 'first'},
+    const {'1': 'last', '3': 4, '4': 1, '5': 8, '10': 'last'},
+  ],
+};
+
+const LogEntrySourceLocation$json = const {
+  '1': 'LogEntrySourceLocation',
+  '2': const [
+    const {'1': 'file', '3': 1, '4': 1, '5': 9, '10': 'file'},
+    const {'1': 'line', '3': 2, '4': 1, '5': 3, '10': 'line'},
+    const {'1': 'function', '3': 3, '4': 1, '5': 9, '10': 'function'},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pb.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pb.dart
new file mode 100644
index 0000000..7156969
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pb.dart
@@ -0,0 +1,649 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_logging;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import '../../api/monitored_resource.pb.dart' as $google$api;
+import 'log_entry.pb.dart';
+import '../../rpc/status.pb.dart' as $google$rpc;
+
+class DeleteLogRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('DeleteLogRequest')
+    ..aOS(1, 'logName')
+    ..hasRequiredFields = false;
+
+  DeleteLogRequest() : super();
+  DeleteLogRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  DeleteLogRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  DeleteLogRequest clone() => new DeleteLogRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static DeleteLogRequest create() => new DeleteLogRequest();
+  static PbList<DeleteLogRequest> createRepeated() =>
+      new PbList<DeleteLogRequest>();
+  static DeleteLogRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyDeleteLogRequest();
+    return _defaultInstance;
+  }
+
+  static DeleteLogRequest _defaultInstance;
+  static void $checkItem(DeleteLogRequest v) {
+    if (v is! DeleteLogRequest) checkItemFailed(v, 'DeleteLogRequest');
+  }
+
+  String get logName => $_getS(0, '');
+  set logName(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasLogName() => $_has(0);
+  void clearLogName() => clearField(1);
+}
+
+class _ReadonlyDeleteLogRequest extends DeleteLogRequest
+    with ReadonlyMessageMixin {}
+
+class WriteLogEntriesRequest_LabelsEntry extends GeneratedMessage {
+  static final BuilderInfo _i =
+      new BuilderInfo('WriteLogEntriesRequest_LabelsEntry')
+        ..aOS(1, 'key')
+        ..aOS(2, 'value')
+        ..hasRequiredFields = false;
+
+  WriteLogEntriesRequest_LabelsEntry() : super();
+  WriteLogEntriesRequest_LabelsEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  WriteLogEntriesRequest_LabelsEntry.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  WriteLogEntriesRequest_LabelsEntry clone() =>
+      new WriteLogEntriesRequest_LabelsEntry()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static WriteLogEntriesRequest_LabelsEntry create() =>
+      new WriteLogEntriesRequest_LabelsEntry();
+  static PbList<WriteLogEntriesRequest_LabelsEntry> createRepeated() =>
+      new PbList<WriteLogEntriesRequest_LabelsEntry>();
+  static WriteLogEntriesRequest_LabelsEntry getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyWriteLogEntriesRequest_LabelsEntry();
+    return _defaultInstance;
+  }
+
+  static WriteLogEntriesRequest_LabelsEntry _defaultInstance;
+  static void $checkItem(WriteLogEntriesRequest_LabelsEntry v) {
+    if (v is! WriteLogEntriesRequest_LabelsEntry)
+      checkItemFailed(v, 'WriteLogEntriesRequest_LabelsEntry');
+  }
+
+  String get key => $_getS(0, '');
+  set key(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  String get value => $_getS(1, '');
+  set value(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyWriteLogEntriesRequest_LabelsEntry
+    extends WriteLogEntriesRequest_LabelsEntry with ReadonlyMessageMixin {}
+
+class WriteLogEntriesRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('WriteLogEntriesRequest')
+    ..aOS(1, 'logName')
+    ..a<$google$api.MonitoredResource>(
+        2,
+        'resource',
+        PbFieldType.OM,
+        $google$api.MonitoredResource.getDefault,
+        $google$api.MonitoredResource.create)
+    ..pp<WriteLogEntriesRequest_LabelsEntry>(
+        3,
+        'labels',
+        PbFieldType.PM,
+        WriteLogEntriesRequest_LabelsEntry.$checkItem,
+        WriteLogEntriesRequest_LabelsEntry.create)
+    ..pp<LogEntry>(
+        4, 'entries', PbFieldType.PM, LogEntry.$checkItem, LogEntry.create)
+    ..aOB(5, 'partialSuccess')
+    ..hasRequiredFields = false;
+
+  WriteLogEntriesRequest() : super();
+  WriteLogEntriesRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  WriteLogEntriesRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  WriteLogEntriesRequest clone() =>
+      new WriteLogEntriesRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static WriteLogEntriesRequest create() => new WriteLogEntriesRequest();
+  static PbList<WriteLogEntriesRequest> createRepeated() =>
+      new PbList<WriteLogEntriesRequest>();
+  static WriteLogEntriesRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyWriteLogEntriesRequest();
+    return _defaultInstance;
+  }
+
+  static WriteLogEntriesRequest _defaultInstance;
+  static void $checkItem(WriteLogEntriesRequest v) {
+    if (v is! WriteLogEntriesRequest)
+      checkItemFailed(v, 'WriteLogEntriesRequest');
+  }
+
+  String get logName => $_getS(0, '');
+  set logName(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasLogName() => $_has(0);
+  void clearLogName() => clearField(1);
+
+  $google$api.MonitoredResource get resource => $_getN(1);
+  set resource($google$api.MonitoredResource v) {
+    setField(2, v);
+  }
+
+  bool hasResource() => $_has(1);
+  void clearResource() => clearField(2);
+
+  List<WriteLogEntriesRequest_LabelsEntry> get labels => $_getList(2);
+
+  List<LogEntry> get entries => $_getList(3);
+
+  bool get partialSuccess => $_get(4, false);
+  set partialSuccess(bool v) {
+    $_setBool(4, v);
+  }
+
+  bool hasPartialSuccess() => $_has(4);
+  void clearPartialSuccess() => clearField(5);
+}
+
+class _ReadonlyWriteLogEntriesRequest extends WriteLogEntriesRequest
+    with ReadonlyMessageMixin {}
+
+class WriteLogEntriesResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('WriteLogEntriesResponse')
+    ..hasRequiredFields = false;
+
+  WriteLogEntriesResponse() : super();
+  WriteLogEntriesResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  WriteLogEntriesResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  WriteLogEntriesResponse clone() =>
+      new WriteLogEntriesResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static WriteLogEntriesResponse create() => new WriteLogEntriesResponse();
+  static PbList<WriteLogEntriesResponse> createRepeated() =>
+      new PbList<WriteLogEntriesResponse>();
+  static WriteLogEntriesResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyWriteLogEntriesResponse();
+    return _defaultInstance;
+  }
+
+  static WriteLogEntriesResponse _defaultInstance;
+  static void $checkItem(WriteLogEntriesResponse v) {
+    if (v is! WriteLogEntriesResponse)
+      checkItemFailed(v, 'WriteLogEntriesResponse');
+  }
+}
+
+class _ReadonlyWriteLogEntriesResponse extends WriteLogEntriesResponse
+    with ReadonlyMessageMixin {}
+
+class WriteLogEntriesPartialErrors_LogEntryErrorsEntry
+    extends GeneratedMessage {
+  static final BuilderInfo _i =
+      new BuilderInfo('WriteLogEntriesPartialErrors_LogEntryErrorsEntry')
+        ..a<int>(1, 'key', PbFieldType.O3)
+        ..a<$google$rpc.Status>(2, 'value', PbFieldType.OM,
+            $google$rpc.Status.getDefault, $google$rpc.Status.create)
+        ..hasRequiredFields = false;
+
+  WriteLogEntriesPartialErrors_LogEntryErrorsEntry() : super();
+  WriteLogEntriesPartialErrors_LogEntryErrorsEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  WriteLogEntriesPartialErrors_LogEntryErrorsEntry.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  WriteLogEntriesPartialErrors_LogEntryErrorsEntry clone() =>
+      new WriteLogEntriesPartialErrors_LogEntryErrorsEntry()
+        ..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static WriteLogEntriesPartialErrors_LogEntryErrorsEntry create() =>
+      new WriteLogEntriesPartialErrors_LogEntryErrorsEntry();
+  static PbList<WriteLogEntriesPartialErrors_LogEntryErrorsEntry>
+      createRepeated() =>
+          new PbList<WriteLogEntriesPartialErrors_LogEntryErrorsEntry>();
+  static WriteLogEntriesPartialErrors_LogEntryErrorsEntry getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance =
+          new _ReadonlyWriteLogEntriesPartialErrors_LogEntryErrorsEntry();
+    return _defaultInstance;
+  }
+
+  static WriteLogEntriesPartialErrors_LogEntryErrorsEntry _defaultInstance;
+  static void $checkItem(WriteLogEntriesPartialErrors_LogEntryErrorsEntry v) {
+    if (v is! WriteLogEntriesPartialErrors_LogEntryErrorsEntry)
+      checkItemFailed(v, 'WriteLogEntriesPartialErrors_LogEntryErrorsEntry');
+  }
+
+  int get key => $_get(0, 0);
+  set key(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  $google$rpc.Status get value => $_getN(1);
+  set value($google$rpc.Status v) {
+    setField(2, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyWriteLogEntriesPartialErrors_LogEntryErrorsEntry
+    extends WriteLogEntriesPartialErrors_LogEntryErrorsEntry
+    with ReadonlyMessageMixin {}
+
+class WriteLogEntriesPartialErrors extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('WriteLogEntriesPartialErrors')
+    ..pp<WriteLogEntriesPartialErrors_LogEntryErrorsEntry>(
+        1,
+        'logEntryErrors',
+        PbFieldType.PM,
+        WriteLogEntriesPartialErrors_LogEntryErrorsEntry.$checkItem,
+        WriteLogEntriesPartialErrors_LogEntryErrorsEntry.create)
+    ..hasRequiredFields = false;
+
+  WriteLogEntriesPartialErrors() : super();
+  WriteLogEntriesPartialErrors.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  WriteLogEntriesPartialErrors.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  WriteLogEntriesPartialErrors clone() =>
+      new WriteLogEntriesPartialErrors()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static WriteLogEntriesPartialErrors create() =>
+      new WriteLogEntriesPartialErrors();
+  static PbList<WriteLogEntriesPartialErrors> createRepeated() =>
+      new PbList<WriteLogEntriesPartialErrors>();
+  static WriteLogEntriesPartialErrors getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyWriteLogEntriesPartialErrors();
+    return _defaultInstance;
+  }
+
+  static WriteLogEntriesPartialErrors _defaultInstance;
+  static void $checkItem(WriteLogEntriesPartialErrors v) {
+    if (v is! WriteLogEntriesPartialErrors)
+      checkItemFailed(v, 'WriteLogEntriesPartialErrors');
+  }
+
+  List<WriteLogEntriesPartialErrors_LogEntryErrorsEntry> get logEntryErrors =>
+      $_getList(0);
+}
+
+class _ReadonlyWriteLogEntriesPartialErrors extends WriteLogEntriesPartialErrors
+    with ReadonlyMessageMixin {}
+
+class ListLogEntriesRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ListLogEntriesRequest')
+    ..pPS(1, 'projectIds')
+    ..aOS(2, 'filter')
+    ..aOS(3, 'orderBy')
+    ..a<int>(4, 'pageSize', PbFieldType.O3)
+    ..aOS(5, 'pageToken')
+    ..pPS(8, 'resourceNames')
+    ..hasRequiredFields = false;
+
+  ListLogEntriesRequest() : super();
+  ListLogEntriesRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListLogEntriesRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListLogEntriesRequest clone() =>
+      new ListLogEntriesRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListLogEntriesRequest create() => new ListLogEntriesRequest();
+  static PbList<ListLogEntriesRequest> createRepeated() =>
+      new PbList<ListLogEntriesRequest>();
+  static ListLogEntriesRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyListLogEntriesRequest();
+    return _defaultInstance;
+  }
+
+  static ListLogEntriesRequest _defaultInstance;
+  static void $checkItem(ListLogEntriesRequest v) {
+    if (v is! ListLogEntriesRequest)
+      checkItemFailed(v, 'ListLogEntriesRequest');
+  }
+
+  List<String> get projectIds => $_getList(0);
+
+  String get filter => $_getS(1, '');
+  set filter(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasFilter() => $_has(1);
+  void clearFilter() => clearField(2);
+
+  String get orderBy => $_getS(2, '');
+  set orderBy(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasOrderBy() => $_has(2);
+  void clearOrderBy() => clearField(3);
+
+  int get pageSize => $_get(3, 0);
+  set pageSize(int v) {
+    $_setUnsignedInt32(3, v);
+  }
+
+  bool hasPageSize() => $_has(3);
+  void clearPageSize() => clearField(4);
+
+  String get pageToken => $_getS(4, '');
+  set pageToken(String v) {
+    $_setString(4, v);
+  }
+
+  bool hasPageToken() => $_has(4);
+  void clearPageToken() => clearField(5);
+
+  List<String> get resourceNames => $_getList(5);
+}
+
+class _ReadonlyListLogEntriesRequest extends ListLogEntriesRequest
+    with ReadonlyMessageMixin {}
+
+class ListLogEntriesResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ListLogEntriesResponse')
+    ..pp<LogEntry>(
+        1, 'entries', PbFieldType.PM, LogEntry.$checkItem, LogEntry.create)
+    ..aOS(2, 'nextPageToken')
+    ..hasRequiredFields = false;
+
+  ListLogEntriesResponse() : super();
+  ListLogEntriesResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListLogEntriesResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListLogEntriesResponse clone() =>
+      new ListLogEntriesResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListLogEntriesResponse create() => new ListLogEntriesResponse();
+  static PbList<ListLogEntriesResponse> createRepeated() =>
+      new PbList<ListLogEntriesResponse>();
+  static ListLogEntriesResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyListLogEntriesResponse();
+    return _defaultInstance;
+  }
+
+  static ListLogEntriesResponse _defaultInstance;
+  static void $checkItem(ListLogEntriesResponse v) {
+    if (v is! ListLogEntriesResponse)
+      checkItemFailed(v, 'ListLogEntriesResponse');
+  }
+
+  List<LogEntry> get entries => $_getList(0);
+
+  String get nextPageToken => $_getS(1, '');
+  set nextPageToken(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasNextPageToken() => $_has(1);
+  void clearNextPageToken() => clearField(2);
+}
+
+class _ReadonlyListLogEntriesResponse extends ListLogEntriesResponse
+    with ReadonlyMessageMixin {}
+
+class ListMonitoredResourceDescriptorsRequest extends GeneratedMessage {
+  static final BuilderInfo _i =
+      new BuilderInfo('ListMonitoredResourceDescriptorsRequest')
+        ..a<int>(1, 'pageSize', PbFieldType.O3)
+        ..aOS(2, 'pageToken')
+        ..hasRequiredFields = false;
+
+  ListMonitoredResourceDescriptorsRequest() : super();
+  ListMonitoredResourceDescriptorsRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListMonitoredResourceDescriptorsRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListMonitoredResourceDescriptorsRequest clone() =>
+      new ListMonitoredResourceDescriptorsRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListMonitoredResourceDescriptorsRequest create() =>
+      new ListMonitoredResourceDescriptorsRequest();
+  static PbList<ListMonitoredResourceDescriptorsRequest> createRepeated() =>
+      new PbList<ListMonitoredResourceDescriptorsRequest>();
+  static ListMonitoredResourceDescriptorsRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyListMonitoredResourceDescriptorsRequest();
+    return _defaultInstance;
+  }
+
+  static ListMonitoredResourceDescriptorsRequest _defaultInstance;
+  static void $checkItem(ListMonitoredResourceDescriptorsRequest v) {
+    if (v is! ListMonitoredResourceDescriptorsRequest)
+      checkItemFailed(v, 'ListMonitoredResourceDescriptorsRequest');
+  }
+
+  int get pageSize => $_get(0, 0);
+  set pageSize(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasPageSize() => $_has(0);
+  void clearPageSize() => clearField(1);
+
+  String get pageToken => $_getS(1, '');
+  set pageToken(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasPageToken() => $_has(1);
+  void clearPageToken() => clearField(2);
+}
+
+class _ReadonlyListMonitoredResourceDescriptorsRequest
+    extends ListMonitoredResourceDescriptorsRequest with ReadonlyMessageMixin {}
+
+class ListMonitoredResourceDescriptorsResponse extends GeneratedMessage {
+  static final BuilderInfo _i =
+      new BuilderInfo('ListMonitoredResourceDescriptorsResponse')
+        ..pp<$google$api.MonitoredResourceDescriptor>(
+            1,
+            'resourceDescriptors',
+            PbFieldType.PM,
+            $google$api.MonitoredResourceDescriptor.$checkItem,
+            $google$api.MonitoredResourceDescriptor.create)
+        ..aOS(2, 'nextPageToken')
+        ..hasRequiredFields = false;
+
+  ListMonitoredResourceDescriptorsResponse() : super();
+  ListMonitoredResourceDescriptorsResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListMonitoredResourceDescriptorsResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListMonitoredResourceDescriptorsResponse clone() =>
+      new ListMonitoredResourceDescriptorsResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListMonitoredResourceDescriptorsResponse create() =>
+      new ListMonitoredResourceDescriptorsResponse();
+  static PbList<ListMonitoredResourceDescriptorsResponse> createRepeated() =>
+      new PbList<ListMonitoredResourceDescriptorsResponse>();
+  static ListMonitoredResourceDescriptorsResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance =
+          new _ReadonlyListMonitoredResourceDescriptorsResponse();
+    return _defaultInstance;
+  }
+
+  static ListMonitoredResourceDescriptorsResponse _defaultInstance;
+  static void $checkItem(ListMonitoredResourceDescriptorsResponse v) {
+    if (v is! ListMonitoredResourceDescriptorsResponse)
+      checkItemFailed(v, 'ListMonitoredResourceDescriptorsResponse');
+  }
+
+  List<$google$api.MonitoredResourceDescriptor> get resourceDescriptors =>
+      $_getList(0);
+
+  String get nextPageToken => $_getS(1, '');
+  set nextPageToken(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasNextPageToken() => $_has(1);
+  void clearNextPageToken() => clearField(2);
+}
+
+class _ReadonlyListMonitoredResourceDescriptorsResponse
+    extends ListMonitoredResourceDescriptorsResponse with ReadonlyMessageMixin {
+}
+
+class ListLogsRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ListLogsRequest')
+    ..aOS(1, 'parent')
+    ..a<int>(2, 'pageSize', PbFieldType.O3)
+    ..aOS(3, 'pageToken')
+    ..hasRequiredFields = false;
+
+  ListLogsRequest() : super();
+  ListLogsRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListLogsRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListLogsRequest clone() => new ListLogsRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListLogsRequest create() => new ListLogsRequest();
+  static PbList<ListLogsRequest> createRepeated() =>
+      new PbList<ListLogsRequest>();
+  static ListLogsRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyListLogsRequest();
+    return _defaultInstance;
+  }
+
+  static ListLogsRequest _defaultInstance;
+  static void $checkItem(ListLogsRequest v) {
+    if (v is! ListLogsRequest) checkItemFailed(v, 'ListLogsRequest');
+  }
+
+  String get parent => $_getS(0, '');
+  set parent(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasParent() => $_has(0);
+  void clearParent() => clearField(1);
+
+  int get pageSize => $_get(1, 0);
+  set pageSize(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasPageSize() => $_has(1);
+  void clearPageSize() => clearField(2);
+
+  String get pageToken => $_getS(2, '');
+  set pageToken(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasPageToken() => $_has(2);
+  void clearPageToken() => clearField(3);
+}
+
+class _ReadonlyListLogsRequest extends ListLogsRequest
+    with ReadonlyMessageMixin {}
+
+class ListLogsResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ListLogsResponse')
+    ..aOS(2, 'nextPageToken')
+    ..pPS(3, 'logNames')
+    ..hasRequiredFields = false;
+
+  ListLogsResponse() : super();
+  ListLogsResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListLogsResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListLogsResponse clone() => new ListLogsResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListLogsResponse create() => new ListLogsResponse();
+  static PbList<ListLogsResponse> createRepeated() =>
+      new PbList<ListLogsResponse>();
+  static ListLogsResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyListLogsResponse();
+    return _defaultInstance;
+  }
+
+  static ListLogsResponse _defaultInstance;
+  static void $checkItem(ListLogsResponse v) {
+    if (v is! ListLogsResponse) checkItemFailed(v, 'ListLogsResponse');
+  }
+
+  String get nextPageToken => $_getS(0, '');
+  set nextPageToken(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasNextPageToken() => $_has(0);
+  void clearNextPageToken() => clearField(2);
+
+  List<String> get logNames => $_getList(1);
+}
+
+class _ReadonlyListLogsResponse extends ListLogsResponse
+    with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbenum.dart
new file mode 100644
index 0000000..d7ea628
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_logging_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbgrpc.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbgrpc.dart
new file mode 100644
index 0000000..0e65060
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbgrpc.dart
@@ -0,0 +1,170 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_logging_pbgrpc;
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'logging.pb.dart';
+import '../../protobuf/empty.pb.dart' as $google$protobuf;
+export 'logging.pb.dart';
+
+class LoggingServiceV2Client extends Client {
+  static final _$deleteLog =
+      new ClientMethod<DeleteLogRequest, $google$protobuf.Empty>(
+          '/google.logging.v2.LoggingServiceV2/DeleteLog',
+          (DeleteLogRequest value) => value.writeToBuffer(),
+          (List<int> value) => new $google$protobuf.Empty.fromBuffer(value));
+  static final _$writeLogEntries =
+      new ClientMethod<WriteLogEntriesRequest, WriteLogEntriesResponse>(
+          '/google.logging.v2.LoggingServiceV2/WriteLogEntries',
+          (WriteLogEntriesRequest value) => value.writeToBuffer(),
+          (List<int> value) => new WriteLogEntriesResponse.fromBuffer(value));
+  static final _$listLogEntries =
+      new ClientMethod<ListLogEntriesRequest, ListLogEntriesResponse>(
+          '/google.logging.v2.LoggingServiceV2/ListLogEntries',
+          (ListLogEntriesRequest value) => value.writeToBuffer(),
+          (List<int> value) => new ListLogEntriesResponse.fromBuffer(value));
+  static final _$listMonitoredResourceDescriptors = new ClientMethod<
+          ListMonitoredResourceDescriptorsRequest,
+          ListMonitoredResourceDescriptorsResponse>(
+      '/google.logging.v2.LoggingServiceV2/ListMonitoredResourceDescriptors',
+      (ListMonitoredResourceDescriptorsRequest value) => value.writeToBuffer(),
+      (List<int> value) =>
+          new ListMonitoredResourceDescriptorsResponse.fromBuffer(value));
+  static final _$listLogs = new ClientMethod<ListLogsRequest, ListLogsResponse>(
+      '/google.logging.v2.LoggingServiceV2/ListLogs',
+      (ListLogsRequest value) => value.writeToBuffer(),
+      (List<int> value) => new ListLogsResponse.fromBuffer(value));
+
+  LoggingServiceV2Client(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<$google$protobuf.Empty> deleteLog(DeleteLogRequest request,
+      {CallOptions options}) {
+    final call = $createCall(_$deleteLog, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<WriteLogEntriesResponse> writeLogEntries(
+      WriteLogEntriesRequest request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$writeLogEntries, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<ListLogEntriesResponse> listLogEntries(
+      ListLogEntriesRequest request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$listLogEntries, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<ListMonitoredResourceDescriptorsResponse>
+      listMonitoredResourceDescriptors(
+          ListMonitoredResourceDescriptorsRequest request,
+          {CallOptions options}) {
+    final call = $createCall(
+        _$listMonitoredResourceDescriptors, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<ListLogsResponse> listLogs(ListLogsRequest request,
+      {CallOptions options}) {
+    final call = $createCall(_$listLogs, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+}
+
+abstract class LoggingServiceV2ServiceBase extends Service {
+  String get $name => 'google.logging.v2.LoggingServiceV2';
+
+  LoggingServiceV2ServiceBase() {
+    $addMethod(new ServiceMethod<DeleteLogRequest, $google$protobuf.Empty>(
+        'DeleteLog',
+        deleteLog_Pre,
+        false,
+        false,
+        (List<int> value) => new DeleteLogRequest.fromBuffer(value),
+        ($google$protobuf.Empty value) => value.writeToBuffer()));
+    $addMethod(
+        new ServiceMethod<WriteLogEntriesRequest, WriteLogEntriesResponse>(
+            'WriteLogEntries',
+            writeLogEntries_Pre,
+            false,
+            false,
+            (List<int> value) => new WriteLogEntriesRequest.fromBuffer(value),
+            (WriteLogEntriesResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<ListLogEntriesRequest, ListLogEntriesResponse>(
+        'ListLogEntries',
+        listLogEntries_Pre,
+        false,
+        false,
+        (List<int> value) => new ListLogEntriesRequest.fromBuffer(value),
+        (ListLogEntriesResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<ListMonitoredResourceDescriptorsRequest,
+            ListMonitoredResourceDescriptorsResponse>(
+        'ListMonitoredResourceDescriptors',
+        listMonitoredResourceDescriptors_Pre,
+        false,
+        false,
+        (List<int> value) =>
+            new ListMonitoredResourceDescriptorsRequest.fromBuffer(value),
+        (ListMonitoredResourceDescriptorsResponse value) =>
+            value.writeToBuffer()));
+    $addMethod(new ServiceMethod<ListLogsRequest, ListLogsResponse>(
+        'ListLogs',
+        listLogs_Pre,
+        false,
+        false,
+        (List<int> value) => new ListLogsRequest.fromBuffer(value),
+        (ListLogsResponse value) => value.writeToBuffer()));
+  }
+
+  Future<$google$protobuf.Empty> deleteLog_Pre(
+      ServiceCall call, Future request) async {
+    return deleteLog(call, await request);
+  }
+
+  Future<WriteLogEntriesResponse> writeLogEntries_Pre(
+      ServiceCall call, Future request) async {
+    return writeLogEntries(call, await request);
+  }
+
+  Future<ListLogEntriesResponse> listLogEntries_Pre(
+      ServiceCall call, Future request) async {
+    return listLogEntries(call, await request);
+  }
+
+  Future<ListMonitoredResourceDescriptorsResponse>
+      listMonitoredResourceDescriptors_Pre(
+          ServiceCall call, Future request) async {
+    return listMonitoredResourceDescriptors(call, await request);
+  }
+
+  Future<ListLogsResponse> listLogs_Pre(
+      ServiceCall call, Future request) async {
+    return listLogs(call, await request);
+  }
+
+  Future<$google$protobuf.Empty> deleteLog(
+      ServiceCall call, DeleteLogRequest request);
+  Future<WriteLogEntriesResponse> writeLogEntries(
+      ServiceCall call, WriteLogEntriesRequest request);
+  Future<ListLogEntriesResponse> listLogEntries(
+      ServiceCall call, ListLogEntriesRequest request);
+  Future<ListMonitoredResourceDescriptorsResponse>
+      listMonitoredResourceDescriptors(
+          ServiceCall call, ListMonitoredResourceDescriptorsRequest request);
+  Future<ListLogsResponse> listLogs(ServiceCall call, ListLogsRequest request);
+}
diff --git a/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbjson.dart
new file mode 100644
index 0000000..9533338
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/logging/v2/logging.pbjson.dart
@@ -0,0 +1,187 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.logging.v2_logging_pbjson;
+
+const DeleteLogRequest$json = const {
+  '1': 'DeleteLogRequest',
+  '2': const [
+    const {'1': 'log_name', '3': 1, '4': 1, '5': 9, '10': 'logName'},
+  ],
+};
+
+const WriteLogEntriesRequest$json = const {
+  '1': 'WriteLogEntriesRequest',
+  '2': const [
+    const {'1': 'log_name', '3': 1, '4': 1, '5': 9, '10': 'logName'},
+    const {
+      '1': 'resource',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.google.api.MonitoredResource',
+      '10': 'resource'
+    },
+    const {
+      '1': 'labels',
+      '3': 3,
+      '4': 3,
+      '5': 11,
+      '6': '.google.logging.v2.WriteLogEntriesRequest.LabelsEntry',
+      '10': 'labels'
+    },
+    const {
+      '1': 'entries',
+      '3': 4,
+      '4': 3,
+      '5': 11,
+      '6': '.google.logging.v2.LogEntry',
+      '10': 'entries'
+    },
+    const {
+      '1': 'partial_success',
+      '3': 5,
+      '4': 1,
+      '5': 8,
+      '10': 'partialSuccess'
+    },
+  ],
+  '3': const [WriteLogEntriesRequest_LabelsEntry$json],
+};
+
+const WriteLogEntriesRequest_LabelsEntry$json = const {
+  '1': 'LabelsEntry',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
+    const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
+  ],
+  '7': const {'7': true},
+};
+
+const WriteLogEntriesResponse$json = const {
+  '1': 'WriteLogEntriesResponse',
+};
+
+const WriteLogEntriesPartialErrors$json = const {
+  '1': 'WriteLogEntriesPartialErrors',
+  '2': const [
+    const {
+      '1': 'log_entry_errors',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6':
+          '.google.logging.v2.WriteLogEntriesPartialErrors.LogEntryErrorsEntry',
+      '10': 'logEntryErrors'
+    },
+  ],
+  '3': const [WriteLogEntriesPartialErrors_LogEntryErrorsEntry$json],
+};
+
+const WriteLogEntriesPartialErrors_LogEntryErrorsEntry$json = const {
+  '1': 'LogEntryErrorsEntry',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 5, '10': 'key'},
+    const {
+      '1': 'value',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.google.rpc.Status',
+      '10': 'value'
+    },
+  ],
+  '7': const {'7': true},
+};
+
+const ListLogEntriesRequest$json = const {
+  '1': 'ListLogEntriesRequest',
+  '2': const [
+    const {'1': 'project_ids', '3': 1, '4': 3, '5': 9, '10': 'projectIds'},
+    const {
+      '1': 'resource_names',
+      '3': 8,
+      '4': 3,
+      '5': 9,
+      '10': 'resourceNames'
+    },
+    const {'1': 'filter', '3': 2, '4': 1, '5': 9, '10': 'filter'},
+    const {'1': 'order_by', '3': 3, '4': 1, '5': 9, '10': 'orderBy'},
+    const {'1': 'page_size', '3': 4, '4': 1, '5': 5, '10': 'pageSize'},
+    const {'1': 'page_token', '3': 5, '4': 1, '5': 9, '10': 'pageToken'},
+  ],
+};
+
+const ListLogEntriesResponse$json = const {
+  '1': 'ListLogEntriesResponse',
+  '2': const [
+    const {
+      '1': 'entries',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6': '.google.logging.v2.LogEntry',
+      '10': 'entries'
+    },
+    const {
+      '1': 'next_page_token',
+      '3': 2,
+      '4': 1,
+      '5': 9,
+      '10': 'nextPageToken'
+    },
+  ],
+};
+
+const ListMonitoredResourceDescriptorsRequest$json = const {
+  '1': 'ListMonitoredResourceDescriptorsRequest',
+  '2': const [
+    const {'1': 'page_size', '3': 1, '4': 1, '5': 5, '10': 'pageSize'},
+    const {'1': 'page_token', '3': 2, '4': 1, '5': 9, '10': 'pageToken'},
+  ],
+};
+
+const ListMonitoredResourceDescriptorsResponse$json = const {
+  '1': 'ListMonitoredResourceDescriptorsResponse',
+  '2': const [
+    const {
+      '1': 'resource_descriptors',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6': '.google.api.MonitoredResourceDescriptor',
+      '10': 'resourceDescriptors'
+    },
+    const {
+      '1': 'next_page_token',
+      '3': 2,
+      '4': 1,
+      '5': 9,
+      '10': 'nextPageToken'
+    },
+  ],
+};
+
+const ListLogsRequest$json = const {
+  '1': 'ListLogsRequest',
+  '2': const [
+    const {'1': 'parent', '3': 1, '4': 1, '5': 9, '10': 'parent'},
+    const {'1': 'page_size', '3': 2, '4': 1, '5': 5, '10': 'pageSize'},
+    const {'1': 'page_token', '3': 3, '4': 1, '5': 9, '10': 'pageToken'},
+  ],
+};
+
+const ListLogsResponse$json = const {
+  '1': 'ListLogsResponse',
+  '2': const [
+    const {'1': 'log_names', '3': 3, '4': 3, '5': 9, '10': 'logNames'},
+    const {
+      '1': 'next_page_token',
+      '3': 2,
+      '4': 1,
+      '5': 9,
+      '10': 'nextPageToken'
+    },
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pb.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pb.dart
new file mode 100644
index 0000000..15cb7be
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pb.dart
@@ -0,0 +1,54 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_any;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class Any extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Any')
+    ..aOS(1, 'typeUrl')
+    ..a<List<int>>(2, 'value', PbFieldType.OY)
+    ..hasRequiredFields = false;
+
+  Any() : super();
+  Any.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Any.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Any clone() => new Any()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Any create() => new Any();
+  static PbList<Any> createRepeated() => new PbList<Any>();
+  static Any getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyAny();
+    return _defaultInstance;
+  }
+
+  static Any _defaultInstance;
+  static void $checkItem(Any v) {
+    if (v is! Any) checkItemFailed(v, 'Any');
+  }
+
+  String get typeUrl => $_getS(0, '');
+  set typeUrl(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasTypeUrl() => $_has(0);
+  void clearTypeUrl() => clearField(1);
+
+  List<int> get value => $_getN(1);
+  set value(List<int> v) {
+    $_setBytes(1, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyAny extends Any with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbenum.dart
new file mode 100644
index 0000000..4db6beb
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_any_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbjson.dart
new file mode 100644
index 0000000..9784050
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/any.pbjson.dart
@@ -0,0 +1,13 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_any_pbjson;
+
+const Any$json = const {
+  '1': 'Any',
+  '2': const [
+    const {'1': 'type_url', '3': 1, '4': 1, '5': 9, '10': 'typeUrl'},
+    const {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pb.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pb.dart
new file mode 100644
index 0000000..79a8602
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pb.dart
@@ -0,0 +1,56 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_duration;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:fixnum/fixnum.dart';
+import 'package:protobuf/protobuf.dart';
+
+class Duration extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Duration')
+    ..aInt64(1, 'seconds')
+    ..a<int>(2, 'nanos', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  Duration() : super();
+  Duration.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Duration.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Duration clone() => new Duration()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Duration create() => new Duration();
+  static PbList<Duration> createRepeated() => new PbList<Duration>();
+  static Duration getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyDuration();
+    return _defaultInstance;
+  }
+
+  static Duration _defaultInstance;
+  static void $checkItem(Duration v) {
+    if (v is! Duration) checkItemFailed(v, 'Duration');
+  }
+
+  Int64 get seconds => $_getI64(0);
+  set seconds(Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  bool hasSeconds() => $_has(0);
+  void clearSeconds() => clearField(1);
+
+  int get nanos => $_get(1, 0);
+  set nanos(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasNanos() => $_has(1);
+  void clearNanos() => clearField(2);
+}
+
+class _ReadonlyDuration extends Duration with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbenum.dart
new file mode 100644
index 0000000..7a03683
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_duration_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbjson.dart
new file mode 100644
index 0000000..2f816fe
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/duration.pbjson.dart
@@ -0,0 +1,13 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_duration_pbjson;
+
+const Duration$json = const {
+  '1': 'Duration',
+  '2': const [
+    const {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'},
+    const {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pb.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pb.dart
new file mode 100644
index 0000000..8dfaa6c
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pb.dart
@@ -0,0 +1,36 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_empty;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class Empty extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Empty')
+    ..hasRequiredFields = false;
+
+  Empty() : super();
+  Empty.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Empty.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Empty clone() => new Empty()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Empty create() => new Empty();
+  static PbList<Empty> createRepeated() => new PbList<Empty>();
+  static Empty getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyEmpty();
+    return _defaultInstance;
+  }
+
+  static Empty _defaultInstance;
+  static void $checkItem(Empty v) {
+    if (v is! Empty) checkItemFailed(v, 'Empty');
+  }
+}
+
+class _ReadonlyEmpty extends Empty with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbenum.dart
new file mode 100644
index 0000000..bcfc3d5
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_empty_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbjson.dart
new file mode 100644
index 0000000..e372809
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/empty.pbjson.dart
@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_empty_pbjson;
+
+const Empty$json = const {
+  '1': 'Empty',
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pb.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pb.dart
new file mode 100644
index 0000000..817369d
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pb.dart
@@ -0,0 +1,208 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_struct;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import 'struct.pbenum.dart';
+
+export 'struct.pbenum.dart';
+
+class Struct_FieldsEntry extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Struct_FieldsEntry')
+    ..aOS(1, 'key')
+    ..a<Value>(2, 'value', PbFieldType.OM, Value.getDefault, Value.create)
+    ..hasRequiredFields = false;
+
+  Struct_FieldsEntry() : super();
+  Struct_FieldsEntry.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Struct_FieldsEntry.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Struct_FieldsEntry clone() =>
+      new Struct_FieldsEntry()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Struct_FieldsEntry create() => new Struct_FieldsEntry();
+  static PbList<Struct_FieldsEntry> createRepeated() =>
+      new PbList<Struct_FieldsEntry>();
+  static Struct_FieldsEntry getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyStruct_FieldsEntry();
+    return _defaultInstance;
+  }
+
+  static Struct_FieldsEntry _defaultInstance;
+  static void $checkItem(Struct_FieldsEntry v) {
+    if (v is! Struct_FieldsEntry) checkItemFailed(v, 'Struct_FieldsEntry');
+  }
+
+  String get key => $_getS(0, '');
+  set key(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasKey() => $_has(0);
+  void clearKey() => clearField(1);
+
+  Value get value => $_getN(1);
+  set value(Value v) {
+    setField(2, v);
+  }
+
+  bool hasValue() => $_has(1);
+  void clearValue() => clearField(2);
+}
+
+class _ReadonlyStruct_FieldsEntry extends Struct_FieldsEntry
+    with ReadonlyMessageMixin {}
+
+class Struct extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Struct')
+    ..pp<Struct_FieldsEntry>(1, 'fields', PbFieldType.PM,
+        Struct_FieldsEntry.$checkItem, Struct_FieldsEntry.create)
+    ..hasRequiredFields = false;
+
+  Struct() : super();
+  Struct.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Struct.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Struct clone() => new Struct()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Struct create() => new Struct();
+  static PbList<Struct> createRepeated() => new PbList<Struct>();
+  static Struct getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyStruct();
+    return _defaultInstance;
+  }
+
+  static Struct _defaultInstance;
+  static void $checkItem(Struct v) {
+    if (v is! Struct) checkItemFailed(v, 'Struct');
+  }
+
+  List<Struct_FieldsEntry> get fields => $_getList(0);
+}
+
+class _ReadonlyStruct extends Struct with ReadonlyMessageMixin {}
+
+class Value extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Value')
+    ..e<NullValue>(1, 'nullValue', PbFieldType.OE, NullValue.NULL_VALUE,
+        NullValue.valueOf, NullValue.values)
+    ..a<double>(2, 'numberValue', PbFieldType.OD)
+    ..aOS(3, 'stringValue')
+    ..aOB(4, 'boolValue')
+    ..a<Struct>(
+        5, 'structValue', PbFieldType.OM, Struct.getDefault, Struct.create)
+    ..a<ListValue>(
+        6, 'listValue', PbFieldType.OM, ListValue.getDefault, ListValue.create)
+    ..hasRequiredFields = false;
+
+  Value() : super();
+  Value.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Value.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Value clone() => new Value()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Value create() => new Value();
+  static PbList<Value> createRepeated() => new PbList<Value>();
+  static Value getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyValue();
+    return _defaultInstance;
+  }
+
+  static Value _defaultInstance;
+  static void $checkItem(Value v) {
+    if (v is! Value) checkItemFailed(v, 'Value');
+  }
+
+  NullValue get nullValue => $_getN(0);
+  set nullValue(NullValue v) {
+    setField(1, v);
+  }
+
+  bool hasNullValue() => $_has(0);
+  void clearNullValue() => clearField(1);
+
+  double get numberValue => $_getN(1);
+  set numberValue(double v) {
+    $_setDouble(1, v);
+  }
+
+  bool hasNumberValue() => $_has(1);
+  void clearNumberValue() => clearField(2);
+
+  String get stringValue => $_getS(2, '');
+  set stringValue(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasStringValue() => $_has(2);
+  void clearStringValue() => clearField(3);
+
+  bool get boolValue => $_get(3, false);
+  set boolValue(bool v) {
+    $_setBool(3, v);
+  }
+
+  bool hasBoolValue() => $_has(3);
+  void clearBoolValue() => clearField(4);
+
+  Struct get structValue => $_getN(4);
+  set structValue(Struct v) {
+    setField(5, v);
+  }
+
+  bool hasStructValue() => $_has(4);
+  void clearStructValue() => clearField(5);
+
+  ListValue get listValue => $_getN(5);
+  set listValue(ListValue v) {
+    setField(6, v);
+  }
+
+  bool hasListValue() => $_has(5);
+  void clearListValue() => clearField(6);
+}
+
+class _ReadonlyValue extends Value with ReadonlyMessageMixin {}
+
+class ListValue extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ListValue')
+    ..pp<Value>(1, 'values', PbFieldType.PM, Value.$checkItem, Value.create)
+    ..hasRequiredFields = false;
+
+  ListValue() : super();
+  ListValue.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ListValue.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ListValue clone() => new ListValue()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ListValue create() => new ListValue();
+  static PbList<ListValue> createRepeated() => new PbList<ListValue>();
+  static ListValue getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyListValue();
+    return _defaultInstance;
+  }
+
+  static ListValue _defaultInstance;
+  static void $checkItem(ListValue v) {
+    if (v is! ListValue) checkItemFailed(v, 'ListValue');
+  }
+
+  List<Value> get values => $_getList(0);
+}
+
+class _ReadonlyListValue extends ListValue with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbenum.dart
new file mode 100644
index 0000000..2f30d43
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbenum.dart
@@ -0,0 +1,25 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_struct_pbenum;
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
+import 'dart:core' show int, dynamic, String, List, Map;
+import 'package:protobuf/protobuf.dart';
+
+class NullValue extends ProtobufEnum {
+  static const NullValue NULL_VALUE = const NullValue._(0, 'NULL_VALUE');
+
+  static const List<NullValue> values = const <NullValue>[
+    NULL_VALUE,
+  ];
+
+  static final Map<int, dynamic> _byValue = ProtobufEnum.initByValue(values);
+  static NullValue valueOf(int value) => _byValue[value] as NullValue;
+  static void $checkItem(NullValue v) {
+    if (v is! NullValue) checkItemFailed(v, 'NullValue');
+  }
+
+  const NullValue._(int v, String n) : super(v, n);
+}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbjson.dart
new file mode 100644
index 0000000..9afd8b7
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/struct.pbjson.dart
@@ -0,0 +1,117 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_struct_pbjson;
+
+const NullValue$json = const {
+  '1': 'NullValue',
+  '2': const [
+    const {'1': 'NULL_VALUE', '2': 0},
+  ],
+};
+
+const Struct$json = const {
+  '1': 'Struct',
+  '2': const [
+    const {
+      '1': 'fields',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6': '.google.protobuf.Struct.FieldsEntry',
+      '10': 'fields'
+    },
+  ],
+  '3': const [Struct_FieldsEntry$json],
+};
+
+const Struct_FieldsEntry$json = const {
+  '1': 'FieldsEntry',
+  '2': const [
+    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
+    const {
+      '1': 'value',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Value',
+      '10': 'value'
+    },
+  ],
+  '7': const {'7': true},
+};
+
+const Value$json = const {
+  '1': 'Value',
+  '2': const [
+    const {
+      '1': 'null_value',
+      '3': 1,
+      '4': 1,
+      '5': 14,
+      '6': '.google.protobuf.NullValue',
+      '9': 0,
+      '10': 'nullValue'
+    },
+    const {
+      '1': 'number_value',
+      '3': 2,
+      '4': 1,
+      '5': 1,
+      '9': 0,
+      '10': 'numberValue'
+    },
+    const {
+      '1': 'string_value',
+      '3': 3,
+      '4': 1,
+      '5': 9,
+      '9': 0,
+      '10': 'stringValue'
+    },
+    const {
+      '1': 'bool_value',
+      '3': 4,
+      '4': 1,
+      '5': 8,
+      '9': 0,
+      '10': 'boolValue'
+    },
+    const {
+      '1': 'struct_value',
+      '3': 5,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.Struct',
+      '9': 0,
+      '10': 'structValue'
+    },
+    const {
+      '1': 'list_value',
+      '3': 6,
+      '4': 1,
+      '5': 11,
+      '6': '.google.protobuf.ListValue',
+      '9': 0,
+      '10': 'listValue'
+    },
+  ],
+  '8': const [
+    const {'1': 'kind'},
+  ],
+};
+
+const ListValue$json = const {
+  '1': 'ListValue',
+  '2': const [
+    const {
+      '1': 'values',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6': '.google.protobuf.Value',
+      '10': 'values'
+    },
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pb.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pb.dart
new file mode 100644
index 0000000..49c181f
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pb.dart
@@ -0,0 +1,56 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_timestamp;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:fixnum/fixnum.dart';
+import 'package:protobuf/protobuf.dart';
+
+class Timestamp extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Timestamp')
+    ..aInt64(1, 'seconds')
+    ..a<int>(2, 'nanos', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  Timestamp() : super();
+  Timestamp.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Timestamp.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Timestamp clone() => new Timestamp()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Timestamp create() => new Timestamp();
+  static PbList<Timestamp> createRepeated() => new PbList<Timestamp>();
+  static Timestamp getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyTimestamp();
+    return _defaultInstance;
+  }
+
+  static Timestamp _defaultInstance;
+  static void $checkItem(Timestamp v) {
+    if (v is! Timestamp) checkItemFailed(v, 'Timestamp');
+  }
+
+  Int64 get seconds => $_getI64(0);
+  set seconds(Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  bool hasSeconds() => $_has(0);
+  void clearSeconds() => clearField(1);
+
+  int get nanos => $_get(1, 0);
+  set nanos(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasNanos() => $_has(1);
+  void clearNanos() => clearField(2);
+}
+
+class _ReadonlyTimestamp extends Timestamp with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbenum.dart
new file mode 100644
index 0000000..9c1ae44
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_timestamp_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbjson.dart
new file mode 100644
index 0000000..b6cf119
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/protobuf/timestamp.pbjson.dart
@@ -0,0 +1,13 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.protobuf_timestamp_pbjson;
+
+const Timestamp$json = const {
+  '1': 'Timestamp',
+  '2': const [
+    const {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'},
+    const {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'},
+  ],
+};
diff --git a/grpc/example/googleapis/lib/src/generated/google/rpc/status.pb.dart b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pb.dart
new file mode 100644
index 0000000..31e034f
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pb.dart
@@ -0,0 +1,61 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.rpc_status;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import '../protobuf/any.pb.dart' as $google$protobuf;
+
+class Status extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Status')
+    ..a<int>(1, 'code', PbFieldType.O3)
+    ..aOS(2, 'message')
+    ..pp<$google$protobuf.Any>(3, 'details', PbFieldType.PM,
+        $google$protobuf.Any.$checkItem, $google$protobuf.Any.create)
+    ..hasRequiredFields = false;
+
+  Status() : super();
+  Status.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Status.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Status clone() => new Status()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Status create() => new Status();
+  static PbList<Status> createRepeated() => new PbList<Status>();
+  static Status getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyStatus();
+    return _defaultInstance;
+  }
+
+  static Status _defaultInstance;
+  static void $checkItem(Status v) {
+    if (v is! Status) checkItemFailed(v, 'Status');
+  }
+
+  int get code => $_get(0, 0);
+  set code(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasCode() => $_has(0);
+  void clearCode() => clearField(1);
+
+  String get message => $_getS(1, '');
+  set message(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasMessage() => $_has(1);
+  void clearMessage() => clearField(2);
+
+  List<$google$protobuf.Any> get details => $_getList(2);
+}
+
+class _ReadonlyStatus extends Status with ReadonlyMessageMixin {}
diff --git a/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbenum.dart b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbenum.dart
new file mode 100644
index 0000000..7bd9759
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.rpc_status_pbenum;
diff --git a/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbjson.dart b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbjson.dart
new file mode 100644
index 0000000..279074d
--- /dev/null
+++ b/grpc/example/googleapis/lib/src/generated/google/rpc/status.pbjson.dart
@@ -0,0 +1,21 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library google.rpc_status_pbjson;
+
+const Status$json = const {
+  '1': 'Status',
+  '2': const [
+    const {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'},
+    const {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'},
+    const {
+      '1': 'details',
+      '3': 3,
+      '4': 3,
+      '5': 11,
+      '6': '.google.protobuf.Any',
+      '10': 'details'
+    },
+  ],
+};
diff --git a/grpc/example/googleapis/pubspec.yaml b/grpc/example/googleapis/pubspec.yaml
new file mode 100644
index 0000000..3620b1c
--- /dev/null
+++ b/grpc/example/googleapis/pubspec.yaml
@@ -0,0 +1,15 @@
+name: googleapis
+description: Dart gRPC client sample for Google APIs
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  async: '>=1.13.3 <3.0.0'
+  grpc:
+    path: ../../
+  protobuf: ^0.10.1
+
+dev_dependencies:
+  test: ^1.3.0
diff --git a/grpc/example/googleapis/tool/regenerate.sh b/grpc/example/googleapis/tool/regenerate.sh
new file mode 100755
index 0000000..6584746
--- /dev/null
+++ b/grpc/example/googleapis/tool/regenerate.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+if [ ! -d "$PROTOBUF" ]; then
+  echo "Please set the PROTOBUF environment variable to your clone of google/protobuf."
+  exit -1
+fi
+
+if [ ! -d "$GOOGLEAPIS" ]; then
+  echo "Please set the GOOGLEAPIS environment variable to your clone of googleapis/googleapis."
+  exit -1
+fi
+
+PROTOC="protoc --dart_out=grpc:lib/src/generated -I$PROTOBUF/src -I$GOOGLEAPIS"
+
+$PROTOC $GOOGLEAPIS/google/logging/v2/logging.proto
+$PROTOC $GOOGLEAPIS/google/logging/v2/log_entry.proto
+$PROTOC $GOOGLEAPIS/google/logging/type/log_severity.proto
+$PROTOC $GOOGLEAPIS/google/logging/type/http_request.proto
+
+$PROTOC $GOOGLEAPIS/google/api/monitored_resource.proto
+$PROTOC $GOOGLEAPIS/google/api/label.proto
+
+$PROTOC $GOOGLEAPIS/google/rpc/status.proto
+
+$PROTOC $PROTOBUF/src/google/protobuf/any.proto
+$PROTOC $PROTOBUF/src/google/protobuf/duration.proto
+$PROTOC $PROTOBUF/src/google/protobuf/empty.proto
+$PROTOC $PROTOBUF/src/google/protobuf/struct.proto
+$PROTOC $PROTOBUF/src/google/protobuf/timestamp.proto
+
+dartfmt -w lib/src/generated
diff --git a/grpc/example/helloworld/README.md b/grpc/example/helloworld/README.md
new file mode 100644
index 0000000..aeb4317
--- /dev/null
+++ b/grpc/example/helloworld/README.md
@@ -0,0 +1,50 @@
+# Description
+The hello world server and client demonstrate how to use Dart gRPC libraries to
+perform unary RPCs.
+
+See the definition of the hello world service in `protos/helloworld.proto`.
+
+# Run the sample code
+To compile and run the example, assuming you are in the root of the helloworld
+folder, i.e., .../example/helloworld/, first get the dependencies by running:
+
+```sh
+$ pub get
+```
+
+Then, to run the server:
+
+```sh
+$ dart bin/server.dart
+```
+
+Likewise, to run the client:
+
+```sh
+$ dart bin/client.dart
+```
+
+# Regenerate the stubs
+
+If you have made changes to the message or service definition in
+`protos/helloworld.proto` and need to regenerate the corresponding Dart files,
+you will need to have protoc version 3.0.0 or higher and the Dart protoc plugin
+version 0.7.9 or higher on your PATH.
+
+To install protoc, see the instructions on
+[the Protocol Buffers website](https://developers.google.com/protocol-buffers/).
+
+The easiest way to get the Dart protoc plugin is by running
+
+```sh
+$ pub global activate protoc_plugin
+```
+
+and follow the directions to add `~/.pub-cache/bin` to your PATH, if you haven't
+already done so.
+
+You can now regenerate the Dart files by running
+
+```sh
+$ protoc --dart_out=grpc:lib/src/generated -Iprotos protos/helloworld.proto
+```
diff --git a/grpc/example/helloworld/bin/client.dart b/grpc/example/helloworld/bin/client.dart
new file mode 100644
index 0000000..6f44dbf
--- /dev/null
+++ b/grpc/example/helloworld/bin/client.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2018, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Dart implementation of the gRPC helloworld.Greeter client.
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'package:helloworld/src/generated/helloworld.pb.dart';
+import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
+
+Future<void> main(List<String> args) async {
+  final channel = new ClientChannel('localhost',
+      port: 50051,
+      options: const ChannelOptions(
+          credentials: const ChannelCredentials.insecure()));
+  final stub = new GreeterClient(channel);
+
+  final name = args.isNotEmpty ? args[0] : 'world';
+
+  try {
+    final response = await stub.sayHello(new HelloRequest()..name = name);
+    print('Greeter client received: ${response.message}');
+  } catch (e) {
+    print('Caught error: $e');
+  }
+  await channel.shutdown();
+}
diff --git a/grpc/example/helloworld/bin/server.dart b/grpc/example/helloworld/bin/server.dart
new file mode 100644
index 0000000..cfed15d
--- /dev/null
+++ b/grpc/example/helloworld/bin/server.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2018, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Dart implementation of the gRPC helloworld.Greeter server.
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'package:helloworld/src/generated/helloworld.pb.dart';
+import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
+
+class GreeterService extends GreeterServiceBase {
+  @override
+  Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {
+    return new HelloReply()..message = 'Hello, ${request.name}!';
+  }
+}
+
+Future<void> main(List<String> args) async {
+  final server = new Server([new GreeterService()]);
+  await server.serve(port: 50051);
+  print('Server listening on port ${server.port}...');
+}
diff --git a/grpc/example/helloworld/lib/src/generated/helloworld.pb.dart b/grpc/example/helloworld/lib/src/generated/helloworld.pb.dart
new file mode 100644
index 0000000..d441d8c
--- /dev/null
+++ b/grpc/example/helloworld/lib/src/generated/helloworld.pb.dart
@@ -0,0 +1,84 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library helloworld_helloworld;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class HelloRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('HelloRequest')
+    ..aOS(1, 'name')
+    ..hasRequiredFields = false;
+
+  HelloRequest() : super();
+  HelloRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  HelloRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  HelloRequest clone() => new HelloRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static HelloRequest create() => new HelloRequest();
+  static PbList<HelloRequest> createRepeated() => new PbList<HelloRequest>();
+  static HelloRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyHelloRequest();
+    return _defaultInstance;
+  }
+
+  static HelloRequest _defaultInstance;
+  static void $checkItem(HelloRequest v) {
+    if (v is! HelloRequest) checkItemFailed(v, 'HelloRequest');
+  }
+
+  String get name => $_getS(0, '');
+  set name(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasName() => $_has(0);
+  void clearName() => clearField(1);
+}
+
+class _ReadonlyHelloRequest extends HelloRequest with ReadonlyMessageMixin {}
+
+class HelloReply extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('HelloReply')
+    ..aOS(1, 'message')
+    ..hasRequiredFields = false;
+
+  HelloReply() : super();
+  HelloReply.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  HelloReply.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  HelloReply clone() => new HelloReply()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static HelloReply create() => new HelloReply();
+  static PbList<HelloReply> createRepeated() => new PbList<HelloReply>();
+  static HelloReply getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyHelloReply();
+    return _defaultInstance;
+  }
+
+  static HelloReply _defaultInstance;
+  static void $checkItem(HelloReply v) {
+    if (v is! HelloReply) checkItemFailed(v, 'HelloReply');
+  }
+
+  String get message => $_getS(0, '');
+  set message(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasMessage() => $_has(0);
+  void clearMessage() => clearField(1);
+}
+
+class _ReadonlyHelloReply extends HelloReply with ReadonlyMessageMixin {}
diff --git a/grpc/example/helloworld/lib/src/generated/helloworld.pbenum.dart b/grpc/example/helloworld/lib/src/generated/helloworld.pbenum.dart
new file mode 100644
index 0000000..167efb8
--- /dev/null
+++ b/grpc/example/helloworld/lib/src/generated/helloworld.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library helloworld_helloworld_pbenum;
diff --git a/grpc/example/helloworld/lib/src/generated/helloworld.pbgrpc.dart b/grpc/example/helloworld/lib/src/generated/helloworld.pbgrpc.dart
new file mode 100644
index 0000000..c390483
--- /dev/null
+++ b/grpc/example/helloworld/lib/src/generated/helloworld.pbgrpc.dart
@@ -0,0 +1,49 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library helloworld_helloworld_pbgrpc;
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'helloworld.pb.dart';
+export 'helloworld.pb.dart';
+
+class GreeterClient extends Client {
+  static final _$sayHello = new ClientMethod<HelloRequest, HelloReply>(
+      '/helloworld.Greeter/SayHello',
+      (HelloRequest value) => value.writeToBuffer(),
+      (List<int> value) => new HelloReply.fromBuffer(value));
+
+  GreeterClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<HelloReply> sayHello(HelloRequest request,
+      {CallOptions options}) {
+    final call = $createCall(_$sayHello, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+}
+
+abstract class GreeterServiceBase extends Service {
+  String get $name => 'helloworld.Greeter';
+
+  GreeterServiceBase() {
+    $addMethod(new ServiceMethod<HelloRequest, HelloReply>(
+        'SayHello',
+        sayHello_Pre,
+        false,
+        false,
+        (List<int> value) => new HelloRequest.fromBuffer(value),
+        (HelloReply value) => value.writeToBuffer()));
+  }
+
+  Future<HelloReply> sayHello_Pre(ServiceCall call, Future request) async {
+    return sayHello(call, await request);
+  }
+
+  Future<HelloReply> sayHello(ServiceCall call, HelloRequest request);
+}
diff --git a/grpc/example/helloworld/lib/src/generated/helloworld.pbjson.dart b/grpc/example/helloworld/lib/src/generated/helloworld.pbjson.dart
new file mode 100644
index 0000000..ea6a1a4
--- /dev/null
+++ b/grpc/example/helloworld/lib/src/generated/helloworld.pbjson.dart
@@ -0,0 +1,19 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library helloworld_helloworld_pbjson;
+
+const HelloRequest$json = const {
+  '1': 'HelloRequest',
+  '2': const [
+    const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
+  ],
+};
+
+const HelloReply$json = const {
+  '1': 'HelloReply',
+  '2': const [
+    const {'1': 'message', '3': 1, '4': 1, '5': 9, '10': 'message'},
+  ],
+};
diff --git a/grpc/example/helloworld/protos/helloworld.proto b/grpc/example/helloworld/protos/helloworld.proto
new file mode 100644
index 0000000..be878ce
--- /dev/null
+++ b/grpc/example/helloworld/protos/helloworld.proto
@@ -0,0 +1,38 @@
+// Copyright 2015 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/grpc/example/helloworld/pubspec.yaml b/grpc/example/helloworld/pubspec.yaml
new file mode 100644
index 0000000..9f14b4a
--- /dev/null
+++ b/grpc/example/helloworld/pubspec.yaml
@@ -0,0 +1,15 @@
+name: helloworld
+description: Dart gRPC sample client and server.
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  async: '>=1.13.3 <3.0.0'
+  grpc:
+    path: ../../
+  protobuf: ^0.10.1
+
+dev_dependencies:
+  test: ^1.3.0
diff --git a/grpc/example/metadata/README.md b/grpc/example/metadata/README.md
new file mode 100644
index 0000000..947605c
--- /dev/null
+++ b/grpc/example/metadata/README.md
@@ -0,0 +1,25 @@
+# Description
+The metadata server and client demonstrate how to handle custom metadata,
+cancellation, and timeouts in Dart gRPC.
+
+See the definition of the metadata service in `protos/metadata.proto`.
+
+# Run the sample code
+To compile and run the example, assuming you are in the root of the metadata
+folder, i.e., .../example/metadata/, first get the dependencies by running:
+
+```sh
+$ pub get
+```
+
+Then, to run the server:
+
+```sh
+$ dart bin/server.dart
+```
+
+Likewise, to run the client:
+
+```sh
+$ dart bin/client.dart
+```
diff --git a/grpc/example/metadata/bin/client.dart b/grpc/example/metadata/bin/client.dart
new file mode 100644
index 0000000..f2febf5
--- /dev/null
+++ b/grpc/example/metadata/bin/client.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:metadata/src/client.dart';
+
+main(List<String> args) {
+  new Client().main(args);
+}
diff --git a/grpc/example/metadata/bin/server.dart b/grpc/example/metadata/bin/server.dart
new file mode 100644
index 0000000..0eb9b7d
--- /dev/null
+++ b/grpc/example/metadata/bin/server.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:metadata/src/server.dart';
+
+main(List<String> args) {
+  new Server().main(args);
+}
diff --git a/grpc/example/metadata/lib/src/client.dart b/grpc/example/metadata/lib/src/client.dart
new file mode 100644
index 0000000..7e3b79b
--- /dev/null
+++ b/grpc/example/metadata/lib/src/client.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'generated/metadata.pbgrpc.dart';
+
+class Client {
+  ClientChannel channel;
+  MetadataClient stub;
+
+  Future<void> main(List<String> args) async {
+    channel = new ClientChannel('127.0.0.1',
+        port: 8080,
+        options: const ChannelOptions(
+            credentials: const ChannelCredentials.insecure()));
+    stub = new MetadataClient(channel);
+    // Run all of the demos in order.
+    await runEcho();
+    await runEchoDelayCancel();
+    await runAddOneCancel();
+    await runFibonacciCancel();
+    await runFibonacciTimeout();
+    await channel.shutdown();
+  }
+
+  /// Run the echo demo.
+  ///
+  /// Send custom metadata with a RPC, and print out the received response and
+  /// metadata.
+  Future<void> runEcho() async {
+    final request = new Record()..value = 'Kaj';
+    final call = stub.echo(request,
+        options: new CallOptions(metadata: {'peer': 'Verner'}));
+    call.headers.then((headers) {
+      print('Received header metadata: $headers');
+    });
+    call.trailers.then((trailers) {
+      print('Received trailer metadata: $trailers');
+    });
+    final response = await call;
+    print('Echo response: ${response.value}');
+  }
+
+  /// Run the echo with delay cancel demo.
+  ///
+  /// Same as the echo demo, but demonstrating per-client custom metadata, as
+  /// well as a per-call metadata. The server will delay the response for the
+  /// requested duration, during which the client will cancel the RPC.
+  Future<void> runEchoDelayCancel() async {
+    final stubWithCustomOptions = new MetadataClient(channel,
+        options: new CallOptions(metadata: {'peer': 'Verner'}));
+    final request = new Record()..value = 'Kaj';
+    final call = stubWithCustomOptions.echo(request,
+        options: new CallOptions(metadata: {'delay': '1'}));
+    call.headers.then((headers) {
+      print('Received header metadata: $headers');
+    });
+    call.trailers.then((trailers) {
+      print('Received trailer metadata: $trailers');
+    });
+    await new Future.delayed(new Duration(milliseconds: 10));
+    call.cancel();
+    try {
+      final response = await call;
+      print('Unexpected echo response: ${response.value}');
+    } catch (error) {
+      print('Expected error: $error');
+    }
+  }
+
+  /// Run the addOne cancel demo.
+  ///
+  /// Makes a bi-directional RPC, sends 4 requests, and cancels the RPC after
+  /// receiving 3 responses.
+  Future<void> runAddOneCancel() async {
+    final numbers = new StreamController<int>();
+    final call =
+        stub.addOne(numbers.stream.map((value) => new Number()..value = value));
+    final receivedThree = new Completer<bool>();
+    final sub = call.listen((number) {
+      print('AddOneCancel: Received ${number.value}');
+      if (number.value == 3) {
+        receivedThree.complete(true);
+      }
+    }, onError: (e) => print('Caught: $e'));
+    numbers.add(1);
+    numbers.add(2);
+    numbers.add(3);
+    numbers.add(4);
+    await receivedThree.future;
+    await call.cancel();
+    await Future.wait([sub.cancel(), numbers.close()]);
+  }
+
+  /// Run the Fibonacci demo.
+  ///
+  /// Call an RPC that returns a stream of Fibonacci numbers. Cancel the call
+  /// after receiving more than 5 responses.
+  Future<void> runFibonacciCancel() async {
+    final call = stub.fibonacci(new Empty());
+    int count = 0;
+    try {
+      await for (var number in call) {
+        count++;
+        print('Received ${number.value} (count=$count)');
+        if (count > 5) {
+          await call.cancel();
+        }
+      }
+    } on GrpcError catch (e) {
+      print('Caught: $e');
+    }
+    print('Final count: $count');
+  }
+
+  /// Run the timeout demo.
+  ///
+  /// Call an RPC that returns a stream of Fibonacci numbers, and specify an RPC
+  /// timeout of 2 seconds.
+  Future<void> runFibonacciTimeout() async {
+    final call = stub.fibonacci(new Empty(),
+        options: new CallOptions(timeout: new Duration(seconds: 2)));
+    int count = 0;
+    try {
+      await for (var number in call) {
+        count++;
+        print('Received ${number.value} (count=$count)');
+      }
+    } on GrpcError catch (e) {
+      print('Caught: $e');
+    }
+    print('Final count: $count');
+  }
+}
diff --git a/grpc/example/metadata/lib/src/generated/metadata.pb.dart b/grpc/example/metadata/lib/src/generated/metadata.pb.dart
new file mode 100644
index 0000000..f5390b5
--- /dev/null
+++ b/grpc/example/metadata/lib/src/generated/metadata.pb.dart
@@ -0,0 +1,108 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc_metadata;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class Record extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Record')
+    ..aOS(1, 'value')
+    ..hasRequiredFields = false;
+
+  Record() : super();
+  Record.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Record.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Record clone() => new Record()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Record create() => new Record();
+  static PbList<Record> createRepeated() => new PbList<Record>();
+  static Record getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyRecord();
+    return _defaultInstance;
+  }
+
+  static Record _defaultInstance;
+  static void $checkItem(Record v) {
+    if (v is! Record) checkItemFailed(v, 'Record');
+  }
+
+  String get value => $_getS(0, '');
+  set value(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasValue() => $_has(0);
+  void clearValue() => clearField(1);
+}
+
+class _ReadonlyRecord extends Record with ReadonlyMessageMixin {}
+
+class Number extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Number')
+    ..a<int>(1, 'value', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  Number() : super();
+  Number.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Number.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Number clone() => new Number()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Number create() => new Number();
+  static PbList<Number> createRepeated() => new PbList<Number>();
+  static Number getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyNumber();
+    return _defaultInstance;
+  }
+
+  static Number _defaultInstance;
+  static void $checkItem(Number v) {
+    if (v is! Number) checkItemFailed(v, 'Number');
+  }
+
+  int get value => $_get(0, 0);
+  set value(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasValue() => $_has(0);
+  void clearValue() => clearField(1);
+}
+
+class _ReadonlyNumber extends Number with ReadonlyMessageMixin {}
+
+class Empty extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Empty')
+    ..hasRequiredFields = false;
+
+  Empty() : super();
+  Empty.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Empty.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Empty clone() => new Empty()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Empty create() => new Empty();
+  static PbList<Empty> createRepeated() => new PbList<Empty>();
+  static Empty getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyEmpty();
+    return _defaultInstance;
+  }
+
+  static Empty _defaultInstance;
+  static void $checkItem(Empty v) {
+    if (v is! Empty) checkItemFailed(v, 'Empty');
+  }
+}
+
+class _ReadonlyEmpty extends Empty with ReadonlyMessageMixin {}
diff --git a/grpc/example/metadata/lib/src/generated/metadata.pbgrpc.dart b/grpc/example/metadata/lib/src/generated/metadata.pbgrpc.dart
new file mode 100644
index 0000000..e7de680
--- /dev/null
+++ b/grpc/example/metadata/lib/src/generated/metadata.pbgrpc.dart
@@ -0,0 +1,87 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc_metadata_pbgrpc;
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'metadata.pb.dart';
+export 'metadata.pb.dart';
+
+class MetadataClient extends Client {
+  static final _$echo = new ClientMethod<Record, Record>(
+      '/grpc.Metadata/Echo',
+      (Record value) => value.writeToBuffer(),
+      (List<int> value) => new Record.fromBuffer(value));
+  static final _$addOne = new ClientMethod<Number, Number>(
+      '/grpc.Metadata/AddOne',
+      (Number value) => value.writeToBuffer(),
+      (List<int> value) => new Number.fromBuffer(value));
+  static final _$fibonacci = new ClientMethod<Empty, Number>(
+      '/grpc.Metadata/Fibonacci',
+      (Empty value) => value.writeToBuffer(),
+      (List<int> value) => new Number.fromBuffer(value));
+
+  MetadataClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<Record> echo(Record request, {CallOptions options}) {
+    final call = $createCall(_$echo, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseStream<Number> addOne(Stream<Number> request, {CallOptions options}) {
+    final call = $createCall(_$addOne, request, options: options);
+    return new ResponseStream(call);
+  }
+
+  ResponseStream<Number> fibonacci(Empty request, {CallOptions options}) {
+    final call = $createCall(_$fibonacci, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseStream(call);
+  }
+}
+
+abstract class MetadataServiceBase extends Service {
+  String get $name => 'grpc.Metadata';
+
+  MetadataServiceBase() {
+    $addMethod(new ServiceMethod<Record, Record>(
+        'Echo',
+        echo_Pre,
+        false,
+        false,
+        (List<int> value) => new Record.fromBuffer(value),
+        (Record value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<Number, Number>(
+        'AddOne',
+        addOne,
+        true,
+        true,
+        (List<int> value) => new Number.fromBuffer(value),
+        (Number value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<Empty, Number>(
+        'Fibonacci',
+        fibonacci_Pre,
+        false,
+        true,
+        (List<int> value) => new Empty.fromBuffer(value),
+        (Number value) => value.writeToBuffer()));
+  }
+
+  Future<Record> echo_Pre(ServiceCall call, Future request) async {
+    return echo(call, await request);
+  }
+
+  Stream<Number> fibonacci_Pre(ServiceCall call, Future request) async* {
+    yield* fibonacci(call, (await request) as Empty);
+  }
+
+  Future<Record> echo(ServiceCall call, Record request);
+  Stream<Number> addOne(ServiceCall call, Stream<Number> request);
+  Stream<Number> fibonacci(ServiceCall call, Empty request);
+}
diff --git a/grpc/example/metadata/lib/src/server.dart b/grpc/example/metadata/lib/src/server.dart
new file mode 100644
index 0000000..96344c9
--- /dev/null
+++ b/grpc/example/metadata/lib/src/server.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart' as grpc;
+
+import 'generated/metadata.pbgrpc.dart';
+
+class MetadataService extends MetadataServiceBase {
+  int callCount = 0;
+
+  @override
+  Future<Record> echo(grpc.ServiceCall call, Record request) async {
+    final peer = call.clientMetadata['peer'];
+    final count = callCount++;
+    print('Echo: Call #$count: Peer: $peer, request: ${request.value}');
+    call.headers['count'] = '${count}';
+    call.trailers['hello'] = request.value;
+
+    final delay = call.clientMetadata['delay'];
+    if (delay != null) {
+      await new Future.delayed(new Duration(seconds: int.parse(delay)));
+    }
+
+    return new Record()..value = peer;
+  }
+
+  @override
+  Stream<Number> addOne(grpc.ServiceCall call, Stream<Number> request) async* {
+    int lastNumber = -1;
+    try {
+      await for (var number in request) {
+        lastNumber = number.value;
+        yield new Number()..value = number.value + 1;
+      }
+    } catch (error) {
+      print('Caught: $error, last number = $lastNumber');
+    } finally {
+      if (call.isCanceled) {
+        print('AddOne: Call canceled');
+      }
+    }
+  }
+
+  /// Streams a Fibonacci number every 500ms until the call is canceled.
+  Stream<Number> fibonacci(grpc.ServiceCall call, Empty request) async* {
+    int previous = 0;
+    int current = 1;
+    try {
+      while (true) {
+        await new Future.delayed(new Duration(milliseconds: 500));
+        yield new Number()..value = current;
+        final next = current + previous;
+        previous = current;
+        current = next;
+      }
+    } finally {
+      if (call.isCanceled) {
+        print('Fibonacci: Canceled.');
+      }
+    }
+  }
+}
+
+class Server {
+  Future<void> main(List<String> args) async {
+    final server = new grpc.Server([new MetadataService()]);
+    await server.serve(port: 8080);
+    print('Server listening on port ${server.port}...');
+  }
+}
diff --git a/grpc/example/metadata/protos/metadata.proto b/grpc/example/metadata/protos/metadata.proto
new file mode 100644
index 0000000..9b6a63f
--- /dev/null
+++ b/grpc/example/metadata/protos/metadata.proto
@@ -0,0 +1,67 @@
+// Copyright 2017, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package grpc;
+
+// Interface exported by the server.
+service Metadata {
+  // Echo metadata.
+  //
+  // Echoes the given input as trailer metadata. Sets a call counter as header
+  // metadata, and returns the value of the 'hello' key in the client metadata
+  // as the result.
+  rpc Echo(Record) returns (Record) {}
+
+  // Adds 1 to the numbers in the request stream.
+  //
+  // Uses bidirectional streaming.
+  rpc AddOne(stream Number) returns (stream Number) {}
+
+  // Fibonacci.
+  //
+  // Streams Fibonacci numbers until the call is canceled or times out.
+  rpc Fibonacci(Empty) returns (stream Number) {}
+}
+
+// A message containing a single string value.
+message Record {
+  string value = 1;
+}
+
+// A message containing a single number.
+message Number {
+  int32 value = 1;
+}
+
+// A message containing nothing.
+message Empty {
+}
+
diff --git a/grpc/example/metadata/pubspec.yaml b/grpc/example/metadata/pubspec.yaml
new file mode 100644
index 0000000..7dfd33c
--- /dev/null
+++ b/grpc/example/metadata/pubspec.yaml
@@ -0,0 +1,15 @@
+name: metadata
+description: Dart gRPC sample client and server.
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  async: '>=1.13.3 <3.0.0'
+  grpc:
+    path: ../../
+  protobuf: ^0.10.1
+
+dev_dependencies:
+  test: ^1.3.0
diff --git a/grpc/example/metadata/tool/regenerate.sh b/grpc/example/metadata/tool/regenerate.sh
new file mode 100755
index 0000000..65e2a27
--- /dev/null
+++ b/grpc/example/metadata/tool/regenerate.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+protoc --dart_out=grpc:lib/src/generated -Iprotos protos/metadata.proto
+rm lib/src/generated/metadata.pb{enum,json}.dart
+dartfmt -w lib/src/generated
diff --git a/grpc/example/route_guide/README.md b/grpc/example/route_guide/README.md
new file mode 100644
index 0000000..ece2692
--- /dev/null
+++ b/grpc/example/route_guide/README.md
@@ -0,0 +1,53 @@
+# Description
+The route guide server and client demonstrate how to use Dart gRPC libraries to
+perform unary, client streaming, server streaming and full duplex RPCs.
+
+See the definition of the route guide service in `protos/route_guide.proto`.
+
+# Run the sample code
+To compile and run the example, assuming you are in the root of the route_guide
+folder, i.e., .../example/route_guide/, first get the dependencies by running:
+
+```sh
+$ pub get
+```
+
+Then, to run the server:
+
+```sh
+$ dart bin/server.dart
+```
+
+Likewise, to run the client:
+
+```sh
+$ dart bin/client.dart
+```
+
+# Regenerate the stubs
+
+If you have made changes to the message or service definition in
+`protos/route_guide.proto` and need to regenerate the corresponding Dart files,
+you will need to have protoc version 3.0.0 or higher and the Dart protoc plugin
+version 0.7.9 or higher on your PATH.
+
+To install protoc with Dart support, take these steps:
+
+1. Install the `protoc` matching your development operating system from
+[the Protocol Buffers releases page](https://github.com/google/protobuf/releases)
+(e.g. `protoc-3.5.1-osx-x86_64.zip` for macOS).
+
+1. Get the Dart protoc plugin by running
+
+    ```sh
+    $ pub global activate protoc_plugin
+    ```
+
+1. Add `~/.pub-cache/bin` to your PATH, if you haven't
+already done so.
+
+You can now regenerate the Dart files by running
+
+```sh
+$ protoc --dart_out=grpc:lib/src/generated -Iprotos protos/route_guide.proto
+```
diff --git a/grpc/example/route_guide/bin/client.dart b/grpc/example/route_guide/bin/client.dart
new file mode 100644
index 0000000..662666e
--- /dev/null
+++ b/grpc/example/route_guide/bin/client.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:route_guide/src/client.dart';
+
+main(List<String> args) async {
+  await new Client().main(args);
+}
diff --git a/grpc/example/route_guide/bin/server.dart b/grpc/example/route_guide/bin/server.dart
new file mode 100644
index 0000000..fada4af
--- /dev/null
+++ b/grpc/example/route_guide/bin/server.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:route_guide/src/server.dart';
+
+main(List<String> args) async {
+  await new Server().main(args);
+}
diff --git a/grpc/example/route_guide/data/route_guide_db.json b/grpc/example/route_guide/data/route_guide_db.json
new file mode 100644
index 0000000..9d6a980
--- /dev/null
+++ b/grpc/example/route_guide/data/route_guide_db.json
@@ -0,0 +1,601 @@
+[{
+    "location": {
+        "latitude": 407838351,
+        "longitude": -746143763
+    },
+    "name": "Patriots Path, Mendham, NJ 07945, USA"
+}, {
+    "location": {
+        "latitude": 408122808,
+        "longitude": -743999179
+    },
+    "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
+}, {
+    "location": {
+        "latitude": 413628156,
+        "longitude": -749015468
+    },
+    "name": "U.S. 6, Shohola, PA 18458, USA"
+}, {
+    "location": {
+        "latitude": 419999544,
+        "longitude": -740371136
+    },
+    "name": "5 Conners Road, Kingston, NY 12401, USA"
+}, {
+    "location": {
+        "latitude": 414008389,
+        "longitude": -743951297
+    },
+    "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
+}, {
+    "location": {
+        "latitude": 419611318,
+        "longitude": -746524769
+    },
+    "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
+}, {
+    "location": {
+        "latitude": 406109563,
+        "longitude": -742186778
+    },
+    "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
+}, {
+    "location": {
+        "latitude": 416802456,
+        "longitude": -742370183
+    },
+    "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
+}, {
+    "location": {
+        "latitude": 412950425,
+        "longitude": -741077389
+    },
+    "name": "Bailey Turn Road, Harriman, NY 10926, USA"
+}, {
+    "location": {
+        "latitude": 412144655,
+        "longitude": -743949739
+    },
+    "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
+}, {
+    "location": {
+        "latitude": 415736605,
+        "longitude": -742847522
+    },
+    "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
+}, {
+    "location": {
+        "latitude": 413843930,
+        "longitude": -740501726
+    },
+    "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
+}, {
+    "location": {
+        "latitude": 410873075,
+        "longitude": -744459023
+    },
+    "name": "Clinton Road, West Milford, NJ 07480, USA"
+}, {
+    "location": {
+        "latitude": 412346009,
+        "longitude": -744026814
+    },
+    "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
+}, {
+    "location": {
+        "latitude": 402948455,
+        "longitude": -747903913
+    },
+    "name": "3 Drake Lane, Pennington, NJ 08534, USA"
+}, {
+    "location": {
+        "latitude": 406337092,
+        "longitude": -740122226
+    },
+    "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
+}, {
+    "location": {
+        "latitude": 406421967,
+        "longitude": -747727624
+    },
+    "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
+}, {
+    "location": {
+        "latitude": 416318082,
+        "longitude": -749677716
+    },
+    "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
+}, {
+    "location": {
+        "latitude": 415301720,
+        "longitude": -748416257
+    },
+    "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
+}, {
+    "location": {
+        "latitude": 402647019,
+        "longitude": -747071791
+    },
+    "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
+}, {
+    "location": {
+        "latitude": 412567807,
+        "longitude": -741058078
+    },
+    "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
+}, {
+    "location": {
+        "latitude": 416855156,
+        "longitude": -744420597
+    },
+    "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
+}, {
+    "location": {
+        "latitude": 404663628,
+        "longitude": -744820157
+    },
+    "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
+}, {
+    "location": {
+        "latitude": 407113723,
+        "longitude": -749746483
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 402133926,
+        "longitude": -743613249
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 400273442,
+        "longitude": -741220915
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 411236786,
+        "longitude": -744070769
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 411633782,
+        "longitude": -746784970
+    },
+    "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
+}, {
+    "location": {
+        "latitude": 415830701,
+        "longitude": -742952812
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 413447164,
+        "longitude": -748712898
+    },
+    "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
+}, {
+    "location": {
+        "latitude": 405047245,
+        "longitude": -749800722
+    },
+    "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
+}, {
+    "location": {
+        "latitude": 418858923,
+        "longitude": -746156790
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 417951888,
+        "longitude": -748484944
+    },
+    "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
+}, {
+    "location": {
+        "latitude": 407033786,
+        "longitude": -743977337
+    },
+    "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
+}, {
+    "location": {
+        "latitude": 417548014,
+        "longitude": -740075041
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 410395868,
+        "longitude": -744972325
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404615353,
+        "longitude": -745129803
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 406589790,
+        "longitude": -743560121
+    },
+    "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
+}, {
+    "location": {
+        "latitude": 414653148,
+        "longitude": -740477477
+    },
+    "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
+}, {
+    "location": {
+        "latitude": 405957808,
+        "longitude": -743255336
+    },
+    "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
+}, {
+    "location": {
+        "latitude": 411733589,
+        "longitude": -741648093
+    },
+    "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
+}, {
+    "location": {
+        "latitude": 412676291,
+        "longitude": -742606606
+    },
+    "name": "1270 Lakes Road, Monroe, NY 10950, USA"
+}, {
+    "location": {
+        "latitude": 409224445,
+        "longitude": -748286738
+    },
+    "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
+}, {
+    "location": {
+        "latitude": 406523420,
+        "longitude": -742135517
+    },
+    "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
+}, {
+    "location": {
+        "latitude": 401827388,
+        "longitude": -740294537
+    },
+    "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
+}, {
+    "location": {
+        "latitude": 410564152,
+        "longitude": -743685054
+    },
+    "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
+}, {
+    "location": {
+        "latitude": 408472324,
+        "longitude": -740726046
+    },
+    "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
+}, {
+    "location": {
+        "latitude": 412452168,
+        "longitude": -740214052
+    },
+    "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
+}, {
+    "location": {
+        "latitude": 409146138,
+        "longitude": -746188906
+    },
+    "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
+}, {
+    "location": {
+        "latitude": 404701380,
+        "longitude": -744781745
+    },
+    "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
+}, {
+    "location": {
+        "latitude": 409642566,
+        "longitude": -746017679
+    },
+    "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
+}, {
+    "location": {
+        "latitude": 408031728,
+        "longitude": -748645385
+    },
+    "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
+}, {
+    "location": {
+        "latitude": 413700272,
+        "longitude": -742135189
+    },
+    "name": "367 Prospect Road, Chester, NY 10918, USA"
+}, {
+    "location": {
+        "latitude": 404310607,
+        "longitude": -740282632
+    },
+    "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
+}, {
+    "location": {
+        "latitude": 409319800,
+        "longitude": -746201391
+    },
+    "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
+}, {
+    "location": {
+        "latitude": 406685311,
+        "longitude": -742108603
+    },
+    "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
+}, {
+    "location": {
+        "latitude": 419018117,
+        "longitude": -749142781
+    },
+    "name": "43 Dreher Road, Roscoe, NY 12776, USA"
+}, {
+    "location": {
+        "latitude": 412856162,
+        "longitude": -745148837
+    },
+    "name": "Swan Street, Pine Island, NY 10969, USA"
+}, {
+    "location": {
+        "latitude": 416560744,
+        "longitude": -746721964
+    },
+    "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
+}, {
+    "location": {
+        "latitude": 405314270,
+        "longitude": -749836354
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 414219548,
+        "longitude": -743327440
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 415534177,
+        "longitude": -742900616
+    },
+    "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
+}, {
+    "location": {
+        "latitude": 406898530,
+        "longitude": -749127080
+    },
+    "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
+}, {
+    "location": {
+        "latitude": 407586880,
+        "longitude": -741670168
+    },
+    "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
+}, {
+    "location": {
+        "latitude": 400106455,
+        "longitude": -742870190
+    },
+    "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
+}, {
+    "location": {
+        "latitude": 400066188,
+        "longitude": -746793294
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 418803880,
+        "longitude": -744102673
+    },
+    "name": "40 Mountain Road, Napanoch, NY 12458, USA"
+}, {
+    "location": {
+        "latitude": 414204288,
+        "longitude": -747895140
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 414777405,
+        "longitude": -740615601
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 415464475,
+        "longitude": -747175374
+    },
+    "name": "48 North Road, Forestburgh, NY 12777, USA"
+}, {
+    "location": {
+        "latitude": 404062378,
+        "longitude": -746376177
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 405688272,
+        "longitude": -749285130
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 400342070,
+        "longitude": -748788996
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 401809022,
+        "longitude": -744157964
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404226644,
+        "longitude": -740517141
+    },
+    "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
+}, {
+    "location": {
+        "latitude": 410322033,
+        "longitude": -747871659
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 407100674,
+        "longitude": -747742727
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 418811433,
+        "longitude": -741718005
+    },
+    "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
+}, {
+    "location": {
+        "latitude": 415034302,
+        "longitude": -743850945
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 411349992,
+        "longitude": -743694161
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404839914,
+        "longitude": -744759616
+    },
+    "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
+}, {
+    "location": {
+        "latitude": 414638017,
+        "longitude": -745957854
+    },
+    "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
+}, {
+    "location": {
+        "latitude": 412127800,
+        "longitude": -740173578
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 401263460,
+        "longitude": -747964303
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 412843391,
+        "longitude": -749086026
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 418512773,
+        "longitude": -743067823
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404318328,
+        "longitude": -740835638
+    },
+    "name": "42-102 Main Street, Belford, NJ 07718, USA"
+}, {
+    "location": {
+        "latitude": 419020746,
+        "longitude": -741172328
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404080723,
+        "longitude": -746119569
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 401012643,
+        "longitude": -744035134
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 404306372,
+        "longitude": -741079661
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 403966326,
+        "longitude": -748519297
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 405002031,
+        "longitude": -748407866
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 409532885,
+        "longitude": -742200683
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 416851321,
+        "longitude": -742674555
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 406411633,
+        "longitude": -741722051
+    },
+    "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
+}, {
+    "location": {
+        "latitude": 413069058,
+        "longitude": -744597778
+    },
+    "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
+}, {
+    "location": {
+        "latitude": 418465462,
+        "longitude": -746859398
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 411733222,
+        "longitude": -744228360
+    },
+    "name": ""
+}, {
+    "location": {
+        "latitude": 410248224,
+        "longitude": -747127767
+    },
+    "name": "3 Hasta Way, Newton, NJ 07860, USA"
+}]
diff --git a/grpc/example/route_guide/lib/src/client.dart b/grpc/example/route_guide/lib/src/client.dart
new file mode 100644
index 0000000..b893779
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/client.dart
@@ -0,0 +1,152 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:math' show Random;
+
+import 'package:grpc/grpc.dart';
+
+import 'common.dart';
+import 'generated/route_guide.pb.dart';
+import 'generated/route_guide.pbgrpc.dart';
+
+class Client {
+  ClientChannel channel;
+  RouteGuideClient stub;
+
+  Future<void> main(List<String> args) async {
+    channel = new ClientChannel('127.0.0.1',
+        port: 8080,
+        options: const ChannelOptions(
+            credentials: const ChannelCredentials.insecure()));
+    stub = new RouteGuideClient(channel,
+        options: new CallOptions(timeout: new Duration(seconds: 30)));
+    // Run all of the demos in order.
+    try {
+      await runGetFeature();
+      await runListFeatures();
+      await runRecordRoute();
+      await runRouteChat();
+    } catch (e) {
+      print('Caught error: $e');
+    }
+    await channel.shutdown();
+  }
+
+  void printFeature(Feature feature) {
+    final latitude = feature.location.latitude;
+    final longitude = feature.location.longitude;
+    final name = feature.name.isEmpty
+        ? 'no feature'
+        : 'feature called "${feature.name}"';
+    print(
+        'Found $name at ${latitude / coordFactor}, ${longitude / coordFactor}');
+  }
+
+  /// Run the getFeature demo. Calls getFeature with a point known to have a
+  /// feature and a point known not to have a feature.
+  Future<void> runGetFeature() async {
+    final point1 = new Point()
+      ..latitude = 409146138
+      ..longitude = -746188906;
+    final point2 = new Point()
+      ..latitude = 0
+      ..longitude = 0;
+
+    printFeature(await stub.getFeature(point1));
+    printFeature(await stub.getFeature(point2));
+  }
+
+  /// Run the listFeatures demo. Calls listFeatures with a rectangle containing
+  /// all of the features in the pre-generated database. Prints each response as
+  /// it comes in.
+  Future<void> runListFeatures() async {
+    final lo = new Point()
+      ..latitude = 400000000
+      ..longitude = -750000000;
+    final hi = new Point()
+      ..latitude = 420000000
+      ..longitude = -730000000;
+    final rect = new Rectangle()
+      ..lo = lo
+      ..hi = hi;
+
+    print('Looking for features between 40, -75 and 42, -73');
+    await for (var feature in stub.listFeatures(rect)) {
+      printFeature(feature);
+    }
+  }
+
+  /// Run the recordRoute demo. Sends several randomly chosen points from the
+  /// pre-generated feature database with a variable delay in between. Prints
+  /// the statistics when they are sent from the server.
+  Future<void> runRecordRoute() async {
+    Stream<Point> generateRoute(int count) async* {
+      final random = new Random();
+
+      for (int i = 0; i < count; i++) {
+        final point = featuresDb[random.nextInt(featuresDb.length)].location;
+        print(
+            'Visiting point ${point.latitude / coordFactor}, ${point.longitude /
+                coordFactor}');
+        yield point;
+        await new Future.delayed(
+            new Duration(milliseconds: 200 + random.nextInt(100)));
+      }
+    }
+
+    final summary = await stub.recordRoute(generateRoute(10));
+    print('Finished trip with ${summary.pointCount} points');
+    print('Passed ${summary.featureCount} features');
+    print('Travelled ${summary.distance} meters');
+    print('It took ${summary.elapsedTime} seconds');
+  }
+
+  /// Run the routeChat demo. Send some chat messages, and print any chat
+  /// messages that are sent from the server.
+  Future<void> runRouteChat() async {
+    RouteNote createNote(String message, int latitude, int longitude) {
+      final location = new Point()
+        ..latitude = latitude
+        ..longitude = longitude;
+      return new RouteNote()
+        ..message = message
+        ..location = location;
+    }
+
+    final notes = <RouteNote>[
+      createNote('First message', 0, 0),
+      createNote('Second message', 0, 1),
+      createNote('Third message', 1, 0),
+      createNote('Fourth message', 0, 0),
+    ];
+
+    Stream<RouteNote> outgoingNotes() async* {
+      for (final note in notes) {
+        // Short delay to simulate some other interaction.
+        await new Future.delayed(new Duration(milliseconds: 10));
+        print('Sending message ${note.message} at ${note.location.latitude}, '
+            '${note.location.longitude}');
+        yield note;
+      }
+    }
+
+    final call = stub.routeChat(outgoingNotes());
+    await for (var note in call) {
+      print('Got message ${note.message} at ${note.location.latitude}, ${note
+          .location.longitude}');
+    }
+  }
+}
diff --git a/grpc/example/route_guide/lib/src/common.dart b/grpc/example/route_guide/lib/src/common.dart
new file mode 100644
index 0000000..f7b2992
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/common.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'generated/route_guide.pb.dart';
+
+const coordFactor = 1e7;
+
+final List<Feature> featuresDb = _readDatabase();
+
+List<Feature> _readDatabase() {
+  final dbData = new File('data/route_guide_db.json').readAsStringSync();
+  final List db = jsonDecode(dbData);
+  return db.map((entry) {
+    final location = new Point()
+      ..latitude = entry['location']['latitude']
+      ..longitude = entry['location']['longitude'];
+    return new Feature()
+      ..name = entry['name']
+      ..location = location;
+  }).toList();
+}
diff --git a/grpc/example/route_guide/lib/src/generated/route_guide.pb.dart b/grpc/example/route_guide/lib/src/generated/route_guide.pb.dart
new file mode 100644
index 0000000..842adf4
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/generated/route_guide.pb.dart
@@ -0,0 +1,254 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library routeguide_route_guide;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class Point extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Point')
+    ..a<int>(1, 'latitude', PbFieldType.O3)
+    ..a<int>(2, 'longitude', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  Point() : super();
+  Point.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Point.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Point clone() => new Point()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Point create() => new Point();
+  static PbList<Point> createRepeated() => new PbList<Point>();
+  static Point getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyPoint();
+    return _defaultInstance;
+  }
+
+  static Point _defaultInstance;
+  static void $checkItem(Point v) {
+    if (v is! Point) checkItemFailed(v, 'Point');
+  }
+
+  int get latitude => $_get(0, 0);
+  set latitude(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasLatitude() => $_has(0);
+  void clearLatitude() => clearField(1);
+
+  int get longitude => $_get(1, 0);
+  set longitude(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasLongitude() => $_has(1);
+  void clearLongitude() => clearField(2);
+}
+
+class _ReadonlyPoint extends Point with ReadonlyMessageMixin {}
+
+class Rectangle extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Rectangle')
+    ..a<Point>(1, 'lo', PbFieldType.OM, Point.getDefault, Point.create)
+    ..a<Point>(2, 'hi', PbFieldType.OM, Point.getDefault, Point.create)
+    ..hasRequiredFields = false;
+
+  Rectangle() : super();
+  Rectangle.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Rectangle.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Rectangle clone() => new Rectangle()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Rectangle create() => new Rectangle();
+  static PbList<Rectangle> createRepeated() => new PbList<Rectangle>();
+  static Rectangle getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyRectangle();
+    return _defaultInstance;
+  }
+
+  static Rectangle _defaultInstance;
+  static void $checkItem(Rectangle v) {
+    if (v is! Rectangle) checkItemFailed(v, 'Rectangle');
+  }
+
+  Point get lo => $_getN(0);
+  set lo(Point v) {
+    setField(1, v);
+  }
+
+  bool hasLo() => $_has(0);
+  void clearLo() => clearField(1);
+
+  Point get hi => $_getN(1);
+  set hi(Point v) {
+    setField(2, v);
+  }
+
+  bool hasHi() => $_has(1);
+  void clearHi() => clearField(2);
+}
+
+class _ReadonlyRectangle extends Rectangle with ReadonlyMessageMixin {}
+
+class Feature extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Feature')
+    ..aOS(1, 'name')
+    ..a<Point>(2, 'location', PbFieldType.OM, Point.getDefault, Point.create)
+    ..hasRequiredFields = false;
+
+  Feature() : super();
+  Feature.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Feature.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Feature clone() => new Feature()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Feature create() => new Feature();
+  static PbList<Feature> createRepeated() => new PbList<Feature>();
+  static Feature getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyFeature();
+    return _defaultInstance;
+  }
+
+  static Feature _defaultInstance;
+  static void $checkItem(Feature v) {
+    if (v is! Feature) checkItemFailed(v, 'Feature');
+  }
+
+  String get name => $_getS(0, '');
+  set name(String v) {
+    $_setString(0, v);
+  }
+
+  bool hasName() => $_has(0);
+  void clearName() => clearField(1);
+
+  Point get location => $_getN(1);
+  set location(Point v) {
+    setField(2, v);
+  }
+
+  bool hasLocation() => $_has(1);
+  void clearLocation() => clearField(2);
+}
+
+class _ReadonlyFeature extends Feature with ReadonlyMessageMixin {}
+
+class RouteNote extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('RouteNote')
+    ..a<Point>(1, 'location', PbFieldType.OM, Point.getDefault, Point.create)
+    ..aOS(2, 'message')
+    ..hasRequiredFields = false;
+
+  RouteNote() : super();
+  RouteNote.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  RouteNote.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  RouteNote clone() => new RouteNote()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static RouteNote create() => new RouteNote();
+  static PbList<RouteNote> createRepeated() => new PbList<RouteNote>();
+  static RouteNote getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyRouteNote();
+    return _defaultInstance;
+  }
+
+  static RouteNote _defaultInstance;
+  static void $checkItem(RouteNote v) {
+    if (v is! RouteNote) checkItemFailed(v, 'RouteNote');
+  }
+
+  Point get location => $_getN(0);
+  set location(Point v) {
+    setField(1, v);
+  }
+
+  bool hasLocation() => $_has(0);
+  void clearLocation() => clearField(1);
+
+  String get message => $_getS(1, '');
+  set message(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasMessage() => $_has(1);
+  void clearMessage() => clearField(2);
+}
+
+class _ReadonlyRouteNote extends RouteNote with ReadonlyMessageMixin {}
+
+class RouteSummary extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('RouteSummary')
+    ..a<int>(1, 'pointCount', PbFieldType.O3)
+    ..a<int>(2, 'featureCount', PbFieldType.O3)
+    ..a<int>(3, 'distance', PbFieldType.O3)
+    ..a<int>(4, 'elapsedTime', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  RouteSummary() : super();
+  RouteSummary.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  RouteSummary.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  RouteSummary clone() => new RouteSummary()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static RouteSummary create() => new RouteSummary();
+  static PbList<RouteSummary> createRepeated() => new PbList<RouteSummary>();
+  static RouteSummary getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyRouteSummary();
+    return _defaultInstance;
+  }
+
+  static RouteSummary _defaultInstance;
+  static void $checkItem(RouteSummary v) {
+    if (v is! RouteSummary) checkItemFailed(v, 'RouteSummary');
+  }
+
+  int get pointCount => $_get(0, 0);
+  set pointCount(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasPointCount() => $_has(0);
+  void clearPointCount() => clearField(1);
+
+  int get featureCount => $_get(1, 0);
+  set featureCount(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasFeatureCount() => $_has(1);
+  void clearFeatureCount() => clearField(2);
+
+  int get distance => $_get(2, 0);
+  set distance(int v) {
+    $_setUnsignedInt32(2, v);
+  }
+
+  bool hasDistance() => $_has(2);
+  void clearDistance() => clearField(3);
+
+  int get elapsedTime => $_get(3, 0);
+  set elapsedTime(int v) {
+    $_setUnsignedInt32(3, v);
+  }
+
+  bool hasElapsedTime() => $_has(3);
+  void clearElapsedTime() => clearField(4);
+}
+
+class _ReadonlyRouteSummary extends RouteSummary with ReadonlyMessageMixin {}
diff --git a/grpc/example/route_guide/lib/src/generated/route_guide.pbenum.dart b/grpc/example/route_guide/lib/src/generated/route_guide.pbenum.dart
new file mode 100644
index 0000000..11b7aee
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/generated/route_guide.pbenum.dart
@@ -0,0 +1,5 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library routeguide_route_guide_pbenum;
diff --git a/grpc/example/route_guide/lib/src/generated/route_guide.pbgrpc.dart b/grpc/example/route_guide/lib/src/generated/route_guide.pbgrpc.dart
new file mode 100644
index 0000000..c0826b4
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/generated/route_guide.pbgrpc.dart
@@ -0,0 +1,107 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library routeguide_route_guide_pbgrpc;
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'route_guide.pb.dart';
+export 'route_guide.pb.dart';
+
+class RouteGuideClient extends Client {
+  static final _$getFeature = new ClientMethod<Point, Feature>(
+      '/routeguide.RouteGuide/GetFeature',
+      (Point value) => value.writeToBuffer(),
+      (List<int> value) => new Feature.fromBuffer(value));
+  static final _$listFeatures = new ClientMethod<Rectangle, Feature>(
+      '/routeguide.RouteGuide/ListFeatures',
+      (Rectangle value) => value.writeToBuffer(),
+      (List<int> value) => new Feature.fromBuffer(value));
+  static final _$recordRoute = new ClientMethod<Point, RouteSummary>(
+      '/routeguide.RouteGuide/RecordRoute',
+      (Point value) => value.writeToBuffer(),
+      (List<int> value) => new RouteSummary.fromBuffer(value));
+  static final _$routeChat = new ClientMethod<RouteNote, RouteNote>(
+      '/routeguide.RouteGuide/RouteChat',
+      (RouteNote value) => value.writeToBuffer(),
+      (List<int> value) => new RouteNote.fromBuffer(value));
+
+  RouteGuideClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<Feature> getFeature(Point request, {CallOptions options}) {
+    final call = $createCall(_$getFeature, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseStream<Feature> listFeatures(Rectangle request,
+      {CallOptions options}) {
+    final call = $createCall(_$listFeatures, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseStream(call);
+  }
+
+  ResponseFuture<RouteSummary> recordRoute(Stream<Point> request,
+      {CallOptions options}) {
+    final call = $createCall(_$recordRoute, request, options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseStream<RouteNote> routeChat(Stream<RouteNote> request,
+      {CallOptions options}) {
+    final call = $createCall(_$routeChat, request, options: options);
+    return new ResponseStream(call);
+  }
+}
+
+abstract class RouteGuideServiceBase extends Service {
+  String get $name => 'routeguide.RouteGuide';
+
+  RouteGuideServiceBase() {
+    $addMethod(new ServiceMethod<Point, Feature>(
+        'GetFeature',
+        getFeature_Pre,
+        false,
+        false,
+        (List<int> value) => new Point.fromBuffer(value),
+        (Feature value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<Rectangle, Feature>(
+        'ListFeatures',
+        listFeatures_Pre,
+        false,
+        true,
+        (List<int> value) => new Rectangle.fromBuffer(value),
+        (Feature value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<Point, RouteSummary>(
+        'RecordRoute',
+        recordRoute,
+        true,
+        false,
+        (List<int> value) => new Point.fromBuffer(value),
+        (RouteSummary value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<RouteNote, RouteNote>(
+        'RouteChat',
+        routeChat,
+        true,
+        true,
+        (List<int> value) => new RouteNote.fromBuffer(value),
+        (RouteNote value) => value.writeToBuffer()));
+  }
+
+  Future<Feature> getFeature_Pre(ServiceCall call, Future request) async {
+    return getFeature(call, await request);
+  }
+
+  Stream<Feature> listFeatures_Pre(ServiceCall call, Future request) async* {
+    yield* listFeatures(call, (await request) as Rectangle);
+  }
+
+  Future<Feature> getFeature(ServiceCall call, Point request);
+  Stream<Feature> listFeatures(ServiceCall call, Rectangle request);
+  Future<RouteSummary> recordRoute(ServiceCall call, Stream<Point> request);
+  Stream<RouteNote> routeChat(ServiceCall call, Stream<RouteNote> request);
+}
diff --git a/grpc/example/route_guide/lib/src/generated/route_guide.pbjson.dart b/grpc/example/route_guide/lib/src/generated/route_guide.pbjson.dart
new file mode 100644
index 0000000..6b93028
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/generated/route_guide.pbjson.dart
@@ -0,0 +1,75 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library routeguide_route_guide_pbjson;
+
+const Point$json = const {
+  '1': 'Point',
+  '2': const [
+    const {'1': 'latitude', '3': 1, '4': 1, '5': 5, '10': 'latitude'},
+    const {'1': 'longitude', '3': 2, '4': 1, '5': 5, '10': 'longitude'},
+  ],
+};
+
+const Rectangle$json = const {
+  '1': 'Rectangle',
+  '2': const [
+    const {
+      '1': 'lo',
+      '3': 1,
+      '4': 1,
+      '5': 11,
+      '6': '.routeguide.Point',
+      '10': 'lo'
+    },
+    const {
+      '1': 'hi',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.routeguide.Point',
+      '10': 'hi'
+    },
+  ],
+};
+
+const Feature$json = const {
+  '1': 'Feature',
+  '2': const [
+    const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
+    const {
+      '1': 'location',
+      '3': 2,
+      '4': 1,
+      '5': 11,
+      '6': '.routeguide.Point',
+      '10': 'location'
+    },
+  ],
+};
+
+const RouteNote$json = const {
+  '1': 'RouteNote',
+  '2': const [
+    const {
+      '1': 'location',
+      '3': 1,
+      '4': 1,
+      '5': 11,
+      '6': '.routeguide.Point',
+      '10': 'location'
+    },
+    const {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'},
+  ],
+};
+
+const RouteSummary$json = const {
+  '1': 'RouteSummary',
+  '2': const [
+    const {'1': 'point_count', '3': 1, '4': 1, '5': 5, '10': 'pointCount'},
+    const {'1': 'feature_count', '3': 2, '4': 1, '5': 5, '10': 'featureCount'},
+    const {'1': 'distance', '3': 3, '4': 1, '5': 5, '10': 'distance'},
+    const {'1': 'elapsed_time', '3': 4, '4': 1, '5': 5, '10': 'elapsedTime'},
+  ],
+};
diff --git a/grpc/example/route_guide/lib/src/server.dart b/grpc/example/route_guide/lib/src/server.dart
new file mode 100644
index 0000000..a8e6676
--- /dev/null
+++ b/grpc/example/route_guide/lib/src/server.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:math' show atan2, cos, max, min, pi, sin, sqrt;
+
+import 'package:grpc/grpc.dart' as grpc;
+
+import 'common.dart';
+import 'generated/route_guide.pb.dart';
+import 'generated/route_guide.pbgrpc.dart';
+
+class RouteGuideService extends RouteGuideServiceBase {
+  final routeNotes = <Point, List<RouteNote>>{};
+
+  /// GetFeature handler. Returns a feature for the given location.
+  /// The [context] object provides access to client metadata, cancellation, etc.
+  @override
+  Future<Feature> getFeature(grpc.ServiceCall call, Point request) async {
+    return featuresDb.firstWhere((f) => f.location == request,
+        orElse: () => new Feature()..location = request);
+  }
+
+  Rectangle _normalize(Rectangle r) {
+    final lo = new Point()
+      ..latitude = min(r.lo.latitude, r.hi.latitude)
+      ..longitude = min(r.lo.longitude, r.hi.longitude);
+
+    final hi = new Point()
+      ..latitude = max(r.lo.latitude, r.hi.latitude)
+      ..longitude = max(r.lo.longitude, r.hi.longitude);
+
+    return new Rectangle()
+      ..lo = lo
+      ..hi = hi;
+  }
+
+  bool _contains(Rectangle r, Point p) {
+    return p.longitude >= r.lo.longitude &&
+        p.longitude <= r.hi.longitude &&
+        p.latitude >= r.lo.latitude &&
+        p.latitude <= r.hi.latitude;
+  }
+
+  /// ListFeatures handler. Returns a stream of features within the given
+  /// rectangle.
+  @override
+  Stream<Feature> listFeatures(
+      grpc.ServiceCall call, Rectangle request) async* {
+    final normalizedRectangle = _normalize(request);
+    // For each feature, check if it is in the given bounding box
+    for (var feature in featuresDb) {
+      if (feature.name.isEmpty) continue;
+      final location = feature.location;
+      if (_contains(normalizedRectangle, location)) {
+        yield feature;
+      }
+    }
+  }
+
+  /// RecordRoute handler. Gets a stream of points, and responds with statistics
+  /// about the "trip": number of points, number of known features visited,
+  /// total distance traveled, and total time spent.
+  @override
+  Future<RouteSummary> recordRoute(
+      grpc.ServiceCall call, Stream<Point> request) async {
+    int pointCount = 0;
+    int featureCount = 0;
+    double distance = 0.0;
+    Point previous;
+    final timer = new Stopwatch();
+
+    await for (var location in request) {
+      if (!timer.isRunning) timer.start();
+      pointCount++;
+      final feature = featuresDb.firstWhere((f) => f.location == location,
+          orElse: () => null);
+      if (feature != null) {
+        featureCount++;
+      }
+      // For each point after the first, add the incremental distance from the
+      // previous point to the total distance value.
+      if (previous != null) distance += _distance(previous, location);
+      previous = location;
+    }
+    timer.stop();
+    return new RouteSummary()
+      ..pointCount = pointCount
+      ..featureCount = featureCount
+      ..distance = distance.round()
+      ..elapsedTime = timer.elapsed.inSeconds;
+  }
+
+  /// RouteChat handler. Receives a stream of message/location pairs, and
+  /// responds with a stream of all previous messages at each of those
+  /// locations.
+  @override
+  Stream<RouteNote> routeChat(
+      grpc.ServiceCall call, Stream<RouteNote> request) async* {
+    await for (var note in request) {
+      final notes = routeNotes.putIfAbsent(note.location, () => <RouteNote>[]);
+      for (var note in notes) yield note;
+      notes.add(note);
+    }
+  }
+
+  /// Calculate the distance between two points using the "haversine" formula.
+  /// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
+  double _distance(Point start, Point end) {
+    double toRadians(double num) {
+      return num * pi / 180;
+    }
+
+    final lat1 = start.latitude / coordFactor;
+    final lat2 = end.latitude / coordFactor;
+    final lon1 = start.longitude / coordFactor;
+    final lon2 = end.longitude / coordFactor;
+    final R = 6371000; // metres
+    final phi1 = toRadians(lat1);
+    final phi2 = toRadians(lat2);
+    final dLat = toRadians(lat2 - lat1);
+    final dLon = toRadians(lon2 - lon1);
+
+    final a = sin(dLat / 2) * sin(dLat / 2) +
+        cos(phi1) * cos(phi2) * sin(dLon / 2) * sin(dLon / 2);
+    final c = 2 * atan2(sqrt(a), sqrt(1 - a));
+
+    return R * c;
+  }
+}
+
+class Server {
+  Future<void> main(List<String> args) async {
+    final server = new grpc.Server([new RouteGuideService()]);
+    await server.serve(port: 8080);
+    print('Server listening on port ${server.port}...');
+  }
+}
diff --git a/grpc/example/route_guide/protos/route_guide.proto b/grpc/example/route_guide/protos/route_guide.proto
new file mode 100644
index 0000000..12c4495
--- /dev/null
+++ b/grpc/example/route_guide/protos/route_guide.proto
@@ -0,0 +1,126 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.routeguide";
+option java_outer_classname = "RouteGuideProto";
+option objc_class_prefix = "RTG";
+
+package routeguide;
+
+// Interface exported by the server.
+service RouteGuide {
+  // A simple RPC.
+  //
+  // Obtains the feature at a given position.
+  //
+  // A feature with an empty name is returned if there's no feature at the given
+  // position.
+  rpc GetFeature(Point) returns (Feature) {}
+
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Features available within the given Rectangle.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field), as the rectangle may cover a large area and contain a
+  // huge number of features.
+  rpc ListFeatures(Rectangle) returns (stream Feature) {}
+
+  // A client-to-server streaming RPC.
+  //
+  // Accepts a stream of Points on a route being traversed, returning a
+  // RouteSummary when traversal is completed.
+  rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+  // A Bidirectional streaming RPC.
+  //
+  // Accepts a stream of RouteNotes sent while a route is being traversed,
+  // while receiving other RouteNotes (e.g. from other users).
+  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+message Point {
+  int32 latitude = 1;
+  int32 longitude = 2;
+}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+message Rectangle {
+  // One corner of the rectangle.
+  Point lo = 1;
+
+  // The other corner of the rectangle.
+  Point hi = 2;
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+message Feature {
+  // The name of the feature.
+  string name = 1;
+
+  // The point where the feature is detected.
+  Point location = 2;
+}
+
+// A RouteNote is a message sent while at a given point.
+message RouteNote {
+  // The location from which the message is sent.
+  Point location = 1;
+
+  // The message to be sent.
+  string message = 2;
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+message RouteSummary {
+  // The number of points received.
+  int32 point_count = 1;
+
+  // The number of known features passed while traversing the route.
+  int32 feature_count = 2;
+
+  // The distance covered in metres.
+  int32 distance = 3;
+
+  // The duration of the traversal in seconds.
+  int32 elapsed_time = 4;
+}
diff --git a/grpc/example/route_guide/pubspec.yaml b/grpc/example/route_guide/pubspec.yaml
new file mode 100644
index 0000000..b247184
--- /dev/null
+++ b/grpc/example/route_guide/pubspec.yaml
@@ -0,0 +1,15 @@
+name: route_guide
+description: Dart gRPC sample client and server.
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  async: '>=1.13.3 <3.0.0'
+  grpc:
+    path: ../../
+  protobuf: ^0.10.1
+
+dev_dependencies:
+  test: ^1.3.0
diff --git a/grpc/interop/bin/client.dart b/grpc/interop/bin/client.dart
new file mode 100644
index 0000000..be55d13
--- /dev/null
+++ b/grpc/interop/bin/client.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'package:args/args.dart';
+
+import 'package:interop/src/client.dart';
+
+const _serverHostArgument = 'server_host';
+const _serverHostOverrideArgument = 'server_host_override';
+const _serverPortArgument = 'server_port';
+const _testCaseArgument = 'test_case';
+const _useTLSArgument = 'use_tls';
+const _useTestCAArgument = 'use_test_ca';
+const _defaultServiceAccountArgument = 'default_service_account';
+const _oauthScopeArgument = 'oauth_scope';
+const _serviceAccountKeyFileArgument = 'service_account_key_file';
+
+/// Clients implement test cases that test certain functionally. Each client is
+/// provided the test case it is expected to run as a command-line parameter.
+/// Names should be lowercase and without spaces.
+///
+/// Clients should accept these arguments:
+///
+/// * --server_host=HOSTNAME
+///     * The server host to connect to. For example, "localhost" or "127.0.0.1"
+/// * --server_host_override=HOSTNAME
+///     * The server host to claim to be connecting to, for use in TLS and
+///       HTTP/2 :authority header. If unspecified, the value of --server_host
+///       will be used
+/// * --server_port=PORT
+///     * The server port to connect to. For example, "8080"
+/// * --test_case=TESTCASE
+///     * The name of the test case to execute. For example, "empty_unary"
+/// * --use_tls=BOOLEAN
+///     * Whether to use a plaintext or encrypted connection
+/// * --use_test_ca=BOOLEAN
+///     * Whether to replace platform root CAs with ca.pem as the CA root
+/// * --default_service_account=ACCOUNT_EMAIL
+///     * Email of the GCE default service account.
+/// * --oauth_scope=SCOPE
+///     * OAuth scope. For example, "https://www.googleapis.com/auth/xapi.zoo"
+/// * --service_account_key_file=PATH
+///     * The path to the service account JSON key file generated from GCE
+///       developer console.
+///
+/// Clients must support TLS with ALPN. Clients must not disable certificate
+/// checking.
+Future<int> main(List<String> args) async {
+  final argumentParser = new ArgParser();
+  argumentParser.addOption(_serverHostArgument,
+      help: 'The server host to connect to. For example, "localhost" or '
+          '"127.0.0.1".');
+  argumentParser.addOption(_serverHostOverrideArgument,
+      help: 'The server host to claim to be connecting to, for use in TLS and '
+          'HTTP/2 :authority header. If unspecified, the value of '
+          '--server_host will be used.');
+  argumentParser.addOption(_serverPortArgument,
+      help: 'The server port to connect to. For example, "8080".');
+  argumentParser.addOption(_testCaseArgument,
+      help:
+          'The name of the test case to execute. For example, "empty_unary".');
+  argumentParser.addOption(_useTLSArgument,
+      defaultsTo: 'false',
+      help: 'Whether to use a plaintext or encrypted connection.');
+  argumentParser.addOption(_useTestCAArgument,
+      help: 'Whether to replace platform root CAs with ca.pem as the CA root.');
+  argumentParser.addOption(_defaultServiceAccountArgument,
+      help: 'Email of the GCE default service account.');
+  argumentParser.addOption(_oauthScopeArgument,
+      help: 'OAuth scope. For example, '
+          '"https://www.googleapis.com/auth/xapi.zoo".');
+  argumentParser.addOption(_serviceAccountKeyFileArgument,
+      help: 'The path to the service account JSON key file generated from GCE '
+          'developer console.');
+  final arguments = argumentParser.parse(args);
+
+  final testClient = new Tester();
+
+  testClient.serverHost = arguments[_serverHostArgument];
+  testClient.serverHostOverride = arguments[_serverHostOverrideArgument];
+  testClient.serverPort = arguments[_serverPortArgument];
+  testClient.testCase = arguments[_testCaseArgument];
+  testClient.useTls = arguments[_useTLSArgument];
+  testClient.useTestCA = arguments[_useTestCAArgument];
+  testClient.defaultServiceAccount = arguments[_defaultServiceAccountArgument];
+  testClient.oauthScope = arguments[_oauthScopeArgument];
+  testClient.serviceAccountKeyFile = arguments[_serviceAccountKeyFileArgument];
+
+  if (!testClient.validate()) {
+    print(argumentParser.usage);
+    return -1;
+  }
+  await testClient.runTest();
+  print('Passed.');
+  return 0;
+}
diff --git a/grpc/interop/bin/server.dart b/grpc/interop/bin/server.dart
new file mode 100644
index 0000000..9cd7ca2
--- /dev/null
+++ b/grpc/interop/bin/server.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:grpc/grpc.dart';
+
+import 'package:interop/src/generated/empty.pb.dart';
+import 'package:interop/src/generated/messages.pb.dart';
+import 'package:interop/src/generated/test.pbgrpc.dart';
+
+const _headerEchoKey = 'x-grpc-test-echo-initial';
+const _trailerEchoKey = 'x-grpc-test-echo-trailing-bin';
+
+class TestService extends TestServiceBase {
+  @override
+  void $onMetadata(ServiceCall context) {
+    final headerEcho = context.clientMetadata[_headerEchoKey];
+    if (headerEcho != null) {
+      context.headers[_headerEchoKey] = headerEcho;
+    }
+    final trailerEcho = context.clientMetadata[_trailerEchoKey];
+    if (trailerEcho != null) {
+      context.trailers[_trailerEchoKey] = trailerEcho;
+    }
+  }
+
+  @override
+  Future<Empty> emptyCall(ServiceCall call, Empty request) async {
+    return new Empty();
+  }
+
+  @override
+  Future<SimpleResponse> unaryCall(
+      ServiceCall call, SimpleRequest request) async {
+    if (request.responseStatus.code != 0) {
+      throw new GrpcError.custom(
+          request.responseStatus.code, request.responseStatus.message);
+    }
+    final payload = new Payload()
+      ..body = new List.filled(request.responseSize, 0);
+    return new SimpleResponse()..payload = payload;
+  }
+
+  @override
+  Future<SimpleResponse> cacheableUnaryCall(
+      ServiceCall call, SimpleRequest request) async {
+    final timestamp = new DateTime.now().microsecond * 1000;
+    final responsePayload = new Payload()..body = ascii.encode('$timestamp');
+    return new SimpleResponse()..payload = responsePayload;
+  }
+
+  @override
+  Future<StreamingInputCallResponse> streamingInputCall(
+      ServiceCall call, Stream<StreamingInputCallRequest> request) async {
+    final aggregatedPayloadSize = await request.fold(
+        0, (size, message) => size + message.payload.body.length);
+    return new StreamingInputCallResponse()
+      ..aggregatedPayloadSize = aggregatedPayloadSize;
+  }
+
+  Payload _payloadForRequest(ResponseParameters entry) =>
+      new Payload()..body = new List.filled(entry.size, 0);
+
+  @override
+  Stream<StreamingOutputCallResponse> streamingOutputCall(
+      ServiceCall call, StreamingOutputCallRequest request) async* {
+    for (final entry in request.responseParameters) {
+      if (entry.intervalUs > 0) {
+        await new Future.delayed(new Duration(microseconds: entry.intervalUs));
+      }
+      yield new StreamingOutputCallResponse()
+        ..payload = _payloadForRequest(entry);
+    }
+  }
+
+  StreamingOutputCallResponse _responseForRequest(
+      StreamingOutputCallRequest request) {
+    if (request.responseStatus.code != 0) {
+      throw new GrpcError.custom(
+          request.responseStatus.code, request.responseStatus.message);
+    }
+    final response = new StreamingOutputCallResponse();
+    if (request.responseParameters.isNotEmpty) {
+      response.payload = _payloadForRequest(request.responseParameters[0]);
+    }
+    return response;
+  }
+
+  @override
+  Stream<StreamingOutputCallResponse> fullDuplexCall(
+      ServiceCall call, Stream<StreamingOutputCallRequest> request) async* {
+    yield* request.map(_responseForRequest);
+  }
+
+  @override
+  Stream<StreamingOutputCallResponse> halfDuplexCall(
+      ServiceCall call, Stream<StreamingOutputCallRequest> request) async* {
+    final bufferedResponses = await request.map(_responseForRequest).toList();
+    yield* new Stream.fromIterable(bufferedResponses);
+  }
+}
+
+Future<void> main(List<String> args) async {
+  final argumentParser = new ArgParser();
+  argumentParser.addOption('port', defaultsTo: '8080');
+  argumentParser.addOption('use_tls', defaultsTo: 'false');
+  argumentParser.addOption('tls_cert_file', defaultsTo: 'server1.pem');
+  argumentParser.addOption('tls_key_file', defaultsTo: 'server1.key');
+  final arguments = argumentParser.parse(args);
+  final port = int.parse(arguments['port']);
+
+  final services = [new TestService()];
+
+  final server = new Server(services);
+
+  ServerTlsCredentials tlsCredentials;
+  if (arguments['use_tls'] == 'true') {
+    final certificate = new File(arguments['tls_cert_file']).readAsBytes();
+    final privateKey = new File(arguments['tls_key_file']).readAsBytes();
+    tlsCredentials = new ServerTlsCredentials(
+        certificate: await certificate, privateKey: await privateKey);
+  }
+  await server.serve(port: port, security: tlsCredentials);
+  print('Server listening on port ${server.port}...');
+}
diff --git a/grpc/interop/ca.pem b/grpc/interop/ca.pem
new file mode 100644
index 0000000..6c8511a
--- /dev/null
+++ b/grpc/interop/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/grpc/interop/lib/src/client.dart b/grpc/interop/lib/src/client.dart
new file mode 100644
index 0000000..d47895f
--- /dev/null
+++ b/grpc/interop/lib/src/client.dart
@@ -0,0 +1,1189 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:collection/collection.dart';
+import 'package:grpc/grpc.dart';
+import 'package:interop/src/generated/empty.pb.dart';
+import 'package:interop/src/generated/messages.pb.dart';
+import 'package:interop/src/generated/test.pbgrpc.dart';
+
+const _headerEchoKey = 'x-grpc-test-echo-initial';
+const _headerEchoData = 'test_initial_metadata_value';
+
+const _trailerEchoKey = 'x-grpc-test-echo-trailing-bin';
+const _trailerEchoData = 'q6ur'; // 0xababab in base64
+
+class Tester {
+  String serverHost;
+  String serverHostOverride;
+  int _serverPort;
+  String testCase;
+  bool _useTls;
+  bool _useTestCA;
+  String defaultServiceAccount;
+  String oauthScope;
+  String serviceAccountKeyFile;
+  String _serviceAccountJson;
+
+  String get serviceAccountJson =>
+      _serviceAccountJson ??= _readServiceAccountJson();
+
+  String _readServiceAccountJson() {
+    if (serviceAccountKeyFile?.isEmpty ?? true) {
+      throw 'Service account key file not specified.';
+    }
+    return new File(serviceAccountKeyFile).readAsStringSync();
+  }
+
+  void set serverPort(String value) {
+    if (value == null) {
+      _serverPort = null;
+      return;
+    }
+    try {
+      _serverPort = int.parse(value);
+    } catch (e) {
+      print('Invalid port "$value": $e');
+    }
+  }
+
+  void set useTls(String value) {
+    _useTls = value != 'false';
+  }
+
+  void set useTestCA(String value) {
+    _useTestCA = value == 'true';
+  }
+
+  ClientChannel channel;
+  TestServiceClient client;
+  UnimplementedServiceClient unimplementedServiceClient;
+
+  bool validate() {
+    if (serverHost == null) {
+      print('Must specify --server_host');
+      return false;
+    }
+    if (_serverPort == null) {
+      print('Must specify --server_port');
+      return false;
+    }
+
+    return true;
+  }
+
+  Future<void> runTest() async {
+    ChannelCredentials credentials;
+    if (_useTls) {
+      List<int> trustedRoot;
+      if (_useTestCA) {
+        trustedRoot = new File('ca.pem').readAsBytesSync();
+      }
+      credentials = new ChannelCredentials.secure(
+          certificates: trustedRoot, authority: serverHostOverride);
+    } else {
+      credentials = const ChannelCredentials.insecure();
+    }
+
+    final options = new ChannelOptions(credentials: credentials);
+    channel =
+        new ClientChannel(serverHost, port: _serverPort, options: options);
+    client = new TestServiceClient(channel);
+    unimplementedServiceClient = new UnimplementedServiceClient(channel);
+    await runTestCase();
+    await channel.shutdown();
+  }
+
+  Future<void> runTestCase() async {
+    switch (testCase) {
+      case 'empty_unary':
+        return emptyUnary();
+      case 'cacheable_unary':
+        return cacheableUnary();
+      case 'large_unary':
+        return largeUnary();
+      case 'client_compressed_unary':
+        return clientCompressedUnary();
+      case 'server_compressed_unary':
+        return serverCompressedUnary();
+      case 'client_streaming':
+        return clientStreaming();
+      case 'client_compressed_streaming':
+        return clientCompressedStreaming();
+      case 'server_streaming':
+        return serverStreaming();
+      case 'server_compressed_streaming':
+        return serverCompressedStreaming();
+      case 'ping_pong':
+        return pingPong();
+      case 'empty_stream':
+        return emptyStream();
+      case 'compute_engine_creds':
+        return computeEngineCreds();
+      case 'service_account_creds':
+        return serviceAccountCreds();
+      case 'jwt_token_creds':
+        return jwtTokenCreds();
+      case 'oauth2_auth_token':
+        return oauth2AuthToken();
+      case 'per_rpc_creds':
+        return perRpcCreds();
+      case 'custom_metadata':
+        return customMetadata();
+      case 'status_code_and_message':
+        return statusCodeAndMessage();
+      case 'unimplemented_method':
+        return unimplementedMethod();
+      case 'unimplemented_service':
+        return unimplementedService();
+      case 'cancel_after_begin':
+        return cancelAfterBegin();
+      case 'cancel_after_first_response':
+        return cancelAfterFirstResponse();
+      case 'timeout_on_sleeping_server':
+        return timeoutOnSleepingServer();
+      default:
+        print('Unknown test case: $testCase');
+    }
+  }
+
+  /// This test verifies that implementations support zero-size messages.
+  /// Ideally, client implementations would verify that the request and response
+  /// were zero bytes serialized, but this is generally prohibitive to perform,
+  /// so is not required.
+  ///
+  /// Procedure:
+  /// 1. Client calls EmptyCall with the default Empty message
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * response is non-null
+  Future<void> emptyUnary() async {
+    final response = await client.emptyCall(new Empty());
+    if (response == null) throw 'Expected non-null response.';
+    if (response is! Empty) throw 'Expected Empty response.';
+  }
+
+  /// This test verifies that gRPC requests marked as cacheable use GET verb
+  /// instead of POST, and that server sets appropriate cache control headers
+  /// for the response to be cached by a proxy. This test requires that the
+  /// server is behind a caching proxy. Use of current timestamp in the request
+  /// prevents accidental cache matches left over from previous tests.
+  ///
+  /// Procedure:
+  /// 1. Client calls CacheableUnaryCall with `SimpleRequest` request with
+  ///    payload set to current timestamp. Timestamp format is irrelevant, and
+  ///    resolution is in nanoseconds.
+  ///    Client adds a `x-user-ip` header with value `1.2.3.4` to the request.
+  ///    This is done since some proxys such as GFE will not cache requests from
+  ///    localhost.
+  ///    Client marks the request as cacheable by setting the cacheable flag in
+  ///    the request context. Longer term this should be driven by the method
+  ///    option specified in the proto file itself.
+  /// 2. Client calls CacheableUnaryCall again immediately with the same request
+  ///    and configuration as the previous call.
+  ///
+  /// Client asserts:
+  /// * Both calls were successful
+  /// * The payload body of both responses is the same.
+  Future<void> cacheableUnary() async {
+    throw 'Not implemented';
+  }
+
+  /// This test verifies unary calls succeed in sending messages, and touches on
+  /// flow control (even if compression is enabled on the channel).
+  ///
+  /// Procedure:
+  /// 1. Client calls UnaryCall with:
+  ///     {
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * response payload body is 314159 bytes in size
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response message against a golden response
+  Future<void> largeUnary() async {
+    final payload = new Payload()..body = new Uint8List(271828);
+    final request = new SimpleRequest()
+      ..responseSize = 314159
+      ..payload = payload;
+    final response = await client.unaryCall(request);
+    final receivedBytes = response.payload.body.length;
+    if (receivedBytes != 314159) {
+      throw 'Response payload mismatch. Expected 314159 bytes, '
+          'got ${receivedBytes}.';
+    }
+  }
+
+  /// This test verifies the client can compress unary messages by sending two
+  /// unary calls, for compressed and uncompressed payloads. It also sends an
+  /// initial probing request to verify whether the server supports the
+  /// CompressedRequest feature by checking if the probing call fails with an
+  /// `INVALID_ARGUMENT` status.
+  ///
+  /// Procedure:
+  /// 1. Client calls UnaryCall with the feature probe, an *uncompressed*
+  ///    message:
+  ///     {
+  ///       expect_compressed: {
+  ///         value: true
+  ///       }
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  /// 1. Client calls UnaryCall with the *compressed* message:
+  ///     {
+  ///       expect_compressed: {
+  ///         value: true
+  ///       }
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  /// 1. Client calls UnaryCall with the *uncompressed* message:
+  ///     {
+  ///     expect_compressed: {
+  ///         value: false
+  ///       }
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// Client asserts:
+  /// * First call failed with `INVALID_ARGUMENT` status.
+  /// * Subsequent calls were successful.
+  /// * Response payload body is 314159 bytes in size.
+  /// * Clients are free to assert that the response payload body contents are
+  ///   zeros and comparing the entire response message against a golden
+  ///   response.
+  Future<void> clientCompressedUnary() async {
+    throw 'Not implemented';
+  }
+
+  /// This test verifies the server can compress unary messages. It sends two
+  /// unary requests, expecting the server's response to be compressed or not
+  /// according to the `response_compressed` boolean.
+  ///
+  /// Whether compression was actually performed is determined by the
+  /// compression bit in the response's message flags. *Note that some languages
+  /// may not have access to the message flags, in which case the client will be
+  /// unable to verify that the `response_compressed` boolean is obeyed by the
+  /// server*.
+  ///
+  /// Procedure:
+  /// 1. Client calls UnaryCall with `SimpleRequest`:
+  ///     {
+  ///       response_compressed: {
+  ///         value: true
+  ///       }
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///     {
+  ///       response_compressed: {
+  ///         value: false
+  ///       }
+  ///       response_size: 314159
+  ///       payload:  {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * if supported by the implementation, when `response_compressed` is true,
+  ///   the response MUST have the compressed message flag set.
+  /// * if supported by the implementation, when `response_compressed` is false,
+  ///   the response MUST NOT have the compressed message flag set.
+  /// * response payload body is 314159 bytes in size in both cases.
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response message against a golden response
+  Future<void> serverCompressedUnary() async {
+    throw 'Not implemented';
+  }
+
+  /// This test verifies that client-only streaming succeeds.
+  ///
+  /// Procedure:
+  /// 1. Client calls StreamingInputCall
+  /// 2. Client sends:
+  ///     {
+  ///       payload: {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  /// 3. Client then sends:
+  ///     {
+  ///       payload: {
+  ///         body: 8 bytes of zeros
+  ///       }
+  ///     }
+  /// 4. Client then sends:
+  ///     {
+  ///       payload: {
+  ///         body: 1828 bytes of zeros
+  ///       }
+  ///     }
+  /// 5. Client then sends:
+  ///     {
+  ///       payload: {
+  ///         body: 45904 bytes of zeros
+  ///       }
+  ///     }
+  /// 6. Client half-closes
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * response aggregated_payload_size is 74922
+  Future<void> clientStreaming() async {
+    StreamingInputCallRequest createRequest(int bytes) {
+      final request = new StreamingInputCallRequest()..payload = new Payload();
+      request.payload.body = new Uint8List(bytes);
+      return request;
+    }
+
+    Stream<StreamingInputCallRequest> requests() async* {
+      yield createRequest(27182);
+      yield createRequest(8);
+      yield createRequest(1828);
+      yield createRequest(45904);
+    }
+
+    final response = await client.streamingInputCall(requests());
+    if (response.aggregatedPayloadSize != 74922) {
+      throw 'Response mismatch. Expected 74922, '
+          'got ${response.aggregatedPayloadSize}';
+    }
+  }
+
+  /// This test verifies the client can compress requests on per-message basis
+  /// by performing a two-request streaming call. It also sends an initial
+  /// probing request to verify whether the server supports the
+  /// CompressedRequest feature by checking if the probing call fails with an
+  /// `INVALID_ARGUMENT` status.
+  ///
+  /// Procedure:
+  /// 1. Client calls `StreamingInputCall` and sends the following
+  ///    feature-probing *uncompressed* `StreamingInputCallRequest` message
+  ///     {
+  ///       expect_compressed: {
+  ///         value: true
+  ///       }
+  ///       payload:  {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  ///    If the call does not fail with `INVALID_ARGUMENT`, the test fails.
+  ///    Otherwise, we continue.
+  /// 1. Client calls `StreamingInputCall` again, sending the *compressed*
+  ///    message
+  ///     {
+  ///       expect_compressed: {
+  ///         value: true
+  ///       }
+  ///       payload: {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  /// 1. And finally, the *uncompressed* message
+  ///     {
+  ///       expect_compressed: {
+  ///         value: false
+  ///       }
+  ///       payload:  {
+  ///         body: 45904 bytes of zeros
+  ///       }
+  ///     }
+  /// 1. Client half-closes
+  ///
+  /// Client asserts:
+  /// * First call fails with `INVALID_ARGUMENT`.
+  /// * Next calls succeeds.
+  /// * Response aggregated payload size is 73086.
+  Future<void> clientCompressedStreaming() async {
+    throw 'Not implemented';
+  }
+
+  /// This test verifies that server-only streaming succeeds.
+  ///
+  /// Procedure:
+  /// 1. Client calls StreamingOutputCall with `StreamingOutputCallRequest`:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 31415
+  ///       }
+  ///       response_parameters: {
+  ///         size: 9
+  ///       }
+  ///       response_parameters: {
+  ///         size: 2653
+  ///       }
+  ///       response_parameters: {
+  ///         size: 58979
+  ///       }
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * exactly four responses
+  /// * response payload bodies are sized (in order): 31415, 9, 2653, 58979
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response messages against golden responses
+  Future<void> serverStreaming() async {
+    final expectedResponses = [31415, 9, 2653, 58979];
+
+    final request = new StreamingOutputCallRequest()
+      ..responseParameters.addAll(expectedResponses
+          .map((size) => new ResponseParameters()..size = size));
+
+    final responses = await client.streamingOutputCall(request).toList();
+    if (responses.length != 4) {
+      throw 'Incorrect number of responses (${responses.length}).';
+    }
+    final responseLengths =
+        responses.map((response) => response.payload.body.length).toList();
+
+    if (!new ListEquality().equals(responseLengths, expectedResponses)) {
+      throw 'Incorrect response lengths received (${responseLengths.join(
+          ', ')} != ${expectedResponses.join(', ')})';
+    }
+  }
+
+  /// This test verifies that the server can compress streaming messages and
+  /// disable compression on individual messages, expecting the server's
+  /// response to be compressed or not according to the `response_compressed`
+  /// boolean.
+  ///
+  /// Whether compression was actually performed is determined by the
+  /// compression bit in the response's message flags. *Note that some languages
+  /// may not have access to the message flags, in which case the client will be
+  /// unable to verify that the `response_compressed` boolean is obeyed by the
+  /// server*.
+  ///
+  /// Procedure:
+  /// 1. Client calls StreamingOutputCall with `StreamingOutputCallRequest`:
+  ///     {
+  ///       response_parameters: {
+  ///         compressed: {
+  ///           value: true
+  ///         }
+  ///         size: 31415
+  ///       }
+  ///       response_parameters: {
+  ///         compressed: {
+  ///           value: false
+  ///         }
+  ///         size: 92653
+  ///       }
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * exactly two responses
+  /// * if supported by the implementation, when `response_compressed` is false,
+  ///   the response's messages MUST NOT have the compressed message flag set.
+  /// * if supported by the implementation, when `response_compressed` is true,
+  ///   the response's messages MUST have the compressed message flag set.
+  /// * response payload bodies are sized (in order): 31415, 92653
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response messages against golden responses
+  Future<void> serverCompressedStreaming() async {
+    throw 'Not implemented';
+  }
+
+  /// This test verifies that full duplex bidi is supported.
+  ///
+  /// Procedure:
+  /// 1. Client calls FullDuplexCall with:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 31415
+  ///       }
+  ///       payload: {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  /// 2. After getting a reply, it sends:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 9
+  ///       }
+  ///       payload: {
+  ///         body: 8 bytes of zeros
+  ///       }
+  ///     }
+  /// 3. After getting a reply, it sends:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 2653
+  ///       }
+  ///       payload: {
+  ///         body: 1828 bytes of zeros
+  ///       }
+  ///     }
+  /// 4. After getting a reply, it sends:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 58979
+  ///       }
+  ///       payload: {
+  ///         body: 45904 bytes of zeros
+  ///       }
+  ///     }
+  /// 5. After getting a reply, client half-closes
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * exactly four responses
+  /// * response payload bodies are sized (in order): 31415, 9, 2653, 58979
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response messages against golden responses
+  Future<void> pingPong() async {
+    final requestSizes = [27182, 8, 1828, 45904];
+    final expectedResponses = [31415, 9, 2653, 58979];
+
+    StreamingOutputCallRequest createRequest(int index) {
+      final payload = new Payload()..body = new Uint8List(requestSizes[index]);
+      final request = new StreamingOutputCallRequest()
+        ..payload = payload
+        ..responseParameters
+            .add(new ResponseParameters()..size = expectedResponses[index]);
+      return request;
+    }
+
+    var index = 0;
+    final requests = new StreamController<int>();
+
+    final responses = client.fullDuplexCall(requests.stream.map(createRequest));
+    requests.add(index);
+    await for (final response in responses) {
+      if (index >= expectedResponses.length) {
+        throw 'Received too many responses. $index > ${expectedResponses
+            .length}.';
+      }
+      if (response.payload.body.length != expectedResponses[index]) {
+        throw 'Response mismatch for response $index: '
+            '${response.payload.body.length} != ${expectedResponses[index]}.';
+      }
+      index++;
+      if (index == requestSizes.length) {
+        requests.close();
+      } else {
+        requests.add(index);
+      }
+    }
+  }
+
+  /// This test verifies that streams support having zero-messages in both
+  /// directions.
+  ///
+  /// Procedure:
+  /// 1. Client calls FullDuplexCall and then half-closes
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * exactly zero responses
+  Future<void> emptyStream() async {
+    final requests = new StreamController<StreamingOutputCallRequest>();
+    final call = client.fullDuplexCall(requests.stream);
+    requests.close();
+    final responses = await call.toList();
+    if (responses.length != 0) {
+      throw 'Received too many responses. ${responses.length} != 0';
+    }
+  }
+
+  /// This test is only for cloud-to-prod path.
+  ///
+  /// This test verifies unary calls succeed in sending messages while using
+  /// Service Credentials from GCE metadata server. The client instance needs to
+  /// be created with desired oauth scope.
+  ///
+  /// The test uses `--default_service_account` with GCE service account email
+  /// and `--oauth_scope` with the OAuth scope to use. For testing against
+  /// grpc-test.sandbox.googleapis.com,
+  /// "https://www.googleapis.com/auth/xapi.zoo" should be passed in as
+  /// `--oauth_scope`.
+  ///
+  /// Procedure:
+  /// 1. Client configures channel to use GCECredentials
+  /// 2. Client calls UnaryCall on the channel with:
+  ///     {
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///       fill_username: true
+  ///       fill_oauth_scope: true
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * received SimpleResponse.username equals the value of
+  ///   `--default_service_account` flag
+  /// * received SimpleResponse.oauth_scope is in `--oauth_scope`
+  /// * response payload body is 314159 bytes in size
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response message against a golden response
+  Future<void> computeEngineCreds() async {
+    final credentials = new ComputeEngineAuthenticator();
+    final clientWithCredentials =
+        new TestServiceClient(channel, options: credentials.toCallOptions);
+
+    final response = await _sendSimpleRequestForAuth(clientWithCredentials,
+        fillUsername: true, fillOauthScope: true);
+
+    final user = response.username;
+    final oauth = response.oauthScope;
+
+    if (user?.isEmpty ?? true) {
+      throw 'Username not received.';
+    }
+    if (oauth?.isEmpty ?? true) {
+      throw 'OAuth scope not received.';
+    }
+
+    if (user != defaultServiceAccount) {
+      throw 'Got user name $user, wanted $defaultServiceAccount';
+    }
+    if (!oauthScope.contains(oauth)) {
+      throw 'Got OAuth scope $oauth, which is not a substring of $oauthScope';
+    }
+  }
+
+  /// This test is only for cloud-to-prod path.
+  ///
+  /// This test verifies unary calls succeed in sending messages while using
+  /// service account credentials.
+  ///
+  /// Test caller should set flag `--service_account_key_file` with the path to
+  /// json key file downloaded from https://console.developers.google.com.
+  /// Alternately, if using a usable auth implementation, she may specify the
+  /// file location in the environment variable GOOGLE_APPLICATION_CREDENTIALS.
+  ///
+  /// Procedure:
+  /// 1. Client configures the channel to use ServiceAccountCredentials
+  /// 2. Client calls UnaryCall with:
+  ///     {
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///       fill_username: true
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * received SimpleResponse.username is not empty and is in the json key
+  ///   file used by the auth library. The client can optionally check the
+  ///   username matches the email address in the key file or equals the value
+  ///   of `--default_service_account` flag.
+  /// * response payload body is 314159 bytes in size
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response message against a golden response
+  Future<void> serviceAccountCreds() async {
+    throw 'Not implemented';
+  }
+
+  /// This test is only for cloud-to-prod path.
+  ///
+  /// This test verifies unary calls succeed in sending messages while using JWT
+  /// token (created by the project's key file)
+  ///
+  /// Test caller should set flag `--service_account_key_file` with the path to
+  /// json key file downloaded from https://console.developers.google.com.
+  /// Alternately, if using a usable auth implementation, she may specify the
+  /// file location in the environment variable GOOGLE_APPLICATION_CREDENTIALS.
+  ///
+  /// Procedure:
+  /// 1. Client configures the channel to use JWTTokenCredentials
+  /// 2. Client calls UnaryCall with:
+  ///     {
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///       fill_username: true
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * received SimpleResponse.username is not empty and is in the json key
+  ///   file used by the auth library. The client can optionally check the
+  ///   username matches the email address in the key file or equals the value
+  ///   of `--default_service_account` flag.
+  /// * response payload body is 314159 bytes in size
+  /// * clients are free to assert that the response payload body contents are
+  ///   zero and comparing the entire response message against a golden response
+  Future<void> jwtTokenCreds() async {
+    final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
+    final clientWithCredentials =
+        new TestServiceClient(channel, options: credentials.toCallOptions);
+
+    final response = await _sendSimpleRequestForAuth(clientWithCredentials,
+        fillUsername: true);
+    final username = response.username;
+    if (username?.isEmpty ?? true) {
+      throw 'Username not received.';
+    }
+    if (!serviceAccountJson.contains(username)) {
+      throw 'Got user name $username, which is not a substring of $serviceAccountJson';
+    }
+  }
+
+  /// This test is only for cloud-to-prod path and some implementations may run
+  /// in GCE only.
+  ///
+  /// This test verifies unary calls succeed in sending messages using an OAuth2
+  /// token that is obtained out of band. For the purpose of the test, the
+  /// OAuth2 token is actually obtained from a service account credentials or
+  /// GCE credentials via the language-specific authorization library.
+  ///
+  /// The difference between this test and the other auth tests is that it first
+  /// uses the authorization library to obtain an authorization token.
+  ///
+  /// The test
+  /// * uses the flag `--service_account_key_file` with the path to a json key
+  ///   file downloaded from https://console.developers.google.com. Alternately,
+  ///   if using a usable auth implementation, it may specify the file location
+  ///   in the environment variable GOOGLE_APPLICATION_CREDENTIALS, *OR* if GCE
+  ///   credentials is used to fetch the token, `--default_service_account` can
+  ///   be used to pass in GCE service account email.
+  /// * uses the flag `--oauth_scope` for the oauth scope. For testing against
+  ///   grpc-test.sandbox.googleapis.com,
+  ///   "https://www.googleapis.com/auth/xapi.zoo" should be passed as the
+  ///   `--oauth_scope`.
+  ///
+  /// Procedure:
+  /// 1. Client uses the auth library to obtain an authorization token
+  /// 2. Client configures the channel to use AccessTokenCredentials with the
+  ///    access token obtained in step 1
+  /// 3. Client calls UnaryCall with the following message
+  ///     {
+  ///       fill_username: true
+  ///       fill_oauth_scope: true
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * received SimpleResponse.username is valid. Depending on whether a
+  ///   service account key file or GCE credentials was used, client should
+  ///   check against the json key file or GCE default service account email.
+  /// * received SimpleResponse.oauth_scope is in `--oauth_scope`
+  Future<void> oauth2AuthToken() async {
+    final credentials =
+        new ServiceAccountAuthenticator(serviceAccountJson, [oauthScope]);
+    final clientWithCredentials =
+        new TestServiceClient(channel, options: credentials.toCallOptions);
+
+    final response = await _sendSimpleRequestForAuth(clientWithCredentials,
+        fillUsername: true, fillOauthScope: true);
+
+    final user = response.username;
+    final oauth = response.oauthScope;
+
+    if (user?.isEmpty ?? true) {
+      throw 'Username not received.';
+    }
+    if (oauth?.isEmpty ?? true) {
+      throw 'OAuth scope not received.';
+    }
+
+    if (!serviceAccountJson.contains(user)) {
+      throw 'Got user name $user, which is not a substring of $serviceAccountJson';
+    }
+    if (!oauthScope.contains(oauth)) {
+      throw 'Got OAuth scope $oauth, which is not a substring of $oauthScope';
+    }
+  }
+
+  /// Similar to the other auth tests, this test is only for cloud-to-prod path.
+  ///
+  /// This test verifies unary calls succeed in sending messages using a JWT or
+  /// a service account credentials set on the RPC.
+  ///
+  /// The test
+  /// * uses the flag `--service_account_key_file` with the path to a json key
+  ///   file downloaded from https://console.developers.google.com. Alternately,
+  ///   if using a usable auth implementation, it may specify the file location
+  ///   in the environment variable GOOGLE_APPLICATION_CREDENTIALS
+  /// * optionally uses the flag `--oauth_scope` for the oauth scope if
+  ///   implementator wishes to use service account credential instead of JWT
+  ///   credential. For testing against grpc-test.sandbox.googleapis.com, oauth
+  ///   scope "https://www.googleapis.com/auth/xapi.zoo" should be used.
+  ///
+  /// Procedure:
+  /// 1. Client configures the channel with just SSL credentials
+  /// 2. Client calls UnaryCall, setting per-call credentials to
+  ///    JWTTokenCredentials. The request is the following message
+  ///     {
+  ///       fill_username: true
+  ///     }
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * received SimpleResponse.username is not empty and is in the json key
+  ///   file used by the auth library. The client can optionally check the
+  ///   username matches the email address in the key file.
+  Future<void> perRpcCreds() async {
+    final credentials =
+        new ServiceAccountAuthenticator(serviceAccountJson, [oauthScope]);
+
+    final response = await _sendSimpleRequestForAuth(client,
+        fillUsername: true,
+        fillOauthScope: true,
+        options: credentials.toCallOptions);
+
+    final user = response.username;
+    final oauth = response.oauthScope;
+
+    if (user?.isEmpty ?? true) {
+      throw 'Username not received.';
+    }
+    if (oauth?.isEmpty ?? true) {
+      throw 'OAuth scope not received.';
+    }
+
+    if (!serviceAccountJson.contains(user)) {
+      throw 'Got user name $user, which is not a substring of $serviceAccountJson';
+    }
+    if (!oauthScope.contains(oauth)) {
+      throw 'Got OAuth scope $oauth, which is not a substring of $oauthScope';
+    }
+  }
+
+  Future<SimpleResponse> _sendSimpleRequestForAuth(TestServiceClient client,
+      {bool fillUsername: false,
+      bool fillOauthScope: false,
+      CallOptions options}) async {
+    final payload = new Payload()..body = new Uint8List(271828);
+    final request = new SimpleRequest()
+      ..responseSize = 314159
+      ..payload = payload
+      ..fillUsername = fillUsername
+      ..fillOauthScope = fillOauthScope;
+    final response = await client.unaryCall(request, options: options);
+    final receivedBytes = response.payload.body.length;
+    if (receivedBytes != 314159) {
+      throw 'Response payload mismatch. Expected 314159 bytes, '
+          'got ${receivedBytes}.';
+    }
+    return response;
+  }
+
+  /// This test verifies that custom metadata in either binary or ascii format
+  /// can be sent as initial-metadata by the client and as both initial- and
+  /// trailing-metadata by the server.
+  ///
+  /// Procedure:
+  /// 1. The client attaches custom metadata with the following keys and values:
+  ///     key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
+  ///     key: "x-grpc-test-echo-trailing-bin", value: 0xababab
+  ///    to a UnaryCall with request:
+  ///     {
+  ///       response_size: 314159
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// 2. The client attaches custom metadata with the following keys and values:
+  ///     key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
+  ///     key: "x-grpc-test-echo-trailing-bin", value: 0xababab
+  ///    to a FullDuplexCall with request:
+  ///     {
+  ///       response_parameters: {
+  ///         size: 314159
+  ///       }
+  ///       payload: {
+  ///         body: 271828 bytes of zeros
+  ///       }
+  ///     }
+  ///    and then half-closes
+  ///
+  /// Client asserts:
+  /// * call was successful
+  /// * metadata with key `"x-grpc-test-echo-initial"` and value
+  ///   `"test_initial_metadata_value"`is received in the initial metadata for
+  ///   calls in Procedure steps 1 and 2.
+  /// * metadata with key `"x-grpc-test-echo-trailing-bin"` and value `0xababab`
+  ///   is received in the trailing metadata for calls in Procedure steps 1 and
+  ///   2.
+  Future<void> customMetadata() async {
+    void validate(Map<String, String> headers, Map<String, String> trailers) {
+      if (headers[_headerEchoKey] != _headerEchoData) {
+        throw 'Invalid header data received.';
+      }
+      if (trailers[_trailerEchoKey] != _trailerEchoData) {
+        throw 'Invalid trailer data received.';
+      }
+    }
+
+    final options = new CallOptions(metadata: {
+      _headerEchoKey: _headerEchoData,
+      _trailerEchoKey: _trailerEchoData,
+    });
+    final unaryCall = client.unaryCall(
+        new SimpleRequest()
+          ..responseSize = 314159
+          ..payload = (new Payload()..body = new Uint8List(271828)),
+        options: options);
+    var headers = await unaryCall.headers;
+    var trailers = await unaryCall.trailers;
+    await unaryCall;
+    validate(headers, trailers);
+
+    Stream<StreamingOutputCallRequest> requests() async* {
+      yield new StreamingOutputCallRequest()
+        ..responseParameters.add(new ResponseParameters()..size = 314159)
+        ..payload = (new Payload()..body = new Uint8List(271828));
+    }
+
+    final fullDuplexCall = client.fullDuplexCall(requests(), options: options);
+    final drain = fullDuplexCall.drain();
+    headers = await fullDuplexCall.headers;
+    trailers = await fullDuplexCall.trailers;
+    await drain;
+    validate(headers, trailers);
+  }
+
+  /// This test verifies unary calls succeed in sending messages, and propagate
+  /// back status code and message sent along with the messages.
+  ///
+  /// Procedure:
+  /// 1. Client calls UnaryCall with:
+  ///     {
+  ///       response_status: {
+  ///         code: 2
+  ///         message: "test status message"
+  ///       }
+  ///     }
+  ///
+  /// 2. Client calls FullDuplexCall with:
+  ///     {
+  ///       response_status: {
+  ///         code: 2
+  ///         message: "test status message"
+  ///       }
+  ///     }
+  ///
+  /// and then half-closes
+  ///
+  /// Client asserts:
+  /// * received status code is the same as the sent code for both Procedure
+  ///   steps 1 and 2
+  /// * received status message is the same as the sent message for both
+  ///   Procedure steps 1 and 2
+  Future<void> statusCodeAndMessage() async {
+    final expectedStatus = new GrpcError.custom(2, 'test status message');
+    final responseStatus = new EchoStatus()
+      ..code = expectedStatus.code
+      ..message = expectedStatus.message;
+    try {
+      await client
+          .unaryCall(new SimpleRequest()..responseStatus = responseStatus);
+      throw 'Did not receive correct status code.';
+    } on GrpcError catch (e) {
+      if (e != expectedStatus) {
+        throw 'Received incorrect status: $e.';
+      }
+    }
+    Stream<StreamingOutputCallRequest> requests() async* {
+      yield new StreamingOutputCallRequest()..responseStatus = responseStatus;
+    }
+
+    try {
+      await for (final _ in client.fullDuplexCall(requests())) {
+        throw 'Received unexpected response.';
+      }
+      throw 'Did not receive correct status code.';
+    } on GrpcError catch (e) {
+      if (e != expectedStatus) {
+        throw 'Received incorrect status: $e.';
+      }
+    }
+  }
+
+  /// This test verifies that calling an unimplemented RPC method returns the
+  /// UNIMPLEMENTED status code.
+  ///
+  /// Procedure:
+  /// * Client calls `grpc.testing.TestService/UnimplementedCall` with an empty
+  ///   request (defined as `grpc.testing.Empty`):
+  ///     {
+  ///     }
+  ///
+  /// Client asserts:
+  /// * received status code is 12 (UNIMPLEMENTED)
+  Future<void> unimplementedMethod() async {
+    try {
+      await client.unimplementedCall(new Empty());
+      throw 'Did not throw.';
+    } on GrpcError catch (e) {
+      if (e.code != StatusCode.unimplemented) {
+        throw 'Unexpected status code ${e.code} - ${e.message}.';
+      }
+    }
+  }
+
+  /// This test verifies calling an unimplemented server returns the
+  /// UNIMPLEMENTED status code.
+  ///
+  /// Procedure:
+  /// * Client calls `grpc.testing.UnimplementedService/UnimplementedCall` with
+  ///   an empty request (defined as `grpc.testing.Empty`)
+  ///
+  /// Client asserts:
+  /// * received status code is 12 (UNIMPLEMENTED)
+  Future<void> unimplementedService() async {
+    try {
+      await unimplementedServiceClient.unimplementedCall(new Empty());
+      throw 'Did not throw.';
+    } on GrpcError catch (e) {
+      if (e.code != StatusCode.unimplemented) {
+        throw 'Unexpected status code ${e.code} - ${e.message}.';
+      }
+    }
+  }
+
+  /// This test verifies that a request can be cancelled after metadata has been
+  /// sent but before payloads are sent.
+  ///
+  /// Procedure:
+  /// 1. Client starts StreamingInputCall
+  /// 2. Client immediately cancels request
+  ///
+  /// Client asserts:
+  /// * Call completed with status CANCELLED
+  Future<void> cancelAfterBegin() async {
+    final requests = new StreamController<StreamingInputCallRequest>();
+    final call = client.streamingInputCall(requests.stream);
+    scheduleMicrotask(call.cancel);
+    try {
+      await call;
+      throw 'Expected exception.';
+    } on GrpcError catch (e) {
+      if (e.code != StatusCode.cancelled) {
+        throw 'Unexpected status code ${e.code} - ${e.message}';
+      }
+    }
+    requests.close();
+  }
+
+  /// This test verifies that a request can be cancelled after receiving a
+  /// message from the server.
+  ///
+  /// Procedure:
+  /// 1. Client starts FullDuplexCall with
+  ///     {
+  ///       response_parameters: {
+  ///         size: 31415
+  ///       }
+  ///       payload: {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// 2. After receiving a response, client cancels request
+  ///
+  /// Client asserts:
+  /// * Call completed with status CANCELLED
+  Future<void> cancelAfterFirstResponse() async {
+    final requests = new StreamController<StreamingOutputCallRequest>();
+    final call = client.fullDuplexCall(requests.stream);
+    final completer = new Completer();
+
+    var receivedResponse = false;
+    call.listen((response) {
+      if (receivedResponse) {
+        completer.completeError('Received too many responses.');
+        return;
+      }
+      receivedResponse = true;
+      if (response.payload.body.length != 31415) {
+        completer.completeError('Invalid response length: '
+            '${response.payload.body.length} != 31415.');
+      }
+      call.cancel();
+    }, onError: (e) {
+      if (e is! GrpcError) completer.completeError('Unexpected error: $e.');
+      if (e.code != StatusCode.cancelled) {
+        completer
+            .completeError('Unexpected status code ${e.code}: ${e.message}.');
+      }
+      completer.complete(true);
+    }, onDone: () {
+      if (!completer.isCompleted) completer.completeError('Expected error.');
+    });
+
+    requests.add(new StreamingOutputCallRequest()
+      ..responseParameters.add(new ResponseParameters()..size = 31415)
+      ..payload = (new Payload()..body = new Uint8List(27182)));
+    await completer.future;
+    requests.close();
+  }
+
+  /// This test verifies that an RPC request whose lifetime exceeds its
+  /// configured timeout value will end with the DeadlineExceeded status.
+  ///
+  /// Procedure:
+  /// 1. Client calls FullDuplexCall with the following request and sets its
+  ///    timeout to 1ms
+  ///     {
+  ///       payload: {
+  ///         body: 27182 bytes of zeros
+  ///       }
+  ///     }
+  ///
+  /// 2. Client waits
+  ///
+  /// Client asserts:
+  /// * Call completed with status DEADLINE_EXCEEDED.
+  Future<void> timeoutOnSleepingServer() async {
+    final requests = new StreamController<StreamingOutputCallRequest>();
+    final call = client.fullDuplexCall(requests.stream,
+        options: new CallOptions(timeout: new Duration(milliseconds: 1)));
+    requests.add(new StreamingOutputCallRequest()
+      ..payload = (new Payload()..body = new Uint8List(27182)));
+    try {
+      await for (final _ in call) {
+        throw 'Unexpected response received.';
+      }
+      throw 'Expected exception.';
+    } on GrpcError catch (e) {
+      if (e.code != StatusCode.deadlineExceeded) {
+        throw 'Unexpected status code ${e.code} - ${e.message}.';
+      }
+    } finally {
+      requests.close();
+    }
+  }
+}
diff --git a/grpc/interop/lib/src/generated/empty.pb.dart b/grpc/interop/lib/src/generated/empty.pb.dart
new file mode 100644
index 0000000..4b040c7
--- /dev/null
+++ b/grpc/interop/lib/src/generated/empty.pb.dart
@@ -0,0 +1,36 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc.testing_empty;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+class Empty extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Empty')
+    ..hasRequiredFields = false;
+
+  Empty() : super();
+  Empty.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Empty.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Empty clone() => new Empty()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Empty create() => new Empty();
+  static PbList<Empty> createRepeated() => new PbList<Empty>();
+  static Empty getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyEmpty();
+    return _defaultInstance;
+  }
+
+  static Empty _defaultInstance;
+  static void $checkItem(Empty v) {
+    if (v is! Empty) checkItemFailed(v, 'Empty');
+  }
+}
+
+class _ReadonlyEmpty extends Empty with ReadonlyMessageMixin {}
diff --git a/grpc/interop/lib/src/generated/messages.pb.dart b/grpc/interop/lib/src/generated/messages.pb.dart
new file mode 100644
index 0000000..fe00e19
--- /dev/null
+++ b/grpc/interop/lib/src/generated/messages.pb.dart
@@ -0,0 +1,655 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc.testing_messages;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
+
+import 'package:protobuf/protobuf.dart';
+
+import 'messages.pbenum.dart';
+
+export 'messages.pbenum.dart';
+
+class BoolValue extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('BoolValue')
+    ..aOB(1, 'value')
+    ..hasRequiredFields = false;
+
+  BoolValue() : super();
+  BoolValue.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  BoolValue.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  BoolValue clone() => new BoolValue()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static BoolValue create() => new BoolValue();
+  static PbList<BoolValue> createRepeated() => new PbList<BoolValue>();
+  static BoolValue getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyBoolValue();
+    return _defaultInstance;
+  }
+
+  static BoolValue _defaultInstance;
+  static void $checkItem(BoolValue v) {
+    if (v is! BoolValue) checkItemFailed(v, 'BoolValue');
+  }
+
+  bool get value => $_get(0, false);
+  set value(bool v) {
+    $_setBool(0, v);
+  }
+
+  bool hasValue() => $_has(0);
+  void clearValue() => clearField(1);
+}
+
+class _ReadonlyBoolValue extends BoolValue with ReadonlyMessageMixin {}
+
+class Payload extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('Payload')
+    ..e<PayloadType>(1, 'type', PbFieldType.OE, PayloadType.COMPRESSABLE,
+        PayloadType.valueOf, PayloadType.values)
+    ..a<List<int>>(2, 'body', PbFieldType.OY)
+    ..hasRequiredFields = false;
+
+  Payload() : super();
+  Payload.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  Payload.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  Payload clone() => new Payload()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static Payload create() => new Payload();
+  static PbList<Payload> createRepeated() => new PbList<Payload>();
+  static Payload getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyPayload();
+    return _defaultInstance;
+  }
+
+  static Payload _defaultInstance;
+  static void $checkItem(Payload v) {
+    if (v is! Payload) checkItemFailed(v, 'Payload');
+  }
+
+  PayloadType get type => $_getN(0);
+  set type(PayloadType v) {
+    setField(1, v);
+  }
+
+  bool hasType() => $_has(0);
+  void clearType() => clearField(1);
+
+  List<int> get body => $_getN(1);
+  set body(List<int> v) {
+    $_setBytes(1, v);
+  }
+
+  bool hasBody() => $_has(1);
+  void clearBody() => clearField(2);
+}
+
+class _ReadonlyPayload extends Payload with ReadonlyMessageMixin {}
+
+class EchoStatus extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('EchoStatus')
+    ..a<int>(1, 'code', PbFieldType.O3)
+    ..aOS(2, 'message')
+    ..hasRequiredFields = false;
+
+  EchoStatus() : super();
+  EchoStatus.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  EchoStatus.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  EchoStatus clone() => new EchoStatus()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static EchoStatus create() => new EchoStatus();
+  static PbList<EchoStatus> createRepeated() => new PbList<EchoStatus>();
+  static EchoStatus getDefault() {
+    if (_defaultInstance == null) _defaultInstance = new _ReadonlyEchoStatus();
+    return _defaultInstance;
+  }
+
+  static EchoStatus _defaultInstance;
+  static void $checkItem(EchoStatus v) {
+    if (v is! EchoStatus) checkItemFailed(v, 'EchoStatus');
+  }
+
+  int get code => $_get(0, 0);
+  set code(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasCode() => $_has(0);
+  void clearCode() => clearField(1);
+
+  String get message => $_getS(1, '');
+  set message(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasMessage() => $_has(1);
+  void clearMessage() => clearField(2);
+}
+
+class _ReadonlyEchoStatus extends EchoStatus with ReadonlyMessageMixin {}
+
+class SimpleRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('SimpleRequest')
+    ..e<PayloadType>(1, 'responseType', PbFieldType.OE,
+        PayloadType.COMPRESSABLE, PayloadType.valueOf, PayloadType.values)
+    ..a<int>(2, 'responseSize', PbFieldType.O3)
+    ..a<Payload>(
+        3, 'payload', PbFieldType.OM, Payload.getDefault, Payload.create)
+    ..aOB(4, 'fillUsername')
+    ..aOB(5, 'fillOauthScope')
+    ..a<BoolValue>(6, 'responseCompressed', PbFieldType.OM,
+        BoolValue.getDefault, BoolValue.create)
+    ..a<EchoStatus>(7, 'responseStatus', PbFieldType.OM, EchoStatus.getDefault,
+        EchoStatus.create)
+    ..a<BoolValue>(8, 'expectCompressed', PbFieldType.OM, BoolValue.getDefault,
+        BoolValue.create)
+    ..hasRequiredFields = false;
+
+  SimpleRequest() : super();
+  SimpleRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  SimpleRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  SimpleRequest clone() => new SimpleRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static SimpleRequest create() => new SimpleRequest();
+  static PbList<SimpleRequest> createRepeated() => new PbList<SimpleRequest>();
+  static SimpleRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlySimpleRequest();
+    return _defaultInstance;
+  }
+
+  static SimpleRequest _defaultInstance;
+  static void $checkItem(SimpleRequest v) {
+    if (v is! SimpleRequest) checkItemFailed(v, 'SimpleRequest');
+  }
+
+  PayloadType get responseType => $_getN(0);
+  set responseType(PayloadType v) {
+    setField(1, v);
+  }
+
+  bool hasResponseType() => $_has(0);
+  void clearResponseType() => clearField(1);
+
+  int get responseSize => $_get(1, 0);
+  set responseSize(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasResponseSize() => $_has(1);
+  void clearResponseSize() => clearField(2);
+
+  Payload get payload => $_getN(2);
+  set payload(Payload v) {
+    setField(3, v);
+  }
+
+  bool hasPayload() => $_has(2);
+  void clearPayload() => clearField(3);
+
+  bool get fillUsername => $_get(3, false);
+  set fillUsername(bool v) {
+    $_setBool(3, v);
+  }
+
+  bool hasFillUsername() => $_has(3);
+  void clearFillUsername() => clearField(4);
+
+  bool get fillOauthScope => $_get(4, false);
+  set fillOauthScope(bool v) {
+    $_setBool(4, v);
+  }
+
+  bool hasFillOauthScope() => $_has(4);
+  void clearFillOauthScope() => clearField(5);
+
+  BoolValue get responseCompressed => $_getN(5);
+  set responseCompressed(BoolValue v) {
+    setField(6, v);
+  }
+
+  bool hasResponseCompressed() => $_has(5);
+  void clearResponseCompressed() => clearField(6);
+
+  EchoStatus get responseStatus => $_getN(6);
+  set responseStatus(EchoStatus v) {
+    setField(7, v);
+  }
+
+  bool hasResponseStatus() => $_has(6);
+  void clearResponseStatus() => clearField(7);
+
+  BoolValue get expectCompressed => $_getN(7);
+  set expectCompressed(BoolValue v) {
+    setField(8, v);
+  }
+
+  bool hasExpectCompressed() => $_has(7);
+  void clearExpectCompressed() => clearField(8);
+}
+
+class _ReadonlySimpleRequest extends SimpleRequest with ReadonlyMessageMixin {}
+
+class SimpleResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('SimpleResponse')
+    ..a<Payload>(
+        1, 'payload', PbFieldType.OM, Payload.getDefault, Payload.create)
+    ..aOS(2, 'username')
+    ..aOS(3, 'oauthScope')
+    ..hasRequiredFields = false;
+
+  SimpleResponse() : super();
+  SimpleResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  SimpleResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  SimpleResponse clone() => new SimpleResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static SimpleResponse create() => new SimpleResponse();
+  static PbList<SimpleResponse> createRepeated() =>
+      new PbList<SimpleResponse>();
+  static SimpleResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlySimpleResponse();
+    return _defaultInstance;
+  }
+
+  static SimpleResponse _defaultInstance;
+  static void $checkItem(SimpleResponse v) {
+    if (v is! SimpleResponse) checkItemFailed(v, 'SimpleResponse');
+  }
+
+  Payload get payload => $_getN(0);
+  set payload(Payload v) {
+    setField(1, v);
+  }
+
+  bool hasPayload() => $_has(0);
+  void clearPayload() => clearField(1);
+
+  String get username => $_getS(1, '');
+  set username(String v) {
+    $_setString(1, v);
+  }
+
+  bool hasUsername() => $_has(1);
+  void clearUsername() => clearField(2);
+
+  String get oauthScope => $_getS(2, '');
+  set oauthScope(String v) {
+    $_setString(2, v);
+  }
+
+  bool hasOauthScope() => $_has(2);
+  void clearOauthScope() => clearField(3);
+}
+
+class _ReadonlySimpleResponse extends SimpleResponse with ReadonlyMessageMixin {
+}
+
+class StreamingInputCallRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('StreamingInputCallRequest')
+    ..a<Payload>(
+        1, 'payload', PbFieldType.OM, Payload.getDefault, Payload.create)
+    ..a<BoolValue>(2, 'expectCompressed', PbFieldType.OM, BoolValue.getDefault,
+        BoolValue.create)
+    ..hasRequiredFields = false;
+
+  StreamingInputCallRequest() : super();
+  StreamingInputCallRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  StreamingInputCallRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  StreamingInputCallRequest clone() =>
+      new StreamingInputCallRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static StreamingInputCallRequest create() => new StreamingInputCallRequest();
+  static PbList<StreamingInputCallRequest> createRepeated() =>
+      new PbList<StreamingInputCallRequest>();
+  static StreamingInputCallRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyStreamingInputCallRequest();
+    return _defaultInstance;
+  }
+
+  static StreamingInputCallRequest _defaultInstance;
+  static void $checkItem(StreamingInputCallRequest v) {
+    if (v is! StreamingInputCallRequest)
+      checkItemFailed(v, 'StreamingInputCallRequest');
+  }
+
+  Payload get payload => $_getN(0);
+  set payload(Payload v) {
+    setField(1, v);
+  }
+
+  bool hasPayload() => $_has(0);
+  void clearPayload() => clearField(1);
+
+  BoolValue get expectCompressed => $_getN(1);
+  set expectCompressed(BoolValue v) {
+    setField(2, v);
+  }
+
+  bool hasExpectCompressed() => $_has(1);
+  void clearExpectCompressed() => clearField(2);
+}
+
+class _ReadonlyStreamingInputCallRequest extends StreamingInputCallRequest
+    with ReadonlyMessageMixin {}
+
+class StreamingInputCallResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('StreamingInputCallResponse')
+    ..a<int>(1, 'aggregatedPayloadSize', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  StreamingInputCallResponse() : super();
+  StreamingInputCallResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  StreamingInputCallResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  StreamingInputCallResponse clone() =>
+      new StreamingInputCallResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static StreamingInputCallResponse create() =>
+      new StreamingInputCallResponse();
+  static PbList<StreamingInputCallResponse> createRepeated() =>
+      new PbList<StreamingInputCallResponse>();
+  static StreamingInputCallResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyStreamingInputCallResponse();
+    return _defaultInstance;
+  }
+
+  static StreamingInputCallResponse _defaultInstance;
+  static void $checkItem(StreamingInputCallResponse v) {
+    if (v is! StreamingInputCallResponse)
+      checkItemFailed(v, 'StreamingInputCallResponse');
+  }
+
+  int get aggregatedPayloadSize => $_get(0, 0);
+  set aggregatedPayloadSize(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasAggregatedPayloadSize() => $_has(0);
+  void clearAggregatedPayloadSize() => clearField(1);
+}
+
+class _ReadonlyStreamingInputCallResponse extends StreamingInputCallResponse
+    with ReadonlyMessageMixin {}
+
+class ResponseParameters extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ResponseParameters')
+    ..a<int>(1, 'size', PbFieldType.O3)
+    ..a<int>(2, 'intervalUs', PbFieldType.O3)
+    ..a<BoolValue>(
+        3, 'compressed', PbFieldType.OM, BoolValue.getDefault, BoolValue.create)
+    ..hasRequiredFields = false;
+
+  ResponseParameters() : super();
+  ResponseParameters.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ResponseParameters.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ResponseParameters clone() =>
+      new ResponseParameters()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ResponseParameters create() => new ResponseParameters();
+  static PbList<ResponseParameters> createRepeated() =>
+      new PbList<ResponseParameters>();
+  static ResponseParameters getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyResponseParameters();
+    return _defaultInstance;
+  }
+
+  static ResponseParameters _defaultInstance;
+  static void $checkItem(ResponseParameters v) {
+    if (v is! ResponseParameters) checkItemFailed(v, 'ResponseParameters');
+  }
+
+  int get size => $_get(0, 0);
+  set size(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasSize() => $_has(0);
+  void clearSize() => clearField(1);
+
+  int get intervalUs => $_get(1, 0);
+  set intervalUs(int v) {
+    $_setUnsignedInt32(1, v);
+  }
+
+  bool hasIntervalUs() => $_has(1);
+  void clearIntervalUs() => clearField(2);
+
+  BoolValue get compressed => $_getN(2);
+  set compressed(BoolValue v) {
+    setField(3, v);
+  }
+
+  bool hasCompressed() => $_has(2);
+  void clearCompressed() => clearField(3);
+}
+
+class _ReadonlyResponseParameters extends ResponseParameters
+    with ReadonlyMessageMixin {}
+
+class StreamingOutputCallRequest extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('StreamingOutputCallRequest')
+    ..e<PayloadType>(1, 'responseType', PbFieldType.OE,
+        PayloadType.COMPRESSABLE, PayloadType.valueOf, PayloadType.values)
+    ..pp<ResponseParameters>(2, 'responseParameters', PbFieldType.PM,
+        ResponseParameters.$checkItem, ResponseParameters.create)
+    ..a<Payload>(
+        3, 'payload', PbFieldType.OM, Payload.getDefault, Payload.create)
+    ..a<EchoStatus>(7, 'responseStatus', PbFieldType.OM, EchoStatus.getDefault,
+        EchoStatus.create)
+    ..hasRequiredFields = false;
+
+  StreamingOutputCallRequest() : super();
+  StreamingOutputCallRequest.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  StreamingOutputCallRequest.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  StreamingOutputCallRequest clone() =>
+      new StreamingOutputCallRequest()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static StreamingOutputCallRequest create() =>
+      new StreamingOutputCallRequest();
+  static PbList<StreamingOutputCallRequest> createRepeated() =>
+      new PbList<StreamingOutputCallRequest>();
+  static StreamingOutputCallRequest getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyStreamingOutputCallRequest();
+    return _defaultInstance;
+  }
+
+  static StreamingOutputCallRequest _defaultInstance;
+  static void $checkItem(StreamingOutputCallRequest v) {
+    if (v is! StreamingOutputCallRequest)
+      checkItemFailed(v, 'StreamingOutputCallRequest');
+  }
+
+  PayloadType get responseType => $_getN(0);
+  set responseType(PayloadType v) {
+    setField(1, v);
+  }
+
+  bool hasResponseType() => $_has(0);
+  void clearResponseType() => clearField(1);
+
+  List<ResponseParameters> get responseParameters => $_getList(1);
+
+  Payload get payload => $_getN(2);
+  set payload(Payload v) {
+    setField(3, v);
+  }
+
+  bool hasPayload() => $_has(2);
+  void clearPayload() => clearField(3);
+
+  EchoStatus get responseStatus => $_getN(3);
+  set responseStatus(EchoStatus v) {
+    setField(7, v);
+  }
+
+  bool hasResponseStatus() => $_has(3);
+  void clearResponseStatus() => clearField(7);
+}
+
+class _ReadonlyStreamingOutputCallRequest extends StreamingOutputCallRequest
+    with ReadonlyMessageMixin {}
+
+class StreamingOutputCallResponse extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('StreamingOutputCallResponse')
+    ..a<Payload>(
+        1, 'payload', PbFieldType.OM, Payload.getDefault, Payload.create)
+    ..hasRequiredFields = false;
+
+  StreamingOutputCallResponse() : super();
+  StreamingOutputCallResponse.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  StreamingOutputCallResponse.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  StreamingOutputCallResponse clone() =>
+      new StreamingOutputCallResponse()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static StreamingOutputCallResponse create() =>
+      new StreamingOutputCallResponse();
+  static PbList<StreamingOutputCallResponse> createRepeated() =>
+      new PbList<StreamingOutputCallResponse>();
+  static StreamingOutputCallResponse getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyStreamingOutputCallResponse();
+    return _defaultInstance;
+  }
+
+  static StreamingOutputCallResponse _defaultInstance;
+  static void $checkItem(StreamingOutputCallResponse v) {
+    if (v is! StreamingOutputCallResponse)
+      checkItemFailed(v, 'StreamingOutputCallResponse');
+  }
+
+  Payload get payload => $_getN(0);
+  set payload(Payload v) {
+    setField(1, v);
+  }
+
+  bool hasPayload() => $_has(0);
+  void clearPayload() => clearField(1);
+}
+
+class _ReadonlyStreamingOutputCallResponse extends StreamingOutputCallResponse
+    with ReadonlyMessageMixin {}
+
+class ReconnectParams extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ReconnectParams')
+    ..a<int>(1, 'maxReconnectBackoffMs', PbFieldType.O3)
+    ..hasRequiredFields = false;
+
+  ReconnectParams() : super();
+  ReconnectParams.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ReconnectParams.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ReconnectParams clone() => new ReconnectParams()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ReconnectParams create() => new ReconnectParams();
+  static PbList<ReconnectParams> createRepeated() =>
+      new PbList<ReconnectParams>();
+  static ReconnectParams getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyReconnectParams();
+    return _defaultInstance;
+  }
+
+  static ReconnectParams _defaultInstance;
+  static void $checkItem(ReconnectParams v) {
+    if (v is! ReconnectParams) checkItemFailed(v, 'ReconnectParams');
+  }
+
+  int get maxReconnectBackoffMs => $_get(0, 0);
+  set maxReconnectBackoffMs(int v) {
+    $_setUnsignedInt32(0, v);
+  }
+
+  bool hasMaxReconnectBackoffMs() => $_has(0);
+  void clearMaxReconnectBackoffMs() => clearField(1);
+}
+
+class _ReadonlyReconnectParams extends ReconnectParams
+    with ReadonlyMessageMixin {}
+
+class ReconnectInfo extends GeneratedMessage {
+  static final BuilderInfo _i = new BuilderInfo('ReconnectInfo')
+    ..aOB(1, 'passed')
+    ..p<int>(2, 'backoffMs', PbFieldType.P3)
+    ..hasRequiredFields = false;
+
+  ReconnectInfo() : super();
+  ReconnectInfo.fromBuffer(List<int> i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromBuffer(i, r);
+  ReconnectInfo.fromJson(String i,
+      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
+      : super.fromJson(i, r);
+  ReconnectInfo clone() => new ReconnectInfo()..mergeFromMessage(this);
+  BuilderInfo get info_ => _i;
+  static ReconnectInfo create() => new ReconnectInfo();
+  static PbList<ReconnectInfo> createRepeated() => new PbList<ReconnectInfo>();
+  static ReconnectInfo getDefault() {
+    if (_defaultInstance == null)
+      _defaultInstance = new _ReadonlyReconnectInfo();
+    return _defaultInstance;
+  }
+
+  static ReconnectInfo _defaultInstance;
+  static void $checkItem(ReconnectInfo v) {
+    if (v is! ReconnectInfo) checkItemFailed(v, 'ReconnectInfo');
+  }
+
+  bool get passed => $_get(0, false);
+  set passed(bool v) {
+    $_setBool(0, v);
+  }
+
+  bool hasPassed() => $_has(0);
+  void clearPassed() => clearField(1);
+
+  List<int> get backoffMs => $_getList(1);
+}
+
+class _ReadonlyReconnectInfo extends ReconnectInfo with ReadonlyMessageMixin {}
diff --git a/grpc/interop/lib/src/generated/messages.pbenum.dart b/grpc/interop/lib/src/generated/messages.pbenum.dart
new file mode 100644
index 0000000..964d000
--- /dev/null
+++ b/grpc/interop/lib/src/generated/messages.pbenum.dart
@@ -0,0 +1,26 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc.testing_messages_pbenum;
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
+import 'dart:core' show int, dynamic, String, List, Map;
+import 'package:protobuf/protobuf.dart';
+
+class PayloadType extends ProtobufEnum {
+  static const PayloadType COMPRESSABLE =
+      const PayloadType._(0, 'COMPRESSABLE');
+
+  static const List<PayloadType> values = const <PayloadType>[
+    COMPRESSABLE,
+  ];
+
+  static final Map<int, dynamic> _byValue = ProtobufEnum.initByValue(values);
+  static PayloadType valueOf(int value) => _byValue[value] as PayloadType;
+  static void $checkItem(PayloadType v) {
+    if (v is! PayloadType) checkItemFailed(v, 'PayloadType');
+  }
+
+  const PayloadType._(int v, String n) : super(v, n);
+}
diff --git a/grpc/interop/lib/src/generated/test.pb.dart b/grpc/interop/lib/src/generated/test.pb.dart
new file mode 100644
index 0000000..d64b754
--- /dev/null
+++ b/grpc/interop/lib/src/generated/test.pb.dart
@@ -0,0 +1,8 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc.testing_test;
+
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
diff --git a/grpc/interop/lib/src/generated/test.pbgrpc.dart b/grpc/interop/lib/src/generated/test.pbgrpc.dart
new file mode 100644
index 0000000..f7ba4db
--- /dev/null
+++ b/grpc/interop/lib/src/generated/test.pbgrpc.dart
@@ -0,0 +1,307 @@
+///
+//  Generated code. Do not modify.
+///
+// ignore_for_file: non_constant_identifier_names,library_prefixes
+library grpc.testing_test_pbgrpc;
+
+import 'dart:async';
+
+import 'package:grpc/grpc.dart';
+
+import 'empty.pb.dart';
+import 'messages.pb.dart';
+export 'test.pb.dart';
+
+class TestServiceClient extends Client {
+  static final _$emptyCall = new ClientMethod<Empty, Empty>(
+      '/grpc.testing.TestService/EmptyCall',
+      (Empty value) => value.writeToBuffer(),
+      (List<int> value) => new Empty.fromBuffer(value));
+  static final _$unaryCall = new ClientMethod<SimpleRequest, SimpleResponse>(
+      '/grpc.testing.TestService/UnaryCall',
+      (SimpleRequest value) => value.writeToBuffer(),
+      (List<int> value) => new SimpleResponse.fromBuffer(value));
+  static final _$cacheableUnaryCall =
+      new ClientMethod<SimpleRequest, SimpleResponse>(
+          '/grpc.testing.TestService/CacheableUnaryCall',
+          (SimpleRequest value) => value.writeToBuffer(),
+          (List<int> value) => new SimpleResponse.fromBuffer(value));
+  static final _$streamingOutputCall =
+      new ClientMethod<StreamingOutputCallRequest, StreamingOutputCallResponse>(
+          '/grpc.testing.TestService/StreamingOutputCall',
+          (StreamingOutputCallRequest value) => value.writeToBuffer(),
+          (List<int> value) =>
+              new StreamingOutputCallResponse.fromBuffer(value));
+  static final _$streamingInputCall =
+      new ClientMethod<StreamingInputCallRequest, StreamingInputCallResponse>(
+          '/grpc.testing.TestService/StreamingInputCall',
+          (StreamingInputCallRequest value) => value.writeToBuffer(),
+          (List<int> value) =>
+              new StreamingInputCallResponse.fromBuffer(value));
+  static final _$fullDuplexCall =
+      new ClientMethod<StreamingOutputCallRequest, StreamingOutputCallResponse>(
+          '/grpc.testing.TestService/FullDuplexCall',
+          (StreamingOutputCallRequest value) => value.writeToBuffer(),
+          (List<int> value) =>
+              new StreamingOutputCallResponse.fromBuffer(value));
+  static final _$halfDuplexCall =
+      new ClientMethod<StreamingOutputCallRequest, StreamingOutputCallResponse>(
+          '/grpc.testing.TestService/HalfDuplexCall',
+          (StreamingOutputCallRequest value) => value.writeToBuffer(),
+          (List<int> value) =>
+              new StreamingOutputCallResponse.fromBuffer(value));
+  static final _$unimplementedCall = new ClientMethod<Empty, Empty>(
+      '/grpc.testing.TestService/UnimplementedCall',
+      (Empty value) => value.writeToBuffer(),
+      (List<int> value) => new Empty.fromBuffer(value));
+
+  TestServiceClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<Empty> emptyCall(Empty request, {CallOptions options}) {
+    final call = $createCall(_$emptyCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<SimpleResponse> unaryCall(SimpleRequest request,
+      {CallOptions options}) {
+    final call = $createCall(_$unaryCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<SimpleResponse> cacheableUnaryCall(SimpleRequest request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$cacheableUnaryCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseStream<StreamingOutputCallResponse> streamingOutputCall(
+      StreamingOutputCallRequest request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$streamingOutputCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseStream(call);
+  }
+
+  ResponseFuture<StreamingInputCallResponse> streamingInputCall(
+      Stream<StreamingInputCallRequest> request,
+      {CallOptions options}) {
+    final call = $createCall(_$streamingInputCall, request, options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseStream<StreamingOutputCallResponse> fullDuplexCall(
+      Stream<StreamingOutputCallRequest> request,
+      {CallOptions options}) {
+    final call = $createCall(_$fullDuplexCall, request, options: options);
+    return new ResponseStream(call);
+  }
+
+  ResponseStream<StreamingOutputCallResponse> halfDuplexCall(
+      Stream<StreamingOutputCallRequest> request,
+      {CallOptions options}) {
+    final call = $createCall(_$halfDuplexCall, request, options: options);
+    return new ResponseStream(call);
+  }
+
+  ResponseFuture<Empty> unimplementedCall(Empty request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$unimplementedCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+}
+
+abstract class TestServiceBase extends Service {
+  String get $name => 'grpc.testing.TestService';
+
+  TestServiceBase() {
+    $addMethod(new ServiceMethod<Empty, Empty>(
+        'EmptyCall',
+        emptyCall_Pre,
+        false,
+        false,
+        (List<int> value) => new Empty.fromBuffer(value),
+        (Empty value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<SimpleRequest, SimpleResponse>(
+        'UnaryCall',
+        unaryCall_Pre,
+        false,
+        false,
+        (List<int> value) => new SimpleRequest.fromBuffer(value),
+        (SimpleResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<SimpleRequest, SimpleResponse>(
+        'CacheableUnaryCall',
+        cacheableUnaryCall_Pre,
+        false,
+        false,
+        (List<int> value) => new SimpleRequest.fromBuffer(value),
+        (SimpleResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<StreamingOutputCallRequest,
+            StreamingOutputCallResponse>(
+        'StreamingOutputCall',
+        streamingOutputCall_Pre,
+        false,
+        true,
+        (List<int> value) => new StreamingOutputCallRequest.fromBuffer(value),
+        (StreamingOutputCallResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<StreamingInputCallRequest,
+            StreamingInputCallResponse>(
+        'StreamingInputCall',
+        streamingInputCall,
+        true,
+        false,
+        (List<int> value) => new StreamingInputCallRequest.fromBuffer(value),
+        (StreamingInputCallResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<StreamingOutputCallRequest,
+            StreamingOutputCallResponse>(
+        'FullDuplexCall',
+        fullDuplexCall,
+        true,
+        true,
+        (List<int> value) => new StreamingOutputCallRequest.fromBuffer(value),
+        (StreamingOutputCallResponse value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<StreamingOutputCallRequest,
+            StreamingOutputCallResponse>(
+        'HalfDuplexCall',
+        halfDuplexCall,
+        true,
+        true,
+        (List<int> value) => new StreamingOutputCallRequest.fromBuffer(value),
+        (StreamingOutputCallResponse value) => value.writeToBuffer()));
+  }
+
+  Future<Empty> emptyCall_Pre(ServiceCall call, Future request) async {
+    return emptyCall(call, await request);
+  }
+
+  Future<SimpleResponse> unaryCall_Pre(ServiceCall call, Future request) async {
+    return unaryCall(call, await request);
+  }
+
+  Future<SimpleResponse> cacheableUnaryCall_Pre(
+      ServiceCall call, Future request) async {
+    return cacheableUnaryCall(call, await request);
+  }
+
+  Stream<StreamingOutputCallResponse> streamingOutputCall_Pre(
+      ServiceCall call, Future request) async* {
+    yield* streamingOutputCall(
+        call, (await request) as StreamingOutputCallRequest);
+  }
+
+  Future<Empty> emptyCall(ServiceCall call, Empty request);
+  Future<SimpleResponse> unaryCall(ServiceCall call, SimpleRequest request);
+  Future<SimpleResponse> cacheableUnaryCall(
+      ServiceCall call, SimpleRequest request);
+  Stream<StreamingOutputCallResponse> streamingOutputCall(
+      ServiceCall call, StreamingOutputCallRequest request);
+  Future<StreamingInputCallResponse> streamingInputCall(
+      ServiceCall call, Stream<StreamingInputCallRequest> request);
+  Stream<StreamingOutputCallResponse> fullDuplexCall(
+      ServiceCall call, Stream<StreamingOutputCallRequest> request);
+  Stream<StreamingOutputCallResponse> halfDuplexCall(
+      ServiceCall call, Stream<StreamingOutputCallRequest> request);
+}
+
+class UnimplementedServiceClient extends Client {
+  static final _$unimplementedCall = new ClientMethod<Empty, Empty>(
+      '/grpc.testing.UnimplementedService/UnimplementedCall',
+      (Empty value) => value.writeToBuffer(),
+      (List<int> value) => new Empty.fromBuffer(value));
+
+  UnimplementedServiceClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<Empty> unimplementedCall(Empty request,
+      {CallOptions options}) {
+    final call = $createCall(
+        _$unimplementedCall, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+}
+
+abstract class UnimplementedServiceBase extends Service {
+  String get $name => 'grpc.testing.UnimplementedService';
+
+  UnimplementedServiceBase() {
+    $addMethod(new ServiceMethod<Empty, Empty>(
+        'UnimplementedCall',
+        unimplementedCall_Pre,
+        false,
+        false,
+        (List<int> value) => new Empty.fromBuffer(value),
+        (Empty value) => value.writeToBuffer()));
+  }
+
+  Future<Empty> unimplementedCall_Pre(ServiceCall call, Future request) async {
+    return unimplementedCall(call, await request);
+  }
+
+  Future<Empty> unimplementedCall(ServiceCall call, Empty request);
+}
+
+class ReconnectServiceClient extends Client {
+  static final _$start = new ClientMethod<ReconnectParams, Empty>(
+      '/grpc.testing.ReconnectService/Start',
+      (ReconnectParams value) => value.writeToBuffer(),
+      (List<int> value) => new Empty.fromBuffer(value));
+  static final _$stop = new ClientMethod<Empty, ReconnectInfo>(
+      '/grpc.testing.ReconnectService/Stop',
+      (Empty value) => value.writeToBuffer(),
+      (List<int> value) => new ReconnectInfo.fromBuffer(value));
+
+  ReconnectServiceClient(ClientChannel channel, {CallOptions options})
+      : super(channel, options: options);
+
+  ResponseFuture<Empty> start(ReconnectParams request, {CallOptions options}) {
+    final call = $createCall(_$start, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+
+  ResponseFuture<ReconnectInfo> stop(Empty request, {CallOptions options}) {
+    final call = $createCall(_$stop, new Stream.fromIterable([request]),
+        options: options);
+    return new ResponseFuture(call);
+  }
+}
+
+abstract class ReconnectServiceBase extends Service {
+  String get $name => 'grpc.testing.ReconnectService';
+
+  ReconnectServiceBase() {
+    $addMethod(new ServiceMethod<ReconnectParams, Empty>(
+        'Start',
+        start_Pre,
+        false,
+        false,
+        (List<int> value) => new ReconnectParams.fromBuffer(value),
+        (Empty value) => value.writeToBuffer()));
+    $addMethod(new ServiceMethod<Empty, ReconnectInfo>(
+        'Stop',
+        stop_Pre,
+        false,
+        false,
+        (List<int> value) => new Empty.fromBuffer(value),
+        (ReconnectInfo value) => value.writeToBuffer()));
+  }
+
+  Future<Empty> start_Pre(ServiceCall call, Future request) async {
+    return start(call, await request);
+  }
+
+  Future<ReconnectInfo> stop_Pre(ServiceCall call, Future request) async {
+    return stop(call, await request);
+  }
+
+  Future<Empty> start(ServiceCall call, ReconnectParams request);
+  Future<ReconnectInfo> stop(ServiceCall call, Empty request);
+}
diff --git a/grpc/interop/protos/empty.proto b/grpc/interop/protos/empty.proto
new file mode 100644
index 0000000..6d0eb93
--- /dev/null
+++ b/grpc/interop/protos/empty.proto
@@ -0,0 +1,43 @@
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+// An empty message that you can re-use to avoid defining duplicated empty
+// messages in your project. A typical example is to use it as argument or the
+// return value of a service API. For instance:
+//
+//   service Foo {
+//     rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
+//   };
+//
+message Empty {}
diff --git a/grpc/interop/protos/messages.proto b/grpc/interop/protos/messages.proto
new file mode 100644
index 0000000..a14922a
--- /dev/null
+++ b/grpc/interop/protos/messages.proto
@@ -0,0 +1,184 @@
+
+// Copyright 2015-2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Message definitions to be used by integration test service definitions.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+// TODO(dgq): Go back to using well-known types once
+// https://github.com/grpc/grpc/issues/6980 has been fixed.
+// import "google/protobuf/wrappers.proto";
+message BoolValue {
+  // The bool value.
+  bool value = 1;
+}
+
+// DEPRECATED, don't use. To be removed shortly.
+// The type of payload that should be returned.
+enum PayloadType {
+  // Compressable text format.
+  COMPRESSABLE = 0;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+  // DEPRECATED, don't use. To be removed shortly.
+  // The type of data in body.
+  PayloadType type = 1;
+  // Primary contents of payload.
+  bytes body = 2;
+}
+
+// A protobuf representation for grpc status. This is used by test
+// clients to specify a status that the server should attempt to return.
+message EchoStatus {
+  int32 code = 1;
+  string message = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+  // DEPRECATED, don't use. To be removed shortly.
+  // Desired payload type in the response from the server.
+  // If response_type is RANDOM, server randomly chooses one from other formats.
+  PayloadType response_type = 1;
+
+  // Desired payload size in the response from the server.
+  int32 response_size = 2;
+
+  // Optional input payload sent along with the request.
+  Payload payload = 3;
+
+  // Whether SimpleResponse should include username.
+  bool fill_username = 4;
+
+  // Whether SimpleResponse should include OAuth scope.
+  bool fill_oauth_scope = 5;
+
+  // Whether to request the server to compress the response. This field is
+  // "nullable" in order to interoperate seamlessly with clients not able to
+  // implement the full compression tests by introspecting the call to verify
+  // the response's compression status.
+  BoolValue response_compressed = 6;
+
+  // Whether server should return a given status
+  EchoStatus response_status = 7;
+
+  // Whether the server should expect this request to be compressed.
+  BoolValue expect_compressed = 8;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+  // Payload to increase message size.
+  Payload payload = 1;
+  // The user the request came from, for verifying authentication was
+  // successful when the client expected it.
+  string username = 2;
+  // OAuth scope.
+  string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+  // Optional input payload sent along with the request.
+  Payload payload = 1;
+
+  // Whether the server should expect this request to be compressed. This field
+  // is "nullable" in order to interoperate seamlessly with servers not able to
+  // implement the full compression tests by introspecting the call to verify
+  // the request's compression status.
+  BoolValue expect_compressed = 2;
+
+  // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+  // Aggregated size of payloads received from the client.
+  int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+  // Desired payload sizes in responses from the server.
+  int32 size = 1;
+
+  // Desired interval between consecutive responses in the response stream in
+  // microseconds.
+  int32 interval_us = 2;
+
+  // Whether to request the server to compress the response. This field is
+  // "nullable" in order to interoperate seamlessly with clients not able to
+  // implement the full compression tests by introspecting the call to verify
+  // the response's compression status.
+  BoolValue compressed = 3;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+  // DEPRECATED, don't use. To be removed shortly.
+  // Desired payload type in the response from the server.
+  // If response_type is RANDOM, the payload from each response in the stream
+  // might be of different types. This is to simulate a mixed type of payload
+  // stream.
+  PayloadType response_type = 1;
+
+  // Configuration for each expected response message.
+  repeated ResponseParameters response_parameters = 2;
+
+  // Optional input payload sent along with the request.
+  Payload payload = 3;
+
+  // Whether server should return a given status.
+  EchoStatus response_status = 7;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+  // Payload to increase response size.
+  Payload payload = 1;
+}
+
+// For reconnect interop test only.
+// Client tells server what reconnection parameters it used.
+message ReconnectParams {
+  int32 max_reconnect_backoff_ms = 1;
+}
+
+// For reconnect interop test only.
+// Server tells client whether its reconnects are following the spec and the
+// reconnect backoffs it saw.
+message ReconnectInfo {
+  bool passed = 1;
+  repeated int32 backoff_ms = 2;
+}
diff --git a/grpc/interop/protos/test.proto b/grpc/interop/protos/test.proto
new file mode 100644
index 0000000..202276b
--- /dev/null
+++ b/grpc/interop/protos/test.proto
@@ -0,0 +1,94 @@
+
+// Copyright 2015-2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+
+syntax = "proto3";
+
+import "empty.proto";
+import "messages.proto";
+
+package grpc.testing;
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+  // One empty request followed by one empty response.
+  rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+
+  // One request followed by one response.
+  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+  // One request followed by one response. Response has cache control
+  // headers set such that a caching HTTP proxy (such as GFE) can
+  // satisfy subsequent requests.
+  rpc CacheableUnaryCall(SimpleRequest) returns (SimpleResponse);
+
+  // One request followed by a sequence of responses (streamed download).
+  // The server returns the payload with client desired type and sizes.
+  rpc StreamingOutputCall(StreamingOutputCallRequest)
+      returns (stream StreamingOutputCallResponse);
+
+  // A sequence of requests followed by one response (streamed upload).
+  // The server returns the aggregated size of client payload as the result.
+  rpc StreamingInputCall(stream StreamingInputCallRequest)
+      returns (StreamingInputCallResponse);
+
+  // A sequence of requests with each request served by the server immediately.
+  // As one request could lead to multiple responses, this interface
+  // demonstrates the idea of full duplexing.
+  rpc FullDuplexCall(stream StreamingOutputCallRequest)
+      returns (stream StreamingOutputCallResponse);
+
+  // A sequence of requests followed by a sequence of responses.
+  // The server buffers all the client requests and then serves them in order. A
+  // stream of responses are returned to the client when the server starts with
+  // first request.
+  rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+      returns (stream StreamingOutputCallResponse);
+
+  // The test server will not implement this method. It will be used
+  // to test the behavior when clients call unimplemented methods.
+  rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+}
+
+// A simple service NOT implemented at servers so clients can test for
+// that case.
+service UnimplementedService {
+  // A call that no server should implement
+  rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+}
+
+// A service used to control reconnect server.
+service ReconnectService {
+  rpc Start(grpc.testing.ReconnectParams) returns (grpc.testing.Empty);
+  rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo);
+}
diff --git a/grpc/interop/pubspec.yaml b/grpc/interop/pubspec.yaml
new file mode 100644
index 0000000..9432b81
--- /dev/null
+++ b/grpc/interop/pubspec.yaml
@@ -0,0 +1,17 @@
+name: interop
+description: Dart gRPC interoperability test suite.
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  args: ^1.5.0
+  async: '>=1.13.3 <3.0.0'
+  collection: ^1.14.2
+  grpc:
+    path: ../
+  protobuf: ^0.10.1
+
+dev_dependencies:
+  test: ^1.3.0
diff --git a/grpc/interop/server1.key b/grpc/interop/server1.key
new file mode 100644
index 0000000..143a5b8
--- /dev/null
+++ b/grpc/interop/server1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
+M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
+3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
+AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
+V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
+tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
+dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
+K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
+81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
+DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
+aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
+ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
+XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
+F98XJ7tIFfJq
+-----END PRIVATE KEY-----
diff --git a/grpc/interop/server1.pem b/grpc/interop/server1.pem
new file mode 100644
index 0000000..f3d43fc
--- /dev/null
+++ b/grpc/interop/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
+MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
+ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
+LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
+zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
+9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
+CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
+em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
+CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
+hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
+y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
+-----END CERTIFICATE-----
diff --git a/grpc/interop/tool/regenerate.sh b/grpc/interop/tool/regenerate.sh
new file mode 100755
index 0000000..066a0f9
--- /dev/null
+++ b/grpc/interop/tool/regenerate.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+mkdir -p lib/src/generated
+protoc --dart_out=grpc:lib/src/generated -Iprotos/ protos/*.proto
+rm lib/src/generated/*.pbjson.dart
+rm lib/src/generated/{empty,test}.pbenum.dart
+dartfmt -w lib/src/generated
diff --git a/grpc/lib/grpc.dart b/grpc/lib/grpc.dart
new file mode 100644
index 0000000..07bdb25
--- /dev/null
+++ b/grpc/lib/grpc.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export 'src/auth/auth.dart';
+
+export 'src/client/call.dart';
+export 'src/client/channel.dart';
+export 'src/client/client.dart';
+export 'src/client/common.dart';
+export 'src/client/connection.dart';
+export 'src/client/method.dart';
+export 'src/client/options.dart';
+
+export 'src/server/call.dart';
+export 'src/server/handler.dart' show ServerHandler;
+export 'src/server/interceptor.dart';
+export 'src/server/server.dart';
+export 'src/server/service.dart';
+
+export 'src/shared/security.dart';
+export 'src/shared/status.dart';
+export 'src/shared/streams.dart';
+export 'src/shared/timeout.dart';
diff --git a/grpc/lib/service_api.dart b/grpc/lib/service_api.dart
new file mode 100644
index 0000000..54a455a
--- /dev/null
+++ b/grpc/lib/service_api.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Exports the minimum api to define server and client stubs.
+///
+/// Mainly intended to be imported by generated code.
+library service_api;
+
+export 'src/client/channel.dart' show ClientChannel;
+export 'src/client/client.dart' show Client;
+export 'src/client/common.dart' show ResponseFuture, ResponseStream;
+export 'src/client/method.dart' show ClientMethod;
+export 'src/client/options.dart' show CallOptions;
+export 'src/server/call.dart' show ServiceCall;
+export 'src/server/server.dart' show Server;
+export 'src/server/service.dart' show Service, ServiceMethod;
diff --git a/grpc/lib/src/auth/auth.dart b/grpc/lib/src/auth/auth.dart
new file mode 100644
index 0000000..27aa57d
--- /dev/null
+++ b/grpc/lib/src/auth/auth.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2018, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:googleapis_auth/auth_io.dart' as auth;
+import 'package:googleapis_auth/src/crypto/rsa_sign.dart';
+import 'package:grpc/src/shared/status.dart';
+import 'package:http/http.dart' as http;
+
+import '../client/options.dart';
+
+const _tokenExpirationThreshold = const Duration(seconds: 30);
+
+abstract class BaseAuthenticator {
+  auth.AccessToken _accessToken;
+  String _lastUri;
+
+  Future<void> authenticate(Map<String, String> metadata, String uri) async {
+    if (uri == null) {
+      throw new GrpcError.unauthenticated(
+          'Credentials require secure transport.');
+    }
+    if (_accessToken == null || _accessToken.hasExpired || uri != _lastUri) {
+      await obtainAccessCredentials(uri);
+      _lastUri = uri;
+    }
+
+    final auth = '${_accessToken.type} ${_accessToken.data}';
+    metadata['authorization'] = auth;
+
+    if (_tokenExpiresSoon) {
+      // Token is about to expire. Extend it prematurely.
+      obtainAccessCredentials(_lastUri).catchError((_) {});
+    }
+  }
+
+  bool get _tokenExpiresSoon => _accessToken.expiry
+      .subtract(_tokenExpirationThreshold)
+      .isBefore(new DateTime.now().toUtc());
+
+  CallOptions get toCallOptions => new CallOptions(providers: [authenticate]);
+
+  Future<void> obtainAccessCredentials(String uri);
+}
+
+abstract class HttpBasedAuthenticator extends BaseAuthenticator {
+  Future<void> _call;
+
+  Future<void> obtainAccessCredentials(String uri) {
+    if (_call == null) {
+      final authClient = new http.Client();
+      _call = obtainCredentialsWithClient(authClient, uri).then((credentials) {
+        _accessToken = credentials.accessToken;
+        _call = null;
+        authClient.close();
+      });
+    }
+    return _call;
+  }
+
+  Future<auth.AccessCredentials> obtainCredentialsWithClient(
+      http.Client client, String uri);
+}
+
+class ComputeEngineAuthenticator extends HttpBasedAuthenticator {
+  Future<auth.AccessCredentials> obtainCredentialsWithClient(
+          http.Client client, String uri) =>
+      auth.obtainAccessCredentialsViaMetadataServer(client);
+}
+
+class ServiceAccountAuthenticator extends HttpBasedAuthenticator {
+  auth.ServiceAccountCredentials _serviceAccountCredentials;
+  final List<String> _scopes;
+  String _projectId;
+
+  ServiceAccountAuthenticator(String serviceAccountJson, this._scopes) {
+    final serviceAccount = jsonDecode(serviceAccountJson);
+    _serviceAccountCredentials =
+        new auth.ServiceAccountCredentials.fromJson(serviceAccount);
+    _projectId = serviceAccount['project_id'];
+  }
+
+  String get projectId => _projectId;
+
+  Future<auth.AccessCredentials> obtainCredentialsWithClient(
+          http.Client client, String uri) =>
+      auth.obtainAccessCredentialsViaServiceAccount(
+          _serviceAccountCredentials, _scopes, client);
+}
+
+class JwtServiceAccountAuthenticator extends BaseAuthenticator {
+  auth.ServiceAccountCredentials _serviceAccountCredentials;
+  String _projectId;
+  String _keyId;
+
+  JwtServiceAccountAuthenticator(String serviceAccountJson) {
+    final serviceAccount = jsonDecode(serviceAccountJson);
+    _serviceAccountCredentials =
+        new auth.ServiceAccountCredentials.fromJson(serviceAccount);
+    _projectId = serviceAccount['project_id'];
+    _keyId = serviceAccount['private_key_id'];
+  }
+
+  String get projectId => _projectId;
+
+  Future<void> obtainAccessCredentials(String uri) async {
+    _accessToken = _jwtTokenFor(_serviceAccountCredentials, _keyId, uri);
+  }
+}
+
+// TODO(jakobr): Expose in googleapis_auth.
+auth.AccessToken _jwtTokenFor(
+    auth.ServiceAccountCredentials credentials, String keyId, String uri,
+    {String user, List<String> scopes}) {
+  // Subtracting 20 seconds from current timestamp to allow for clock skew among
+  // servers.
+  final timestamp =
+      (new DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000) - 20;
+  final expiry = timestamp + 3600;
+
+  final header = <String, String>{'alg': 'RS256', 'typ': 'JWT'};
+  if (keyId != null) {
+    header['kid'] = keyId;
+  }
+
+  final claims = <String, dynamic>{
+    'iss': credentials.email,
+    'aud': uri,
+    'exp': expiry,
+    'iat': timestamp,
+    'sub': user ?? credentials.email
+  };
+  if (scopes != null) {
+    claims['scope'] = scopes.join(' ');
+  }
+
+  final headerBase64 = _base64url(ascii.encode(jsonEncode(header)));
+  final claimsBase64 = _base64url(utf8.encode(jsonEncode(claims)));
+
+  final data = '$headerBase64.$claimsBase64';
+
+  final signer = new RS256Signer(credentials.privateRSAKey);
+  final signature = signer.sign(ascii.encode(data));
+
+  final jwt = '$data.${_base64url(signature)}';
+
+  return new auth.AccessToken('Bearer', jwt,
+      new DateTime.fromMillisecondsSinceEpoch(expiry * 1000, isUtc: true));
+}
+
+String _base64url(List<int> bytes) {
+  return base64Url.encode(bytes).replaceAll('=', '');
+}
diff --git a/grpc/lib/src/client/call.dart b/grpc/lib/src/client/call.dart
new file mode 100644
index 0000000..b1c890f
--- /dev/null
+++ b/grpc/lib/src/client/call.dart
@@ -0,0 +1,308 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'package:http2/transport.dart';
+
+import '../shared/status.dart';
+import '../shared/streams.dart';
+
+import 'common.dart';
+import 'connection.dart';
+import 'method.dart';
+import 'options.dart';
+
+const _reservedHeaders = const [
+  'content-type',
+  'te',
+  'grpc-timeout',
+  'grpc-accept-encoding',
+  'user-agent',
+];
+
+/// An active call to a gRPC endpoint.
+class ClientCall<Q, R> implements Response {
+  final ClientMethod<Q, R> _method;
+  final Stream<Q> _requests;
+  final CallOptions options;
+
+  final _headers = new Completer<Map<String, String>>();
+  final _trailers = new Completer<Map<String, String>>();
+  bool _hasReceivedResponses = false;
+
+  Map<String, String> _headerMetadata;
+
+  TransportStream _stream;
+  StreamController<R> _responses;
+  StreamSubscription<StreamMessage> _requestSubscription;
+  StreamSubscription<GrpcMessage> _responseSubscription;
+
+  bool isCancelled = false;
+  Timer _timeoutTimer;
+
+  ClientCall(this._method, this._requests, this.options) {
+    _responses = new StreamController(onListen: _onResponseListen);
+    if (options.timeout != null) {
+      _timeoutTimer = new Timer(options.timeout, _onTimedOut);
+    }
+  }
+
+  String get path => _method.path;
+
+  void onConnectionError(error) {
+    _terminateWithError(new GrpcError.unavailable('Error connecting: $error'));
+  }
+
+  void _terminateWithError(GrpcError error) {
+    if (!_responses.isClosed) {
+      _responses.addError(error);
+    }
+    _safeTerminate();
+  }
+
+  static Map<String, String> _sanitizeMetadata(Map<String, String> metadata) {
+    final sanitizedMetadata = <String, String>{};
+    metadata.forEach((String key, String value) {
+      final lowerCaseKey = key.trim().toLowerCase();
+      if (!lowerCaseKey.startsWith(':') &&
+          !_reservedHeaders.contains(lowerCaseKey)) {
+        sanitizedMetadata[lowerCaseKey] = value.trim();
+      }
+    });
+    return sanitizedMetadata;
+  }
+
+  void onConnectionReady(ClientConnection connection) {
+    if (isCancelled) return;
+
+    if (options.metadataProviders.isEmpty) {
+      _sendRequest(connection, _sanitizeMetadata(options.metadata));
+    } else {
+      final metadata = new Map<String, String>.from(options.metadata);
+      String audience;
+      if (connection.options.credentials.isSecure) {
+        final port = connection.port != 443 ? ':${connection.port}' : '';
+        final lastSlashPos = path.lastIndexOf('/');
+        final audiencePath =
+            lastSlashPos == -1 ? path : path.substring(0, lastSlashPos);
+        audience = 'https://${connection.authority}$port$audiencePath';
+      }
+      Future.forEach(options.metadataProviders,
+              (provider) => provider(metadata, audience))
+          .then((_) => _sendRequest(connection, _sanitizeMetadata(metadata)))
+          .catchError(_onMetadataProviderError);
+    }
+  }
+
+  void _onMetadataProviderError(error) {
+    _terminateWithError(new GrpcError.internal('Error making call: $error'));
+  }
+
+  void _sendRequest(ClientConnection connection, Map<String, String> metadata) {
+    try {
+      _stream = connection.makeRequest(path, options.timeout, metadata);
+    } catch (e) {
+      _terminateWithError(new GrpcError.unavailable('Error making call: $e'));
+      return;
+    }
+    _requestSubscription = _requests
+        .map(_method.requestSerializer)
+        .map(GrpcHttpEncoder.frame)
+        .map<StreamMessage>((bytes) => new DataStreamMessage(bytes))
+        .handleError(_onRequestError)
+        .listen(_stream.outgoingMessages.add,
+            onError: _stream.outgoingMessages.addError,
+            onDone: _stream.outgoingMessages.close,
+            cancelOnError: true);
+    // The response stream might have been listened to before _stream was ready,
+    // so try setting up the subscription here as well.
+    _onResponseListen();
+  }
+
+  void _onTimedOut() {
+    _responses.addError(new GrpcError.deadlineExceeded('Deadline exceeded'));
+    _safeTerminate();
+  }
+
+  /// Subscribe to incoming response messages, once [_stream] is available, and
+  /// the caller has subscribed to the [_responses] stream.
+  void _onResponseListen() {
+    if (_stream != null &&
+        _responses.hasListener &&
+        _responseSubscription == null) {
+      _responseSubscription = _stream.incomingMessages
+          .transform(new GrpcHttpDecoder())
+          .transform(grpcDecompressor())
+          .listen(_onResponseData,
+              onError: _onResponseError,
+              onDone: _onResponseDone,
+              cancelOnError: true);
+      if (_responses.isPaused) {
+        _responseSubscription.pause();
+      }
+      _responses.onPause = _responseSubscription.pause;
+      _responses.onResume = _responseSubscription.resume;
+      _responses.onCancel = _responseSubscription.cancel;
+    }
+  }
+
+  /// Emit an error response to the user, and tear down this call.
+  void _responseError(GrpcError error) {
+    _responses.addError(error);
+    _timeoutTimer?.cancel();
+    _requestSubscription?.cancel();
+    _responseSubscription.cancel();
+    _responses.close();
+    _stream.terminate();
+  }
+
+  /// Data handler for responses coming from the server. Handles header/trailer
+  /// metadata, and forwards response objects to [_responses].
+  void _onResponseData(GrpcMessage data) {
+    if (data is GrpcData) {
+      if (!_headers.isCompleted) {
+        _responseError(
+            new GrpcError.unimplemented('Received data before headers'));
+        return;
+      }
+      if (_trailers.isCompleted) {
+        _responseError(
+            new GrpcError.unimplemented('Received data after trailers'));
+        return;
+      }
+      _responses.add(_method.responseDeserializer(data.data));
+      _hasReceivedResponses = true;
+    } else if (data is GrpcMetadata) {
+      if (!_headers.isCompleted) {
+        // TODO(jakobr): Parse, and extract common headers.
+        _headerMetadata = data.metadata;
+        _headers.complete(_headerMetadata);
+        return;
+      }
+      if (_trailers.isCompleted) {
+        _responseError(
+            new GrpcError.unimplemented('Received multiple trailers'));
+        return;
+      }
+      final metadata = data.metadata;
+      _trailers.complete(metadata);
+      // TODO(jakobr): Parse more!
+      if (metadata.containsKey('grpc-status')) {
+        final status = int.parse(metadata['grpc-status']);
+        final message = metadata['grpc-message'];
+        if (status != 0) {
+          _responseError(new GrpcError.custom(status, message));
+        }
+      }
+    } else {
+      _responseError(new GrpcError.unimplemented('Unexpected frame received'));
+    }
+  }
+
+  /// Handler for response errors. Forward the error to the [_responses] stream,
+  /// wrapped if necessary.
+  void _onResponseError(error) {
+    if (error is GrpcError) {
+      _responseError(error);
+      return;
+    }
+    _responseError(new GrpcError.unknown(error.toString()));
+  }
+
+  /// Handles closure of the response stream. Verifies that server has sent
+  /// response messages and header/trailer metadata, as necessary.
+  void _onResponseDone() {
+    if (!_headers.isCompleted) {
+      _responseError(new GrpcError.unavailable('Did not receive anything'));
+      return;
+    }
+    if (!_trailers.isCompleted) {
+      if (_hasReceivedResponses) {
+        // Trailers are required after receiving data.
+        _responseError(new GrpcError.unavailable('Missing trailers'));
+        return;
+      }
+
+      // Only received a header frame and no data frames, so the header
+      // should contain "trailers" as well (Trailers-Only).
+      _trailers.complete(_headerMetadata);
+      final status = _headerMetadata['grpc-status'];
+      // If status code is missing, we must treat it as '0'. As in 'success'.
+      final statusCode = status != null ? int.parse(status) : 0;
+      if (statusCode != 0) {
+        final message = _headerMetadata['grpc-message'];
+        _responseError(new GrpcError.custom(statusCode, message));
+      }
+    }
+    _timeoutTimer?.cancel();
+    _responses.close();
+    _responseSubscription.cancel();
+  }
+
+  /// Error handler for the requests stream. Something went wrong while trying
+  /// to send the request to the server. Abort the request, and forward the
+  /// error to the user code on the [_responses] stream.
+  void _onRequestError(error) {
+    if (error is! GrpcError) {
+      error = new GrpcError.unknown(error.toString());
+    }
+
+    _responses.addError(error);
+    _timeoutTimer?.cancel();
+    _responses.close();
+    _requestSubscription?.cancel();
+    _responseSubscription?.cancel();
+    _stream.terminate();
+  }
+
+  Stream<R> get response => _responses.stream;
+
+  @override
+  Future<Map<String, String>> get headers => _headers.future;
+
+  @override
+  Future<Map<String, String>> get trailers => _trailers.future;
+
+  @override
+  Future<void> cancel() {
+    if (!_responses.isClosed) {
+      _responses.addError(new GrpcError.cancelled('Cancelled by client.'));
+    }
+    return _terminate();
+  }
+
+  Future<void> _terminate() async {
+    isCancelled = true;
+    _timeoutTimer?.cancel();
+    // Don't await _responses.close() here. It'll only complete once the done
+    // event has been delivered, and it's the caller of this function that is
+    // reading from responses as well, so we might end up deadlocked.
+    _responses.close();
+    _stream?.terminate();
+    final futures = <Future>[];
+    if (_requestSubscription != null) {
+      futures.add(_requestSubscription.cancel());
+    }
+    if (_responseSubscription != null) {
+      futures.add(_responseSubscription.cancel());
+    }
+    await Future.wait(futures);
+  }
+
+  Future<void> _safeTerminate() {
+    return _terminate().catchError((_) {});
+  }
+}
diff --git a/grpc/lib/src/client/channel.dart b/grpc/lib/src/client/channel.dart
new file mode 100644
index 0000000..68bd22f
--- /dev/null
+++ b/grpc/lib/src/client/channel.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import '../shared/status.dart';
+
+import 'call.dart';
+import 'connection.dart';
+import 'method.dart';
+import 'options.dart';
+
+/// A channel to a virtual RPC endpoint.
+///
+/// For each RPC, the channel picks a [ClientConnection] to dispatch the call.
+/// RPCs on the same channel may be sent to different connections, depending on
+/// load balancing settings.
+class ClientChannel {
+  final String host;
+  final int port;
+  final ChannelOptions options;
+
+  // TODO(jakobr): Multiple connections, load balancing.
+  ClientConnection _connection;
+
+  bool _isShutdown = false;
+
+  ClientChannel(this.host,
+      {this.port = 443, this.options = const ChannelOptions()});
+
+  /// Shuts down this channel.
+  ///
+  /// No further RPCs can be made on this channel. RPCs already in progress will
+  /// be allowed to complete.
+  Future<void> shutdown() async {
+    if (_isShutdown) return;
+    _isShutdown = true;
+    if (_connection != null) await _connection.shutdown();
+  }
+
+  /// Terminates this channel.
+  ///
+  /// RPCs already in progress will be terminated. No further RPCs can be made
+  /// on this channel.
+  Future<void> terminate() async {
+    _isShutdown = true;
+    if (_connection != null) await _connection.terminate();
+  }
+
+  /// Returns a connection to this [Channel]'s RPC endpoint.
+  ///
+  /// The connection may be shared between multiple RPCs.
+  Future<ClientConnection> getConnection() async {
+    if (_isShutdown) throw new GrpcError.unavailable('Channel shutting down.');
+    return _connection ??= new ClientConnection(host, port, options);
+  }
+
+  /// Initiates a new RPC on this connection.
+  ClientCall<Q, R> createCall<Q, R>(
+      ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options) {
+    final call = new ClientCall(method, requests, options);
+    getConnection().then((connection) {
+      if (call.isCancelled) return;
+      connection.dispatchCall(call);
+    }, onError: call.onConnectionError);
+    return call;
+  }
+}
diff --git a/grpc/lib/src/client/client.dart b/grpc/lib/src/client/client.dart
new file mode 100644
index 0000000..cce6d1c
--- /dev/null
+++ b/grpc/lib/src/client/client.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'call.dart';
+import 'channel.dart';
+import 'method.dart';
+import 'options.dart';
+
+/// Base class for client stubs.
+class Client {
+  final ClientChannel _channel;
+  final CallOptions _options;
+
+  Client(this._channel, {CallOptions options})
+      : _options = options ?? new CallOptions();
+
+  ClientCall<Q, R> $createCall<Q, R>(
+      ClientMethod<Q, R> method, Stream<Q> requests,
+      {CallOptions options}) {
+    return _channel.createCall(method, requests, _options.mergedWith(options));
+  }
+}
diff --git a/grpc/lib/src/client/common.dart b/grpc/lib/src/client/common.dart
new file mode 100644
index 0000000..87189a7
--- /dev/null
+++ b/grpc/lib/src/client/common.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+
+import '../shared/status.dart';
+import 'call.dart';
+
+/// A gRPC response.
+abstract class Response {
+  /// Header metadata returned from the server.
+  ///
+  /// The [headers] future will complete before any response objects become
+  /// available. If [cancel] is called before the headers are available, the
+  /// returned future will complete with an error.
+  Future<Map<String, String>> get headers;
+
+  /// Trailer metadata returned from the server.
+  ///
+  /// The [trailers] future will complete after all responses have been received
+  /// from the server. If [cancel] is called before the trailers are available,
+  /// the returned future will complete with an error.
+  Future<Map<String, String>> get trailers;
+
+  /// Cancel this gRPC call. Any remaining request objects will not be sent, and
+  /// no further responses will be received.
+  Future<void> cancel();
+}
+
+/// A gRPC response producing a single value.
+class ResponseFuture<R> extends DelegatingFuture<R>
+    with _ResponseMixin<dynamic, R> {
+  final ClientCall<dynamic, R> _call;
+
+  static R _ensureOnlyOneResponse<R>(R previous, R element) {
+    if (previous != null) {
+      throw new GrpcError.unimplemented('More than one response received');
+    }
+    return element;
+  }
+
+  static R _ensureOneResponse<R>(R value) {
+    if (value == null)
+      throw new GrpcError.unimplemented('No responses received');
+    return value;
+  }
+
+  ResponseFuture(this._call)
+      : super(_call.response
+            .fold(null, _ensureOnlyOneResponse)
+            .then(_ensureOneResponse));
+}
+
+/// A gRPC response producing a stream of values.
+class ResponseStream<R> extends DelegatingStream<R>
+    with _ResponseMixin<dynamic, R> {
+  final ClientCall<dynamic, R> _call;
+
+  ResponseStream(this._call) : super(_call.response);
+}
+
+abstract class _ResponseMixin<Q, R> implements Response {
+  ClientCall<Q, R> get _call;
+
+  @override
+  Future<Map<String, String>> get headers => _call.headers;
+
+  @override
+  Future<Map<String, String>> get trailers => _call.trailers;
+
+  @override
+  Future<void> cancel() => _call.cancel();
+}
diff --git a/grpc/lib/src/client/connection.dart b/grpc/lib/src/client/connection.dart
new file mode 100644
index 0000000..371b3c0
--- /dev/null
+++ b/grpc/lib/src/client/connection.dart
@@ -0,0 +1,286 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:http2/transport.dart';
+import 'package:meta/meta.dart';
+
+import '../shared/timeout.dart';
+
+import 'call.dart';
+import 'options.dart';
+
+enum ConnectionState {
+  /// Actively trying to connect.
+  connecting,
+
+  /// Connection successfully established.
+  ready,
+
+  /// Some transient failure occurred, waiting to re-connect.
+  transientFailure,
+
+  /// Not currently connected, and no pending RPCs.
+  idle,
+
+  /// Shutting down, no further RPCs allowed.
+  shutdown
+}
+
+/// A connection to a single RPC endpoint.
+///
+/// RPCs made on a connection are always sent to the same endpoint.
+class ClientConnection {
+  static final _methodPost = new Header.ascii(':method', 'POST');
+  static final _schemeHttp = new Header.ascii(':scheme', 'http');
+  static final _schemeHttps = new Header.ascii(':scheme', 'https');
+  static final _contentTypeGrpc =
+      new Header.ascii('content-type', 'application/grpc');
+  static final _teTrailers = new Header.ascii('te', 'trailers');
+  static final _grpcAcceptEncoding =
+      new Header.ascii('grpc-accept-encoding', 'identity');
+
+  final String host;
+  final int port;
+  final ChannelOptions options;
+
+  ConnectionState _state = ConnectionState.idle;
+  void Function(ClientConnection connection) onStateChanged;
+  final _pendingCalls = <ClientCall>[];
+
+  ClientTransportConnection _transport;
+
+  /// Used for idle and reconnect timeout, depending on [_state].
+  Timer _timer;
+  Duration _currentReconnectDelay;
+
+  ClientConnection(this.host, this.port, this.options);
+
+  ConnectionState get state => _state;
+
+  static List<Header> createCallHeaders(bool useTls, String authority,
+      String path, Duration timeout, Map<String, String> metadata,
+      {String userAgent}) {
+    final headers = [
+      _methodPost,
+      useTls ? _schemeHttps : _schemeHttp,
+      new Header(ascii.encode(':path'), utf8.encode(path)),
+      new Header(ascii.encode(':authority'), utf8.encode(authority)),
+    ];
+    if (timeout != null) {
+      headers.add(new Header.ascii('grpc-timeout', toTimeoutString(timeout)));
+    }
+    headers.addAll([
+      _contentTypeGrpc,
+      _teTrailers,
+      _grpcAcceptEncoding,
+      new Header.ascii('user-agent', userAgent ?? defaultUserAgent),
+    ]);
+    metadata?.forEach((key, value) {
+      headers.add(new Header(ascii.encode(key), utf8.encode(value)));
+    });
+    return headers;
+  }
+
+  String get authority => options.credentials.authority ?? host;
+
+  @visibleForTesting
+  Future<ClientTransportConnection> connectTransport() async {
+    final securityContext = options.credentials.securityContext;
+
+    var socket = await Socket.connect(host, port);
+    if (_state == ConnectionState.shutdown) {
+      socket.destroy();
+      throw 'Shutting down';
+    }
+    if (securityContext != null) {
+      socket = await SecureSocket.secure(socket,
+          host: authority,
+          context: securityContext,
+          onBadCertificate: _validateBadCertificate);
+      if (_state == ConnectionState.shutdown) {
+        socket.destroy();
+        throw 'Shutting down';
+      }
+    }
+    socket.done.then(_handleSocketClosed);
+    return new ClientTransportConnection.viaSocket(socket);
+  }
+
+  bool _validateBadCertificate(X509Certificate certificate) {
+    final validator = options.credentials.onBadCertificate;
+    if (validator == null) return false;
+    return validator(certificate, authority);
+  }
+
+  void _connect() {
+    if (_state != ConnectionState.idle &&
+        _state != ConnectionState.transientFailure) {
+      return;
+    }
+    _setState(ConnectionState.connecting);
+    connectTransport().then((transport) {
+      _currentReconnectDelay = null;
+      _transport = transport;
+      _transport.onActiveStateChanged = _handleActiveStateChanged;
+      _setState(ConnectionState.ready);
+      _pendingCalls.forEach(_startCall);
+      _pendingCalls.clear();
+    }).catchError(_handleConnectionFailure);
+  }
+
+  void dispatchCall(ClientCall call) {
+    switch (_state) {
+      case ConnectionState.ready:
+        _startCall(call);
+        break;
+      case ConnectionState.shutdown:
+        _shutdownCall(call);
+        break;
+      default:
+        _pendingCalls.add(call);
+        if (_state == ConnectionState.idle) {
+          _connect();
+        }
+    }
+  }
+
+  ClientTransportStream makeRequest(
+      String path, Duration timeout, Map<String, String> metadata) {
+    final headers = createCallHeaders(
+        options.credentials.isSecure, authority, path, timeout, metadata,
+        userAgent: options.userAgent);
+    return _transport.makeRequest(headers);
+  }
+
+  void _startCall(ClientCall call) {
+    if (call.isCancelled) return;
+    call.onConnectionReady(this);
+  }
+
+  void _failCall(ClientCall call, dynamic error) {
+    if (call.isCancelled) return;
+    call.onConnectionError(error);
+  }
+
+  void _shutdownCall(ClientCall call) {
+    _failCall(call, 'Connection shutting down.');
+  }
+
+  /// Shuts down this connection.
+  ///
+  /// No further calls may be made on this connection, but existing calls
+  /// are allowed to finish.
+  Future<void> shutdown() async {
+    if (_state == ConnectionState.shutdown) return null;
+    _setShutdownState();
+    await _transport?.finish();
+  }
+
+  /// Terminates this connection.
+  ///
+  /// All open calls are terminated immediately, and no further calls may be
+  /// made on this connection.
+  Future<void> terminate() async {
+    _setShutdownState();
+    await _transport?.terminate();
+  }
+
+  void _setShutdownState() {
+    _setState(ConnectionState.shutdown);
+    _cancelTimer();
+    _pendingCalls.forEach(_shutdownCall);
+    _pendingCalls.clear();
+  }
+
+  void _setState(ConnectionState state) {
+    _state = state;
+    if (onStateChanged != null) {
+      onStateChanged(this);
+    }
+  }
+
+  void _handleIdleTimeout() {
+    if (_timer == null || _state != ConnectionState.ready) return;
+    _cancelTimer();
+    _transport?.finish()?.catchError((_) => {}); // TODO(jakobr): Log error.
+    _transport = null;
+    _setState(ConnectionState.idle);
+  }
+
+  void _cancelTimer() {
+    _timer?.cancel();
+    _timer = null;
+  }
+
+  void _handleActiveStateChanged(bool isActive) {
+    if (isActive) {
+      _cancelTimer();
+    } else {
+      if (options.idleTimeout != null) {
+        _timer ??= new Timer(options.idleTimeout, _handleIdleTimeout);
+      }
+    }
+  }
+
+  bool _hasPendingCalls() {
+    // Get rid of pending calls that have timed out.
+    _pendingCalls.removeWhere((call) => call.isCancelled);
+    return _pendingCalls.isNotEmpty;
+  }
+
+  void _handleConnectionFailure(error) {
+    _transport = null;
+    if (_state == ConnectionState.shutdown || _state == ConnectionState.idle) {
+      return;
+    }
+    // TODO(jakobr): Log error.
+    _cancelTimer();
+    _pendingCalls.forEach((call) => _failCall(call, error));
+    _pendingCalls.clear();
+    _setState(ConnectionState.idle);
+  }
+
+  void _handleReconnect() {
+    if (_timer == null || _state != ConnectionState.transientFailure) return;
+    _cancelTimer();
+    _connect();
+  }
+
+  void _handleSocketClosed(_) {
+    _cancelTimer();
+    _transport = null;
+
+    if (_state == ConnectionState.idle && _state == ConnectionState.shutdown) {
+      // All good.
+      return;
+    }
+
+    // We were not planning to close the socket.
+    if (!_hasPendingCalls()) {
+      // No pending calls. Just hop to idle, and wait for a new RPC.
+      _setState(ConnectionState.idle);
+      return;
+    }
+
+    // We have pending RPCs. Reconnect after backoff delay.
+    _setState(ConnectionState.transientFailure);
+    _currentReconnectDelay = options.backoffStrategy(_currentReconnectDelay);
+    _timer = new Timer(_currentReconnectDelay, _handleReconnect);
+  }
+}
diff --git a/grpc/lib/src/client/method.dart b/grpc/lib/src/client/method.dart
new file mode 100644
index 0000000..dca28eb
--- /dev/null
+++ b/grpc/lib/src/client/method.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Description of a gRPC method.
+class ClientMethod<Q, R> {
+  final String path;
+  final List<int> Function(Q value) requestSerializer;
+  final R Function(List<int> value) responseDeserializer;
+
+  ClientMethod(this.path, this.requestSerializer, this.responseDeserializer);
+}
diff --git a/grpc/lib/src/client/options.dart b/grpc/lib/src/client/options.dart
new file mode 100644
index 0000000..90e1d94
--- /dev/null
+++ b/grpc/lib/src/client/options.dart
@@ -0,0 +1,159 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'dart:math';
+
+import '../shared/security.dart';
+
+const defaultIdleTimeout = const Duration(minutes: 5);
+const defaultUserAgent = 'dart-grpc/1.0.3';
+
+typedef Duration BackoffStrategy(Duration lastBackoff);
+
+// Backoff algorithm from https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
+const _minConnectTimeout = const Duration(seconds: 20);
+const _initialBackoff = const Duration(seconds: 1);
+const _maxBackoff = const Duration(seconds: 120);
+const _multiplier = 1.6;
+const _jitter = 0.2;
+final _random = new Random();
+
+Duration defaultBackoffStrategy(Duration lastBackoff) {
+  if (lastBackoff == null) return _initialBackoff;
+  final jitter = _random.nextDouble() * 2 * _jitter - _jitter;
+  final nextBackoff = lastBackoff * (_multiplier + jitter);
+  return nextBackoff < _maxBackoff ? nextBackoff : _maxBackoff;
+}
+
+/// Handler for checking certificates that fail validation. If this handler
+/// returns `true`, the bad certificate is allowed, and the TLS handshake can
+/// continue. If the handler returns `false`, the TLS handshake fails, and the
+/// connection is aborted.
+typedef bool BadCertificateHandler(X509Certificate certificate, String host);
+
+/// Bad certificate handler that disables all certificate checks.
+/// DO NOT USE IN PRODUCTION!
+/// Can be used during development and testing to accept self-signed
+/// certificates, etc.
+bool allowBadCertificates(X509Certificate certificate, String host) => true;
+
+/// Options controlling TLS security settings on a [ClientChannel].
+class ChannelCredentials {
+  final bool isSecure;
+  final List<int> _certificateBytes;
+  final String _certificatePassword;
+  final String authority;
+  final BadCertificateHandler onBadCertificate;
+
+  const ChannelCredentials._(this.isSecure, this._certificateBytes,
+      this._certificatePassword, this.authority, this.onBadCertificate);
+
+  /// Disable TLS. RPCs are sent in clear text.
+  const ChannelCredentials.insecure({String authority})
+      : this._(false, null, null, authority, null);
+
+  /// Enable TLS and optionally specify the [certificates] to trust. If
+  /// [certificates] is not provided, the default trust store is used.
+  const ChannelCredentials.secure(
+      {List<int> certificates,
+      String password,
+      String authority,
+      BadCertificateHandler onBadCertificate})
+      : this._(true, certificates, password, authority, onBadCertificate);
+
+  SecurityContext get securityContext {
+    if (!isSecure) return null;
+    if (_certificateBytes != null) {
+      return createSecurityContext(false)
+        ..setTrustedCertificatesBytes(_certificateBytes,
+            password: _certificatePassword);
+    }
+    final context = new SecurityContext(withTrustedRoots: true);
+    if (SecurityContext.alpnSupported) {
+      context.setAlpnProtocols(supportedAlpnProtocols, false);
+    }
+    return context;
+  }
+}
+
+/// Options controlling how connections are made on a [ClientChannel].
+class ChannelOptions {
+  final ChannelCredentials credentials;
+  final Duration idleTimeout;
+  final BackoffStrategy backoffStrategy;
+  final String userAgent;
+
+  const ChannelOptions({
+    ChannelCredentials credentials,
+    Duration idleTimeout,
+    String userAgent,
+    BackoffStrategy backoffStrategy,
+  })  : this.credentials = credentials ?? const ChannelCredentials.secure(),
+        this.idleTimeout = idleTimeout ?? defaultIdleTimeout,
+        this.userAgent = userAgent ?? defaultUserAgent,
+        this.backoffStrategy = backoffStrategy ?? defaultBackoffStrategy;
+}
+
+/// Provides per-RPC metadata.
+///
+/// Metadata providers will be invoked for every RPC, and can add their own
+/// metadata to the RPC. If the function returns a [Future], the RPC will await
+/// completion of the returned [Future] before transmitting the request.
+///
+/// The metadata provider is given the current [metadata] map (possibly modified
+/// by previous metadata providers) and the [uri] that is being called, and is
+/// expected to modify the map before returning or before completing the
+/// returned [Future].
+typedef FutureOr<void> MetadataProvider(
+    Map<String, String> metadata, String uri);
+
+/// Runtime options for an RPC.
+class CallOptions {
+  final Map<String, String> metadata;
+  final Duration timeout;
+  final List<MetadataProvider> metadataProviders;
+
+  CallOptions._(this.metadata, this.timeout, this.metadataProviders);
+
+  /// Creates a [CallOptions] object.
+  ///
+  /// [CallOptions] can specify static [metadata], set the [timeout], and
+  /// configure per-RPC metadata [providers]. The metadata [providers] are
+  /// invoked in order for every RPC, and can modify the outgoing metadata
+  /// (including metadata provided by previous providers).
+  factory CallOptions(
+      {Map<String, String> metadata,
+      Duration timeout,
+      List<MetadataProvider> providers}) {
+    return new CallOptions._(new Map.unmodifiable(metadata ?? {}), timeout,
+        new List.unmodifiable(providers ?? []));
+  }
+
+  factory CallOptions.from(Iterable<CallOptions> options) =>
+      options.fold(new CallOptions(), (p, o) => p.mergedWith(o));
+
+  CallOptions mergedWith(CallOptions other) {
+    if (other == null) return this;
+    final mergedMetadata = new Map.from(metadata)..addAll(other.metadata);
+    final mergedTimeout = other.timeout ?? timeout;
+    final mergedProviders = new List.from(metadataProviders)
+      ..addAll(other.metadataProviders);
+    return new CallOptions._(new Map.unmodifiable(mergedMetadata),
+        mergedTimeout, new List.unmodifiable(mergedProviders));
+  }
+}
diff --git a/grpc/lib/src/server/call.dart b/grpc/lib/src/server/call.dart
new file mode 100644
index 0000000..656dc48
--- /dev/null
+++ b/grpc/lib/src/server/call.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Server-side context for a gRPC call.
+///
+/// Gives the method handler access to custom metadata from the client, and
+/// ability to set custom metadata on the header/trailer sent to the client.
+abstract class ServiceCall {
+  /// Custom metadata from the client.
+  Map<String, String> get clientMetadata;
+
+  /// Custom metadata to be sent to the client. Will be [null] once the headers
+  /// have been sent, either when [sendHeaders] is called, or when the first
+  /// response message is sent.
+  Map<String, String> get headers;
+
+  /// Custom metadata to be sent to the client after all response messages.
+  Map<String, String> get trailers;
+
+  /// Deadline for this call. If the call is still active after this time, then
+  /// the client or server may cancel it.
+  DateTime get deadline;
+
+  /// Returns [true] if the [deadline] has been exceeded.
+  bool get isTimedOut;
+
+  /// Returns [true] if the client has canceled this call.
+  bool get isCanceled;
+
+  /// Send response headers. This is done automatically before sending the first
+  /// response message, but can be done manually before the first response is
+  /// ready, if necessary.
+  void sendHeaders();
+
+  /// Send response trailers. A trailer indicating success ([status] == 0) will
+  /// be sent automatically when all responses are sent. This method can be used
+  /// to send a different status code, if needed.
+  ///
+  /// The call will be closed after calling this method, and no further
+  /// responses can be sent.
+  void sendTrailers({int status, String message});
+}
diff --git a/grpc/lib/src/server/handler.dart b/grpc/lib/src/server/handler.dart
new file mode 100644
index 0000000..74754e4
--- /dev/null
+++ b/grpc/lib/src/server/handler.dart
@@ -0,0 +1,364 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:http2/transport.dart';
+
+import '../shared/status.dart';
+import '../shared/streams.dart';
+import '../shared/timeout.dart';
+
+import 'call.dart';
+import 'interceptor.dart';
+import 'service.dart';
+
+/// Handles an incoming gRPC call.
+class ServerHandler_ extends ServiceCall {
+  final ServerTransportStream _stream;
+  final Service Function(String service) _serviceLookup;
+  final List<Interceptor> _interceptors;
+
+  StreamSubscription<GrpcMessage> _incomingSubscription;
+
+  Service _service;
+  ServiceMethod _descriptor;
+
+  Map<String, String> _clientMetadata;
+
+  StreamController _requests;
+  bool _hasReceivedRequest = false;
+
+  Stream _responses;
+  StreamSubscription _responseSubscription;
+  bool _headersSent = false;
+
+  Map<String, String> _customHeaders = {};
+  Map<String, String> _customTrailers = {};
+
+  DateTime _deadline;
+  bool _isCanceled = false;
+  bool _isTimedOut = false;
+  Timer _timeoutTimer;
+
+  ServerHandler_(this._serviceLookup, this._stream, this._interceptors);
+
+  DateTime get deadline => _deadline;
+
+  bool get isCanceled => _isCanceled;
+
+  bool get isTimedOut => _isTimedOut;
+
+  Map<String, String> get clientMetadata => _clientMetadata;
+
+  Map<String, String> get headers => _customHeaders;
+
+  Map<String, String> get trailers => _customTrailers;
+
+  void handle() {
+    _stream.onTerminated = (_) => cancel();
+
+    _incomingSubscription = _stream.incomingMessages
+        .transform(new GrpcHttpDecoder())
+        .transform(grpcDecompressor())
+        .listen(_onDataIdle,
+            onError: _onError, onDone: _onDoneError, cancelOnError: true);
+  }
+
+  /// Cancel response subscription, if active. If the stream exits with an
+  /// error, just ignore it. The client is long gone, so it doesn't care.
+  /// We need the catchError() handler here, since otherwise the error would
+  /// be an unhandled exception.
+  void _cancelResponseSubscription() {
+    _responseSubscription?.cancel()?.catchError((_) {});
+  }
+
+  // -- Idle state, incoming data --
+
+  void _onDataIdle(GrpcMessage message) async {
+    if (message is! GrpcMetadata) {
+      _sendError(new GrpcError.unimplemented('Expected header frame'));
+      _sinkIncoming();
+      return;
+    }
+    _incomingSubscription.pause();
+
+    final headerMessage = message
+        as GrpcMetadata; // TODO(jakobr): Cast should not be necessary here.
+    _clientMetadata = headerMessage.metadata;
+    final path = _clientMetadata[':path'];
+    final pathSegments = path.split('/');
+    if (pathSegments.length < 3) {
+      _sendError(new GrpcError.unimplemented('Invalid path'));
+      _sinkIncoming();
+      return;
+    }
+    final serviceName = pathSegments[1];
+    final methodName = pathSegments[2];
+
+    _service = _serviceLookup(serviceName);
+    _descriptor = _service?.$lookupMethod(methodName);
+    if (_descriptor == null) {
+      _sendError(new GrpcError.unimplemented('Path $path not found'));
+      _sinkIncoming();
+      return;
+    }
+
+    final error = await _applyInterceptors();
+    if (error != null) {
+      _sendError(error);
+      _sinkIncoming();
+      return;
+    }
+
+    _startStreamingRequest();
+  }
+
+  Future<GrpcError> _applyInterceptors() async {
+    try {
+      for (final interceptor in _interceptors) {
+        final error = await interceptor(this, this._descriptor);
+        if (error != null) {
+          return error;
+        }
+      }
+    } catch (error) {
+      final grpcError = new GrpcError.internal(error.toString());
+      return grpcError;
+    }
+    return null;
+  }
+
+  void _startStreamingRequest() {
+    _requests = _descriptor.createRequestStream(_incomingSubscription);
+    _incomingSubscription.onData(_onDataActive);
+
+    _service.$onMetadata(this);
+    _responses = _descriptor.handle(this, _requests.stream);
+
+    _responseSubscription = _responses.listen(_onResponse,
+        onError: _onResponseError,
+        onDone: _onResponseDone,
+        cancelOnError: true);
+    _incomingSubscription.onData(_onDataActive);
+    _incomingSubscription.onDone(_onDoneExpected);
+
+    final timeout = fromTimeoutString(_clientMetadata['grpc-timeout']);
+    if (timeout != null) {
+      _deadline = new DateTime.now().add(timeout);
+      _timeoutTimer = new Timer(timeout, _onTimedOut);
+    }
+  }
+
+  void _onTimedOut() {
+    if (_isCanceled) return;
+    _isTimedOut = true;
+    _isCanceled = true;
+    final error = new GrpcError.deadlineExceeded('Deadline exceeded');
+    _sendError(error);
+    if (!_requests.isClosed) {
+      _requests
+        ..addError(error)
+        ..close();
+    }
+  }
+
+  // -- Active state, incoming data --
+
+  void _onDataActive(GrpcMessage message) {
+    if (message is! GrpcData) {
+      final error = new GrpcError.unimplemented('Expected request');
+      _sendError(error);
+      _requests
+        ..addError(error)
+        ..close();
+      return;
+    }
+
+    if (_hasReceivedRequest && !_descriptor.streamingRequest) {
+      final error = new GrpcError.unimplemented('Too many requests');
+      _sendError(error);
+      _requests
+        ..addError(error)
+        ..close();
+      return;
+    }
+
+    // TODO(jakobr): Cast should not be necessary here.
+    final data = message as GrpcData;
+    var request;
+    try {
+      request = _descriptor.deserialize(data.data);
+    } catch (error) {
+      final grpcError =
+          new GrpcError.internal('Error deserializing request: $error');
+      _sendError(grpcError);
+      _requests
+        ..addError(grpcError)
+        ..close();
+      return;
+    }
+    _requests.add(request);
+    _hasReceivedRequest = true;
+  }
+
+  // -- Active state, outgoing response data --
+
+  void _onResponse(response) {
+    try {
+      final bytes = _descriptor.serialize(response);
+      if (!_headersSent) {
+        sendHeaders();
+      }
+      _stream.sendData(GrpcHttpEncoder.frame(bytes));
+    } catch (error) {
+      final grpcError =
+          new GrpcError.internal('Error sending response: $error');
+      if (!_requests.isClosed) {
+        // If we can, alert the handler that things are going wrong.
+        _requests
+          ..addError(grpcError)
+          ..close();
+      }
+      _sendError(grpcError);
+      _cancelResponseSubscription();
+    }
+  }
+
+  void _onResponseDone() {
+    sendTrailers();
+  }
+
+  void _onResponseError(error) {
+    if (error is GrpcError) {
+      _sendError(error);
+    } else {
+      _sendError(new GrpcError.unknown(error.toString()));
+    }
+  }
+
+  void sendHeaders() {
+    if (_headersSent) throw new GrpcError.internal('Headers already sent');
+
+    _customHeaders..remove(':status')..remove('content-type');
+
+    // TODO(jakobr): Should come from package:http2?
+    final outgoingHeadersMap = <String, String>{
+      ':status': '200',
+      'content-type': 'application/grpc'
+    };
+
+    outgoingHeadersMap.addAll(_customHeaders);
+    _customHeaders = null;
+
+    final outgoingHeaders = <Header>[];
+    outgoingHeadersMap.forEach((key, value) =>
+        outgoingHeaders.add(new Header(ascii.encode(key), utf8.encode(value))));
+    _stream.sendHeaders(outgoingHeaders);
+    _headersSent = true;
+  }
+
+  void sendTrailers({int status = 0, String message}) {
+    _timeoutTimer?.cancel();
+
+    final outgoingTrailersMap = <String, String>{};
+    if (!_headersSent) {
+      // TODO(jakobr): Should come from package:http2?
+      outgoingTrailersMap[':status'] = '200';
+      outgoingTrailersMap['content-type'] = 'application/grpc';
+
+      _customHeaders..remove(':status')..remove('content-type');
+      outgoingTrailersMap.addAll(_customHeaders);
+      _customHeaders = null;
+    }
+    _customTrailers..remove(':status')..remove('content-type');
+    outgoingTrailersMap.addAll(_customTrailers);
+    _customTrailers = null;
+    outgoingTrailersMap['grpc-status'] = status.toString();
+    if (message != null) {
+      outgoingTrailersMap['grpc-message'] = message;
+    }
+
+    final outgoingTrailers = <Header>[];
+    outgoingTrailersMap.forEach((key, value) => outgoingTrailers
+        .add(new Header(ascii.encode(key), utf8.encode(value))));
+    _stream.sendHeaders(outgoingTrailers, endStream: true);
+    // We're done!
+    _cancelResponseSubscription();
+    _sinkIncoming();
+  }
+
+  // -- All states, incoming error / stream closed --
+
+  void _onError(error) {
+    // Exception from the incoming stream. Most likely a cancel request from the
+    // client, so we treat it as such.
+    _timeoutTimer?.cancel();
+    _isCanceled = true;
+    if (_requests != null && !_requests.isClosed) {
+      _requests.addError(new GrpcError.cancelled('Cancelled'));
+    }
+    _cancelResponseSubscription();
+    _incomingSubscription.cancel();
+    _stream.terminate();
+  }
+
+  void _onDoneError() {
+    _sendError(new GrpcError.unavailable('Request stream closed unexpectedly'));
+    _onDone();
+  }
+
+  void _onDoneExpected() {
+    if (!(_hasReceivedRequest || _descriptor.streamingRequest)) {
+      final error = new GrpcError.unimplemented('No request received');
+      _sendError(error);
+      _requests.addError(error);
+    }
+    _onDone();
+  }
+
+  void _onDone() {
+    _requests?.close();
+    _incomingSubscription.cancel();
+  }
+
+  /// Sink incoming requests. This is used when an error has already been
+  /// reported, but we still need to consume the request stream from the client.
+  void _sinkIncoming() {
+    _incomingSubscription
+      ..onData((_) {})
+      ..onDone(_onDone);
+  }
+
+  void _sendError(GrpcError error) {
+    sendTrailers(status: error.code, message: error.message);
+  }
+
+  void cancel() {
+    _isCanceled = true;
+    _timeoutTimer?.cancel();
+    _cancelResponseSubscription();
+  }
+}
+
+@Deprecated(
+    'This is an internal class, and will not be part of the public interface in next major version.')
+// TODO(sigurdm): Remove this class from grpc.dart exports.
+class ServerHandler extends ServerHandler_ {
+  ServerHandler(Service Function(String service) serviceLookup, stream,
+      [List<Interceptor> interceptors = const <Interceptor>[]])
+      : super(serviceLookup, stream, interceptors);
+}
diff --git a/grpc/lib/src/server/interceptor.dart b/grpc/lib/src/server/interceptor.dart
new file mode 100644
index 0000000..65426a6
--- /dev/null
+++ b/grpc/lib/src/server/interceptor.dart
@@ -0,0 +1,14 @@
+import 'dart:async';
+
+import '../shared/status.dart';
+import 'call.dart';
+import 'service.dart';
+
+/// A gRPC Interceptor.
+///
+/// An interceptor is called before the corresponding [ServiceMethod] invocation.
+/// If the interceptor returns a [GrpcError], the error will be returned as a response and [ServiceMethod] wouldn't be called.
+/// If the interceptor throws [Exception], [GrpcError.internal] with exception.toString() will be returned.
+/// If the interceptor returns null, the corresponding [ServiceMethod] of [Service] will be called.
+typedef Interceptor = FutureOr<GrpcError> Function(
+    ServiceCall call, ServiceMethod method);
diff --git a/grpc/lib/src/server/server.dart b/grpc/lib/src/server/server.dart
new file mode 100644
index 0000000..d549f63
--- /dev/null
+++ b/grpc/lib/src/server/server.dart
@@ -0,0 +1,148 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:http2/transport.dart';
+import 'package:meta/meta.dart';
+
+import '../shared/security.dart';
+
+import 'handler.dart';
+import 'interceptor.dart';
+import 'service.dart';
+
+class ServerTlsCredentials {
+  final List<int> certificate;
+  final String certificatePassword;
+  final List<int> privateKey;
+  final String privateKeyPassword;
+
+  /// TLS credentials for a [Server].
+  ///
+  /// If the [certificate] or [privateKey] is encrypted, the password must also
+  /// be provided.
+  ServerTlsCredentials(
+      {this.certificate,
+      this.certificatePassword,
+      this.privateKey,
+      this.privateKeyPassword});
+
+  SecurityContext get securityContext {
+    final context = createSecurityContext(true);
+    if (privateKey != null) {
+      context.usePrivateKeyBytes(privateKey, password: privateKeyPassword);
+    }
+    if (certificate != null) {
+      context.useCertificateChainBytes(certificate,
+          password: certificatePassword);
+    }
+    return context;
+  }
+}
+
+/// A gRPC server.
+///
+/// Listens for incoming RPCs, dispatching them to the right [Service] handler.
+class Server {
+  final Map<String, Service> _services = {};
+  final List<Interceptor> _interceptors;
+
+  ServerSocket _insecureServer;
+  SecureServerSocket _secureServer;
+  final _connections = <ServerTransportConnection>[];
+
+  /// Create a server for the given [services].
+  Server(List<Service> services,
+      [List<Interceptor> interceptors = const <Interceptor>[]])
+      : _interceptors = interceptors {
+    for (final service in services) {
+      _services[service.$name] = service;
+    }
+  }
+
+  /// The port that the server is listening on, or `null` if the server is not
+  /// active.
+  int get port {
+    if (_secureServer != null) return _secureServer.port;
+    if (_insecureServer != null) return _insecureServer.port;
+    return null;
+  }
+
+  Service lookupService(String service) => _services[service];
+
+  Future<void> serve(
+      {dynamic address, int port, ServerTlsCredentials security}) async {
+    // TODO(dart-lang/grpc-dart#9): Handle HTTP/1.1 upgrade to h2c, if allowed.
+    Stream<Socket> server;
+    if (security != null) {
+      _secureServer = await SecureServerSocket.bind(
+          address ?? InternetAddress.anyIPv4,
+          port ?? 443,
+          security.securityContext);
+      server = _secureServer;
+    } else {
+      _insecureServer = await ServerSocket.bind(
+          address ?? InternetAddress.anyIPv4, port ?? 80);
+      server = _insecureServer;
+    }
+    server.listen((socket) {
+      final connection = new ServerTransportConnection.viaSocket(socket);
+      _connections.add(connection);
+      ServerHandler_ handler;
+      // TODO(jakobr): Set active state handlers, close connection after idle
+      // timeout.
+      connection.incomingStreams.listen((stream) {
+        handler = serveStream_(stream);
+      }, onError: (error) {
+        print('Connection error: $error');
+      }, onDone: () {
+        // TODO(sigurdm): This is not correct behavior in the presence of
+        // half-closed tcp streams.
+        // Half-closed  streams seems to not be fully supported by package:http2.
+        // https://github.com/dart-lang/http2/issues/42
+        handler?.cancel();
+        _connections.remove(connection);
+      });
+    }, onError: (error) {
+      print('Socket error: $error');
+    });
+  }
+
+  @visibleForTesting
+  ServerHandler_ serveStream_(ServerTransportStream stream) {
+    return new ServerHandler_(lookupService, stream, _interceptors)..handle();
+  }
+
+  @Deprecated(
+      'This is internal functionality, and will be removed in next major version.')
+  void serveStream(ServerTransportStream stream) {
+    serveStream_(stream);
+  }
+
+  Future<void> shutdown() async {
+    final done = _connections.map((connection) => connection.finish()).toList();
+    if (_insecureServer != null) {
+      done.add(_insecureServer.close());
+    }
+    if (_secureServer != null) {
+      done.add(_secureServer.close());
+    }
+    await Future.wait(done);
+    _insecureServer = null;
+    _secureServer = null;
+  }
+}
diff --git a/grpc/lib/src/server/service.dart b/grpc/lib/src/server/service.dart
new file mode 100644
index 0000000..26d869c
--- /dev/null
+++ b/grpc/lib/src/server/service.dart
@@ -0,0 +1,109 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+
+import '../shared/status.dart';
+import 'call.dart';
+
+/// Definition of a gRPC service method.
+class ServiceMethod<Q, R> {
+  final String name;
+
+  final bool streamingRequest;
+  final bool streamingResponse;
+
+  final Q Function(List<int> request) requestDeserializer;
+  final List<int> Function(R response) responseSerializer;
+
+  final Function handler;
+
+  ServiceMethod(
+      this.name,
+      this.handler,
+      this.streamingRequest,
+      this.streamingResponse,
+      this.requestDeserializer,
+      this.responseSerializer);
+
+  StreamController<Q> createRequestStream(StreamSubscription incoming) =>
+      new StreamController<Q>(
+          onListen: incoming.resume,
+          onPause: incoming.pause,
+          onResume: incoming.resume);
+
+  Q deserialize(List<int> data) => requestDeserializer(data);
+
+  List<int> serialize(dynamic response) => responseSerializer(response as R);
+
+  Stream<R> handle(ServiceCall call, Stream<Q> requests) {
+    if (streamingResponse) {
+      if (streamingRequest) {
+        return handler(call, requests);
+      } else {
+        return handler(call, _toSingleFuture(requests));
+      }
+    } else {
+      Future<R> response;
+      if (streamingRequest) {
+        response = handler(call, requests);
+      } else {
+        response = handler(call, _toSingleFuture(requests));
+      }
+      return response.asStream();
+    }
+  }
+
+  Future<Q> _toSingleFuture(Stream<Q> stream) {
+    Q _ensureOnlyOneRequest(Q previous, Q element) {
+      if (previous != null) {
+        throw new GrpcError.unimplemented('More than one request received');
+      }
+      return element;
+    }
+
+    Q _ensureOneRequest(Q value) {
+      if (value == null)
+        throw new GrpcError.unimplemented('No requests received');
+      return value;
+    }
+
+    final future =
+        stream.fold(null, _ensureOnlyOneRequest).then(_ensureOneRequest);
+    // Make sure errors on the future aren't unhandled, but return the original
+    // future so the request handler can also get the error.
+    future.catchError((_) {});
+    return future;
+  }
+}
+
+/// Definition of a gRPC service.
+abstract class Service {
+  final Map<String, ServiceMethod> _$methods = {};
+
+  String get $name;
+
+  void $addMethod(ServiceMethod method) {
+    _$methods[method.name] = method;
+  }
+
+  /// Client metadata handler.
+  ///
+  /// Services can override this method to provide common handling of incoming
+  /// metadata from the client.
+  void $onMetadata(ServiceCall context) {}
+
+  ServiceMethod $lookupMethod(String name) => _$methods[name];
+}
diff --git a/grpc/lib/src/shared/security.dart b/grpc/lib/src/shared/security.dart
new file mode 100644
index 0000000..2e09f37
--- /dev/null
+++ b/grpc/lib/src/shared/security.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:io';
+
+const supportedAlpnProtocols = const ['grpc-exp', 'h2'];
+
+// TODO: Simplify once we have a stable Dart 1.25 release (update pubspec to
+// require SDK >=1.25.0, and remove check for alpnSupported).
+SecurityContext createSecurityContext(bool isServer) =>
+    SecurityContext.alpnSupported
+        ? (new SecurityContext()
+          ..setAlpnProtocols(supportedAlpnProtocols, isServer))
+        : new SecurityContext();
diff --git a/grpc/lib/src/shared/status.dart b/grpc/lib/src/shared/status.dart
new file mode 100644
index 0000000..4879cd3
--- /dev/null
+++ b/grpc/lib/src/shared/status.dart
@@ -0,0 +1,244 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+class StatusCode {
+  /// The operation completed successfully.
+  static const ok = 0;
+
+  /// The operation was cancelled (typically by the caller).
+  static const cancelled = 1;
+
+  /// Unknown error. An example of where this error may be returned is if a
+  /// Status value received from another address space belongs to an error-space
+  /// that is not known in this address space. Also errors raised by APIs that
+  /// do not return enough error information may be converted to this error.
+  static const unknown = 2;
+
+  /// Client specified an invalid argument. Note that this differs from
+  /// [failedPrecondition]. [invalidArgument] indicates arguments that are
+  /// problematic regardless of the state of the system (e.g., a malformed file
+  /// name).
+  static const invalidArgument = 3;
+
+  /// Deadline expired before operation could complete. For operations that
+  /// change the state of the system, this error may be returned even if the
+  /// operation has completed successfully. For example, a successful response
+  /// from a server could have been delayed long enough for the deadline to
+  /// expire.
+  static const deadlineExceeded = 4;
+
+  /// Some requested entity (e.g., file or directory) was not found.
+  static const notFound = 5;
+
+  /// Some entity that we attempted to create (e.g., file or directory) already
+  /// exists.
+  static const alreadyExists = 6;
+
+  /// The caller does not have permission to execute the specified operation.
+  /// [permissionDenied] must not be used for rejections caused by exhausting
+  /// some resource (use [resourceExhausted] instead for those errors).
+  /// [permissionDenied] must not be used if the caller cannot be identified
+  /// (use [unauthenticated] instead for those errors).
+  static const permissionDenied = 7;
+
+  /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
+  /// entire file system is out of space.
+  static const resourceExhausted = 8;
+
+  /// Operation was rejected because the system is not in a state required for
+  /// the operation's execution. For example, directory to be deleted may be
+  /// non-empty, an rmdir operation is applied to a non-directory, etc.
+  ///
+  /// A litmus test that may help a service implementor in deciding between
+  /// [failedPrecondition], [aborted], and [unavailable]:
+  /// (a) Use [unavailable] if the client can retry just the failing call.
+  /// (b) Use [aborted] if the client should retry at a higher-level (e.g.,
+  ///     restarting a read-modify-write sequence).
+  /// (c) Use [failedPrecondition] if the client should not retry until the
+  ///     system state has been explicitly fixed.  E.g., if an "rmdir" fails
+  ///     because the directory is non-empty, [failedPrecondition] should be
+  ///     returned since the client should not retry unless they have first
+  ///     fixed up the directory by deleting files from it.
+  static const failedPrecondition = 9;
+
+  /// The operation was aborted, typically due to a concurrency issue like
+  /// sequencer check failures, transaction aborts, etc.
+  ///
+  /// See litmus test above for deciding between [failedPrecondition],
+  /// [aborted], and [unavailable].
+  static const aborted = 10;
+
+  /// Operation was attempted past the valid range. E.g., seeking or reading
+  /// past end of file.
+  ///
+  /// Unlike invalidArgument, this error indicates a problem that may be fixed
+  /// if the system state changes. For example, a 32-bit file system will
+  /// generate invalidArgument if asked to read at an offset that is not in the
+  /// range [0,2^32-1], but it will generate [outOfRange] if asked to read from
+  /// an offset past the current file size.
+  ///
+  /// There is a fair bit of overlap between [failedPrecondition] and
+  /// [outOfRange]. We recommend using [outOfRange] (the more specific error)
+  /// when it applies so that callers who are iterating through a space can
+  /// easily look for an [outOfRange] error to detect when they are done.
+  static const outOfRange = 11;
+
+  /// Operation is not implemented or not supported/enabled in this service.
+  static const unimplemented = 12;
+
+  /// Internal errors. Means some invariants expected by underlying system has
+  /// been broken. If you see one of these errors, something is very broken.
+  static const internal = 13;
+
+  /// The service is currently unavailable.  This is a most likely a transient
+  /// condition and may be corrected by retrying with a backoff.
+  ///
+  /// See litmus test above for deciding between [failedPrecondition],
+  /// [aborted], and [unavailable].
+  static const unavailable = 14;
+
+  /// Unrecoverable data loss or corruption.
+  static const dataLoss = 15;
+
+  /// The request does not have valid authentication credentials for the
+  /// operation.
+  static const unauthenticated = 16;
+}
+
+class GrpcError {
+  final int code;
+  final String message;
+
+  /// Custom error code.
+  GrpcError.custom(this.code, [this.message]);
+
+  /// The operation completed successfully.
+  GrpcError.ok([this.message]) : code = StatusCode.ok;
+
+  /// The operation was cancelled (typically by the caller).
+  GrpcError.cancelled([this.message]) : code = StatusCode.cancelled;
+
+  /// Unknown error. An example of where this error may be returned is if a
+  /// Status value received from another address space belongs to an error-space
+  /// that is not known in this address space. Also errors raised by APIs that
+  /// do not return enough error information may be converted to this error.
+  GrpcError.unknown([this.message]) : code = StatusCode.unknown;
+
+  /// Client specified an invalid argument. Note that this differs from
+  /// [failedPrecondition]. [invalidArgument] indicates arguments that are
+  /// problematic regardless of the state of the system (e.g., a malformed file
+  /// name).
+  GrpcError.invalidArgument([this.message]) : code = StatusCode.invalidArgument;
+
+  /// Deadline expired before operation could complete. For operations that
+  /// change the state of the system, this error may be returned even if the
+  /// operation has completed successfully. For example, a successful response
+  /// from a server could have been delayed long enough for the deadline to
+  /// expire.
+  GrpcError.deadlineExceeded([this.message])
+      : code = StatusCode.deadlineExceeded;
+
+  /// Some requested entity (e.g., file or directory) was not found.
+  GrpcError.notFound([this.message]) : code = StatusCode.notFound;
+
+  /// Some entity that we attempted to create (e.g., file or directory) already
+  /// exists.
+  GrpcError.alreadyExists([this.message]) : code = StatusCode.alreadyExists;
+
+  /// The caller does not have permission to execute the specified operation.
+  /// [permissionDenied] must not be used for rejections caused by exhausting
+  /// some resource (use [resourceExhausted] instead for those errors).
+  /// [permissionDenied] must not be used if the caller cannot be identified
+  /// (use [unauthenticated] instead for those errors).
+  GrpcError.permissionDenied([this.message])
+      : code = StatusCode.permissionDenied;
+
+  /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
+  /// entire file system is out of space.
+  GrpcError.resourceExhausted([this.message])
+      : code = StatusCode.resourceExhausted;
+
+  /// Operation was rejected because the system is not in a state required for
+  /// the operation's execution. For example, directory to be deleted may be
+  /// non-empty, an rmdir operation is applied to a non-directory, etc.
+  ///
+  /// A litmus test that may help a service implementor in deciding between
+  /// [failedPrecondition], [aborted], and [unavailable]:
+  /// (a) Use [unavailable] if the client can retry just the failing call.
+  /// (b) Use [aborted] if the client should retry at a higher-level (e.g.,
+  ///     restarting a read-modify-write sequence).
+  /// (c) Use [failedPrecondition] if the client should not retry until the
+  ///     system state has been explicitly fixed.  E.g., if an "rmdir" fails
+  ///     because the directory is non-empty, [failedPrecondition] should be
+  ///     returned since the client should not retry unless they have first
+  ///     fixed up the directory by deleting files from it.
+  GrpcError.failedPrecondition([this.message])
+      : code = StatusCode.failedPrecondition;
+
+  /// The operation was aborted, typically due to a concurrency issue like
+  /// sequencer check failures, transaction aborts, etc.
+  ///
+  /// See litmus test above for deciding between [failedPrecondition],
+  /// [aborted], and [unavailable].
+  GrpcError.aborted([this.message]) : code = StatusCode.aborted;
+
+  /// Operation was attempted past the valid range. E.g., seeking or reading
+  /// past end of file.
+  ///
+  /// Unlike invalidArgument, this error indicates a problem that may be fixed
+  /// if the system state changes. For example, a 32-bit file system will
+  /// generate invalidArgument if asked to read at an offset that is not in the
+  /// range [0,2^32-1], but it will generate [outOfRange] if asked to read from
+  /// an offset past the current file size.
+  ///
+  /// There is a fair bit of overlap between [failedPrecondition] and
+  /// [outOfRange]. We recommend using [outOfRange] (the more specific error)
+  /// when it applies so that callers who are iterating through a space can
+  /// easily look for an [outOfRange] error to detect when they are done.
+  GrpcError.outOfRange([this.message]) : code = StatusCode.outOfRange;
+
+  /// Operation is not implemented or not supported/enabled in this service.
+  GrpcError.unimplemented([this.message]) : code = StatusCode.unimplemented;
+
+  /// Internal errors. Means some invariants expected by underlying system has
+  /// been broken. If you see one of these errors, something is very broken.
+  GrpcError.internal([this.message]) : code = StatusCode.internal;
+
+  /// The service is currently unavailable.  This is a most likely a transient
+  /// condition and may be corrected by retrying with a backoff.
+  ///
+  /// See litmus test above for deciding between [failedPrecondition],
+  /// [aborted], and [unavailable].
+  GrpcError.unavailable([this.message]) : code = StatusCode.unavailable;
+
+  /// Unrecoverable data loss or corruption.
+  GrpcError.dataLoss([this.message]) : code = StatusCode.dataLoss;
+
+  /// The request does not have valid authentication credentials for the
+  /// operation.
+  GrpcError.unauthenticated([this.message]) : code = StatusCode.unauthenticated;
+
+  @override
+  bool operator ==(other) {
+    if (other is! GrpcError) return false;
+    return code == other.code && message == other.message;
+  }
+
+  @override
+  int get hashCode => code.hashCode ^ (message?.hashCode ?? 17);
+
+  @override
+  String toString() => 'gRPC Error ($code, $message)';
+}
diff --git a/grpc/lib/src/shared/streams.dart b/grpc/lib/src/shared/streams.dart
new file mode 100644
index 0000000..7e3148b
--- /dev/null
+++ b/grpc/lib/src/shared/streams.dart
@@ -0,0 +1,204 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:math';
+import 'dart:typed_data';
+
+import 'package:http2/transport.dart';
+
+import 'status.dart';
+
+abstract class GrpcMessage {}
+
+class GrpcMetadata extends GrpcMessage {
+  final Map<String, String> metadata;
+  GrpcMetadata(this.metadata);
+
+  @override
+  String toString() => 'gRPC Metadata ($metadata)';
+}
+
+class GrpcData extends GrpcMessage {
+  final List<int> data;
+  final bool isCompressed;
+  GrpcData(this.data, {this.isCompressed});
+
+  @override
+  String toString() => 'gRPC Data (${data.length} bytes)';
+}
+
+StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor() =>
+    new StreamTransformer<GrpcMessage, GrpcMessage>.fromHandlers(
+        handleData: (GrpcMessage value, EventSink<GrpcMessage> sink) {
+      if (value is GrpcData) {
+        if (value.isCompressed) {
+          // TODO(dart-lang/grpc-dart#6): Actually handle decompression.
+          sink.add(new GrpcData(value.data, isCompressed: false));
+          return;
+        }
+      }
+      sink.add(value);
+    });
+
+class GrpcHttpEncoder extends Converter<GrpcMessage, StreamMessage> {
+  @override
+  StreamMessage convert(GrpcMessage input) {
+    if (input is GrpcMetadata) {
+      final headers = <Header>[];
+      input.metadata.forEach((key, value) {
+        headers.add(new Header(ascii.encode(key), utf8.encode(value)));
+      });
+      return new HeadersStreamMessage(headers);
+    } else if (input is GrpcData) {
+      return new DataStreamMessage(frame(input.data));
+    }
+    throw new GrpcError.internal('Unexpected message type');
+  }
+
+  static List<int> frame(List<int> payload) {
+    final payloadLength = payload.length;
+    final bytes = new Uint8List(payloadLength + 5);
+    final header = bytes.buffer.asByteData(0, 5);
+    header.setUint8(0, 0); // TODO(dart-lang/grpc-dart#6): Handle compression
+    header.setUint32(1, payloadLength);
+    bytes.setRange(5, bytes.length, payload);
+    return bytes;
+  }
+}
+
+class GrpcHttpDecoder extends Converter<StreamMessage, GrpcMessage> {
+  @override
+  GrpcMessage convert(StreamMessage input) {
+    final sink = new _GrpcMessageSink();
+    startChunkedConversion(sink)
+      ..add(input)
+      ..close();
+    return sink.message;
+  }
+
+  @override
+  Sink<StreamMessage> startChunkedConversion(Sink<GrpcMessage> sink) {
+    return new _GrpcMessageConversionSink(sink);
+  }
+}
+
+class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
+  final Sink<GrpcMessage> _out;
+
+  final _dataHeader = new Uint8List(5);
+  Uint8List _data;
+  int _dataOffset = 0;
+
+  _GrpcMessageConversionSink(this._out);
+
+  void _addData(DataStreamMessage chunk) {
+    final chunkData = chunk.bytes;
+    final chunkLength = chunkData.length;
+    var chunkReadOffset = 0;
+
+    while (chunkReadOffset < chunkLength) {
+      if (_data == null) {
+        // Reading header.
+        final headerRemaining = _dataHeader.lengthInBytes - _dataOffset;
+        final chunkRemaining = chunkLength - chunkReadOffset;
+        final toCopy = min(headerRemaining, chunkRemaining);
+        _dataHeader.setRange(
+            _dataOffset, _dataOffset + toCopy, chunkData, chunkReadOffset);
+        _dataOffset += toCopy;
+        chunkReadOffset += toCopy;
+        if (_dataOffset == _dataHeader.lengthInBytes) {
+          final dataLength = _dataHeader.buffer.asByteData().getUint32(1);
+          // TODO(jakobr): Sanity check dataLength. Max size?
+          _data = new Uint8List(dataLength);
+          _dataOffset = 0;
+        }
+      }
+      if (_data != null) {
+        // Reading data.
+        final dataRemaining = _data.lengthInBytes - _dataOffset;
+        if (dataRemaining > 0) {
+          final chunkRemaining = chunkLength - chunkReadOffset;
+          final toCopy = min(dataRemaining, chunkRemaining);
+          _data.setRange(
+              _dataOffset, _dataOffset + toCopy, chunkData, chunkReadOffset);
+          _dataOffset += toCopy;
+          chunkReadOffset += toCopy;
+        }
+        if (_dataOffset == _data.lengthInBytes) {
+          _out.add(new GrpcData(_data,
+              isCompressed: _dataHeader.buffer.asByteData().getUint8(0) != 0));
+          _data = null;
+          _dataOffset = 0;
+        }
+      }
+    }
+  }
+
+  void _addHeaders(HeadersStreamMessage chunk) {
+    if (_data != null || _dataOffset != 0) {
+      // We were in the middle of receiving data, so receiving a header frame
+      // is a violation of the gRPC protocol.
+      throw new GrpcError.unimplemented('Received header while reading data');
+    }
+    final headers = <String, String>{};
+    for (var header in chunk.headers) {
+      // TODO(jakobr): Handle duplicate header names correctly.
+      headers[ascii.decode(header.name)] = ascii.decode(header.value);
+    }
+    // TODO(jakobr): Check :status, go to error mode if not 2xx.
+    _out.add(new GrpcMetadata(headers));
+  }
+
+  @override
+  void add(StreamMessage chunk) {
+    if (chunk is DataStreamMessage) {
+      _addData(chunk);
+    } else if (chunk is HeadersStreamMessage) {
+      _addHeaders(chunk);
+    } else {
+      // No clue what this is.
+      throw new GrpcError.unimplemented('Received unknown HTTP/2 frame type');
+    }
+  }
+
+  @override
+  void close() {
+    if (_data != null || _dataOffset != 0) {
+      throw new GrpcError.unavailable('Closed in non-idle state');
+    }
+    _out.close();
+  }
+}
+
+class _GrpcMessageSink extends Sink<GrpcMessage> {
+  GrpcMessage message;
+
+  @override
+  void add(GrpcMessage data) {
+    if (message != null) {
+      throw 'Too many messages received!';
+    }
+    message = data;
+  }
+
+  @override
+  void close() {
+    if (message == null) {
+      throw 'No messages received!';
+    }
+  }
+}
diff --git a/grpc/lib/src/shared/timeout.dart b/grpc/lib/src/shared/timeout.dart
new file mode 100644
index 0000000..965ab88
--- /dev/null
+++ b/grpc/lib/src/shared/timeout.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
+// for details. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// Convert [timeout] to grpc-timeout header string format.
+// Mostly inspired by grpc-java implementation.
+// TODO(jakobr): Modify to match grpc/core implementation instead.
+String toTimeoutString(Duration duration) {
+  if (duration == null) return null;
+  const cutoff = 100000;
+  final timeout = duration.inMicroseconds;
+  if (timeout < 0) {
+    // Smallest possible timeout.
+    return '1n';
+  } else if (timeout < cutoff) {
+    return '${timeout}u';
+  } else if (timeout < cutoff * 1000) {
+    return '${timeout ~/ 1000}m';
+  } else if (timeout < cutoff * 1000 * 1000) {
+    return '${timeout ~/ 1000000}S';
+  } else if (timeout < cutoff * 1000 * 1000 * 60) {
+    return '${timeout ~/ 60000000}M';
+  } else {
+    return '${timeout ~/ 3600000000}H';
+  }
+}
+
+/// Convert [timeout] from grpc-timeout header string format to [Duration].
+/// Returns [null] if [timeout] is not correctly formatted.
+Duration fromTimeoutString(String timeout) {
+  if (timeout == null) return null;
+  if (timeout.length < 2) return null;
+  final value = int.tryParse(timeout.substring(0, timeout.length - 1));
+  if (value == null) return null;
+  switch (timeout[timeout.length - 1]) {
+    case 'n':
+      return new Duration(microseconds: value * 1000);
+    case 'u':
+      return new Duration(microseconds: value);
+    case 'm':
+      return new Duration(milliseconds: value);
+    case 'S':
+      return new Duration(seconds: value);
+    case 'M':
+      return new Duration(minutes: value);
+    case 'H':
+      return new Duration(hours: value);
+    default:
+      return null;
+  }
+}
diff --git a/grpc/pubspec.yaml b/grpc/pubspec.yaml
new file mode 100644
index 0000000..341ab8f
--- /dev/null
+++ b/grpc/pubspec.yaml
@@ -0,0 +1,19 @@
+name: grpc
+description: Dart implementation of gRPC, a high performance, open-source universal RPC framework.
+version: 1.0.3
+author: Dart Team <misc@dartlang.org>
+homepage: https://github.com/dart-lang/grpc-dart
+
+environment:
+  sdk: '>=2.0.0 <3.0.0'
+
+dependencies:
+  async: '>=1.13.3 <3.0.0'
+  googleapis_auth: ^0.2.5+3
+  meta: ^1.0.5
+  http: '>=0.11.3+17 <0.13.0'
+  http2: '>=0.1.7 <2.0.0'
+
+dev_dependencies:
+  mockito: ^4.0.0
+  test: ^1.6.1
diff --git a/http2/.dart_tool/pub/bin/sdk-version b/http2/.dart_tool/pub/bin/sdk-version
new file mode 100644
index 0000000..227cea2
--- /dev/null
+++ b/http2/.dart_tool/pub/bin/sdk-version
@@ -0,0 +1 @@
+2.0.0
diff --git a/http2/.dart_tool/pub/bin/test/test.dart.snapshot.dart2 b/http2/.dart_tool/pub/bin/test/test.dart.snapshot.dart2
new file mode 100644
index 0000000..bea517b
--- /dev/null
+++ b/http2/.dart_tool/pub/bin/test/test.dart.snapshot.dart2
Binary files differ
diff --git a/http2/.gitignore b/http2/.gitignore
new file mode 100644
index 0000000..a19c373
--- /dev/null
+++ b/http2/.gitignore
@@ -0,0 +1,16 @@
+# Don’t commit the following directories created by pub.
+build/
+.packages
+.pub/
+packages
+.buildlog
+
+# Or the files created by dart2js.
+*.dart.js
+*.dart.precompiled.js
+*.js_
+*.js.deps
+*.js.map
+
+# Include when developing application packages.
+pubspec.lock
diff --git a/http2/.idea/codeStyles/codeStyleConfig.xml b/http2/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..c79f34c
--- /dev/null
+++ b/http2/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Google Configuration Checker Style" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/http2/.idea/http2.iml b/http2/.idea/http2.iml
new file mode 100644
index 0000000..ae9af97
--- /dev/null
+++ b/http2/.idea/http2.iml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
+      <excludeFolder url="file://$MODULE_DIR$/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Dart SDK" level="project" />
+    <orderEntry type="library" name="Dart Packages" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/http2/.idea/libraries/Dart_Packages.xml b/http2/.idea/libraries/Dart_Packages.xml
new file mode 100644
index 0000000..bb6094d
--- /dev/null
+++ b/http2/.idea/libraries/Dart_Packages.xml
@@ -0,0 +1,428 @@
+<component name="libraryTable">
+  <library name="Dart Packages" type="DartPackagesLibraryType">
+    <properties>
+      <option name="packageNameToDirsMap">
+        <entry key="analyzer">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.33.3+1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="args">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="async">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="boolean_selector">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="charcode">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="collection">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="convert">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="crypto">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="csslib">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="front_end">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.6+4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="glob">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="html">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="http">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.12.0/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="http_multi_server">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="http_parser">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="io">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="js">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="json_rpc_2">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="kernel">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.6+4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="logging">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="matcher">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="meta">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="mime">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="mockito">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mockito-4.0.0/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="multi_server_socket">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="node_preamble">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="package_config">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="package_resolver">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="path">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="plugin">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="pool">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="pub_semver">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="shelf">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="shelf_packages_handler">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="shelf_static">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="shelf_web_socket">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="source_map_stack_trace">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="source_maps">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.8/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="source_span">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="stack_trace">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="stream_channel">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="string_scanner">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.4/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="term_glyph">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="test">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.5.1+1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="test_api">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="test_core">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_core-0.2.0+1/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="typed_data">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="utf">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="vm_service_client">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="watcher">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="web_socket_channel">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
+            </list>
+          </value>
+        </entry>
+        <entry key="yaml">
+          <value>
+            <list>
+              <option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
+            </list>
+          </value>
+        </entry>
+      </option>
+    </properties>
+    <CLASSES>
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.33.3+1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.6+4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.12.0/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.6+4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mockito-4.0.0/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.8/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.4/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.5.1+1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_core-0.2.0+1/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
+      <root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/http2/.idea/libraries/Dart_SDK.xml b/http2/.idea/libraries/Dart_SDK.xml
new file mode 100644
index 0000000..e2b2cff
--- /dev/null
+++ b/http2/.idea/libraries/Dart_SDK.xml
@@ -0,0 +1,27 @@
+<component name="libraryTable">
+  <library name="Dart SDK">
+    <CLASSES>
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/async" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/cli" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/collection" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/convert" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/core" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/developer" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/html" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/indexed_db" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/io" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/isolate" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/js" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/js_util" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/math" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/mirrors" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/svg" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/typed_data" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/web_audio" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/web_gl" />
+      <root url="file://$USER_HOME$/install/dart-sdk/lib/web_sql" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/http2/.idea/modules.xml b/http2/.idea/modules.xml
new file mode 100644
index 0000000..b9f6041
--- /dev/null
+++ b/http2/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/http2.iml" filepath="$PROJECT_DIR$/.idea/http2.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/http2/.idea/vcs.xml b/http2/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1ddf
--- /dev/null
+++ b/http2/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/http2/.idea/workspace.xml b/http2/.idea/workspace.xml
new file mode 100644
index 0000000..4f75e60
--- /dev/null
+++ b/http2/.idea/workspace.xml
@@ -0,0 +1,806 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="ac17af96-834b-47c0-bec3-b5f93f5e3dc2" name="Default" comment="" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="TRACKING_ENABLED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+      <file leaf-file-name="transport.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/lib/transport.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="15">
+              <caret line="1" column="12" selection-start-line="1" selection-start-column="12" selection-end-line="1" selection-end-column="12" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="multiprotocol_server.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/lib/multiprotocol_server.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="30">
+              <caret line="4" selection-start-line="4" selection-end-line="4" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="CHANGELOG.md" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="75">
+              <caret line="5" column="48" selection-start-line="5" selection-start-column="48" selection-end-line="5" selection-end-column="48" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="README.md" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/README.md">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="13">
+              <caret line="13" column="27" selection-start-line="13" selection-start-column="27" selection-end-line="13" selection-end-column="27" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/pubspec.yaml">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="150">
+              <caret line="10" column="17" selection-start-line="10" selection-start-column="17" selection-end-line="10" selection-end-column="17" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="out_of_stream_ids_test.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/manual_test/out_of_stream_ids_test.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state>
+              <folding>
+                <element signature="e#0#2139#0" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="transport_test.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/test/transport_test.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="333">
+              <caret line="512" column="9" selection-start-line="512" selection-start-column="9" selection-end-line="512" selection-end-column="9" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="http2.dart" pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/lib/http2.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="720">
+              <caret line="48" lean-forward="true" selection-start-line="48" selection-end-line="48" />
+              <folding>
+                <element signature="e#0#2418#0" expanded="true" />
+                <element signature="e#2364#2388#0" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="codereview.settings" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/codereview.settings">
+          <provider selected="true" editor-type-id="text-editor" />
+        </entry>
+      </file>
+      <file leaf-file-name="client.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/lib/src/testing/client.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="30">
+              <caret line="4" selection-start-line="4" selection-end-line="4" />
+              <folding>
+                <element signature="e#217#237#0" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="FindInProjectRecents">
+    <findStrings>
+      <find>therefore not be used to make new streams</find>
+      <find>no longer active</find>
+      <find>StreamMessageQueueIn</find>
+      <find>The http/2 connection</find>
+      <find>nego</find>
+      <find>library</find>
+    </findStrings>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/example/request_resource.dart" />
+        <option value="$PROJECT_DIR$/example/minimal.dart" />
+        <option value="$PROJECT_DIR$/example/display_headers.dart" />
+        <option value="$PROJECT_DIR$/README.md" />
+        <option value="$PROJECT_DIR$/lib/transport.dart" />
+        <option value="$PROJECT_DIR$/lib/src/error_handler.dart" />
+        <option value="$PROJECT_DIR$/lib/multiprotocol_server.dart" />
+        <option value="$PROJECT_DIR$/lib/src/artificial_server_socket.dart" />
+        <option value="$PROJECT_DIR$/lib/src/byte_utils.dart" />
+        <option value="$PROJECT_DIR$/lib/src/connection.dart" />
+        <option value="$PROJECT_DIR$/lib/src/connection_preface.dart" />
+        <option value="$PROJECT_DIR$/lib/src/sync_errors.dart" />
+        <option value="$PROJECT_DIR$/lib/src/async_utils/async_utils.dart" />
+        <option value="$PROJECT_DIR$/lib/src/flowcontrol/connection_queues.dart" />
+        <option value="$PROJECT_DIR$/lib/src/flowcontrol/stream_queues.dart" />
+        <option value="$PROJECT_DIR$/lib/src/flowcontrol/window.dart" />
+        <option value="$PROJECT_DIR$/lib/src/flowcontrol/window_handler.dart" />
+        <option value="$PROJECT_DIR$/lib/src/frames/frame_defragmenter.dart" />
+        <option value="$PROJECT_DIR$/lib/src/frames/frames.dart" />
+        <option value="$PROJECT_DIR$/lib/src/hpack/hpack.dart" />
+        <option value="$PROJECT_DIR$/lib/src/hpack/huffman.dart" />
+        <option value="$PROJECT_DIR$/lib/src/hpack/huffman_table.dart" />
+        <option value="$PROJECT_DIR$/lib/src/ping/ping_handler.dart" />
+        <option value="$PROJECT_DIR$/lib/src/settings/settings.dart" />
+        <option value="$PROJECT_DIR$/lib/src/streams/stream_handler.dart" />
+        <option value="$PROJECT_DIR$/lib/src/testing/debug.dart" />
+        <option value="$PROJECT_DIR$/test/client_test.dart" />
+        <option value="$PROJECT_DIR$/test/client_websites_test.dart" />
+        <option value="$PROJECT_DIR$/test/multiprotocol_server_test.dart" />
+        <option value="$PROJECT_DIR$/test/server_test.dart" />
+        <option value="$PROJECT_DIR$/test/src/connection_preface_test.dart" />
+        <option value="$PROJECT_DIR$/test/src/error_matchers.dart" />
+        <option value="$PROJECT_DIR$/test/src/streams/helper.dart" />
+        <option value="$PROJECT_DIR$/test/src/streams/simple_flow_test.dart" />
+        <option value="$PROJECT_DIR$/test/src/streams/simple_push_test.dart" />
+        <option value="$PROJECT_DIR$/test/src/streams/streams_test.dart" />
+        <option value="$PROJECT_DIR$/lib/src/testing/client.dart" />
+        <option value="$PROJECT_DIR$/pubspec.yaml" />
+        <option value="$PROJECT_DIR$/CHANGELOG.md" />
+        <option value="$PROJECT_DIR$/lib/http2.dart" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectFrameBounds">
+    <option name="x" value="447" />
+    <option name="y" value="54" />
+    <option name="width" value="1831" />
+    <option name="height" value="1435" />
+  </component>
+  <component name="ProjectLevelVcsManager">
+    <ConfirmationsSetting value="1" id="Add" />
+  </component>
+  <component name="ProjectView">
+    <navigator proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="doc" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="doc" type="462c0819:PsiDirectoryNode" />
+              <item name="api" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="example" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="lib" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="lib" type="462c0819:PsiDirectoryNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="manual_test" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="http2" type="b2602c69:ProjectViewProjectNode" />
+              <item name="http2" type="462c0819:PsiDirectoryNode" />
+              <item name="test" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+      <pane id="PackagesPane" />
+      <pane id="Scope" />
+      <pane id="AndroidView" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="dart.analysis.tool.window.force.activate" value="false" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="RunManager" selected="Dart Test.tests in http2">
+    <configuration default="true" type="Application" factoryName="Application">
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    </configuration>
+    <configuration name="display_headers.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" temporary="true" nameIsGenerated="true">
+      <option name="filePath" value="$PROJECT_DIR$/example/display_headers.dart" />
+      <option name="workingDirectory" value="$PROJECT_DIR$" />
+    </configuration>
+    <configuration name="minimal.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" temporary="true" nameIsGenerated="true">
+      <option name="filePath" value="$PROJECT_DIR$/example/minimal.dart" />
+      <option name="workingDirectory" value="$PROJECT_DIR$" />
+    </configuration>
+    <configuration name="request_resource.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" temporary="true" nameIsGenerated="true">
+      <option name="filePath" value="$PROJECT_DIR$/example/request_resource.dart" />
+      <option name="workingDirectory" value="$PROJECT_DIR$" />
+    </configuration>
+    <configuration name="tests in http2" type="DartTestRunConfigurationType" factoryName="Dart Test" temporary="true" nameIsGenerated="true">
+      <option name="filePath" value="$PROJECT_DIR$" />
+      <option name="scope" value="FOLDER" />
+    </configuration>
+    <configuration default="true" type="JUnit" factoryName="JUnit">
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="TEST_OBJECT" value="class" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="singleModule" />
+      </option>
+      <patterns />
+    </configuration>
+    <configuration default="true" type="TestNG" factoryName="TestNG">
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="SUITE_NAME" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="GROUP_NAME" />
+      <option name="TEST_OBJECT" value="CLASS" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
+      <option name="OUTPUT_DIRECTORY" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="singleModule" />
+      </option>
+      <option name="USE_DEFAULT_REPORTERS" value="false" />
+      <option name="PROPERTIES_FILE" />
+      <properties />
+      <listeners />
+    </configuration>
+    <list>
+      <item itemvalue="Dart Command Line App.request_resource.dart" />
+      <item itemvalue="Dart Command Line App.display_headers.dart" />
+      <item itemvalue="Dart Command Line App.minimal.dart" />
+      <item itemvalue="Dart Test.tests in http2" />
+    </list>
+    <recent_temporary>
+      <list>
+        <item itemvalue="Dart Test.tests in http2" />
+        <item itemvalue="Dart Command Line App.minimal.dart" />
+        <item itemvalue="Dart Command Line App.display_headers.dart" />
+        <item itemvalue="Dart Command Line App.request_resource.dart" />
+      </list>
+    </recent_temporary>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="ac17af96-834b-47c0-bec3-b5f93f5e3dc2" name="Default" comment="" />
+      <created>1540293392764</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1540293392764</updated>
+    </task>
+    <servers />
+  </component>
+  <component name="TestHistory">
+    <history-entry file="tests_in_http2 - 2018.12.13 at 14h 57m 07s.xml">
+      <configuration name="tests in http2" configurationId="DartTestRunConfigurationType" />
+    </history-entry>
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="447" y="54" width="1831" height="1435" extended-state="0" />
+    <layout>
+      <window_info anchor="right" id="Palette" order="3" />
+      <window_info anchor="bottom" id="TODO" order="6" />
+      <window_info anchor="bottom" id="Messages" weight="0.32981133" />
+      <window_info anchor="right" id="Palette&#9;" order="3" />
+      <window_info id="Image Layers" order="2" />
+      <window_info anchor="right" id="Capture Analysis" order="3" />
+      <window_info anchor="bottom" id="Event Log" order="7" side_tool="true" />
+      <window_info anchor="right" id="Maven Projects" order="3" />
+      <window_info anchor="bottom" id="Dart Analysis" order="7" weight="0.32981133" />
+      <window_info anchor="bottom" id="Run" order="2" weight="0.35698113" />
+      <window_info anchor="bottom" id="Version Control" order="7" weight="0.60075474" />
+      <window_info anchor="bottom" id="Code Review" order="7" />
+      <window_info active="true" anchor="bottom" id="Terminal" order="7" visible="true" weight="0.2792453" />
+      <window_info id="Capture Tool" order="2" />
+      <window_info id="Designer" order="2" />
+      <window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.24986331" />
+      <window_info anchor="bottom" id="Find" order="1" weight="0.15773585" />
+      <window_info id="Structure" order="1" side_tool="true" weight="0.25" />
+      <window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
+      <window_info id="UI Designer" order="2" />
+      <window_info anchor="right" id="Theme Preview" order="3" />
+      <window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
+      <window_info id="Favorites" order="2" side_tool="true" />
+      <window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
+      <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
+      <window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
+      <window_info anchor="bottom" id="Message" order="0" />
+      <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
+    </layout>
+  </component>
+  <component name="VcsContentAnnotationSettings">
+    <option name="myLimit" value="2678400000" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager>
+      <breakpoints>
+        <line-breakpoint enabled="true" type="Dart">
+          <url>file://$PROJECT_DIR$/lib/src/flowcontrol/connection_queues.dart</url>
+        </line-breakpoint>
+      </breakpoints>
+      <option name="time" value="1" />
+    </breakpoint-manager>
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/lib/transport.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="795">
+          <caret line="156" column="33" selection-start-line="156" selection-start-column="33" selection-end-line="156" selection-end-column="33" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/streams/stream_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="2715">
+          <caret line="198" column="27" selection-start-line="198" selection-start-column="27" selection-end-line="198" selection-end-column="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/error_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="960">
+          <caret line="68" column="17" selection-start-line="68" selection-start-column="17" selection-end-line="68" selection-end-column="17" />
+          <folding>
+            <element signature="e#251#271#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/stream_queues.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="4020">
+          <caret line="280" column="7" selection-start-line="280" selection-start-column="7" selection-end-line="280" selection-end-column="7" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/connection_queues.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="2895">
+          <caret line="210" column="15" selection-start-line="210" selection-start-column="15" selection-end-line="210" selection-end-column="15" />
+          <folding>
+            <element signature="e#410#430#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/connection.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="5820">
+          <caret line="405" column="61" selection-start-line="405" selection-start-column="61" selection-end-line="405" selection-end-column="61" />
+          <folding>
+            <element signature="e#0#17299#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/example/request_resource.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="90">
+          <caret line="6" column="22" lean-forward="true" selection-start-line="6" selection-start-column="22" selection-end-line="6" selection-end-column="22" />
+          <folding>
+            <element signature="e#0#20#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$USER_HOME$/install/dart-sdk/lib/io/secure_socket.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="150">
+          <caret line="12" column="15" selection-start-line="12" selection-start-column="15" selection-end-line="12" selection-end-column="15" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/example/minimal.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="495">
+          <caret line="33" column="26" selection-start-line="33" selection-start-column="26" selection-end-line="33" selection-end-column="26" />
+          <folding>
+            <element signature="e#0#22#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/example/display_headers.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="60">
+          <caret line="4" column="27" selection-start-line="4" selection-start-column="27" selection-end-line="4" selection-end-column="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/doc/api/index.html">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/error_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#251#271#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/connection_preface.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="60">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#0#3195#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/sync_errors.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="60">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/connection_queues.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="8" selection-start-line="8" selection-end-line="8" />
+          <folding>
+            <element signature="e#410#430#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/stream_queues.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/window.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" column="14" lean-forward="true" selection-start-line="4" selection-start-column="14" selection-end-line="4" selection-end-column="14" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/flowcontrol/window_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/frames/frame_defragmenter.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/frames/frames.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/hpack/hpack.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="15">
+          <caret line="7" selection-start-line="7" selection-end-line="7" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/hpack/huffman.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/hpack/huffman_table.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/settings/settings.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/streams/stream_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/testing/debug.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/multiprotocol_server_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/server_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/connection_preface_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/error_matchers.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/streams/helper.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/streams/simple_flow_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/streams/simple_push_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/src/streams/streams_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/ping/ping_handler.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#217#237#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/artificial_server_socket.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#216#236#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/async_utils/async_utils.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#217#237#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/byte_utils.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/client_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#217#237#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/connection.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state>
+          <folding>
+            <element signature="e#0#17299#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/client_websites_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#217#237#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/testing/client.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+          <folding>
+            <element signature="e#217#237#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pubspec.yaml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="150">
+          <caret line="10" column="17" selection-start-line="10" selection-start-column="17" selection-end-line="10" selection-end-column="17" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/transport_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="333">
+          <caret line="512" column="9" selection-start-line="512" selection-start-column="9" selection-end-line="512" selection-end-column="9" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/manual_test/out_of_stream_ids_test.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state>
+          <folding>
+            <element signature="e#0#2139#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/README.md">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="13">
+          <caret line="13" column="27" selection-start-line="13" selection-start-column="27" selection-end-line="13" selection-end-column="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/transport.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="15">
+          <caret line="1" column="12" selection-start-line="1" selection-start-column="12" selection-end-line="1" selection-end-column="12" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/multiprotocol_server.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="30">
+          <caret line="4" selection-start-line="4" selection-end-line="4" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/codereview.settings">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="75">
+          <caret line="5" column="48" selection-start-line="5" selection-start-column="48" selection-end-line="5" selection-end-column="48" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/http2.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="720">
+          <caret line="48" lean-forward="true" selection-start-line="48" selection-end-line="48" />
+          <folding>
+            <element signature="e#0#2418#0" expanded="true" />
+            <element signature="e#2364#2388#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ProjectJDKs.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>
\ No newline at end of file
diff --git a/http2/.test_config b/http2/.test_config
new file mode 100644
index 0000000..2fa4b96
--- /dev/null
+++ b/http2/.test_config
@@ -0,0 +1,5 @@
+{
+  "test_package": {
+    "platforms" : ["vm"]
+  }
+}
diff --git a/http2/.travis.yml b/http2/.travis.yml
new file mode 100644
index 0000000..9d69cdd
--- /dev/null
+++ b/http2/.travis.yml
@@ -0,0 +1,16 @@
+language: dart
+dart:
+  - dev
+
+dart_task:
+  - test
+  - dartfmt
+  - dartanalyzer
+
+# Only building master means that we don't run two builds for each pull request.
+branches:
+  only: [master]
+
+cache:
+ directories:
+   - $HOME/.pub-cache
diff --git a/http2/AUTHORS b/http2/AUTHORS
new file mode 100644
index 0000000..93b7228
--- /dev/null
+++ b/http2/AUTHORS
@@ -0,0 +1,8 @@
+# Below is a list of people and organizations that have contributed
+# to the project. Names should be added to the list like so:
+#
+#   Name/Organization <email address>
+
+Google Inc. <*@google.com>
+
+Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
diff --git a/http2/BUILD.gn b/http2/BUILD.gn
new file mode 100644
index 0000000..3e07fb2
--- /dev/null
+++ b/http2/BUILD.gn
@@ -0,0 +1,16 @@
+# This file is generated by importer.py for http2-1.0.0
+
+import("//build/dart/dart_library.gni")
+
+dart_library("http2") {
+  package_name = "http2"
+
+  # This parameter is left empty as we don't care about analysis or exporting
+  # these sources outside of the tree.
+  sources = []
+
+  disable_analysis = true
+
+  deps = [
+  ]
+}
diff --git a/http2/CHANGELOG.md b/http2/CHANGELOG.md
new file mode 100644
index 0000000..641a73b
--- /dev/null
+++ b/http2/CHANGELOG.md
@@ -0,0 +1,80 @@
+## 1.0
+
+* Graduate package to 1.0.
+* `package:http2/http2.dart` now reexports `package:http2/transport.dart`.
+
+## 0.1.9
+
+* Discard messages incoming after stream cancellation.
+
+## 0.1.8+2
+
+* On connection termination, try to dispatch existing messages, thereby avoiding
+  terminating existing streams.
+
+* Fix `ClientTransportConnection.isOpen` to return `false` if we have exhausted
+  the number of max-concurrent-streams.
+
+## 0.1.8+1
+
+* Switch all uppercase constants from `dart:convert` to lowercase.
+
+## 0.1.8
+
+* More changes required for making tests pass under Dart 2.0 runtime.
+* Modify sdk constraint to require '>=2.0.0-dev.40.0'.
+
+## 0.1.7
+
+* Fixes for Dart 2.0.
+
+## 0.1.6
+
+* Strong mode fixes and other cleanup.
+
+## 0.1.5
+
+* Removed use of new `Function` syntax, since it isn't fully supported in Dart
+  1.24.
+
+## 0.1.4
+
+* Added an `onActiveStateChanged` callback to `Connection`, which is invoked when
+  the connection changes state from idle to active or from active to idle. This
+  can be used to implement an idle connection timeout.
+
+## 0.1.3
+
+* Fixed a bug where a closed window would not open correctly due to an increase
+  in initial window size.
+
+## 0.1.2
+
+* The endStream bit is now set on the requested frame, instead of on an empty
+  data frame following it.
+* Added an `onTerminated` hook that is called when a TransportStream receives
+  a RST_STREAM frame.
+
+## 0.1.1+2
+
+* Add errorCode to exception toString message.
+
+## 0.1.1+1
+
+* Fixing a performance issue in case the underlying socket is not writeable
+* Allow clients of MultiProtocolHttpServer to supply [http.ServerSettings]
+* Allow the draft version 'h2-14' in the ALPN protocol negogiation.
+
+## 0.1.1
+
+* Adding support for MultiProtocolHttpServer in the
+  `package:http2/multiprotocol_server.dart` library
+
+## 0.1.0
+
+* First version of a HTTP/2 transport implementation in the
+  `package:http2/transport.dart` library
+
+## 0.0.1
+
+- Initial version
diff --git a/http2/CONTRIBUTING.md b/http2/CONTRIBUTING.md
new file mode 100644
index 0000000..6f5e0ea
--- /dev/null
+++ b/http2/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+Want to contribute? Great! First, read this page (including the small print at
+the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review.
+
+### File headers
+All files in the project must start with the following header.
+
+    // Copyright (c) 2015, 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.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than the
+one above, the
+[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).
diff --git a/http2/LICENSE b/http2/LICENSE
new file mode 100644
index 0000000..de31e1a
--- /dev/null
+++ b/http2/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2015, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of Google Inc. nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/http2/README.md b/http2/README.md
new file mode 100644
index 0000000..b910282
--- /dev/null
+++ b/http2/README.md
@@ -0,0 +1,62 @@
+# HTTP/2 for Dart
+
+This library provides an http/2 interface on top of a bidirectional stream of bytes.
+
+## Usage:
+
+Here is a minimal example of connecting to a http/2 capable server, requesting a resource and
+iterating over the response.
+
+```dart
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:http2/http2.dart';
+
+main() async {
+  var uri = Uri.parse('https://www.google.com/');
+
+  var transport = new ClientTransportConnection.viaSocket(
+    await SecureSocket.connect(
+      uri.host,
+      uri.port,
+      supportedProtocols: ['h2'],
+    ),
+  );
+
+  var stream = transport.makeRequest(
+    [
+      new Header.ascii(':method', 'GET'),
+      new Header.ascii(':path', uri.path),
+      new Header.ascii(':scheme', uri.scheme),
+      new Header.ascii(':authority', uri.host),
+    ],
+    endStream: true,
+  );
+
+  await for (var message in stream.incomingMessages) {
+    if (message is HeadersStreamMessage) {
+      for (var header in message.headers) {
+        var name = utf8.decode(header.name);
+        var value = utf8.decode(header.value);
+        print('Header: $name: $value');
+      }
+    } else if (message is DataStreamMessage) {
+      // Use [message.bytes] (but respect 'content-encoding' header)
+    }
+  }
+  await transport.finish();
+}
+```
+
+An example with better error handling is available [here][example].
+
+See the [API docs][api] for more details.
+
+## Features and bugs
+
+Please file feature requests and bugs at the [issue tracker][tracker].
+
+[tracker]: https://github.com/dart-lang/http2/issues
+[api]: http://www.dartdocs.org/documentation/http2/latest
+[example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart.
diff --git a/http2/analysis_options.yaml b/http2/analysis_options.yaml
new file mode 100644
index 0000000..7244d25
--- /dev/null
+++ b/http2/analysis_options.yaml
@@ -0,0 +1,37 @@
+analyzer:
+  strong-mode: true
+  errors:
+    unused_element: error
+    unused_import: error
+    unused_local_variable: error
+    dead_code: error
+linter:
+  rules:
+    #- annotate_overrides
+    - avoid_empty_else
+    - avoid_init_to_null
+    - avoid_return_types_on_setters
+    - await_only_futures
+    - camel_case_types
+    - comment_references
+    - control_flow_in_finally
+    - directives_ordering
+    - empty_catches
+    - empty_constructor_bodies
+    - empty_statements
+    - hash_and_equals
+    - implementation_imports
+    - library_names
+    - library_prefixes
+    - non_constant_identifier_names
+    - only_throw_errors
+    - prefer_final_fields
+    - prefer_is_not_empty
+    #- prefer_single_quotes
+    - slash_for_doc_comments
+    - test_types_in_equals
+    - test_types_in_equals
+    - throw_in_finally
+    - type_init_formals
+    - unrelated_type_equality_checks
+    - valid_regexps
diff --git a/http2/codereview.settings b/http2/codereview.settings
new file mode 100644
index 0000000..69e23ea
--- /dev/null
+++ b/http2/codereview.settings
@@ -0,0 +1,3 @@
+CODE_REVIEW_SERVER: https://codereview.chromium.org/
+VIEW_VC: https://github.com/dart-lang/http2/commit/
+CC_LIST: reviews@dartlang.org
diff --git a/http2/doc/api/index.json b/http2/doc/api/index.json
new file mode 100644
index 0000000..2b4e5bf
--- /dev/null
+++ b/http2/doc/api/index.json
@@ -0,0 +1 @@
+[{"name":"http2.http2","qualifiedName":"http2.http2","href":"http2.http2/http2.http2-library.html","type":"library","overriddenDepth":0},{"name":"Header","qualifiedName":"http2.http2.Header","href":"http2.http2/Header-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"http2.http2","type":"library"}},{"name":"Header","qualifiedName":"http2.http2.Header","href":"http2.http2/Header/Header.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"operator ==","qualifiedName":"http2.http2.Header.==","href":"http2.http2/Header/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"Header.ascii","qualifiedName":"http2.http2.Header.ascii","href":"http2.http2/Header/Header.ascii.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"hashCode","qualifiedName":"http2.http2.Header.hashCode","href":"http2.http2/Header/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"name","qualifiedName":"http2.http2.Header.name","href":"http2.http2/Header/name.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"neverIndexed","qualifiedName":"http2.http2.Header.neverIndexed","href":"http2.http2/Header/neverIndexed.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"noSuchMethod","qualifiedName":"http2.http2.Header.noSuchMethod","href":"http2.http2/Header/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"runtimeType","qualifiedName":"http2.http2.Header.runtimeType","href":"http2.http2/Header/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"toString","qualifiedName":"http2.http2.Header.toString","href":"http2.http2/Header/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"value","qualifiedName":"http2.http2.Header.value","href":"http2.http2/Header/value.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Header","type":"class"}},{"name":"http2.multiprotocol_server","qualifiedName":"http2.multiprotocol_server","href":"http2.multiprotocol_server/http2.multiprotocol_server-library.html","type":"library","overriddenDepth":0},{"name":"MultiProtocolHttpServer","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer","href":"http2.multiprotocol_server/MultiProtocolHttpServer-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"http2.multiprotocol_server","type":"library"}},{"name":"operator ==","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.==","href":"http2.multiprotocol_server/MultiProtocolHttpServer/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"address","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.address","href":"http2.multiprotocol_server/MultiProtocolHttpServer/address.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"bind","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.bind","href":"http2.multiprotocol_server/MultiProtocolHttpServer/bind.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"close","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.close","href":"http2.multiprotocol_server/MultiProtocolHttpServer/close.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"hashCode","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.hashCode","href":"http2.multiprotocol_server/MultiProtocolHttpServer/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"noSuchMethod","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.noSuchMethod","href":"http2.multiprotocol_server/MultiProtocolHttpServer/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"port","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.port","href":"http2.multiprotocol_server/MultiProtocolHttpServer/port.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"runtimeType","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.runtimeType","href":"http2.multiprotocol_server/MultiProtocolHttpServer/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"startServing","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.startServing","href":"http2.multiprotocol_server/MultiProtocolHttpServer/startServing.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"toString","qualifiedName":"http2.multiprotocol_server.MultiProtocolHttpServer.toString","href":"http2.multiprotocol_server/MultiProtocolHttpServer/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"MultiProtocolHttpServer","type":"class"}},{"name":"transport","qualifiedName":"transport","href":"transport/transport-library.html","type":"library","overriddenDepth":0},{"name":"ActiveStateHandler","qualifiedName":"transport.ActiveStateHandler","href":"transport/ActiveStateHandler.html","type":"typedef","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"ClientSettings","qualifiedName":"transport.ClientSettings","href":"transport/ClientSettings-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"ClientSettings","qualifiedName":"transport.ClientSettings","href":"transport/ClientSettings/ClientSettings.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ClientSettings","type":"class"}},{"name":"allowServerPushes","qualifiedName":"transport.ClientSettings.allowServerPushes","href":"transport/ClientSettings/allowServerPushes.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"ClientSettings","type":"class"}},{"name":"ClientTransportConnection","qualifiedName":"transport.ClientTransportConnection","href":"transport/ClientTransportConnection-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"isOpen","qualifiedName":"transport.ClientTransportConnection.isOpen","href":"transport/ClientTransportConnection/isOpen.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportConnection","type":"class"}},{"name":"makeRequest","qualifiedName":"transport.ClientTransportConnection.makeRequest","href":"transport/ClientTransportConnection/makeRequest.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportConnection","type":"class"}},{"name":"ClientTransportConnection.viaSocket","qualifiedName":"transport.ClientTransportConnection.viaSocket","href":"transport/ClientTransportConnection/ClientTransportConnection.viaSocket.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportConnection","type":"class"}},{"name":"ClientTransportConnection.viaStreams","qualifiedName":"transport.ClientTransportConnection.viaStreams","href":"transport/ClientTransportConnection/ClientTransportConnection.viaStreams.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportConnection","type":"class"}},{"name":"ClientTransportStream","qualifiedName":"transport.ClientTransportStream","href":"transport/ClientTransportStream-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"ClientTransportStream","qualifiedName":"transport.ClientTransportStream","href":"transport/ClientTransportStream/ClientTransportStream.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportStream","type":"class"}},{"name":"peerPushes","qualifiedName":"transport.ClientTransportStream.peerPushes","href":"transport/ClientTransportStream/peerPushes.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"ClientTransportStream","type":"class"}},{"name":"DataStreamMessage","qualifiedName":"transport.DataStreamMessage","href":"transport/DataStreamMessage-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"DataStreamMessage","qualifiedName":"transport.DataStreamMessage","href":"transport/DataStreamMessage/DataStreamMessage.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"DataStreamMessage","type":"class"}},{"name":"bytes","qualifiedName":"transport.DataStreamMessage.bytes","href":"transport/DataStreamMessage/bytes.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"DataStreamMessage","type":"class"}},{"name":"toString","qualifiedName":"transport.DataStreamMessage.toString","href":"transport/DataStreamMessage/toString.html","type":"method","overriddenDepth":1,"enclosedBy":{"name":"DataStreamMessage","type":"class"}},{"name":"HeadersStreamMessage","qualifiedName":"transport.HeadersStreamMessage","href":"transport/HeadersStreamMessage-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"HeadersStreamMessage","qualifiedName":"transport.HeadersStreamMessage","href":"transport/HeadersStreamMessage/HeadersStreamMessage.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"HeadersStreamMessage","type":"class"}},{"name":"headers","qualifiedName":"transport.HeadersStreamMessage.headers","href":"transport/HeadersStreamMessage/headers.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"HeadersStreamMessage","type":"class"}},{"name":"toString","qualifiedName":"transport.HeadersStreamMessage.toString","href":"transport/HeadersStreamMessage/toString.html","type":"method","overriddenDepth":1,"enclosedBy":{"name":"HeadersStreamMessage","type":"class"}},{"name":"ServerSettings","qualifiedName":"transport.ServerSettings","href":"transport/ServerSettings-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"ServerSettings","qualifiedName":"transport.ServerSettings","href":"transport/ServerSettings/ServerSettings.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ServerSettings","type":"class"}},{"name":"ServerTransportConnection","qualifiedName":"transport.ServerTransportConnection","href":"transport/ServerTransportConnection-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"incomingStreams","qualifiedName":"transport.ServerTransportConnection.incomingStreams","href":"transport/ServerTransportConnection/incomingStreams.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportConnection","type":"class"}},{"name":"ServerTransportConnection.viaSocket","qualifiedName":"transport.ServerTransportConnection.viaSocket","href":"transport/ServerTransportConnection/ServerTransportConnection.viaSocket.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportConnection","type":"class"}},{"name":"ServerTransportConnection.viaStreams","qualifiedName":"transport.ServerTransportConnection.viaStreams","href":"transport/ServerTransportConnection/ServerTransportConnection.viaStreams.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportConnection","type":"class"}},{"name":"ServerTransportStream","qualifiedName":"transport.ServerTransportStream","href":"transport/ServerTransportStream-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"ServerTransportStream","qualifiedName":"transport.ServerTransportStream","href":"transport/ServerTransportStream/ServerTransportStream.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportStream","type":"class"}},{"name":"canPush","qualifiedName":"transport.ServerTransportStream.canPush","href":"transport/ServerTransportStream/canPush.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportStream","type":"class"}},{"name":"push","qualifiedName":"transport.ServerTransportStream.push","href":"transport/ServerTransportStream/push.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"ServerTransportStream","type":"class"}},{"name":"Settings","qualifiedName":"transport.Settings","href":"transport/Settings-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"Settings","qualifiedName":"transport.Settings","href":"transport/Settings/Settings.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"operator ==","qualifiedName":"transport.Settings.==","href":"transport/Settings/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"concurrentStreamLimit","qualifiedName":"transport.Settings.concurrentStreamLimit","href":"transport/Settings/concurrentStreamLimit.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"hashCode","qualifiedName":"transport.Settings.hashCode","href":"transport/Settings/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.Settings.noSuchMethod","href":"transport/Settings/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.Settings.runtimeType","href":"transport/Settings/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"streamWindowSize","qualifiedName":"transport.Settings.streamWindowSize","href":"transport/Settings/streamWindowSize.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"toString","qualifiedName":"transport.Settings.toString","href":"transport/Settings/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"Settings","type":"class"}},{"name":"StreamMessage","qualifiedName":"transport.StreamMessage","href":"transport/StreamMessage-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"StreamMessage","qualifiedName":"transport.StreamMessage","href":"transport/StreamMessage/StreamMessage.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"operator ==","qualifiedName":"transport.StreamMessage.==","href":"transport/StreamMessage/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"endStream","qualifiedName":"transport.StreamMessage.endStream","href":"transport/StreamMessage/endStream.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"hashCode","qualifiedName":"transport.StreamMessage.hashCode","href":"transport/StreamMessage/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.StreamMessage.noSuchMethod","href":"transport/StreamMessage/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.StreamMessage.runtimeType","href":"transport/StreamMessage/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"toString","qualifiedName":"transport.StreamMessage.toString","href":"transport/StreamMessage/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"StreamMessage","type":"class"}},{"name":"StreamTransportException","qualifiedName":"transport.StreamTransportException","href":"transport/StreamTransportException-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"StreamTransportException","qualifiedName":"transport.StreamTransportException","href":"transport/StreamTransportException/StreamTransportException.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"StreamTransportException","type":"class"}},{"name":"TransportConnection","qualifiedName":"transport.TransportConnection","href":"transport/TransportConnection-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"TransportConnection","qualifiedName":"transport.TransportConnection","href":"transport/TransportConnection/TransportConnection.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"operator ==","qualifiedName":"transport.TransportConnection.==","href":"transport/TransportConnection/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"finish","qualifiedName":"transport.TransportConnection.finish","href":"transport/TransportConnection/finish.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"hashCode","qualifiedName":"transport.TransportConnection.hashCode","href":"transport/TransportConnection/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.TransportConnection.noSuchMethod","href":"transport/TransportConnection/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"onActiveStateChanged","qualifiedName":"transport.TransportConnection.onActiveStateChanged","href":"transport/TransportConnection/onActiveStateChanged.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"ping","qualifiedName":"transport.TransportConnection.ping","href":"transport/TransportConnection/ping.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.TransportConnection.runtimeType","href":"transport/TransportConnection/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"terminate","qualifiedName":"transport.TransportConnection.terminate","href":"transport/TransportConnection/terminate.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"toString","qualifiedName":"transport.TransportConnection.toString","href":"transport/TransportConnection/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportConnection","type":"class"}},{"name":"TransportConnectionException","qualifiedName":"transport.TransportConnectionException","href":"transport/TransportConnectionException-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"TransportConnectionException","qualifiedName":"transport.TransportConnectionException","href":"transport/TransportConnectionException/TransportConnectionException.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"TransportConnectionException","type":"class"}},{"name":"errorCode","qualifiedName":"transport.TransportConnectionException.errorCode","href":"transport/TransportConnectionException/errorCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportConnectionException","type":"class"}},{"name":"TransportException","qualifiedName":"transport.TransportException","href":"transport/TransportException-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"TransportException","qualifiedName":"transport.TransportException","href":"transport/TransportException/TransportException.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"operator ==","qualifiedName":"transport.TransportException.==","href":"transport/TransportException/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"hashCode","qualifiedName":"transport.TransportException.hashCode","href":"transport/TransportException/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"message","qualifiedName":"transport.TransportException.message","href":"transport/TransportException/message.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.TransportException.noSuchMethod","href":"transport/TransportException/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.TransportException.runtimeType","href":"transport/TransportException/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"toString","qualifiedName":"transport.TransportException.toString","href":"transport/TransportException/toString.html","type":"method","overriddenDepth":1,"enclosedBy":{"name":"TransportException","type":"class"}},{"name":"TransportStream","qualifiedName":"transport.TransportStream","href":"transport/TransportStream-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"TransportStream","qualifiedName":"transport.TransportStream","href":"transport/TransportStream/TransportStream.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"operator ==","qualifiedName":"transport.TransportStream.==","href":"transport/TransportStream/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"hashCode","qualifiedName":"transport.TransportStream.hashCode","href":"transport/TransportStream/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"id","qualifiedName":"transport.TransportStream.id","href":"transport/TransportStream/id.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"incomingMessages","qualifiedName":"transport.TransportStream.incomingMessages","href":"transport/TransportStream/incomingMessages.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.TransportStream.noSuchMethod","href":"transport/TransportStream/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"onTerminated","qualifiedName":"transport.TransportStream.onTerminated","href":"transport/TransportStream/onTerminated.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"outgoingMessages","qualifiedName":"transport.TransportStream.outgoingMessages","href":"transport/TransportStream/outgoingMessages.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.TransportStream.runtimeType","href":"transport/TransportStream/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"sendData","qualifiedName":"transport.TransportStream.sendData","href":"transport/TransportStream/sendData.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"sendHeaders","qualifiedName":"transport.TransportStream.sendHeaders","href":"transport/TransportStream/sendHeaders.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"terminate","qualifiedName":"transport.TransportStream.terminate","href":"transport/TransportStream/terminate.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"toString","qualifiedName":"transport.TransportStream.toString","href":"transport/TransportStream/toString.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStream","type":"class"}},{"name":"TransportStreamPush","qualifiedName":"transport.TransportStreamPush","href":"transport/TransportStreamPush-class.html","type":"class","overriddenDepth":0,"enclosedBy":{"name":"transport","type":"library"}},{"name":"TransportStreamPush","qualifiedName":"transport.TransportStreamPush","href":"transport/TransportStreamPush/TransportStreamPush.html","type":"constructor","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"operator ==","qualifiedName":"transport.TransportStreamPush.==","href":"transport/TransportStreamPush/operator_equals.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"hashCode","qualifiedName":"transport.TransportStreamPush.hashCode","href":"transport/TransportStreamPush/hashCode.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"noSuchMethod","qualifiedName":"transport.TransportStreamPush.noSuchMethod","href":"transport/TransportStreamPush/noSuchMethod.html","type":"method","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"requestHeaders","qualifiedName":"transport.TransportStreamPush.requestHeaders","href":"transport/TransportStreamPush/requestHeaders.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"runtimeType","qualifiedName":"transport.TransportStreamPush.runtimeType","href":"transport/TransportStreamPush/runtimeType.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"stream","qualifiedName":"transport.TransportStreamPush.stream","href":"transport/TransportStreamPush/stream.html","type":"property","overriddenDepth":0,"enclosedBy":{"name":"TransportStreamPush","type":"class"}},{"name":"toString","qualifiedName":"transport.TransportStreamPush.toString","href":"transport/TransportStreamPush/toString.html","type":"method","overriddenDepth":1,"enclosedBy":{"name":"TransportStreamPush","type":"class"}}]
diff --git a/http2/doc/api/static-assets/css/bootstrap.css.map b/http2/doc/api/static-assets/css/bootstrap.css.map
new file mode 100644
index 0000000..2fd84f3
--- /dev/null
+++ b/http2/doc/api/static-assets/css/bootstrap.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA,6DAA4D;ACQ5D;EACE,yBAAA;EACA,4BAAA;EACA,gCAAA;EDND;ACaD;EACE,WAAA;EDXD;ACwBD;;;;;;;;;;;;;EAaE,gBAAA;EDtBD;AC8BD;;;;EAIE,uBAAA;EACA,0BAAA;ED5BD;ACoCD;EACE,eAAA;EACA,WAAA;EDlCD;AC0CD;;EAEE,eAAA;EDxCD;ACkDD;EACE,+BAAA;EDhDD;ACuDD;;EAEE,YAAA;EDrDD;AC+DD;EACE,2BAAA;ED7DD;ACoED;;EAEE,mBAAA;EDlED;ACyED;EACE,oBAAA;EDvED;AC+ED;EACE,gBAAA;EACA,kBAAA;ED7ED;ACoFD;EACE,kBAAA;EACA,aAAA;EDlFD;ACyFD;EACE,gBAAA;EDvFD;AC8FD;;EAEE,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,0BAAA;ED5FD;AC+FD;EACE,aAAA;ED7FD;ACgGD;EACE,iBAAA;ED9FD;ACwGD;EACE,WAAA;EDtGD;AC6GD;EACE,kBAAA;ED3GD;ACqHD;EACE,kBAAA;EDnHD;AC0HD;EACE,8BAAA;EACA,iCAAA;UAAA,yBAAA;EACA,WAAA;EDxHD;AC+HD;EACE,gBAAA;ED7HD;ACoID;;;;EAIE,mCAAA;EACA,gBAAA;EDlID;ACoJD;;;;;EAKE,gBAAA;EACA,eAAA;EACA,WAAA;EDlJD;ACyJD;EACE,mBAAA;EDvJD;ACiKD;;EAEE,sBAAA;ED/JD;AC0KD;;;;EAIE,4BAAA;EACA,iBAAA;EDxKD;AC+KD;;EAEE,iBAAA;ED7KD;ACoLD;;EAEE,WAAA;EACA,YAAA;EDlLD;AC0LD;EACE,qBAAA;EDxLD;ACmMD;;EAEE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,YAAA;EDjMD;AC0MD;;EAEE,cAAA;EDxMD;ACiND;EACE,+BAAA;EACA,8BAAA;EACA,iCAAA;EACA,yBAAA;ED/MD;ACwND;;EAEE,0BAAA;EDtND;AC6ND;EACE,2BAAA;EACA,eAAA;EACA,gCAAA;ED3ND;ACmOD;EACE,WAAA;EACA,YAAA;EDjOD;ACwOD;EACE,gBAAA;EDtOD;AC8OD;EACE,mBAAA;ED5OD;ACsPD;EACE,2BAAA;EACA,mBAAA;EDpPD;ACuPD;;EAEE,YAAA;EDrPD;AACD,sFAAqF;AE1ErF;EAnGI;;;IAGI,oCAAA;IACA,wBAAA;IACA,qCAAA;YAAA,6BAAA;IACA,8BAAA;IFgLL;EE7KC;;IAEI,4BAAA;IF+KL;EE5KC;IACI,8BAAA;IF8KL;EE3KC;IACI,+BAAA;IF6KL;EExKC;;IAEI,aAAA;IF0KL;EEvKC;;IAEI,wBAAA;IACA,0BAAA;IFyKL;EEtKC;IACI,6BAAA;IFwKL;EErKC;;IAEI,0BAAA;IFuKL;EEpKC;IACI,4BAAA;IFsKL;EEnKC;;;IAGI,YAAA;IACA,WAAA;IFqKL;EElKC;;IAEI,yBAAA;IFoKL;EE7JC;IACI,6BAAA;IF+JL;EE3JC;IACI,eAAA;IF6JL;EE3JC;;IAGQ,mCAAA;IF4JT;EEzJC;IACI,wBAAA;IF2JL;EExJC;IACI,sCAAA;IF0JL;EE3JC;;IAKQ,mCAAA;IF0JT;EEvJC;;IAGQ,mCAAA;IFwJT;EACF;AGpPD;EACE,qCAAA;EACA,uDAAA;EACA,iYAAA;EHsPD;AG9OD;EACE,oBAAA;EACA,UAAA;EACA,uBAAA;EACA,qCAAA;EACA,oBAAA;EACA,qBAAA;EACA,gBAAA;EACA,qCAAA;EACA,oCAAA;EHgPD;AG5OmC;EAAW,gBAAA;EH+O9C;AG9OmC;EAAW,gBAAA;EHiP9C;AG/OmC;;EAAW,kBAAA;EHmP9C;AGlPmC;EAAW,kBAAA;EHqP9C;AGpPmC;EAAW,kBAAA;EHuP9C;AGtPmC;EAAW,kBAAA;EHyP9C;AGxPmC;EAAW,kBAAA;EH2P9C;AG1PmC;EAAW,kBAAA;EH6P9C;AG5PmC;EAAW,kBAAA;EH+P9C;AG9PmC;EAAW,kBAAA;EHiQ9C;AGhQmC;EAAW,kBAAA;EHmQ9C;AGlQmC;EAAW,kBAAA;EHqQ9C;AGpQmC;EAAW,kBAAA;EHuQ9C;AGtQmC;EAAW,kBAAA;EHyQ9C;AGxQmC;EAAW,kBAAA;EH2Q9C;AG1QmC;EAAW,kBAAA;EH6Q9C;AG5QmC;EAAW,kBAAA;EH+Q9C;AG9QmC;EAAW,kBAAA;EHiR9C;AGhRmC;EAAW,kBAAA;EHmR9C;AGlRmC;EAAW,kBAAA;EHqR9C;AGpRmC;EAAW,kBAAA;EHuR9C;AGtRmC;EAAW,kBAAA;EHyR9C;AGxRmC;EAAW,kBAAA;EH2R9C;AG1RmC;EAAW,kBAAA;EH6R9C;AG5RmC;EAAW,kBAAA;EH+R9C;AG9RmC;EAAW,kBAAA;EHiS9C;AGhSmC;EAAW,kBAAA;EHmS9C;AGlSmC;EAAW,kBAAA;EHqS9C;AGpSmC;EAAW,kBAAA;EHuS9C;AGtSmC;EAAW,kBAAA;EHyS9C;AGxSmC;EAAW,kBAAA;EH2S9C;AG1SmC;EAAW,kBAAA;EH6S9C;AG5SmC;EAAW,kBAAA;EH+S9C;AG9SmC;EAAW,kBAAA;EHiT9C;AGhTmC;EAAW,kBAAA;EHmT9C;AGlTmC;EAAW,kBAAA;EHqT9C;AGpTmC;EAAW,kBAAA;EHuT9C;AGtTmC;EAAW,kBAAA;EHyT9C;AGxTmC;EAAW,kBAAA;EH2T9C;AG1TmC;EAAW,kBAAA;EH6T9C;AG5TmC;EAAW,kBAAA;EH+T9C;AG9TmC;EAAW,kBAAA;EHiU9C;AGhUmC;EAAW,kBAAA;EHmU9C;AGlUmC;EAAW,kBAAA;EHqU9C;AGpUmC;EAAW,kBAAA;EHuU9C;AGtUmC;EAAW,kBAAA;EHyU9C;AGxUmC;EAAW,kBAAA;EH2U9C;AG1UmC;EAAW,kBAAA;EH6U9C;AG5UmC;EAAW,kBAAA;EH+U9C;AG9UmC;EAAW,kBAAA;EHiV9C;AGhVmC;EAAW,kBAAA;EHmV9C;AGlVmC;EAAW,kBAAA;EHqV9C;AGpVmC;EAAW,kBAAA;EHuV9C;AGtVmC;EAAW,kBAAA;EHyV9C;AGxVmC;EAAW,kBAAA;EH2V9C;AG1VmC;EAAW,kBAAA;EH6V9C;AG5VmC;EAAW,kBAAA;EH+V9C;AG9VmC;EAAW,kBAAA;EHiW9C;AGhWmC;EAAW,kBAAA;EHmW9C;AGlWmC;EAAW,kBAAA;EHqW9C;AGpWmC;EAAW,kBAAA;EHuW9C;AGtWmC;EAAW,kBAAA;EHyW9C;AGxWmC;EAAW,kBAAA;EH2W9C;AG1WmC;EAAW,kBAAA;EH6W9C;AG5WmC;EAAW,kBAAA;EH+W9C;AG9WmC;EAAW,kBAAA;EHiX9C;AGhXmC;EAAW,kBAAA;EHmX9C;AGlXmC;EAAW,kBAAA;EHqX9C;AGpXmC;EAAW,kBAAA;EHuX9C;AGtXmC;EAAW,kBAAA;EHyX9C;AGxXmC;EAAW,kBAAA;EH2X9C;AG1XmC;EAAW,kBAAA;EH6X9C;AG5XmC;EAAW,kBAAA;EH+X9C;AG9XmC;EAAW,kBAAA;EHiY9C;AGhYmC;EAAW,kBAAA;EHmY9C;AGlYmC;EAAW,kBAAA;EHqY9C;AGpYmC;EAAW,kBAAA;EHuY9C;AGtYmC;EAAW,kBAAA;EHyY9C;AGxYmC;EAAW,kBAAA;EH2Y9C;AG1YmC;EAAW,kBAAA;EH6Y9C;AG5YmC;EAAW,kBAAA;EH+Y9C;AG9YmC;EAAW,kBAAA;EHiZ9C;AGhZmC;EAAW,kBAAA;EHmZ9C;AGlZmC;EAAW,kBAAA;EHqZ9C;AGpZmC;EAAW,kBAAA;EHuZ9C;AGtZmC;EAAW,kBAAA;EHyZ9C;AGxZmC;EAAW,kBAAA;EH2Z9C;AG1ZmC;EAAW,kBAAA;EH6Z9C;AG5ZmC;EAAW,kBAAA;EH+Z9C;AG9ZmC;EAAW,kBAAA;EHia9C;AGhamC;EAAW,kBAAA;EHma9C;AGlamC;EAAW,kBAAA;EHqa9C;AGpamC;EAAW,kBAAA;EHua9C;AGtamC;EAAW,kBAAA;EHya9C;AGxamC;EAAW,kBAAA;EH2a9C;AG1amC;EAAW,kBAAA;EH6a9C;AG5amC;EAAW,kBAAA;EH+a9C;AG9amC;EAAW,kBAAA;EHib9C;AGhbmC;EAAW,kBAAA;EHmb9C;AGlbmC;EAAW,kBAAA;EHqb9C;AGpbmC;EAAW,kBAAA;EHub9C;AGtbmC;EAAW,kBAAA;EHyb9C;AGxbmC;EAAW,kBAAA;EH2b9C;AG1bmC;EAAW,kBAAA;EH6b9C;AG5bmC;EAAW,kBAAA;EH+b9C;AG9bmC;EAAW,kBAAA;EHic9C;AGhcmC;EAAW,kBAAA;EHmc9C;AGlcmC;EAAW,kBAAA;EHqc9C;AGpcmC;EAAW,kBAAA;EHuc9C;AGtcmC;EAAW,kBAAA;EHyc9C;AGxcmC;EAAW,kBAAA;EH2c9C;AG1cmC;EAAW,kBAAA;EH6c9C;AG5cmC;EAAW,kBAAA;EH+c9C;AG9cmC;EAAW,kBAAA;EHid9C;AGhdmC;EAAW,kBAAA;EHmd9C;AGldmC;EAAW,kBAAA;EHqd9C;AGpdmC;EAAW,kBAAA;EHud9C;AGtdmC;EAAW,kBAAA;EHyd9C;AGxdmC;EAAW,kBAAA;EH2d9C;AG1dmC;EAAW,kBAAA;EH6d9C;AG5dmC;EAAW,kBAAA;EH+d9C;AG9dmC;EAAW,kBAAA;EHie9C;AGhemC;EAAW,kBAAA;EHme9C;AGlemC;EAAW,kBAAA;EHqe9C;AGpemC;EAAW,kBAAA;EHue9C;AGtemC;EAAW,kBAAA;EHye9C;AGxemC;EAAW,kBAAA;EH2e9C;AG1emC;EAAW,kBAAA;EH6e9C;AG5emC;EAAW,kBAAA;EH+e9C;AG9emC;EAAW,kBAAA;EHif9C;AGhfmC;EAAW,kBAAA;EHmf9C;AGlfmC;EAAW,kBAAA;EHqf9C;AGpfmC;EAAW,kBAAA;EHuf9C;AGtfmC;EAAW,kBAAA;EHyf9C;AGxfmC;EAAW,kBAAA;EH2f9C;AG1fmC;EAAW,kBAAA;EH6f9C;AG5fmC;EAAW,kBAAA;EH+f9C;AG9fmC;EAAW,kBAAA;EHigB9C;AGhgBmC;EAAW,kBAAA;EHmgB9C;AGlgBmC;EAAW,kBAAA;EHqgB9C;AGpgBmC;EAAW,kBAAA;EHugB9C;AGtgBmC;EAAW,kBAAA;EHygB9C;AGxgBmC;EAAW,kBAAA;EH2gB9C;AG1gBmC;EAAW,kBAAA;EH6gB9C;AG5gBmC;EAAW,kBAAA;EH+gB9C;AG9gBmC;EAAW,kBAAA;EHihB9C;AGhhBmC;EAAW,kBAAA;EHmhB9C;AGlhBmC;EAAW,kBAAA;EHqhB9C;AGphBmC;EAAW,kBAAA;EHuhB9C;AGthBmC;EAAW,kBAAA;EHyhB9C;AGxhBmC;EAAW,kBAAA;EH2hB9C;AG1hBmC;EAAW,kBAAA;EH6hB9C;AG5hBmC;EAAW,kBAAA;EH+hB9C;AG9hBmC;EAAW,kBAAA;EHiiB9C;AGhiBmC;EAAW,kBAAA;EHmiB9C;AGliBmC;EAAW,kBAAA;EHqiB9C;AGpiBmC;EAAW,kBAAA;EHuiB9C;AGtiBmC;EAAW,kBAAA;EHyiB9C;AGxiBmC;EAAW,kBAAA;EH2iB9C;AG1iBmC;EAAW,kBAAA;EH6iB9C;AG5iBmC;EAAW,kBAAA;EH+iB9C;AG9iBmC;EAAW,kBAAA;EHijB9C;AGhjBmC;EAAW,kBAAA;EHmjB9C;AGljBmC;EAAW,kBAAA;EHqjB9C;AGpjBmC;EAAW,kBAAA;EHujB9C;AGtjBmC;EAAW,kBAAA;EHyjB9C;AGxjBmC;EAAW,kBAAA;EH2jB9C;AG1jBmC;EAAW,kBAAA;EH6jB9C;AG5jBmC;EAAW,kBAAA;EH+jB9C;AG9jBmC;EAAW,kBAAA;EHikB9C;AGhkBmC;EAAW,kBAAA;EHmkB9C;AGlkBmC;EAAW,kBAAA;EHqkB9C;AGpkBmC;EAAW,kBAAA;EHukB9C;AGtkBmC;EAAW,kBAAA;EHykB9C;AGxkBmC;EAAW,kBAAA;EH2kB9C;AG1kBmC;EAAW,kBAAA;EH6kB9C;AG5kBmC;EAAW,kBAAA;EH+kB9C;AG9kBmC;EAAW,kBAAA;EHilB9C;AGhlBmC;EAAW,kBAAA;EHmlB9C;AGllBmC;EAAW,kBAAA;EHqlB9C;AGplBmC;EAAW,kBAAA;EHulB9C;AGtlBmC;EAAW,kBAAA;EHylB9C;AGxlBmC;EAAW,kBAAA;EH2lB9C;AG1lBmC;EAAW,kBAAA;EH6lB9C;AG5lBmC;EAAW,kBAAA;EH+lB9C;AG9lBmC;EAAW,kBAAA;EHimB9C;AGhmBmC;EAAW,kBAAA;EHmmB9C;AGlmBmC;EAAW,kBAAA;EHqmB9C;AGpmBmC;EAAW,kBAAA;EHumB9C;AGtmBmC;EAAW,kBAAA;EHymB9C;AGxmBmC;EAAW,kBAAA;EH2mB9C;AG1mBmC;EAAW,kBAAA;EH6mB9C;AG5mBmC;EAAW,kBAAA;EH+mB9C;AG9mBmC;EAAW,kBAAA;EHinB9C;AGhnBmC;EAAW,kBAAA;EHmnB9C;AGlnBmC;EAAW,kBAAA;EHqnB9C;AGpnBmC;EAAW,kBAAA;EHunB9C;AGtnBmC;EAAW,kBAAA;EHynB9C;AGxnBmC;EAAW,kBAAA;EH2nB9C;AG1nBmC;EAAW,kBAAA;EH6nB9C;AG5nBmC;EAAW,kBAAA;EH+nB9C;AG9nBmC;EAAW,kBAAA;EHioB9C;AGhoBmC;EAAW,kBAAA;EHmoB9C;AGloBmC;EAAW,kBAAA;EHqoB9C;AGpoBmC;EAAW,kBAAA;EHuoB9C;AGtoBmC;EAAW,kBAAA;EHyoB9C;AGhoBmC;EAAW,kBAAA;EHmoB9C;AGloBmC;EAAW,kBAAA;EHqoB9C;AGpoBmC;EAAW,kBAAA;EHuoB9C;AGtoBmC;EAAW,kBAAA;EHyoB9C;AGxoBmC;EAAW,kBAAA;EH2oB9C;AG1oBmC;EAAW,kBAAA;EH6oB9C;AG5oBmC;EAAW,kBAAA;EH+oB9C;AG9oBmC;EAAW,kBAAA;EHipB9C;AGhpBmC;EAAW,kBAAA;EHmpB9C;AGlpBmC;EAAW,kBAAA;EHqpB9C;AGppBmC;EAAW,kBAAA;EHupB9C;AGtpBmC;EAAW,kBAAA;EHypB9C;AGxpBmC;EAAW,kBAAA;EH2pB9C;AG1pBmC;EAAW,kBAAA;EH6pB9C;AG5pBmC;EAAW,kBAAA;EH+pB9C;AG9pBmC;EAAW,kBAAA;EHiqB9C;AGhqBmC;EAAW,kBAAA;EHmqB9C;AGlqBmC;EAAW,kBAAA;EHqqB9C;AGpqBmC;EAAW,kBAAA;EHuqB9C;AGtqBmC;EAAW,kBAAA;EHyqB9C;AGxqBmC;EAAW,kBAAA;EH2qB9C;AG1qBmC;EAAW,kBAAA;EH6qB9C;AG5qBmC;EAAW,kBAAA;EH+qB9C;AG9qBmC;EAAW,kBAAA;EHirB9C;AGhrBmC;EAAW,kBAAA;EHmrB9C;AGlrBmC;EAAW,kBAAA;EHqrB9C;AGprBmC;EAAW,kBAAA;EHurB9C;AGtrBmC;EAAW,kBAAA;EHyrB9C;AGxrBmC;EAAW,kBAAA;EH2rB9C;AG1rBmC;EAAW,kBAAA;EH6rB9C;AG5rBmC;EAAW,kBAAA;EH+rB9C;AG9rBmC;EAAW,kBAAA;EHisB9C;AGhsBmC;EAAW,kBAAA;EHmsB9C;AGlsBmC;EAAW,kBAAA;EHqsB9C;AGpsBmC;EAAW,kBAAA;EHusB9C;AGtsBmC;EAAW,kBAAA;EHysB9C;AGxsBmC;EAAW,kBAAA;EH2sB9C;AG1sBmC;EAAW,kBAAA;EH6sB9C;AG5sBmC;EAAW,kBAAA;EH+sB9C;AG9sBmC;EAAW,kBAAA;EHitB9C;AGhtBmC;EAAW,kBAAA;EHmtB9C;AGltBmC;EAAW,kBAAA;EHqtB9C;AGptBmC;EAAW,kBAAA;EHutB9C;AGttBmC;EAAW,kBAAA;EHytB9C;AGxtBmC;EAAW,kBAAA;EH2tB9C;AG1tBmC;EAAW,kBAAA;EH6tB9C;AG5tBmC;EAAW,kBAAA;EH+tB9C;AG9tBmC;EAAW,kBAAA;EHiuB9C;AGhuBmC;EAAW,kBAAA;EHmuB9C;AGluBmC;EAAW,kBAAA;EHquB9C;AGpuBmC;EAAW,kBAAA;EHuuB9C;AGtuBmC;EAAW,kBAAA;EHyuB9C;AGxuBmC;EAAW,kBAAA;EH2uB9C;AG1uBmC;EAAW,kBAAA;EH6uB9C;AG5uBmC;EAAW,kBAAA;EH+uB9C;AG9uBmC;EAAW,kBAAA;EHivB9C;AIvhCD;ECgEE,gCAAA;EACG,6BAAA;EACK,wBAAA;EL09BT;AIzhCD;;EC6DE,gCAAA;EACG,6BAAA;EACK,wBAAA;ELg+BT;AIvhCD;EACE,iBAAA;EACA,+CAAA;EJyhCD;AIthCD;EACE,6DAAA;EACA,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,2BAAA;EJwhCD;AIphCD;;;;EAIE,sBAAA;EACA,oBAAA;EACA,sBAAA;EJshCD;AIhhCD;EACE,gBAAA;EACA,uBAAA;EJkhCD;AIhhCC;;EAEE,gBAAA;EACA,4BAAA;EJkhCH;AI/gCC;EErDA,sBAAA;EAEA,4CAAA;EACA,sBAAA;ENskCD;AIzgCD;EACE,WAAA;EJ2gCD;AIrgCD;EACE,wBAAA;EJugCD;AIngCD;;;;;EGvEE,gBAAA;EACA,iBAAA;EACA,cAAA;EPilCD;AIvgCD;EACE,oBAAA;EJygCD;AIngCD;EACE,cAAA;EACA,yBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;EC6FA,0CAAA;EACK,qCAAA;EACG,kCAAA;EEvLR,uBAAA;EACA,iBAAA;EACA,cAAA;EPimCD;AIngCD;EACE,oBAAA;EJqgCD;AI//BD;EACE,kBAAA;EACA,qBAAA;EACA,WAAA;EACA,+BAAA;EJigCD;AIz/BD;EACE,oBAAA;EACA,YAAA;EACA,aAAA;EACA,cAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,WAAA;EJ2/BD;AIn/BC;;EAEE,kBAAA;EACA,aAAA;EACA,cAAA;EACA,WAAA;EACA,mBAAA;EACA,YAAA;EJq/BH;AIz+BD;EACE,iBAAA;EJ2+BD;AQnoCD;;;;;;;;;;;;EAEE,sBAAA;EACA,kBAAA;EACA,kBAAA;EACA,gBAAA;ER+oCD;AQppCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,qBAAA;EACA,gBAAA;EACA,gBAAA;ERqqCH;AQjqCD;;;;;;EAGE,kBAAA;EACA,qBAAA;ERsqCD;AQ1qCD;;;;;;;;;;;;EAQI,gBAAA;ERgrCH;AQ7qCD;;;;;;EAGE,kBAAA;EACA,qBAAA;ERkrCD;AQtrCD;;;;;;;;;;;;EAQI,gBAAA;ER4rCH;AQxrCD;;EAAU,iBAAA;ER4rCT;AQ3rCD;;EAAU,iBAAA;ER+rCT;AQ9rCD;;EAAU,iBAAA;ERksCT;AQjsCD;;EAAU,iBAAA;ERqsCT;AQpsCD;;EAAU,iBAAA;ERwsCT;AQvsCD;;EAAU,iBAAA;ER2sCT;AQrsCD;EACE,kBAAA;ERusCD;AQpsCD;EACE,qBAAA;EACA,iBAAA;EACA,kBAAA;EACA,kBAAA;ERssCD;AQjsCD;EAAA;IAFI,iBAAA;IRusCD;EACF;AQ/rCD;;EAEE,gBAAA;ERisCD;AQ9rCD;;EAEE,2BAAA;EACA,eAAA;ERgsCD;AQ5rCD;EAAuB,kBAAA;ER+rCtB;AQ9rCD;EAAuB,mBAAA;ERisCtB;AQhsCD;EAAuB,oBAAA;ERmsCtB;AQlsCD;EAAuB,qBAAA;ERqsCtB;AQpsCD;EAAuB,qBAAA;ERusCtB;AQpsCD;EAAuB,2BAAA;ERusCtB;AQtsCD;EAAuB,2BAAA;ERysCtB;AQxsCD;EAAuB,4BAAA;ER2sCtB;AQxsCD;EACE,gBAAA;ER0sCD;AQxsCD;ECrGE,gBAAA;ETgzCD;AS/yCC;EACE,gBAAA;ETizCH;AQ3sCD;ECxGE,gBAAA;ETszCD;ASrzCC;EACE,gBAAA;ETuzCH;AQ9sCD;EC3GE,gBAAA;ET4zCD;AS3zCC;EACE,gBAAA;ET6zCH;AQjtCD;EC9GE,gBAAA;ETk0CD;ASj0CC;EACE,gBAAA;ETm0CH;AQptCD;ECjHE,gBAAA;ETw0CD;ASv0CC;EACE,gBAAA;ETy0CH;AQntCD;EAGE,aAAA;EE3HA,2BAAA;EV+0CD;AU90CC;EACE,2BAAA;EVg1CH;AQptCD;EE9HE,2BAAA;EVq1CD;AUp1CC;EACE,2BAAA;EVs1CH;AQvtCD;EEjIE,2BAAA;EV21CD;AU11CC;EACE,2BAAA;EV41CH;AQ1tCD;EEpIE,2BAAA;EVi2CD;AUh2CC;EACE,2BAAA;EVk2CH;AQ7tCD;EEvIE,2BAAA;EVu2CD;AUt2CC;EACE,2BAAA;EVw2CH;AQ3tCD;EACE,qBAAA;EACA,qBAAA;EACA,kCAAA;ER6tCD;AQrtCD;;EAEE,eAAA;EACA,qBAAA;ERutCD;AQ1tCD;;;;EAMI,kBAAA;ER0tCH;AQntCD;EACE,iBAAA;EACA,kBAAA;ERqtCD;AQjtCD;EALE,iBAAA;EACA,kBAAA;EAMA,mBAAA;ERotCD;AQttCD;EAKI,uBAAA;EACA,mBAAA;EACA,oBAAA;ERotCH;AQ/sCD;EACE,eAAA;EACA,qBAAA;ERitCD;AQ/sCD;;EAEE,yBAAA;ERitCD;AQ/sCD;EACE,mBAAA;ERitCD;AQ/sCD;EACE,gBAAA;ERitCD;AQxrCD;EAAA;IAVM,aAAA;IACA,cAAA;IACA,aAAA;IACA,mBAAA;IGtNJ,kBAAA;IACA,yBAAA;IACA,qBAAA;IX65CC;EQlsCH;IAHM,oBAAA;IRwsCH;EACF;AQ/rCD;;EAGE,cAAA;EACA,mCAAA;ERgsCD;AQ9rCD;EACE,gBAAA;EA9IqB,2BAAA;ER+0CtB;AQ5rCD;EACE,oBAAA;EACA,kBAAA;EACA,mBAAA;EACA,gCAAA;ER8rCD;AQzrCG;;;EACE,kBAAA;ER6rCL;AQvsCD;;;EAmBI,gBAAA;EACA,gBAAA;EACA,yBAAA;EACA,gBAAA;ERyrCH;AQvrCG;;;EACE,wBAAA;ER2rCL;AQnrCD;;EAEE,qBAAA;EACA,iBAAA;EACA,iCAAA;EACA,gBAAA;EACA,mBAAA;ERqrCD;AQ/qCG;;;;;;EAAW,aAAA;ERurCd;AQtrCG;;;;;;EACE,wBAAA;ER6rCL;AQvrCD;EACE,qBAAA;EACA,oBAAA;EACA,yBAAA;ERyrCD;AY/9CD;;;;EAIE,gEAAA;EZi+CD;AY79CD;EACE,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,2BAAA;EACA,oBAAA;EZ+9CD;AY39CD;EACE,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,2BAAA;EACA,oBAAA;EACA,wDAAA;UAAA,gDAAA;EZ69CD;AYn+CD;EASI,YAAA;EACA,iBAAA;EACA,mBAAA;EACA,0BAAA;UAAA,kBAAA;EZ69CH;AYx9CD;EACE,gBAAA;EACA,gBAAA;EACA,kBAAA;EACA,iBAAA;EACA,yBAAA;EACA,uBAAA;EACA,uBAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;EZ09CD;AYr+CD;EAeI,YAAA;EACA,oBAAA;EACA,gBAAA;EACA,uBAAA;EACA,+BAAA;EACA,kBAAA;EZy9CH;AYp9CD;EACE,mBAAA;EACA,oBAAA;EZs9CD;AahhDD;ECHE,oBAAA;EACA,mBAAA;EACA,oBAAA;EACA,qBAAA;EdshDD;AahhDC;EAAA;IAFE,cAAA;IbshDD;EACF;AalhDC;EAAA;IAFE,cAAA;IbwhDD;EACF;AaphDD;EAAA;IAFI,eAAA;Ib0hDD;EACF;AajhDD;ECvBE,oBAAA;EACA,mBAAA;EACA,oBAAA;EACA,qBAAA;Ed2iDD;Aa9gDD;ECvBE,oBAAA;EACA,qBAAA;EdwiDD;AexiDG;EACE,oBAAA;EAEA,iBAAA;EAEA,oBAAA;EACA,qBAAA;EfwiDL;AexhDG;EACE,aAAA;Ef0hDL;AenhDC;EACE,aAAA;EfqhDH;AethDC;EACE,qBAAA;EfwhDH;AezhDC;EACE,qBAAA;Ef2hDH;Ae5hDC;EACE,YAAA;Ef8hDH;Ae/hDC;EACE,qBAAA;EfiiDH;AeliDC;EACE,qBAAA;EfoiDH;AeriDC;EACE,YAAA;EfuiDH;AexiDC;EACE,qBAAA;Ef0iDH;Ae3iDC;EACE,qBAAA;Ef6iDH;Ae9iDC;EACE,YAAA;EfgjDH;AejjDC;EACE,qBAAA;EfmjDH;AepjDC;EACE,oBAAA;EfsjDH;AexiDC;EACE,aAAA;Ef0iDH;Ae3iDC;EACE,qBAAA;Ef6iDH;Ae9iDC;EACE,qBAAA;EfgjDH;AejjDC;EACE,YAAA;EfmjDH;AepjDC;EACE,qBAAA;EfsjDH;AevjDC;EACE,qBAAA;EfyjDH;Ae1jDC;EACE,YAAA;Ef4jDH;Ae7jDC;EACE,qBAAA;Ef+jDH;AehkDC;EACE,qBAAA;EfkkDH;AenkDC;EACE,YAAA;EfqkDH;AetkDC;EACE,qBAAA;EfwkDH;AezkDC;EACE,oBAAA;Ef2kDH;AevkDC;EACE,aAAA;EfykDH;AezlDC;EACE,YAAA;Ef2lDH;Ae5lDC;EACE,oBAAA;Ef8lDH;Ae/lDC;EACE,oBAAA;EfimDH;AelmDC;EACE,WAAA;EfomDH;AermDC;EACE,oBAAA;EfumDH;AexmDC;EACE,oBAAA;Ef0mDH;Ae3mDC;EACE,WAAA;Ef6mDH;Ae9mDC;EACE,oBAAA;EfgnDH;AejnDC;EACE,oBAAA;EfmnDH;AepnDC;EACE,WAAA;EfsnDH;AevnDC;EACE,oBAAA;EfynDH;Ae1nDC;EACE,mBAAA;Ef4nDH;AexnDC;EACE,YAAA;Ef0nDH;Ae5mDC;EACE,mBAAA;Ef8mDH;Ae/mDC;EACE,2BAAA;EfinDH;AelnDC;EACE,2BAAA;EfonDH;AernDC;EACE,kBAAA;EfunDH;AexnDC;EACE,2BAAA;Ef0nDH;Ae3nDC;EACE,2BAAA;Ef6nDH;Ae9nDC;EACE,kBAAA;EfgoDH;AejoDC;EACE,2BAAA;EfmoDH;AepoDC;EACE,2BAAA;EfsoDH;AevoDC;EACE,kBAAA;EfyoDH;Ae1oDC;EACE,2BAAA;Ef4oDH;Ae7oDC;EACE,0BAAA;Ef+oDH;AehpDC;EACE,iBAAA;EfkpDH;AalpDD;EElCI;IACE,aAAA;IfurDH;EehrDD;IACE,aAAA;IfkrDD;EenrDD;IACE,qBAAA;IfqrDD;EetrDD;IACE,qBAAA;IfwrDD;EezrDD;IACE,YAAA;If2rDD;Ee5rDD;IACE,qBAAA;If8rDD;Ee/rDD;IACE,qBAAA;IfisDD;EelsDD;IACE,YAAA;IfosDD;EersDD;IACE,qBAAA;IfusDD;EexsDD;IACE,qBAAA;If0sDD;Ee3sDD;IACE,YAAA;If6sDD;Ee9sDD;IACE,qBAAA;IfgtDD;EejtDD;IACE,oBAAA;IfmtDD;EersDD;IACE,aAAA;IfusDD;EexsDD;IACE,qBAAA;If0sDD;Ee3sDD;IACE,qBAAA;If6sDD;Ee9sDD;IACE,YAAA;IfgtDD;EejtDD;IACE,qBAAA;IfmtDD;EeptDD;IACE,qBAAA;IfstDD;EevtDD;IACE,YAAA;IfytDD;Ee1tDD;IACE,qBAAA;If4tDD;Ee7tDD;IACE,qBAAA;If+tDD;EehuDD;IACE,YAAA;IfkuDD;EenuDD;IACE,qBAAA;IfquDD;EetuDD;IACE,oBAAA;IfwuDD;EepuDD;IACE,aAAA;IfsuDD;EetvDD;IACE,YAAA;IfwvDD;EezvDD;IACE,oBAAA;If2vDD;Ee5vDD;IACE,oBAAA;If8vDD;Ee/vDD;IACE,WAAA;IfiwDD;EelwDD;IACE,oBAAA;IfowDD;EerwDD;IACE,oBAAA;IfuwDD;EexwDD;IACE,WAAA;If0wDD;Ee3wDD;IACE,oBAAA;If6wDD;Ee9wDD;IACE,oBAAA;IfgxDD;EejxDD;IACE,WAAA;IfmxDD;EepxDD;IACE,oBAAA;IfsxDD;EevxDD;IACE,mBAAA;IfyxDD;EerxDD;IACE,YAAA;IfuxDD;EezwDD;IACE,mBAAA;If2wDD;Ee5wDD;IACE,2BAAA;If8wDD;Ee/wDD;IACE,2BAAA;IfixDD;EelxDD;IACE,kBAAA;IfoxDD;EerxDD;IACE,2BAAA;IfuxDD;EexxDD;IACE,2BAAA;If0xDD;Ee3xDD;IACE,kBAAA;If6xDD;Ee9xDD;IACE,2BAAA;IfgyDD;EejyDD;IACE,2BAAA;IfmyDD;EepyDD;IACE,kBAAA;IfsyDD;EevyDD;IACE,2BAAA;IfyyDD;Ee1yDD;IACE,0BAAA;If4yDD;Ee7yDD;IACE,iBAAA;If+yDD;EACF;AavyDD;EE3CI;IACE,aAAA;Ifq1DH;Ee90DD;IACE,aAAA;Ifg1DD;Eej1DD;IACE,qBAAA;Ifm1DD;Eep1DD;IACE,qBAAA;Ifs1DD;Eev1DD;IACE,YAAA;Ify1DD;Ee11DD;IACE,qBAAA;If41DD;Ee71DD;IACE,qBAAA;If+1DD;Eeh2DD;IACE,YAAA;Ifk2DD;Een2DD;IACE,qBAAA;Ifq2DD;Eet2DD;IACE,qBAAA;Ifw2DD;Eez2DD;IACE,YAAA;If22DD;Ee52DD;IACE,qBAAA;If82DD;Ee/2DD;IACE,oBAAA;Ifi3DD;Een2DD;IACE,aAAA;Ifq2DD;Eet2DD;IACE,qBAAA;Ifw2DD;Eez2DD;IACE,qBAAA;If22DD;Ee52DD;IACE,YAAA;If82DD;Ee/2DD;IACE,qBAAA;Ifi3DD;Eel3DD;IACE,qBAAA;Ifo3DD;Eer3DD;IACE,YAAA;Ifu3DD;Eex3DD;IACE,qBAAA;If03DD;Ee33DD;IACE,qBAAA;If63DD;Ee93DD;IACE,YAAA;Ifg4DD;Eej4DD;IACE,qBAAA;Ifm4DD;Eep4DD;IACE,oBAAA;Ifs4DD;Eel4DD;IACE,aAAA;Ifo4DD;Eep5DD;IACE,YAAA;Ifs5DD;Eev5DD;IACE,oBAAA;Ify5DD;Ee15DD;IACE,oBAAA;If45DD;Ee75DD;IACE,WAAA;If+5DD;Eeh6DD;IACE,oBAAA;Ifk6DD;Een6DD;IACE,oBAAA;Ifq6DD;Eet6DD;IACE,WAAA;Ifw6DD;Eez6DD;IACE,oBAAA;If26DD;Ee56DD;IACE,oBAAA;If86DD;Ee/6DD;IACE,WAAA;Ifi7DD;Eel7DD;IACE,oBAAA;Ifo7DD;Eer7DD;IACE,mBAAA;Ifu7DD;Een7DD;IACE,YAAA;Ifq7DD;Eev6DD;IACE,mBAAA;Ify6DD;Ee16DD;IACE,2BAAA;If46DD;Ee76DD;IACE,2BAAA;If+6DD;Eeh7DD;IACE,kBAAA;Ifk7DD;Een7DD;IACE,2BAAA;Ifq7DD;Eet7DD;IACE,2BAAA;Ifw7DD;Eez7DD;IACE,kBAAA;If27DD;Ee57DD;IACE,2BAAA;If87DD;Ee/7DD;IACE,2BAAA;Ifi8DD;Eel8DD;IACE,kBAAA;Ifo8DD;Eer8DD;IACE,2BAAA;Ifu8DD;Eex8DD;IACE,0BAAA;If08DD;Ee38DD;IACE,iBAAA;If68DD;EACF;Aal8DD;EE9CI;IACE,aAAA;Ifm/DH;Ee5+DD;IACE,aAAA;If8+DD;Ee/+DD;IACE,qBAAA;Ifi/DD;Eel/DD;IACE,qBAAA;Ifo/DD;Eer/DD;IACE,YAAA;Ifu/DD;Eex/DD;IACE,qBAAA;If0/DD;Ee3/DD;IACE,qBAAA;If6/DD;Ee9/DD;IACE,YAAA;IfggED;EejgED;IACE,qBAAA;IfmgED;EepgED;IACE,qBAAA;IfsgED;EevgED;IACE,YAAA;IfygED;Ee1gED;IACE,qBAAA;If4gED;Ee7gED;IACE,oBAAA;If+gED;EejgED;IACE,aAAA;IfmgED;EepgED;IACE,qBAAA;IfsgED;EevgED;IACE,qBAAA;IfygED;Ee1gED;IACE,YAAA;If4gED;Ee7gED;IACE,qBAAA;If+gED;EehhED;IACE,qBAAA;IfkhED;EenhED;IACE,YAAA;IfqhED;EethED;IACE,qBAAA;IfwhED;EezhED;IACE,qBAAA;If2hED;Ee5hED;IACE,YAAA;If8hED;Ee/hED;IACE,qBAAA;IfiiED;EeliED;IACE,oBAAA;IfoiED;EehiED;IACE,aAAA;IfkiED;EeljED;IACE,YAAA;IfojED;EerjED;IACE,oBAAA;IfujED;EexjED;IACE,oBAAA;If0jED;Ee3jED;IACE,WAAA;If6jED;Ee9jED;IACE,oBAAA;IfgkED;EejkED;IACE,oBAAA;IfmkED;EepkED;IACE,WAAA;IfskED;EevkED;IACE,oBAAA;IfykED;Ee1kED;IACE,oBAAA;If4kED;Ee7kED;IACE,WAAA;If+kED;EehlED;IACE,oBAAA;IfklED;EenlED;IACE,mBAAA;IfqlED;EejlED;IACE,YAAA;IfmlED;EerkED;IACE,mBAAA;IfukED;EexkED;IACE,2BAAA;If0kED;Ee3kED;IACE,2BAAA;If6kED;Ee9kED;IACE,kBAAA;IfglED;EejlED;IACE,2BAAA;IfmlED;EeplED;IACE,2BAAA;IfslED;EevlED;IACE,kBAAA;IfylED;Ee1lED;IACE,2BAAA;If4lED;Ee7lED;IACE,2BAAA;If+lED;EehmED;IACE,kBAAA;IfkmED;EenmED;IACE,2BAAA;IfqmED;EetmED;IACE,0BAAA;IfwmED;EezmED;IACE,iBAAA;If2mED;EACF;AgB/qED;EACE,+BAAA;EhBirED;AgB/qED;EACE,kBAAA;EACA,qBAAA;EACA,gBAAA;EACA,kBAAA;EhBirED;AgB/qED;EACE,kBAAA;EhBirED;AgB3qED;EACE,aAAA;EACA,iBAAA;EACA,qBAAA;EhB6qED;AgBhrED;;;;;;EAWQ,cAAA;EACA,yBAAA;EACA,qBAAA;EACA,+BAAA;EhB6qEP;AgB3rED;EAoBI,wBAAA;EACA,kCAAA;EhB0qEH;AgB/rED;;;;;;EA8BQ,eAAA;EhByqEP;AgBvsED;EAoCI,+BAAA;EhBsqEH;AgB1sED;EAyCI,2BAAA;EhBoqEH;AgB7pED;;;;;;EAOQ,cAAA;EhB8pEP;AgBnpED;EACE,2BAAA;EhBqpED;AgBtpED;;;;;;EAQQ,2BAAA;EhBspEP;AgB9pED;;EAeM,0BAAA;EhBmpEL;AgBzoED;EAEI,2BAAA;EhB0oEH;AgBjoED;EAEI,2BAAA;EhBkoEH;AgBznED;EACE,kBAAA;EACA,aAAA;EACA,uBAAA;EhB2nED;AgBtnEG;;EACE,kBAAA;EACA,aAAA;EACA,qBAAA;EhBynEL;AiBrwEC;;;;;;;;;;;;EAOI,2BAAA;EjB4wEL;AiBtwEC;;;;;EAMI,2BAAA;EjBuwEL;AiB1xEC;;;;;;;;;;;;EAOI,2BAAA;EjBiyEL;AiB3xEC;;;;;EAMI,2BAAA;EjB4xEL;AiB/yEC;;;;;;;;;;;;EAOI,2BAAA;EjBszEL;AiBhzEC;;;;;EAMI,2BAAA;EjBizEL;AiBp0EC;;;;;;;;;;;;EAOI,2BAAA;EjB20EL;AiBr0EC;;;;;EAMI,2BAAA;EjBs0EL;AiBz1EC;;;;;;;;;;;;EAOI,2BAAA;EjBg2EL;AiB11EC;;;;;EAMI,2BAAA;EjB21EL;AgBzsED;EACE,kBAAA;EACA,mBAAA;EhB2sED;AgB9oED;EAAA;IA1DI,aAAA;IACA,qBAAA;IACA,oBAAA;IACA,8CAAA;IACA,2BAAA;IhB4sED;EgBtpEH;IAlDM,kBAAA;IhB2sEH;EgBzpEH;;;;;;IAzCY,qBAAA;IhB0sET;EgBjqEH;IAjCM,WAAA;IhBqsEH;EgBpqEH;;;;;;IAxBY,gBAAA;IhBosET;EgB5qEH;;;;;;IApBY,iBAAA;IhBwsET;EgBprEH;;;;IAPY,kBAAA;IhBisET;EACF;AkB35ED;EACE,YAAA;EACA,WAAA;EACA,WAAA;EAIA,cAAA;ElB05ED;AkBv5ED;EACE,gBAAA;EACA,aAAA;EACA,YAAA;EACA,qBAAA;EACA,iBAAA;EACA,sBAAA;EACA,gBAAA;EACA,WAAA;EACA,kCAAA;ElBy5ED;AkBt5ED;EACE,uBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;ElBw5ED;AkB74ED;Eb4BE,gCAAA;EACG,6BAAA;EACK,wBAAA;ELo3ET;AkB74ED;;EAEE,iBAAA;EACA,oBAAA;EACA,qBAAA;ElB+4ED;AkB34ED;EACE,gBAAA;ElB64ED;AkBz4ED;EACE,gBAAA;EACA,aAAA;ElB24ED;AkBv4ED;;EAEE,cAAA;ElBy4ED;AkBr4ED;;;EZxEE,sBAAA;EAEA,4CAAA;EACA,sBAAA;ENi9ED;AkBr4ED;EACE,gBAAA;EACA,kBAAA;EACA,iBAAA;EACA,yBAAA;EACA,gBAAA;ElBu4ED;AkB72ED;EACE,gBAAA;EACA,aAAA;EACA,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,2BAAA;EACA,wBAAA;EACA,2BAAA;EACA,oBAAA;EbzDA,0DAAA;EACQ,kDAAA;EAyHR,wFAAA;EACK,2EAAA;EACG,wEAAA;ELizET;AmBz7EC;EACE,uBAAA;EACA,YAAA;EdUF,wFAAA;EACQ,gFAAA;ELk7ET;AKj5EC;EACE,gBAAA;EACA,YAAA;ELm5EH;AKj5EC;EAA0B,gBAAA;ELo5E3B;AKn5EC;EAAgC,gBAAA;ELs5EjC;AkBr3EC;;;EAGE,2BAAA;EACA,YAAA;ElBu3EH;AkBp3EC;;EAEE,qBAAA;ElBs3EH;AkBl3EC;EACE,cAAA;ElBo3EH;AkBx2ED;EACE,0BAAA;ElB02ED;AkBt0ED;EAxBE;;;;IAIE,mBAAA;IlBi2ED;EkB/1EC;;;;;;;;IAEE,mBAAA;IlBu2EH;EkBp2EC;;;;;;;;IAEE,mBAAA;IlB42EH;EACF;AkBl2ED;EACE,qBAAA;ElBo2ED;AkB51ED;;EAEE,oBAAA;EACA,gBAAA;EACA,kBAAA;EACA,qBAAA;ElB81ED;AkBn2ED;;EAQI,kBAAA;EACA,oBAAA;EACA,kBAAA;EACA,qBAAA;EACA,iBAAA;ElB+1EH;AkB51ED;;;;EAIE,oBAAA;EACA,oBAAA;EACA,oBAAA;ElB81ED;AkB31ED;;EAEE,kBAAA;ElB61ED;AkBz1ED;;EAEE,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,kBAAA;EACA,wBAAA;EACA,qBAAA;EACA,iBAAA;ElB21ED;AkBz1ED;;EAEE,eAAA;EACA,mBAAA;ElB21ED;AkBl1EC;;;;;;EAGE,qBAAA;ElBu1EH;AkBj1EC;;;;EAEE,qBAAA;ElBq1EH;AkB/0EC;;;;EAGI,qBAAA;ElBk1EL;AkBv0ED;EAEE,kBAAA;EACA,qBAAA;EAEA,kBAAA;EACA,kBAAA;ElBu0ED;AkBr0EC;;EAEE,iBAAA;EACA,kBAAA;ElBu0EH;AkB1zED;EC1PE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;EnBujFD;AmBrjFC;EACE,cAAA;EACA,mBAAA;EnBujFH;AmBpjFC;;EAEE,cAAA;EnBsjFH;AkBt0ED;EC7PE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;EnBskFD;AmBpkFC;EACE,cAAA;EACA,mBAAA;EnBskFH;AmBnkFC;;EAEE,cAAA;EnBqkFH;AkBr1ED;EAKI,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,kBAAA;ElBm1EH;AkB/0ED;EC1QE,cAAA;EACA,oBAAA;EACA,iBAAA;EACA,wBAAA;EACA,oBAAA;EnB4lFD;AmB1lFC;EACE,cAAA;EACA,mBAAA;EnB4lFH;AmBzlFC;;EAEE,cAAA;EnB2lFH;AkB31ED;EC7QE,cAAA;EACA,oBAAA;EACA,iBAAA;EACA,wBAAA;EACA,oBAAA;EnB2mFD;AmBzmFC;EACE,cAAA;EACA,mBAAA;EnB2mFH;AmBxmFC;;EAEE,cAAA;EnB0mFH;AkB12ED;EAKI,cAAA;EACA,oBAAA;EACA,iBAAA;EACA,wBAAA;EACA,kBAAA;ElBw2EH;AkB/1ED;EAEE,oBAAA;ElBg2ED;AkBl2ED;EAMI,uBAAA;ElB+1EH;AkB31ED;EACE,oBAAA;EACA,QAAA;EACA,UAAA;EACA,YAAA;EACA,gBAAA;EACA,aAAA;EACA,cAAA;EACA,mBAAA;EACA,oBAAA;EACA,sBAAA;ElB61ED;AkB31ED;EACE,aAAA;EACA,cAAA;EACA,mBAAA;ElB61ED;AkB31ED;EACE,aAAA;EACA,cAAA;EACA,mBAAA;ElB61ED;AkBz1ED;;;;;;;;;;ECrXI,gBAAA;EnB0tFH;AkBr2ED;ECjXI,uBAAA;Ed+CF,0DAAA;EACQ,kDAAA;EL2qFT;AmBztFG;EACE,uBAAA;Ed4CJ,2EAAA;EACQ,mEAAA;ELgrFT;AkB/2ED;ECvWI,gBAAA;EACA,uBAAA;EACA,2BAAA;EnBytFH;AkBp3ED;ECjWI,gBAAA;EnBwtFH;AkBp3ED;;;;;;;;;;ECxXI,gBAAA;EnBwvFH;AkBh4ED;ECpXI,uBAAA;Ed+CF,0DAAA;EACQ,kDAAA;ELysFT;AmBvvFG;EACE,uBAAA;Ed4CJ,2EAAA;EACQ,mEAAA;EL8sFT;AkB14ED;EC1WI,gBAAA;EACA,uBAAA;EACA,2BAAA;EnBuvFH;AkB/4ED;ECpWI,gBAAA;EnBsvFH;AkB/4ED;;;;;;;;;;EC3XI,gBAAA;EnBsxFH;AkB35ED;ECvXI,uBAAA;Ed+CF,0DAAA;EACQ,kDAAA;ELuuFT;AmBrxFG;EACE,uBAAA;Ed4CJ,2EAAA;EACQ,mEAAA;EL4uFT;AkBr6ED;EC7WI,gBAAA;EACA,uBAAA;EACA,2BAAA;EnBqxFH;AkB16ED;ECvWI,gBAAA;EnBoxFH;AkBt6EC;EACG,WAAA;ElBw6EJ;AkBt6EC;EACG,QAAA;ElBw6EJ;AkB95ED;EACE,gBAAA;EACA,iBAAA;EACA,qBAAA;EACA,gBAAA;ElBg6ED;AkB70ED;EAAA;IA9DM,uBAAA;IACA,kBAAA;IACA,wBAAA;IlB+4EH;EkBn1EH;IAvDM,uBAAA;IACA,aAAA;IACA,wBAAA;IlB64EH;EkBx1EH;IAhDM,uBAAA;IlB24EH;EkB31EH;IA5CM,uBAAA;IACA,wBAAA;IlB04EH;EkB/1EH;;;IAtCQ,aAAA;IlB04EL;EkBp2EH;IAhCM,aAAA;IlBu4EH;EkBv2EH;IA5BM,kBAAA;IACA,wBAAA;IlBs4EH;EkB32EH;;IApBM,uBAAA;IACA,eAAA;IACA,kBAAA;IACA,wBAAA;IlBm4EH;EkBl3EH;;IAdQ,iBAAA;IlBo4EL;EkBt3EH;;IATM,oBAAA;IACA,gBAAA;IlBm4EH;EkB33EH;IAHM,QAAA;IlBi4EH;EACF;AkBv3ED;;;;EASI,eAAA;EACA,kBAAA;EACA,kBAAA;ElBo3EH;AkB/3ED;;EAiBI,kBAAA;ElBk3EH;AkBn4ED;EJjfE,oBAAA;EACA,qBAAA;Edu3FD;AkBh2EC;EAAA;IAVI,mBAAA;IACA,kBAAA;IACA,kBAAA;IlB82EH;EACF;AkB94ED;EAwCI,aAAA;ElBy2EH;AkB51EC;EAAA;IAHM,0BAAA;IlBm2EL;EACF;AkB11EC;EAAA;IAHM,kBAAA;IlBi2EL;EACF;AoBn5FD;EACE,uBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,wBAAA;EACA,gCAAA;MAAA,4BAAA;EACA,iBAAA;EACA,wBAAA;EACA,+BAAA;EACA,qBAAA;EC6BA,mBAAA;EACA,iBAAA;EACA,yBAAA;EACA,oBAAA;EhB4KA,2BAAA;EACG,wBAAA;EACC,uBAAA;EACI,mBAAA;EL8sFT;AoBt5FG;;;;;;EdrBF,sBAAA;EAEA,4CAAA;EACA,sBAAA;ENk7FD;AoB15FC;;;EAGE,gBAAA;EACA,uBAAA;EpB45FH;AoBz5FC;;EAEE,YAAA;EACA,wBAAA;Ef2BF,0DAAA;EACQ,kDAAA;ELi4FT;AoBz5FC;;;EAGE,qBAAA;EACA,sBAAA;EE9CF,eAAA;EAGA,2BAAA;EjB8DA,0BAAA;EACQ,kBAAA;EL24FT;AoBr5FD;ECrDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB68FD;AqB38FC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB68FP;AqB38FC;;;EAGE,wBAAA;ErB68FH;AqBx8FG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErBs9FT;AoB97FD;ECnBI,gBAAA;EACA,2BAAA;ErBo9FH;AoB/7FD;ECxDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB0/FD;AqBx/FC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB0/FP;AqBx/FC;;;EAGE,wBAAA;ErB0/FH;AqBr/FG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErBmgGT;AoBx+FD;ECtBI,gBAAA;EACA,2BAAA;ErBigGH;AoBx+FD;EC5DE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBuiGD;AqBriGC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBuiGP;AqBriGC;;;EAGE,wBAAA;ErBuiGH;AqBliGG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErBgjGT;AoBjhGD;EC1BI,gBAAA;EACA,2BAAA;ErB8iGH;AoBjhGD;EChEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBolGD;AqBllGC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBolGP;AqBllGC;;;EAGE,wBAAA;ErBolGH;AqB/kGG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErB6lGT;AoB1jGD;EC9BI,gBAAA;EACA,2BAAA;ErB2lGH;AoB1jGD;ECpEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBioGD;AqB/nGC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBioGP;AqB/nGC;;;EAGE,wBAAA;ErBioGH;AqB5nGG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErB0oGT;AoBnmGD;EClCI,gBAAA;EACA,2BAAA;ErBwoGH;AoBnmGD;ECxEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB8qGD;AqB5qGC;;;;;;EAME,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB8qGP;AqB5qGC;;;EAGE,wBAAA;ErB8qGH;AqBzqGG;;;;;;;;;;;;;;;;;;EAME,2BAAA;EACI,uBAAA;ErBurGT;AoB5oGD;ECtCI,gBAAA;EACA,2BAAA;ErBqrGH;AoBvoGD;EACE,gBAAA;EACA,qBAAA;EACA,kBAAA;EpByoGD;AoBvoGC;;;;;EAKE,+BAAA;Ef7BF,0BAAA;EACQ,kBAAA;ELuqGT;AoBxoGC;;;;EAIE,2BAAA;EpB0oGH;AoBxoGC;;EAEE,gBAAA;EACA,4BAAA;EACA,+BAAA;EpB0oGH;AoBtoGG;;;;EAEE,gBAAA;EACA,uBAAA;EpB0oGL;AoBjoGD;;EC/EE,oBAAA;EACA,iBAAA;EACA,wBAAA;EACA,oBAAA;ErBotGD;AoBpoGD;;ECnFE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;ErB2tGD;AoBvoGD;;ECvFE,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;ErBkuGD;AoBtoGD;EACE,gBAAA;EACA,aAAA;EpBwoGD;AoBpoGD;EACE,iBAAA;EpBsoGD;AoB/nGC;;;EACE,aAAA;EpBmoGH;AuBvxGD;EACE,YAAA;ElBoLA,0CAAA;EACK,qCAAA;EACG,kCAAA;ELsmGT;AuB1xGC;EACE,YAAA;EvB4xGH;AuBxxGD;EACE,eAAA;EvB0xGD;AuBxxGC;EAAY,gBAAA;EvB2xGb;AuB1xGC;EAAY,oBAAA;EvB6xGb;AuB5xGC;EAAY,0BAAA;EvB+xGb;AuB5xGD;EACE,oBAAA;EACA,WAAA;EACA,kBAAA;ElBuKA,iDAAA;EACQ,4CAAA;KAAA,yCAAA;EAOR,oCAAA;EACQ,+BAAA;KAAA,4BAAA;EAGR,0CAAA;EACQ,qCAAA;KAAA,kCAAA;ELgnGT;AwB1zGD;EACE,uBAAA;EACA,UAAA;EACA,WAAA;EACA,kBAAA;EACA,wBAAA;EACA,wBAAA;EACA,qCAAA;EACA,oCAAA;ExB4zGD;AwBxzGD;;EAEE,oBAAA;ExB0zGD;AwBtzGD;EACE,YAAA;ExBwzGD;AwBpzGD;EACE,oBAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,eAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,2BAAA;EACA,2BAAA;EACA,uCAAA;EACA,oBAAA;EnBuBA,qDAAA;EACQ,6CAAA;EmBtBR,sCAAA;UAAA,8BAAA;ExBuzGD;AwBlzGC;EACE,UAAA;EACA,YAAA;ExBozGH;AwB70GD;ECxBE,aAAA;EACA,eAAA;EACA,kBAAA;EACA,2BAAA;EzBw2GD;AwBn1GD;EAmCI,gBAAA;EACA,mBAAA;EACA,aAAA;EACA,qBAAA;EACA,yBAAA;EACA,gBAAA;EACA,qBAAA;ExBmzGH;AwB7yGC;;EAEE,uBAAA;EACA,gBAAA;EACA,2BAAA;ExB+yGH;AwBzyGC;;;EAGE,gBAAA;EACA,uBAAA;EACA,YAAA;EACA,2BAAA;ExB2yGH;AwBlyGC;;;EAGE,gBAAA;ExBoyGH;AwBhyGC;;EAEE,uBAAA;EACA,+BAAA;EACA,wBAAA;EE1GF,qEAAA;EF4GE,qBAAA;ExBkyGH;AwB7xGD;EAGI,gBAAA;ExB6xGH;AwBhyGD;EAQI,YAAA;ExB2xGH;AwBnxGD;EACE,YAAA;EACA,UAAA;ExBqxGD;AwB7wGD;EACE,SAAA;EACA,aAAA;ExB+wGD;AwB3wGD;EACE,gBAAA;EACA,mBAAA;EACA,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,qBAAA;ExB6wGD;AwBzwGD;EACE,iBAAA;EACA,SAAA;EACA,UAAA;EACA,WAAA;EACA,QAAA;EACA,cAAA;ExB2wGD;AwBvwGD;EACE,UAAA;EACA,YAAA;ExBywGD;AwBjwGD;;EAII,eAAA;EACA,0BAAA;EACA,aAAA;ExBiwGH;AwBvwGD;;EAUI,WAAA;EACA,cAAA;EACA,oBAAA;ExBiwGH;AwB5uGD;EAXE;IAnEA,YAAA;IACA,UAAA;IxB8zGC;EwB5vGD;IAzDA,SAAA;IACA,aAAA;IxBwzGC;EACF;A2Bv8GD;;EAEE,oBAAA;EACA,uBAAA;EACA,wBAAA;E3By8GD;A2B78GD;;EAMI,oBAAA;EACA,aAAA;E3B28GH;A2Bz8GG;;;;;;;;EAIE,YAAA;E3B+8GL;A2Bz8GD;;;;EAKI,mBAAA;E3B08GH;A2Br8GD;EACE,mBAAA;E3Bu8GD;A2Bx8GD;;EAMI,aAAA;E3Bs8GH;A2B58GD;;;EAWI,kBAAA;E3Bs8GH;A2Bl8GD;EACE,kBAAA;E3Bo8GD;A2Bh8GD;EACE,gBAAA;E3Bk8GD;A2Bj8GC;ECjDA,+BAAA;EACG,4BAAA;E5Bq/GJ;A2Bh8GD;;EC9CE,8BAAA;EACG,2BAAA;E5Bk/GJ;A2B/7GD;EACE,aAAA;E3Bi8GD;A2B/7GD;EACE,kBAAA;E3Bi8GD;A2B/7GD;;EClEE,+BAAA;EACG,4BAAA;E5BqgHJ;A2B97GD;EChEE,8BAAA;EACG,2BAAA;E5BigHJ;A2B77GD;;EAEE,YAAA;E3B+7GD;A2B96GD;EACE,mBAAA;EACA,oBAAA;E3Bg7GD;A2B96GD;EACE,oBAAA;EACA,qBAAA;E3Bg7GD;A2B36GD;EtB9CE,0DAAA;EACQ,kDAAA;EL49GT;A2B36GC;EtBlDA,0BAAA;EACQ,kBAAA;ELg+GT;A2Bx6GD;EACE,gBAAA;E3B06GD;A2Bv6GD;EACE,yBAAA;EACA,wBAAA;E3By6GD;A2Bt6GD;EACE,yBAAA;E3Bw6GD;A2Bj6GD;;;EAII,gBAAA;EACA,aAAA;EACA,aAAA;EACA,iBAAA;E3Bk6GH;A2Bz6GD;EAcM,aAAA;E3B85GL;A2B56GD;;;;EAsBI,kBAAA;EACA,gBAAA;E3B45GH;A2Bv5GC;EACE,kBAAA;E3By5GH;A2Bv5GC;EACE,8BAAA;ECnKF,+BAAA;EACC,8BAAA;E5B6jHF;A2Bx5GC;EACE,gCAAA;EC/KF,4BAAA;EACC,2BAAA;E5B0kHF;A2Bx5GD;EACE,kBAAA;E3B05GD;A2Bx5GD;;EC9KE,+BAAA;EACC,8BAAA;E5B0kHF;A2Bv5GD;EC5LE,4BAAA;EACC,2BAAA;E5BslHF;A2Bn5GD;EACE,gBAAA;EACA,aAAA;EACA,qBAAA;EACA,2BAAA;E3Bq5GD;A2Bz5GD;;EAOI,aAAA;EACA,qBAAA;EACA,WAAA;E3Bs5GH;A2B/5GD;EAYI,aAAA;E3Bs5GH;A2Bl6GD;EAgBI,YAAA;E3Bq5GH;A2Bp4GD;;;;EAKM,oBAAA;EACA,wBAAA;EACA,sBAAA;E3Bq4GL;A6B9mHD;EACE,oBAAA;EACA,gBAAA;EACA,2BAAA;E7BgnHD;A6B7mHC;EACE,aAAA;EACA,iBAAA;EACA,kBAAA;E7B+mHH;A6BxnHD;EAeI,oBAAA;EACA,YAAA;EAKA,aAAA;EAEA,aAAA;EACA,kBAAA;E7BumHH;A6B9lHD;;;EV8BE,cAAA;EACA,oBAAA;EACA,iBAAA;EACA,wBAAA;EACA,oBAAA;EnBqkHD;AmBnkHC;;;EACE,cAAA;EACA,mBAAA;EnBukHH;AmBpkHC;;;;;;EAEE,cAAA;EnB0kHH;A6BhnHD;;;EVyBE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;EnB4lHD;AmB1lHC;;;EACE,cAAA;EACA,mBAAA;EnB8lHH;AmB3lHC;;;;;;EAEE,cAAA;EnBimHH;A6B9nHD;;;EAGE,qBAAA;E7BgoHD;A6B9nHC;;;EACE,kBAAA;E7BkoHH;A6B9nHD;;EAEE,WAAA;EACA,qBAAA;EACA,wBAAA;E7BgoHD;A6B3nHD;EACE,mBAAA;EACA,iBAAA;EACA,qBAAA;EACA,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;E7B6nHD;A6B1nHC;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;E7B4nHH;A6B1nHC;EACE,oBAAA;EACA,iBAAA;EACA,oBAAA;E7B4nHH;A6BhpHD;;EA0BI,eAAA;E7B0nHH;A6BrnHD;;;;;;;EDhGE,+BAAA;EACG,4BAAA;E5B8tHJ;A6BtnHD;EACE,iBAAA;E7BwnHD;A6BtnHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;E5BmuHJ;A6BvnHD;EACE,gBAAA;E7BynHD;A6BpnHD;EACE,oBAAA;EAGA,cAAA;EACA,qBAAA;E7BonHD;A6BznHD;EAUI,oBAAA;E7BknHH;A6B5nHD;EAYM,mBAAA;E7BmnHL;A6BhnHG;;;EAGE,YAAA;E7BknHL;A6B7mHC;;EAGI,oBAAA;E7B8mHL;A6B3mHC;;EAGI,mBAAA;E7B4mHL;A8BtwHD;EACE,kBAAA;EACA,iBAAA;EACA,kBAAA;E9BwwHD;A8B3wHD;EAOI,oBAAA;EACA,gBAAA;E9BuwHH;A8B/wHD;EAWM,oBAAA;EACA,gBAAA;EACA,oBAAA;E9BuwHL;A8BtwHK;;EAEE,uBAAA;EACA,2BAAA;E9BwwHP;A8BnwHG;EACE,gBAAA;E9BqwHL;A8BnwHK;;EAEE,gBAAA;EACA,uBAAA;EACA,+BAAA;EACA,qBAAA;E9BqwHP;A8B9vHG;;;EAGE,2BAAA;EACA,uBAAA;E9BgwHL;A8BzyHD;ELHE,aAAA;EACA,eAAA;EACA,kBAAA;EACA,2BAAA;EzB+yHD;A8B/yHD;EA0DI,iBAAA;E9BwvHH;A8B/uHD;EACE,kCAAA;E9BivHD;A8BlvHD;EAGI,aAAA;EAEA,qBAAA;E9BivHH;A8BtvHD;EASM,mBAAA;EACA,yBAAA;EACA,+BAAA;EACA,4BAAA;E9BgvHL;A8B/uHK;EACE,uCAAA;E9BivHP;A8B3uHK;;;EAGE,gBAAA;EACA,2BAAA;EACA,2BAAA;EACA,kCAAA;EACA,iBAAA;E9B6uHP;A8BxuHC;EAqDA,aAAA;EA8BA,kBAAA;E9BypHD;A8B5uHC;EAwDE,aAAA;E9BurHH;A8B/uHC;EA0DI,oBAAA;EACA,oBAAA;E9BwrHL;A8BnvHC;EAgEE,WAAA;EACA,YAAA;E9BsrHH;A8B1qHD;EAAA;IAPM,qBAAA;IACA,WAAA;I9BqrHH;E8B/qHH;IAJQ,kBAAA;I9BsrHL;EACF;A8BhwHC;EAuFE,iBAAA;EACA,oBAAA;E9B4qHH;A8BpwHC;;;EA8FE,2BAAA;E9B2qHH;A8B7pHD;EAAA;IATM,kCAAA;IACA,4BAAA;I9B0qHH;E8BlqHH;;;IAHM,8BAAA;I9B0qHH;EACF;A8B3wHD;EAEI,aAAA;E9B4wHH;A8B9wHD;EAMM,oBAAA;E9B2wHL;A8BjxHD;EASM,kBAAA;E9B2wHL;A8BtwHK;;;EAGE,gBAAA;EACA,2BAAA;E9BwwHP;A8BhwHD;EAEI,aAAA;E9BiwHH;A8BnwHD;EAIM,iBAAA;EACA,gBAAA;E9BkwHL;A8BtvHD;EACE,aAAA;E9BwvHD;A8BzvHD;EAII,aAAA;E9BwvHH;A8B5vHD;EAMM,oBAAA;EACA,oBAAA;E9ByvHL;A8BhwHD;EAYI,WAAA;EACA,YAAA;E9BuvHH;A8B3uHD;EAAA;IAPM,qBAAA;IACA,WAAA;I9BsvHH;E8BhvHH;IAJQ,kBAAA;I9BuvHL;EACF;A8B/uHD;EACE,kBAAA;E9BivHD;A8BlvHD;EAKI,iBAAA;EACA,oBAAA;E9BgvHH;A8BtvHD;;;EAYI,2BAAA;E9B+uHH;A8BjuHD;EAAA;IATM,kCAAA;IACA,4BAAA;I9B8uHH;E8BtuHH;;;IAHM,8BAAA;I9B8uHH;EACF;A8BruHD;EAEI,eAAA;E9BsuHH;A8BxuHD;EAKI,gBAAA;E9BsuHH;A8B7tHD;EAEE,kBAAA;EF3OA,4BAAA;EACC,2BAAA;E5B08HF;A+Bp8HD;EACE,oBAAA;EACA,kBAAA;EACA,qBAAA;EACA,+BAAA;E/Bs8HD;A+B97HD;EAAA;IAFI,oBAAA;I/Bo8HD;EACF;A+Br7HD;EAAA;IAFI,aAAA;I/B27HD;EACF;A+B76HD;EACE,qBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mCAAA;EACA,4DAAA;UAAA,oDAAA;EAEA,mCAAA;E/B86HD;A+B56HC;EACE,kBAAA;E/B86HH;A+Bl5HD;EAAA;IAxBI,aAAA;IACA,eAAA;IACA,0BAAA;YAAA,kBAAA;I/B86HD;E+B56HC;IACE,2BAAA;IACA,yBAAA;IACA,mBAAA;IACA,8BAAA;I/B86HH;E+B36HC;IACE,qBAAA;I/B66HH;E+Bx6HC;;;IAGE,iBAAA;IACA,kBAAA;I/B06HH;EACF;A+Bt6HD;;EAGI,mBAAA;E/Bu6HH;A+Bl6HC;EAAA;;IAFI,mBAAA;I/By6HH;EACF;A+Bh6HD;;;;EAII,qBAAA;EACA,oBAAA;E/Bk6HH;A+B55HC;EAAA;;;;IAHI,iBAAA;IACA,gBAAA;I/Bs6HH;EACF;A+B15HD;EACE,eAAA;EACA,uBAAA;E/B45HD;A+Bv5HD;EAAA;IAFI,kBAAA;I/B65HD;EACF;A+Bz5HD;;EAEE,iBAAA;EACA,UAAA;EACA,SAAA;EACA,eAAA;E/B25HD;A+Br5HD;EAAA;;IAFI,kBAAA;I/B45HD;EACF;A+B15HD;EACE,QAAA;EACA,uBAAA;E/B45HD;A+B15HD;EACE,WAAA;EACA,kBAAA;EACA,uBAAA;E/B45HD;A+Bt5HD;EACE,aAAA;EACA,oBAAA;EACA,iBAAA;EACA,mBAAA;EACA,cAAA;E/Bw5HD;A+Bt5HC;;EAEE,uBAAA;E/Bw5HH;A+Bj6HD;EAaI,gBAAA;E/Bu5HH;A+B94HD;EALI;;IAEE,oBAAA;I/Bs5HH;EACF;A+B54HD;EACE,oBAAA;EACA,cAAA;EACA,oBAAA;EACA,mBAAA;EC9LA,iBAAA;EACA,oBAAA;ED+LA,+BAAA;EACA,wBAAA;EACA,+BAAA;EACA,oBAAA;E/B+4HD;A+B34HC;EACE,YAAA;E/B64HH;A+B35HD;EAmBI,gBAAA;EACA,aAAA;EACA,aAAA;EACA,oBAAA;E/B24HH;A+Bj6HD;EAyBI,iBAAA;E/B24HH;A+Br4HD;EAAA;IAFI,eAAA;I/B24HD;EACF;A+Bl4HD;EACE,qBAAA;E/Bo4HD;A+Br4HD;EAII,mBAAA;EACA,sBAAA;EACA,mBAAA;E/Bo4HH;A+Bx2HC;EAAA;IAtBI,kBAAA;IACA,aAAA;IACA,aAAA;IACA,eAAA;IACA,+BAAA;IACA,WAAA;IACA,0BAAA;YAAA,kBAAA;I/Bk4HH;E+Bl3HD;;IAbM,4BAAA;I/Bm4HL;E+Bt3HD;IAVM,mBAAA;I/Bm4HL;E+Bl4HK;;IAEE,wBAAA;I/Bo4HP;EACF;A+Bl3HD;EAAA;IAXI,aAAA;IACA,WAAA;I/Bi4HD;E+Bv3HH;IAPM,aAAA;I/Bi4HH;E+B13HH;IALQ,mBAAA;IACA,sBAAA;I/Bk4HL;EACF;A+Bv3HD;EACE,oBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mCAAA;EACA,sCAAA;E1B9NA,8FAAA;EACQ,sFAAA;E2B/DR,iBAAA;EACA,oBAAA;EhCwpID;AkBvqHD;EAAA;IA9DM,uBAAA;IACA,kBAAA;IACA,wBAAA;IlByuHH;EkB7qHH;IAvDM,uBAAA;IACA,aAAA;IACA,wBAAA;IlBuuHH;EkBlrHH;IAhDM,uBAAA;IlBquHH;EkBrrHH;IA5CM,uBAAA;IACA,wBAAA;IlBouHH;EkBzrHH;;;IAtCQ,aAAA;IlBouHL;EkB9rHH;IAhCM,aAAA;IlBiuHH;EkBjsHH;IA5BM,kBAAA;IACA,wBAAA;IlBguHH;EkBrsHH;;IApBM,uBAAA;IACA,eAAA;IACA,kBAAA;IACA,wBAAA;IlB6tHH;EkB5sHH;;IAdQ,iBAAA;IlB8tHL;EkBhtHH;;IATM,oBAAA;IACA,gBAAA;IlB6tHH;EkBrtHH;IAHM,QAAA;IlB2tHH;EACF;A+Bh6HC;EAAA;IANI,oBAAA;I/B06HH;E+Bx6HG;IACE,kBAAA;I/B06HL;EACF;A+Bz5HD;EAAA;IARI,aAAA;IACA,WAAA;IACA,gBAAA;IACA,iBAAA;IACA,gBAAA;IACA,mBAAA;I1BzPF,0BAAA;IACQ,kBAAA;IL+pIP;EACF;A+B/5HD;EACE,eAAA;EHpUA,4BAAA;EACC,2BAAA;E5BsuIF;A+B/5HD;EACE,kBAAA;EHzUA,8BAAA;EACC,6BAAA;EAOD,+BAAA;EACC,8BAAA;E5BquIF;A+B35HD;EChVE,iBAAA;EACA,oBAAA;EhC8uID;A+B55HC;ECnVA,kBAAA;EACA,qBAAA;EhCkvID;A+B75HC;ECtVA,kBAAA;EACA,qBAAA;EhCsvID;A+Bv5HD;EChWE,kBAAA;EACA,qBAAA;EhC0vID;A+Bn5HD;EAAA;IAJI,aAAA;IACA,mBAAA;IACA,oBAAA;I/B25HD;EACF;A+B93HD;EAhBE;IExWA,wBAAA;IjC0vIC;E+Bj5HD;IE5WA,yBAAA;IF8WE,qBAAA;I/Bm5HD;E+Br5HD;IAKI,iBAAA;I/Bm5HH;EACF;A+B14HD;EACE,2BAAA;EACA,uBAAA;E/B44HD;A+B94HD;EAKI,gBAAA;E/B44HH;A+B34HG;;EAEE,gBAAA;EACA,+BAAA;E/B64HL;A+Bt5HD;EAcI,gBAAA;E/B24HH;A+Bz5HD;EAmBM,gBAAA;E/By4HL;A+Bv4HK;;EAEE,gBAAA;EACA,+BAAA;E/By4HP;A+Br4HK;;;EAGE,gBAAA;EACA,2BAAA;E/Bu4HP;A+Bn4HK;;;EAGE,gBAAA;EACA,+BAAA;E/Bq4HP;A+B76HD;EA8CI,uBAAA;E/Bk4HH;A+Bj4HG;;EAEE,2BAAA;E/Bm4HL;A+Bp7HD;EAoDM,2BAAA;E/Bm4HL;A+Bv7HD;;EA0DI,uBAAA;E/Bi4HH;A+B13HK;;;EAGE,2BAAA;EACA,gBAAA;E/B43HP;A+B31HC;EAAA;IAzBQ,gBAAA;I/Bw3HP;E+Bv3HO;;IAEE,gBAAA;IACA,+BAAA;I/By3HT;E+Br3HO;;;IAGE,gBAAA;IACA,2BAAA;I/Bu3HT;E+Bn3HO;;;IAGE,gBAAA;IACA,+BAAA;I/Bq3HT;EACF;A+Bv9HD;EA8GI,gBAAA;E/B42HH;A+B32HG;EACE,gBAAA;E/B62HL;A+B79HD;EAqHI,gBAAA;E/B22HH;A+B12HG;;EAEE,gBAAA;E/B42HL;A+Bx2HK;;;;EAEE,gBAAA;E/B42HP;A+Bp2HD;EACE,2BAAA;EACA,uBAAA;E/Bs2HD;A+Bx2HD;EAKI,gBAAA;E/Bs2HH;A+Br2HG;;EAEE,gBAAA;EACA,+BAAA;E/Bu2HL;A+Bh3HD;EAcI,gBAAA;E/Bq2HH;A+Bn3HD;EAmBM,gBAAA;E/Bm2HL;A+Bj2HK;;EAEE,gBAAA;EACA,+BAAA;E/Bm2HP;A+B/1HK;;;EAGE,gBAAA;EACA,2BAAA;E/Bi2HP;A+B71HK;;;EAGE,gBAAA;EACA,+BAAA;E/B+1HP;A+Bv4HD;EA+CI,uBAAA;E/B21HH;A+B11HG;;EAEE,2BAAA;E/B41HL;A+B94HD;EAqDM,2BAAA;E/B41HL;A+Bj5HD;;EA2DI,uBAAA;E/B01HH;A+Bp1HK;;;EAGE,2BAAA;EACA,gBAAA;E/Bs1HP;A+B/yHC;EAAA;IA/BQ,uBAAA;I/Bk1HP;E+BnzHD;IA5BQ,2BAAA;I/Bk1HP;E+BtzHD;IAzBQ,gBAAA;I/Bk1HP;E+Bj1HO;;IAEE,gBAAA;IACA,+BAAA;I/Bm1HT;E+B/0HO;;;IAGE,gBAAA;IACA,2BAAA;I/Bi1HT;E+B70HO;;;IAGE,gBAAA;IACA,+BAAA;I/B+0HT;EACF;A+Bv7HD;EA+GI,gBAAA;E/B20HH;A+B10HG;EACE,gBAAA;E/B40HL;A+B77HD;EAsHI,gBAAA;E/B00HH;A+Bz0HG;;EAEE,gBAAA;E/B20HL;A+Bv0HK;;;;EAEE,gBAAA;E/B20HP;AkCr9ID;EACE,mBAAA;EACA,qBAAA;EACA,kBAAA;EACA,2BAAA;EACA,oBAAA;ElCu9ID;AkC59ID;EAQI,uBAAA;ElCu9IH;AkC/9ID;EAWM,mBAAA;EACA,gBAAA;EACA,gBAAA;ElCu9IL;AkCp+ID;EAkBI,gBAAA;ElCq9IH;AmCz+ID;EACE,uBAAA;EACA,iBAAA;EACA,gBAAA;EACA,oBAAA;EnC2+ID;AmC/+ID;EAOI,iBAAA;EnC2+IH;AmCl/ID;;EAUM,oBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;EACA,uBAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;EACA,mBAAA;EnC4+IL;AmC1+IG;;EAGI,gBAAA;EPXN,gCAAA;EACG,6BAAA;E5Bu/IJ;AmCz+IG;;EPvBF,iCAAA;EACG,8BAAA;E5BogJJ;AmCp+IG;;;;EAEE,gBAAA;EACA,2BAAA;EACA,uBAAA;EnCw+IL;AmCl+IG;;;;;;EAGE,YAAA;EACA,gBAAA;EACA,2BAAA;EACA,uBAAA;EACA,iBAAA;EnCu+IL;AmC7hJD;;;;;;EAiEM,gBAAA;EACA,2BAAA;EACA,uBAAA;EACA,qBAAA;EnCo+IL;AmC39ID;;EC1EM,oBAAA;EACA,iBAAA;EpCyiJL;AoCviJG;;ERMF,gCAAA;EACG,6BAAA;E5BqiJJ;AoCtiJG;;ERRF,iCAAA;EACG,8BAAA;E5BkjJJ;AmCr+ID;;EC/EM,mBAAA;EACA,iBAAA;EpCwjJL;AoCtjJG;;ERMF,gCAAA;EACG,6BAAA;E5BojJJ;AoCrjJG;;ERRF,iCAAA;EACG,8BAAA;E5BikJJ;AqCpkJD;EACE,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,oBAAA;ErCskJD;AqC1kJD;EAOI,iBAAA;ErCskJH;AqC7kJD;;EAUM,uBAAA;EACA,mBAAA;EACA,2BAAA;EACA,2BAAA;EACA,qBAAA;ErCukJL;AqCrlJD;;EAmBM,uBAAA;EACA,2BAAA;ErCskJL;AqC1lJD;;EA2BM,cAAA;ErCmkJL;AqC9lJD;;EAkCM,aAAA;ErCgkJL;AqClmJD;;;;EA2CM,gBAAA;EACA,2BAAA;EACA,qBAAA;ErC6jJL;AsC3mJD;EACE,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,qBAAA;EACA,0BAAA;EACA,sBAAA;EtC6mJD;AsCzmJG;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;EtC2mJL;AsCtmJC;EACE,eAAA;EtCwmJH;AsCpmJC;EACE,oBAAA;EACA,WAAA;EtCsmJH;AsC/lJD;ECtCE,2BAAA;EvCwoJD;AuCroJG;;EAEE,2BAAA;EvCuoJL;AsClmJD;EC1CE,2BAAA;EvC+oJD;AuC5oJG;;EAEE,2BAAA;EvC8oJL;AsCrmJD;EC9CE,2BAAA;EvCspJD;AuCnpJG;;EAEE,2BAAA;EvCqpJL;AsCxmJD;EClDE,2BAAA;EvC6pJD;AuC1pJG;;EAEE,2BAAA;EvC4pJL;AsC3mJD;ECtDE,2BAAA;EvCoqJD;AuCjqJG;;EAEE,2BAAA;EvCmqJL;AsC9mJD;EC1DE,2BAAA;EvC2qJD;AuCxqJG;;EAEE,2BAAA;EvC0qJL;AwC5qJD;EACE,uBAAA;EACA,iBAAA;EACA,kBAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,0BAAA;EACA,qBAAA;EACA,oBAAA;EACA,2BAAA;EACA,qBAAA;ExC8qJD;AwC3qJC;EACE,eAAA;ExC6qJH;AwCzqJC;EACE,oBAAA;EACA,WAAA;ExC2qJH;AwCxqJC;;EAEE,QAAA;EACA,kBAAA;ExC0qJH;AwCrqJG;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;ExCuqJL;AwClqJC;;EAEE,gBAAA;EACA,2BAAA;ExCoqJH;AwCjqJC;EACE,cAAA;ExCmqJH;AwChqJC;EACE,mBAAA;ExCkqJH;AwC/pJC;EACE,kBAAA;ExCiqJH;AyC3tJD;EACE,oBAAA;EACA,qBAAA;EACA,gBAAA;EACA,2BAAA;EzC6tJD;AyCjuJD;;EAQI,gBAAA;EzC6tJH;AyCruJD;EAYI,qBAAA;EACA,iBAAA;EACA,kBAAA;EzC4tJH;AyC1uJD;EAkBI,2BAAA;EzC2tJH;AyCxtJC;;EAEE,oBAAA;EzC0tJH;AyCjvJD;EA2BI,iBAAA;EzCytJH;AyCxsJD;EAAA;IAbI,iBAAA;IzCytJD;EyCvtJC;;IAEE,oBAAA;IACA,qBAAA;IzCytJH;EyCjtJH;;IAHM,iBAAA;IzCwtJH;EACF;A0CjwJD;EACE,gBAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;ErCiLA,6CAAA;EACK,wCAAA;EACG,qCAAA;ELmlJT;A0C7wJD;;EAaI,mBAAA;EACA,oBAAA;E1CowJH;A0ChwJC;;;EAGE,uBAAA;E1CkwJH;A0CvxJD;EA0BI,cAAA;EACA,gBAAA;E1CgwJH;A2CzxJD;EACE,eAAA;EACA,qBAAA;EACA,+BAAA;EACA,oBAAA;E3C2xJD;A2C/xJD;EAQI,eAAA;EAEA,gBAAA;E3CyxJH;A2CnyJD;EAeI,mBAAA;E3CuxJH;A2CtyJD;;EAqBI,kBAAA;E3CqxJH;A2C1yJD;EAyBI,iBAAA;E3CoxJH;A2C5wJD;;EAEE,qBAAA;E3C8wJD;A2ChxJD;;EAMI,oBAAA;EACA,WAAA;EACA,cAAA;EACA,gBAAA;E3C8wJH;A2CtwJD;ECvDE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5Cg0JD;A2C3wJD;EClDI,2BAAA;E5Cg0JH;A2C9wJD;EC/CI,gBAAA;E5Cg0JH;A2C7wJD;EC3DE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5C20JD;A2ClxJD;ECtDI,2BAAA;E5C20JH;A2CrxJD;ECnDI,gBAAA;E5C20JH;A2CpxJD;EC/DE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5Cs1JD;A2CzxJD;EC1DI,2BAAA;E5Cs1JH;A2C5xJD;ECvDI,gBAAA;E5Cs1JH;A2C3xJD;ECnEE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5Ci2JD;A2ChyJD;EC9DI,2BAAA;E5Ci2JH;A2CnyJD;EC3DI,gBAAA;E5Ci2JH;A6Cn2JD;EACE;IAAQ,6BAAA;I7Cs2JP;E6Cr2JD;IAAQ,0BAAA;I7Cw2JP;EACF;A6Cr2JD;EACE;IAAQ,6BAAA;I7Cw2JP;E6Cv2JD;IAAQ,0BAAA;I7C02JP;EACF;A6C72JD;EACE;IAAQ,6BAAA;I7Cw2JP;E6Cv2JD;IAAQ,0BAAA;I7C02JP;EACF;A6Cn2JD;EACE,kBAAA;EACA,cAAA;EACA,qBAAA;EACA,2BAAA;EACA,oBAAA;ExCsCA,wDAAA;EACQ,gDAAA;ELg0JT;A6Cl2JD;EACE,aAAA;EACA,WAAA;EACA,cAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2BAAA;ExCyBA,wDAAA;EACQ,gDAAA;EAyHR,qCAAA;EACK,gCAAA;EACG,6BAAA;ELotJT;A6C/1JD;;ECCI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDAF,oCAAA;UAAA,4BAAA;E7Cm2JD;A6C51JD;;ExC5CE,4DAAA;EACK,uDAAA;EACG,oDAAA;EL44JT;A6Cz1JD;EErEE,2BAAA;E/Ci6JD;A+C95JC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9Ci3JH;A6C71JD;EEzEE,2BAAA;E/Cy6JD;A+Ct6JC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9Cy3JH;A6Cj2JD;EE7EE,2BAAA;E/Ci7JD;A+C96JC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9Ci4JH;A6Cr2JD;EEjFE,2BAAA;E/Cy7JD;A+Ct7JC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9Cy4JH;AgDj8JD;EAEE,kBAAA;EhDk8JD;AgDh8JC;EACE,eAAA;EhDk8JH;AgD97JD;;EAEE,SAAA;EACA,kBAAA;EhDg8JD;AgD77JD;EACE,gBAAA;EhD+7JD;AgD57JD;EACE,gBAAA;EhD87JD;AgD37JD;;EAEE,oBAAA;EhD67JD;AgD17JD;;EAEE,qBAAA;EhD47JD;AgDz7JD;;;EAGE,qBAAA;EACA,qBAAA;EhD27JD;AgDx7JD;EACE,wBAAA;EhD07JD;AgDv7JD;EACE,wBAAA;EhDy7JD;AgDr7JD;EACE,eAAA;EACA,oBAAA;EhDu7JD;AgDj7JD;EACE,iBAAA;EACA,kBAAA;EhDm7JD;AiDr+JD;EAEE,qBAAA;EACA,iBAAA;EjDs+JD;AiD99JD;EACE,oBAAA;EACA,gBAAA;EACA,oBAAA;EAEA,qBAAA;EACA,2BAAA;EACA,2BAAA;EjD+9JD;AiD59JC;ErB3BA,8BAAA;EACC,6BAAA;E5B0/JF;AiD79JC;EACE,kBAAA;ErBvBF,iCAAA;EACC,gCAAA;E5Bu/JF;AiDt9JD;EACE,gBAAA;EjDw9JD;AiDz9JD;EAII,gBAAA;EjDw9JH;AiDp9JC;;EAEE,uBAAA;EACA,gBAAA;EACA,2BAAA;EjDs9JH;AiDh9JC;;;EAGE,2BAAA;EACA,gBAAA;EACA,qBAAA;EjDk9JH;AiDv9JC;;;EASI,gBAAA;EjDm9JL;AiD59JC;;;EAYI,gBAAA;EjDq9JL;AiDh9JC;;;EAGE,YAAA;EACA,gBAAA;EACA,2BAAA;EACA,uBAAA;EjDk9JH;AiDx9JC;;;;;;;;;EAYI,gBAAA;EjDu9JL;AiDn+JC;;;EAeI,gBAAA;EjDy9JL;AkDrjKC;EACE,gBAAA;EACA,2BAAA;ElDujKH;AkDrjKG;EACE,gBAAA;ElDujKL;AkDxjKG;EAII,gBAAA;ElDujKP;AkDpjKK;;EAEE,gBAAA;EACA,2BAAA;ElDsjKP;AkDpjKK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDsjKP;AkD3kKC;EACE,gBAAA;EACA,2BAAA;ElD6kKH;AkD3kKG;EACE,gBAAA;ElD6kKL;AkD9kKG;EAII,gBAAA;ElD6kKP;AkD1kKK;;EAEE,gBAAA;EACA,2BAAA;ElD4kKP;AkD1kKK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElD4kKP;AkDjmKC;EACE,gBAAA;EACA,2BAAA;ElDmmKH;AkDjmKG;EACE,gBAAA;ElDmmKL;AkDpmKG;EAII,gBAAA;ElDmmKP;AkDhmKK;;EAEE,gBAAA;EACA,2BAAA;ElDkmKP;AkDhmKK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDkmKP;AkDvnKC;EACE,gBAAA;EACA,2BAAA;ElDynKH;AkDvnKG;EACE,gBAAA;ElDynKL;AkD1nKG;EAII,gBAAA;ElDynKP;AkDtnKK;;EAEE,gBAAA;EACA,2BAAA;ElDwnKP;AkDtnKK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDwnKP;AiD5hKD;EACE,eAAA;EACA,oBAAA;EjD8hKD;AiD5hKD;EACE,kBAAA;EACA,kBAAA;EjD8hKD;AmDlpKD;EACE,qBAAA;EACA,2BAAA;EACA,+BAAA;EACA,oBAAA;E9C0DA,mDAAA;EACQ,2CAAA;EL2lKT;AmDjpKD;EACE,eAAA;EnDmpKD;AmD9oKD;EACE,oBAAA;EACA,sCAAA;EvBpBA,8BAAA;EACC,6BAAA;E5BqqKF;AmDppKD;EAMI,gBAAA;EnDipKH;AmD5oKD;EACE,eAAA;EACA,kBAAA;EACA,iBAAA;EACA,gBAAA;EnD8oKD;AmDlpKD;;;;;EAWI,gBAAA;EnD8oKH;AmDzoKD;EACE,oBAAA;EACA,2BAAA;EACA,+BAAA;EvBxCA,iCAAA;EACC,gCAAA;E5BorKF;AmDnoKD;;EAGI,kBAAA;EnDooKH;AmDvoKD;;EAMM,qBAAA;EACA,kBAAA;EnDqoKL;AmDjoKG;;EAEI,eAAA;EvBvEN,8BAAA;EACC,6BAAA;E5B2sKF;AmDhoKG;;EAEI,kBAAA;EvBtEN,iCAAA;EACC,gCAAA;E5BysKF;AmD7nKD;EAEI,qBAAA;EnD8nKH;AmD3nKD;EACE,qBAAA;EnD6nKD;AmDrnKD;;;EAII,kBAAA;EnDsnKH;AmD1nKD;;;EAOM,oBAAA;EACA,qBAAA;EnDwnKL;AmDhoKD;;EvBnGE,8BAAA;EACC,6BAAA;E5BuuKF;AmDroKD;;;;EAmBQ,6BAAA;EACA,8BAAA;EnDwnKP;AmD5oKD;;;;;;;;EAwBU,6BAAA;EnD8nKT;AmDtpKD;;;;;;;;EA4BU,8BAAA;EnDooKT;AmDhqKD;;EvB3FE,iCAAA;EACC,gCAAA;E5B+vKF;AmDrqKD;;;;EAyCQ,gCAAA;EACA,iCAAA;EnDkoKP;AmD5qKD;;;;;;;;EA8CU,gCAAA;EnDwoKT;AmDtrKD;;;;;;;;EAkDU,iCAAA;EnD8oKT;AmDhsKD;;;;EA2DI,+BAAA;EnD2oKH;AmDtsKD;;EA+DI,eAAA;EnD2oKH;AmD1sKD;;EAmEI,WAAA;EnD2oKH;AmD9sKD;;;;;;;;;;;;EA0EU,gBAAA;EnDkpKT;AmD5tKD;;;;;;;;;;;;EA8EU,iBAAA;EnD4pKT;AmD1uKD;;;;;;;;EAuFU,kBAAA;EnD6pKT;AmDpvKD;;;;;;;;EAgGU,kBAAA;EnD8pKT;AmD9vKD;EAsGI,WAAA;EACA,kBAAA;EnD2pKH;AmDjpKD;EACE,qBAAA;EnDmpKD;AmDppKD;EAKI,kBAAA;EACA,oBAAA;EnDkpKH;AmDxpKD;EASM,iBAAA;EnDkpKL;AmD3pKD;EAcI,kBAAA;EnDgpKH;AmD9pKD;;EAkBM,+BAAA;EnDgpKL;AmDlqKD;EAuBI,eAAA;EnD8oKH;AmDrqKD;EAyBM,kCAAA;EnD+oKL;AmDxoKD;ECpPE,uBAAA;EpD+3KD;AoD73KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpD+3KH;AoDl4KC;EAMI,2BAAA;EpD+3KL;AoDr4KC;EASI,gBAAA;EACA,2BAAA;EpD+3KL;AoD53KC;EAEI,8BAAA;EpD63KL;AmDvpKD;ECvPE,uBAAA;EpDi5KD;AoD/4KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDi5KH;AoDp5KC;EAMI,2BAAA;EpDi5KL;AoDv5KC;EASI,gBAAA;EACA,2BAAA;EpDi5KL;AoD94KC;EAEI,8BAAA;EpD+4KL;AmDtqKD;EC1PE,uBAAA;EpDm6KD;AoDj6KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDm6KH;AoDt6KC;EAMI,2BAAA;EpDm6KL;AoDz6KC;EASI,gBAAA;EACA,2BAAA;EpDm6KL;AoDh6KC;EAEI,8BAAA;EpDi6KL;AmDrrKD;EC7PE,uBAAA;EpDq7KD;AoDn7KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDq7KH;AoDx7KC;EAMI,2BAAA;EpDq7KL;AoD37KC;EASI,gBAAA;EACA,2BAAA;EpDq7KL;AoDl7KC;EAEI,8BAAA;EpDm7KL;AmDpsKD;EChQE,uBAAA;EpDu8KD;AoDr8KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDu8KH;AoD18KC;EAMI,2BAAA;EpDu8KL;AoD78KC;EASI,gBAAA;EACA,2BAAA;EpDu8KL;AoDp8KC;EAEI,8BAAA;EpDq8KL;AmDntKD;ECnQE,uBAAA;EpDy9KD;AoDv9KC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDy9KH;AoD59KC;EAMI,2BAAA;EpDy9KL;AoD/9KC;EASI,gBAAA;EACA,2BAAA;EpDy9KL;AoDt9KC;EAEI,8BAAA;EpDu9KL;AqDv+KD;EACE,oBAAA;EACA,gBAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;ErDy+KD;AqD9+KD;;;;;EAYI,oBAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA;EACA,cAAA;EACA,aAAA;EACA,WAAA;ErDy+KH;AqDp+KD;EACE,wBAAA;ErDs+KD;AqDl+KD;EACE,qBAAA;ErDo+KD;AsD//KD;EACE,kBAAA;EACA,eAAA;EACA,qBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;EjDwDA,yDAAA;EACQ,iDAAA;EL08KT;AsDzgLD;EASI,oBAAA;EACA,mCAAA;EtDmgLH;AsD9/KD;EACE,eAAA;EACA,oBAAA;EtDggLD;AsD9/KD;EACE,cAAA;EACA,oBAAA;EtDggLD;AuDthLD;EACE,cAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,8BAAA;EjCRA,cAAA;EAGA,2BAAA;EtB+hLD;AuDvhLC;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;EjCfF,cAAA;EAGA,2BAAA;EtBuiLD;AuDnhLC;EACE,YAAA;EACA,iBAAA;EACA,yBAAA;EACA,WAAA;EACA,0BAAA;EvDqhLH;AwD1iLD;EACE,kBAAA;ExD4iLD;AwDxiLD;EACE,eAAA;EACA,kBAAA;EACA,iBAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,mCAAA;EAIA,YAAA;ExDuiLD;AwDpiLC;EnD+GA,uCAAA;EACI,mCAAA;EACC,kCAAA;EACG,+BAAA;EAkER,qDAAA;EAEK,2CAAA;EACG,qCAAA;ELu3KT;AwD1iLC;EnD2GA,oCAAA;EACI,gCAAA;EACC,+BAAA;EACG,4BAAA;ELk8KT;AwD9iLD;EACE,oBAAA;EACA,kBAAA;ExDgjLD;AwD5iLD;EACE,oBAAA;EACA,aAAA;EACA,cAAA;ExD8iLD;AwD1iLD;EACE,oBAAA;EACA,2BAAA;EACA,2BAAA;EACA,sCAAA;EACA,oBAAA;EnDaA,kDAAA;EACQ,0CAAA;EmDZR,sCAAA;UAAA,8BAAA;EAEA,YAAA;ExD4iLD;AwDxiLD;EACE,iBAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,2BAAA;ExD0iLD;AwDxiLC;ElCrEA,YAAA;EAGA,0BAAA;EtB8mLD;AwD3iLC;ElCtEA,cAAA;EAGA,2BAAA;EtBknLD;AwD1iLD;EACE,eAAA;EACA,kCAAA;EACA,2BAAA;ExD4iLD;AwDziLD;EACE,kBAAA;ExD2iLD;AwDviLD;EACE,WAAA;EACA,yBAAA;ExDyiLD;AwDpiLD;EACE,oBAAA;EACA,eAAA;ExDsiLD;AwDliLD;EACE,eAAA;EACA,mBAAA;EACA,+BAAA;ExDoiLD;AwDviLD;EAQI,kBAAA;EACA,kBAAA;ExDkiLH;AwD3iLD;EAaI,mBAAA;ExDiiLH;AwD9iLD;EAiBI,gBAAA;ExDgiLH;AwD3hLD;EACE,oBAAA;EACA,cAAA;EACA,aAAA;EACA,cAAA;EACA,kBAAA;ExD6hLD;AwD3gLD;EAZE;IACE,cAAA;IACA,mBAAA;IxD0hLD;EwDxhLD;InDvEA,mDAAA;IACQ,2CAAA;ILkmLP;EwDvhLD;IAAY,cAAA;IxD0hLX;EACF;AwDrhLD;EAFE;IAAY,cAAA;IxD2hLX;EACF;AyD1qLD;EACE,oBAAA;EACA,eAAA;EACA,gBAAA;EAEA,6DAAA;EACA,iBAAA;EACA,qBAAA;EACA,kBAAA;EnCXA,YAAA;EAGA,0BAAA;EtBqrLD;AyD1qLC;EnCdA,cAAA;EAGA,2BAAA;EtByrLD;AyD7qLC;EAAW,kBAAA;EAAmB,gBAAA;EzDirL/B;AyDhrLC;EAAW,kBAAA;EAAmB,gBAAA;EzDorL/B;AyDnrLC;EAAW,iBAAA;EAAmB,gBAAA;EzDurL/B;AyDtrLC;EAAW,mBAAA;EAAmB,gBAAA;EzD0rL/B;AyDtrLD;EACE,kBAAA;EACA,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,uBAAA;EACA,2BAAA;EACA,oBAAA;EzDwrLD;AyDprLD;EACE,oBAAA;EACA,UAAA;EACA,WAAA;EACA,2BAAA;EACA,qBAAA;EzDsrLD;AyDlrLC;EACE,WAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;EACA,2BAAA;EzDorLH;AyDlrLC;EACE,WAAA;EACA,YAAA;EACA,qBAAA;EACA,yBAAA;EACA,2BAAA;EzDorLH;AyDlrLC;EACE,WAAA;EACA,WAAA;EACA,qBAAA;EACA,yBAAA;EACA,2BAAA;EzDorLH;AyDlrLC;EACE,UAAA;EACA,SAAA;EACA,kBAAA;EACA,6BAAA;EACA,6BAAA;EzDorLH;AyDlrLC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,6BAAA;EACA,4BAAA;EzDorLH;AyDlrLC;EACE,QAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;EACA,8BAAA;EzDorLH;AyDlrLC;EACE,QAAA;EACA,YAAA;EACA,kBAAA;EACA,yBAAA;EACA,8BAAA;EzDorLH;AyDlrLC;EACE,QAAA;EACA,WAAA;EACA,kBAAA;EACA,yBAAA;EACA,8BAAA;EzDorLH;A0DlxLD;EACE,oBAAA;EACA,QAAA;EACA,SAAA;EACA,eAAA;EACA,eAAA;EACA,kBAAA;EACA,cAAA;EAEA,6DAAA;EACA,iBAAA;EACA,qBAAA;EACA,yBAAA;EACA,kBAAA;EACA,2BAAA;EACA,sCAAA;UAAA,8BAAA;EACA,2BAAA;EACA,sCAAA;EACA,oBAAA;ErD6CA,mDAAA;EACQ,2CAAA;EqD1CR,qBAAA;E1DkxLD;A0D/wLC;EAAY,mBAAA;E1DkxLb;A0DjxLC;EAAY,mBAAA;E1DoxLb;A0DnxLC;EAAY,kBAAA;E1DsxLb;A0DrxLC;EAAY,oBAAA;E1DwxLb;A0DrxLD;EACE,WAAA;EACA,mBAAA;EACA,iBAAA;EACA,2BAAA;EACA,kCAAA;EACA,4BAAA;E1DuxLD;A0DpxLD;EACE,mBAAA;E1DsxLD;A0D9wLC;;EAEE,oBAAA;EACA,gBAAA;EACA,UAAA;EACA,WAAA;EACA,2BAAA;EACA,qBAAA;E1DgxLH;A0D7wLD;EACE,oBAAA;E1D+wLD;A0D7wLD;EACE,oBAAA;EACA,aAAA;E1D+wLD;A0D3wLC;EACE,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,2BAAA;EACA,uCAAA;EACA,eAAA;E1D6wLH;A0D5wLG;EACE,cAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,2BAAA;E1D8wLL;A0D3wLC;EACE,UAAA;EACA,aAAA;EACA,mBAAA;EACA,sBAAA;EACA,6BAAA;EACA,yCAAA;E1D6wLH;A0D5wLG;EACE,cAAA;EACA,WAAA;EACA,eAAA;EACA,sBAAA;EACA,6BAAA;E1D8wLL;A0D3wLC;EACE,WAAA;EACA,oBAAA;EACA,qBAAA;EACA,8BAAA;EACA,0CAAA;EACA,YAAA;E1D6wLH;A0D5wLG;EACE,cAAA;EACA,UAAA;EACA,oBAAA;EACA,qBAAA;EACA,8BAAA;E1D8wLL;A0D1wLC;EACE,UAAA;EACA,cAAA;EACA,mBAAA;EACA,uBAAA;EACA,4BAAA;EACA,wCAAA;E1D4wLH;A0D3wLG;EACE,cAAA;EACA,YAAA;EACA,uBAAA;EACA,4BAAA;EACA,eAAA;E1D6wLL;A2D14LD;EACE,oBAAA;E3D44LD;A2Dz4LD;EACE,oBAAA;EACA,kBAAA;EACA,aAAA;E3D24LD;A2D94LD;EAMI,eAAA;EACA,oBAAA;EtD6KF,2CAAA;EACK,sCAAA;EACG,mCAAA;EL+tLT;A2Dr5LD;;EAcM,gBAAA;E3D24LL;A2Dj3LC;EAAA;ItDiKA,wDAAA;IAEK,8CAAA;IACG,wCAAA;IA7JR,qCAAA;IAEQ,6BAAA;IA+GR,2BAAA;IAEQ,mBAAA;ILowLP;E2D/4LG;;ItDmHJ,4CAAA;IACQ,oCAAA;IsDjHF,SAAA;I3Dk5LL;E2Dh5LG;;ItD8GJ,6CAAA;IACQ,qCAAA;IsD5GF,SAAA;I3Dm5LL;E2Dj5LG;;;ItDyGJ,yCAAA;IACQ,iCAAA;IsDtGF,SAAA;I3Do5LL;EACF;A2D17LD;;;EA6CI,gBAAA;E3Dk5LH;A2D/7LD;EAiDI,SAAA;E3Di5LH;A2Dl8LD;;EAsDI,oBAAA;EACA,QAAA;EACA,aAAA;E3Dg5LH;A2Dx8LD;EA4DI,YAAA;E3D+4LH;A2D38LD;EA+DI,aAAA;E3D+4LH;A2D98LD;;EAmEI,SAAA;E3D+4LH;A2Dl9LD;EAuEI,aAAA;E3D84LH;A2Dr9LD;EA0EI,YAAA;E3D84LH;A2Dt4LD;EACE,oBAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA;EACA,YAAA;ErC9FA,cAAA;EAGA,2BAAA;EqC6FA,iBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2CAAA;E3Dy4LD;A2Dp4LC;EblGE,oGAAA;EACA,+FAAA;EACA,sHAAA;EAAA,gGAAA;EACA,6BAAA;EACA,wHAAA;E9Cy+LH;A2Dx4LC;EACE,YAAA;EACA,UAAA;EbvGA,oGAAA;EACA,+FAAA;EACA,sHAAA;EAAA,gGAAA;EACA,6BAAA;EACA,wHAAA;E9Ck/LH;A2D14LC;;EAEE,YAAA;EACA,gBAAA;EACA,uBAAA;ErCtHF,cAAA;EAGA,2BAAA;EtBigMD;A2D36LD;;;;EAsCI,oBAAA;EACA,UAAA;EACA,YAAA;EACA,uBAAA;E3D24LH;A2Dp7LD;;EA6CI,WAAA;EACA,oBAAA;E3D24LH;A2Dz7LD;;EAkDI,YAAA;EACA,qBAAA;E3D24LH;A2D97LD;;EAuDI,aAAA;EACA,cAAA;EACA,mBAAA;EACA,gBAAA;EACA,oBAAA;E3D24LH;A2Dt4LG;EACE,kBAAA;E3Dw4LL;A2Dp4LG;EACE,kBAAA;E3Ds4LL;A2D53LD;EACE,oBAAA;EACA,cAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;E3D83LD;A2Dv4LD;EAYI,uBAAA;EACA,aAAA;EACA,cAAA;EACA,aAAA;EACA,qBAAA;EACA,2BAAA;EACA,qBAAA;EACA,iBAAA;EAWA,2BAAA;EACA,oCAAA;E3Do3LH;A2Dn5LD;EAkCI,WAAA;EACA,aAAA;EACA,cAAA;EACA,2BAAA;E3Do3LH;A2D72LD;EACE,oBAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,aAAA;EACA,mBAAA;EACA,sBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2CAAA;E3D+2LD;A2D92LC;EACE,mBAAA;E3Dg3LH;A2Dv0LD;EAhCE;;;;IAKI,aAAA;IACA,cAAA;IACA,mBAAA;IACA,iBAAA;I3Dy2LH;E2Dj3LD;;IAYI,oBAAA;I3Dy2LH;E2Dr3LD;;IAgBI,qBAAA;I3Dy2LH;E2Dp2LD;IACE,WAAA;IACA,YAAA;IACA,sBAAA;I3Ds2LD;E2Dl2LD;IACE,cAAA;I3Do2LD;EACF;A4DlmMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,cAAA;EACA,gBAAA;E5DgoMH;A4D9nMC;;;;;;;;;;;;;;;EACE,aAAA;E5D8oMH;AiCtpMD;E4BRE,gBAAA;EACA,mBAAA;EACA,oBAAA;E7DiqMD;AiCxpMD;EACE,yBAAA;EjC0pMD;AiCxpMD;EACE,wBAAA;EjC0pMD;AiClpMD;EACE,0BAAA;EjCopMD;AiClpMD;EACE,2BAAA;EjCopMD;AiClpMD;EACE,oBAAA;EjCopMD;AiClpMD;E6BzBE,aAAA;EACA,oBAAA;EACA,mBAAA;EACA,+BAAA;EACA,WAAA;E9D8qMD;AiChpMD;EACE,0BAAA;EjCkpMD;AiC3oMD;EACE,iBAAA;EjC6oMD;A+D9qMD;EACE,qBAAA;E/DgrMD;A+D1qMD;;;;ECdE,0BAAA;EhE8rMD;A+DzqMD;;;;;;;;;;;;EAYE,0BAAA;E/D2qMD;A+DpqMD;EAAA;IChDE,2BAAA;IhEwtMC;EgEvtMD;IAAU,gBAAA;IhE0tMT;EgEztMD;IAAU,+BAAA;IhE4tMT;EgE3tMD;;IACU,gCAAA;IhE8tMT;EACF;A+D9qMD;EAAA;IAFI,2BAAA;I/DorMD;EACF;A+D9qMD;EAAA;IAFI,4BAAA;I/DorMD;EACF;A+D9qMD;EAAA;IAFI,kCAAA;I/DorMD;EACF;A+D7qMD;EAAA;ICrEE,2BAAA;IhEsvMC;EgErvMD;IAAU,gBAAA;IhEwvMT;EgEvvMD;IAAU,+BAAA;IhE0vMT;EgEzvMD;;IACU,gCAAA;IhE4vMT;EACF;A+DvrMD;EAAA;IAFI,2BAAA;I/D6rMD;EACF;A+DvrMD;EAAA;IAFI,4BAAA;I/D6rMD;EACF;A+DvrMD;EAAA;IAFI,kCAAA;I/D6rMD;EACF;A+DtrMD;EAAA;IC1FE,2BAAA;IhEoxMC;EgEnxMD;IAAU,gBAAA;IhEsxMT;EgErxMD;IAAU,+BAAA;IhEwxMT;EgEvxMD;;IACU,gCAAA;IhE0xMT;EACF;A+DhsMD;EAAA;IAFI,2BAAA;I/DssMD;EACF;A+DhsMD;EAAA;IAFI,4BAAA;I/DssMD;EACF;A+DhsMD;EAAA;IAFI,kCAAA;I/DssMD;EACF;A+D/rMD;EAAA;IC/GE,2BAAA;IhEkzMC;EgEjzMD;IAAU,gBAAA;IhEozMT;EgEnzMD;IAAU,+BAAA;IhEszMT;EgErzMD;;IACU,gCAAA;IhEwzMT;EACF;A+DzsMD;EAAA;IAFI,2BAAA;I/D+sMD;EACF;A+DzsMD;EAAA;IAFI,4BAAA;I/D+sMD;EACF;A+DzsMD;EAAA;IAFI,kCAAA;I/D+sMD;EACF;A+DxsMD;EAAA;IC5HE,0BAAA;IhEw0MC;EACF;A+DxsMD;EAAA;ICjIE,0BAAA;IhE60MC;EACF;A+DxsMD;EAAA;ICtIE,0BAAA;IhEk1MC;EACF;A+DxsMD;EAAA;IC3IE,0BAAA;IhEu1MC;EACF;A+DrsMD;ECnJE,0BAAA;EhE21MD;A+DlsMD;EAAA;ICjKE,2BAAA;IhEu2MC;EgEt2MD;IAAU,gBAAA;IhEy2MT;EgEx2MD;IAAU,+BAAA;IhE22MT;EgE12MD;;IACU,gCAAA;IhE62MT;EACF;A+DhtMD;EACE,0BAAA;E/DktMD;A+D7sMD;EAAA;IAFI,2BAAA;I/DmtMD;EACF;A+DjtMD;EACE,0BAAA;E/DmtMD;A+D9sMD;EAAA;IAFI,4BAAA;I/DotMD;EACF;A+DltMD;EACE,0BAAA;E/DotMD;A+D/sMD;EAAA;IAFI,kCAAA;I/DqtMD;EACF;A+D9sMD;EAAA;ICpLE,0BAAA;IhEs4MC;EACF","file":"bootstrap.css","sourcesContent":["/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\nhtml {\n  font-family: sans-serif;\n  -ms-text-size-adjust: 100%;\n  -webkit-text-size-adjust: 100%;\n}\nbody {\n  margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block;\n  vertical-align: baseline;\n}\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n[hidden],\ntemplate {\n  display: none;\n}\na {\n  background-color: transparent;\n}\na:active,\na:hover {\n  outline: 0;\n}\nabbr[title] {\n  border-bottom: 1px dotted;\n}\nb,\nstrong {\n  font-weight: bold;\n}\ndfn {\n  font-style: italic;\n}\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\nmark {\n  background: #ff0;\n  color: #000;\n}\nsmall {\n  font-size: 80%;\n}\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\nsup {\n  top: -0.5em;\n}\nsub {\n  bottom: -0.25em;\n}\nimg {\n  border: 0;\n}\nsvg:not(:root) {\n  overflow: hidden;\n}\nfigure {\n  margin: 1em 40px;\n}\nhr {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n  height: 0;\n}\npre {\n  overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit;\n  font: inherit;\n  margin: 0;\n}\nbutton {\n  overflow: visible;\n}\nbutton,\nselect {\n  text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button;\n  cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\ninput {\n  line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: textfield;\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n  border: 0;\n  padding: 0;\n}\ntextarea {\n  overflow: auto;\n}\noptgroup {\n  font-weight: bold;\n}\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\ntd,\nth {\n  padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n  *,\n  *:before,\n  *:after {\n    background: transparent !important;\n    color: #000 !important;\n    box-shadow: none !important;\n    text-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  a[href^=\"#\"]:after,\n  a[href^=\"javascript:\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n  thead {\n    display: table-header-group;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  img {\n    max-width: 100% !important;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  select {\n    background: #fff !important;\n  }\n  .navbar {\n    display: none;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #ddd !important;\n  }\n}\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n  content: \"\\2a\";\n}\n.glyphicon-plus:before {\n  content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n  content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n.glyphicon-lock:before {\n  content: \"\\e033\";\n}\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n  content: \"\\e044\";\n}\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n.glyphicon-camera:before {\n  content: \"\\e046\";\n}\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n.glyphicon-fire:before {\n  content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n  content: \"\\e109\";\n}\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n.glyphicon-bell:before {\n  content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n  content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n  content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n  content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n  content: \"\\e146\";\n}\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n.glyphicon-cd:before {\n  content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n  content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n  content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n  content: \"\\e204\";\n}\n.glyphicon-copy:before {\n  content: \"\\e205\";\n}\n.glyphicon-paste:before {\n  content: \"\\e206\";\n}\n.glyphicon-alert:before {\n  content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n  content: \"\\e210\";\n}\n.glyphicon-king:before {\n  content: \"\\e211\";\n}\n.glyphicon-queen:before {\n  content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n  content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n  content: \"\\e214\";\n}\n.glyphicon-knight:before {\n  content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n  content: \"\\e216\";\n}\n.glyphicon-tent:before {\n  content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n  content: \"\\e218\";\n}\n.glyphicon-bed:before {\n  content: \"\\e219\";\n}\n.glyphicon-apple:before {\n  content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n  content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n  content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n  content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n  content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n  content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n  content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n  content: \"\\e227\";\n}\n.glyphicon-btc:before {\n  content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n  content: \"\\e227\";\n}\n.glyphicon-yen:before {\n  content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n  content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n  content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n  content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n  content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n  content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n  content: \"\\e232\";\n}\n.glyphicon-education:before {\n  content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n  content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n  content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n  content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n  content: \"\\e237\";\n}\n.glyphicon-oil:before {\n  content: \"\\e238\";\n}\n.glyphicon-grain:before {\n  content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n  content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n  content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n  content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n  content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n  content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n  content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n  content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n  content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n  content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n  content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n  content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n  content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n  content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n  content: \"\\e253\";\n}\n.glyphicon-console:before {\n  content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n  content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n  content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n  content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n  content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n  content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n  content: \"\\e260\";\n}\n* {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #333333;\n  background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\na {\n  color: #337ab7;\n  text-decoration: none;\n}\na:hover,\na:focus {\n  color: #23527c;\n  text-decoration: underline;\n}\na:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\nfigure {\n  margin: 0;\n}\nimg {\n  vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  max-width: 100%;\n  height: auto;\n}\n.img-rounded {\n  border-radius: 6px;\n}\n.img-thumbnail {\n  padding: 4px;\n  line-height: 1.42857143;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n  -o-transition: all 0.2s ease-in-out;\n  transition: all 0.2s ease-in-out;\n  display: inline-block;\n  max-width: 100%;\n  height: auto;\n}\n.img-circle {\n  border-radius: 50%;\n}\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n}\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  clip: auto;\n}\n[role=\"button\"] {\n  cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n  font-weight: normal;\n  line-height: 1;\n  color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n  font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n  font-size: 75%;\n}\nh1,\n.h1 {\n  font-size: 36px;\n}\nh2,\n.h2 {\n  font-size: 30px;\n}\nh3,\n.h3 {\n  font-size: 24px;\n}\nh4,\n.h4 {\n  font-size: 18px;\n}\nh5,\n.h5 {\n  font-size: 14px;\n}\nh6,\n.h6 {\n  font-size: 12px;\n}\np {\n  margin: 0 0 10px;\n}\n.lead {\n  margin-bottom: 20px;\n  font-size: 16px;\n  font-weight: 300;\n  line-height: 1.4;\n}\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\nsmall,\n.small {\n  font-size: 85%;\n}\nmark,\n.mark {\n  background-color: #fcf8e3;\n  padding: .2em;\n}\n.text-left {\n  text-align: left;\n}\n.text-right {\n  text-align: right;\n}\n.text-center {\n  text-align: center;\n}\n.text-justify {\n  text-align: justify;\n}\n.text-nowrap {\n  white-space: nowrap;\n}\n.text-lowercase {\n  text-transform: lowercase;\n}\n.text-uppercase {\n  text-transform: uppercase;\n}\n.text-capitalize {\n  text-transform: capitalize;\n}\n.text-muted {\n  color: #777777;\n}\n.text-primary {\n  color: #337ab7;\n}\na.text-primary:hover {\n  color: #286090;\n}\n.text-success {\n  color: #3c763d;\n}\na.text-success:hover {\n  color: #2b542c;\n}\n.text-info {\n  color: #31708f;\n}\na.text-info:hover {\n  color: #245269;\n}\n.text-warning {\n  color: #8a6d3b;\n}\na.text-warning:hover {\n  color: #66512c;\n}\n.text-danger {\n  color: #a94442;\n}\na.text-danger:hover {\n  color: #843534;\n}\n.bg-primary {\n  color: #fff;\n  background-color: #337ab7;\n}\na.bg-primary:hover {\n  background-color: #286090;\n}\n.bg-success {\n  background-color: #dff0d8;\n}\na.bg-success:hover {\n  background-color: #c1e2b3;\n}\n.bg-info {\n  background-color: #d9edf7;\n}\na.bg-info:hover {\n  background-color: #afd9ee;\n}\n.bg-warning {\n  background-color: #fcf8e3;\n}\na.bg-warning:hover {\n  background-color: #f7ecb5;\n}\n.bg-danger {\n  background-color: #f2dede;\n}\na.bg-danger:hover {\n  background-color: #e4b9b9;\n}\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n  margin-left: -5px;\n}\n.list-inline > li {\n  display: inline-block;\n  padding-left: 5px;\n  padding-right: 5px;\n}\ndl {\n  margin-top: 0;\n  margin-bottom: 20px;\n}\ndt,\ndd {\n  line-height: 1.42857143;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0;\n}\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    clear: left;\n    text-align: right;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n}\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #777777;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  font-size: 17.5px;\n  border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n  margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n  display: block;\n  font-size: 80%;\n  line-height: 1.42857143;\n  color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n  content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n  text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n  content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n  content: '\\00A0 \\2014';\n}\naddress {\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #ffffff;\n  background-color: #333333;\n  border-radius: 3px;\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: bold;\n  box-shadow: none;\n}\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.42857143;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: #333333;\n  background-color: #f5f5f5;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border-radius: 0;\n}\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n.container {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n@media (min-width: 768px) {\n  .container {\n    width: 750px;\n  }\n}\n@media (min-width: 992px) {\n  .container {\n    width: 970px;\n  }\n}\n@media (min-width: 1200px) {\n  .container {\n    width: 1170px;\n  }\n}\n.container-fluid {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.row {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n  float: left;\n}\n.col-xs-12 {\n  width: 100%;\n}\n.col-xs-11 {\n  width: 91.66666667%;\n}\n.col-xs-10 {\n  width: 83.33333333%;\n}\n.col-xs-9 {\n  width: 75%;\n}\n.col-xs-8 {\n  width: 66.66666667%;\n}\n.col-xs-7 {\n  width: 58.33333333%;\n}\n.col-xs-6 {\n  width: 50%;\n}\n.col-xs-5 {\n  width: 41.66666667%;\n}\n.col-xs-4 {\n  width: 33.33333333%;\n}\n.col-xs-3 {\n  width: 25%;\n}\n.col-xs-2 {\n  width: 16.66666667%;\n}\n.col-xs-1 {\n  width: 8.33333333%;\n}\n.col-xs-pull-12 {\n  right: 100%;\n}\n.col-xs-pull-11 {\n  right: 91.66666667%;\n}\n.col-xs-pull-10 {\n  right: 83.33333333%;\n}\n.col-xs-pull-9 {\n  right: 75%;\n}\n.col-xs-pull-8 {\n  right: 66.66666667%;\n}\n.col-xs-pull-7 {\n  right: 58.33333333%;\n}\n.col-xs-pull-6 {\n  right: 50%;\n}\n.col-xs-pull-5 {\n  right: 41.66666667%;\n}\n.col-xs-pull-4 {\n  right: 33.33333333%;\n}\n.col-xs-pull-3 {\n  right: 25%;\n}\n.col-xs-pull-2 {\n  right: 16.66666667%;\n}\n.col-xs-pull-1 {\n  right: 8.33333333%;\n}\n.col-xs-pull-0 {\n  right: auto;\n}\n.col-xs-push-12 {\n  left: 100%;\n}\n.col-xs-push-11 {\n  left: 91.66666667%;\n}\n.col-xs-push-10 {\n  left: 83.33333333%;\n}\n.col-xs-push-9 {\n  left: 75%;\n}\n.col-xs-push-8 {\n  left: 66.66666667%;\n}\n.col-xs-push-7 {\n  left: 58.33333333%;\n}\n.col-xs-push-6 {\n  left: 50%;\n}\n.col-xs-push-5 {\n  left: 41.66666667%;\n}\n.col-xs-push-4 {\n  left: 33.33333333%;\n}\n.col-xs-push-3 {\n  left: 25%;\n}\n.col-xs-push-2 {\n  left: 16.66666667%;\n}\n.col-xs-push-1 {\n  left: 8.33333333%;\n}\n.col-xs-push-0 {\n  left: auto;\n}\n.col-xs-offset-12 {\n  margin-left: 100%;\n}\n.col-xs-offset-11 {\n  margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n  margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n  margin-left: 75%;\n}\n.col-xs-offset-8 {\n  margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n  margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n  margin-left: 50%;\n}\n.col-xs-offset-5 {\n  margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n  margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n  margin-left: 25%;\n}\n.col-xs-offset-2 {\n  margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n  margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n  margin-left: 0%;\n}\n@media (min-width: 768px) {\n  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n    float: left;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-11 {\n    width: 91.66666667%;\n  }\n  .col-sm-10 {\n    width: 83.33333333%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-8 {\n    width: 66.66666667%;\n  }\n  .col-sm-7 {\n    width: 58.33333333%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-5 {\n    width: 41.66666667%;\n  }\n  .col-sm-4 {\n    width: 33.33333333%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-2 {\n    width: 16.66666667%;\n  }\n  .col-sm-1 {\n    width: 8.33333333%;\n  }\n  .col-sm-pull-12 {\n    right: 100%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-sm-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-sm-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-sm-pull-0 {\n    right: auto;\n  }\n  .col-sm-push-12 {\n    left: 100%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666667%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666667%;\n  }\n  .col-sm-push-7 {\n    left: 58.33333333%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666667%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-2 {\n    left: 16.66666667%;\n  }\n  .col-sm-push-1 {\n    left: 8.33333333%;\n  }\n  .col-sm-push-0 {\n    left: auto;\n  }\n  .col-sm-offset-12 {\n    margin-left: 100%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-sm-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 992px) {\n  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n    float: left;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-11 {\n    width: 91.66666667%;\n  }\n  .col-md-10 {\n    width: 83.33333333%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-8 {\n    width: 66.66666667%;\n  }\n  .col-md-7 {\n    width: 58.33333333%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-5 {\n    width: 41.66666667%;\n  }\n  .col-md-4 {\n    width: 33.33333333%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-2 {\n    width: 16.66666667%;\n  }\n  .col-md-1 {\n    width: 8.33333333%;\n  }\n  .col-md-pull-12 {\n    right: 100%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-md-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-md-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-md-pull-0 {\n    right: auto;\n  }\n  .col-md-push-12 {\n    left: 100%;\n  }\n  .col-md-push-11 {\n    left: 91.66666667%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-8 {\n    left: 66.66666667%;\n  }\n  .col-md-push-7 {\n    left: 58.33333333%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-5 {\n    left: 41.66666667%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-2 {\n    left: 16.66666667%;\n  }\n  .col-md-push-1 {\n    left: 8.33333333%;\n  }\n  .col-md-push-0 {\n    left: auto;\n  }\n  .col-md-offset-12 {\n    margin-left: 100%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 1200px) {\n  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n    float: left;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-11 {\n    width: 91.66666667%;\n  }\n  .col-lg-10 {\n    width: 83.33333333%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-8 {\n    width: 66.66666667%;\n  }\n  .col-lg-7 {\n    width: 58.33333333%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-5 {\n    width: 41.66666667%;\n  }\n  .col-lg-4 {\n    width: 33.33333333%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-2 {\n    width: 16.66666667%;\n  }\n  .col-lg-1 {\n    width: 8.33333333%;\n  }\n  .col-lg-pull-12 {\n    right: 100%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-lg-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-lg-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-lg-pull-0 {\n    right: auto;\n  }\n  .col-lg-push-12 {\n    left: 100%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666667%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666667%;\n  }\n  .col-lg-push-7 {\n    left: 58.33333333%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666667%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-2 {\n    left: 16.66666667%;\n  }\n  .col-lg-push-1 {\n    left: 8.33333333%;\n  }\n  .col-lg-push-0 {\n    left: auto;\n  }\n  .col-lg-offset-12 {\n    margin-left: 100%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0%;\n  }\n}\ntable {\n  background-color: transparent;\n}\ncaption {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  color: #777777;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.42857143;\n  vertical-align: top;\n  border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n.table > tbody + tbody {\n  border-top: 2px solid #dddddd;\n}\n.table .table {\n  background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\n.table-bordered {\n  border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n  background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n  background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n  background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n  background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n  background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n  background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n  background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n  background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n  background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n  background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n  background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n  background-color: #ebcccc;\n}\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid #dddddd;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  min-width: 0;\n}\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n  display: inline-block;\n  max-width: 100%;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  line-height: normal;\n}\ninput[type=\"file\"] {\n  display: block;\n}\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\nselect[multiple],\nselect[size] {\n  height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\noutput {\n  display: block;\n  padding-top: 7px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n}\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n  background-color: #ffffff;\n  background-image: none;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n  color: #999999;\n  opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n  color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n  color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  background-color: #eeeeee;\n  opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n}\ntextarea.form-control {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"],\n  input[type=\"time\"],\n  input[type=\"datetime-local\"],\n  input[type=\"month\"] {\n    line-height: 34px;\n  }\n  input[type=\"date\"].input-sm,\n  input[type=\"time\"].input-sm,\n  input[type=\"datetime-local\"].input-sm,\n  input[type=\"month\"].input-sm,\n  .input-group-sm input[type=\"date\"],\n  .input-group-sm input[type=\"time\"],\n  .input-group-sm input[type=\"datetime-local\"],\n  .input-group-sm input[type=\"month\"] {\n    line-height: 30px;\n  }\n  input[type=\"date\"].input-lg,\n  input[type=\"time\"].input-lg,\n  input[type=\"datetime-local\"].input-lg,\n  input[type=\"month\"].input-lg,\n  .input-group-lg input[type=\"date\"],\n  .input-group-lg input[type=\"time\"],\n  .input-group-lg input[type=\"datetime-local\"],\n  .input-group-lg input[type=\"month\"] {\n    line-height: 46px;\n  }\n}\n.form-group {\n  margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n  min-height: 20px;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n  cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n  cursor: not-allowed;\n}\n.form-control-static {\n  padding-top: 7px;\n  padding-bottom: 7px;\n  margin-bottom: 0;\n  min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n  height: auto;\n}\n.form-group-sm .form-control {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.form-group-sm .form-control {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.form-group-sm .form-control,\nselect[multiple].form-group-sm .form-control {\n  height: auto;\n}\n.form-group-sm .form-control-static {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  min-height: 32px;\n}\n.input-lg {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-lg {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n  height: auto;\n}\n.form-group-lg .form-control {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.form-group-lg .form-control {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.form-group-lg .form-control,\nselect[multiple].form-group-lg .form-control {\n  height: auto;\n}\n.form-group-lg .form-control-static {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  min-height: 38px;\n}\n.has-feedback {\n  position: relative;\n}\n.has-feedback .form-control {\n  padding-right: 42.5px;\n}\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  display: block;\n  width: 34px;\n  height: 34px;\n  line-height: 34px;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback {\n  width: 46px;\n  height: 46px;\n  line-height: 46px;\n}\n.input-sm + .form-control-feedback {\n  width: 30px;\n  height: 30px;\n  line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n  color: #3c763d;\n}\n.has-success .form-control {\n  border-color: #3c763d;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n  border-color: #2b542c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n  color: #3c763d;\n  border-color: #3c763d;\n  background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n  color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n  color: #8a6d3b;\n}\n.has-warning .form-control {\n  border-color: #8a6d3b;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n  border-color: #66512c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n  color: #8a6d3b;\n  border-color: #8a6d3b;\n  background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n  color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n  color: #a94442;\n}\n.has-error .form-control {\n  border-color: #a94442;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n  border-color: #843534;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n  color: #a94442;\n  border-color: #a94442;\n  background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n  color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n  top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n  top: 0;\n}\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-static {\n    display: inline-block;\n  }\n  .form-inline .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .form-inline .input-group .input-group-addon,\n  .form-inline .input-group .input-group-btn,\n  .form-inline .input-group .form-control {\n    width: auto;\n  }\n  .form-inline .input-group > .form-control {\n    width: 100%;\n  }\n  .form-inline .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio label,\n  .form-inline .checkbox label {\n    padding-left: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .form-inline .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  margin-top: 0;\n  margin-bottom: 0;\n  padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n  min-height: 27px;\n}\n.form-horizontal .form-group {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    text-align: right;\n    margin-bottom: 0;\n    padding-top: 7px;\n  }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n  right: 15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-lg .control-label {\n    padding-top: 14.333333px;\n  }\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-sm .control-label {\n    padding-top: 6px;\n  }\n}\n.btn {\n  display: inline-block;\n  margin-bottom: 0;\n  font-weight: normal;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none;\n  border: 1px solid transparent;\n  white-space: nowrap;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  border-radius: 4px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n  color: #333333;\n  text-decoration: none;\n}\n.btn:active,\n.btn.active {\n  outline: 0;\n  background-image: none;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  cursor: not-allowed;\n  pointer-events: none;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-default {\n  color: #333333;\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n.btn-default:hover,\n.btn-default:focus,\n.btn-default.focus,\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  color: #333333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n.btn-default .badge {\n  color: #ffffff;\n  background-color: #333333;\n}\n.btn-primary {\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary:hover,\n.btn-primary:focus,\n.btn-primary.focus,\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  color: #ffffff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.btn-success {\n  color: #ffffff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success:hover,\n.btn-success:focus,\n.btn-success.focus,\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  color: #ffffff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success .badge {\n  color: #5cb85c;\n  background-color: #ffffff;\n}\n.btn-info {\n  color: #ffffff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info:hover,\n.btn-info:focus,\n.btn-info.focus,\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  color: #ffffff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info .badge {\n  color: #5bc0de;\n  background-color: #ffffff;\n}\n.btn-warning {\n  color: #ffffff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning:hover,\n.btn-warning:focus,\n.btn-warning.focus,\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  color: #ffffff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning .badge {\n  color: #f0ad4e;\n  background-color: #ffffff;\n}\n.btn-danger {\n  color: #ffffff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger:hover,\n.btn-danger:focus,\n.btn-danger.focus,\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  color: #ffffff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger .badge {\n  color: #d9534f;\n  background-color: #ffffff;\n}\n.btn-link {\n  color: #337ab7;\n  font-weight: normal;\n  border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n  color: #23527c;\n  text-decoration: underline;\n  background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #777777;\n  text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-block {\n  display: block;\n  width: 100%;\n}\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n  -o-transition: opacity 0.15s linear;\n  transition: opacity 0.15s linear;\n}\n.fade.in {\n  opacity: 1;\n}\n.collapse {\n  display: none;\n}\n.collapse.in {\n  display: block;\n}\ntr.collapse.in {\n  display: table-row;\n}\ntbody.collapse.in {\n  display: table-row-group;\n}\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition-property: height, visibility;\n  transition-property: height, visibility;\n  -webkit-transition-duration: 0.35s;\n  transition-duration: 0.35s;\n  -webkit-transition-timing-function: ease;\n  transition-timing-function: ease;\n}\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px dashed;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n  position: relative;\n}\n.dropdown-toggle:focus {\n  outline: 0;\n}\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  list-style: none;\n  font-size: 14px;\n  text-align: left;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.42857143;\n  color: #333333;\n  white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  text-decoration: none;\n  color: #262626;\n  background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #ffffff;\n  text-decoration: none;\n  outline: 0;\n  background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  cursor: not-allowed;\n}\n.open > .dropdown-menu {\n  display: block;\n}\n.open > a {\n  outline: 0;\n}\n.dropdown-menu-right {\n  left: auto;\n  right: 0;\n}\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.42857143;\n  color: #777777;\n  white-space: nowrap;\n}\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: 990;\n}\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0;\n  border-bottom: 4px solid;\n  content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    left: auto;\n    right: 0;\n  }\n  .navbar-right .dropdown-menu-left {\n    left: 0;\n    right: auto;\n  }\n}\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n  margin-left: -1px;\n}\n.btn-toolbar {\n  margin-left: -5px;\n}\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n  float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n  margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn .caret {\n  margin-left: 0;\n}\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n  margin-top: -1px;\n  margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-bottom-left-radius: 4px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n  float: none;\n  display: table-cell;\n  width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n  width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n  left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n  float: none;\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-group .form-control {\n  position: relative;\n  z-index: 2;\n  float: left;\n  width: 100%;\n  margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  color: #555555;\n  text-align: center;\n  background-color: #eeeeee;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n.input-group-btn {\n  position: relative;\n  font-size: 0;\n  white-space: nowrap;\n}\n.input-group-btn > .btn {\n  position: relative;\n}\n.input-group-btn > .btn + .btn {\n  margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n  margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n  margin-left: -1px;\n}\n.nav {\n  margin-bottom: 0;\n  padding-left: 0;\n  list-style: none;\n}\n.nav > li {\n  position: relative;\n  display: block;\n}\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n  color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #777777;\n  text-decoration: none;\n  background-color: transparent;\n  cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eeeeee;\n  border-color: #337ab7;\n}\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.nav > li > a > img {\n  max-width: none;\n}\n.nav-tabs {\n  border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.42857143;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555555;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-bottom-color: transparent;\n  cursor: default;\n}\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n.nav-tabs.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-tabs.nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs.nav-justified > .active > a,\n  .nav-tabs.nav-justified > .active > a:hover,\n  .nav-tabs.nav-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n.nav-pills > li {\n  float: left;\n}\n.nav-pills > li > a {\n  border-radius: 4px;\n}\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #ffffff;\n  background-color: #337ab7;\n}\n.nav-stacked > li {\n  float: none;\n}\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n.nav-justified {\n  width: 100%;\n}\n.nav-justified > li {\n  float: none;\n}\n.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs-justified > .active > a,\n  .nav-tabs-justified > .active > a:hover,\n  .nav-tabs-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar {\n  position: relative;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: 15px;\n  padding-left: 15px;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n  -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-static-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n  max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    max-height: 200px;\n  }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container-fluid > .navbar-header,\n  .container > .navbar-collapse,\n  .container-fluid > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n.navbar-static-top {\n  z-index: 1000;\n  border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n  border-width: 1px 0 0;\n}\n.navbar-brand {\n  float: left;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n  height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n.navbar-brand > img {\n  display: block;\n}\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand,\n  .navbar > .container-fluid .navbar-brand {\n    margin-left: -15px;\n  }\n}\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: 15px;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.navbar-toggle:focus {\n  outline: 0;\n}\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n}\n.navbar-form {\n  margin-left: -15px;\n  margin-right: -15px;\n  padding: 10px 15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control-static {\n    display: inline-block;\n  }\n  .navbar-form .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .navbar-form .input-group .input-group-addon,\n  .navbar-form .input-group .input-group-btn,\n  .navbar-form .input-group .form-control {\n    width: auto;\n  }\n  .navbar-form .input-group > .form-control {\n    width: 100%;\n  }\n  .navbar-form .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio label,\n  .navbar-form .checkbox label {\n    padding-left: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .navbar-form .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n  .navbar-form .form-group:last-child {\n    margin-bottom: 0;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    -webkit-box-shadow: none;\n    box-shadow: none;\n  }\n}\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n  margin-top: 14px;\n  margin-bottom: 14px;\n}\n.navbar-text {\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n  .navbar-text {\n    float: left;\n    margin-left: 15px;\n    margin-right: 15px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n    margin-right: -15px;\n  }\n  .navbar-right ~ .navbar-right {\n    margin-right: 0;\n  }\n}\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n  color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n.navbar-default .navbar-text {\n  color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n  color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333333;\n  background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #cccccc;\n  background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n  border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  background-color: #e7e7e7;\n  color: #555555;\n}\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #cccccc;\n    background-color: transparent;\n  }\n}\n.navbar-default .navbar-link {\n  color: #777777;\n}\n.navbar-default .navbar-link:hover {\n  color: #333333;\n}\n.navbar-default .btn-link {\n  color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n  color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n  color: #cccccc;\n}\n.navbar-inverse {\n  background-color: #222222;\n  border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444444;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n  border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  background-color: #080808;\n  color: #ffffff;\n}\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #9d9d9d;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #ffffff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #ffffff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444444;\n    background-color: transparent;\n  }\n}\n.navbar-inverse .navbar-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n  color: #ffffff;\n}\n.navbar-inverse .btn-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n  color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n  color: #444444;\n}\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n.breadcrumb > li {\n  display: inline-block;\n}\n.breadcrumb > li + li:before {\n  content: \"/\\00a0\";\n  padding: 0 5px;\n  color: #cccccc;\n}\n.breadcrumb > .active {\n  color: #777777;\n}\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n.pagination > li {\n  display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  line-height: 1.42857143;\n  text-decoration: none;\n  color: #337ab7;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-bottom-right-radius: 4px;\n  border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  color: #23527c;\n  background-color: #eeeeee;\n  border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n  cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #777777;\n  background-color: #ffffff;\n  border-color: #dddddd;\n  cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-bottom-left-radius: 6px;\n  border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-bottom-right-radius: 6px;\n  border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-bottom-right-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  list-style: none;\n  text-align: center;\n}\n.pager li {\n  display: inline;\n}\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #777777;\n  background-color: #ffffff;\n  cursor: not-allowed;\n}\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.label:empty {\n  display: none;\n}\n.btn .label {\n  position: relative;\n  top: -1px;\n}\n.label-default {\n  background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #5e5e5e;\n}\n.label-primary {\n  background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #286090;\n}\n.label-success {\n  background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n.label-info {\n  background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n.label-warning {\n  background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n.label-danger {\n  background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  color: #ffffff;\n  line-height: 1;\n  vertical-align: baseline;\n  white-space: nowrap;\n  text-align: center;\n  background-color: #777777;\n  border-radius: 10px;\n}\n.badge:empty {\n  display: none;\n}\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n  top: 0;\n  padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.list-group-item > .badge {\n  float: right;\n}\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n.jumbotron {\n  padding: 30px 15px;\n  margin-bottom: 30px;\n  color: inherit;\n  background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n  color: inherit;\n}\n.jumbotron p {\n  margin-bottom: 15px;\n  font-size: 21px;\n  font-weight: 200;\n}\n.jumbotron > hr {\n  border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n  border-radius: 6px;\n}\n.jumbotron .container {\n  max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding: 48px 0;\n  }\n  .container .jumbotron,\n  .container-fluid .jumbotron {\n    padding-left: 60px;\n    padding-right: 60px;\n  }\n  .jumbotron h1,\n  .jumbotron .h1 {\n    font-size: 63px;\n  }\n}\n.thumbnail {\n  display: block;\n  padding: 4px;\n  margin-bottom: 20px;\n  line-height: 1.42857143;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: border 0.2s ease-in-out;\n  -o-transition: border 0.2s ease-in-out;\n  transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n  margin-left: auto;\n  margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n  border-color: #337ab7;\n}\n.thumbnail .caption {\n  padding: 9px;\n  color: #333333;\n}\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n.alert .alert-link {\n  font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n.alert > p + p {\n  margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n  padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n.alert-success {\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n  color: #3c763d;\n}\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n  color: #2b542c;\n}\n.alert-info {\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n  color: #31708f;\n}\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n  color: #245269;\n}\n.alert-warning {\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n  color: #8a6d3b;\n}\n.alert-warning hr {\n  border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n  color: #66512c;\n}\n.alert-danger {\n  background-color: #f2dede;\n  border-color: #ebccd1;\n  color: #a94442;\n}\n.alert-danger hr {\n  border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n  color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n.progress {\n  overflow: hidden;\n  height: 20px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: 12px;\n  line-height: 20px;\n  color: #ffffff;\n  text-align: center;\n  background-color: #337ab7;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-transition: width 0.6s ease;\n  -o-transition: width 0.6s ease;\n  transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n  -o-animation: progress-bar-stripes 2s linear infinite;\n  animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n  margin-top: 15px;\n}\n.media:first-child {\n  margin-top: 0;\n}\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n.media-body {\n  width: 10000px;\n}\n.media-object {\n  display: block;\n}\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n.media-middle {\n  vertical-align: middle;\n}\n.media-bottom {\n  vertical-align: bottom;\n}\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n.list-group {\n  margin-bottom: 20px;\n  padding-left: 0;\n}\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\na.list-group-item {\n  color: #555555;\n}\na.list-group-item .list-group-item-heading {\n  color: #333333;\n}\na.list-group-item:hover,\na.list-group-item:focus {\n  text-decoration: none;\n  color: #555555;\n  background-color: #f5f5f5;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n  background-color: #eeeeee;\n  color: #777777;\n  cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n  color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n  color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n  color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n  color: #c7ddef;\n}\n.list-group-item-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n}\na.list-group-item-success {\n  color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-success:hover,\na.list-group-item-success:focus {\n  color: #3c763d;\n  background-color: #d0e9c6;\n}\na.list-group-item-success.active,\na.list-group-item-success.active:hover,\na.list-group-item-success.active:focus {\n  color: #fff;\n  background-color: #3c763d;\n  border-color: #3c763d;\n}\n.list-group-item-info {\n  color: #31708f;\n  background-color: #d9edf7;\n}\na.list-group-item-info {\n  color: #31708f;\n}\na.list-group-item-info .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-info:hover,\na.list-group-item-info:focus {\n  color: #31708f;\n  background-color: #c4e3f3;\n}\na.list-group-item-info.active,\na.list-group-item-info.active:hover,\na.list-group-item-info.active:focus {\n  color: #fff;\n  background-color: #31708f;\n  border-color: #31708f;\n}\n.list-group-item-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n}\na.list-group-item-warning {\n  color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-warning:hover,\na.list-group-item-warning:focus {\n  color: #8a6d3b;\n  background-color: #faf2cc;\n}\na.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus {\n  color: #fff;\n  background-color: #8a6d3b;\n  border-color: #8a6d3b;\n}\n.list-group-item-danger {\n  color: #a94442;\n  background-color: #f2dede;\n}\na.list-group-item-danger {\n  color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-danger:hover,\na.list-group-item-danger:focus {\n  color: #a94442;\n  background-color: #ebcccc;\n}\na.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus {\n  color: #fff;\n  background-color: #a94442;\n  border-color: #a94442;\n}\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n.panel {\n  margin-bottom: 20px;\n  background-color: #ffffff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n  padding: 15px;\n}\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n  color: inherit;\n}\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n  color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n  color: inherit;\n}\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #dddddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n  margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n  border-width: 1px 0;\n  border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n  border-top: 0;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n  border-bottom: 0;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n  margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n  border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n  border-bottom-left-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n  border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n  border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n  border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n  border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n  border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n  border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n  border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n  border-bottom: 0;\n}\n.panel > .table-responsive {\n  border: 0;\n  margin-bottom: 0;\n}\n.panel-group {\n  margin-bottom: 20px;\n}\n.panel-group .panel {\n  margin-bottom: 0;\n  border-radius: 4px;\n}\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n  border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n  border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n  border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n  color: #333333;\n  background-color: #f5f5f5;\n  border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n  color: #f5f5f5;\n  background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #dddddd;\n}\n.panel-primary {\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n  color: #ffffff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n  color: #337ab7;\n  background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #337ab7;\n}\n.panel-success {\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n  color: #dff0d8;\n  background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n.panel-info {\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n  color: #d9edf7;\n  background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #bce8f1;\n}\n.panel-warning {\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n  color: #fcf8e3;\n  background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #faebcc;\n}\n.panel-danger {\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n  color: #f2dede;\n  background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  height: 100%;\n  width: 100%;\n  border: 0;\n}\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000000;\n  text-shadow: 0 1px 0 #ffffff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n  color: #000000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n.modal-open {\n  overflow: hidden;\n}\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1050;\n  -webkit-overflow-scrolling: touch;\n  outline: 0;\n}\n.modal.fade .modal-dialog {\n  -webkit-transform: translate(0, -25%);\n  -ms-transform: translate(0, -25%);\n  -o-transform: translate(0, -25%);\n  transform: translate(0, -25%);\n  -webkit-transition: -webkit-transform 0.3s ease-out;\n  -moz-transition: -moz-transform 0.3s ease-out;\n  -o-transition: -o-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n  -ms-transform: translate(0, 0);\n  -o-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n.modal-content {\n  position: relative;\n  background-color: #ffffff;\n  border: 1px solid #999999;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  background-clip: padding-box;\n  outline: 0;\n}\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  background-color: #000000;\n}\n.modal-backdrop.fade {\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n.modal-header {\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n  min-height: 16.42857143px;\n}\n.modal-header .close {\n  margin-top: -2px;\n}\n.modal-title {\n  margin: 0;\n  line-height: 1.42857143;\n}\n.modal-body {\n  position: relative;\n  padding: 15px;\n}\n.modal-footer {\n  padding: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n  margin-left: 5px;\n  margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n@media (min-width: 768px) {\n  .modal-dialog {\n    width: 600px;\n    margin: 30px auto;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg {\n    width: 900px;\n  }\n}\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 12px;\n  font-weight: normal;\n  line-height: 1.4;\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.tooltip.in {\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.tooltip.top {\n  margin-top: -3px;\n  padding: 5px 0;\n}\n.tooltip.right {\n  margin-left: 3px;\n  padding: 0 5px;\n}\n.tooltip.bottom {\n  margin-top: 3px;\n  padding: 5px 0;\n}\n.tooltip.left {\n  margin-left: -3px;\n  padding: 0 5px;\n}\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #ffffff;\n  text-align: center;\n  text-decoration: none;\n  background-color: #000000;\n  border-radius: 4px;\n}\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n  bottom: 0;\n  right: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-width: 5px 5px 5px 0;\n  border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-width: 5px 0 5px 5px;\n  border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000000;\n}\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1.42857143;\n  text-align: left;\n  background-color: #ffffff;\n  background-clip: padding-box;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  white-space: normal;\n}\n.popover.top {\n  margin-top: -10px;\n}\n.popover.right {\n  margin-left: 10px;\n}\n.popover.bottom {\n  margin-top: 10px;\n}\n.popover.left {\n  margin-left: -10px;\n}\n.popover-title {\n  margin: 0;\n  padding: 8px 14px;\n  font-size: 14px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n.popover-content {\n  padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.popover > .arrow {\n  border-width: 11px;\n}\n.popover > .arrow:after {\n  border-width: 10px;\n  content: \"\";\n}\n.popover.top > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-bottom-width: 0;\n  border-top-color: #999999;\n  border-top-color: rgba(0, 0, 0, 0.25);\n  bottom: -11px;\n}\n.popover.top > .arrow:after {\n  content: \" \";\n  bottom: 1px;\n  margin-left: -10px;\n  border-bottom-width: 0;\n  border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-left-width: 0;\n  border-right-color: #999999;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n  content: \" \";\n  left: 1px;\n  bottom: -10px;\n  border-left-width: 0;\n  border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-top-width: 0;\n  border-bottom-color: #999999;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n  top: -11px;\n}\n.popover.bottom > .arrow:after {\n  content: \" \";\n  top: 1px;\n  margin-left: -10px;\n  border-top-width: 0;\n  border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-right-width: 0;\n  border-left-color: #999999;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n  content: \" \";\n  right: 1px;\n  border-right-width: 0;\n  border-left-color: #ffffff;\n  bottom: -10px;\n}\n.carousel {\n  position: relative;\n}\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n}\n.carousel-inner > .item {\n  display: none;\n  position: relative;\n  -webkit-transition: 0.6s ease-in-out left;\n  -o-transition: 0.6s ease-in-out left;\n  transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n  .carousel-inner > .item {\n    -webkit-transition: -webkit-transform 0.6s ease-in-out;\n    -moz-transition: -moz-transform 0.6s ease-in-out;\n    -o-transition: -o-transform 0.6s ease-in-out;\n    transition: transform 0.6s ease-in-out;\n    -webkit-backface-visibility: hidden;\n    -moz-backface-visibility: hidden;\n    backface-visibility: hidden;\n    -webkit-perspective: 1000;\n    -moz-perspective: 1000;\n    perspective: 1000;\n  }\n  .carousel-inner > .item.next,\n  .carousel-inner > .item.active.right {\n    -webkit-transform: translate3d(100%, 0, 0);\n    transform: translate3d(100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.prev,\n  .carousel-inner > .item.active.left {\n    -webkit-transform: translate3d(-100%, 0, 0);\n    transform: translate3d(-100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.next.left,\n  .carousel-inner > .item.prev.right,\n  .carousel-inner > .item.active {\n    -webkit-transform: translate3d(0, 0, 0);\n    transform: translate3d(0, 0, 0);\n    left: 0;\n  }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n.carousel-inner > .active {\n  left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.carousel-inner > .next {\n  left: 100%;\n}\n.carousel-inner > .prev {\n  left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n.carousel-inner > .active.left {\n  left: -100%;\n}\n.carousel-inner > .active.right {\n  left: 100%;\n}\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: 15%;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n  font-size: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n  left: auto;\n  right: 0;\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n  outline: 0;\n  color: #ffffff;\n  text-decoration: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n  display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n  left: 50%;\n  margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n  right: 50%;\n  margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  margin-top: -10px;\n  line-height: 1;\n  font-family: serif;\n}\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n}\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  border: 1px solid #ffffff;\n  border-radius: 10px;\n  cursor: pointer;\n  background-color: #000 \\9;\n  background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n  margin: 0;\n  width: 12px;\n  height: 12px;\n  background-color: #ffffff;\n}\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n  text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -15px;\n    font-size: 30px;\n  }\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .icon-prev {\n    margin-left: -15px;\n  }\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-next {\n    margin-right: -15px;\n  }\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n  content: \" \";\n  display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n  clear: both;\n}\n.center-block {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n.hidden {\n  display: none !important;\n}\n.affix {\n  position: fixed;\n}\n@-ms-viewport {\n  width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  table.visible-xs {\n    display: table;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-block {\n    display: block !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline {\n    display: inline !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  table.visible-sm {\n    display: table;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-block {\n    display: block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  table.visible-md {\n    display: table;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-block {\n    display: block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  table.visible-lg {\n    display: table;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-block {\n    display: block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (max-width: 767px) {\n  .hidden-xs {\n    display: none !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm {\n    display: none !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md {\n    display: none !important;\n  }\n}\n@media (min-width: 1200px) {\n  .hidden-lg {\n    display: none !important;\n  }\n}\n.visible-print {\n  display: none !important;\n}\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  table.visible-print {\n    display: table;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n}\n.visible-print-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-block {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline-block {\n    display: inline-block !important;\n  }\n}\n@media print {\n  .hidden-print {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n//    user zoom.\n//\n\nhtml {\n  font-family: sans-serif; // 1\n  -ms-text-size-adjust: 100%; // 2\n  -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n  margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; // 1\n  vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background-color: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n//    (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box; // 2\n  box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n    *,\n    *:before,\n    *:after {\n        background: transparent !important;\n        color: #000 !important; // Black prints faster: h5bp.com/s\n        box-shadow: none !important;\n        text-shadow: none !important;\n    }\n\n    a,\n    a:visited {\n        text-decoration: underline;\n    }\n\n    a[href]:after {\n        content: \" (\" attr(href) \")\";\n    }\n\n    abbr[title]:after {\n        content: \" (\" attr(title) \")\";\n    }\n\n    // Don't show links that are fragment identifiers,\n    // or use the `javascript:` pseudo protocol\n    a[href^=\"#\"]:after,\n    a[href^=\"javascript:\"]:after {\n        content: \"\";\n    }\n\n    pre,\n    blockquote {\n        border: 1px solid #999;\n        page-break-inside: avoid;\n    }\n\n    thead {\n        display: table-header-group; // h5bp.com/t\n    }\n\n    tr,\n    img {\n        page-break-inside: avoid;\n    }\n\n    img {\n        max-width: 100% !important;\n    }\n\n    p,\n    h2,\n    h3 {\n        orphans: 3;\n        widows: 3;\n    }\n\n    h2,\n    h3 {\n        page-break-after: avoid;\n    }\n\n    // Bootstrap specific changes start\n    //\n    // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n    // Once fixed, we can just straight up remove this.\n    select {\n        background: #fff !important;\n    }\n\n    // Bootstrap components\n    .navbar {\n        display: none;\n    }\n    .btn,\n    .dropup > .btn {\n        > .caret {\n            border-top-color: #000 !important;\n        }\n    }\n    .label {\n        border: 1px solid #000;\n    }\n\n    .table {\n        border-collapse: collapse !important;\n\n        td,\n        th {\n            background-color: #fff !important;\n        }\n    }\n    .table-bordered {\n        th,\n        td {\n            border: 1px solid #ddd !important;\n        }\n    }\n\n    // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// <a href=\"#\"><span class=\"glyphicon glyphicon-star\"></span> Star</a>\n\n// Import the fonts\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('@{icon-font-path}@{icon-font-name}.eot');\n  src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n       url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n       url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n       url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n       url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk               { &:before { content: \"\\2a\"; } }\n.glyphicon-plus                   { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur                    { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus                  { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud                  { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope               { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil                 { &:before { content: \"\\270f\"; } }\n.glyphicon-glass                  { &:before { content: \"\\e001\"; } }\n.glyphicon-music                  { &:before { content: \"\\e002\"; } }\n.glyphicon-search                 { &:before { content: \"\\e003\"; } }\n.glyphicon-heart                  { &:before { content: \"\\e005\"; } }\n.glyphicon-star                   { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty             { &:before { content: \"\\e007\"; } }\n.glyphicon-user                   { &:before { content: \"\\e008\"; } }\n.glyphicon-film                   { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large               { &:before { content: \"\\e010\"; } }\n.glyphicon-th                     { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list                { &:before { content: \"\\e012\"; } }\n.glyphicon-ok                     { &:before { content: \"\\e013\"; } }\n.glyphicon-remove                 { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in                { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out               { &:before { content: \"\\e016\"; } }\n.glyphicon-off                    { &:before { content: \"\\e017\"; } }\n.glyphicon-signal                 { &:before { content: \"\\e018\"; } }\n.glyphicon-cog                    { &:before { content: \"\\e019\"; } }\n.glyphicon-trash                  { &:before { content: \"\\e020\"; } }\n.glyphicon-home                   { &:before { content: \"\\e021\"; } }\n.glyphicon-file                   { &:before { content: \"\\e022\"; } }\n.glyphicon-time                   { &:before { content: \"\\e023\"; } }\n.glyphicon-road                   { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt           { &:before { content: \"\\e025\"; } }\n.glyphicon-download               { &:before { content: \"\\e026\"; } }\n.glyphicon-upload                 { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox                  { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle            { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat                 { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh                { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt               { &:before { content: \"\\e032\"; } }\n.glyphicon-lock                   { &:before { content: \"\\e033\"; } }\n.glyphicon-flag                   { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones             { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off             { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down            { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up              { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode                 { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode                { &:before { content: \"\\e040\"; } }\n.glyphicon-tag                    { &:before { content: \"\\e041\"; } }\n.glyphicon-tags                   { &:before { content: \"\\e042\"; } }\n.glyphicon-book                   { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark               { &:before { content: \"\\e044\"; } }\n.glyphicon-print                  { &:before { content: \"\\e045\"; } }\n.glyphicon-camera                 { &:before { content: \"\\e046\"; } }\n.glyphicon-font                   { &:before { content: \"\\e047\"; } }\n.glyphicon-bold                   { &:before { content: \"\\e048\"; } }\n.glyphicon-italic                 { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height            { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width             { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left             { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center           { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right            { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify          { &:before { content: \"\\e055\"; } }\n.glyphicon-list                   { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left            { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right           { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video         { &:before { content: \"\\e059\"; } }\n.glyphicon-picture                { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker             { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust                 { &:before { content: \"\\e063\"; } }\n.glyphicon-tint                   { &:before { content: \"\\e064\"; } }\n.glyphicon-edit                   { &:before { content: \"\\e065\"; } }\n.glyphicon-share                  { &:before { content: \"\\e066\"; } }\n.glyphicon-check                  { &:before { content: \"\\e067\"; } }\n.glyphicon-move                   { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward          { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward          { &:before { content: \"\\e070\"; } }\n.glyphicon-backward               { &:before { content: \"\\e071\"; } }\n.glyphicon-play                   { &:before { content: \"\\e072\"; } }\n.glyphicon-pause                  { &:before { content: \"\\e073\"; } }\n.glyphicon-stop                   { &:before { content: \"\\e074\"; } }\n.glyphicon-forward                { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward           { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward           { &:before { content: \"\\e077\"; } }\n.glyphicon-eject                  { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left           { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right          { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign              { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign             { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign            { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign                { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign          { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign              { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot             { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle          { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle              { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle             { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left             { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right            { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up               { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down             { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt              { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full            { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small           { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign       { &:before { content: \"\\e101\"; } }\n.glyphicon-gift                   { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf                   { &:before { content: \"\\e103\"; } }\n.glyphicon-fire                   { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open               { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close              { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign           { &:before { content: \"\\e107\"; } }\n.glyphicon-plane                  { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar               { &:before { content: \"\\e109\"; } }\n.glyphicon-random                 { &:before { content: \"\\e110\"; } }\n.glyphicon-comment                { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet                 { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up             { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down           { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet                { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart          { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close           { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open            { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical        { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal      { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd                    { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn               { &:before { content: \"\\e122\"; } }\n.glyphicon-bell                   { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate            { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up              { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down            { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right             { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left              { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up                { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down              { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right     { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left      { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up        { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down      { &:before { content: \"\\e134\"; } }\n.glyphicon-globe                  { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench                 { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks                  { &:before { content: \"\\e137\"; } }\n.glyphicon-filter                 { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase              { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen             { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard              { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip              { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty            { &:before { content: \"\\e143\"; } }\n.glyphicon-link                   { &:before { content: \"\\e144\"; } }\n.glyphicon-phone                  { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin                { &:before { content: \"\\e146\"; } }\n.glyphicon-usd                    { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp                    { &:before { content: \"\\e149\"; } }\n.glyphicon-sort                   { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet       { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt   { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order          { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt      { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes     { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked              { &:before { content: \"\\e157\"; } }\n.glyphicon-expand                 { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down          { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up            { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in                 { &:before { content: \"\\e161\"; } }\n.glyphicon-flash                  { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out                { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window             { &:before { content: \"\\e164\"; } }\n.glyphicon-record                 { &:before { content: \"\\e165\"; } }\n.glyphicon-save                   { &:before { content: \"\\e166\"; } }\n.glyphicon-open                   { &:before { content: \"\\e167\"; } }\n.glyphicon-saved                  { &:before { content: \"\\e168\"; } }\n.glyphicon-import                 { &:before { content: \"\\e169\"; } }\n.glyphicon-export                 { &:before { content: \"\\e170\"; } }\n.glyphicon-send                   { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk            { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved           { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove          { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save            { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open            { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card            { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer               { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery                { &:before { content: \"\\e179\"; } }\n.glyphicon-header                 { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed             { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone               { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt              { &:before { content: \"\\e183\"; } }\n.glyphicon-tower                  { &:before { content: \"\\e184\"; } }\n.glyphicon-stats                  { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video               { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video               { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles              { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo           { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby            { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1              { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1              { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1              { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark         { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark      { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download         { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload           { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer           { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous         { &:before { content: \"\\e200\"; } }\n.glyphicon-cd                     { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file              { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file              { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up               { &:before { content: \"\\e204\"; } }\n.glyphicon-copy                   { &:before { content: \"\\e205\"; } }\n.glyphicon-paste                  { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door                   { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key                    { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert                  { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer              { &:before { content: \"\\e210\"; } }\n.glyphicon-king                   { &:before { content: \"\\e211\"; } }\n.glyphicon-queen                  { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn                   { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop                 { &:before { content: \"\\e214\"; } }\n.glyphicon-knight                 { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula           { &:before { content: \"\\e216\"; } }\n.glyphicon-tent                   { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard             { &:before { content: \"\\e218\"; } }\n.glyphicon-bed                    { &:before { content: \"\\e219\"; } }\n.glyphicon-apple                  { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase                  { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass              { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp                   { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate              { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank             { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors               { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin                { &:before { content: \"\\e227\"; } }\n.glyphicon-btc                    { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt                    { &:before { content: \"\\e227\"; } }\n.glyphicon-yen                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble                  { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub                    { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale                  { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly              { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted       { &:before { content: \"\\e232\"; } }\n.glyphicon-education              { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal      { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical        { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger         { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window           { &:before { content: \"\\e237\"; } }\n.glyphicon-oil                    { &:before { content: \"\\e238\"; } }\n.glyphicon-grain                  { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses             { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size              { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color             { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background        { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top       { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom    { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left      { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical  { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right     { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right         { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left          { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom        { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top           { &:before { content: \"\\e253\"; } }\n.glyphicon-console                { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript            { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript              { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left              { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right             { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down              { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up                { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n  .box-sizing(border-box);\n}\n*:before,\n*:after {\n  .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @text-color;\n  background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\n// Links\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n  }\n\n  &:focus {\n    .tab-focus();\n  }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n  margin: 0;\n}\n\n\n// Images\n\nimg {\n  vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n  .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n  border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n  padding: @thumbnail-padding;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  // Keep them at most 100% wide\n  .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n  border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n  margin-top:    @line-height-computed;\n  margin-bottom: @line-height-computed;\n  border: 0;\n  border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0,0,0,0);\n  border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n  &:active,\n  &:focus {\n    position: static;\n    width: auto;\n    height: auto;\n    margin: 0;\n    overflow: visible;\n    clip: auto;\n  }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n// Upstream patch for normalize.css submitted: https://github.com/necolas/normalize.css/pull/379 - remove this fix once that is merged\n\n[role=\"button\"] {\n  cursor: pointer;\n}","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  font-family: @headings-font-family;\n  font-weight: @headings-font-weight;\n  line-height: @headings-line-height;\n  color: @headings-color;\n\n  small,\n  .small {\n    font-weight: normal;\n    line-height: 1;\n    color: @headings-small-color;\n  }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n  margin-top: @line-height-computed;\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 65%;\n  }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n  margin-top: (@line-height-computed / 2);\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 75%;\n  }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n  margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n  margin-bottom: @line-height-computed;\n  font-size: floor((@font-size-base * 1.15));\n  font-weight: 300;\n  line-height: 1.4;\n\n  @media (min-width: @screen-sm-min) {\n    font-size: (@font-size-base * 1.5);\n  }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n  font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n  background-color: @state-warning-bg;\n  padding: .2em;\n}\n\n// Alignment\n.text-left           { text-align: left; }\n.text-right          { text-align: right; }\n.text-center         { text-align: center; }\n.text-justify        { text-align: justify; }\n.text-nowrap         { white-space: nowrap; }\n\n// Transformation\n.text-lowercase      { text-transform: lowercase; }\n.text-uppercase      { text-transform: uppercase; }\n.text-capitalize     { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n  color: @text-muted;\n}\n.text-primary {\n  .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n  .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n  .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n  .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n  .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n  // Given the contrast here, this is the only class to have its color inverted\n  // automatically.\n  color: #fff;\n  .bg-variant(@brand-primary);\n}\n.bg-success {\n  .bg-variant(@state-success-bg);\n}\n.bg-info {\n  .bg-variant(@state-info-bg);\n}\n.bg-warning {\n  .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n  .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n  padding-bottom: ((@line-height-computed / 2) - 1);\n  margin: (@line-height-computed * 2) 0 @line-height-computed;\n  border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n  margin-top: 0;\n  margin-bottom: (@line-height-computed / 2);\n  ul,\n  ol {\n    margin-bottom: 0;\n  }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n  .list-unstyled();\n  margin-left: -5px;\n\n  > li {\n    display: inline-block;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n// Description Lists\ndl {\n  margin-top: 0; // Remove browser default\n  margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n  line-height: @line-height-base;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n  dd {\n    &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    dt {\n      float: left;\n      width: (@dl-horizontal-offset - 20);\n      clear: left;\n      text-align: right;\n      .text-overflow();\n    }\n    dd {\n      margin-left: @dl-horizontal-offset;\n    }\n  }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n  font-size: 90%;\n  .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n  padding: (@line-height-computed / 2) @line-height-computed;\n  margin: 0 0 @line-height-computed;\n  font-size: @blockquote-font-size;\n  border-left: 5px solid @blockquote-border-color;\n\n  p,\n  ul,\n  ol {\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  // Note: Deprecated small and .small as of v3.1.0\n  // Context: https://github.com/twbs/bootstrap/issues/11660\n  footer,\n  small,\n  .small {\n    display: block;\n    font-size: 80%; // back to default font-size\n    line-height: @line-height-base;\n    color: @blockquote-small-color;\n\n    &:before {\n      content: '\\2014 \\00A0'; // em dash, nbsp\n    }\n  }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid @blockquote-border-color;\n  border-left: 0;\n  text-align: right;\n\n  // Account for citation\n  footer,\n  small,\n  .small {\n    &:before { content: ''; }\n    &:after {\n      content: '\\00A0 \\2014'; // nbsp, em dash\n    }\n  }\n}\n\n// Addresses\naddress {\n  margin-bottom: @line-height-computed;\n  font-style: normal;\n  line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover {\n    color: darken(@color, 10%);\n  }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover {\n    background-color: darken(@color, 10%);\n  }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n  font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @code-color;\n  background-color: @code-bg;\n  border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @kbd-color;\n  background-color: @kbd-bg;\n  border-radius: @border-radius-small;\n  box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n  kbd {\n    padding: 0;\n    font-size: 100%;\n    font-weight: bold;\n    box-shadow: none;\n  }\n}\n\n// Blocks of code\npre {\n  display: block;\n  padding: ((@line-height-computed - 1) / 2);\n  margin: 0 0 (@line-height-computed / 2);\n  font-size: (@font-size-base - 1); // 14px to 13px\n  line-height: @line-height-base;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: @pre-color;\n  background-color: @pre-bg;\n  border: 1px solid @pre-border-color;\n  border-radius: @border-radius-base;\n\n  // Account for some code outputs that place code tags in pre tags\n  code {\n    padding: 0;\n    font-size: inherit;\n    color: inherit;\n    white-space: pre-wrap;\n    background-color: transparent;\n    border-radius: 0;\n  }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n  max-height: @pre-scrollable-max-height;\n  overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n  .container-fixed();\n\n  @media (min-width: @screen-sm-min) {\n    width: @container-sm;\n  }\n  @media (min-width: @screen-md-min) {\n    width: @container-md;\n  }\n  @media (min-width: @screen-lg-min) {\n    width: @container-lg;\n  }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n  .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n  .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n  .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n  .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n  .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  (@gutter / -2);\n  margin-right: (@gutter / -2);\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n  left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n  right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  (@grid-gutter-width / 2);\n      padding-right: (@grid-gutter-width / 2);\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n  .col-@{class}-push-0 {\n    left: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n  .col-@{class}-pull-0 {\n    right: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  background-color: @table-bg;\n}\ncaption {\n  padding-top: @table-cell-padding;\n  padding-bottom: @table-cell-padding;\n  color: @text-muted;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: @line-height-computed;\n  // Cells\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-cell-padding;\n        line-height: @line-height-base;\n        vertical-align: top;\n        border-top: 1px solid @table-border-color;\n      }\n    }\n  }\n  // Bottom align for column headings\n  > thead > tr > th {\n    vertical-align: bottom;\n    border-bottom: 2px solid @table-border-color;\n  }\n  // Remove top border from thead by default\n  > caption + thead,\n  > colgroup + thead,\n  > thead:first-child {\n    > tr:first-child {\n      > th,\n      > td {\n        border-top: 0;\n      }\n    }\n  }\n  // Account for multiple tbody instances\n  > tbody + tbody {\n    border-top: 2px solid @table-border-color;\n  }\n\n  // Nesting\n  .table {\n    background-color: @body-bg;\n  }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-condensed-cell-padding;\n      }\n    }\n  }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n  border: 1px solid @table-border-color;\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        border: 1px solid @table-border-color;\n      }\n    }\n  }\n  > thead > tr {\n    > th,\n    > td {\n      border-bottom-width: 2px;\n    }\n  }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n  > tbody > tr:nth-of-type(odd) {\n    background-color: @table-bg-accent;\n  }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n  > tbody > tr:hover {\n    background-color: @table-bg-hover;\n  }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n  position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n  float: none;\n  display: table-column;\n}\ntable {\n  td,\n  th {\n    &[class*=\"col-\"] {\n      position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n      float: none;\n      display: table-cell;\n    }\n  }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n  @media screen and (max-width: @screen-xs-max) {\n    width: 100%;\n    margin-bottom: (@line-height-computed * 0.75);\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid @table-border-color;\n\n    // Tighten up spacing\n    > .table {\n      margin-bottom: 0;\n\n      // Ensure the content doesn't wrap\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th,\n          > td {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n\n    // Special overrides for the bordered tables\n    > .table-bordered {\n      border: 0;\n\n      // Nuke the appropriate borders so that the parent can handle them\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th:first-child,\n          > td:first-child {\n            border-left: 0;\n          }\n          > th:last-child,\n          > td:last-child {\n            border-right: 0;\n          }\n        }\n      }\n\n      // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n      // chances are there will be only one `tr` in a `thead` and that would\n      // remove the border altogether.\n      > tbody,\n      > tfoot {\n        > tr:last-child {\n          > th,\n          > td {\n            border-bottom: 0;\n          }\n        }\n      }\n\n    }\n  }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &:hover > .@{state},\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n  // so we reset that to ensure it behaves more like a standard block element.\n  // See https://github.com/twbs/bootstrap/issues/12359.\n  min-width: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: @line-height-computed;\n  font-size: (@font-size-base * 1.5);\n  line-height: inherit;\n  color: @legend-color;\n  border: 0;\n  border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n  display: inline-block;\n  max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n  .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9; // IE8-9\n  line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n  display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  .tab-focus();\n}\n\n// Adjust output element\noutput {\n  display: block;\n  padding-top: (@padding-base-vertical + 1);\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n  background-color: @input-bg;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid @input-border;\n  border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS.\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n  .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n  // Customize the `:focus` state to imitate native WebKit styles.\n  .form-control-focus();\n\n  // Placeholder\n  .placeholder();\n\n  // Disabled and read-only inputs\n  //\n  // HTML5 says that controls under a fieldset > legend:first-child won't be\n  // disabled if the fieldset is disabled. Due to implementation difficulty, we\n  // don't honor that edge case; we style them as disabled anyway.\n  &[disabled],\n  &[readonly],\n  fieldset[disabled] & {\n    background-color: @input-bg-disabled;\n    opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n  }\n\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n\n  // Reset height for `textarea`s\n  textarea& {\n    height: auto;\n  }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"],\n  input[type=\"time\"],\n  input[type=\"datetime-local\"],\n  input[type=\"month\"] {\n    line-height: @input-height-base;\n\n    &.input-sm,\n    .input-group-sm & {\n      line-height: @input-height-small;\n    }\n\n    &.input-lg,\n    .input-group-lg & {\n      line-height: @input-height-large;\n    }\n  }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n  margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n\n  label {\n    min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n    padding-left: 20px;\n    margin-bottom: 0;\n    font-weight: normal;\n    cursor: pointer;\n  }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because <label>s don't inherit their parent's `cursor`.\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  &[disabled],\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used directly on <label>s\n.radio-inline,\n.checkbox-inline {\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used on elements with <label> descendants\n.radio,\n.checkbox {\n  &.disabled,\n  fieldset[disabled] & {\n    label {\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n  // Size it appropriately next to real form controls\n  padding-top: (@padding-base-vertical + 1);\n  padding-bottom: (@padding-base-vertical + 1);\n  // Remove default margin from `p`\n  margin-bottom: 0;\n  min-height: (@line-height-computed + @font-size-base);\n\n  &.input-lg,\n  &.input-sm {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n//\n// The `.form-group-* form-control` variations are sadly duplicated to avoid the\n// issue documented in https://github.com/twbs/bootstrap/issues/15074.\n\n.input-sm {\n  .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n}\n.form-group-sm {\n  .form-control {\n    .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n  }\n  .form-control-static {\n    height: @input-height-small;\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    line-height: @line-height-small;\n    min-height: (@line-height-computed + @font-size-small);\n  }\n}\n\n.input-lg {\n  .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n}\n.form-group-lg {\n  .form-control {\n    .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n  }\n  .form-control-static {\n    height: @input-height-large;\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    line-height: @line-height-large;\n    min-height: (@line-height-computed + @font-size-large);\n  }\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n  // Enable absolute positioning\n  position: relative;\n\n  // Ensure icons don't overlap text\n  .form-control {\n    padding-right: (@input-height-base * 1.25);\n  }\n}\n// Feedback icon (requires .glyphicon classes)\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2; // Ensure icon is above input groups\n  display: block;\n  width: @input-height-base;\n  height: @input-height-base;\n  line-height: @input-height-base;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback {\n  width: @input-height-large;\n  height: @input-height-large;\n  line-height: @input-height-large;\n}\n.input-sm + .form-control-feedback {\n  width: @input-height-small;\n  height: @input-height-small;\n  line-height: @input-height-small;\n}\n\n// Feedback states\n.has-success {\n  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n// Reposition feedback icon if input has visible label above\n.has-feedback label {\n\n  & ~ .form-control-feedback {\n     top: (@line-height-computed + 5); // Height of the `label` and its margin\n  }\n  &.sr-only ~ .form-control-feedback {\n     top: 0;\n  }\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n  display: block; // account for any element using help-block\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n  // Kick in the inline\n  @media (min-width: @screen-sm-min) {\n    // Inline-block all the things for \"inline\"\n    .form-group {\n      display: inline-block;\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // In navbar-form, allow folks to *not* use `.form-group`\n    .form-control {\n      display: inline-block;\n      width: auto; // Prevent labels from stacking above inputs in `.form-group`\n      vertical-align: middle;\n    }\n\n    // Make static controls behave like regular ones\n    .form-control-static {\n      display: inline-block;\n    }\n\n    .input-group {\n      display: inline-table;\n      vertical-align: middle;\n\n      .input-group-addon,\n      .input-group-btn,\n      .form-control {\n        width: auto;\n      }\n    }\n\n    // Input groups need that 100% width though\n    .input-group > .form-control {\n      width: 100%;\n    }\n\n    .control-label {\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // Remove default margin on radios/checkboxes that were used for stacking, and\n    // then undo the floating of radios and checkboxes to match.\n    .radio,\n    .checkbox {\n      display: inline-block;\n      margin-top: 0;\n      margin-bottom: 0;\n      vertical-align: middle;\n\n      label {\n        padding-left: 0;\n      }\n    }\n    .radio input[type=\"radio\"],\n    .checkbox input[type=\"checkbox\"] {\n      position: relative;\n      margin-left: 0;\n    }\n\n    // Re-override the feedback icon.\n    .has-feedback .form-control-feedback {\n      top: 0;\n    }\n  }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n  // Consistent vertical alignment of radios and checkboxes\n  //\n  // Labels also get some reset styles, but that is scoped to a media query below.\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n  }\n  // Account for padding we're adding to ensure the alignment and of help text\n  // and other content below items\n  .radio,\n  .checkbox {\n    min-height: (@line-height-computed + (@padding-base-vertical + 1));\n  }\n\n  // Make form groups behave like rows\n  .form-group {\n    .make-row();\n  }\n\n  // Reset spacing and right align labels, but scope to media queries so that\n  // labels on narrow viewports stack the same as a default form example.\n  @media (min-width: @screen-sm-min) {\n    .control-label {\n      text-align: right;\n      margin-bottom: 0;\n      padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n    }\n  }\n\n  // Validation states\n  //\n  // Reposition the icon because it's now within a grid column and columns have\n  // `position: relative;` on them. Also accounts for the grid gutter padding.\n  .has-feedback .form-control-feedback {\n    right: (@grid-gutter-width / 2);\n  }\n\n  // Form group sizes\n  //\n  // Quick utility class for applying `.input-lg` and `.input-sm` styles to the\n  // inputs and labels within a `.form-group`.\n  .form-group-lg {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: ((@padding-large-vertical * @line-height-large) + 1);\n      }\n    }\n  }\n  .form-group-sm {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: (@padding-small-vertical + 1);\n      }\n    }\n  }\n}\n","// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline,\n  &.radio label,\n  &.checkbox label,\n  &.radio-inline label,\n  &.checkbox-inline label  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-border-focus` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n  display: inline-block;\n  margin-bottom: 0; // For input.btn\n  font-weight: @btn-font-weight;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  white-space: nowrap;\n  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);\n  .user-select(none);\n\n  &,\n  &:active,\n  &.active {\n    &:focus,\n    &.focus {\n      .tab-focus();\n    }\n  }\n\n  &:hover,\n  &:focus,\n  &.focus {\n    color: @btn-default-color;\n    text-decoration: none;\n  }\n\n  &:active,\n  &.active {\n    outline: 0;\n    background-image: none;\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n    pointer-events: none; // Future-proof disabling of clicks\n    .opacity(.65);\n    .box-shadow(none);\n  }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n  .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n  .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n  .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n  .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n  color: @link-color;\n  font-weight: normal;\n  border-radius: 0;\n\n  &,\n  &:active,\n  &.active,\n  &[disabled],\n  fieldset[disabled] & {\n    background-color: transparent;\n    .box-shadow(none);\n  }\n  &,\n  &:hover,\n  &:focus,\n  &:active {\n    border-color: transparent;\n  }\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n    background-color: transparent;\n  }\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus {\n      color: @btn-link-disabled-color;\n      text-decoration: none;\n    }\n  }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n  // line-height: ensure even-numbered height of button next to large input\n  .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n.btn-sm {\n  // line-height: ensure proper height of button next to small input\n  .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n.btn-xs {\n  .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  &.btn-block {\n    width: 100%;\n  }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:hover,\n  &:focus,\n  &.focus,\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &.focus,\n    &:active,\n    &.active {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n  opacity: 0;\n  .transition(opacity .15s linear);\n  &.in {\n    opacity: 1;\n  }\n}\n\n.collapse {\n  display: none;\n\n  &.in      { display: block; }\n  tr&.in    { display: table-row; }\n  tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  .transition-property(~\"height, visibility\");\n  .transition-duration(.35s);\n  .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top:   @caret-width-base dashed;\n  border-right: @caret-width-base solid transparent;\n  border-left:  @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n  position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: @zindex-dropdown;\n  display: none; // none by default, but block on \"open\" of the menu\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0; // override default ul\n  list-style: none;\n  font-size: @font-size-base;\n  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n  background-color: @dropdown-bg;\n  border: 1px solid @dropdown-fallback-border; // IE8 fallback\n  border: 1px solid @dropdown-border;\n  border-radius: @border-radius-base;\n  .box-shadow(0 6px 12px rgba(0,0,0,.175));\n  background-clip: padding-box;\n\n  // Aligns the dropdown menu to right\n  //\n  // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n  &.pull-right {\n    right: 0;\n    left: auto;\n  }\n\n  // Dividers (basically an hr) within the dropdown\n  .divider {\n    .nav-divider(@dropdown-divider-bg);\n  }\n\n  // Links within the dropdown menu\n  > li > a {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: @line-height-base;\n    color: @dropdown-link-color;\n    white-space: nowrap; // prevent links from randomly breaking onto new lines\n  }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @dropdown-link-hover-color;\n    background-color: @dropdown-link-hover-bg;\n  }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-active-color;\n    text-decoration: none;\n    outline: 0;\n    background-color: @dropdown-link-active-bg;\n  }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-disabled-color;\n  }\n\n  // Nuke hover/focus effects\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: transparent;\n    background-image: none; // Remove CSS gradient\n    .reset-filter();\n    cursor: @cursor-disabled;\n  }\n}\n\n// Open state for the dropdown\n.open {\n  // Show the menu\n  > .dropdown-menu {\n    display: block;\n  }\n\n  // Remove the outline when :focus is triggered\n  > a {\n    outline: 0;\n  }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n  left: auto; // Reset the default from `.dropdown-menu`\n  right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: @font-size-small;\n  line-height: @line-height-base;\n  color: @dropdown-header-color;\n  white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n  // Reverse the caret\n  .caret {\n    border-top: 0;\n    border-bottom: @caret-width-base solid;\n    content: \"\";\n  }\n  // Different positioning for bottom up menu\n  .dropdown-menu {\n    top: auto;\n    bottom: 100%;\n    margin-bottom: 2px;\n  }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-right {\n    .dropdown-menu {\n      .dropdown-menu-right();\n    }\n    // Necessary for overrides of the default right aligned menu.\n    // Will remove come v4 in all likelihood.\n    .dropdown-menu-left {\n      .dropdown-menu-left();\n    }\n  }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle; // match .btn alignment given font-size hack above\n  > .btn {\n    position: relative;\n    float: left;\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      z-index: 2;\n    }\n  }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n  .btn + .btn,\n  .btn + .btn-group,\n  .btn-group + .btn,\n  .btn-group + .btn-group {\n    margin-left: -1px;\n  }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n  margin-left: -5px; // Offset the first child's margin\n  &:extend(.clearfix all);\n\n  .btn-group,\n  .input-group {\n    float: left;\n  }\n  > .btn,\n  > .btn-group,\n  > .input-group {\n    margin-left: 5px;\n  }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  &:not(:last-child):not(.dropdown-toggle) {\n    .border-right-radius(0);\n  }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-right-radius(0);\n  }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n  .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n  // Show no shadow for `.btn-link` since it has no other button styles.\n  &.btn-link {\n    .box-shadow(none);\n  }\n}\n\n\n// Reposition the caret\n.btn .caret {\n  margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n  border-width: @caret-width-large @caret-width-large 0;\n  border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n  border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n  > .btn,\n  > .btn-group,\n  > .btn-group > .btn {\n    display: block;\n    float: none;\n    width: 100%;\n    max-width: 100%;\n  }\n\n  // Clear floats so dropdown menus can be properly placed\n  > .btn-group {\n    &:extend(.clearfix all);\n    > .btn {\n      float: none;\n    }\n  }\n\n  > .btn + .btn,\n  > .btn + .btn-group,\n  > .btn-group + .btn,\n  > .btn-group + .btn-group {\n    margin-top: -1px;\n    margin-left: 0;\n  }\n}\n\n.btn-group-vertical > .btn {\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n  &:first-child:not(:last-child) {\n    border-top-right-radius: @border-radius-base;\n    .border-bottom-radius(0);\n  }\n  &:last-child:not(:first-child) {\n    border-bottom-left-radius: @border-radius-base;\n    .border-top-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-bottom-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n  > .btn,\n  > .btn-group {\n    float: none;\n    display: table-cell;\n    width: 1%;\n  }\n  > .btn-group .btn {\n    width: 100%;\n  }\n\n  > .btn-group .dropdown-menu {\n    left: auto;\n  }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n  > .btn,\n  > .btn-group > .btn {\n    input[type=\"radio\"],\n    input[type=\"checkbox\"] {\n      position: absolute;\n      clip: rect(0,0,0,0);\n      pointer-events: none;\n    }\n  }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n  position: relative; // For dropdowns\n  display: table;\n  border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n  // Undo padding and float of grid classes\n  &[class*=\"col-\"] {\n    float: none;\n    padding-left: 0;\n    padding-right: 0;\n  }\n\n  .form-control {\n    // Ensure that the input is always above the *appended* addon button for\n    // proper border colors.\n    position: relative;\n    z-index: 2;\n\n    // IE9 fubars the placeholder attribute in text inputs and the arrows on\n    // select elements in input groups. To fix it, we float the input. Details:\n    // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n    float: left;\n\n    width: 100%;\n    margin-bottom: 0;\n  }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 1;\n  color: @input-color;\n  text-align: center;\n  background-color: @input-group-addon-bg;\n  border: 1px solid @input-group-addon-border-color;\n  border-radius: @border-radius-base;\n\n  // Sizing\n  &.input-sm {\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    border-radius: @border-radius-small;\n  }\n  &.input-lg {\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    border-radius: @border-radius-large;\n  }\n\n  // Nuke default margins from checkboxes and radios to vertically center within.\n  input[type=\"radio\"],\n  input[type=\"checkbox\"] {\n    margin-top: 0;\n  }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  .border-right-radius(0);\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  .border-left-radius(0);\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n  position: relative;\n  // Jankily prevent input button groups from wrapping with `white-space` and\n  // `font-size` in combination with `inline-block` on buttons.\n  font-size: 0;\n  white-space: nowrap;\n\n  // Negative margin for spacing, position for bringing hovered/focused/actived\n  // element above the siblings.\n  > .btn {\n    position: relative;\n    + .btn {\n      margin-left: -1px;\n    }\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active {\n      z-index: 2;\n    }\n  }\n\n  // Negative margin to only have a 1px border between the two\n  &:first-child {\n    > .btn,\n    > .btn-group {\n      margin-right: -1px;\n    }\n  }\n  &:last-child {\n    > .btn,\n    > .btn-group {\n      margin-left: -1px;\n    }\n  }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n  margin-bottom: 0;\n  padding-left: 0; // Override default ul/ol\n  list-style: none;\n  &:extend(.clearfix all);\n\n  > li {\n    position: relative;\n    display: block;\n\n    > a {\n      position: relative;\n      display: block;\n      padding: @nav-link-padding;\n      &:hover,\n      &:focus {\n        text-decoration: none;\n        background-color: @nav-link-hover-bg;\n      }\n    }\n\n    // Disabled state sets text to gray and nukes hover/tab effects\n    &.disabled > a {\n      color: @nav-disabled-link-color;\n\n      &:hover,\n      &:focus {\n        color: @nav-disabled-link-hover-color;\n        text-decoration: none;\n        background-color: transparent;\n        cursor: @cursor-disabled;\n      }\n    }\n  }\n\n  // Open dropdowns\n  .open > a {\n    &,\n    &:hover,\n    &:focus {\n      background-color: @nav-link-hover-bg;\n      border-color: @link-color;\n    }\n  }\n\n  // Nav dividers (deprecated with v3.0.1)\n  //\n  // This should have been removed in v3 with the dropping of `.nav-list`, but\n  // we missed it. We don't currently support this anywhere, but in the interest\n  // of maintaining backward compatibility in case you use it, it's deprecated.\n  .nav-divider {\n    .nav-divider();\n  }\n\n  // Prevent IE8 from misplacing imgs\n  //\n  // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n  > li > a > img {\n    max-width: none;\n  }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n  border-bottom: 1px solid @nav-tabs-border-color;\n  > li {\n    float: left;\n    // Make the list-items overlay the bottom border\n    margin-bottom: -1px;\n\n    // Actual tabs (as links)\n    > a {\n      margin-right: 2px;\n      line-height: @line-height-base;\n      border: 1px solid transparent;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n      &:hover {\n        border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n      }\n    }\n\n    // Active state, and its :hover to override normal :hover\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-tabs-active-link-hover-color;\n        background-color: @nav-tabs-active-link-hover-bg;\n        border: 1px solid @nav-tabs-active-link-hover-border-color;\n        border-bottom-color: transparent;\n        cursor: default;\n      }\n    }\n  }\n  // pulling this in mainly for less shorthand\n  &.nav-justified {\n    .nav-justified();\n    .nav-tabs-justified();\n  }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n  > li {\n    float: left;\n\n    // Links rendered as pills\n    > a {\n      border-radius: @nav-pills-border-radius;\n    }\n    + li {\n      margin-left: 2px;\n    }\n\n    // Active state\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-pills-active-link-hover-color;\n        background-color: @nav-pills-active-link-hover-bg;\n      }\n    }\n  }\n}\n\n\n// Stacked pills\n.nav-stacked {\n  > li {\n    float: none;\n    + li {\n      margin-top: 2px;\n      margin-left: 0; // no need for this gap between nav items\n    }\n  }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n  width: 100%;\n\n  > li {\n    float: none;\n    > a {\n      text-align: center;\n      margin-bottom: 5px;\n    }\n  }\n\n  > .dropdown .dropdown-menu {\n    top: auto;\n    left: auto;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li {\n      display: table-cell;\n      width: 1%;\n      > a {\n        margin-bottom: 0;\n      }\n    }\n  }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n  border-bottom: 0;\n\n  > li > a {\n    // Override margin from .nav-tabs\n    margin-right: 0;\n    border-radius: @border-radius-base;\n  }\n\n  > .active > a,\n  > .active > a:hover,\n  > .active > a:focus {\n    border: 1px solid @nav-tabs-justified-link-border-color;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li > a {\n      border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n    }\n    > .active > a,\n    > .active > a:hover,\n    > .active > a:focus {\n      border-bottom-color: @nav-tabs-justified-active-link-border-color;\n    }\n  }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n  > .tab-pane {\n    display: none;\n  }\n  > .active {\n    display: block;\n  }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n  // make dropdown border overlap tab border\n  margin-top: -1px;\n  // Remove the top rounded corners here since there is a hard edge above the menu\n  .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n  position: relative;\n  min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n  margin-bottom: @navbar-margin-bottom;\n  border: 1px solid transparent;\n\n  // Prevent floats from breaking the navbar\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: @navbar-border-radius;\n  }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n  }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: @navbar-padding-horizontal;\n  padding-left:  @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n  &:extend(.clearfix all);\n  -webkit-overflow-scrolling: touch;\n\n  &.in {\n    overflow-y: auto;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n\n    &.collapse {\n      display: block !important;\n      height: auto !important;\n      padding-bottom: 0; // Override default setting\n      overflow: visible !important;\n    }\n\n    &.in {\n      overflow-y: visible;\n    }\n\n    // Undo the collapse side padding for navbars with containers to ensure\n    // alignment of right-aligned contents.\n    .navbar-fixed-top &,\n    .navbar-static-top &,\n    .navbar-fixed-bottom & {\n      padding-left: 0;\n      padding-right: 0;\n    }\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  .navbar-collapse {\n    max-height: @navbar-collapse-max-height;\n\n    @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n      max-height: 200px;\n    }\n  }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n  > .navbar-header,\n  > .navbar-collapse {\n    margin-right: -@navbar-padding-horizontal;\n    margin-left:  -@navbar-padding-horizontal;\n\n    @media (min-width: @grid-float-breakpoint) {\n      margin-right: 0;\n      margin-left:  0;\n    }\n  }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n  z-index: @zindex-navbar;\n  border-width: 0 0 1px;\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: @zindex-navbar-fixed;\n\n  // Undo the rounded corners\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0; // override .navbar defaults\n  border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n  float: left;\n  padding: @navbar-padding-vertical @navbar-padding-horizontal;\n  font-size: @font-size-large;\n  line-height: @line-height-computed;\n  height: @navbar-height;\n\n  &:hover,\n  &:focus {\n    text-decoration: none;\n  }\n\n  > img {\n    display: block;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    .navbar > .container &,\n    .navbar > .container-fluid & {\n      margin-left: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: @navbar-padding-horizontal;\n  padding: 9px 10px;\n  .navbar-vertical-align(34px);\n  background-color: transparent;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  border-radius: @border-radius-base;\n\n  // We remove the `outline` here, but later compensate by attaching `:hover`\n  // styles to `:focus`.\n  &:focus {\n    outline: 0;\n  }\n\n  // Bars\n  .icon-bar {\n    display: block;\n    width: 22px;\n    height: 2px;\n    border-radius: 1px;\n  }\n  .icon-bar + .icon-bar {\n    margin-top: 4px;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    display: none;\n  }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n  margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n  > li > a {\n    padding-top:    10px;\n    padding-bottom: 10px;\n    line-height: @line-height-computed;\n  }\n\n  @media (max-width: @grid-float-breakpoint-max) {\n    // Dropdowns get custom display when collapsed\n    .open .dropdown-menu {\n      position: static;\n      float: none;\n      width: auto;\n      margin-top: 0;\n      background-color: transparent;\n      border: 0;\n      box-shadow: none;\n      > li > a,\n      .dropdown-header {\n        padding: 5px 15px 5px 25px;\n      }\n      > li > a {\n        line-height: @line-height-computed;\n        &:hover,\n        &:focus {\n          background-image: none;\n        }\n      }\n    }\n  }\n\n  // Uncollapse the nav\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin: 0;\n\n    > li {\n      float: left;\n      > a {\n        padding-top:    @navbar-padding-vertical;\n        padding-bottom: @navbar-padding-vertical;\n      }\n    }\n  }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n  margin-left: -@navbar-padding-horizontal;\n  margin-right: -@navbar-padding-horizontal;\n  padding: 10px @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n\n  // Mixin behavior for optimum display\n  .form-inline();\n\n  .form-group {\n    @media (max-width: @grid-float-breakpoint-max) {\n      margin-bottom: 5px;\n\n      &:last-child {\n        margin-bottom: 0;\n      }\n    }\n  }\n\n  // Vertically center in expanded, horizontal navbar\n  .navbar-vertical-align(@input-height-base);\n\n  // Undo 100% width for pull classes\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    .box-shadow(none);\n  }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  .border-top-radius(@navbar-border-radius);\n  .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n  .navbar-vertical-align(@input-height-base);\n\n  &.btn-sm {\n    .navbar-vertical-align(@input-height-small);\n  }\n  &.btn-xs {\n    .navbar-vertical-align(22);\n  }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n  .navbar-vertical-align(@line-height-computed);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin-left: @navbar-padding-horizontal;\n    margin-right: @navbar-padding-horizontal;\n  }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-left  { .pull-left(); }\n  .navbar-right {\n    .pull-right();\n    margin-right: -@navbar-padding-horizontal;\n\n    ~ .navbar-right {\n      margin-right: 0;\n    }\n  }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  background-color: @navbar-default-bg;\n  border-color: @navbar-default-border;\n\n  .navbar-brand {\n    color: @navbar-default-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-brand-hover-color;\n      background-color: @navbar-default-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-default-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-default-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-hover-color;\n        background-color: @navbar-default-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-active-color;\n        background-color: @navbar-default-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n        background-color: @navbar-default-link-disabled-bg;\n      }\n    }\n  }\n\n  .navbar-toggle {\n    border-color: @navbar-default-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-default-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-default-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: @navbar-default-border;\n  }\n\n  // Dropdown menu items\n  .navbar-nav {\n    // Remove background color from open dropdown\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-default-link-active-bg;\n        color: @navbar-default-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display when collapsed\n      .open .dropdown-menu {\n        > li > a {\n          color: @navbar-default-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-hover-color;\n            background-color: @navbar-default-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-active-color;\n            background-color: @navbar-default-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-disabled-color;\n            background-color: @navbar-default-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n\n  // Links in navbars\n  //\n  // Add a class to ensure links outside the navbar nav are colored correctly.\n\n  .navbar-link {\n    color: @navbar-default-link-color;\n    &:hover {\n      color: @navbar-default-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-default-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n      }\n    }\n  }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n  background-color: @navbar-inverse-bg;\n  border-color: @navbar-inverse-border;\n\n  .navbar-brand {\n    color: @navbar-inverse-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-brand-hover-color;\n      background-color: @navbar-inverse-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-inverse-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-inverse-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-hover-color;\n        background-color: @navbar-inverse-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-active-color;\n        background-color: @navbar-inverse-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n        background-color: @navbar-inverse-link-disabled-bg;\n      }\n    }\n  }\n\n  // Darken the responsive nav toggle\n  .navbar-toggle {\n    border-color: @navbar-inverse-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-inverse-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-inverse-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: darken(@navbar-inverse-bg, 7%);\n  }\n\n  // Dropdowns\n  .navbar-nav {\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-inverse-link-active-bg;\n        color: @navbar-inverse-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display\n      .open .dropdown-menu {\n        > .dropdown-header {\n          border-color: @navbar-inverse-border;\n        }\n        .divider {\n          background-color: @navbar-inverse-border;\n        }\n        > li > a {\n          color: @navbar-inverse-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-hover-color;\n            background-color: @navbar-inverse-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-active-color;\n            background-color: @navbar-inverse-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-disabled-color;\n            background-color: @navbar-inverse-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n  .navbar-link {\n    color: @navbar-inverse-link-color;\n    &:hover {\n      color: @navbar-inverse-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-inverse-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n      }\n    }\n  }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n  .clearfix();\n}\n.center-block {\n  .center-block();\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n  display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n  position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n  margin-bottom: @line-height-computed;\n  list-style: none;\n  background-color: @breadcrumb-bg;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline-block;\n\n    + li:before {\n      content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n      padding: 0 5px;\n      color: @breadcrumb-color;\n    }\n  }\n\n  > .active {\n    color: @breadcrumb-active-color;\n  }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline; // Remove list-style and block-level defaults\n    > a,\n    > span {\n      position: relative;\n      float: left; // Collapse white-space\n      padding: @padding-base-vertical @padding-base-horizontal;\n      line-height: @line-height-base;\n      text-decoration: none;\n      color: @pagination-color;\n      background-color: @pagination-bg;\n      border: 1px solid @pagination-border;\n      margin-left: -1px;\n    }\n    &:first-child {\n      > a,\n      > span {\n        margin-left: 0;\n        .border-left-radius(@border-radius-base);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius-base);\n      }\n    }\n  }\n\n  > li > a,\n  > li > span {\n    &:hover,\n    &:focus {\n      color: @pagination-hover-color;\n      background-color: @pagination-hover-bg;\n      border-color: @pagination-hover-border;\n    }\n  }\n\n  > .active > a,\n  > .active > span {\n    &,\n    &:hover,\n    &:focus {\n      z-index: 2;\n      color: @pagination-active-color;\n      background-color: @pagination-active-bg;\n      border-color: @pagination-active-border;\n      cursor: default;\n    }\n  }\n\n  > .disabled {\n    > span,\n    > span:hover,\n    > span:focus,\n    > a,\n    > a:hover,\n    > a:focus {\n      color: @pagination-disabled-color;\n      background-color: @pagination-disabled-bg;\n      border-color: @pagination-disabled-border;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n  .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n  .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  list-style: none;\n  text-align: center;\n  &:extend(.clearfix all);\n  li {\n    display: inline;\n    > a,\n    > span {\n      display: inline-block;\n      padding: 5px 14px;\n      background-color: @pager-bg;\n      border: 1px solid @pager-border;\n      border-radius: @pager-border-radius;\n    }\n\n    > a:hover,\n    > a:focus {\n      text-decoration: none;\n      background-color: @pager-hover-bg;\n    }\n  }\n\n  .next {\n    > a,\n    > span {\n      float: right;\n    }\n  }\n\n  .previous {\n    > a,\n    > span {\n      float: left;\n    }\n  }\n\n  .disabled {\n    > a,\n    > a:hover,\n    > a:focus,\n    > span {\n      color: @pager-disabled-color;\n      background-color: @pager-bg;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: @label-color;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n\n  // Add hover effects, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @label-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Empty labels collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for labels in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n  .label-variant(@label-default-bg);\n}\n\n.label-primary {\n  .label-variant(@label-primary-bg);\n}\n\n.label-success {\n  .label-variant(@label-success-bg);\n}\n\n.label-info {\n  .label-variant(@label-info-bg);\n}\n\n.label-warning {\n  .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n  .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n  background-color: @color;\n\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: @font-size-small;\n  font-weight: @badge-font-weight;\n  color: @badge-color;\n  line-height: @badge-line-height;\n  vertical-align: baseline;\n  white-space: nowrap;\n  text-align: center;\n  background-color: @badge-bg;\n  border-radius: @badge-border-radius;\n\n  // Empty badges collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for badges in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n\n  .btn-xs &,\n  .btn-group-xs > .btn & {\n    top: 0;\n    padding: 1px 5px;\n  }\n\n  // Hover state, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @badge-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Account for badges in navs\n  .list-group-item.active > &,\n  .nav-pills > .active > a > & {\n    color: @badge-active-color;\n    background-color: @badge-active-bg;\n  }\n\n  .list-group-item > & {\n    float: right;\n  }\n\n  .list-group-item > & + & {\n    margin-right: 5px;\n  }\n\n  .nav-pills > li > a > & {\n    margin-left: 3px;\n  }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding: @jumbotron-padding (@jumbotron-padding / 2);\n  margin-bottom: @jumbotron-padding;\n  color: @jumbotron-color;\n  background-color: @jumbotron-bg;\n\n  h1,\n  .h1 {\n    color: @jumbotron-heading-color;\n  }\n\n  p {\n    margin-bottom: (@jumbotron-padding / 2);\n    font-size: @jumbotron-font-size;\n    font-weight: 200;\n  }\n\n  > hr {\n    border-top-color: darken(@jumbotron-bg, 10%);\n  }\n\n  .container &,\n  .container-fluid & {\n    border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n  }\n\n  .container {\n    max-width: 100%;\n  }\n\n  @media screen and (min-width: @screen-sm-min) {\n    padding: (@jumbotron-padding * 1.6) 0;\n\n    .container &,\n    .container-fluid & {\n      padding-left:  (@jumbotron-padding * 2);\n      padding-right: (@jumbotron-padding * 2);\n    }\n\n    h1,\n    .h1 {\n      font-size: (@font-size-base * 4.5);\n    }\n  }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n  display: block;\n  padding: @thumbnail-padding;\n  margin-bottom: @line-height-computed;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(border .2s ease-in-out);\n\n  > img,\n  a > img {\n    &:extend(.img-responsive);\n    margin-left: auto;\n    margin-right: auto;\n  }\n\n  // Add a hover state for linked versions only\n  a&:hover,\n  a&:focus,\n  a&.active {\n    border-color: @link-color;\n  }\n\n  // Image captions\n  .caption {\n    padding: @thumbnail-caption-padding;\n    color: @thumbnail-caption-color;\n  }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n  padding: @alert-padding;\n  margin-bottom: @line-height-computed;\n  border: 1px solid transparent;\n  border-radius: @alert-border-radius;\n\n  // Headings for larger alerts\n  h4 {\n    margin-top: 0;\n    // Specified for the h4 to prevent conflicts of changing @headings-color\n    color: inherit;\n  }\n\n  // Provide class for links that match alerts\n  .alert-link {\n    font-weight: @alert-link-font-weight;\n  }\n\n  // Improve alignment and spacing of inner content\n  > p,\n  > ul {\n    margin-bottom: 0;\n  }\n\n  > p + p {\n    margin-top: 5px;\n  }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n  padding-right: (@alert-padding + 20);\n\n  // Adjust close link position\n  .close {\n    position: relative;\n    top: -2px;\n    right: -21px;\n    color: inherit;\n  }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n  .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n  .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n  .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n  .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n  overflow: hidden;\n  height: @line-height-computed;\n  margin-bottom: @line-height-computed;\n  background-color: @progress-bg;\n  border-radius: @progress-border-radius;\n  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: @font-size-small;\n  line-height: @line-height-computed;\n  color: @progress-bar-color;\n  text-align: center;\n  background-color: @progress-bar-bg;\n  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n  .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  #gradient > .striped();\n  background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n  .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n  .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n  .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n  .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n  .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n  background-color: @color;\n\n  // Deprecated parent class requirement as of v3.2.0\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n",".media {\n  // Proper spacing between instances of .media\n  margin-top: 15px;\n\n  &:first-child {\n    margin-top: 0;\n  }\n}\n\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n\n.media-body {\n  width: 10000px;\n}\n\n.media-object {\n  display: block;\n}\n\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n\n.media-middle {\n  vertical-align: middle;\n}\n\n.media-bottom {\n  vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol>, or <div>.\n\n.list-group {\n  // No need to set list-style: none; since .list-group-item is block level\n  margin-bottom: 20px;\n  padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  // Place the border on the list items and negative margin up for better styling\n  margin-bottom: -1px;\n  background-color: @list-group-bg;\n  border: 1px solid @list-group-border;\n\n  // Round the first and last items\n  &:first-child {\n    .border-top-radius(@list-group-border-radius);\n  }\n  &:last-child {\n    margin-bottom: 0;\n    .border-bottom-radius(@list-group-border-radius);\n  }\n}\n\n\n// Linked list items\n//\n// Use anchor elements instead of `li`s or `div`s to create linked list items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item {\n  color: @list-group-link-color;\n\n  .list-group-item-heading {\n    color: @list-group-link-heading-color;\n  }\n\n  // Hover state\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @list-group-link-hover-color;\n    background-color: @list-group-hover-bg;\n  }\n}\n\n.list-group-item {\n  // Disabled state\n  &.disabled,\n  &.disabled:hover,\n  &.disabled:focus {\n    background-color: @list-group-disabled-bg;\n    color: @list-group-disabled-color;\n    cursor: @cursor-disabled;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-disabled-text-color;\n    }\n  }\n\n  // Active class on item itself, not parent\n  &.active,\n  &.active:hover,\n  &.active:focus {\n    z-index: 2; // Place active items above their siblings for proper border styling\n    color: @list-group-active-color;\n    background-color: @list-group-active-bg;\n    border-color: @list-group-active-border;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading,\n    .list-group-item-heading > small,\n    .list-group-item-heading > .small {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-active-text-color;\n    }\n  }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n","// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a& {\n      color: @color;\n\n      .list-group-item-heading {\n        color: inherit;\n      }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n  margin-bottom: @line-height-computed;\n  background-color: @panel-bg;\n  border: 1px solid transparent;\n  border-radius: @panel-border-radius;\n  .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n  padding: @panel-body-padding;\n  &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n  padding: @panel-heading-padding;\n  border-bottom: 1px solid transparent;\n  .border-top-radius((@panel-border-radius - 1));\n\n  > .dropdown .dropdown-toggle {\n    color: inherit;\n  }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: ceil((@font-size-base * 1.125));\n  color: inherit;\n\n  > a,\n  > small,\n  > .small,\n  > small > a,\n  > .small > a {\n    color: inherit;\n  }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n  padding: @panel-footer-padding;\n  background-color: @panel-footer-bg;\n  border-top: 1px solid @panel-inner-border;\n  .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n  > .list-group,\n  > .panel-collapse > .list-group {\n    margin-bottom: 0;\n\n    .list-group-item {\n      border-width: 1px 0;\n      border-radius: 0;\n    }\n\n    // Add border top radius for first one\n    &:first-child {\n      .list-group-item:first-child {\n        border-top: 0;\n        .border-top-radius((@panel-border-radius - 1));\n      }\n    }\n    // Add border bottom radius for last one\n    &:last-child {\n      .list-group-item:last-child {\n        border-bottom: 0;\n        .border-bottom-radius((@panel-border-radius - 1));\n      }\n    }\n  }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n  .list-group-item:first-child {\n    border-top-width: 0;\n  }\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n  > .table,\n  > .table-responsive > .table,\n  > .panel-collapse > .table {\n    margin-bottom: 0;\n\n    caption {\n      padding-left: @panel-body-padding;\n      padding-right: @panel-body-padding;\n    }\n  }\n  // Add border top radius for first one\n  > .table:first-child,\n  > .table-responsive:first-child > .table:first-child {\n    .border-top-radius((@panel-border-radius - 1));\n\n    > thead:first-child,\n    > tbody:first-child {\n      > tr:first-child {\n        border-top-left-radius: (@panel-border-radius - 1);\n        border-top-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-top-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-top-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  // Add border bottom radius for last one\n  > .table:last-child,\n  > .table-responsive:last-child > .table:last-child {\n    .border-bottom-radius((@panel-border-radius - 1));\n\n    > tbody:last-child,\n    > tfoot:last-child {\n      > tr:last-child {\n        border-bottom-left-radius: (@panel-border-radius - 1);\n        border-bottom-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-bottom-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-bottom-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  > .panel-body + .table,\n  > .panel-body + .table-responsive,\n  > .table + .panel-body,\n  > .table-responsive + .panel-body {\n    border-top: 1px solid @table-border-color;\n  }\n  > .table > tbody:first-child > tr:first-child th,\n  > .table > tbody:first-child > tr:first-child td {\n    border-top: 0;\n  }\n  > .table-bordered,\n  > .table-responsive > .table-bordered {\n    border: 0;\n    > thead,\n    > tbody,\n    > tfoot {\n      > tr {\n        > th:first-child,\n        > td:first-child {\n          border-left: 0;\n        }\n        > th:last-child,\n        > td:last-child {\n          border-right: 0;\n        }\n      }\n    }\n    > thead,\n    > tbody {\n      > tr:first-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n    > tbody,\n    > tfoot {\n      > tr:last-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n  > .table-responsive {\n    border: 0;\n    margin-bottom: 0;\n  }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n  margin-bottom: @line-height-computed;\n\n  // Tighten up margin so it's only between panels\n  .panel {\n    margin-bottom: 0;\n    border-radius: @panel-border-radius;\n\n    + .panel {\n      margin-top: 5px;\n    }\n  }\n\n  .panel-heading {\n    border-bottom: 0;\n\n    + .panel-collapse > .panel-body,\n    + .panel-collapse > .list-group {\n      border-top: 1px solid @panel-inner-border;\n    }\n  }\n\n  .panel-footer {\n    border-top: 0;\n    + .panel-collapse .panel-body {\n      border-bottom: 1px solid @panel-inner-border;\n    }\n  }\n}\n\n\n// Contextual variations\n.panel-default {\n  .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n  .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n  .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n  .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n  .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n  .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse > .panel-body {\n      border-top-color: @border;\n    }\n    .badge {\n      color: @heading-bg-color;\n      background-color: @heading-text-color;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse > .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n","// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n\n  .embed-responsive-item,\n  iframe,\n  embed,\n  object,\n  video {\n    position: absolute;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    height: 100%;\n    width: 100%;\n    border: 0;\n  }\n}\n\n// Modifier class for 16:9 aspect ratio\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n\n// Modifier class for 4:3 aspect ratio\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: @well-bg;\n  border: 1px solid @well-border;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n  blockquote {\n    border-color: #ddd;\n    border-color: rgba(0,0,0,.15);\n  }\n}\n\n// Sizes\n.well-lg {\n  padding: 24px;\n  border-radius: @border-radius-large;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-size-base * 1.5);\n  font-weight: @close-font-weight;\n  line-height: 1;\n  color: @close-color;\n  text-shadow: @close-text-shadow;\n  .opacity(.2);\n\n  &:hover,\n  &:focus {\n    color: @close-color;\n    text-decoration: none;\n    cursor: pointer;\n    .opacity(.5);\n  }\n\n  // Additional properties for button version\n  // iOS requires the button element instead of an anchor tag.\n  // If you want the anchor version, it requires `href=\"#\"`.\n  // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n  button& {\n    padding: 0;\n    cursor: pointer;\n    background: transparent;\n    border: 0;\n    -webkit-appearance: none;\n  }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scroll\n// .modal           - container to scroll within\n// .modal-dialog    - positioning shell for the actual modal\n// .modal-content   - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n  overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal;\n  -webkit-overflow-scrolling: touch;\n\n  // Prevent Chrome on Windows from adding a focus outline. For details, see\n  // https://github.com/twbs/bootstrap/pull/10951.\n  outline: 0;\n\n  // When fading in the modal, animate it to slide down\n  &.fade .modal-dialog {\n    .translate(0, -25%);\n    .transition-transform(~\"0.3s ease-out\");\n  }\n  &.in .modal-dialog { .translate(0, 0) }\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n  position: relative;\n  background-color: @modal-content-bg;\n  border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n  border: 1px solid @modal-content-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 3px 9px rgba(0,0,0,.5));\n  background-clip: padding-box;\n  // Remove focus outline from opened modal\n  outline: 0;\n}\n\n// Modal background\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal-background;\n  background-color: @modal-backdrop-bg;\n  // Fade for backdrop\n  &.fade { .opacity(0); }\n  &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n  padding: @modal-title-padding;\n  border-bottom: 1px solid @modal-header-border-color;\n  min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n  margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n  margin: 0;\n  line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n  position: relative;\n  padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n  padding: @modal-inner-padding;\n  text-align: right; // right align buttons\n  border-top: 1px solid @modal-footer-border-color;\n  &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n  // Properly space out buttons\n  .btn + .btn {\n    margin-left: 5px;\n    margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n  }\n  // but override that for button groups\n  .btn-group .btn + .btn {\n    margin-left: -1px;\n  }\n  // and override it for block buttons as well\n  .btn-block + .btn-block {\n    margin-left: 0;\n  }\n}\n\n// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n  // Automatically set modal's width for larger viewports\n  .modal-dialog {\n    width: @modal-md;\n    margin: 30px auto;\n  }\n  .modal-content {\n    .box-shadow(0 5px 15px rgba(0,0,0,.5));\n  }\n\n  // Modal sizes\n  .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n  .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  z-index: @zindex-tooltip;\n  display: block;\n  // Reset font and text properties given new insertion method\n  font-family: @font-family-base;\n  font-size: @font-size-small;\n  font-weight: normal;\n  line-height: 1.4;\n  .opacity(0);\n\n  &.in     { .opacity(@tooltip-opacity); }\n  &.top    { margin-top:  -3px; padding: @tooltip-arrow-width 0; }\n  &.right  { margin-left:  3px; padding: 0 @tooltip-arrow-width; }\n  &.bottom { margin-top:   3px; padding: @tooltip-arrow-width 0; }\n  &.left   { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n  max-width: @tooltip-max-width;\n  padding: 3px 8px;\n  color: @tooltip-color;\n  text-align: center;\n  text-decoration: none;\n  background-color: @tooltip-bg;\n  border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\n.tooltip {\n  &.top .tooltip-arrow {\n    bottom: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-left .tooltip-arrow {\n    bottom: 0;\n    right: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-right .tooltip-arrow {\n    bottom: 0;\n    left: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.right .tooltip-arrow {\n    top: 50%;\n    left: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-right-color: @tooltip-arrow-color;\n  }\n  &.left .tooltip-arrow {\n    top: 50%;\n    right: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-left-color: @tooltip-arrow-color;\n  }\n  &.bottom .tooltip-arrow {\n    top: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-left .tooltip-arrow {\n    top: 0;\n    right: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-right .tooltip-arrow {\n    top: 0;\n    left: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: @zindex-popover;\n  display: none;\n  max-width: @popover-max-width;\n  padding: 1px;\n  // Reset font and text properties given new insertion method\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: @line-height-base;\n  text-align: left;\n  background-color: @popover-bg;\n  background-clip: padding-box;\n  border: 1px solid @popover-fallback-border-color;\n  border: 1px solid @popover-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n  // Overrides for proper insertion\n  white-space: normal;\n\n  // Offset the popover to account for the popover arrow\n  &.top     { margin-top: -@popover-arrow-width; }\n  &.right   { margin-left: @popover-arrow-width; }\n  &.bottom  { margin-top: @popover-arrow-width; }\n  &.left    { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n  margin: 0; // reset heading margin\n  padding: 8px 14px;\n  font-size: @font-size-base;\n  background-color: @popover-title-bg;\n  border-bottom: 1px solid darken(@popover-title-bg, 5%);\n  border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n  &,\n  &:after {\n    position: absolute;\n    display: block;\n    width: 0;\n    height: 0;\n    border-color: transparent;\n    border-style: solid;\n  }\n}\n.popover > .arrow {\n  border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n  border-width: @popover-arrow-width;\n  content: \"\";\n}\n\n.popover {\n  &.top > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-bottom-width: 0;\n    border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-top-color: @popover-arrow-outer-color;\n    bottom: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      bottom: 1px;\n      margin-left: -@popover-arrow-width;\n      border-bottom-width: 0;\n      border-top-color: @popover-arrow-color;\n    }\n  }\n  &.right > .arrow {\n    top: 50%;\n    left: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-left-width: 0;\n    border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-right-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      left: 1px;\n      bottom: -@popover-arrow-width;\n      border-left-width: 0;\n      border-right-color: @popover-arrow-color;\n    }\n  }\n  &.bottom > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-top-width: 0;\n    border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-bottom-color: @popover-arrow-outer-color;\n    top: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      top: 1px;\n      margin-left: -@popover-arrow-width;\n      border-top-width: 0;\n      border-bottom-color: @popover-arrow-color;\n    }\n  }\n\n  &.left > .arrow {\n    top: 50%;\n    right: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-right-width: 0;\n    border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-left-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      right: 1px;\n      border-right-width: 0;\n      border-left-color: @popover-arrow-color;\n      bottom: -@popover-arrow-width;\n    }\n  }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n\n  > .item {\n    display: none;\n    position: relative;\n    .transition(.6s ease-in-out left);\n\n    // Account for jankitude on images\n    > img,\n    > a > img {\n      &:extend(.img-responsive);\n      line-height: 1;\n    }\n\n    // WebKit CSS3 transforms for supported devices\n    @media all and (transform-3d), (-webkit-transform-3d) {\n      .transition-transform(~'0.6s ease-in-out');\n      .backface-visibility(~'hidden');\n      .perspective(1000);\n\n      &.next,\n      &.active.right {\n        .translate3d(100%, 0, 0);\n        left: 0;\n      }\n      &.prev,\n      &.active.left {\n        .translate3d(-100%, 0, 0);\n        left: 0;\n      }\n      &.next.left,\n      &.prev.right,\n      &.active {\n        .translate3d(0, 0, 0);\n        left: 0;\n      }\n    }\n  }\n\n  > .active,\n  > .next,\n  > .prev {\n    display: block;\n  }\n\n  > .active {\n    left: 0;\n  }\n\n  > .next,\n  > .prev {\n    position: absolute;\n    top: 0;\n    width: 100%;\n  }\n\n  > .next {\n    left: 100%;\n  }\n  > .prev {\n    left: -100%;\n  }\n  > .next.left,\n  > .prev.right {\n    left: 0;\n  }\n\n  > .active.left {\n    left: -100%;\n  }\n  > .active.right {\n    left: 100%;\n  }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: @carousel-control-width;\n  .opacity(@carousel-control-opacity);\n  font-size: @carousel-control-font-size;\n  color: @carousel-control-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  // We can't have this transition here because WebKit cancels the carousel\n  // animation if you trip this while in the middle of another animation.\n\n  // Set gradients for backgrounds\n  &.left {\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n  }\n  &.right {\n    left: auto;\n    right: 0;\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n  }\n\n  // Hover/focus state\n  &:hover,\n  &:focus {\n    outline: 0;\n    color: @carousel-control-color;\n    text-decoration: none;\n    .opacity(.9);\n  }\n\n  // Toggles\n  .icon-prev,\n  .icon-next,\n  .glyphicon-chevron-left,\n  .glyphicon-chevron-right {\n    position: absolute;\n    top: 50%;\n    z-index: 5;\n    display: inline-block;\n  }\n  .icon-prev,\n  .glyphicon-chevron-left {\n    left: 50%;\n    margin-left: -10px;\n  }\n  .icon-next,\n  .glyphicon-chevron-right {\n    right: 50%;\n    margin-right: -10px;\n  }\n  .icon-prev,\n  .icon-next {\n    width:  20px;\n    height: 20px;\n    margin-top: -10px;\n    line-height: 1;\n    font-family: serif;\n  }\n\n\n  .icon-prev {\n    &:before {\n      content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n    }\n  }\n  .icon-next {\n    &:before {\n      content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n    }\n  }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n\n  li {\n    display: inline-block;\n    width:  10px;\n    height: 10px;\n    margin: 1px;\n    text-indent: -999px;\n    border: 1px solid @carousel-indicator-border-color;\n    border-radius: 10px;\n    cursor: pointer;\n\n    // IE8-9 hack for event handling\n    //\n    // Internet Explorer 8-9 does not support clicks on elements without a set\n    // `background-color`. We cannot use `filter` since that's not viewed as a\n    // background color by the browser. Thus, a hack is needed.\n    // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer\n    //\n    // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n    // set alpha transparency for the best results possible.\n    background-color: #000 \\9; // IE8\n    background-color: rgba(0,0,0,0); // IE9\n  }\n  .active {\n    margin: 0;\n    width:  12px;\n    height: 12px;\n    background-color: @carousel-indicator-active-bg;\n  }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: @carousel-caption-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  & .btn {\n    text-shadow: none; // No shadow for button elements in carousel-caption\n  }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n  // Scale up the controls a smidge\n  .carousel-control {\n    .glyphicon-chevron-left,\n    .glyphicon-chevron-right,\n    .icon-prev,\n    .icon-next {\n      width: 30px;\n      height: 30px;\n      margin-top: -15px;\n      font-size: 30px;\n    }\n    .glyphicon-chevron-left,\n    .icon-prev {\n      margin-left: -15px;\n    }\n    .glyphicon-chevron-right,\n    .icon-next {\n      margin-right: -15px;\n    }\n  }\n\n  // Show and left align the captions\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n\n  // Move up the indicators\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n","// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n","// Center-align a block level element\n\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n","// CSS image replacement\n//\n// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n  width: device-width;\n}\n\n\n// Visibility utilities\n// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n\n.visible-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-visibility();\n  }\n}\n.visible-xs-block {\n  @media (max-width: @screen-xs-max) {\n    display: block !important;\n  }\n}\n.visible-xs-inline {\n  @media (max-width: @screen-xs-max) {\n    display: inline !important;\n  }\n}\n.visible-xs-inline-block {\n  @media (max-width: @screen-xs-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-visibility();\n  }\n}\n.visible-sm-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: block !important;\n  }\n}\n.visible-sm-inline {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline !important;\n  }\n}\n.visible-sm-inline-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-visibility();\n  }\n}\n.visible-md-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: block !important;\n  }\n}\n.visible-md-inline {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline !important;\n  }\n}\n.visible-md-inline-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-visibility();\n  }\n}\n.visible-lg-block {\n  @media (min-width: @screen-lg-min) {\n    display: block !important;\n  }\n}\n.visible-lg-inline {\n  @media (min-width: @screen-lg-min) {\n    display: inline !important;\n  }\n}\n.visible-lg-inline-block {\n  @media (min-width: @screen-lg-min) {\n    display: inline-block !important;\n  }\n}\n\n.hidden-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-invisibility();\n  }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n  .responsive-invisibility();\n\n  @media print {\n    .responsive-visibility();\n  }\n}\n.visible-print-block {\n  display: none !important;\n\n  @media print {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n\n  @media print {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n\n  @media print {\n    display: inline-block !important;\n  }\n}\n\n.hidden-print {\n  @media print {\n    .responsive-invisibility();\n  }\n}\n","// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n"]}
\ No newline at end of file
diff --git a/http2/doc/api/static-assets/favicon.png b/http2/doc/api/static-assets/favicon.png
new file mode 100644
index 0000000..7d3901d
--- /dev/null
+++ b/http2/doc/api/static-assets/favicon.png
Binary files differ
diff --git a/http2/doc/api/static-assets/play_button.svg b/http2/doc/api/static-assets/play_button.svg
new file mode 100644
index 0000000..c39a2f4
--- /dev/null
+++ b/http2/doc/api/static-assets/play_button.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="68" height="68" viewBox="0 0 17.992 17.992"><path d="M17.992 8.996A8.996 8.996 0 1 0 0 8.996a8.996 8.996 0 0 0 17.992 0m-2.23 0l-9.895 5.713V3.282l9.896 5.714h2.229z" fill-opacity=".198"/><path d="M15.763 8.996l-9.896 5.713V3.283z" fill="#7d7d7d" fill-opacity=".821"/></svg>
\ No newline at end of file
diff --git a/http2/doc/api/static-assets/readme.md b/http2/doc/api/static-assets/readme.md
new file mode 100644
index 0000000..287f566
--- /dev/null
+++ b/http2/doc/api/static-assets/readme.md
@@ -0,0 +1,19 @@
+# highlight.js
+
+Generated from https://highlightjs.org/download/ on 2017-08-30
+
+Included languages:
+
+* bash
+* css
+* dart
+* java
+* javascript
+* json
+* markdown
+* objectivec
+* ruby - dragged in by `yaml` - 🙄
+* shell
+* swift
+* xml - includes html
+* yaml
diff --git a/http2/example/display_headers.dart b/http2/example/display_headers.dart
new file mode 100644
index 0000000..c1faa4d
--- /dev/null
+++ b/http2/example/display_headers.dart
@@ -0,0 +1,63 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:http2/transport.dart';
+
+main(List<String> args) async {
+  if (args == null || args.length != 1) {
+    print('Usage: dart display_headers.dart <HTTPS_URI>');
+    exit(1);
+  }
+
+  var uriArg = args[0];
+
+  if (!uriArg.startsWith('https://')) {
+    print('URI must start with https://');
+    exit(1);
+  }
+
+  var uri = Uri.parse(uriArg);
+
+  var socket = await connect(uri);
+
+  // The default client settings will disable server pushes. We
+  // therefore do not need to deal with [stream.peerPushes].
+  var transport = new ClientTransportConnection.viaSocket(socket);
+
+  var headers = [
+    new Header.ascii(':method', 'GET'),
+    new Header.ascii(':path', uri.path),
+    new Header.ascii(':scheme', uri.scheme),
+    new Header.ascii(':authority', uri.host),
+  ];
+
+  var stream = transport.makeRequest(headers, endStream: true);
+  await for (var message in stream.incomingMessages) {
+    if (message is HeadersStreamMessage) {
+      for (var header in message.headers) {
+        var name = utf8.decode(header.name);
+        var value = utf8.decode(header.value);
+        print('$name: $value');
+      }
+    } else if (message is DataStreamMessage) {
+      // Use [message.bytes] (but respect 'content-encoding' header)
+    }
+  }
+  await transport.finish();
+}
+
+Future<Socket> connect(Uri uri) async {
+  bool useSSL = uri.scheme == 'https';
+  if (useSSL) {
+    var secureSocket = await SecureSocket.connect(uri.host, uri.port,
+        supportedProtocols: ['h2']);
+    if (secureSocket.selectedProtocol != 'h2') {
+      throw new Exception("Failed to negogiate http/2 via alpn. Maybe server "
+          "doesn't support http/2.");
+    }
+    return secureSocket;
+  } else {
+    return await Socket.connect(uri.host, uri.port);
+  }
+}
diff --git a/http2/experimental/server.dart b/http2/experimental/server.dart
new file mode 100644
index 0000000..6637cc2
--- /dev/null
+++ b/http2/experimental/server.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2015, 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 http2.experimental.server;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:http2/src/testing/debug.dart' hide print;
+import 'package:http2/transport.dart';
+
+const bool DEBUGGING = false;
+
+const String HOSTNAME = 'localhost';
+const int PORT = 7777;
+
+main() async {
+  String localFile(String path) => Platform.script.resolve(path).toFilePath();
+
+  var context = new SecurityContext()
+    ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart')
+    ..useCertificateChain(localFile('server_chain.pem'))
+    ..setAlpnProtocols(['h2'], true);
+
+  var server = await SecureServerSocket.bind(HOSTNAME, PORT, context);
+  print('HTTP/2 server listening on https://$HOSTNAME:$PORT');
+
+  runZoned(() {
+    server.listen(handleClient);
+  }, onError: (e, s) {
+    print("Unexpected error: $e");
+    print("Unexpected error - stack: $s");
+  });
+}
+
+handleClient(SecureSocket socket) {
+  dumpInfo('main', 'Got new https client');
+
+  var connection;
+  if (DEBUGGING) {
+    connection = debugPrintingConnection(socket);
+  } else {
+    connection = new ServerTransportConnection.viaSocket(socket);
+  }
+
+  connection.incomingStreams.listen((ServerTransportStream stream) async {
+    dumpInfo('main', 'Got new HTTP/2 stream with id: ${stream.id}');
+
+    String path;
+    stream.incomingMessages.listen((StreamMessage msg) async {
+      dumpInfo('${stream.id}', 'Got new incoming message');
+      if (msg is HeadersStreamMessage) {
+        dumpHeaders('${stream.id}', msg.headers);
+        if (path == null) {
+          path = pathFromHeaders(msg.headers);
+          if (path == null) throw new Exception('no path given');
+
+          if (path == '/') {
+            sendHtml(stream);
+          } else if (['/iframe', '/iframe2'].contains(path)) {
+            sendIFrameHtml(stream, path);
+          } else {
+            send404(stream, path);
+          }
+        }
+      } else if (msg is DataStreamMessage) {
+        dumpData('${stream.id}', msg.bytes);
+      }
+    });
+  });
+}
+
+void dumpHeaders(String prefix, List<Header> headers) {
+  for (int i = 0; i < headers.length; i++) {
+    String key = ascii.decode(headers[i].name);
+    String value = ascii.decode(headers[i].value);
+    print('[$prefix] $key: $value');
+  }
+}
+
+String pathFromHeaders(List<Header> headers) {
+  for (int i = 0; i < headers.length; i++) {
+    if (ascii.decode(headers[i].name) == ':path') {
+      return ascii.decode(headers[i].value);
+    }
+  }
+  throw new Exception('Expected a :path header, but did not find one.');
+}
+
+void dumpData(String prefix, List<int> data) {
+  print('[$prefix] Got ${data.length} bytes.');
+}
+
+void dumpInfo(String prefix, String msg) {
+  print('[$prefix] $msg');
+}
+
+Future sendHtml(ServerTransportStream stream) async {
+  push(stream, '/iframe', sendIFrameHtml);
+  push(stream, '/iframe2', sendIFrameHtml);
+  push(stream, '/favicon.ico', send404);
+
+  stream.sendHeaders([
+    new Header.ascii(':status', '200'),
+    new Header.ascii('content-type', 'text/html; charset=utf-8'),
+  ]);
+  stream.sendData(ascii.encode('''
+<html>
+  <head><title>hello</title></head>
+  <body>
+    <h1> head </h1>
+    first <br />
+    <iframe src='/iframe' with="100" height="100"></iframe> <br />
+    second <br />
+    <iframe src='/iframe2' with="100" height="100"></iframe> <br />
+  </body>
+</html>
+'''));
+  return stream.outgoingMessages.close();
+}
+
+Future push(ServerTransportStream stream, String path,
+    Future sendResponse(TransportStream stream, String path)) async {
+  var requestHeaders = [
+    new Header.ascii(':authority', '$HOSTNAME:$PORT'),
+    new Header.ascii(':method', 'GET'),
+    new Header.ascii(':path', path),
+    new Header.ascii(':scheme', 'https'),
+  ];
+
+  var pushStream = stream.push(requestHeaders);
+  await sendResponse(pushStream, path);
+}
+
+Future sendIFrameHtml(TransportStream stream, String path) async {
+  stream.sendHeaders([
+    new Header.ascii(':status', '200'),
+    new Header.ascii('content-type', 'text/html; charset=utf-8'),
+  ]);
+  stream.sendData(ascii.encode('''
+<html>
+  <head><title>Content for '$path' inside an IFrame.</title></head>
+  <body>
+    <h2>Content for '$path' inside an IFrame.</h2>
+  </body>
+</html>
+'''));
+  await stream.outgoingMessages.close();
+}
+
+Future send404(TransportStream stream, String path) async {
+  stream.sendHeaders([
+    new Header.ascii(':status', '404'),
+    new Header.ascii('content-type', 'text/html; charset=utf-8'),
+  ]);
+  stream.sendData(ascii.encode('''
+<html>
+  <head><title>Path '$path' was not found on this server.</title></head>
+  <body>
+    <h1>Path '$path' was not found on this server.</h1>
+  </body>
+</html>
+'''));
+  return stream.outgoingMessages.close();
+}
diff --git a/http2/experimental/server_chain.pem b/http2/experimental/server_chain.pem
new file mode 100644
index 0000000..4163fe7
--- /dev/null
+++ b/http2/experimental/server_chain.pem
@@ -0,0 +1,57 @@
+-----BEGIN CERTIFICATE-----
+MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
+BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa
+MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq
+Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu
+EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki
+we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb
+N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI
+7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg
+hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
+BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS
+YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd
+AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4
+CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM
+4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG
+MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5
+V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV
+BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw
+WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx
+EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP
+DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE
+YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu
+MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7
+B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd
+IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb
+oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC
+cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8
+x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ
+e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX
+NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4
+0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh
+FKvRDxsW
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV
+BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw
+WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv
+dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw
+siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj
+kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2
+hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV
+DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU
+ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD
+26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ
+lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X
+J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/
+uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE
+4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k
+t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W
+r6AL284qtw==
+-----END CERTIFICATE-----
diff --git a/http2/experimental/server_key.pem b/http2/experimental/server_key.pem
new file mode 100644
index 0000000..1fd2324
--- /dev/null
+++ b/http2/experimental/server_key.pem
@@ -0,0 +1,29 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP
+xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE
+ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5
+Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1
+qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc
+gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU
+0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF
+gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS
+oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn
+oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ
+kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh
+zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa
+J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe
+d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX
+TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76
+ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW
+HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN
+goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im
+EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j
+ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS
+YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3
+q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT
+Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z
+Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH
+QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE
+xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w
+AUukhVtTNn4=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/http2/lib/http2.dart b/http2/lib/http2.dart
new file mode 100644
index 0000000..3f1ed78
--- /dev/null
+++ b/http2/lib/http2.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, 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.
+
+/// This library provides an http/2 interface on top of a bidirectional stream
+/// of bytes.
+///
+/// The client and server sides can be created via [ClientTransportStream] and
+/// [ServerTransportStream] respectively. Both sides can be configured via
+/// settings (see [ClientSettings] and [ServerSettings]). The settings will be
+/// communicated to the remote peer (if necessary) and will be valid during the
+/// entire lifetime of the connection.
+///
+/// A http/2 transport allows a client to open a bidirectional stream (see
+/// [ClientTransportConnection.makeRequest]) and a server can open (or push) a
+/// unidirectional stream to the client via [ServerTransportStream.push].
+///
+/// In both cases (unidirectional and bidirectional stream), one can send
+/// headers and data to the other side (via [HeadersStreamMessage] and
+/// [DataStreamMessage]). These messages are ordered and will arrive in the same
+/// order as they were sent (data messages may be split up into multiple smaller
+/// chunks or might be combined).
+///
+/// In the most common case, each direction will send one [HeadersStreamMessage]
+/// followed by zero or more [DataStreamMessage]s.
+///
+/// Establishing a bidirectional stream of bytes to a server is up to the user
+/// of this library. There are 3 common ways to achive this
+///
+///     * connect to a server via SSL and use the ALPN (SSL) protocol extension
+///       to negotiate with the server to speak http/2 (the ALPN protocol
+///       identifier for http/2 is `h2`)
+///
+///     * have prior knowledge about the server - i.e. know ahead of time that
+///       the server will speak http/2 via an unencrypted tcp connection
+///
+///     * use a http/1.1 connection and upgrade it to http/2
+///
+/// The first way is the most common way and can be done in Dart by using
+/// `dart:io`s secure socket implementation (by using a `SecurityContext` and
+/// including 'h2' in the list of protocols used for ALPN).
+///
+/// A simple example on how to connect to a http/2 capable server and
+/// requesting a resource is available at https://github.com/dart-lang/http2/blob/master/example/display_headers.dart.
+library http2.http2;
+
+import 'transport.dart';
+export 'transport.dart';
diff --git a/http2/lib/multiprotocol_server.dart b/http2/lib/multiprotocol_server.dart
new file mode 100644
index 0000000..6fd168e
--- /dev/null
+++ b/http2/lib/multiprotocol_server.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2016 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 http2.multiprotocol_server;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'src/artificial_server_socket.dart';
+import 'transport.dart' as http2;
+
+/// Handles protocol negotiation with HTTP/1.1 and HTTP/2 clients.
+///
+/// Given a (host, port) pair and a [SecurityContext], [MultiProtocolHttpServer]
+/// will negotiate with the client whether HTTP/1.1 or HTTP/2 should be spoken.
+///
+/// The user must supply 2 callback functions to [startServing], which:
+///   * one handles HTTP/1.1 clients (called with a [HttpRequest])
+///   * one handles HTTP/2 clients (called with a [http2.ServerTransportStream])
+class MultiProtocolHttpServer {
+  final SecureServerSocket _serverSocket;
+  final http2.ServerSettings _settings;
+
+  _ServerSocketController _http11Controller;
+  HttpServer _http11Server;
+
+  StreamController<http2.ServerTransportStream> _http2Controller;
+  Stream<http2.ServerTransportStream> _http2Server;
+  final _http2Connections = new Set<http2.ServerTransportConnection>();
+
+  MultiProtocolHttpServer._(this._serverSocket, this._settings) {
+    _http11Controller =
+        new _ServerSocketController(_serverSocket.address, _serverSocket.port);
+    _http11Server = new HttpServer.listenOn(_http11Controller.stream);
+
+    _http2Controller = new StreamController();
+    _http2Server = _http2Controller.stream;
+  }
+
+  /// Binds a new [SecureServerSocket] with a security [context] at [port] and
+  /// [address] (see [SecureServerSocket.bind] for a description of supported
+  /// types for [address]).
+  ///
+  /// Optionally [settings] can be supplied which will be used for HTTP/2
+  /// clients.
+  ///
+  /// See also [startServing].
+  static Future<MultiProtocolHttpServer> bind(
+      address, int port, SecurityContext context,
+      {http2.ServerSettings settings}) async {
+    context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true);
+    var secureServer = await SecureServerSocket.bind(address, port, context);
+    return new MultiProtocolHttpServer._(secureServer, settings);
+  }
+
+  /// The port this multi-protocol HTTP server runs on.
+  int get port => _serverSocket.port;
+
+  /// The address this multi-protocol HTTP server runs on.
+  InternetAddress get address => _serverSocket.address;
+
+  /// Starts listening for HTTP/1.1 and HTTP/2 clients and calls the given
+  /// callbacks for new clients.
+  ///
+  /// It is expected that [callbackHttp11] and [callbackHttp2] will never throw
+  /// an exception (i.e. these must take care of error handling themselves).
+  void startServing(void callbackHttp11(HttpRequest request),
+      void callbackHttp2(http2.ServerTransportStream stream),
+      {void onError(error, StackTrace stack)}) {
+    // 1. Start listening on the real [SecureServerSocket].
+    _serverSocket.listen((SecureSocket socket) {
+      var protocol = socket.selectedProtocol;
+      if (protocol == null || protocol == 'http/1.1') {
+        _http11Controller.addHttp11Socket(socket);
+      } else if (protocol == 'h2' || protocol == 'h2-14') {
+        var connection = new http2.ServerTransportConnection.viaSocket(socket,
+            settings: _settings);
+        _http2Connections.add(connection);
+        connection.incomingStreams.listen(_http2Controller.add,
+            onError: onError,
+            onDone: () => _http2Connections.remove(connection));
+      } else {
+        socket.destroy();
+        throw new Exception("Unexpected negotiated ALPN protocol: $protocol.");
+      }
+    }, onError: onError);
+
+    // 2. Drain all incoming http/1.1 and http/2 connections and call the
+    // respective handlers.
+    _http11Server.listen(callbackHttp11);
+    _http2Server.listen(callbackHttp2);
+  }
+
+  /// Closes this [MultiProtocolHttpServer].
+  ///
+  /// Completes once everything has been successfully shut down.
+  Future close({bool force: false}) {
+    return _serverSocket.close().whenComplete(() {
+      Future done1 = _http11Server.close(force: force);
+      Future done2 = Future.wait(
+          _http2Connections.map((c) => force ? c.terminate() : c.finish()));
+      return Future.wait([done1, done2]);
+    });
+  }
+}
+
+/// An internal helper class.
+class _ServerSocketController {
+  final InternetAddress address;
+  final int port;
+  final StreamController<Socket> _controller = new StreamController();
+
+  _ServerSocketController(this.address, this.port);
+
+  ArtificialServerSocket get stream {
+    return new ArtificialServerSocket(address, port, _controller.stream);
+  }
+
+  void addHttp11Socket(Socket socket) {
+    _controller.add(socket);
+  }
+
+  Future close() => _controller.close();
+}
diff --git a/http2/lib/src/artificial_server_socket.dart b/http2/lib/src/artificial_server_socket.dart
new file mode 100644
index 0000000..4c5cf07
--- /dev/null
+++ b/http2/lib/src/artificial_server_socket.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2016 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 http2.artificial_server_socket;
+
+import 'dart:async';
+import 'dart:io';
+
+/// Custom implementation of the [ServerSocket] interface.
+///
+/// This class can be used to create a [ServerSocket] using [Stream<Socket>] and
+/// a [InternetAddress] and `port` (an example use case is to filter [Socket]s
+/// and keep the [ServerSocket] interface for APIs that expect it,
+/// e.g. `new HttpServer.listenOn()`).
+class ArtificialServerSocket extends StreamView<Socket>
+    implements ServerSocket {
+  ArtificialServerSocket(this.address, this.port, Stream<Socket> stream)
+      : super(stream);
+
+  // ########################################################################
+  // These are the methods of [ServerSocket] in addition to [Stream<Socket>].
+  // ########################################################################
+
+  final InternetAddress address;
+
+  final int port;
+
+  /// Closing of an [ArtificialServerSocket] is not possible and an exception
+  /// will be thrown when calling this method.
+  Future<ServerSocket> close() async {
+    throw new Exception("Did not expect close() to be called.");
+  }
+}
diff --git a/http2/lib/src/async_utils/async_utils.dart b/http2/lib/src/async_utils/async_utils.dart
new file mode 100644
index 0000000..3f6d44a
--- /dev/null
+++ b/http2/lib/src/async_utils/async_utils.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2015, 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 http2.src.async_utils.async_utils;
+
+import 'dart:async';
+import 'dart:io';
+
+/// An interface for `StreamSink`-like classes to indicate whether adding data
+/// would be buffered and when the buffer is empty again.
+class BufferIndicator {
+  final StreamController _controller =
+      new StreamController.broadcast(sync: true);
+
+  /// A state variable indicating whether buffereing would occur at the moment.
+  bool _wouldBuffer = true;
+
+  /// Indicates whether calling [BufferedBytesWriter.add] would buffer the data
+  /// if called.
+  ///
+  /// This can be used at a higher level as a way to do custom buffering and
+  /// possibly prioritization.
+  bool get wouldBuffer {
+    return _wouldBuffer;
+  }
+
+  /// Signals that no buffering is happening at the moment.
+  void markUnBuffered() {
+    if (_wouldBuffer) {
+      _wouldBuffer = false;
+      _controller.add(null);
+    }
+  }
+
+  /// Signals that buffering starts to happen.
+  void markBuffered() {
+    _wouldBuffer = true;
+  }
+
+  /// A broadcast stream notifying users that the [BufferedBytesWriter.add]
+  /// method would not buffer the data if called.
+  Stream get bufferEmptyEvents => _controller.stream;
+}
+
+/// Contains a [StreamSink] and a [BufferIndicator] to indicate whether writes
+/// to the sink would cause buffering.
+///
+/// It uses the `pause signal` from the `sink.addStream()` as an indicator
+/// whether the underlying stream cannot handle more data and would buffer.
+class BufferedSink {
+  /// The indicator whether the underlying sink is buffering at the moment.
+  final BufferIndicator bufferIndicator = new BufferIndicator();
+
+  /// A intermediate [StreamController] used to catch pause signals and to
+  /// propagate the change via [bufferIndicator].
+  StreamController<List<int>> _controller;
+
+  /// A future which completes once the sink has been closed.
+  Future _doneFuture;
+
+  BufferedSink(StreamSink<List<int>> dataSink) {
+    bufferIndicator.markBuffered();
+
+    _controller = new StreamController<List<int>>(
+        onListen: () {
+          bufferIndicator.markUnBuffered();
+        },
+        onPause: () {
+          bufferIndicator.markBuffered();
+        },
+        onResume: () {
+          bufferIndicator.markUnBuffered();
+        },
+        onCancel: () {
+          // TODO: We may want to propagate cancel events as errors.
+          // Currently `_doneFuture` will just complete normally if the sink
+          // cancelled.
+        },
+        sync: true);
+    _doneFuture =
+        Future.wait([_controller.stream.pipe(dataSink), dataSink.done]);
+  }
+
+  /// The underlying sink.
+  StreamSink<List<int>> get sink => _controller;
+
+  /// The future which will complete once this sink has been closed.
+  Future get doneFuture => _doneFuture;
+}
+
+/// A small wrapper around [BufferedSink] which writes data in batches.
+class BufferedBytesWriter {
+  /// A buffer which will be used for batching writes.
+  final BytesBuilder _builder = new BytesBuilder(copy: false);
+
+  /// The underlying [BufferedSink].
+  final BufferedSink _bufferedSink;
+
+  BufferedBytesWriter(StreamSink<List<int>> outgoing)
+      : _bufferedSink = new BufferedSink(outgoing);
+
+  /// An indicator whether the underlying sink is buffering at the moment.
+  BufferIndicator get bufferIndicator => _bufferedSink.bufferIndicator;
+
+  /// Adds [data] immediately to the underlying buffer.
+  ///
+  /// If there is buffered data which was added with [addBufferedData] and it
+  /// has not been flushed with [flushBufferedData] an error will be thrown.
+  void add(List<int> data) {
+    if (_builder.length > 0) {
+      throw new StateError(
+          'Cannot trigger an asynchronous write while there is buffered data.');
+    }
+    _bufferedSink.sink.add(data);
+  }
+
+  /// Queues up [bytes] to be written.
+  void addBufferedData(List<int> bytes) {
+    _builder.add(bytes);
+  }
+
+  /// Flushes all data which was enqueued by [addBufferedData].
+  void flushBufferedData() {
+    if (_builder.length > 0) {
+      _bufferedSink.sink.add(_builder.takeBytes());
+    }
+  }
+
+  /// Closes this sink.
+  Future close() {
+    flushBufferedData();
+    return _bufferedSink.sink.close().whenComplete(() => doneFuture);
+  }
+
+  /// The future which will complete once this sink has been closed.
+  Future get doneFuture => _bufferedSink.doneFuture;
+}
diff --git a/http2/lib/src/byte_utils.dart b/http2/lib/src/byte_utils.dart
new file mode 100644
index 0000000..756e741
--- /dev/null
+++ b/http2/lib/src/byte_utils.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2015, 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 http2.src.byte_utils;
+
+import 'dart:typed_data';
+
+List<int> viewOrSublist(List<int> data, int offset, int length) {
+  if (data is Uint8List) {
+    return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length);
+  } else {
+    return data.sublist(offset, offset + length);
+  }
+}
+
+int readInt64(List<int> bytes, int offset) {
+  int high = readInt32(bytes, offset);
+  int low = readInt32(bytes, offset + 4);
+  return high << 32 | low;
+}
+
+int readInt32(List<int> bytes, int offset) {
+  return (bytes[offset] << 24) |
+      (bytes[offset + 1] << 16) |
+      (bytes[offset + 2] << 8) |
+      bytes[offset + 3];
+}
+
+int readInt24(List<int> bytes, int offset) {
+  return (bytes[offset] << 16) | (bytes[offset + 1] << 8) | bytes[offset + 2];
+}
+
+int readInt16(List<int> bytes, int offset) {
+  return (bytes[offset] << 8) | bytes[offset + 1];
+}
+
+void setInt64(List<int> bytes, int offset, int value) {
+  setInt32(bytes, offset, value >> 32);
+  setInt32(bytes, offset + 4, value & 0xffffffff);
+}
+
+void setInt32(List<int> bytes, int offset, int value) {
+  bytes[offset] = (value >> 24) & 0xff;
+  bytes[offset + 1] = (value >> 16) & 0xff;
+  bytes[offset + 2] = (value >> 8) & 0xff;
+  bytes[offset + 3] = value & 0xff;
+}
+
+void setInt24(List<int> bytes, int offset, int value) {
+  bytes[offset] = (value >> 16) & 0xff;
+  bytes[offset + 1] = (value >> 8) & 0xff;
+  bytes[offset + 2] = value & 0xff;
+}
+
+void setInt16(List<int> bytes, int offset, int value) {
+  bytes[offset] = (value >> 8) & 0xff;
+  bytes[offset + 1] = value & 0xff;
+}
diff --git a/http2/lib/src/connection.dart b/http2/lib/src/connection.dart
new file mode 100644
index 0000000..271b86f
--- /dev/null
+++ b/http2/lib/src/connection.dart
@@ -0,0 +1,490 @@
+// Copyright (c) 2015, 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 http2.src.conn;
+
+import 'dart:async';
+import 'dart:convert' show utf8;
+
+import '../transport.dart';
+import 'connection_preface.dart';
+import 'flowcontrol/connection_queues.dart';
+import 'flowcontrol/queue_messages.dart';
+import 'flowcontrol/window.dart';
+import 'flowcontrol/window_handler.dart';
+import 'frames/frame_defragmenter.dart';
+import 'frames/frames.dart';
+import 'hpack/hpack.dart';
+import 'ping/ping_handler.dart';
+import 'settings/settings.dart';
+import 'streams/stream_handler.dart';
+import 'sync_errors.dart';
+
+class ConnectionState {
+  /// The connection has been established, we're waiting for the settings frame
+  /// of the remote end.
+  static const int Initialized = 1;
+
+  /// The connection has been established and is fully operational.
+  static const int Operational = 2;
+
+  /// The connection is no longer accepting new streams or creating new streams.
+  static const int Finishing = 3;
+
+  /// The connection has been terminated and cannot be used anymore.
+  static const int Terminated = 4;
+
+  /// Whether we actively were finishing the connection.
+  static const int FinishingActive = 1;
+
+  /// Whether we passively were finishing the connection.
+  static const int FinishingPassive = 2;
+
+  int state = Initialized;
+  int finishingState = 0;
+
+  ConnectionState();
+
+  bool get isInitialized => state == ConnectionState.Initialized;
+
+  bool get isOperational => state == ConnectionState.Operational;
+
+  bool get isFinishing => state == ConnectionState.Finishing;
+
+  bool get isTerminated => state == ConnectionState.Terminated;
+
+  bool get activeFinishing =>
+      state == Finishing && (finishingState & FinishingActive) != 0;
+
+  bool get passiveFinishing =>
+      state == Finishing && (finishingState & FinishingPassive) != 0;
+
+  String toString() {
+    String message = '';
+
+    void add(bool condition, String flag) {
+      if (condition) {
+        if (message.length == 0) {
+          message = flag;
+        } else {
+          message = '$message/$flag';
+        }
+      }
+    }
+
+    add(isInitialized, 'Initialized');
+    add(isOperational, 'IsOperational');
+    add(isFinishing, 'IsFinishing');
+    add(isTerminated, 'IsTerminated');
+    add(activeFinishing, 'ActiveFinishing');
+    add(passiveFinishing, 'PassiveFinishing');
+
+    return message;
+  }
+}
+
+abstract class Connection {
+  /// The settings the other end has acknowledged to use when communicating with
+  /// us.
+  final ActiveSettings acknowledgedSettings = new ActiveSettings();
+
+  /// The settings we have to obey communicating with the other side.
+  final ActiveSettings peerSettings = new ActiveSettings();
+
+  /// Whether this connection is a client connection.
+  final bool isClientConnection;
+
+  /// Active state handler for this connection.
+  ActiveStateHandler onActiveStateChanged;
+
+  /// The HPack context for this connection.
+  final HPackContext _hpackContext = new HPackContext();
+
+  /// The flow window for this connection of the peer.
+  final Window _peerWindow = new Window();
+
+  /// The flow window for this connection of this end.
+  final Window _localWindow = new Window();
+
+  /// Used for defragmenting PushPromise/Header frames.
+  final FrameDefragmenter _defragmenter = new FrameDefragmenter();
+
+  /// The outgoing frames of this connection;
+  FrameWriter _frameWriter;
+
+  /// A subscription of incoming [Frame]s.
+  StreamSubscription<Frame> _frameReaderSubscription;
+
+  /// The incoming connection-level message queue.
+  ConnectionMessageQueueIn _incomingQueue;
+
+  /// The outgoing connection-level message queue.
+  ConnectionMessageQueueOut _outgoingQueue;
+
+  /// The ping handler used for making pings & handling remote pings.
+  PingHandler _pingHandler;
+
+  /// The settings handler used for changing settings & for handling remote
+  /// setting changes.
+  SettingsHandler _settingsHandler;
+
+  /// The set of active streams this connection has.
+  StreamHandler _streams;
+
+  /// The connection-level flow control window handler for outgoing messages.
+  OutgoingConnectionWindowHandler _connectionWindowHandler;
+
+  /// The state of this connection.
+  ConnectionState _state;
+
+  Connection(Stream<List<int>> incoming, StreamSink<List<int>> outgoing,
+      Settings settings,
+      {this.isClientConnection: true}) {
+    _setupConnection(incoming, outgoing, settings);
+  }
+
+  /// Runs all setup necessary before new streams can be created with the remote
+  /// peer.
+  void _setupConnection(Stream<List<int>> incoming,
+      StreamSink<List<int>> outgoing, Settings settingsObject) {
+    // Setup frame reading.
+    var incomingFrames =
+        new FrameReader(incoming, acknowledgedSettings).startDecoding();
+    _frameReaderSubscription = incomingFrames.listen((Frame frame) {
+      _catchProtocolErrors(() => _handleFrameImpl(frame));
+    }, onError: (error, stack) {
+      _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true);
+    }, onDone: () {
+      // Ensure existing messages from lower levels are sent to the upper
+      // levels before we terminate everything.
+      _incomingQueue.forceDispatchIncomingMessages();
+      _streams.forceDispatchIncomingMessages();
+
+      _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true);
+    });
+
+    // Setup frame writing.
+    _frameWriter =
+        new FrameWriter(_hpackContext.encoder, outgoing, peerSettings);
+    _frameWriter.doneFuture.then((_) {
+      _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true);
+    }).catchError((error, stack) {
+      _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true);
+    });
+
+    // Setup handlers.
+    _settingsHandler = new SettingsHandler(_hpackContext.encoder, _frameWriter,
+        acknowledgedSettings, peerSettings);
+    _pingHandler = new PingHandler(_frameWriter);
+
+    var settings = _decodeSettings(settingsObject);
+
+    // Do the initial settings handshake (possibly with pushes disabled).
+    _settingsHandler.changeSettings(settings).catchError((error) {
+      // TODO: The [error] can contain sensitive information we now expose via
+      // a [Goaway] frame. We should somehow ensure we're only sending useful
+      // but non-sensitive information.
+      _terminate(ErrorCode.PROTOCOL_ERROR,
+          message: 'Failed to set initial settings (error: $error).');
+    });
+
+    _settingsHandler.onInitialWindowSizeChange.listen((int difference) {
+      _catchProtocolErrors(() {
+        _streams.processInitialWindowSizeSettingChange(difference);
+      });
+    });
+
+    // Setup the connection window handler, which keeps track of the
+    // size of the outgoing connection window.
+    _connectionWindowHandler = new OutgoingConnectionWindowHandler(_peerWindow);
+
+    var connectionWindowUpdater =
+        new IncomingWindowHandler.connection(_frameWriter, _localWindow);
+
+    // Setup queues for outgoing/incoming messages on the connection level.
+    _outgoingQueue =
+        new ConnectionMessageQueueOut(_connectionWindowHandler, _frameWriter);
+    _incomingQueue = new ConnectionMessageQueueIn(
+        connectionWindowUpdater, _catchProtocolErrors);
+
+    if (isClientConnection) {
+      _streams = new StreamHandler.client(
+          _frameWriter,
+          _incomingQueue,
+          _outgoingQueue,
+          _settingsHandler.peerSettings,
+          _settingsHandler.acknowledgedSettings,
+          _activeStateHandler);
+    } else {
+      _streams = new StreamHandler.server(
+          _frameWriter,
+          _incomingQueue,
+          _outgoingQueue,
+          _settingsHandler.peerSettings,
+          _settingsHandler.acknowledgedSettings,
+          _activeStateHandler);
+    }
+
+    // NOTE: We're not waiting until initial settings have been exchanged
+    // before we start using the connection (i.e. we don't wait for half a
+    // round-trip-time).
+    _state = new ConnectionState();
+  }
+
+  List<Setting> _decodeSettings(Settings settings) {
+    var settingsList = <Setting>[];
+
+    // By default a endpoitn can make an unlimited number of concurrent streams.
+    if (settings.concurrentStreamLimit != null) {
+      settingsList.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS,
+          settings.concurrentStreamLimit));
+    }
+
+    // By default the stream level flow control window is 64 KiB.
+    if (settings.streamWindowSize != null) {
+      settingsList.add(new Setting(
+          Setting.SETTINGS_INITIAL_WINDOW_SIZE, settings.streamWindowSize));
+    }
+
+    if (settings is ClientSettings) {
+      // By default the server is allowed to do server pushes.
+      if (settings.allowServerPushes == null ||
+          settings.allowServerPushes == false) {
+        settingsList.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0));
+      }
+    } else if (settings is ServerSettings) {
+      // No special server settings at the moment.
+    } else {
+      assert(false);
+    }
+
+    return settingsList;
+  }
+
+  /// Pings the remote peer (can e.g. be used for measuring latency).
+  Future ping() {
+    return _pingHandler.ping().catchError((e, s) {
+      return new Future.error(
+          new TransportException('The connection has been terminated.'));
+    }, test: (e) => e is TerminatedException);
+  }
+
+  /// Finishes this connection.
+  Future finish() {
+    _finishing(active: true);
+
+    // TODO: There is probably more we need to wait for.
+    return _streams.done.whenComplete(() {
+      var futures = [_frameWriter.close()];
+      var f = _frameReaderSubscription.cancel();
+      if (f != null) futures.add(f);
+      return Future.wait(futures);
+    });
+  }
+
+  /// Terminates this connection forcefully.
+  Future terminate() {
+    return _terminate(ErrorCode.NO_ERROR);
+  }
+
+  void _activeStateHandler(bool isActive) {
+    if (onActiveStateChanged != null) {
+      onActiveStateChanged(isActive);
+    }
+  }
+
+  /// Invokes the passed in closure and catches any exceptions.
+  void _catchProtocolErrors(void fn()) {
+    try {
+      fn();
+    } on ProtocolException catch (error) {
+      _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error');
+    } on FlowControlException catch (error) {
+      _terminate(ErrorCode.FLOW_CONTROL_ERROR, message: '$error');
+    } on FrameSizeException catch (error) {
+      _terminate(ErrorCode.FRAME_SIZE_ERROR, message: '$error');
+    } on HPackDecodingException catch (error) {
+      _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error');
+    } on TerminatedException {
+      // We tried to perform an action even though the connection was already
+      // terminated.
+      // TODO: Can this even happen and if so, how should we propagate this
+      // error?
+    } catch (error) {
+      _terminate(ErrorCode.INTERNAL_ERROR, message: '$error');
+    }
+  }
+
+  void _handleFrameImpl(Frame frame) {
+    // The first frame from the other side must be a [SettingsFrame], otherwise
+    // we terminate the connection.
+    if (_state.isInitialized) {
+      if (frame is! SettingsFrame) {
+        _terminate(ErrorCode.PROTOCOL_ERROR,
+            message: 'Expected to first receive a settings frame.');
+        return;
+      }
+      _state.state = ConnectionState.Operational;
+    }
+
+    // Try to defragment [frame] if it is a Headers/PushPromise frame.
+    frame = _defragmenter.tryDefragmentFrame(frame);
+    if (frame == null) return;
+
+    // Try to decode headers if it's a Headers/PushPromise frame.
+    // [This needs to be done even if the frames get ignored, since the entire
+    //  connection shares one HPack compression context.]
+    if (frame is HeadersFrame) {
+      frame.decodedHeaders =
+          _hpackContext.decoder.decode(frame.headerBlockFragment);
+    } else if (frame is PushPromiseFrame) {
+      frame.decodedHeaders =
+          _hpackContext.decoder.decode(frame.headerBlockFragment);
+    }
+
+    // Handle the frame as either a connection or a stream frame.
+    if (frame.header.streamId == 0) {
+      if (frame is SettingsFrame) {
+        _settingsHandler.handleSettingsFrame(frame);
+      } else if (frame is PingFrame) {
+        _pingHandler.processPingFrame(frame);
+      } else if (frame is WindowUpdateFrame) {
+        _connectionWindowHandler.processWindowUpdate(frame);
+      } else if (frame is GoawayFrame) {
+        _streams.processGoawayFrame(frame);
+        _finishing(active: false);
+      } else if (frame is UnknownFrame) {
+        // We can safely ignore these.
+      } else {
+        throw new ProtocolException(
+            'Cannot handle frame type ${frame.runtimeType} with stream-id 0.');
+      }
+    } else {
+      _streams.processStreamFrame(_state, frame);
+    }
+  }
+
+  void _finishing({bool active: true, String message}) {
+    // If this connection is already dead, we return.
+    if (_state.isTerminated) return;
+
+    // If this connection is already finishing, we make sure to store the
+    // passive bit, since this information is used by [StreamHandler].
+    //
+    // Vice versa should not matter: If we started passively finishing, an
+    // active finish should be a NOP.
+    if (_state.isFinishing) {
+      if (!active) _state.finishingState |= ConnectionState.FinishingPassive;
+      return;
+    }
+
+    assert(_state.isInitialized || _state.isOperational);
+
+    // If we are actively finishing this connection, we'll send a
+    // GoawayFrame otherwise we'll just propagate the message.
+    if (active) {
+      _state.state = ConnectionState.Finishing;
+      _state.finishingState |= ConnectionState.FinishingActive;
+
+      _outgoingQueue.enqueueMessage(new GoawayMessage(
+          _streams.highestPeerInitiatedStream,
+          ErrorCode.NO_ERROR,
+          message != null ? utf8.encode(message) : []));
+    } else {
+      _state.state = ConnectionState.Finishing;
+      _state.finishingState |= ConnectionState.FinishingPassive;
+    }
+
+    _streams.startClosing();
+  }
+
+  /// Terminates this connection (if it is not already terminated).
+  ///
+  /// The returned future will never complete with an error.
+  Future _terminate(int errorCode,
+      {bool causedByTransportError: false, String message}) {
+    // TODO: When do we complete here?
+    if (_state.state != ConnectionState.Terminated) {
+      _state.state = ConnectionState.Terminated;
+
+      var cancelFuture = new Future.sync(_frameReaderSubscription.cancel);
+      if (!causedByTransportError) {
+        _outgoingQueue.enqueueMessage(new GoawayMessage(
+            _streams.highestPeerInitiatedStream,
+            errorCode,
+            message != null ? utf8.encode(message) : []));
+      }
+      var closeFuture = _frameWriter.close().catchError((e, s) {
+        // We ignore any errors after writing to [GoawayFrame]
+      });
+
+      // Close all lower level handlers with an error message.
+      // (e.g. if there is a pending connection.ping(), it's returned
+      //  Future will complete with this error).
+      var exception = new TransportConnectionException(
+          errorCode, 'Connection is being forcefully terminated.');
+
+      // Close all streams & stream queues
+      _streams.terminate(exception);
+
+      // Close the connection queues
+      _incomingQueue.terminate(exception);
+      _outgoingQueue.terminate(exception);
+
+      _pingHandler.terminate(exception);
+      _settingsHandler.terminate(exception);
+
+      return Future.wait([cancelFuture, closeFuture]).catchError((_) {});
+    }
+    return new Future.value();
+  }
+}
+
+class ClientConnection extends Connection implements ClientTransportConnection {
+  ClientConnection._(Stream<List<int>> incoming, StreamSink<List<int>> outgoing,
+      Settings settings)
+      : super(incoming, outgoing, settings, isClientConnection: true);
+
+  factory ClientConnection(Stream<List<int>> incoming,
+      StreamSink<List<int>> outgoing, ClientSettings clientSettings) {
+    outgoing.add(CONNECTION_PREFACE);
+    return new ClientConnection._(incoming, outgoing, clientSettings);
+  }
+
+  bool get isOpen =>
+      !_state.isFinishing && !_state.isTerminated && _streams.canOpenStream;
+
+  ClientTransportStream makeRequest(List<Header> headers,
+      {bool endStream: false}) {
+    if (_state.isFinishing) {
+      throw new StateError(
+          'The http/2 connection is finishing and can therefore not be used to '
+          'make new streams.');
+    } else if (_state.isTerminated) {
+      throw new StateError(
+          'The http/2 connection is no longer active and can therefore not be '
+          'used to make new streams.');
+    }
+    var hStream = _streams.newStream(headers, endStream: endStream);
+    if (_streams.ranOutOfStreamIds) {
+      _finishing(active: true, message: 'Ran out of stream ids');
+    }
+    return hStream;
+  }
+}
+
+class ServerConnection extends Connection implements ServerTransportConnection {
+  ServerConnection._(Stream<List<int>> incoming, StreamSink<List<int>> outgoing,
+      Settings settings)
+      : super(incoming, outgoing, settings, isClientConnection: false);
+
+  factory ServerConnection(Stream<List<int>> incoming,
+      StreamSink<List<int>> outgoing, ServerSettings serverSettings) {
+    var frameBytes = readConnectionPreface(incoming);
+    return new ServerConnection._(frameBytes, outgoing, serverSettings);
+  }
+
+  Stream<ServerTransportStream> get incomingStreams =>
+      _streams.incomingStreams.cast<ServerTransportStream>();
+}
diff --git a/http2/lib/src/connection_preface.dart b/http2/lib/src/connection_preface.dart
new file mode 100644
index 0000000..08cf7e8
--- /dev/null
+++ b/http2/lib/src/connection_preface.dart
@@ -0,0 +1,118 @@
+// Copyright (c) 2015, 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 http2.src.connection_preface;
+
+import 'dart:async';
+import 'dart:math';
+
+import 'byte_utils.dart';
+
+/// This is a set of bytes with which a client connection begins in the normal
+/// case. It can be used on a server to distinguish HTTP/1.1 and HTTP/2 clients.
+const List<int> CONNECTION_PREFACE = const [
+  0x50,
+  0x52,
+  0x49,
+  0x20,
+  0x2a,
+  0x20,
+  0x48,
+  0x54,
+  0x54,
+  0x50,
+  0x2f,
+  0x32,
+  0x2e,
+  0x30,
+  0x0d,
+  0x0a,
+  0x0d,
+  0x0a,
+  0x53,
+  0x4d,
+  0x0d,
+  0x0a,
+  0x0d,
+  0x0a
+];
+
+/// Reads the connection preface from [incoming].
+///
+/// The returned `Stream` will be a duplicate of `incoming` without the
+/// connection preface. If an error occurs while reading the connection
+/// preface, the returned stream will have only an error.
+Stream<List<int>> readConnectionPreface(Stream<List<int>> incoming) {
+  StreamController<List<int>> result;
+  StreamSubscription subscription;
+  bool connectionPrefaceRead = false;
+  var prefaceBuffer = <int>[];
+  bool terminated = false;
+
+  terminate(error) {
+    if (!terminated) {
+      result.addError(error);
+      result.close();
+      subscription.cancel();
+    }
+    terminated = true;
+  }
+
+  bool compareConnectionPreface(List<int> data) {
+    for (int i = 0; i < CONNECTION_PREFACE.length; i++) {
+      if (data[i] != CONNECTION_PREFACE[i]) {
+        terminate('Connection preface does not match.');
+        return false;
+      }
+    }
+    prefaceBuffer = null;
+    connectionPrefaceRead = true;
+    return true;
+  }
+
+  void onData(List<int> data) {
+    if (connectionPrefaceRead) {
+      // Forward data after reading preface.
+      result.add(data);
+    } else {
+      if (prefaceBuffer.isEmpty && data.length > CONNECTION_PREFACE.length) {
+        if (!compareConnectionPreface(data)) return;
+        data = data.sublist(CONNECTION_PREFACE.length);
+      } else if (prefaceBuffer.length < CONNECTION_PREFACE.length) {
+        int remaining = CONNECTION_PREFACE.length - prefaceBuffer.length;
+
+        int end = min(data.length, remaining);
+        var part1 = viewOrSublist(data, 0, end);
+        var part2 = viewOrSublist(data, end, data.length - end);
+        prefaceBuffer.addAll(part1);
+
+        if (prefaceBuffer.length == CONNECTION_PREFACE.length) {
+          if (!compareConnectionPreface(prefaceBuffer)) return;
+        }
+        data = part2;
+      }
+      if (data.length > 0) {
+        result.add(data);
+      }
+    }
+  }
+
+  result = new StreamController(
+      onListen: () {
+        subscription = incoming.listen(onData,
+            onError: (e, StackTrace s) => result.addError(e, s),
+            onDone: () {
+              if (prefaceBuffer != null) {
+                terminate('EOS before connection preface could be read.');
+              } else {
+                result.close();
+              }
+            });
+      },
+      onPause: () => subscription.pause(),
+      onResume: () => subscription.resume(),
+      onCancel: () => subscription.cancel());
+
+  return result.stream;
+}
diff --git a/http2/lib/src/error_handler.dart b/http2/lib/src/error_handler.dart
new file mode 100644
index 0000000..ec1935d
--- /dev/null
+++ b/http2/lib/src/error_handler.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2015, 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 http2.src.error_handler;
+
+import 'dart:async';
+
+import 'sync_errors.dart';
+
+/// Used by classes which may be terminated from the outside.
+class TerminatableMixin {
+  bool _terminated = false;
+
+  /// Terminates this stream message queue. Further operations on it will fail.
+  void terminate([error]) {
+    if (!wasTerminated) {
+      _terminated = true;
+      onTerminated(error);
+    }
+  }
+
+  bool get wasTerminated => _terminated;
+
+  void onTerminated(error) {
+    // Subclasses can override this method if they want.
+  }
+
+  T ensureNotTerminatedSync<T>(T f()) {
+    if (wasTerminated) {
+      throw new TerminatedException();
+    }
+    return f();
+  }
+
+  Future ensureNotTerminatedAsync(Future f()) {
+    if (wasTerminated) {
+      return new Future.error(new TerminatedException());
+    }
+    return f();
+  }
+}
+
+/// Used by classes which may be cancelled.
+class CancellableMixin {
+  bool _cancelled = false;
+  final _cancelCompleter = new Completer<void>.sync();
+
+  Future<void> get onCancel => _cancelCompleter.future;
+
+  /// Cancel this stream message queue. Further operations on it will fail.
+  void cancel() {
+    if (!wasCancelled) {
+      _cancelled = true;
+      _cancelCompleter.complete();
+    }
+  }
+
+  bool get wasCancelled => _cancelled;
+}
+
+/// Used by classes which may be closed.
+class ClosableMixin {
+  bool _closing = false;
+  final Completer _completer = new Completer();
+
+  Future get done => _completer.future;
+
+  bool get isClosing => _closing;
+  bool get wasClosed => _completer.isCompleted;
+
+  void startClosing() {
+    if (!_closing) {
+      _closing = true;
+
+      onClosing();
+    }
+    onCheckForClose();
+  }
+
+  void onCheckForClose() {
+    // Subclasses can override this method if they want.
+  }
+
+  void onClosing() {
+    // Subclasses can override this method if they want.
+  }
+
+  dynamic ensureNotClosingSync(f()) {
+    if (isClosing) {
+      throw new StateError('Was in the process of closing.');
+    }
+    return f();
+  }
+
+  void closeWithValue([value]) {
+    if (!wasClosed) {
+      _completer.complete(value);
+    }
+  }
+
+  void closeWithError(error) {
+    if (!wasClosed) {
+      _completer.complete(error);
+    }
+  }
+}
diff --git a/http2/lib/src/flowcontrol/connection_queues.dart b/http2/lib/src/flowcontrol/connection_queues.dart
new file mode 100644
index 0000000..4d28db2
--- /dev/null
+++ b/http2/lib/src/flowcontrol/connection_queues.dart
@@ -0,0 +1,355 @@
+// Copyright (c) 2015, 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.
+
+// TODO: Take priorities into account.
+// TODO: Properly fragment large data frames, so they are not taking up too much
+// bandwidth.
+library http2.src.flowcontrol.connection_flow_controller;
+
+import 'dart:async';
+import 'dart:collection';
+
+import '../../transport.dart';
+
+import '../byte_utils.dart';
+import '../error_handler.dart';
+import '../frames/frames.dart';
+
+import 'queue_messages.dart';
+import 'stream_queues.dart';
+import 'window_handler.dart';
+
+/// The last place before messages coming from the application get encoded and
+/// send as [Frame]s.
+///
+/// It will convert [Message]s from higher layers and send them via [Frame]s.
+///
+/// - It will queue messages until the connection-level flow control window
+///   allows sending the message and the underlying [StreamSink] is not
+///   buffering.
+/// - It will use a [FrameWriter] to write a new frame to the connection.
+// TODO: Make [StreamsHandler] call [connectionOut.startClosing()] once
+//   * all streams have been closed
+//   * the connection state is finishing
+class ConnectionMessageQueueOut extends Object
+    with TerminatableMixin, ClosableMixin {
+  /// The handler which will be used for increasing the connection-level flow
+  /// control window.
+  final OutgoingConnectionWindowHandler _connectionWindow;
+
+  /// The buffered [Message]s which are to be delivered to the remote peer.
+  final Queue<Message> _messages = new Queue<Message>();
+
+  /// The [FrameWriter] used for writing Headers/Data/PushPromise frames.
+  final FrameWriter _frameWriter;
+
+  ConnectionMessageQueueOut(this._connectionWindow, this._frameWriter) {
+    _frameWriter.bufferIndicator.bufferEmptyEvents.listen((_) {
+      _trySendMessages();
+    });
+    _connectionWindow.positiveWindow.bufferEmptyEvents.listen((_) {
+      _trySendMessages();
+    });
+  }
+
+  /// The number of pending messages which haven't been written to the wire.
+  int get pendingMessages => _messages.length;
+
+  /// Enqueues a new [Message] which should be delivered to the remote peer.
+  void enqueueMessage(Message message) {
+    ensureNotClosingSync(() {
+      if (!wasTerminated) {
+        _messages.addLast(message);
+        _trySendMessages();
+      }
+    });
+  }
+
+  void onTerminated(error) {
+    _messages.clear();
+    closeWithError(error);
+  }
+
+  void onCheckForClose() {
+    if (isClosing && _messages.length == 0) {
+      closeWithValue();
+    }
+  }
+
+  void _trySendMessages() {
+    if (!wasTerminated) {
+      // We can make progress if
+      //   * there is at least one message to send
+      //   * the underlying frame writer / sink / socket doesn't block
+      //   * either one
+      //     * the next message is a non-flow control message (e.g. headers)
+      //     * the connection window is positive
+
+      if (_messages.length > 0 &&
+          !_frameWriter.bufferIndicator.wouldBuffer &&
+          (!_connectionWindow.positiveWindow.wouldBuffer ||
+              _messages.first is! DataMessage)) {
+        _trySendMessage();
+
+        // If we have more messages and we can send them, we'll run them
+        // using `Timer.run()` to let other things get in-between.
+        if (_messages.length > 0 &&
+            !_frameWriter.bufferIndicator.wouldBuffer &&
+            (!_connectionWindow.positiveWindow.wouldBuffer ||
+                _messages.first is! DataMessage)) {
+          // TODO: If all the frame writer methods would return the
+          // number of bytes written, we could just say, we loop here until 10kb
+          // and after words, we'll make `Timer.run()`.
+          Timer.run(_trySendMessages);
+        } else {
+          onCheckForClose();
+        }
+      }
+    }
+  }
+
+  void _trySendMessage() {
+    Message message = _messages.first;
+    if (message is HeadersMessage) {
+      _messages.removeFirst();
+      _frameWriter.writeHeadersFrame(message.streamId, message.headers,
+          endStream: message.endStream);
+    } else if (message is PushPromiseMessage) {
+      _messages.removeFirst();
+      _frameWriter.writePushPromiseFrame(
+          message.streamId, message.promisedStreamId, message.headers);
+    } else if (message is DataMessage) {
+      _messages.removeFirst();
+
+      if (_connectionWindow.peerWindowSize >= message.bytes.length) {
+        _connectionWindow.decreaseWindow(message.bytes.length);
+        _frameWriter.writeDataFrame(message.streamId, message.bytes,
+            endStream: message.endStream);
+      } else {
+        // NOTE: We need to fragment the DataMessage.
+        // TODO: Do not fragment if the number of bytes we can send is too low
+        int len = _connectionWindow.peerWindowSize;
+        var head = viewOrSublist(message.bytes, 0, len);
+        var tail =
+            viewOrSublist(message.bytes, len, message.bytes.length - len);
+
+        _connectionWindow.decreaseWindow(head.length);
+        _frameWriter.writeDataFrame(message.streamId, head, endStream: false);
+
+        var tailMessage =
+            new DataMessage(message.streamId, tail, message.endStream);
+        _messages.addFirst(tailMessage);
+      }
+    } else if (message is ResetStreamMessage) {
+      _messages.removeFirst();
+      _frameWriter.writeRstStreamFrame(message.streamId, message.errorCode);
+    } else if (message is GoawayMessage) {
+      _messages.removeFirst();
+      _frameWriter.writeGoawayFrame(
+          message.lastStreamId, message.errorCode, message.debugData);
+    } else {
+      throw new StateError(
+          'Unexpected message in queue: ${message.runtimeType}');
+    }
+  }
+}
+
+/// The first place an incoming stream message gets delivered to.
+///
+/// The [ConnectionMessageQueueIn] will be given [Frame]s which were sent to
+/// any stream on this connection.
+///
+/// - It will extract the necessary data from the [Frame] and store it in a new
+///   [Message] object.
+/// - It will multiplex the created [Message]es to a stream-specific
+///   [StreamMessageQueueIn].
+/// - If the [StreamMessageQueueIn] cannot accept more data, the data will be
+///   buffered until it can.
+/// - [DataMessage]s which have been successfully delivered to a stream-specific
+///   [StreamMessageQueueIn] will increase the flow control window for the
+///   connection.
+///
+/// Incoming [DataFrame]s will decrease the flow control window the peer has
+/// available.
+// TODO: Make [StreamsHandler] call [connectionOut.startClosing()] once
+//   * all streams have been closed
+//   * the connection state is finishing
+class ConnectionMessageQueueIn extends Object
+    with TerminatableMixin, ClosableMixin {
+  /// The handler which will be used for increasing the connection-level flow
+  /// control window.
+  final IncomingWindowHandler _windowUpdateHandler;
+
+  /// Catches any protocol errors and acts upon them.
+  final Function _catchProtocolErrors;
+
+  /// A mapping from stream-id to the corresponding stream-specific
+  /// [StreamMessageQueueIn].
+  final Map<int, StreamMessageQueueIn> _stream2messageQueue = {};
+
+  /// A buffer for [Message]s which cannot be received by their
+  /// [StreamMessageQueueIn].
+  final Map<int, Queue<Message>> _stream2pendingMessages = {};
+
+  /// The number of pending messages which haven't been delivered
+  /// to the stream-specific queue. (for debugging purposes)
+  int _count = 0;
+
+  ConnectionMessageQueueIn(
+      this._windowUpdateHandler, this._catchProtocolErrors);
+
+  void onTerminated(error) {
+    // NOTE: The higher level will be shutdown first, so all streams
+    // should have been removed at this point.
+    assert(_stream2messageQueue.isEmpty);
+    assert(_stream2pendingMessages.isEmpty);
+    closeWithError(error);
+  }
+
+  void onCheckForClose() {
+    if (isClosing) {
+      assert(_stream2messageQueue.isEmpty == _stream2pendingMessages.isEmpty);
+      if (_stream2messageQueue.isEmpty) {
+        closeWithValue();
+      }
+    }
+  }
+
+  /// The number of pending messages which haven't been delivered
+  /// to the stream-specific queue. (for debugging purposes)
+  int get pendingMessages => _count;
+
+  /// Registers a stream specific [StreamMessageQueueIn] for a new stream id.
+  void insertNewStreamMessageQueue(int streamId, StreamMessageQueueIn mq) {
+    if (_stream2messageQueue.containsKey(streamId)) {
+      throw new ArgumentError(
+          'Cannot register a SteramMessageQueueIn for the same streamId '
+          'multiple times');
+    }
+
+    var pendingMessages = new Queue<Message>();
+    _stream2pendingMessages[streamId] = pendingMessages;
+    _stream2messageQueue[streamId] = mq;
+
+    mq.bufferIndicator.bufferEmptyEvents.listen((_) {
+      _catchProtocolErrors(() {
+        _tryDispatch(streamId, mq, pendingMessages);
+      });
+    });
+  }
+
+  /// Removes a stream id and its message queue from this connection-level
+  /// message queue.
+  void removeStreamMessageQueue(int streamId) {
+    _stream2pendingMessages.remove(streamId);
+    _stream2messageQueue.remove(streamId);
+  }
+
+  /// Processes an incoming [DataFrame] which is addressed to a specific stream.
+  void processDataFrame(DataFrame frame) {
+    var streamId = frame.header.streamId;
+    var message =
+        new DataMessage(streamId, frame.bytes, frame.hasEndStreamFlag);
+
+    _windowUpdateHandler.gotData(message.bytes.length);
+    _addMessage(streamId, message);
+  }
+
+  /// If a [DataFrame] will be ignored, this method will take the minimal
+  /// action necessary.
+  void processIgnoredDataFrame(DataFrame frame) {
+    _windowUpdateHandler.gotData(frame.bytes.length);
+  }
+
+  /// Processes an incoming [HeadersFrame] which is addressed to a specific
+  /// stream.
+  void processHeadersFrame(HeadersFrame frame) {
+    var streamId = frame.header.streamId;
+    var message = new HeadersMessage(
+        streamId, frame.decodedHeaders, frame.hasEndStreamFlag);
+    // NOTE: Header frames do not affect flow control - only data frames do.
+    _addMessage(streamId, message);
+  }
+
+  /// Processes an incoming [PushPromiseFrame] which is addressed to a specific
+  /// stream.
+  void processPushPromiseFrame(
+      PushPromiseFrame frame, ClientTransportStream pushedStream) {
+    var streamId = frame.header.streamId;
+    var message = new PushPromiseMessage(streamId, frame.decodedHeaders,
+        frame.promisedStreamId, pushedStream, false);
+
+    // NOTE:
+    //    * Header frames do not affect flow control - only data frames do.
+    //    * At this point we might reorder a push message earlier than
+    //      data/headers messages.
+    _addPushMessage(streamId, message);
+  }
+
+  void _addMessage(int streamId, Message message) {
+    _count++;
+
+    // TODO: Do we need to do a runtime check here and
+    // raise a protocol error if we cannot find the registered stream?
+    var streamMQ = _stream2messageQueue[streamId];
+    var pendingMessages = _stream2pendingMessages[streamId];
+    pendingMessages.addLast(message);
+    _tryDispatch(streamId, streamMQ, pendingMessages);
+  }
+
+  void _addPushMessage(int streamId, PushPromiseMessage message) {
+    _count++;
+
+    // TODO: Do we need to do a runtime check here and
+    // raise a protocol error if we cannot find the registered stream?
+    var streamMQ = _stream2messageQueue[streamId];
+    streamMQ.enqueueMessage(message);
+  }
+
+  void _tryDispatch(
+      int streamId, StreamMessageQueueIn mq, Queue<Message> pendingMessages) {
+    int bytesDeliveredToStream = 0;
+    while (!mq.bufferIndicator.wouldBuffer && pendingMessages.length > 0) {
+      _count--;
+
+      var message = pendingMessages.removeFirst();
+      if (message is DataMessage) {
+        bytesDeliveredToStream += message.bytes.length;
+      }
+      mq.enqueueMessage(message);
+      if (message.endStream) {
+        assert(pendingMessages.isEmpty);
+
+        _stream2messageQueue.remove(streamId);
+        _stream2pendingMessages.remove(streamId);
+      }
+    }
+    if (bytesDeliveredToStream > 0) {
+      _windowUpdateHandler.dataProcessed(bytesDeliveredToStream);
+    }
+
+    onCheckForClose();
+  }
+
+  void forceDispatchIncomingMessages() {
+    final toBeRemoved = new Set<int>();
+    _stream2pendingMessages.forEach((int streamId, Queue<Message> messages) {
+      final mq = _stream2messageQueue[streamId];
+      while (messages.isNotEmpty) {
+        _count--;
+        final message = messages.removeFirst();
+        mq.enqueueMessage(message);
+        if (message.endStream) {
+          toBeRemoved.add(streamId);
+          break;
+        }
+      }
+    });
+
+    for (final streamId in toBeRemoved) {
+      _stream2messageQueue.remove(streamId);
+      _stream2pendingMessages.remove(streamId);
+    }
+  }
+}
diff --git a/http2/lib/src/flowcontrol/queue_messages.dart b/http2/lib/src/flowcontrol/queue_messages.dart
new file mode 100644
index 0000000..b30ab3f
--- /dev/null
+++ b/http2/lib/src/flowcontrol/queue_messages.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2015, 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 http2.src.flowcontrol.queue_messages;
+
+import '../../transport.dart';
+
+/// The subclasses of [Message] are objects that are coming from the
+/// connection layer on top of frames.
+///
+/// Messages on a HTTP/2 stream will be represented by a different class
+/// hierarchy.
+abstract class Message {
+  final int streamId;
+  final bool endStream;
+
+  Message(this.streamId, this.endStream);
+}
+
+class HeadersMessage extends Message {
+  final List<Header> headers;
+
+  HeadersMessage(int streamId, this.headers, bool endStream)
+      : super(streamId, endStream);
+
+  String toString() =>
+      'HeadersMessage(headers: ${headers.length}, endStream: $endStream)';
+}
+
+class DataMessage extends Message {
+  final List<int> bytes;
+
+  DataMessage(int streamId, this.bytes, bool endStream)
+      : super(streamId, endStream);
+
+  String toString() =>
+      'DataMessage(bytes: ${bytes.length}, endStream: $endStream)';
+}
+
+class PushPromiseMessage extends Message {
+  final List<Header> headers;
+  final int promisedStreamId;
+  final ClientTransportStream pushedStream;
+
+  PushPromiseMessage(int streamId, this.headers, this.promisedStreamId,
+      this.pushedStream, bool endStream)
+      : super(streamId, endStream);
+
+  String toString() => 'PushPromiseMessage(bytes: ${headers.length}, '
+      'promisedStreamId: $promisedStreamId, endStream: $endStream)';
+}
+
+class ResetStreamMessage extends Message {
+  final int errorCode;
+
+  ResetStreamMessage(int streamId, this.errorCode) : super(streamId, false);
+
+  String toString() => 'ResetStreamMessage(errorCode: $errorCode)';
+}
+
+class GoawayMessage extends Message {
+  final int lastStreamId;
+  final int errorCode;
+  final List<int> debugData;
+
+  GoawayMessage(this.lastStreamId, this.errorCode, this.debugData)
+      : super(0, false);
+
+  String toString() => 'GoawayMessage(lastStreamId: ${lastStreamId}, '
+      'errorCode: ${errorCode}, debugData: ${debugData.length})';
+}
diff --git a/http2/lib/src/flowcontrol/stream_queues.dart b/http2/lib/src/flowcontrol/stream_queues.dart
new file mode 100644
index 0000000..dc6d2f2
--- /dev/null
+++ b/http2/lib/src/flowcontrol/stream_queues.dart
@@ -0,0 +1,331 @@
+// Copyright (c) 2015, 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 http2.src.flowcontrol.stream_queues;
+
+import 'dart:async';
+import 'dart:collection';
+
+import '../../transport.dart';
+import '../async_utils/async_utils.dart';
+import '../byte_utils.dart';
+import '../error_handler.dart';
+
+import 'connection_queues.dart';
+import 'queue_messages.dart';
+import 'window_handler.dart';
+
+/// This class will buffer any headers/data messages in the order they were
+/// added.
+///
+/// It will ensure that we never send more data than the remote flow control
+/// window allows.
+class StreamMessageQueueOut extends Object
+    with TerminatableMixin, ClosableMixin {
+  /// The id of the stream this message queue belongs to.
+  final int streamId;
+
+  /// The stream-level flow control handler.
+  final OutgoingStreamWindowHandler streamWindow;
+
+  /// The underlying connection-level message queue.
+  final ConnectionMessageQueueOut connectionMessageQueue;
+
+  /// A indicator for whether this queue is currently buffering.
+  final BufferIndicator bufferIndicator = new BufferIndicator();
+
+  /// Buffered [Message]s which will be written to the underlying connection
+  /// if the flow control window allows so.
+  final Queue<Message> _messages = new Queue<Message>();
+
+  /// Debugging data on how much data should be written to the underlying
+  /// connection message queue.
+  int toBeWrittenBytes = 0;
+
+  /// Debugging data on how much data was written to the underlying connection
+  /// message queue.
+  int writtenBytes = 0;
+
+  StreamMessageQueueOut(
+      this.streamId, this.streamWindow, this.connectionMessageQueue) {
+    streamWindow.positiveWindow.bufferEmptyEvents.listen((_) {
+      if (!wasTerminated) {
+        _trySendData();
+      }
+    });
+    if (streamWindow.positiveWindow.wouldBuffer) {
+      bufferIndicator.markBuffered();
+    } else {
+      bufferIndicator.markUnBuffered();
+    }
+  }
+
+  /// Debugging data about how many messages are pending to be written to the
+  /// connection message queue.
+  int get pendingMessages => _messages.length;
+
+  /// Enqueues a new [Message] which is to be delivered to the connection
+  /// message queue.
+  void enqueueMessage(Message message) {
+    if (message is! ResetStreamMessage) ensureNotClosingSync(() {});
+    if (!wasTerminated) {
+      if (message.endStream) startClosing();
+
+      if (message is DataMessage) {
+        toBeWrittenBytes += message.bytes.length;
+      }
+
+      _messages.addLast(message);
+      _trySendData();
+
+      if (_messages.length > 0) {
+        bufferIndicator.markBuffered();
+      }
+    }
+  }
+
+  void onTerminated(error) {
+    _messages.clear();
+    closeWithError(error);
+  }
+
+  void onCheckForClose() {
+    if (isClosing && _messages.isEmpty) closeWithValue();
+  }
+
+  void _trySendData() {
+    int queueLenBefore = _messages.length;
+
+    while (_messages.length > 0) {
+      Message message = _messages.first;
+
+      if (message is HeadersMessage) {
+        _messages.removeFirst();
+        connectionMessageQueue.enqueueMessage(message);
+      } else if (message is DataMessage) {
+        int bytesAvailable = streamWindow.peerWindowSize;
+        if (bytesAvailable > 0 || message.bytes.length == 0) {
+          _messages.removeFirst();
+
+          // Do we need to fragment?
+          DataMessage messageToSend = message;
+          List<int> messageBytes = message.bytes;
+          // TODO: Do not fragment if the number of bytes we can send is too low
+          if (messageBytes.length > bytesAvailable) {
+            var partA = viewOrSublist(messageBytes, 0, bytesAvailable);
+            var partB = viewOrSublist(messageBytes, bytesAvailable,
+                messageBytes.length - bytesAvailable);
+            var messageA = new DataMessage(message.streamId, partA, false);
+            var messageB =
+                new DataMessage(message.streamId, partB, message.endStream);
+
+            // Put the second fragment back into the front of the queue.
+            _messages.addFirst(messageB);
+
+            // Send the first fragment.
+            messageToSend = messageA;
+          }
+
+          writtenBytes += messageToSend.bytes.length;
+          streamWindow.decreaseWindow(messageToSend.bytes.length);
+          connectionMessageQueue.enqueueMessage(messageToSend);
+        } else {
+          break;
+        }
+      } else if (message is ResetStreamMessage) {
+        _messages.removeFirst();
+        connectionMessageQueue.enqueueMessage(message);
+      } else {
+        throw new StateError('Unknown messages type: ${message.runtimeType}');
+      }
+    }
+    if (queueLenBefore > 0 && _messages.isEmpty) {
+      bufferIndicator.markUnBuffered();
+    }
+
+    onCheckForClose();
+  }
+}
+
+/// Keeps a list of [Message] which should be delivered to the
+/// [TransportStream].
+///
+/// It will keep messages up to the stream flow control window size if the
+/// [messages] listener is paused.
+class StreamMessageQueueIn extends Object
+    with TerminatableMixin, ClosableMixin, CancellableMixin {
+  /// The stream-level window our peer is using when sending us messages.
+  final IncomingWindowHandler windowHandler;
+
+  /// A indicator whether this [StreamMessageQueueIn] is currently buffering.
+  final BufferIndicator bufferIndicator = new BufferIndicator();
+
+  /// The pending [Message]s which are to be delivered via the [messages]
+  /// stream.
+  final Queue<Message> _pendingMessages = new Queue<Message>();
+
+  /// The [StreamController] used for producing the [messages] stream.
+  StreamController<StreamMessage> _incomingMessagesC;
+
+  /// The [StreamController] used for producing the [serverPushes] stream.
+  StreamController<TransportStreamPush> _serverPushStreamsC;
+
+  StreamMessageQueueIn(this.windowHandler) {
+    // We start by marking it as buffered, since no one is listening yet and
+    // incoming messages will get buffered.
+    bufferIndicator.markBuffered();
+
+    _incomingMessagesC = new StreamController(
+        onListen: () {
+          if (!wasClosed && !wasTerminated) {
+            _tryDispatch();
+            _tryUpdateBufferIndicator();
+          }
+        },
+        onPause: () {
+          _tryUpdateBufferIndicator();
+          // TODO: Would we ever want to decrease the window size in this
+          // situation?
+        },
+        onResume: () {
+          if (!wasClosed && !wasTerminated) {
+            _tryDispatch();
+            _tryUpdateBufferIndicator();
+          }
+        },
+        onCancel: cancel);
+
+    _serverPushStreamsC = new StreamController(onListen: () {
+      if (!wasClosed && !wasTerminated) {
+        _tryDispatch();
+        _tryUpdateBufferIndicator();
+      }
+    });
+  }
+
+  /// Debugging data: the number of pending messages in this queue.
+  int get pendingMessages => _pendingMessages.length;
+
+  /// The stream of [StreamMessage]s which come from the remote peer.
+  Stream<StreamMessage> get messages => _incomingMessagesC.stream;
+
+  /// The stream of [TransportStreamPush]es which come from the remote peer.
+  Stream<TransportStreamPush> get serverPushes => _serverPushStreamsC.stream;
+
+  /// A lower layer enqueues a new [Message] which should be delivered to the
+  /// app.
+  void enqueueMessage(Message message) {
+    ensureNotClosingSync(() {
+      if (!wasTerminated) {
+        if (message is PushPromiseMessage) {
+          // NOTE: If server pushes were enabled, the client is responsible for
+          // either rejecting or handling them.
+          assert(!_serverPushStreamsC.isClosed);
+          var transportStreamPush =
+              new TransportStreamPush(message.headers, message.pushedStream);
+          _serverPushStreamsC.add(transportStreamPush);
+          return;
+        }
+
+        if (message is DataMessage) {
+          windowHandler.gotData(message.bytes.length);
+        }
+        _pendingMessages.add(message);
+        if (message.endStream) startClosing();
+
+        _tryDispatch();
+        _tryUpdateBufferIndicator();
+      }
+    });
+  }
+
+  void onTerminated(exception) {
+    _pendingMessages.clear();
+    if (!wasClosed) {
+      if (exception != null) {
+        _incomingMessagesC.addError(exception);
+      }
+      _incomingMessagesC.close();
+      _serverPushStreamsC.close();
+      closeWithError(exception);
+    }
+  }
+
+  void onCloseCheck() {
+    if (isClosing && !wasClosed && _pendingMessages.isEmpty) {
+      _incomingMessagesC.close();
+      _serverPushStreamsC.close();
+      closeWithValue();
+    }
+  }
+
+  void forceDispatchIncomingMessages() {
+    while (_pendingMessages.isNotEmpty) {
+      final message = _pendingMessages.removeFirst();
+      assert(!_incomingMessagesC.isClosed);
+      if (message is HeadersMessage) {
+        _incomingMessagesC.add(new HeadersStreamMessage(message.headers,
+            endStream: message.endStream));
+      } else if (message is DataMessage) {
+        if (message.bytes.length > 0) {
+          _incomingMessagesC.add(new DataStreamMessage(message.bytes,
+              endStream: message.endStream));
+        }
+      } else {
+        // This can never happen.
+        assert(false);
+      }
+      if (message.endStream) {
+        onCloseCheck();
+      }
+    }
+  }
+
+  void _tryDispatch() {
+    while (!wasTerminated && _pendingMessages.isNotEmpty) {
+      bool handled = wasCancelled;
+
+      var message = _pendingMessages.first;
+      if (wasCancelled) {
+        _pendingMessages.removeFirst();
+      } else if (message is HeadersMessage || message is DataMessage) {
+        assert(!_incomingMessagesC.isClosed);
+        if (_incomingMessagesC.hasListener && !_incomingMessagesC.isPaused) {
+          _pendingMessages.removeFirst();
+          if (message is HeadersMessage) {
+            // NOTE: Header messages do not affect flow control - only
+            // data messages do.
+            _incomingMessagesC.add(new HeadersStreamMessage(message.headers,
+                endStream: message.endStream));
+          } else if (message is DataMessage) {
+            if (message.bytes.length > 0) {
+              _incomingMessagesC.add(new DataStreamMessage(message.bytes,
+                  endStream: message.endStream));
+              windowHandler.dataProcessed(message.bytes.length);
+            }
+          } else {
+            // This can never happen.
+            assert(false);
+          }
+          handled = true;
+        }
+      }
+      if (handled) {
+        if (message.endStream) {
+          onCloseCheck();
+        }
+      } else {
+        break;
+      }
+    }
+  }
+
+  void _tryUpdateBufferIndicator() {
+    if (_incomingMessagesC.isPaused || _pendingMessages.length > 0) {
+      bufferIndicator.markBuffered();
+    } else if (bufferIndicator.wouldBuffer && !_incomingMessagesC.isPaused) {
+      bufferIndicator.markUnBuffered();
+    }
+  }
+}
diff --git a/http2/lib/src/flowcontrol/window.dart b/http2/lib/src/flowcontrol/window.dart
new file mode 100644
index 0000000..10ba3f7
--- /dev/null
+++ b/http2/lib/src/flowcontrol/window.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2015, 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 http2.src.window;
+
+class Window {
+  static const int MAX_WINDOW_SIZE = (1 << 31) - 1;
+
+  /// The size available in this window.
+  ///
+  /// The default flow control window for the entire connection and for new
+  /// streams is 65535).
+  ///
+  /// NOTE: This value can potentially become negative.
+  int _size;
+
+  Window({int initialSize: (1 << 16) - 1}) : _size = initialSize;
+
+  /// The current size of the flow control window.
+  int get size => _size;
+
+  void modify(int difference) {
+    _size += difference;
+  }
+}
diff --git a/http2/lib/src/flowcontrol/window_handler.dart b/http2/lib/src/flowcontrol/window_handler.dart
new file mode 100644
index 0000000..909fa2b
--- /dev/null
+++ b/http2/lib/src/flowcontrol/window_handler.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2015, 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 http2.src.window_handler;
+
+import '../async_utils/async_utils.dart';
+import '../frames/frames.dart';
+import '../sync_errors.dart';
+
+import 'window.dart';
+
+abstract class AbstractOutgoingWindowHandler {
+  /// The connection flow control window.
+  final Window _peerWindow;
+
+  /// Indicates when the outgoing connection window turned positive and we can
+  /// send data frames again.
+  final BufferIndicator positiveWindow = new BufferIndicator();
+
+  AbstractOutgoingWindowHandler(this._peerWindow) {
+    if (_peerWindow.size > 0) {
+      positiveWindow.markUnBuffered();
+    }
+  }
+
+  /// The flow control window size we use for sending data. We are not allowed
+  /// to let this window be negative.
+  int get peerWindowSize => _peerWindow.size;
+
+  /// Process a window update frame received from the remote end.
+  void processWindowUpdate(WindowUpdateFrame frame) {
+    int increment = frame.windowSizeIncrement;
+    if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) {
+      throw new FlowControlException(
+          'Window update received from remote peer would make flow control '
+          'window too large.');
+    } else {
+      _peerWindow.modify(increment);
+    }
+
+    // If we transitioned from an negative/empty window to a positive window
+    // we'll fire an event that more data frames can be sent now.
+    if (positiveWindow.wouldBuffer && _peerWindow.size > 0) {
+      positiveWindow.markUnBuffered();
+    }
+  }
+
+  /// Update the peer window by subtracting [numberOfBytes].
+  ///
+  /// The remote peer will send us [WindowUpdateFrame]s which will increase
+  /// the window again at a later point in time.
+  void decreaseWindow(int numberOfBytes) {
+    _peerWindow.modify(-numberOfBytes);
+    if (_peerWindow.size <= 0) {
+      positiveWindow.markBuffered();
+    }
+  }
+}
+
+/// Handles the connection window for outgoing data frames.
+class OutgoingConnectionWindowHandler extends AbstractOutgoingWindowHandler {
+  OutgoingConnectionWindowHandler(Window window) : super(window);
+}
+
+/// Handles the window for outgoing messages to the peer.
+class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler {
+  OutgoingStreamWindowHandler(Window window) : super(window);
+
+  /// Update the peer window by adding [difference] to it.
+  ///
+  ///
+  /// The remote peer has send a new [SettingsFrame] which updated the default
+  /// stream level [Setting.SETTINGS_INITIAL_WINDOW_SIZE]. This causes all
+  /// existing streams to update the flow stream-level flow control window.
+  void processInitialWindowSizeSettingChange(int difference) {
+    if ((_peerWindow.size + difference) > Window.MAX_WINDOW_SIZE) {
+      throw new FlowControlException(
+          'Window update received from remote peer would make flow control '
+          'window too large.');
+    } else {
+      _peerWindow.modify(difference);
+      if (_peerWindow.size <= 0) {
+        positiveWindow.markBuffered();
+      } else if (positiveWindow.wouldBuffer) {
+        positiveWindow.markUnBuffered();
+      }
+    }
+  }
+}
+
+/// Mirrors the flow control window the remote end is using.
+class IncomingWindowHandler {
+  /// The [FrameWriter] used for writing [WindowUpdateFrame]s to the wire.
+  final FrameWriter _frameWriter;
+
+  /// The mirror of the [Window] the remote end sees.
+  ///
+  /// If [_localWindow ] turns negative, it means the remote peer sent us more
+  /// data than we allowed it to send.
+  final Window _localWindow;
+
+  /// The stream id this window handler is for (is `0` for connection level).
+  final int _streamId;
+
+  IncomingWindowHandler.stream(
+      this._frameWriter, this._localWindow, this._streamId);
+
+  IncomingWindowHandler.connection(this._frameWriter, this._localWindow)
+      : _streamId = 0;
+
+  /// The current size for the incoming data window.
+  ///
+  /// (This should never get negative, otherwise the peer send us more data
+  ///  than we told it to send.)
+  int get localWindowSize => _localWindow.size;
+
+  /// Signals that we received [numberOfBytes] from the remote peer.
+  void gotData(int numberOfBytes) {
+    _localWindow.modify(-numberOfBytes);
+
+    // If this turns negative, it means the remote end send us more data
+    // then we announced we can handle (i.e. the remote window size must be
+    // negative).
+    //
+    // NOTE: [_localWindow.size] tracks the amount of data we advertised that we
+    // can handle. The value can change in three situations:
+    //
+    //    a) We received data from the remote end (we can handle now less data)
+    //         => This is handled by [gotData].
+    //
+    //    b) We processed data from the remote end (we can handle now more data)
+    //         => This is handled by [dataProcessed].
+    //
+    //    c) We increase/decrease the initial stream window size after the
+    //       stream was created (newer streams will start with the changed
+    //       initial stream window size).
+    //         => This is not an issue, because we don't support changing the
+    //            initial window size later on -- only during the initial
+    //            settings exchange. Since streams (and therefore instances
+    //            of [IncomingWindowHandler]) are only created after sending out
+    //            our initial settings.
+    //
+    if (_localWindow.size < 0) {
+      throw new FlowControlException(
+          'Connection level flow control window became negative.');
+    }
+  }
+
+  /// Tell the peer we received [numberOfBytes] bytes. It will increase it's
+  /// sending window then.
+  ///
+  // TODO/FIXME: If we pause and don't want to get more data, we have to
+  //  - either stop sending window update frames
+  //  - or decreasing the window size
+  void dataProcessed(int numberOfBytes) {
+    _localWindow.modify(numberOfBytes);
+
+    // TODO: This can be optimized by delaying the window update to
+    // send one update with a bigger difference than multiple small update
+    // frames.
+    _frameWriter.writeWindowUpdate(numberOfBytes, streamId: _streamId);
+  }
+}
diff --git a/http2/lib/src/frames/frame_defragmenter.dart b/http2/lib/src/frames/frame_defragmenter.dart
new file mode 100644
index 0000000..44fc076
--- /dev/null
+++ b/http2/lib/src/frames/frame_defragmenter.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2015, 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 http2.src.frames.frame_defragmenter;
+
+import '../sync_errors.dart';
+
+import 'frames.dart';
+
+/// Class used for defragmenting [HeadersFrame]s and [PushPromiseFrame]s.
+// TODO: Somehow emit an error if too many continuation frames have been sent
+// (since we're buffering all of them).
+class FrameDefragmenter {
+  /// The current incomplete [HeadersFrame] fragment.
+  HeadersFrame _headersFrame;
+
+  /// The current incomplete [PushPromiseFrame] fragment.
+  PushPromiseFrame _pushPromiseFrame;
+
+  /// Tries to defragment [frame].
+  ///
+  /// If the given [frame] is a [HeadersFrame] or a [PushPromiseFrame] which
+  /// needs de-fragmentation, it will be saved and `null` will be returned.
+  ///
+  /// If there is currently an incomplete [HeadersFrame] or [PushPromiseFrame]
+  /// saved, [frame] needs to be a [ContinuationFrame]. It will be added to the
+  /// saved frame. In case the defragmentation is complete, the defragmented
+  /// [HeadersFrame] or [PushPromiseFrame] will be returned.
+  ///
+  /// All other [Frame] types will be returned.
+  // TODO: Consider handling continuation frames without preceding
+  // headers/push-promise frame here instead of the call site?
+  Frame tryDefragmentFrame(Frame frame) {
+    if (_headersFrame != null) {
+      if (frame is ContinuationFrame) {
+        if (_headersFrame.header.streamId != frame.header.streamId) {
+          throw new ProtocolException(
+              'Defragmentation: frames have different stream ids.');
+        }
+        _headersFrame = _headersFrame.addBlockContinuation(frame);
+
+        if (frame.hasEndHeadersFlag) {
+          var frame = _headersFrame;
+          _headersFrame = null;
+          return frame;
+        } else {
+          return null;
+        }
+      } else {
+        throw new ProtocolException(
+            'Defragmentation: Incomplete frame must be followed by '
+            'continuation frame.');
+      }
+    } else if (_pushPromiseFrame != null) {
+      if (frame is ContinuationFrame) {
+        if (_pushPromiseFrame.header.streamId != frame.header.streamId) {
+          throw new ProtocolException(
+              'Defragmentation: frames have different stream ids.');
+        }
+        _pushPromiseFrame = _pushPromiseFrame.addBlockContinuation(frame);
+
+        if (frame.hasEndHeadersFlag) {
+          var frame = _pushPromiseFrame;
+          _pushPromiseFrame = null;
+          return frame;
+        } else {
+          return null;
+        }
+      } else {
+        throw new ProtocolException(
+            'Defragmentation: Incomplete frame must be followed by '
+            'continuation frame.');
+      }
+    } else {
+      if (frame is HeadersFrame) {
+        if (!frame.hasEndHeadersFlag) {
+          _headersFrame = frame;
+          return null;
+        }
+      } else if (frame is PushPromiseFrame) {
+        if (!frame.hasEndHeadersFlag) {
+          _pushPromiseFrame = frame;
+          return null;
+        }
+      }
+    }
+
+    // If this frame is not relevant for header defragmentation, we pass it to
+    // the next stage.
+    return frame;
+  }
+}
diff --git a/http2/lib/src/frames/frame_reader.dart b/http2/lib/src/frames/frame_reader.dart
new file mode 100644
index 0000000..2812dc6
--- /dev/null
+++ b/http2/lib/src/frames/frame_reader.dart
@@ -0,0 +1,284 @@
+// Copyright (c) 2015, 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.
+
+part of http2.src.frames;
+
+/// Used for converting a `Stream<List<int>>` to a `Stream<Frame>`.
+class FrameReader {
+  final Stream<List<int>> _inputStream;
+
+  /// Connection settings which this reader needs to ensure the remote end is
+  /// complying with.
+  ActiveSettings _localSettings;
+
+  StreamSubscription<List<int>> _subscription;
+  StreamController<Frame> _framesController;
+
+  FrameReader(this._inputStream, this._localSettings);
+
+  /// Starts to listen on the input stream and decodes HTTP/2 transport frames.
+  Stream<Frame> startDecoding() {
+    List<List<int>> bufferedData = new List<List<int>>();
+    int bufferedLength = 0;
+
+    FrameHeader tryReadHeader() {
+      if (bufferedLength >= FRAME_HEADER_SIZE) {
+        // Get at least FRAME_HEADER_SIZE bytes in the first byte array.
+        _mergeLists(bufferedData, FRAME_HEADER_SIZE);
+
+        // Read the frame header from the first byte array.
+        return _readFrameHeader(bufferedData[0], 0);
+      }
+      return null;
+    }
+
+    Frame tryReadFrame(FrameHeader header) {
+      int totalFrameLen = FRAME_HEADER_SIZE + header.length;
+      if (bufferedLength >= totalFrameLen) {
+        // Get the whole frame in the first byte array.
+        _mergeLists(bufferedData, totalFrameLen);
+
+        // Read the frame.
+        Frame frame = _readFrame(header, bufferedData[0], FRAME_HEADER_SIZE);
+
+        // Update bufferedData/bufferedLength
+        int firstChunkLen = bufferedData[0].length;
+        if (firstChunkLen == totalFrameLen) {
+          bufferedData.removeAt(0);
+        } else {
+          bufferedData[0] = viewOrSublist(
+              bufferedData[0], totalFrameLen, firstChunkLen - totalFrameLen);
+        }
+        bufferedLength -= totalFrameLen;
+
+        return frame;
+      }
+      return null;
+    }
+
+    _framesController = new StreamController(
+        onListen: () {
+          FrameHeader header;
+
+          void terminateWithError(error, [StackTrace stack]) {
+            header = null;
+            _framesController.addError(error, stack);
+            _subscription.cancel();
+            _framesController.close();
+          }
+
+          _subscription = _inputStream.listen((List<int> data) {
+            bufferedData.add(data);
+            bufferedLength += data.length;
+
+            try {
+              while (true) {
+                if (header == null) {
+                  header = tryReadHeader();
+                }
+                if (header != null) {
+                  if (header.length > _localSettings.maxFrameSize) {
+                    terminateWithError(
+                        new FrameSizeException('Incoming frame is too big.'));
+                    return;
+                  }
+
+                  Frame frame = tryReadFrame(header);
+
+                  if (frame != null) {
+                    _framesController.add(frame);
+                    header = null;
+                  } else {
+                    break;
+                  }
+                } else {
+                  break;
+                }
+              }
+            } catch (error, stack) {
+              terminateWithError(error, stack);
+            }
+          }, onError: (error, StackTrace stack) {
+            terminateWithError(error, stack);
+          }, onDone: () {
+            if (bufferedLength == 0) {
+              _framesController.close();
+            } else {
+              terminateWithError(new FrameSizeException(
+                  'Incoming byte stream ended with incomplete frame'));
+            }
+          });
+        },
+        onPause: () => _subscription.pause(),
+        onResume: () => _subscription.resume());
+
+    return _framesController.stream;
+  }
+
+  /// Combine combines/merges `List<int>`s of `bufferedData` until
+  /// `numberOfBytes` have been accumulated.
+  ///
+  /// After calling `mergeLists`, `bufferedData[0]` will contain at least
+  /// `numberOfBytes` bytes.
+  void _mergeLists(List<List<int>> bufferedData, int numberOfBytes) {
+    if (bufferedData[0].length < numberOfBytes) {
+      int numLists = 0;
+      int accumulatedLength = 0;
+      while (accumulatedLength < numberOfBytes &&
+          numLists <= bufferedData.length) {
+        accumulatedLength += bufferedData[numLists++].length;
+      }
+      assert(accumulatedLength >= numberOfBytes);
+      var newList = new Uint8List(accumulatedLength);
+      int offset = 0;
+      for (int i = 0; i < numLists; i++) {
+        List<int> data = bufferedData[i];
+        newList.setRange(offset, offset + data.length, data);
+        offset += data.length;
+      }
+      bufferedData[0] = newList;
+      bufferedData.removeRange(1, numLists);
+    }
+  }
+
+  /// Reads a FrameHeader] from [bytes], starting at [offset].
+  FrameHeader _readFrameHeader(List<int> bytes, int offset) {
+    int length = readInt24(bytes, offset);
+    int type = bytes[offset + 3];
+    int flags = bytes[offset + 4];
+    int streamId = readInt32(bytes, offset + 5) & 0x7fffffff;
+
+    return new FrameHeader(length, type, flags, streamId);
+  }
+
+  /// Reads a [Frame] from [bytes], starting at [frameOffset].
+  Frame _readFrame(FrameHeader header, List<int> bytes, int frameOffset) {
+    int frameEnd = frameOffset + header.length;
+
+    int offset = frameOffset;
+    switch (header.type) {
+      case FrameType.DATA:
+        int padLength = 0;
+        if (_isFlagSet(header.flags, DataFrame.FLAG_PADDED)) {
+          _checkFrameLengthCondition((frameEnd - offset) >= 1);
+          padLength = bytes[offset++];
+        }
+        int dataLen = frameEnd - offset - padLength;
+        _checkFrameLengthCondition(dataLen >= 0);
+        var dataBytes = viewOrSublist(bytes, offset, dataLen);
+        return new DataFrame(header, padLength, dataBytes);
+
+      case FrameType.HEADERS:
+        int padLength = 0;
+        if (_isFlagSet(header.flags, HeadersFrame.FLAG_PADDED)) {
+          _checkFrameLengthCondition((frameEnd - offset) >= 1);
+          padLength = bytes[offset++];
+        }
+        int streamDependency;
+        bool exclusiveDependency = false;
+        int weight;
+        if (_isFlagSet(header.flags, HeadersFrame.FLAG_PRIORITY)) {
+          _checkFrameLengthCondition((frameEnd - offset) >= 5);
+          exclusiveDependency = (bytes[offset] & 0x80) == 0x80;
+          streamDependency = readInt32(bytes, offset) & 0x7fffffff;
+          offset += 4;
+          weight = bytes[offset++];
+        }
+        int headerBlockLen = frameEnd - offset - padLength;
+        _checkFrameLengthCondition(headerBlockLen >= 0);
+        var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen);
+        return new HeadersFrame(header, padLength, exclusiveDependency,
+            streamDependency, weight, headerBlockFragment);
+
+      case FrameType.PRIORITY:
+        _checkFrameLengthCondition(
+            (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH,
+            message: 'Priority frame length must be exactly 5 bytes.');
+        bool exclusiveDependency = (bytes[offset] & 0x80) == 0x80;
+        int streamDependency = readInt32(bytes, offset) & 0x7fffffff;
+        int weight = bytes[offset + 4];
+        return new PriorityFrame(
+            header, exclusiveDependency, streamDependency, weight);
+
+      case FrameType.RST_STREAM:
+        _checkFrameLengthCondition(
+            (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH,
+            message: 'Rst frames must have a length of 4.');
+        int errorCode = readInt32(bytes, offset);
+        return new RstStreamFrame(header, errorCode);
+
+      case FrameType.SETTINGS:
+        _checkFrameLengthCondition((header.length % 6) == 0,
+            message: 'Settings frame length must be a multiple of 6 bytes.');
+
+        int count = header.length ~/ 6;
+        var settings = new List<Setting>(count);
+        for (int i = 0; i < count; i++) {
+          int identifier = readInt16(bytes, offset + 6 * i);
+          int value = readInt32(bytes, offset + 6 * i + 2);
+          settings[i] = new Setting(identifier, value);
+        }
+        var frame = new SettingsFrame(header, settings);
+        if (frame.hasAckFlag) {
+          _checkFrameLengthCondition(header.length == 0,
+              message: 'Settings frame length must 0 for ACKs.');
+        }
+        return frame;
+
+      case FrameType.PUSH_PROMISE:
+        int padLength = 0;
+        if (_isFlagSet(header.flags, PushPromiseFrame.FLAG_PADDED)) {
+          _checkFrameLengthCondition((frameEnd - offset) >= 1);
+          padLength = bytes[offset++];
+        }
+        int promisedStreamId = readInt32(bytes, offset) & 0x7fffffff;
+        offset += 4;
+        int headerBlockLen = frameEnd - offset - padLength;
+        _checkFrameLengthCondition(headerBlockLen >= 0);
+        var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen);
+        return new PushPromiseFrame(
+            header, padLength, promisedStreamId, headerBlockFragment);
+
+      case FrameType.PING:
+        _checkFrameLengthCondition(
+            (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH,
+            message: 'Ping frames must have a length of 8.');
+        var opaqueData = readInt64(bytes, offset);
+        return new PingFrame(header, opaqueData);
+
+      case FrameType.GOAWAY:
+        _checkFrameLengthCondition((frameEnd - offset) >= 8);
+        int lastStreamId = readInt32(bytes, offset);
+        int errorCode = readInt32(bytes, offset + 4);
+        var debugData = viewOrSublist(bytes, offset + 8, header.length - 8);
+        return new GoawayFrame(header, lastStreamId, errorCode, debugData);
+
+      case FrameType.WINDOW_UPDATE:
+        _checkFrameLengthCondition(
+            (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH,
+            message: 'Window update frames must have a length of 4.');
+        int windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff;
+        return new WindowUpdateFrame(header, windowSizeIncrement);
+
+      case FrameType.CONTINUATION:
+        var headerBlockFragment =
+            viewOrSublist(bytes, offset, frameEnd - offset);
+        return new ContinuationFrame(header, headerBlockFragment);
+
+      default:
+        // Unknown frames should be ignored according to spec.
+        return new UnknownFrame(
+            header, viewOrSublist(bytes, offset, frameEnd - offset));
+    }
+  }
+
+  /// Checks that [condition] is `true` and raises an [FrameSizeException]
+  /// otherwise.
+  void _checkFrameLengthCondition(bool condition,
+      {String message: 'Frame not long enough.'}) {
+    if (!condition) {
+      throw new FrameSizeException(message);
+    }
+  }
+}
diff --git a/http2/lib/src/frames/frame_types.dart b/http2/lib/src/frames/frame_types.dart
new file mode 100644
index 0000000..205f2a8
--- /dev/null
+++ b/http2/lib/src/frames/frame_types.dart
@@ -0,0 +1,330 @@
+// Copyright (c) 2015, 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.
+
+part of http2.src.frames;
+
+const int FRAME_HEADER_SIZE = 9;
+
+class FrameType {
+  static const int DATA = 0;
+  static const int HEADERS = 1;
+  static const int PRIORITY = 2;
+  static const int RST_STREAM = 3;
+  static const int SETTINGS = 4;
+  static const int PUSH_PROMISE = 5;
+  static const int PING = 6;
+  static const int GOAWAY = 7;
+  static const int WINDOW_UPDATE = 8;
+  static const int CONTINUATION = 9;
+}
+
+class ErrorCode {
+  static const int NO_ERROR = 0;
+  static const int PROTOCOL_ERROR = 1;
+  static const int INTERNAL_ERROR = 2;
+  static const int FLOW_CONTROL_ERROR = 3;
+  static const int SETTINGS_TIMEOUT = 4;
+  static const int STREAM_CLOSED = 5;
+  static const int FRAME_SIZE_ERROR = 6;
+  static const int REFUSED_STREAM = 7;
+  static const int CANCEL = 8;
+  static const int COMPRESSION_ERROR = 9;
+  static const int CONNECT_ERROR = 10;
+  static const int ENHANCE_YOUR_CALM = 11;
+  static const int INADEQUATE_SECURITY = 12;
+  static const int HTTP_1_1_REQUIRED = 13;
+}
+
+class FrameHeader {
+  final int length;
+  final int type;
+  final int flags;
+  final int streamId;
+
+  FrameHeader(this.length, this.type, this.flags, this.streamId);
+
+  Map toJson() =>
+      {'length': length, 'type': type, 'flags': flags, 'streamId': streamId};
+}
+
+class Frame {
+  static const int MAX_LEN = (1 << 24) - 1;
+
+  final FrameHeader header;
+
+  Frame(this.header);
+
+  Map toJson() => {'header': header.toJson()};
+}
+
+class DataFrame extends Frame {
+  static const int FLAG_END_STREAM = 0x1;
+  static const int FLAG_PADDED = 0x8;
+
+  /// The number of padding bytes.
+  final int padLength;
+
+  final List<int> bytes;
+
+  DataFrame(FrameHeader header, this.padLength, this.bytes) : super(header);
+
+  bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM);
+  bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'padLength': padLength,
+      'bytes (length)': bytes.length,
+      'bytes (up to 4 bytes)': bytes.length > 4 ? bytes.sublist(0, 4) : bytes,
+    });
+}
+
+class HeadersFrame extends Frame {
+  static const int FLAG_END_STREAM = 0x1;
+  static const int FLAG_END_HEADERS = 0x4;
+  static const int FLAG_PADDED = 0x8;
+  static const int FLAG_PRIORITY = 0x20;
+
+  // NOTE: This is the size a [HeadersFrame] can have in addition to padding
+  // and header block fragment data.
+  static const int MAX_CONSTANT_PAYLOAD = 6;
+
+  /// The number of padding bytes (might be null).
+  final int padLength;
+
+  final bool exclusiveDependency;
+  final int streamDependency;
+  final int weight;
+  final List<int> headerBlockFragment;
+
+  HeadersFrame(FrameHeader header, this.padLength, this.exclusiveDependency,
+      this.streamDependency, this.weight, this.headerBlockFragment)
+      : super(header);
+
+  /// This will be set from the outside after decoding.
+  List<Header> decodedHeaders;
+
+  bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM);
+  bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS);
+  bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED);
+  bool get hasPriorityFlag => _isFlagSet(header.flags, FLAG_PRIORITY);
+
+  HeadersFrame addBlockContinuation(ContinuationFrame frame) {
+    var fragment = frame.headerBlockFragment;
+    var flags = header.flags | frame.header.flags;
+    var fh = new FrameHeader(
+        header.length + fragment.length, header.type, flags, header.streamId);
+
+    var mergedHeaderBlockFragment =
+        new Uint8List(headerBlockFragment.length + fragment.length);
+
+    mergedHeaderBlockFragment.setRange(
+        0, headerBlockFragment.length, headerBlockFragment);
+
+    mergedHeaderBlockFragment.setRange(
+        headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment);
+
+    return new HeadersFrame(fh, padLength, exclusiveDependency,
+        streamDependency, weight, mergedHeaderBlockFragment);
+  }
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'padLength': padLength,
+      'exclusiveDependency': exclusiveDependency,
+      'streamDependency': streamDependency,
+      'weight': weight,
+      'headerBlockFragment (length)': headerBlockFragment.length
+    });
+}
+
+class PriorityFrame extends Frame {
+  static const int FIXED_FRAME_LENGTH = 5;
+
+  final bool exclusiveDependency;
+  final int streamDependency;
+  final int weight;
+
+  PriorityFrame(FrameHeader header, this.exclusiveDependency,
+      this.streamDependency, this.weight)
+      : super(header);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'exclusiveDependency': exclusiveDependency,
+      'streamDependency': streamDependency,
+      'weight': weight,
+    });
+}
+
+class RstStreamFrame extends Frame {
+  static const int FIXED_FRAME_LENGTH = 4;
+
+  final int errorCode;
+
+  RstStreamFrame(FrameHeader header, this.errorCode) : super(header);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'errorCode': errorCode,
+    });
+}
+
+class Setting {
+  static const int SETTINGS_HEADER_TABLE_SIZE = 1;
+  static const int SETTINGS_ENABLE_PUSH = 2;
+  static const int SETTINGS_MAX_CONCURRENT_STREAMS = 3;
+  static const int SETTINGS_INITIAL_WINDOW_SIZE = 4;
+  static const int SETTINGS_MAX_FRAME_SIZE = 5;
+  static const int SETTINGS_MAX_HEADER_LIST_SIZE = 6;
+
+  final int identifier;
+  final int value;
+
+  Setting(this.identifier, this.value);
+
+  Map toJson() => {'identifier': identifier, 'value': value};
+}
+
+class SettingsFrame extends Frame {
+  static const int FLAG_ACK = 0x1;
+
+  // A setting consist of a 2 byte identifier and a 4 byte value.
+  static const int SETTING_SIZE = 6;
+
+  final List<Setting> settings;
+
+  SettingsFrame(FrameHeader header, this.settings) : super(header);
+
+  bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'settings': settings.map((s) => s.toJson()).toList(),
+    });
+}
+
+class PushPromiseFrame extends Frame {
+  static const int FLAG_END_HEADERS = 0x4;
+  static const int FLAG_PADDED = 0x8;
+
+  // NOTE: This is the size a [PushPromiseFrame] can have in addition to padding
+  // and header block fragment data.
+  static const int MAX_CONSTANT_PAYLOAD = 5;
+
+  final int padLength;
+  final int promisedStreamId;
+  final List<int> headerBlockFragment;
+
+  /// This will be set from the outside after decoding.
+  List<Header> decodedHeaders;
+
+  PushPromiseFrame(FrameHeader header, this.padLength, this.promisedStreamId,
+      this.headerBlockFragment)
+      : super(header);
+
+  bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS);
+  bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED);
+
+  PushPromiseFrame addBlockContinuation(ContinuationFrame frame) {
+    var fragment = frame.headerBlockFragment;
+    var flags = header.flags | frame.header.flags;
+    var fh = new FrameHeader(
+        header.length + fragment.length, header.type, flags, header.streamId);
+
+    var mergedHeaderBlockFragment =
+        new Uint8List(headerBlockFragment.length + fragment.length);
+
+    mergedHeaderBlockFragment.setRange(
+        0, headerBlockFragment.length, headerBlockFragment);
+
+    mergedHeaderBlockFragment.setRange(
+        headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment);
+
+    return new PushPromiseFrame(
+        fh, padLength, promisedStreamId, mergedHeaderBlockFragment);
+  }
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'padLength': padLength,
+      'promisedStreamId': promisedStreamId,
+      'headerBlockFragment (len)': headerBlockFragment.length,
+    });
+}
+
+class PingFrame extends Frame {
+  static const int FIXED_FRAME_LENGTH = 8;
+
+  static const int FLAG_ACK = 0x1;
+
+  final int opaqueData;
+
+  PingFrame(FrameHeader header, this.opaqueData) : super(header);
+
+  bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'opaqueData': opaqueData,
+    });
+}
+
+class GoawayFrame extends Frame {
+  final int lastStreamId;
+  final int errorCode;
+  final List<int> debugData;
+
+  GoawayFrame(
+      FrameHeader header, this.lastStreamId, this.errorCode, this.debugData)
+      : super(header);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'lastStreamId': lastStreamId,
+      'errorCode': errorCode,
+      'debugData (length)': debugData.length,
+    });
+}
+
+class WindowUpdateFrame extends Frame {
+  static const int FIXED_FRAME_LENGTH = 4;
+
+  final int windowSizeIncrement;
+
+  WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement)
+      : super(header);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'windowSizeIncrement': windowSizeIncrement,
+    });
+}
+
+class ContinuationFrame extends Frame {
+  static const int FLAG_END_HEADERS = 0x4;
+
+  final List<int> headerBlockFragment;
+
+  ContinuationFrame(FrameHeader header, this.headerBlockFragment)
+      : super(header);
+
+  bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'headerBlockFragment (length)': headerBlockFragment.length,
+    });
+}
+
+class UnknownFrame extends Frame {
+  final List<int> data;
+
+  UnknownFrame(FrameHeader header, this.data) : super(header);
+
+  Map toJson() => super.toJson()
+    ..addAll({
+      'data (length)': data.length,
+    });
+}
diff --git a/http2/lib/src/frames/frame_utils.dart b/http2/lib/src/frames/frame_utils.dart
new file mode 100644
index 0000000..73a3173
--- /dev/null
+++ b/http2/lib/src/frames/frame_utils.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2015, 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.
+
+part of http2.src.frames;
+
+bool _isFlagSet(int value, int flag) => value & flag == flag;
diff --git a/http2/lib/src/frames/frame_writer.dart b/http2/lib/src/frames/frame_writer.dart
new file mode 100644
index 0000000..6c79581
--- /dev/null
+++ b/http2/lib/src/frames/frame_writer.dart
@@ -0,0 +1,293 @@
+// Copyright (c) 2015, 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.
+
+part of http2.src.frames;
+
+// TODO: No support for writing padded information.
+// TODO: No support for stream priorities.
+class FrameWriter {
+  /// The HPack compression context.
+  final HPackEncoder _hpackEncoder;
+
+  /// A buffered writer for outgoing bytes.
+  final BufferedBytesWriter _outWriter;
+
+  /// Connection settings which this writer needs to respect.
+  final ActiveSettings _peerSettings;
+
+  /// This is the maximum over all stream id's we've written to the underlying
+  /// sink.
+  int _highestWrittenStreamId = 0;
+
+  FrameWriter(
+      this._hpackEncoder, StreamSink<List<int>> outgoing, this._peerSettings)
+      : _outWriter = new BufferedBytesWriter(outgoing);
+
+  /// A indicator whether writes would be buffered.
+  BufferIndicator get bufferIndicator => _outWriter.bufferIndicator;
+
+  /// This is the maximum over all stream id's we've written to the underlying
+  /// sink.
+  int get highestWrittenStreamId => _highestWrittenStreamId;
+
+  void writeDataFrame(int streamId, List<int> data, {bool endStream: false}) {
+    while (data.length > _peerSettings.maxFrameSize) {
+      var chunk = viewOrSublist(data, 0, _peerSettings.maxFrameSize);
+      data = viewOrSublist(data, _peerSettings.maxFrameSize,
+          data.length - _peerSettings.maxFrameSize);
+      _writeDataFrameNoFragment(streamId, chunk, false);
+    }
+    _writeDataFrameNoFragment(streamId, data, endStream);
+  }
+
+  void _writeDataFrameNoFragment(int streamId, List<int> data, bool endStream) {
+    int type = FrameType.DATA;
+    int flags = endStream ? DataFrame.FLAG_END_STREAM : 0;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + data.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, data.length);
+    offset += FRAME_HEADER_SIZE;
+
+    buffer.setRange(offset, offset + data.length, data);
+
+    _writeData(buffer);
+  }
+
+  void writeHeadersFrame(int streamId, List<Header> headers,
+      {bool endStream: true}) {
+    var fragment = _hpackEncoder.encode(headers);
+    var maxSize =
+        _peerSettings.maxFrameSize - HeadersFrame.MAX_CONSTANT_PAYLOAD;
+
+    if (fragment.length < maxSize) {
+      _writeHeadersFrameNoFragment(streamId, fragment, true, endStream);
+    } else {
+      var chunk = fragment.sublist(0, maxSize);
+      fragment = fragment.sublist(maxSize);
+      _writeHeadersFrameNoFragment(streamId, chunk, false, endStream);
+      while (fragment.length > _peerSettings.maxFrameSize) {
+        var chunk = fragment.sublist(0, _peerSettings.maxFrameSize);
+        fragment = fragment.sublist(_peerSettings.maxFrameSize);
+        _writeContinuationFrame(streamId, chunk, false);
+      }
+      _writeContinuationFrame(streamId, fragment, true);
+    }
+  }
+
+  void _writeHeadersFrameNoFragment(
+      int streamId, List<int> fragment, bool endHeaders, bool endStream) {
+    int type = FrameType.HEADERS;
+    int flags = 0;
+    if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS;
+    if (endStream) flags |= HeadersFrame.FLAG_END_STREAM;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length);
+    offset += FRAME_HEADER_SIZE;
+
+    buffer.setRange(offset, buffer.length, fragment);
+
+    _writeData(buffer);
+  }
+
+  void _writeContinuationFrame(
+      int streamId, List<int> fragment, bool endHeaders) {
+    int type = FrameType.CONTINUATION;
+    int flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length);
+    offset += FRAME_HEADER_SIZE;
+
+    buffer.setRange(offset, buffer.length, fragment);
+
+    _writeData(buffer);
+  }
+
+  void writePriorityFrame(int streamId, int streamDependency, int weight,
+      {bool exclusive: false}) {
+    int type = FrameType.PRIORITY;
+    int flags = 0;
+
+    var buffer =
+        new Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, 5);
+    offset += FRAME_HEADER_SIZE;
+
+    if (exclusive) {
+      setInt32(buffer, offset, (1 << 31) | streamDependency);
+    } else {
+      setInt32(buffer, offset, streamDependency);
+    }
+    buffer[offset + 4] = weight;
+
+    _writeData(buffer);
+  }
+
+  void writeRstStreamFrame(int streamId, int errorCode) {
+    int type = FrameType.RST_STREAM;
+    int flags = 0;
+
+    var buffer =
+        new Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, 4);
+    offset += FRAME_HEADER_SIZE;
+
+    setInt32(buffer, offset, errorCode);
+
+    _writeData(buffer);
+  }
+
+  void writeSettingsFrame(List<Setting> settings) {
+    int type = FrameType.SETTINGS;
+    int flags = 0;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + 6 * settings.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, 0, 6 * settings.length);
+    offset += FRAME_HEADER_SIZE;
+
+    for (int i = 0; i < settings.length; i++) {
+      var setting = settings[i];
+      setInt16(buffer, offset + 6 * i, setting.identifier);
+      setInt32(buffer, offset + 6 * i + 2, setting.value);
+    }
+
+    _writeData(buffer);
+  }
+
+  void writeSettingsAckFrame() {
+    int type = FrameType.SETTINGS;
+    int flags = SettingsFrame.FLAG_ACK;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, 0, 0);
+    offset += FRAME_HEADER_SIZE;
+
+    _writeData(buffer);
+  }
+
+  void writePushPromiseFrame(
+      int streamId, int promisedStreamId, List<Header> headers) {
+    var fragment = _hpackEncoder.encode(headers);
+    var maxSize =
+        _peerSettings.maxFrameSize - PushPromiseFrame.MAX_CONSTANT_PAYLOAD;
+
+    if (fragment.length < maxSize) {
+      _writePushPromiseFrameNoFragmentation(
+          streamId, promisedStreamId, fragment, true);
+    } else {
+      var chunk = fragment.sublist(0, maxSize);
+      fragment = fragment.sublist(maxSize);
+      _writePushPromiseFrameNoFragmentation(
+          streamId, promisedStreamId, chunk, false);
+      while (fragment.length > _peerSettings.maxFrameSize) {
+        var chunk = fragment.sublist(0, _peerSettings.maxFrameSize);
+        fragment = fragment.sublist(_peerSettings.maxFrameSize);
+        _writeContinuationFrame(streamId, chunk, false);
+      }
+      _writeContinuationFrame(streamId, chunk, true);
+    }
+  }
+
+  void _writePushPromiseFrameNoFragmentation(
+      int streamId, int promisedStreamId, List<int> fragment, bool endHeaders) {
+    int type = FrameType.PUSH_PROMISE;
+    int flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + 4 + fragment.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, 4 + fragment.length);
+    offset += FRAME_HEADER_SIZE;
+
+    setInt32(buffer, offset, promisedStreamId);
+    buffer.setRange(offset + 4, offset + 4 + fragment.length, fragment);
+
+    _writeData(buffer);
+  }
+
+  void writePingFrame(int opaqueData, {bool ack: false}) {
+    int type = FrameType.PING;
+    int flags = ack ? PingFrame.FLAG_ACK : 0;
+
+    var buffer =
+        new Uint8List(FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH);
+    int offset = 0;
+
+    _setFrameHeader(buffer, 0, type, flags, 0, 8);
+    offset += FRAME_HEADER_SIZE;
+
+    setInt64(buffer, offset, opaqueData);
+    _writeData(buffer);
+  }
+
+  void writeGoawayFrame(int lastStreamId, int errorCode, List<int> debugData) {
+    int type = FrameType.GOAWAY;
+    int flags = 0;
+
+    var buffer = new Uint8List(FRAME_HEADER_SIZE + 8 + debugData.length);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, 0, 8 + debugData.length);
+    offset += FRAME_HEADER_SIZE;
+
+    setInt32(buffer, offset, lastStreamId);
+    setInt32(buffer, offset + 4, errorCode);
+    buffer.setRange(offset + 8, buffer.length, debugData);
+
+    _writeData(buffer);
+  }
+
+  void writeWindowUpdate(int sizeIncrement, {int streamId: 0}) {
+    int type = FrameType.WINDOW_UPDATE;
+    int flags = 0;
+
+    var buffer =
+        new Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH);
+    int offset = 0;
+
+    _setFrameHeader(buffer, offset, type, flags, streamId, 4);
+    offset += FRAME_HEADER_SIZE;
+
+    setInt32(buffer, offset, sizeIncrement);
+
+    _writeData(buffer);
+  }
+
+  void _writeData(List<int> bytes) {
+    _outWriter.add(bytes);
+  }
+
+  /// Closes the underlying sink and returns [doneFuture].
+  Future close() {
+    return _outWriter.close().whenComplete(() => doneFuture);
+  }
+
+  /// The future which will complete once this writer is done.
+  Future get doneFuture => _outWriter.doneFuture;
+
+  void _setFrameHeader(List<int> bytes, int offset, int type, int flags,
+      int streamId, int length) {
+    setInt24(bytes, offset, length);
+    bytes[3] = type;
+    bytes[4] = flags;
+    setInt32(bytes, 5, streamId);
+
+    _highestWrittenStreamId = max(_highestWrittenStreamId, streamId);
+  }
+}
diff --git a/http2/lib/src/frames/frames.dart b/http2/lib/src/frames/frames.dart
new file mode 100644
index 0000000..8374eee
--- /dev/null
+++ b/http2/lib/src/frames/frames.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2015, 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 http2.src.frames;
+
+import 'dart:async';
+import 'dart:math' show max;
+import 'dart:typed_data';
+
+import '../async_utils/async_utils.dart';
+import '../byte_utils.dart';
+import '../hpack/hpack.dart';
+import '../settings/settings.dart';
+import '../sync_errors.dart';
+
+part "frame_types.dart";
+part "frame_utils.dart";
+part "frame_reader.dart";
+part "frame_writer.dart";
diff --git a/http2/lib/src/hpack/hpack.dart b/http2/lib/src/hpack/hpack.dart
new file mode 100644
index 0000000..4821511
--- /dev/null
+++ b/http2/lib/src/hpack/hpack.dart
@@ -0,0 +1,349 @@
+// Copyright (c) 2015, 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.
+
+/// Implements a [HPackContext] for encoding/decoding headers according to the
+/// HPACK specificaiton. See here for more information:
+///   https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10
+library http2.hpack;
+
+import 'dart:convert' show ascii;
+import 'dart:io';
+
+import '../byte_utils.dart';
+
+import 'huffman.dart';
+import 'huffman_table.dart';
+
+/// Exception raised due to encoding/decoding errors.
+class HPackDecodingException implements Exception {
+  final String _message;
+
+  HPackDecodingException(this._message);
+
+  String toString() => 'HPackDecodingException: $_message';
+}
+
+/// A HPACK encoding/decoding context.
+///
+/// This is a statefull class, so encoding/decoding changes internal state.
+class HPackContext {
+  final HPackEncoder encoder = new HPackEncoder();
+  final HPackDecoder decoder = new HPackDecoder();
+
+  HPackContext(
+      {int maxSendingHeaderTableSize: 4096,
+      int maxReceivingHeaderTableSize: 4096}) {
+    encoder.updateMaxSendingHeaderTableSize(maxSendingHeaderTableSize);
+    decoder.updateMaxReceivingHeaderTableSize(maxReceivingHeaderTableSize);
+  }
+}
+
+/// A HTTP/2 header.
+class Header {
+  final List<int> name;
+  final List<int> value;
+  final bool neverIndexed;
+
+  Header(this.name, this.value, {this.neverIndexed: false});
+
+  factory Header.ascii(String name, String value) {
+    return new Header(ascii.encode(name), ascii.encode(value));
+  }
+}
+
+/// A stateful HPACK decoder.
+class HPackDecoder {
+  int _maxHeaderTableSize;
+
+  final IndexTable _table = new IndexTable();
+
+  void updateMaxReceivingHeaderTableSize(int newMaximumSize) {
+    _maxHeaderTableSize = newMaximumSize;
+  }
+
+  List<Header> decode(List<int> data) {
+    int offset = 0;
+
+    int readInteger(int prefixBits) {
+      assert(prefixBits <= 8 && prefixBits > 0);
+
+      var byte = data[offset++] & ((1 << prefixBits) - 1);
+
+      int integer;
+      if (byte == ((1 << prefixBits) - 1)) {
+        // Length encodeded.
+        integer = 0;
+        int shift = 0;
+        while (true) {
+          bool done = (data[offset] & 0x80) != 0x80;
+          integer += (data[offset++] & 0x7f) << shift;
+          shift += 7;
+          if (done) break;
+        }
+        integer += (1 << prefixBits) - 1;
+      } else {
+        // In place length.
+        integer = byte;
+      }
+
+      return integer;
+    }
+
+    List<int> readStringLiteral() {
+      bool isHuffmanEncoding = (data[offset] & 0x80) != 0;
+      int length = readInteger(7);
+
+      var sublist = viewOrSublist(data, offset, length);
+      offset += length;
+      if (isHuffmanEncoding) {
+        return http2HuffmanCodec.decode(sublist);
+      } else {
+        return sublist;
+      }
+    }
+
+    Header readHeaderFieldInternal(int index, {bool neverIndexed: false}) {
+      List<int> name, value;
+      if (index > 0) {
+        name = _table.lookup(index).name;
+        value = readStringLiteral();
+      } else {
+        name = readStringLiteral();
+        value = readStringLiteral();
+      }
+      return new Header(name, value, neverIndexed: neverIndexed);
+    }
+
+    try {
+      List<Header> headers = [];
+      while (offset < data.length) {
+        int byte = data[offset];
+        bool isIndexedField = (byte & 0x80) != 0;
+        bool isIncrementalIndexing = (byte & 0xc0) == 0x40;
+
+        bool isWithoutIndexing = (byte & 0xf0) == 0;
+        bool isNeverIndexing = (byte & 0xf0) == 0x10;
+        bool isDynamicTableSizeUpdate = (byte & 0xe0) == 0x20;
+
+        if (isIndexedField) {
+          int index = readInteger(7);
+          var field = _table.lookup(index);
+          headers.add(field);
+        } else if (isIncrementalIndexing) {
+          var field = readHeaderFieldInternal(readInteger(6));
+          _table.addHeaderField(field);
+          headers.add(field);
+        } else if (isWithoutIndexing) {
+          headers.add(readHeaderFieldInternal(readInteger(4)));
+        } else if (isNeverIndexing) {
+          headers
+              .add(readHeaderFieldInternal(readInteger(4), neverIndexed: true));
+        } else if (isDynamicTableSizeUpdate) {
+          int newMaxSize = readInteger(5);
+          if (newMaxSize <= _maxHeaderTableSize) {
+            _table.updateMaxSize(newMaxSize);
+          } else {
+            throw new HPackDecodingException(
+                'Dynamic table size update failed: '
+                'A new value of $newMaxSize exceeds the limit of '
+                '$_maxHeaderTableSize');
+          }
+        } else {
+          throw new HPackDecodingException('Invalid encoding of headers.');
+        }
+      }
+      return headers;
+    } on RangeError catch (e) {
+      throw new HPackDecodingException('$e');
+    } on HuffmanDecodingException catch (e) {
+      throw new HPackDecodingException('$e');
+    }
+  }
+}
+
+/// A stateful HPACK encoder.
+// TODO: Currently we encode all headers:
+//    - without huffman encoding
+//    - without using the dynamic table
+class HPackEncoder {
+  int _maxHeaderTableSize = 4096;
+
+  final IndexTable _table = new IndexTable();
+
+  void updateMaxSendingHeaderTableSize(int newMaximumSize) {
+    // TODO: Once we start encoding via dynamic table we need to let the other
+    // side know the maximum table size we're using.
+    _maxHeaderTableSize = newMaximumSize;
+  }
+
+  List<int> encode(List<Header> headers) {
+    var bytesBuilder = new BytesBuilder();
+    int currentByte = 0;
+
+    void writeInteger(int prefixBits, int value) {
+      assert(prefixBits <= 8);
+
+      if (value < (1 << prefixBits) - 1) {
+        currentByte |= value;
+        bytesBuilder.addByte(currentByte);
+      } else {
+        // Length encodeded.
+        currentByte |= (1 << prefixBits) - 1;
+        value -= (1 << prefixBits) - 1;
+        bytesBuilder.addByte(currentByte);
+        bool done = false;
+        while (!done) {
+          currentByte = value & 0x7f;
+          value = value >> 7;
+          done = value == 0;
+          if (!done) currentByte |= 0x80;
+          bytesBuilder.addByte(currentByte);
+        }
+      }
+      currentByte = 0;
+    }
+
+    void writeStringLiteral(List<int> bytes) {
+      // TODO: Support huffman encoding.
+      currentByte = 0; // 1 would be huffman encoding
+      writeInteger(7, bytes.length);
+      bytesBuilder.add(bytes);
+    }
+
+    void writeLiteralHeaderWithoutIndexing(Header header) {
+      bytesBuilder.addByte(0);
+      writeStringLiteral(header.name);
+      writeStringLiteral(header.value);
+    }
+
+    for (var header in headers) {
+      writeLiteralHeaderWithoutIndexing(header);
+    }
+
+    return bytesBuilder.takeBytes();
+  }
+}
+
+class IndexTable {
+  static final List<Header> _staticTable = [
+    null,
+    new Header(ascii.encode(':authority'), const []),
+    new Header(ascii.encode(':method'), ascii.encode('GET')),
+    new Header(ascii.encode(':method'), ascii.encode('POST')),
+    new Header(ascii.encode(':path'), ascii.encode('/')),
+    new Header(ascii.encode(':path'), ascii.encode('/index.html')),
+    new Header(ascii.encode(':scheme'), ascii.encode('http')),
+    new Header(ascii.encode(':scheme'), ascii.encode('https')),
+    new Header(ascii.encode(':status'), ascii.encode('200')),
+    new Header(ascii.encode(':status'), ascii.encode('204')),
+    new Header(ascii.encode(':status'), ascii.encode('206')),
+    new Header(ascii.encode(':status'), ascii.encode('304')),
+    new Header(ascii.encode(':status'), ascii.encode('400')),
+    new Header(ascii.encode(':status'), ascii.encode('404')),
+    new Header(ascii.encode(':status'), ascii.encode('500')),
+    new Header(ascii.encode('accept-charset'), const []),
+    new Header(ascii.encode('accept-encoding'), ascii.encode('gzip, deflate')),
+    new Header(ascii.encode('accept-language'), const []),
+    new Header(ascii.encode('accept-ranges'), const []),
+    new Header(ascii.encode('accept'), const []),
+    new Header(ascii.encode('access-control-allow-origin'), const []),
+    new Header(ascii.encode('age'), const []),
+    new Header(ascii.encode('allow'), const []),
+    new Header(ascii.encode('authorization'), const []),
+    new Header(ascii.encode('cache-control'), const []),
+    new Header(ascii.encode('content-disposition'), const []),
+    new Header(ascii.encode('content-encoding'), const []),
+    new Header(ascii.encode('content-language'), const []),
+    new Header(ascii.encode('content-length'), const []),
+    new Header(ascii.encode('content-location'), const []),
+    new Header(ascii.encode('content-range'), const []),
+    new Header(ascii.encode('content-type'), const []),
+    new Header(ascii.encode('cookie'), const []),
+    new Header(ascii.encode('date'), const []),
+    new Header(ascii.encode('etag'), const []),
+    new Header(ascii.encode('expect'), const []),
+    new Header(ascii.encode('expires'), const []),
+    new Header(ascii.encode('from'), const []),
+    new Header(ascii.encode('host'), const []),
+    new Header(ascii.encode('if-match'), const []),
+    new Header(ascii.encode('if-modified-since'), const []),
+    new Header(ascii.encode('if-none-match'), const []),
+    new Header(ascii.encode('if-range'), const []),
+    new Header(ascii.encode('if-unmodified-since'), const []),
+    new Header(ascii.encode('last-modified'), const []),
+    new Header(ascii.encode('link'), const []),
+    new Header(ascii.encode('location'), const []),
+    new Header(ascii.encode('max-forwards'), const []),
+    new Header(ascii.encode('proxy-authenticate'), const []),
+    new Header(ascii.encode('proxy-authorization'), const []),
+    new Header(ascii.encode('range'), const []),
+    new Header(ascii.encode('referer'), const []),
+    new Header(ascii.encode('refresh'), const []),
+    new Header(ascii.encode('retry-after'), const []),
+    new Header(ascii.encode('server'), const []),
+    new Header(ascii.encode('set-cookie'), const []),
+    new Header(ascii.encode('strict-transport-security'), const []),
+    new Header(ascii.encode('transfer-encoding'), const []),
+    new Header(ascii.encode('user-agent'), const []),
+    new Header(ascii.encode('vary'), const []),
+    new Header(ascii.encode('via'), const []),
+    new Header(ascii.encode('www-authenticate'), const []),
+  ];
+
+  final List<Header> _dynamicTable = [];
+
+  /// The maximum size the dynamic table can grow to before entries need to be
+  /// evicted.
+  int _maximumSize = 4096;
+
+  /// The current size of the dynamic table.
+  int _currentSize = 0;
+
+  IndexTable();
+
+  /// Updates the maximum size which the dynamic table can grow to.
+  void updateMaxSize(int newMaxDynTableSize) {
+    _maximumSize = newMaxDynTableSize;
+    _reduce();
+  }
+
+  /// Lookup an item by index.
+  Header lookup(int index) {
+    if (index <= 0) {
+      throw new HPackDecodingException(
+          'Invalid index (was: $index) for table lookup.');
+    }
+    if (index < _staticTable.length) {
+      return _staticTable[index];
+    }
+    index -= _staticTable.length;
+    if (index < _dynamicTable.length) {
+      return _dynamicTable[index];
+    }
+    throw new HPackDecodingException(
+        'Invalid index (was: $index) for table lookup.');
+  }
+
+  /// Adds a new header field to the dynamic table - and evicts entries as
+  /// necessary.
+  void addHeaderField(Header header) {
+    _dynamicTable.insert(0, header);
+    _currentSize += _sizeOf(header);
+    _reduce();
+  }
+
+  /// Removes as many entries as required to be within the limit of
+  /// [_maximumSize].
+  void _reduce() {
+    while (_currentSize > _maximumSize) {
+      Header h = _dynamicTable.removeLast();
+      _currentSize -= _sizeOf(h);
+    }
+  }
+
+  /// Returns the "size" a [header] has.
+  ///
+  /// This is specified to be the number of octets of name/value plus 32.
+  int _sizeOf(Header header) => header.name.length + header.value.length + 32;
+}
diff --git a/http2/lib/src/hpack/huffman.dart b/http2/lib/src/hpack/huffman.dart
new file mode 100644
index 0000000..7dd4d09
--- /dev/null
+++ b/http2/lib/src/hpack/huffman.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2015, 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 http2.src.hpack.huffman;
+
+import 'dart:io';
+
+import 'huffman_table.dart';
+
+class HuffmanDecodingException implements Exception {
+  final String _message;
+
+  HuffmanDecodingException(this._message);
+
+  String toString() => 'HuffmanDecodingException: $_message';
+}
+
+/// A codec used for encoding/decoding using a huffman codec.
+class HuffmanCodec {
+  final HuffmanEncoder _encoder;
+  final HuffmanDecoder _decoder;
+
+  HuffmanCodec(this._encoder, this._decoder);
+
+  List<int> decode(List<int> bytes) => _decoder.decode(bytes);
+
+  List<int> encode(List<int> bytes) => _encoder.encode(bytes);
+}
+
+/// A huffman decoder based on a [HuffmanTreeNode].
+class HuffmanDecoder {
+  final HuffmanTreeNode _root;
+
+  HuffmanDecoder(this._root);
+
+  /// Decodes [bytes] using a huffman tree.
+  List<int> decode(List<int> bytes) {
+    var buffer = new BytesBuilder();
+
+    int currentByteOffset = 0;
+    HuffmanTreeNode node = _root;
+    int currentDepth = 0;
+    while (currentByteOffset < bytes.length) {
+      var byte = bytes[currentByteOffset];
+      for (int currentBit = 7; currentBit >= 0; currentBit--) {
+        bool right = (byte >> currentBit) & 1 == 1;
+        if (right) {
+          node = node.right;
+        } else {
+          node = node.left;
+        }
+        currentDepth++;
+        if (node.value != null) {
+          if (node.value == EOS_BYTE) {
+            throw new HuffmanDecodingException(
+                'More than 7 bit padding is not allowed. Found entire EOS '
+                'encoding');
+          }
+          buffer.addByte(node.value);
+          node = _root;
+          currentDepth = 0;
+        }
+      }
+      currentByteOffset++;
+    }
+
+    if (node != _root) {
+      if (currentDepth > 7) {
+        throw new HuffmanDecodingException(
+            'Incomplete encoding of a byte or more than 7 bit padding.');
+      }
+
+      while (node.right != null) node = node.right;
+
+      if (node.value != 256) {
+        throw new HuffmanDecodingException('Incomplete encoding of a byte.');
+      }
+    }
+
+    return buffer.takeBytes();
+  }
+}
+
+/// A huffman encoder based on a list of codewords.
+class HuffmanEncoder {
+  final List<EncodedHuffmanValue> _codewords;
+
+  HuffmanEncoder(this._codewords);
+
+  /// Encodes [bytes] using a list of codewords.
+  List<int> encode(List<int> bytes) {
+    var buffer = new BytesBuilder();
+
+    int currentByte = 0;
+    int currentBitOffset = 7;
+
+    writeValue(int value, int numBits) {
+      int i = numBits - 1;
+      while (i >= 0) {
+        if (currentBitOffset == 7 && i >= 7) {
+          assert(currentByte == 0);
+
+          buffer.addByte((value >> (i - 7)) & 0xff);
+          currentBitOffset = 7;
+          currentByte = 0;
+          i -= 8;
+        } else {
+          currentByte |= ((value >> i) & 1) << currentBitOffset;
+
+          currentBitOffset--;
+          if (currentBitOffset == -1) {
+            buffer.addByte(currentByte);
+            currentBitOffset = 7;
+            currentByte = 0;
+          }
+          i--;
+        }
+      }
+    }
+
+    for (int i = 0; i < bytes.length; i++) {
+      var byte = bytes[i];
+      var value = _codewords[byte];
+      writeValue(value.encodedBytes, value.numBits);
+    }
+
+    if (currentBitOffset < 7) {
+      writeValue(0xff, 1 + currentBitOffset);
+    }
+
+    return buffer.takeBytes();
+  }
+}
+
+/// Specifies the encoding of a specific value using huffman encoding.
+class EncodedHuffmanValue {
+  /// An integer representation of the encoded bit-string.
+  final int encodedBytes;
+
+  /// The number of bits in [encodedBytes].
+  final int numBits;
+
+  const EncodedHuffmanValue(this.encodedBytes, this.numBits);
+}
+
+/// A node in the huffman tree.
+class HuffmanTreeNode {
+  HuffmanTreeNode left;
+  HuffmanTreeNode right;
+  int value;
+}
+
+/// Generates a huffman decoding tree.
+HuffmanTreeNode generateHuffmanTree(List<EncodedHuffmanValue> valueEncodings) {
+  HuffmanTreeNode root = new HuffmanTreeNode();
+
+  for (int byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) {
+    var entry = valueEncodings[byteOffset];
+
+    HuffmanTreeNode current = root;
+    for (int bitNr = 0; bitNr < entry.numBits; bitNr++) {
+      bool right =
+          ((entry.encodedBytes >> (entry.numBits - bitNr - 1)) & 1) == 1;
+
+      if (right) {
+        if (current.right == null) {
+          current.right = new HuffmanTreeNode();
+        }
+        current = current.right;
+      } else {
+        if (current.left == null) {
+          current.left = new HuffmanTreeNode();
+        }
+        current = current.left;
+      }
+    }
+
+    current.value = byteOffset;
+  }
+
+  return root;
+}
diff --git a/http2/lib/src/hpack/huffman_table.dart b/http2/lib/src/hpack/huffman_table.dart
new file mode 100644
index 0000000..90471c8
--- /dev/null
+++ b/http2/lib/src/hpack/huffman_table.dart
@@ -0,0 +1,278 @@
+// Copyright (c) 2015, 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 http2.hpack.huffman_table;
+
+import 'huffman.dart';
+
+/// The huffman codec for encoding/decoding HTTP/2 header blocks.
+final HuffmanCodec http2HuffmanCodec = new HuffmanCodec(
+    new HuffmanEncoder(_codeWords),
+    new HuffmanDecoder(generateHuffmanTree(_codeWords)));
+
+/// This is the integer representing the End-of-String symbol
+/// (it is not representable by a byte).
+const int EOS_BYTE = 256;
+
+/// This list of byte encodings via huffman encoding was generated from the
+/// HPACK specification.
+const List<EncodedHuffmanValue> _codeWords = const <EncodedHuffmanValue>[
+  const EncodedHuffmanValue(0x1ff8, 13),
+  const EncodedHuffmanValue(0x7fffd8, 23),
+  const EncodedHuffmanValue(0xfffffe2, 28),
+  const EncodedHuffmanValue(0xfffffe3, 28),
+  const EncodedHuffmanValue(0xfffffe4, 28),
+  const EncodedHuffmanValue(0xfffffe5, 28),
+  const EncodedHuffmanValue(0xfffffe6, 28),
+  const EncodedHuffmanValue(0xfffffe7, 28),
+  const EncodedHuffmanValue(0xfffffe8, 28),
+  const EncodedHuffmanValue(0xffffea, 24),
+  const EncodedHuffmanValue(0x3ffffffc, 30),
+  const EncodedHuffmanValue(0xfffffe9, 28),
+  const EncodedHuffmanValue(0xfffffea, 28),
+  const EncodedHuffmanValue(0x3ffffffd, 30),
+  const EncodedHuffmanValue(0xfffffeb, 28),
+  const EncodedHuffmanValue(0xfffffec, 28),
+  const EncodedHuffmanValue(0xfffffed, 28),
+  const EncodedHuffmanValue(0xfffffee, 28),
+  const EncodedHuffmanValue(0xfffffef, 28),
+  const EncodedHuffmanValue(0xffffff0, 28),
+  const EncodedHuffmanValue(0xffffff1, 28),
+  const EncodedHuffmanValue(0xffffff2, 28),
+  const EncodedHuffmanValue(0x3ffffffe, 30),
+  const EncodedHuffmanValue(0xffffff3, 28),
+  const EncodedHuffmanValue(0xffffff4, 28),
+  const EncodedHuffmanValue(0xffffff5, 28),
+  const EncodedHuffmanValue(0xffffff6, 28),
+  const EncodedHuffmanValue(0xffffff7, 28),
+  const EncodedHuffmanValue(0xffffff8, 28),
+  const EncodedHuffmanValue(0xffffff9, 28),
+  const EncodedHuffmanValue(0xffffffa, 28),
+  const EncodedHuffmanValue(0xffffffb, 28),
+  const EncodedHuffmanValue(0x14, 6),
+  const EncodedHuffmanValue(0x3f8, 10),
+  const EncodedHuffmanValue(0x3f9, 10),
+  const EncodedHuffmanValue(0xffa, 12),
+  const EncodedHuffmanValue(0x1ff9, 13),
+  const EncodedHuffmanValue(0x15, 6),
+  const EncodedHuffmanValue(0xf8, 8),
+  const EncodedHuffmanValue(0x7fa, 11),
+  const EncodedHuffmanValue(0x3fa, 10),
+  const EncodedHuffmanValue(0x3fb, 10),
+  const EncodedHuffmanValue(0xf9, 8),
+  const EncodedHuffmanValue(0x7fb, 11),
+  const EncodedHuffmanValue(0xfa, 8),
+  const EncodedHuffmanValue(0x16, 6),
+  const EncodedHuffmanValue(0x17, 6),
+  const EncodedHuffmanValue(0x18, 6),
+  const EncodedHuffmanValue(0x0, 5),
+  const EncodedHuffmanValue(0x1, 5),
+  const EncodedHuffmanValue(0x2, 5),
+  const EncodedHuffmanValue(0x19, 6),
+  const EncodedHuffmanValue(0x1a, 6),
+  const EncodedHuffmanValue(0x1b, 6),
+  const EncodedHuffmanValue(0x1c, 6),
+  const EncodedHuffmanValue(0x1d, 6),
+  const EncodedHuffmanValue(0x1e, 6),
+  const EncodedHuffmanValue(0x1f, 6),
+  const EncodedHuffmanValue(0x5c, 7),
+  const EncodedHuffmanValue(0xfb, 8),
+  const EncodedHuffmanValue(0x7ffc, 15),
+  const EncodedHuffmanValue(0x20, 6),
+  const EncodedHuffmanValue(0xffb, 12),
+  const EncodedHuffmanValue(0x3fc, 10),
+  const EncodedHuffmanValue(0x1ffa, 13),
+  const EncodedHuffmanValue(0x21, 6),
+  const EncodedHuffmanValue(0x5d, 7),
+  const EncodedHuffmanValue(0x5e, 7),
+  const EncodedHuffmanValue(0x5f, 7),
+  const EncodedHuffmanValue(0x60, 7),
+  const EncodedHuffmanValue(0x61, 7),
+  const EncodedHuffmanValue(0x62, 7),
+  const EncodedHuffmanValue(0x63, 7),
+  const EncodedHuffmanValue(0x64, 7),
+  const EncodedHuffmanValue(0x65, 7),
+  const EncodedHuffmanValue(0x66, 7),
+  const EncodedHuffmanValue(0x67, 7),
+  const EncodedHuffmanValue(0x68, 7),
+  const EncodedHuffmanValue(0x69, 7),
+  const EncodedHuffmanValue(0x6a, 7),
+  const EncodedHuffmanValue(0x6b, 7),
+  const EncodedHuffmanValue(0x6c, 7),
+  const EncodedHuffmanValue(0x6d, 7),
+  const EncodedHuffmanValue(0x6e, 7),
+  const EncodedHuffmanValue(0x6f, 7),
+  const EncodedHuffmanValue(0x70, 7),
+  const EncodedHuffmanValue(0x71, 7),
+  const EncodedHuffmanValue(0x72, 7),
+  const EncodedHuffmanValue(0xfc, 8),
+  const EncodedHuffmanValue(0x73, 7),
+  const EncodedHuffmanValue(0xfd, 8),
+  const EncodedHuffmanValue(0x1ffb, 13),
+  const EncodedHuffmanValue(0x7fff0, 19),
+  const EncodedHuffmanValue(0x1ffc, 13),
+  const EncodedHuffmanValue(0x3ffc, 14),
+  const EncodedHuffmanValue(0x22, 6),
+  const EncodedHuffmanValue(0x7ffd, 15),
+  const EncodedHuffmanValue(0x3, 5),
+  const EncodedHuffmanValue(0x23, 6),
+  const EncodedHuffmanValue(0x4, 5),
+  const EncodedHuffmanValue(0x24, 6),
+  const EncodedHuffmanValue(0x5, 5),
+  const EncodedHuffmanValue(0x25, 6),
+  const EncodedHuffmanValue(0x26, 6),
+  const EncodedHuffmanValue(0x27, 6),
+  const EncodedHuffmanValue(0x6, 5),
+  const EncodedHuffmanValue(0x74, 7),
+  const EncodedHuffmanValue(0x75, 7),
+  const EncodedHuffmanValue(0x28, 6),
+  const EncodedHuffmanValue(0x29, 6),
+  const EncodedHuffmanValue(0x2a, 6),
+  const EncodedHuffmanValue(0x7, 5),
+  const EncodedHuffmanValue(0x2b, 6),
+  const EncodedHuffmanValue(0x76, 7),
+  const EncodedHuffmanValue(0x2c, 6),
+  const EncodedHuffmanValue(0x8, 5),
+  const EncodedHuffmanValue(0x9, 5),
+  const EncodedHuffmanValue(0x2d, 6),
+  const EncodedHuffmanValue(0x77, 7),
+  const EncodedHuffmanValue(0x78, 7),
+  const EncodedHuffmanValue(0x79, 7),
+  const EncodedHuffmanValue(0x7a, 7),
+  const EncodedHuffmanValue(0x7b, 7),
+  const EncodedHuffmanValue(0x7ffe, 15),
+  const EncodedHuffmanValue(0x7fc, 11),
+  const EncodedHuffmanValue(0x3ffd, 14),
+  const EncodedHuffmanValue(0x1ffd, 13),
+  const EncodedHuffmanValue(0xffffffc, 28),
+  const EncodedHuffmanValue(0xfffe6, 20),
+  const EncodedHuffmanValue(0x3fffd2, 22),
+  const EncodedHuffmanValue(0xfffe7, 20),
+  const EncodedHuffmanValue(0xfffe8, 20),
+  const EncodedHuffmanValue(0x3fffd3, 22),
+  const EncodedHuffmanValue(0x3fffd4, 22),
+  const EncodedHuffmanValue(0x3fffd5, 22),
+  const EncodedHuffmanValue(0x7fffd9, 23),
+  const EncodedHuffmanValue(0x3fffd6, 22),
+  const EncodedHuffmanValue(0x7fffda, 23),
+  const EncodedHuffmanValue(0x7fffdb, 23),
+  const EncodedHuffmanValue(0x7fffdc, 23),
+  const EncodedHuffmanValue(0x7fffdd, 23),
+  const EncodedHuffmanValue(0x7fffde, 23),
+  const EncodedHuffmanValue(0xffffeb, 24),
+  const EncodedHuffmanValue(0x7fffdf, 23),
+  const EncodedHuffmanValue(0xffffec, 24),
+  const EncodedHuffmanValue(0xffffed, 24),
+  const EncodedHuffmanValue(0x3fffd7, 22),
+  const EncodedHuffmanValue(0x7fffe0, 23),
+  const EncodedHuffmanValue(0xffffee, 24),
+  const EncodedHuffmanValue(0x7fffe1, 23),
+  const EncodedHuffmanValue(0x7fffe2, 23),
+  const EncodedHuffmanValue(0x7fffe3, 23),
+  const EncodedHuffmanValue(0x7fffe4, 23),
+  const EncodedHuffmanValue(0x1fffdc, 21),
+  const EncodedHuffmanValue(0x3fffd8, 22),
+  const EncodedHuffmanValue(0x7fffe5, 23),
+  const EncodedHuffmanValue(0x3fffd9, 22),
+  const EncodedHuffmanValue(0x7fffe6, 23),
+  const EncodedHuffmanValue(0x7fffe7, 23),
+  const EncodedHuffmanValue(0xffffef, 24),
+  const EncodedHuffmanValue(0x3fffda, 22),
+  const EncodedHuffmanValue(0x1fffdd, 21),
+  const EncodedHuffmanValue(0xfffe9, 20),
+  const EncodedHuffmanValue(0x3fffdb, 22),
+  const EncodedHuffmanValue(0x3fffdc, 22),
+  const EncodedHuffmanValue(0x7fffe8, 23),
+  const EncodedHuffmanValue(0x7fffe9, 23),
+  const EncodedHuffmanValue(0x1fffde, 21),
+  const EncodedHuffmanValue(0x7fffea, 23),
+  const EncodedHuffmanValue(0x3fffdd, 22),
+  const EncodedHuffmanValue(0x3fffde, 22),
+  const EncodedHuffmanValue(0xfffff0, 24),
+  const EncodedHuffmanValue(0x1fffdf, 21),
+  const EncodedHuffmanValue(0x3fffdf, 22),
+  const EncodedHuffmanValue(0x7fffeb, 23),
+  const EncodedHuffmanValue(0x7fffec, 23),
+  const EncodedHuffmanValue(0x1fffe0, 21),
+  const EncodedHuffmanValue(0x1fffe1, 21),
+  const EncodedHuffmanValue(0x3fffe0, 22),
+  const EncodedHuffmanValue(0x1fffe2, 21),
+  const EncodedHuffmanValue(0x7fffed, 23),
+  const EncodedHuffmanValue(0x3fffe1, 22),
+  const EncodedHuffmanValue(0x7fffee, 23),
+  const EncodedHuffmanValue(0x7fffef, 23),
+  const EncodedHuffmanValue(0xfffea, 20),
+  const EncodedHuffmanValue(0x3fffe2, 22),
+  const EncodedHuffmanValue(0x3fffe3, 22),
+  const EncodedHuffmanValue(0x3fffe4, 22),
+  const EncodedHuffmanValue(0x7ffff0, 23),
+  const EncodedHuffmanValue(0x3fffe5, 22),
+  const EncodedHuffmanValue(0x3fffe6, 22),
+  const EncodedHuffmanValue(0x7ffff1, 23),
+  const EncodedHuffmanValue(0x3ffffe0, 26),
+  const EncodedHuffmanValue(0x3ffffe1, 26),
+  const EncodedHuffmanValue(0xfffeb, 20),
+  const EncodedHuffmanValue(0x7fff1, 19),
+  const EncodedHuffmanValue(0x3fffe7, 22),
+  const EncodedHuffmanValue(0x7ffff2, 23),
+  const EncodedHuffmanValue(0x3fffe8, 22),
+  const EncodedHuffmanValue(0x1ffffec, 25),
+  const EncodedHuffmanValue(0x3ffffe2, 26),
+  const EncodedHuffmanValue(0x3ffffe3, 26),
+  const EncodedHuffmanValue(0x3ffffe4, 26),
+  const EncodedHuffmanValue(0x7ffffde, 27),
+  const EncodedHuffmanValue(0x7ffffdf, 27),
+  const EncodedHuffmanValue(0x3ffffe5, 26),
+  const EncodedHuffmanValue(0xfffff1, 24),
+  const EncodedHuffmanValue(0x1ffffed, 25),
+  const EncodedHuffmanValue(0x7fff2, 19),
+  const EncodedHuffmanValue(0x1fffe3, 21),
+  const EncodedHuffmanValue(0x3ffffe6, 26),
+  const EncodedHuffmanValue(0x7ffffe0, 27),
+  const EncodedHuffmanValue(0x7ffffe1, 27),
+  const EncodedHuffmanValue(0x3ffffe7, 26),
+  const EncodedHuffmanValue(0x7ffffe2, 27),
+  const EncodedHuffmanValue(0xfffff2, 24),
+  const EncodedHuffmanValue(0x1fffe4, 21),
+  const EncodedHuffmanValue(0x1fffe5, 21),
+  const EncodedHuffmanValue(0x3ffffe8, 26),
+  const EncodedHuffmanValue(0x3ffffe9, 26),
+  const EncodedHuffmanValue(0xffffffd, 28),
+  const EncodedHuffmanValue(0x7ffffe3, 27),
+  const EncodedHuffmanValue(0x7ffffe4, 27),
+  const EncodedHuffmanValue(0x7ffffe5, 27),
+  const EncodedHuffmanValue(0xfffec, 20),
+  const EncodedHuffmanValue(0xfffff3, 24),
+  const EncodedHuffmanValue(0xfffed, 20),
+  const EncodedHuffmanValue(0x1fffe6, 21),
+  const EncodedHuffmanValue(0x3fffe9, 22),
+  const EncodedHuffmanValue(0x1fffe7, 21),
+  const EncodedHuffmanValue(0x1fffe8, 21),
+  const EncodedHuffmanValue(0x7ffff3, 23),
+  const EncodedHuffmanValue(0x3fffea, 22),
+  const EncodedHuffmanValue(0x3fffeb, 22),
+  const EncodedHuffmanValue(0x1ffffee, 25),
+  const EncodedHuffmanValue(0x1ffffef, 25),
+  const EncodedHuffmanValue(0xfffff4, 24),
+  const EncodedHuffmanValue(0xfffff5, 24),
+  const EncodedHuffmanValue(0x3ffffea, 26),
+  const EncodedHuffmanValue(0x7ffff4, 23),
+  const EncodedHuffmanValue(0x3ffffeb, 26),
+  const EncodedHuffmanValue(0x7ffffe6, 27),
+  const EncodedHuffmanValue(0x3ffffec, 26),
+  const EncodedHuffmanValue(0x3ffffed, 26),
+  const EncodedHuffmanValue(0x7ffffe7, 27),
+  const EncodedHuffmanValue(0x7ffffe8, 27),
+  const EncodedHuffmanValue(0x7ffffe9, 27),
+  const EncodedHuffmanValue(0x7ffffea, 27),
+  const EncodedHuffmanValue(0x7ffffeb, 27),
+  const EncodedHuffmanValue(0xffffffe, 28),
+  const EncodedHuffmanValue(0x7ffffec, 27),
+  const EncodedHuffmanValue(0x7ffffed, 27),
+  const EncodedHuffmanValue(0x7ffffee, 27),
+  const EncodedHuffmanValue(0x7ffffef, 27),
+  const EncodedHuffmanValue(0x7fffff0, 27),
+  const EncodedHuffmanValue(0x3ffffee, 26),
+  const EncodedHuffmanValue(0x3fffffff, 30),
+];
diff --git a/http2/lib/src/ping/ping_handler.dart b/http2/lib/src/ping/ping_handler.dart
new file mode 100644
index 0000000..bd8bc17
--- /dev/null
+++ b/http2/lib/src/ping/ping_handler.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2015, 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 http2.src.ping.ping_handler;
+
+import 'dart:async';
+
+import '../error_handler.dart';
+import '../frames/frames.dart';
+import '../sync_errors.dart';
+
+/// Responsible for pinging the other end and for handling pings from the
+/// other end.
+// TODO: We currently write unconditionally to the [FrameWriter]: we might want
+// to consider be more aware what [Framewriter.bufferIndicator.wouldBuffer]
+// says.
+class PingHandler extends Object with TerminatableMixin {
+  final FrameWriter _frameWriter;
+  final Map<int, Completer> _remainingPings = {};
+  int _nextId = 1;
+
+  PingHandler(this._frameWriter);
+
+  void onTerminated(error) {
+    var values = _remainingPings.values.toList();
+    _remainingPings.clear();
+    values.forEach((Completer c) => c.completeError(error));
+  }
+
+  void processPingFrame(PingFrame frame) {
+    ensureNotTerminatedSync(() {
+      if (frame.header.streamId != 0) {
+        throw new ProtocolException('Ping frames must have a stream id of 0.');
+      }
+
+      if (!frame.hasAckFlag) {
+        _frameWriter.writePingFrame(frame.opaqueData, ack: true);
+      } else {
+        Completer c = _remainingPings.remove(frame.opaqueData);
+        if (c != null) {
+          c.complete();
+        } else {
+          // NOTE: It is not specified what happens when one gets an ACK for a
+          // ping we never sent. We be very strict and fail in this case.
+          throw new ProtocolException(
+              'Received ping ack with unknown opaque data.');
+        }
+      }
+    });
+  }
+
+  Future ping() {
+    return ensureNotTerminatedAsync(() {
+      Completer c = new Completer();
+      var id = _nextId++;
+      _remainingPings[id] = c;
+      _frameWriter.writePingFrame(id);
+      return c.future;
+    });
+  }
+}
diff --git a/http2/lib/src/settings/settings.dart b/http2/lib/src/settings/settings.dart
new file mode 100644
index 0000000..6ee2284
--- /dev/null
+++ b/http2/lib/src/settings/settings.dart
@@ -0,0 +1,225 @@
+// Copyright (c) 2015, 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 http2.src.settings;
+
+import 'dart:async';
+
+import '../error_handler.dart';
+import '../frames/frames.dart';
+import '../hpack/hpack.dart';
+import '../sync_errors.dart';
+
+/// The settings a remote peer can choose to set.
+class ActiveSettings {
+  /// Allows the sender to inform the remote endpoint of the maximum size of the
+  /// header compression table used to decode header blocks, in octets. The
+  /// encoder can select any size equal to or less than this value by using
+  /// signaling specific to the header compression format inside a header block.
+  /// The initial value is 4,096 octets.
+  int headerTableSize;
+
+  /// This setting can be use to disable server push (Section 8.2). An endpoint
+  /// MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a
+  /// value of 0. An endpoint that has both set this parameter to 0 and had it
+  /// acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a
+  /// connection error (Section 5.4.1) of type PROTOCOL_ERROR.
+  ///
+  /// The initial value is 1, which indicates that server push is permitted.
+  /// Any value other than 0 or 1 MUST be treated as a connection error
+  /// (Section 5.4.1) of type PROTOCOL_ERROR.
+  bool enablePush;
+
+  /// Indicates the maximum number of concurrent streams that the sender will
+  /// allow. This limit is directional: it applies to the number of streams that
+  /// the sender permits the receiver to create. Initially there is no limit to
+  /// this value. It is recommended that this value be no smaller than 100, so
+  /// as to not unnecessarily limit parallelism.
+  ///
+  /// A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as
+  /// special by endpoints. A zero value does prevent the creation of new
+  /// streams, however this can also happen for any limit that is exhausted with
+  /// active streams. Servers SHOULD only set a zero value for short durations;
+  /// if a server does not wish to accept requests, closing the connection is
+  /// more appropriate.
+  int maxConcurrentStreams;
+
+  /// Indicates the sender's initial window size (in octets) for stream level
+  /// flow control. The initial value is 2^16-1 (65,535) octets.
+  ///
+  /// This setting affects the window size of all streams, including existing
+  /// streams, see Section 6.9.2.
+  /// Values above the maximum flow control window size of 231-1 MUST be treated
+  /// as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR.
+  int initialWindowSize;
+
+  /// Indicates the size of the largest frame payload that the sender is willing
+  /// to receive, in octets.
+  ///
+  /// The initial value is 2^14 (16,384) octets. The value advertised by an
+  /// endpoint MUST be between this initial value and the maximum allowed frame
+  /// size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range
+  /// MUST be treated as a connection error (Section 5.4.1) of type
+  /// PROTOCOL_ERROR.
+  int maxFrameSize;
+
+  /// This advisory setting informs a peer of the maximum size of header list
+  /// that the sender is prepared to accept, in octets. The value is based on
+  /// the uncompressed size of header fields, including the length of the name
+  /// and value in octets plus an overhead of 32 octets for each header field.
+  ///
+  /// For any given request, a lower limit than what is advertised MAY be
+  /// enforced. The initial value of this setting is unlimited.
+  int maxHeaderListSize;
+
+  ActiveSettings(
+      {this.headerTableSize: 4096,
+      this.enablePush: true,
+      this.maxConcurrentStreams,
+      this.initialWindowSize: (1 << 16) - 1,
+      this.maxFrameSize: (1 << 14),
+      this.maxHeaderListSize});
+}
+
+/// Handles remote and local connection [Setting]s.
+///
+/// Incoming [SettingsFrame]s will be handled here to update the peer settings.
+/// Changes to [_toBeAcknowledgedSettings] can be made, the peer will then be
+/// notified of the setting changes it should use.
+class SettingsHandler extends Object with TerminatableMixin {
+  /// Certain settings changes can change the maximum allowed dynamic table
+  /// size used by the HPack encoder.
+  final HPackEncoder _hpackEncoder;
+
+  final FrameWriter _frameWriter;
+
+  /// A list of outstanding setting changes.
+  final List<List<Setting>> _toBeAcknowledgedSettings = [];
+
+  /// A list of completers for outstanding setting changes.
+  final List<Completer> _toBeAcknowledgedCompleters = [];
+
+  /// The local settings, which the remote side ACKed to obey.
+  final ActiveSettings _acknowledgedSettings;
+
+  /// The peer settings, which we ACKed and are obeying.
+  final ActiveSettings _peerSettings;
+
+  final _onInitialWindowSizeChangeController =
+      new StreamController<int>.broadcast(sync: true);
+
+  /// Events are fired when a SettingsFrame changes the initial size
+  /// of stream windows.
+  Stream<int> get onInitialWindowSizeChange =>
+      _onInitialWindowSizeChangeController.stream;
+
+  SettingsHandler(this._hpackEncoder, this._frameWriter,
+      this._acknowledgedSettings, this._peerSettings);
+
+  /// The settings for this endpoint of the connection which the remote peer
+  /// has ACKed and uses.
+  ActiveSettings get acknowledgedSettings => _acknowledgedSettings;
+
+  /// The settings for the remote endpoint of the connection which this
+  /// endpoint should use.
+  ActiveSettings get peerSettings => _peerSettings;
+
+  /// Handles an incoming [SettingsFrame] which can be an ACK or a settings
+  /// change.
+  void handleSettingsFrame(SettingsFrame frame) {
+    ensureNotTerminatedSync(() {
+      assert(frame.header.streamId == 0);
+
+      if (frame.hasAckFlag) {
+        assert(frame.header.length == 0);
+
+        if (_toBeAcknowledgedSettings.isEmpty) {
+          // NOTE: The specification does not say anything about ACKed settings
+          // which were never sent to the other side. We consider this definitly
+          // an error.
+          throw new ProtocolException(
+              'Received an acknowledged settings frame which did not have a '
+              'outstanding settings request.');
+        }
+        List<Setting> settingChanges = _toBeAcknowledgedSettings.removeAt(0);
+        Completer completer = _toBeAcknowledgedCompleters.removeAt(0);
+        _modifySettings(_acknowledgedSettings, settingChanges, false);
+        completer.complete();
+      } else {
+        _modifySettings(_peerSettings, frame.settings, true);
+        _frameWriter.writeSettingsAckFrame();
+      }
+    });
+  }
+
+  void onTerminated(error) {
+    _toBeAcknowledgedSettings.clear();
+    _toBeAcknowledgedCompleters
+        .forEach((Completer c) => c.completeError(error));
+  }
+
+  Future changeSettings(List<Setting> changes) {
+    return ensureNotTerminatedAsync(() {
+      // TODO: Have a timeout: When ACK doesn't get back in a reasonable time
+      // frame we should quit with ErrorCode.SETTINGS_TIMEOUT.
+      var completer = new Completer();
+      _toBeAcknowledgedSettings.add(changes);
+      _toBeAcknowledgedCompleters.add(completer);
+      _frameWriter.writeSettingsFrame(changes);
+      return completer.future;
+    });
+  }
+
+  void _modifySettings(
+      ActiveSettings base, List<Setting> changes, bool peerSettings) {
+    for (var setting in changes) {
+      switch (setting.identifier) {
+        case Setting.SETTINGS_ENABLE_PUSH:
+          if (setting.value == 0) {
+            base.enablePush = false;
+          } else if (setting.value == 1) {
+            base.enablePush = true;
+          } else {
+            throw new ProtocolException(
+                'The push setting can be only set to 0 or 1.');
+          }
+          break;
+
+        case Setting.SETTINGS_HEADER_TABLE_SIZE:
+          base.headerTableSize = setting.value;
+          if (peerSettings) {
+            _hpackEncoder.updateMaxSendingHeaderTableSize(base.headerTableSize);
+          }
+          break;
+
+        case Setting.SETTINGS_MAX_HEADER_LIST_SIZE:
+          // TODO: Propagate this signal to the HPackContext.
+          base.maxHeaderListSize = setting.value;
+          break;
+
+        case Setting.SETTINGS_MAX_CONCURRENT_STREAMS:
+          // NOTE: We will not force closing of existing streams if the limit is
+          // lower than the current number of open streams. But we will prevent
+          // new streams from being created if the number of existing streams
+          // is above this limit.
+          base.maxConcurrentStreams = setting.value;
+          break;
+
+        case Setting.SETTINGS_INITIAL_WINDOW_SIZE:
+          if (setting.value < (1 << 31)) {
+            int difference = setting.value - base.initialWindowSize;
+            _onInitialWindowSizeChangeController.add(difference);
+            base.initialWindowSize = setting.value;
+          } else {
+            throw new FlowControlException('Invalid initial window size.');
+          }
+          break;
+
+        default:
+          // Spec says to ignore unknown settings.
+          break;
+      }
+    }
+  }
+}
diff --git a/http2/lib/src/streams/stream_handler.dart b/http2/lib/src/streams/stream_handler.dart
new file mode 100644
index 0000000..0f79b52
--- /dev/null
+++ b/http2/lib/src/streams/stream_handler.dart
@@ -0,0 +1,874 @@
+// Copyright (c) 2015, 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 http2.src.stream_handler;
+
+import 'dart:async';
+import 'dart:math';
+
+import '../../transport.dart';
+
+import '../connection.dart';
+import '../error_handler.dart';
+import '../flowcontrol/connection_queues.dart';
+import '../flowcontrol/queue_messages.dart';
+import '../flowcontrol/stream_queues.dart';
+import '../flowcontrol/window.dart';
+import '../flowcontrol/window_handler.dart';
+import '../frames/frames.dart';
+import '../hpack/hpack.dart';
+import '../settings/settings.dart';
+import '../sync_errors.dart';
+
+/// Represents the current state of a stream.
+enum StreamState {
+  ReservedLocal,
+  ReservedRemote,
+  Idle,
+  Open,
+  HalfClosedLocal,
+  HalfClosedRemote,
+  Closed,
+
+  /// The [Terminated] state is an artificial state and signals that this stream
+  /// has been forcefully terminated.
+  Terminated,
+}
+
+/// Represents a HTTP/2 stream.
+class Http2StreamImpl extends TransportStream
+    implements ClientTransportStream, ServerTransportStream {
+  /// The id of this stream.
+  ///
+  ///   * odd numbered streams are client streams
+  ///   * even numbered streams are opened from the server
+  final int id;
+
+  // The queue for incoming [StreamMessage]s.
+  final StreamMessageQueueIn incomingQueue;
+
+  // The queue for outgoing [StreamMessage]s.
+  final StreamMessageQueueOut outgoingQueue;
+
+  // The stream controller to which the application can
+  // add outgoing messages.
+  final StreamController<StreamMessage> _outgoingC;
+
+  final OutgoingStreamWindowHandler windowHandler;
+
+  // The state of this stream.
+  StreamState state = StreamState.Idle;
+
+  // Error code from RST_STREAM frame, if the stream has been terminated
+  // remotely.
+  int _terminatedErrorCode;
+
+  // Termination handler. Invoked if the stream receives an RST_STREAM frame.
+  void Function(int) _onTerminated;
+
+  final ZoneUnaryCallback<bool, Http2StreamImpl> _canPushFun;
+  final ZoneBinaryCallback<ServerTransportStream, Http2StreamImpl, List<Header>>
+      _pushStreamFun;
+  final ZoneUnaryCallback<dynamic, Http2StreamImpl> _terminateStreamFun;
+
+  StreamSubscription _outgoingCSubscription;
+
+  Http2StreamImpl(
+      this.incomingQueue,
+      this.outgoingQueue,
+      this._outgoingC,
+      this.id,
+      this.windowHandler,
+      this._canPushFun,
+      this._pushStreamFun,
+      this._terminateStreamFun);
+
+  /// A stream of data and/or headers from the remote end.
+  Stream<StreamMessage> get incomingMessages => incomingQueue.messages;
+
+  /// A sink for writing data and/or headers to the remote end.
+  StreamSink<StreamMessage> get outgoingMessages => _outgoingC.sink;
+
+  /// Streams which the server pushed to this endpoint.
+  Stream<TransportStreamPush> get peerPushes => incomingQueue.serverPushes;
+
+  bool get canPush => _canPushFun(this);
+
+  /// Pushes a new stream to a client.
+  ///
+  /// The [requestHeaders] are the headers to which the pushed stream
+  /// responds to.
+  ServerTransportStream push(List<Header> requestHeaders) =>
+      _pushStreamFun(this, requestHeaders);
+
+  void terminate() => _terminateStreamFun(this);
+
+  set onTerminated(void handler(int v)) {
+    _onTerminated = handler;
+    if (_terminatedErrorCode != null && _onTerminated != null) {
+      _onTerminated(_terminatedErrorCode);
+    }
+  }
+
+  void _handleTerminated(int errorCode) {
+    _terminatedErrorCode = errorCode;
+    if (_onTerminated != null) {
+      _onTerminated(_terminatedErrorCode);
+    }
+  }
+}
+
+/// Handles [Frame]s with a non-zero stream-id.
+///
+/// It keeps track of open streams, their state, their queues, forwards
+/// messages from the connection level to stream level and vise versa.
+// TODO: Handle stream/connection queue errors & forward to connection object.
+class StreamHandler extends Object with TerminatableMixin, ClosableMixin {
+  static const int MAX_STREAM_ID = (1 << 31) - 1;
+
+  final FrameWriter _frameWriter;
+  final ConnectionMessageQueueIn incomingQueue;
+  final ConnectionMessageQueueOut outgoingQueue;
+
+  final StreamController<TransportStream> _newStreamsC = new StreamController();
+
+  final ActiveSettings _peerSettings;
+  final ActiveSettings _localSettings;
+
+  final Map<int, Http2StreamImpl> _openStreams = {};
+  int nextStreamId;
+  int lastRemoteStreamId;
+
+  int _highestStreamIdReceived = 0;
+
+  /// Represents the highest stream id this connection has received from the
+  /// remote side.
+  int get highestPeerInitiatedStream => _highestStreamIdReceived;
+
+  bool get isServer => nextStreamId.isEven;
+
+  bool get ranOutOfStreamIds => _ranOutOfStreamIds();
+
+  /// Whether it is possible to open a new stream to the remote end (e.g. based
+  /// on whether we have reached the limit of maximum concurrent open streams).
+  bool get canOpenStream => _canCreateNewStream();
+
+  final ActiveStateHandler _onActiveStateChanged;
+
+  StreamHandler._(
+      this._frameWriter,
+      this.incomingQueue,
+      this.outgoingQueue,
+      this._peerSettings,
+      this._localSettings,
+      this._onActiveStateChanged,
+      this.nextStreamId,
+      this.lastRemoteStreamId);
+
+  factory StreamHandler.client(
+      FrameWriter writer,
+      ConnectionMessageQueueIn incomingQueue,
+      ConnectionMessageQueueOut outgoingQueue,
+      ActiveSettings peerSettings,
+      ActiveSettings localSettings,
+      ActiveStateHandler onActiveStateChanged) {
+    return new StreamHandler._(writer, incomingQueue, outgoingQueue,
+        peerSettings, localSettings, onActiveStateChanged, 1, 0);
+  }
+
+  factory StreamHandler.server(
+      FrameWriter writer,
+      ConnectionMessageQueueIn incomingQueue,
+      ConnectionMessageQueueOut outgoingQueue,
+      ActiveSettings peerSettings,
+      ActiveSettings localSettings,
+      ActiveStateHandler onActiveStateChanged) {
+    return new StreamHandler._(writer, incomingQueue, outgoingQueue,
+        peerSettings, localSettings, onActiveStateChanged, 2, -1);
+  }
+
+  void onTerminated(exception) {
+    _openStreams.values.toList().forEach((stream) =>
+        _closeStreamAbnormally(stream, exception, propagateException: true));
+    startClosing();
+  }
+
+  void forceDispatchIncomingMessages() {
+    _openStreams.forEach((int streamId, Http2StreamImpl stream) {
+      stream.incomingQueue.forceDispatchIncomingMessages();
+    });
+  }
+
+  Stream<TransportStream> get incomingStreams => _newStreamsC.stream;
+
+  List<TransportStream> get openStreams => _openStreams.values.toList();
+
+  void processInitialWindowSizeSettingChange(int difference) {
+    // If the initialFlowWindow size was changed via a SettingsFrame, all
+    // existing streams must be updated to reflect this change.
+    _openStreams.values.forEach((Http2StreamImpl stream) {
+      stream.windowHandler.processInitialWindowSizeSettingChange(difference);
+    });
+  }
+
+  void processGoawayFrame(GoawayFrame frame) {
+    var lastStreamId = frame.lastStreamId;
+    var streamIds = _openStreams.keys
+        .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id))
+        .toList();
+    for (int id in streamIds) {
+      var exception = new StreamException(
+          id,
+          'Remote end was telling us to stop. This stream was not processed '
+          'and can therefore be retried (on a new connection).');
+      _closeStreamIdAbnormally(id, exception, propagateException: true);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //// New local/remote Stream handling
+  ////////////////////////////////////////////////////////////////////////////
+
+  bool _isPeerInitiatedStream(int streamId) {
+    bool isServerStreamId = streamId.isEven;
+    bool isLocalStream = isServerStreamId == isServer;
+    return !isLocalStream;
+  }
+
+  Http2StreamImpl newStream(List<Header> headers, {bool endStream: false}) {
+    return ensureNotTerminatedSync(() {
+      var stream = newLocalStream();
+      _sendHeaders(stream, headers, endStream: endStream);
+      return stream;
+    });
+  }
+
+  Http2StreamImpl newLocalStream() {
+    return ensureNotTerminatedSync(() {
+      assert(_canCreateNewStream());
+
+      if (MAX_STREAM_ID < nextStreamId) {
+        throw new StateError(
+            'Cannot create new streams, since a wrap around would happen.');
+      }
+      int streamId = nextStreamId;
+      nextStreamId += 2;
+      return _newStreamInternal(streamId);
+    });
+  }
+
+  Http2StreamImpl newRemoteStream(int remoteStreamId) {
+    return ensureNotTerminatedSync(() {
+      assert(remoteStreamId <= MAX_STREAM_ID);
+      // NOTE: We cannot enforce that a new stream id is 2 higher than the last
+      // used stream id. Meaning there can be "holes" in the sense that stream
+      // ids are not used:
+      //
+      // http/2 spec:
+      //   The first use of a new stream identifier implicitly closes all
+      //   streams in the "idle" state that might have been initiated by that
+      //   peer with a lower-valued stream identifier.  For example, if a client
+      //   sends a HEADERS frame on stream 7 without ever sending a frame on
+      //   stream 5, then stream 5 transitions to the "closed" state when the
+      //   first frame for stream 7 is sent or received.
+
+      if (remoteStreamId <= lastRemoteStreamId) {
+        throw new ProtocolException('Remote tried to open new stream which is '
+            'not in "idle" state.');
+      }
+
+      bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0;
+      assert(!sameDirection);
+
+      lastRemoteStreamId = remoteStreamId;
+      return _newStreamInternal(remoteStreamId);
+    });
+  }
+
+  Http2StreamImpl _newStreamInternal(int streamId) {
+    // For each new stream we must:
+    //   - setup sending/receiving [Window]s with correct initial size
+    //   - setup sending/receiving WindowHandlers which take care of
+    //     updating the windows.
+    //   - setup incoming/outgoing stream queues, which buffer data
+    //     that is not handled by
+    //       * the application [incoming]
+    //       * the underlying transport [outgoing]
+    //   - register incoming stream queue in connection-level queue
+
+    var outgoingStreamWindow =
+        new Window(initialSize: _peerSettings.initialWindowSize);
+
+    var incomingStreamWindow =
+        new Window(initialSize: _localSettings.initialWindowSize);
+
+    var windowOutHandler =
+        new OutgoingStreamWindowHandler(outgoingStreamWindow);
+
+    var windowInHandler = new IncomingWindowHandler.stream(
+        _frameWriter, incomingStreamWindow, streamId);
+
+    var streamQueueIn = new StreamMessageQueueIn(windowInHandler);
+    var streamQueueOut =
+        new StreamMessageQueueOut(streamId, windowOutHandler, outgoingQueue);
+
+    incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn);
+
+    var _outgoingC = new StreamController<StreamMessage>();
+    var stream = new Http2StreamImpl(
+        streamQueueIn,
+        streamQueueOut,
+        _outgoingC,
+        streamId,
+        windowOutHandler,
+        this._canPush,
+        this._push,
+        this._terminateStream);
+    final wasIdle = _openStreams.isEmpty;
+    _openStreams[stream.id] = stream;
+
+    _setupOutgoingMessageHandling(stream);
+
+    // Handle incoming stream cancellation. RST is only sent when streamQueueOut
+    // has been closed because RST make the stream 'closed'.
+    streamQueueIn.onCancel.then((_) {
+      // If our side is done sending data, i.e. we have enqueued the
+      // end-of-stream in the outgoing message queue, but the remote end is
+      // still sending us data, despite us not being interested in it, we will
+      // reset the stream.
+      if (stream.state == StreamState.HalfClosedLocal) {
+        stream.outgoingQueue.enqueueMessage(
+            new ResetStreamMessage(stream.id, ErrorCode.CANCEL));
+      }
+    });
+
+    // NOTE: We are not interested whether the streams were normally finished
+    // or abnormally terminated. Therefore we use 'catchError((_) {})'!
+    var streamDone = [streamQueueIn.done, streamQueueOut.done];
+    Future.wait(streamDone).catchError((_) {}).whenComplete(() {
+      _cleanupClosedStream(stream);
+    });
+
+    if (wasIdle) {
+      _onActiveStateChanged(true);
+    }
+
+    return stream;
+  }
+
+  bool _canPush(Http2StreamImpl stream) {
+    bool openState = (stream.state == StreamState.Open ||
+        stream.state == StreamState.HalfClosedRemote);
+    bool pushEnabled = this._peerSettings.enablePush;
+    return openState &&
+        pushEnabled &&
+        _canCreateNewStream() &&
+        !_ranOutOfStreamIds();
+  }
+
+  ServerTransportStream _push(
+      Http2StreamImpl stream, List<Header> requestHeaders) {
+    if (stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedRemote) {
+      throw new StateError('Cannot push based on a stream that is neither open '
+          'nor half-closed-remote.');
+    }
+
+    if (!_peerSettings.enablePush) {
+      throw new StateError('Client did disable server pushes.');
+    }
+
+    if (!_canCreateNewStream()) {
+      throw new StateError('Maximum number of streams reached.');
+    }
+
+    if (_ranOutOfStreamIds()) {
+      throw new StateError('There are no more stream ids left. Please use a '
+          'new connection.');
+    }
+
+    Http2StreamImpl pushStream = newLocalStream();
+
+    // NOTE: Since there was no real request from the client, we simulate it
+    // by adding a synthetic `endStream = true` Data message into the incoming
+    // queue.
+    _changeState(pushStream, StreamState.ReservedLocal);
+    // TODO: We should wait for us to send the headers frame before doing this
+    // transition.
+    _changeState(pushStream, StreamState.HalfClosedRemote);
+    pushStream.incomingQueue
+        .enqueueMessage(new DataMessage(stream.id, const <int>[], true));
+
+    _frameWriter.writePushPromiseFrame(
+        stream.id, pushStream.id, requestHeaders);
+
+    return pushStream;
+  }
+
+  void _terminateStream(Http2StreamImpl stream) {
+    if (stream.state == StreamState.Open ||
+        stream.state == StreamState.HalfClosedLocal ||
+        stream.state == StreamState.HalfClosedRemote ||
+        stream.state == StreamState.ReservedLocal ||
+        stream.state == StreamState.ReservedRemote) {
+      _frameWriter.writeRstStreamFrame(stream.id, ErrorCode.CANCEL);
+      _closeStreamAbnormally(stream, null, propagateException: false);
+    }
+  }
+
+  void _setupOutgoingMessageHandling(Http2StreamImpl stream) {
+    stream._outgoingCSubscription =
+        stream._outgoingC.stream.listen((StreamMessage msg) {
+      if (!wasTerminated) {
+        _handleNewOutgoingMessage(stream, msg);
+      }
+    }, onError: (error, stack) {
+      if (!wasTerminated) {
+        stream.terminate();
+      }
+    }, onDone: () {
+      if (!wasTerminated) {
+        // Stream should already have been closed by the last frame, but we
+        // allow multiple close calls, just to make sure.
+        _handleOutgoingClose(stream);
+      }
+    });
+    stream.outgoingQueue.bufferIndicator.bufferEmptyEvents.listen((_) {
+      if (stream._outgoingCSubscription.isPaused) {
+        stream._outgoingCSubscription.resume();
+      }
+    });
+  }
+
+  void _handleNewOutgoingMessage(Http2StreamImpl stream, StreamMessage msg) {
+    if (stream.state == StreamState.Idle) {
+      if (msg is! HeadersStreamMessage) {
+        var exception = new TransportException(
+            'The first message on a stream needs to be a headers frame.');
+        _closeStreamAbnormally(stream, exception);
+        return;
+      }
+      _changeState(stream, StreamState.Open);
+    }
+
+    if (msg is DataStreamMessage) {
+      _sendData(stream, msg.bytes, endStream: msg.endStream);
+    } else if (msg is HeadersStreamMessage) {
+      _sendHeaders(stream, msg.headers, endStream: msg.endStream);
+    }
+
+    if (stream.outgoingQueue.bufferIndicator.wouldBuffer &&
+        !stream._outgoingCSubscription.isPaused) {
+      stream._outgoingCSubscription.pause();
+    }
+  }
+
+  void _handleOutgoingClose(Http2StreamImpl stream) {
+    // We allow multiple close calls.
+    if (stream.state != StreamState.HalfClosedLocal &&
+        stream.state != StreamState.Closed &&
+        stream.state != StreamState.Terminated) {
+      _sendData(stream, const [], endStream: true);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //// Process incoming stream frames
+  ////////////////////////////////////////////////////////////////////////////
+
+  void processStreamFrame(ConnectionState connectionState, Frame frame) {
+    try {
+      _processStreamFrameInternal(connectionState, frame);
+    } on StreamClosedException catch (exception) {
+      _frameWriter.writeRstStreamFrame(
+          exception.streamId, ErrorCode.STREAM_CLOSED);
+      _closeStreamIdAbnormally(exception.streamId, exception);
+    } on StreamException catch (exception) {
+      _frameWriter.writeRstStreamFrame(
+          exception.streamId, ErrorCode.INTERNAL_ERROR);
+      _closeStreamIdAbnormally(exception.streamId, exception);
+    }
+  }
+
+  void _processStreamFrameInternal(
+      ConnectionState connectionState, Frame frame) {
+    // If we initiated a close of the connection and the received frame belongs
+    // to a stream id which is higher than the last peer-initiated stream we
+    // processed, we'll ignore it.
+    // http/2 spec:
+    //     After sending a GOAWAY frame, the sender can discard frames for
+    //     streams initiated by the receiver with identifiers higher than the
+    //     identified last stream. However, any frames that alter connection
+    //     state cannot be completely ignored. For instance, HEADERS,
+    //     PUSH_PROMISE, and CONTINUATION frames MUST be minimally processed to
+    //     ensure the state maintained for header compression is consistent
+    //     (see Section 4.3); similarly, DATA frames MUST be counted toward
+    //     the connection flow-control window. Failure to process these
+    //     frames can cause flow control or header compression state to become
+    //     unsynchronized.
+    if (connectionState.activeFinishing &&
+        _isPeerInitiatedStream(frame.header.streamId) &&
+        frame.header.streamId > highestPeerInitiatedStream) {
+      // Even if the frame will be ignored, we still need to process it in a
+      // minimal way to ensure the connection window will be updated.
+      if (frame is DataFrame) {
+        incomingQueue.processIgnoredDataFrame(frame);
+      }
+      return null;
+    }
+
+    // TODO: Consider splitting this method into client/server handling.
+    return ensureNotTerminatedSync(() {
+      var stream = _openStreams[frame.header.streamId];
+      if (stream == null) {
+        bool frameBelongsToIdleStream() {
+          int streamId = frame.header.streamId;
+          bool isServerStreamId = frame.header.streamId.isEven;
+          bool isLocalStream = isServerStreamId == isServer;
+          bool isIdleStream = isLocalStream
+              ? streamId >= nextStreamId
+              : streamId > lastRemoteStreamId;
+          return isIdleStream;
+        }
+
+        if (_isPeerInitiatedStream(frame.header.streamId)) {
+          // Update highest stream id we received and processed (we update it
+          // before processing, so if it was an error, the client will not
+          // retry it).
+          _highestStreamIdReceived =
+              max(_highestStreamIdReceived, frame.header.streamId);
+        }
+
+        if (frame is HeadersFrame) {
+          if (isServer) {
+            Http2StreamImpl newStream = newRemoteStream(frame.header.streamId);
+            _changeState(newStream, StreamState.Open);
+
+            _handleHeadersFrame(newStream, frame);
+            _newStreamsC.add(newStream);
+          } else {
+            // A server cannot open new streams to the client. The only way
+            // for a server to start a new stream is via a PUSH_PROMISE_FRAME.
+            throw new ProtocolException(
+                'HTTP/2 clients cannot receive HEADER_FRAMEs as a connection'
+                'attempt.');
+          }
+        } else if (frame is WindowUpdateFrame) {
+          if (frameBelongsToIdleStream()) {
+            // We treat this as a protocol error even though not enforced
+            // or specified by the HTTP/2 spec.
+            throw new ProtocolException(
+                'Got a WINDOW_UPDATE_FRAME for an "idle" stream id.');
+          } else {
+            // We must be able to receive window update frames for streams that
+            // have been already closed. The specification does not mention
+            // what happens if the streamId is belonging to an "idle" / unused
+            // stream.
+          }
+        } else if (frame is RstStreamFrame) {
+          if (frameBelongsToIdleStream()) {
+            // [RstFrame]s for streams which haven't been established (known as
+            // idle streams) must be treated as a connection error.
+            throw new ProtocolException(
+                'Got a RST_STREAM_FRAME for an "idle" stream id.');
+          } else {
+            // [RstFrame]s for already dead (known as "closed") streams should
+            // be ignored. (If the stream was in "HalfClosedRemote" and we did
+            // send an endStream=true, it will be removed from the stream set).
+          }
+        } else if (frame is PriorityFrame) {
+          // http/2 spec:
+          //     The PRIORITY frame can be sent for a stream in the "idle" or
+          //     "closed" states. This allows for the reprioritization of a
+          //     group of dependent streams by altering the priority of an
+          //     unused or closed parent stream.
+          //
+          // As long as we do not handle stream priorities, we can safely ignore
+          // such frames on idle streams.
+          //
+          // NOTE: Firefox for example sends [PriorityFrame]s even without
+          // opening any streams (e.g. streams 3,5,7,9,11 [PriorityFrame]s and
+          // stream 13 is the first real stream opened by a [HeadersFrame].
+          //
+          // TODO: When implementing priorities for HTTP/2 streams, these frames
+          // need to be taken into account.
+        } else if (frame is PushPromiseFrame) {
+          throw new ProtocolException('Cannot push on a non-existent stream '
+              '(stream ${frame.header.streamId} does not exist)');
+        } else {
+          throw new StreamClosedException(
+              frame.header.streamId,
+              'No open stream found and was not a headers frame opening a '
+              'new stream.');
+        }
+      } else {
+        if (frame is HeadersFrame) {
+          _handleHeadersFrame(stream, frame);
+        } else if (frame is DataFrame) {
+          _handleDataFrame(stream, frame);
+        } else if (frame is PushPromiseFrame) {
+          _handlePushPromiseFrame(stream, frame);
+        } else if (frame is WindowUpdateFrame) {
+          _handleWindowUpdate(stream, frame);
+        } else if (frame is RstStreamFrame) {
+          _handleRstFrame(stream, frame);
+        } else {
+          throw new ProtocolException(
+              'Unsupported frame type ${frame.runtimeType}.');
+        }
+      }
+    });
+  }
+
+  void _handleHeadersFrame(Http2StreamImpl stream, HeadersFrame frame) {
+    if (stream.state == StreamState.ReservedRemote) {
+      _changeState(stream, StreamState.HalfClosedLocal);
+    }
+
+    if (stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedLocal) {
+      throw new StreamClosedException(
+          stream.id, 'Expected open state (was: ${stream.state}).');
+    }
+
+    incomingQueue.processHeadersFrame(frame);
+
+    if (frame.hasEndStreamFlag) _handleEndOfStreamRemote(stream);
+  }
+
+  void _handleDataFrame(Http2StreamImpl stream, DataFrame frame) {
+    if (stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedLocal) {
+      throw new StreamClosedException(
+          stream.id, 'Expected open state (was: ${stream.state}).');
+    }
+
+    incomingQueue.processDataFrame(frame);
+
+    if (frame.hasEndStreamFlag) _handleEndOfStreamRemote(stream);
+  }
+
+  void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) {
+    if (stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedLocal) {
+      throw new ProtocolException(
+          'Expected open state (was: ${stream.state}).');
+    }
+
+    var pushedStream = newRemoteStream(frame.promisedStreamId);
+    _changeState(pushedStream, StreamState.ReservedRemote);
+
+    incomingQueue.processPushPromiseFrame(frame, pushedStream);
+  }
+
+  void _handleWindowUpdate(Http2StreamImpl stream, WindowUpdateFrame frame) {
+    stream.windowHandler.processWindowUpdate(frame);
+  }
+
+  void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) {
+    stream._handleTerminated(frame.errorCode);
+    var exception = new StreamTransportException(
+        'Stream was terminated by peer (errorCode: ${frame.errorCode}).');
+    _closeStreamAbnormally(stream, exception, propagateException: true);
+  }
+
+  void _handleEndOfStreamRemote(Http2StreamImpl stream) {
+    if (stream.state == StreamState.Open) {
+      _changeState(stream, StreamState.HalfClosedRemote);
+    } else if (stream.state == StreamState.HalfClosedLocal) {
+      _changeState(stream, StreamState.Closed);
+      // TODO: We have to make sure that we
+      //   - remove the stream for data structures which only care about the
+      //     state
+      //   - keep the stream in data structures which need to be emptied
+      //     (e.g. MessageQueues which are not empty yet).
+      _openStreams.remove(stream.id);
+    } else {
+      throw new StateError(
+          'Got an end-of-stream from the remote end, but this stream is '
+          'neither in the Open nor in the HalfClosedLocal state. '
+          'This should never happen.');
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //// Process outgoing stream messages
+  ////////////////////////////////////////////////////////////////////////////
+
+  void _sendHeaders(Http2StreamImpl stream, List<Header> headers,
+      {bool endStream: false}) {
+    if (stream.state != StreamState.Idle &&
+        stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedRemote) {
+      throw new StateError('Idle state expected.');
+    }
+
+    stream.outgoingQueue
+        .enqueueMessage(new HeadersMessage(stream.id, headers, endStream));
+
+    if (stream.state == StreamState.Idle) {
+      _changeState(stream, StreamState.Open);
+    }
+
+    if (endStream) {
+      _endStream(stream);
+    }
+  }
+
+  void _sendData(Http2StreamImpl stream, List<int> data,
+      {bool endStream: false}) {
+    if (stream.state != StreamState.Open &&
+        stream.state != StreamState.HalfClosedRemote) {
+      throw new StateError('Open state expected (was: ${stream.state}).');
+    }
+
+    stream.outgoingQueue
+        .enqueueMessage(new DataMessage(stream.id, data, endStream));
+
+    if (endStream) {
+      _endStream(stream);
+    }
+  }
+
+  void _endStream(Http2StreamImpl stream) {
+    if (stream.state == StreamState.Open) {
+      _changeState(stream, StreamState.HalfClosedLocal);
+    } else if (stream.state == StreamState.HalfClosedRemote) {
+      _changeState(stream, StreamState.Closed);
+    } else {
+      throw new StateError(
+          'Invalid state transition. This should never happen.');
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //// Stream closing
+  ////////////////////////////////////////////////////////////////////////////
+
+  void _cleanupClosedStream(Http2StreamImpl stream) {
+    // NOTE: This function should only be called once
+    //     * all incoming data has been delivered to the application
+    //     * all outgoing data has been added to the connection queue.
+    incomingQueue.removeStreamMessageQueue(stream.id);
+    _openStreams.remove(stream.id);
+    if (stream.state != StreamState.Terminated) {
+      _changeState(stream, StreamState.Terminated);
+    }
+    if (_openStreams.isEmpty) {
+      _onActiveStateChanged(false);
+    }
+    onCheckForClose();
+  }
+
+  void _closeStreamIdAbnormally(int streamId, Exception exception,
+      {bool propagateException: false}) {
+    Http2StreamImpl stream = _openStreams[streamId];
+    if (stream != null) {
+      _closeStreamAbnormally(stream, exception,
+          propagateException: propagateException);
+    }
+  }
+
+  void _closeStreamAbnormally(Http2StreamImpl stream, Object exception,
+      {bool propagateException: false}) {
+    incomingQueue.removeStreamMessageQueue(stream.id);
+
+    if (stream.state != StreamState.Terminated) {
+      _changeState(stream, StreamState.Terminated);
+    }
+    stream.incomingQueue.terminate(propagateException ? exception : null);
+    stream._outgoingCSubscription.cancel();
+    stream._outgoingC.close();
+
+    // NOTE: we're not adding an error here.
+    stream.outgoingQueue.terminate();
+
+    onCheckForClose();
+  }
+
+  void onClosing() {
+    _newStreamsC.close();
+  }
+
+  void onCheckForClose() {
+    if (isClosing && _openStreams.isEmpty) {
+      closeWithValue();
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //// State transitioning & Counting of active streams
+  ////////////////////////////////////////////////////////////////////////////
+
+  /// The number of streams which we initiated and which are in one of the open
+  /// states (i.e. [StreamState.Open], [StreamState.HalfClosedLocal] or
+  /// [StreamState.HalfClosedRemote])
+  int _numberOfActiveStreams = 0;
+
+  bool _canCreateNewStream() {
+    int limit = _peerSettings.maxConcurrentStreams;
+    return limit == null || _numberOfActiveStreams < limit;
+  }
+
+  bool _ranOutOfStreamIds() {
+    return nextStreamId > MAX_STREAM_ID;
+  }
+
+  void _changeState(Http2StreamImpl stream, StreamState to) {
+    StreamState from = stream.state;
+
+    // In checked mode we'll test that the state transition is allowed.
+    assert((from == StreamState.Idle && to == StreamState.ReservedLocal) ||
+        (from == StreamState.Idle && to == StreamState.ReservedRemote) ||
+        (from == StreamState.Idle && to == StreamState.Open) ||
+        (from == StreamState.Open && to == StreamState.HalfClosedLocal) ||
+        (from == StreamState.Open && to == StreamState.HalfClosedRemote) ||
+        (from == StreamState.Open && to == StreamState.Closed) ||
+        (from == StreamState.HalfClosedLocal && to == StreamState.Closed) ||
+        (from == StreamState.HalfClosedRemote && to == StreamState.Closed) ||
+        (from == StreamState.ReservedLocal &&
+            to == StreamState.HalfClosedRemote) ||
+        (from == StreamState.ReservedLocal && to == StreamState.Closed) ||
+        (from == StreamState.ReservedRemote && to == StreamState.Closed) ||
+        (from == StreamState.ReservedRemote &&
+            to == StreamState.HalfClosedLocal) ||
+        (from != StreamState.Terminated && to == StreamState.Terminated));
+
+    // If we initiated the stream and it became "open" or "closed" we need to
+    // update the [_numberOfActiveStreams] counter.
+    if (_didInitiateStream(stream)) {
+      // NOTE: We wait until the stream is completely done.
+      // (If we waited only until `StreamState.Closed` then we might still have
+      //  the endStream header/data message buffered, but not yet sent out).
+      switch (stream.state) {
+        case StreamState.ReservedLocal:
+        case StreamState.ReservedRemote:
+        case StreamState.Idle:
+          if (to == StreamState.Open ||
+              to == StreamState.HalfClosedLocal ||
+              to == StreamState.HalfClosedRemote) {
+            _numberOfActiveStreams++;
+          }
+          break;
+        case StreamState.Open:
+        case StreamState.HalfClosedLocal:
+        case StreamState.HalfClosedRemote:
+        case StreamState.Closed:
+          if (to == StreamState.Terminated) {
+            _numberOfActiveStreams--;
+          }
+          break;
+        case StreamState.Terminated:
+          // There is nothing to do here.
+          break;
+      }
+    }
+    stream.state = to;
+  }
+
+  bool _didInitiateStream(Http2StreamImpl stream) {
+    int id = stream.id;
+    return (isServer && id.isEven) || (!isServer && id.isOdd);
+  }
+}
diff --git a/http2/lib/src/sync_errors.dart b/http2/lib/src/sync_errors.dart
new file mode 100644
index 0000000..f8afa05
--- /dev/null
+++ b/http2/lib/src/sync_errors.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2015, 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 http2.src.sync_errors;
+
+class ProtocolException implements Exception {
+  final String _message;
+
+  ProtocolException(this._message);
+
+  String toString() => 'ProtocolError: $_message';
+}
+
+class FlowControlException implements Exception {
+  final String _message;
+
+  FlowControlException(this._message);
+
+  String toString() => 'FlowControlException: $_message';
+}
+
+class FrameSizeException implements Exception {
+  final String _message;
+
+  FrameSizeException(this._message);
+
+  String toString() => 'FrameSizeException: $_message';
+}
+
+class TerminatedException implements Exception {
+  String toString() => 'TerminatedException: The object has been terminated.';
+}
+
+class StreamException implements Exception {
+  final String _message;
+  final int streamId;
+
+  StreamException(this.streamId, this._message);
+
+  String toString() => 'StreamException(stream id: $streamId): $_message';
+}
+
+class StreamClosedException extends StreamException {
+  StreamClosedException(int streamId, [String message = ''])
+      : super(streamId, message);
+
+  String toString() => 'StreamClosedException(stream id: $streamId): $_message';
+}
diff --git a/http2/lib/src/testing/client.dart b/http2/lib/src/testing/client.dart
new file mode 100644
index 0000000..a7d9dc4
--- /dev/null
+++ b/http2/lib/src/testing/client.dart
@@ -0,0 +1,152 @@
+// Copyright (c) 2015, 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 http2.client;
+
+import 'dart:async';
+import 'dart:convert' show ascii;
+import 'dart:io';
+
+import '../../transport.dart';
+
+class Request {
+  final String method;
+  final Uri uri;
+
+  Request(this.method, this.uri);
+}
+
+class Response {
+  final Map<String, List<String>> headers;
+  final Stream<List<int>> stream;
+  final Stream<ServerPush> serverPushes;
+
+  Response(this.headers, this.stream, this.serverPushes);
+}
+
+class ServerPush {
+  final Map<String, List<String>> requestHeaders;
+  final Future<Response> response;
+
+  ServerPush(this.requestHeaders, this.response);
+}
+
+class ClientConnection {
+  final ClientTransportConnection connection;
+
+  /// Assumes the protocol on [socket] was negogiated to be http/2.
+  ///
+  /// If [settings] are omitted, the default [ClientSettings] will be used.
+  ClientConnection(Socket socket, {ClientSettings settings})
+      : connection =
+            new ClientTransportConnection.viaSocket(socket, settings: settings);
+
+  Future<Response> makeRequest(Request request) {
+    var path = request.uri.path;
+    if (path.isEmpty) path = '/';
+
+    var headers = [
+      new Header.ascii(':method', request.method),
+      new Header.ascii(':path', path),
+      new Header.ascii(':scheme', request.uri.scheme),
+      new Header.ascii(':authority', '${request.uri.host}'),
+    ];
+
+    return _handleStream(connection.makeRequest(headers, endStream: true));
+  }
+
+  Future close() {
+    return connection.finish();
+  }
+
+  Future<Response> _handleStream(ClientTransportStream stream) {
+    var completer = new Completer<Response>();
+    bool isFirst = true;
+    var controller = new StreamController<List<int>>();
+    var serverPushController = new StreamController<ServerPush>(sync: true);
+    stream.incomingMessages.listen((StreamMessage msg) {
+      if (isFirst) {
+        isFirst = false;
+        var headerMap = _convertHeaders((msg as HeadersStreamMessage).headers);
+        completer.complete(new Response(
+            headerMap, controller.stream, serverPushController.stream));
+      } else {
+        controller.add((msg as DataStreamMessage).bytes);
+      }
+    }, onDone: controller.close);
+    _handlePeerPushes(stream.peerPushes).pipe(serverPushController);
+    return completer.future;
+  }
+
+  Stream<ServerPush> _handlePeerPushes(
+      Stream<TransportStreamPush> serverPushes) {
+    var pushesController = new StreamController<ServerPush>();
+    serverPushes.listen((TransportStreamPush push) {
+      var responseCompleter = new Completer<Response>();
+      var serverPush = new ServerPush(
+          _convertHeaders(push.requestHeaders), responseCompleter.future);
+
+      pushesController.add(serverPush);
+
+      bool isFirst = true;
+      var dataController = new StreamController<List<int>>();
+      push.stream.incomingMessages.listen((StreamMessage msg) {
+        if (isFirst) {
+          isFirst = false;
+          var headerMap =
+              _convertHeaders((msg as HeadersStreamMessage).headers);
+          var response = new Response(
+              headerMap, dataController.stream, new Stream.fromIterable([]));
+          responseCompleter.complete(response);
+        } else {
+          dataController.add((msg as DataStreamMessage).bytes);
+        }
+      }, onDone: dataController.close);
+    }, onDone: pushesController.close);
+    return pushesController.stream;
+  }
+
+  Map<String, List<String>> _convertHeaders(List<Header> headers) {
+    var headerMap = <String, List<String>>{};
+    for (var header in headers) {
+      headerMap
+          .putIfAbsent(ascii.decode(header.name), () => [])
+          .add(ascii.decode(header.value));
+    }
+    return headerMap;
+  }
+}
+
+/// Tries to connect to [uri] via a secure socket connection and establishes a
+/// http/2 connection.
+///
+/// If [allowServerPushes] is `true`, server pushes need to be handled by the
+/// client. The maximum number of concurrent server pushes can be configured via
+/// [maxConcurrentPushes] (default is `null` meaning no limit).
+Future<ClientConnection> connect(Uri uri,
+    {bool allowServerPushes: false, int maxConcurrentPushes}) async {
+  const List<String> Http2AlpnProtocols = const <String>[
+    'h2-14',
+    'h2-15',
+    'h2-16',
+    'h2-17',
+    'h2'
+  ];
+
+  bool useSSL = uri.scheme == 'https';
+  var settings = new ClientSettings(
+      concurrentStreamLimit: maxConcurrentPushes,
+      allowServerPushes: allowServerPushes);
+  if (useSSL) {
+    SecureSocket socket = await SecureSocket.connect(uri.host, uri.port,
+        supportedProtocols: Http2AlpnProtocols);
+    if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) {
+      throw new Exception('Server does not support HTTP/2.');
+    }
+    return new ClientConnection(socket, settings: settings);
+  } else {
+    Socket socket = await Socket.connect(uri.host, uri.port);
+    return new ClientConnection(socket, settings: settings);
+  }
+}
diff --git a/http2/lib/src/testing/debug.dart b/http2/lib/src/testing/debug.dart
new file mode 100644
index 0000000..b4e0b2c
--- /dev/null
+++ b/http2/lib/src/testing/debug.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2015, 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 http2.debug;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import '../../transport.dart';
+import '../connection_preface.dart';
+import '../frames/frames.dart';
+import '../settings/settings.dart';
+
+final jsonEncoder = new JsonEncoder.withIndent('  ');
+
+TransportConnection debugPrintingConnection(Socket socket,
+    {bool isServer: true, bool verbose: true}) {
+  TransportConnection connection;
+
+  var incoming = decodeVerbose(socket, isServer, verbose: verbose);
+  var outgoing = decodeOutgoingVerbose(socket, isServer, verbose: verbose);
+  if (isServer) {
+    connection = new ServerTransportConnection.viaStreams(incoming, outgoing);
+  } else {
+    connection = new ClientTransportConnection.viaStreams(incoming, outgoing);
+  }
+  return connection;
+}
+
+Stream<List<int>> decodeVerbose(Stream<List<int>> inc, bool isServer,
+    {bool verbose: true}) {
+  String name = isServer ? 'server' : 'client';
+
+  var sc = new StreamController<List<int>>();
+  var sDebug = new StreamController<List<int>>();
+
+  _pipeAndCopy(inc, sc, sDebug);
+
+  if (!isServer) {
+    _decodeFrames(sDebug.stream).listen((frame) {
+      print('[$name/stream:${frame.header.streamId}] '
+          'Incoming ${frame.runtimeType}:');
+      if (verbose) {
+        print(jsonEncoder.convert(frame.toJson()));
+        print('');
+      }
+    }, onError: (e, s) {
+      print('[$name] Stream error: $e.');
+    }, onDone: () {
+      print('[$name] Closed.');
+    });
+  } else {
+    var s3 = readConnectionPreface(sDebug.stream);
+    _decodeFrames(s3).listen((frame) {
+      print('[$name/stream:${frame.header.streamId}] '
+          'Incoming ${frame.runtimeType}:');
+      if (verbose) {
+        print(jsonEncoder.convert(frame.toJson()));
+        print('');
+      }
+    }, onError: (e, s) {
+      print('[$name] Stream error: $e.');
+    }, onDone: () {
+      print('[$name] Closed.');
+    });
+  }
+
+  return sc.stream;
+}
+
+StreamSink<List<int>> decodeOutgoingVerbose(
+    StreamSink<List<int>> sink, bool isServer,
+    {bool verbose: true}) {
+  String name = isServer ? 'server' : 'client';
+
+  var proxySink = new StreamController<List<int>>();
+  var copy = new StreamController<List<int>>();
+
+  if (!isServer) {
+    _decodeFrames(readConnectionPreface(copy.stream)).listen((Frame frame) {
+      print('[$name/stream:${frame.header.streamId}] '
+          'Outgoing ${frame.runtimeType}:');
+      if (verbose) {
+        print(jsonEncoder.convert(frame.toJson()));
+        print('');
+      }
+    }, onError: (e, s) {
+      print('[$name] Outgoing stream error: $e');
+    }, onDone: () {
+      print('[$name] Closing.');
+    });
+  } else {
+    _decodeFrames(copy.stream).listen((Frame frame) {
+      print('[$name/stream:${frame.header.streamId}] '
+          'Outgoing ${frame.runtimeType}:');
+      if (verbose) {
+        print(jsonEncoder.convert(frame.toJson()));
+        print('');
+      }
+    }, onError: (e, s) {
+      print('[$name] Outgoing stream error: $e');
+    }, onDone: () {
+      print('[$name] Closing.');
+      proxySink.close();
+    });
+  }
+
+  _pipeAndCopy(proxySink.stream, sink, copy);
+
+  return proxySink;
+}
+
+Stream<Frame> _decodeFrames(Stream<List<int>> bytes) {
+  var settings = new ActiveSettings();
+  var decoder = new FrameReader(bytes, settings);
+  return decoder.startDecoding();
+}
+
+Future _pipeAndCopy(Stream<List<int>> from, StreamSink to, StreamSink to2) {
+  var c = new Completer();
+  from.listen((List<int> data) {
+    to.add(data);
+    to2.add(data);
+  }, onError: (e, StackTrace s) {
+    to.addError(e, s);
+    to2.addError(e, s);
+  }, onDone: () {
+    Future.wait([to.close(), to2.close()])
+        .then(c.complete)
+        .catchError(c.completeError);
+  });
+  return c.future;
+}
+
+void print(String s) {
+  stderr.writeln(s);
+}
diff --git a/http2/lib/transport.dart b/http2/lib/transport.dart
new file mode 100644
index 0000000..c0ec7bd
--- /dev/null
+++ b/http2/lib/transport.dart
@@ -0,0 +1,236 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'src/connection.dart';
+import 'src/hpack/hpack.dart' show Header;
+
+export 'src/hpack/hpack.dart' show Header;
+
+typedef void ActiveStateHandler(bool isActive);
+
+/// Settings for a [TransportConnection].
+abstract class Settings {
+  /// The maximum number of concurrent streams the remote end can open
+  /// (defaults to being unlimited).
+  final int concurrentStreamLimit;
+
+  /// The default stream window size the remote peer can use when creating new
+  /// streams (defaults to 65535 bytes).
+  final int streamWindowSize;
+
+  const Settings({this.concurrentStreamLimit, this.streamWindowSize});
+}
+
+/// Settings for a [TransportConnection] a server can make.
+class ServerSettings extends Settings {
+  const ServerSettings({int concurrentStreamLimit, int streamWindowSize})
+      : super(
+            concurrentStreamLimit: concurrentStreamLimit,
+            streamWindowSize: streamWindowSize);
+}
+
+/// Settings for a [TransportConnection] a client can make.
+class ClientSettings extends Settings {
+  /// Whether the client allows pushes from the server (defaults to false).
+  final bool allowServerPushes;
+
+  const ClientSettings(
+      {int concurrentStreamLimit,
+      int streamWindowSize,
+      this.allowServerPushes: false})
+      : super(
+            concurrentStreamLimit: concurrentStreamLimit,
+            streamWindowSize: streamWindowSize);
+}
+
+/// Represents a HTTP/2 connection.
+abstract class TransportConnection {
+  /// Pings the other end.
+  Future ping();
+
+  /// Sets the active state callback.
+  ///
+  /// This callback is invoked with `true` when the number of active streams
+  /// goes from 0 to 1 (the connection goes from idle to active), and with
+  /// `false` when the number of active streams becomes 0 (the connection goes
+  /// from active to idle).
+  set onActiveStateChanged(ActiveStateHandler callback);
+
+  /// Finish this connection.
+  ///
+  /// No new streams will be accepted or can be created.
+  Future finish();
+
+  /// Terminates this connection forcefully.
+  Future terminate();
+}
+
+abstract class ClientTransportConnection extends TransportConnection {
+  factory ClientTransportConnection.viaSocket(Socket socket,
+          {ClientSettings settings}) =>
+      new ClientTransportConnection.viaStreams(socket, socket,
+          settings: settings);
+
+  factory ClientTransportConnection.viaStreams(
+      Stream<List<int>> incoming, StreamSink<List<int>> outgoing,
+      {ClientSettings settings}) {
+    if (settings == null) settings = const ClientSettings();
+    return new ClientConnection(incoming, outgoing, settings);
+  }
+
+  /// Whether this connection is open and can be used to make new requests
+  /// via [makeRequest].
+  bool get isOpen;
+
+  /// Creates a new outgoing stream.
+  ClientTransportStream makeRequest(List<Header> headers,
+      {bool endStream: false});
+}
+
+abstract class ServerTransportConnection extends TransportConnection {
+  factory ServerTransportConnection.viaSocket(Socket socket,
+      {ServerSettings settings}) {
+    return new ServerTransportConnection.viaStreams(socket, socket,
+        settings: settings);
+  }
+
+  factory ServerTransportConnection.viaStreams(
+      Stream<List<int>> incoming, StreamSink<List<int>> outgoing,
+      {ServerSettings settings:
+          const ServerSettings(concurrentStreamLimit: 1000)}) {
+    if (settings == null) settings = const ServerSettings();
+    return new ServerConnection(incoming, outgoing, settings);
+  }
+
+  /// Incoming HTTP/2 streams.
+  Stream<ServerTransportStream> get incomingStreams;
+}
+
+/// Represents a HTTP/2 stream.
+abstract class TransportStream {
+  /// The id of this stream.
+  ///
+  ///   * odd numbered streams are client streams
+  ///   * even numbered streams are opened from the server
+  int get id;
+
+  /// A stream of data and/or headers from the remote end.
+  Stream<StreamMessage> get incomingMessages;
+
+  /// A sink for writing data and/or headers to the remote end.
+  StreamSink<StreamMessage> get outgoingMessages;
+
+  /// Sets the termination handler on this stream.
+  ///
+  /// The handler will be called if the stream receives an RST_STREAM frame.
+  set onTerminated(void value(int v));
+
+  /// Terminates this HTTP/2 stream in an un-normal way.
+  ///
+  /// For normal termination, one can cancel the [StreamSubscription] from
+  /// `incoming.listen()` and close the `outgoing` [StreamSink].
+  ///
+  /// Terminating this HTTP/2 stream will free up all resources associated with
+  /// it locally and will notify the remote end that this stream is no longer
+  /// used.
+  void terminate();
+
+  // For convenience only.
+  void sendHeaders(List<Header> headers, {bool endStream: false}) {
+    outgoingMessages
+        .add(new HeadersStreamMessage(headers, endStream: endStream));
+    if (endStream) outgoingMessages.close();
+  }
+
+  void sendData(List<int> bytes, {bool endStream: false}) {
+    outgoingMessages.add(new DataStreamMessage(bytes, endStream: endStream));
+    if (endStream) outgoingMessages.close();
+  }
+}
+
+abstract class ClientTransportStream extends TransportStream {
+  /// Streams which the remote end pushed to this endpoint.
+  ///
+  /// If peer pushes were enabled, the client is responsible to either
+  /// handle or reject any peer push.
+  Stream<TransportStreamPush> get peerPushes;
+}
+
+abstract class ServerTransportStream extends TransportStream {
+  /// Whether a method to [push] will succeed. Requirements for this getter to
+  /// return `true` are:
+  ///    * this stream must be in the Open or HalfClosedRemote state
+  ///    * the client needs to have the "enable push" settings enabled
+  ///    * the number of active streams has not reached the maximum
+  bool get canPush;
+
+  /// Pushes a new stream to the remote peer.
+  ServerTransportStream push(List<Header> requestHeaders);
+}
+
+/// Represents a message which can be sent over a HTTP/2 stream.
+abstract class StreamMessage {
+  final bool endStream;
+
+  StreamMessage({bool endStream}) : this.endStream = endStream ?? false;
+}
+
+/// Represents a data message which can be sent over a HTTP/2 stream.
+class DataStreamMessage extends StreamMessage {
+  final List<int> bytes;
+
+  DataStreamMessage(this.bytes, {bool endStream}) : super(endStream: endStream);
+
+  String toString() => 'DataStreamMessage(${bytes.length} bytes)';
+}
+
+/// Represents a headers message which can be sent over a HTTP/2 stream.
+class HeadersStreamMessage extends StreamMessage {
+  final List<Header> headers;
+
+  HeadersStreamMessage(this.headers, {bool endStream})
+      : super(endStream: endStream);
+
+  String toString() => 'HeadersStreamMessage(${headers.length} headers)';
+}
+
+/// Represents a remote stream push.
+class TransportStreamPush {
+  /// The request headers which [stream] is the response to.
+  final List<Header> requestHeaders;
+
+  /// The remote stream push.
+  final ClientTransportStream stream;
+
+  TransportStreamPush(this.requestHeaders, this.stream);
+
+  String toString() =>
+      'TransportStreamPush(${requestHeaders.length} request headers headers)';
+}
+
+/// An exception thrown by the HTTP/2 implementation.
+class TransportException implements Exception {
+  final String message;
+
+  TransportException(this.message);
+
+  String toString() => 'HTTP/2 error: $message';
+}
+
+/// An exception thrown when a HTTP/2 connection error occurred.
+class TransportConnectionException extends TransportException {
+  final int errorCode;
+
+  TransportConnectionException(int errorCode, String details)
+      : errorCode = errorCode,
+        super('Connection error: $details (errorCode: $errorCode)');
+}
+
+/// An exception thrown when a HTTP/2 stream error occured.
+class StreamTransportException extends TransportException {
+  StreamTransportException(String details) : super('Stream error: $details');
+}
diff --git a/http2/manual_test/out_of_stream_ids_test.dart b/http2/manual_test/out_of_stream_ids_test.dart
new file mode 100644
index 0000000..4e22f4b
--- /dev/null
+++ b/http2/manual_test/out_of_stream_ids_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2015, 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.
+
+/// ---------------------------------------------------------------------------
+/// In order to run this test one needs to change the following line in
+/// ../lib/src/streams/stream_handler.dart
+///
+///    -  static const int MAX_STREAM_ID = (1 << 31) - 1;
+///    +  static const int MAX_STREAM_ID = (1 << 5) - 1;
+///
+/// without this patch this test will run for a _long_ time.
+/// ---------------------------------------------------------------------------
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'package:http2/transport.dart';
+import 'package:http2/src/streams/stream_handler.dart';
+
+import '../test/transport_test.dart';
+
+main() {
+  group('transport-test', () {
+    transportTest('client-runs-out-of-stream-ids',
+        (ClientTransportConnection client,
+            ServerTransportConnection server) async {
+      Future serverFun() async {
+        await for (ServerTransportStream stream in server.incomingStreams) {
+          stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true);
+          expect(await stream.incomingMessages.toList(), hasLength(1));
+        }
+        await server.finish();
+      }
+
+      Future clientFun() async {
+        var headers = [new Header.ascii('a', 'b')];
+
+        const kMaxStreamId = StreamHandler.MAX_STREAM_ID;
+        for (int i = 1; i <= kMaxStreamId; i += 2) {
+          var stream = client.makeRequest(headers, endStream: true);
+          var messages = await stream.incomingMessages.toList();
+          expect(messages, hasLength(1));
+        }
+
+        expect(client.isOpen, false);
+        expect(() => client.makeRequest(headers),
+            throwsA(const TypeMatcher<StateError>()));
+
+        await new Future.delayed(const Duration(seconds: 1));
+        await client.finish();
+      }
+
+      var serverFuture = serverFun();
+      var clientFuture = clientFun();
+
+      await serverFuture;
+      await clientFuture;
+    });
+  });
+}
diff --git a/http2/pubspec.yaml b/http2/pubspec.yaml
new file mode 100644
index 0000000..e3e2651
--- /dev/null
+++ b/http2/pubspec.yaml
@@ -0,0 +1,12 @@
+name: http2
+version: 1.0.0
+description: A HTTP/2 implementation in Dart.
+author: Dart Team <misc@dartlang.org>
+homepage: https://github.com/dart-lang/http2
+
+environment:
+  sdk: '>=2.0.0-dev.56.0 <3.0.0'
+
+dev_dependencies:
+  mockito: ^4.0.0
+  test: ^1.2.0