Version 1.22.0-dev.9.1

Cherry-pick 1d6bd5255d14ed822eb0f2e58a7b2013309e9c23 to dev
diff --git a/sdk/lib/io/bytes_builder.dart b/sdk/lib/io/bytes_builder.dart
index 1b61d30..2dca39d 100644
--- a/sdk/lib/io/bytes_builder.dart
+++ b/sdk/lib/io/bytes_builder.dart
@@ -83,22 +83,24 @@
   // Start with 1024 bytes.
   static const int _INIT_SIZE = 1024;
 
-  static final _emptyList = new Uint8List(0);
-
   int _length = 0;
   Uint8List _buffer;
 
-  _CopyingBytesBuilder([int initialCapacity = 0])
-      : _buffer = (initialCapacity <= 0)
-                      ? _emptyList
-                      : new Uint8List(_pow2roundup(initialCapacity));
-
   void add(List<int> bytes) {
     int bytesLength = bytes.length;
     if (bytesLength == 0) return;
     int required = _length + bytesLength;
-    if (_buffer.length < required) {
-      _grow(required);
+    if (_buffer == null) {
+      int size = _pow2roundup(required);
+      size = max(size, _INIT_SIZE);
+      _buffer = new Uint8List(size);
+    } else if (_buffer.length < required) {
+      // We will create a list in the range of 2-4 times larger than
+      // required.
+      int size = _pow2roundup(required) * 2;
+      var newBuffer = new Uint8List(size);
+      newBuffer.setRange(0, _buffer.length, _buffer);
+      _buffer = newBuffer;
     }
     assert(_buffer.length >= required);
     if (bytes is Uint8List) {
@@ -111,40 +113,17 @@
     _length = required;
   }
 
-  void addByte(int byte) {
-    if (_buffer.length == _length) {
-      // The grow algorithm always at least doubles.
-      // If we added one to _length it would quadruple unnecessarily.
-      _grow(_length);
-    }
-    assert(_buffer.length > _length);
-    _buffer[_length] = byte;
-    _length++;
-  }
-
-  void _grow(int required) {
-    // We will create a list in the range of 2-4 times larger than
-    // required.
-    int newSize = required * 2;
-    if (newSize < _INIT_SIZE) {
-      newSize = _INIT_SIZE;
-    } else {
-      newSize = _pow2roundup(newSize);
-    }
-    var newBuffer = new Uint8List(newSize);
-    newBuffer.setRange(0, _buffer.length, _buffer);
-    _buffer = newBuffer;
-  }
+  void addByte(int byte) { add([byte]); }
 
   List<int> takeBytes() {
-    if (_length == 0) return _emptyList;
+    if (_buffer == null) return new Uint8List(0);
     var buffer = new Uint8List.view(_buffer.buffer, 0, _length);
     clear();
     return buffer;
   }
 
   List<int> toBytes() {
-    if (_length == 0) return _emptyList;
+    if (_buffer == null) return new Uint8List(0);
     return new Uint8List.fromList(
         new Uint8List.view(_buffer.buffer, 0, _length));
   }
@@ -157,11 +136,10 @@
 
   void clear() {
     _length = 0;
-    _buffer = _emptyList;
+    _buffer = null;
   }
 
-  static int _pow2roundup(int x) {
-    assert(x > 0);
+  int _pow2roundup(int x) {
     --x;
     x |= x >> 1;
     x |= x >> 2;
@@ -188,15 +166,12 @@
     _length += typedBytes.length;
   }
 
-  void addByte(int byte) {
-    _chunks.add(new Uint8List(1)..[0] = byte);
-    _length++;
-  }
+  void addByte(int byte) { add([byte]); }
 
   List<int> takeBytes() {
-    if (_length == 0) return _CopyingBytesBuilder._emptyList;
+    if (_chunks.length == 0) return new Uint8List(0);
     if (_chunks.length == 1) {
-      var buffer = _chunks[0];
+      var buffer = _chunks.single;
       clear();
       return buffer;
     }
@@ -211,7 +186,7 @@
   }
 
   List<int> toBytes() {
-    if (_length == 0) return _CopyingBytesBuilder._emptyList;
+    if (_chunks.length == 0) return new Uint8List(0);
     var buffer = new Uint8List(_length);
     int offset = 0;
     for (var chunk in _chunks) {
diff --git a/sdk/lib/io/http_headers.dart b/sdk/lib/io/http_headers.dart
index 40df48a..3769b3f 100644
--- a/sdk/lib/io/http_headers.dart
+++ b/sdk/lib/io/http_headers.dart
@@ -465,32 +465,42 @@
     _mutable = false;
   }
 
-  void _build(BytesBuilder builder) {
+  int _write(Uint8List buffer, int offset) {
+    void write(List<int> bytes) {
+      int len = bytes.length;
+      for (int i = 0; i < len; i++) {
+        buffer[offset + i] = bytes[i];
+      }
+      offset += len;
+    }
+
+    // Format headers.
     for (String name in _headers.keys) {
       List<String> values = _headers[name];
       bool fold = _foldHeader(name);
       var nameData = name.codeUnits;
-      builder.add(nameData);
-      builder.addByte(_CharCode.COLON);
-      builder.addByte(_CharCode.SP);
+      write(nameData);
+      buffer[offset++] = _CharCode.COLON;
+      buffer[offset++] = _CharCode.SP;
       for (int i = 0; i < values.length; i++) {
         if (i > 0) {
           if (fold) {
-            builder.addByte(_CharCode.COMMA);
-            builder.addByte(_CharCode.SP);
+            buffer[offset++] = _CharCode.COMMA;
+            buffer[offset++] = _CharCode.SP;
           } else {
-            builder.addByte(_CharCode.CR);
-            builder.addByte(_CharCode.LF);
-            builder.add(nameData);
-            builder.addByte(_CharCode.COLON);
-            builder.addByte(_CharCode.SP);
+            buffer[offset++] = _CharCode.CR;
+            buffer[offset++] = _CharCode.LF;
+            write(nameData);
+            buffer[offset++] = _CharCode.COLON;
+            buffer[offset++] = _CharCode.SP;
           }
         }
-        builder.add(values[i].codeUnits);
+        write(values[i].codeUnits);
       }
-      builder.addByte(_CharCode.CR);
-      builder.addByte(_CharCode.LF);
+      buffer[offset++] = _CharCode.CR;
+      buffer[offset++] = _CharCode.LF;
     }
+    return offset;
   }
 
   String toString() {
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index ac7d67a..a965afa 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -574,20 +574,29 @@
   }
 
   void _writeHeader() {
-    BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
+    Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
+    int offset = 0;
+
+    void write(List<int> bytes) {
+      int len = bytes.length;
+      for (int i = 0; i < len; i++) {
+        buffer[offset + i] = bytes[i];
+      }
+      offset += len;
+    }
 
     // Write status line.
     if (headers.protocolVersion == "1.1") {
-      buffer.add(_Const.HTTP11);
+      write(_Const.HTTP11);
     } else {
-      buffer.add(_Const.HTTP10);
+      write(_Const.HTTP10);
     }
-    buffer.addByte(_CharCode.SP);
-    buffer.add(statusCode.toString().codeUnits);
-    buffer.addByte(_CharCode.SP);
-    buffer.add(reasonPhrase.codeUnits);
-    buffer.addByte(_CharCode.CR);
-    buffer.addByte(_CharCode.LF);
+    buffer[offset++] = _CharCode.SP;
+    write(statusCode.toString().codeUnits);
+    buffer[offset++] = _CharCode.SP;
+    write(reasonPhrase.codeUnits);
+    buffer[offset++] = _CharCode.CR;
+    buffer[offset++] = _CharCode.LF;
 
     var session = _httpRequest._session;
     if (session != null && !session._destroyed) {
@@ -621,11 +630,10 @@
     headers._finalize();
 
     // Write headers.
-    headers._build(buffer);
-    buffer.addByte(_CharCode.CR);
-    buffer.addByte(_CharCode.LF);
-    Uint8List headerBytes = buffer.takeBytes();
-    _outgoing.setHeader(headerBytes, headerBytes.length);
+    offset = headers._write(buffer, offset);
+    buffer[offset++] = _CharCode.CR;
+    buffer[offset++] = _CharCode.LF;
+    _outgoing.setHeader(buffer, offset);
   }
 
   String _findReasonPhrase(int statusCode) {
@@ -813,18 +821,27 @@
   }
 
   void _writeHeader() {
-    BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
+    Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
+    int offset = 0;
+
+    void write(List<int> bytes) {
+      int len = bytes.length;
+      for (int i = 0; i < len; i++) {
+        buffer[offset + i] = bytes[i];
+      }
+      offset += len;
+    }
 
     // Write the request method.
-    buffer.add(method.codeUnits);
-    buffer.addByte(_CharCode.SP);
+    write(method.codeUnits);
+    buffer[offset++] = _CharCode.SP;
     // Write the request URI.
-    buffer.add(_requestUri().codeUnits);
-    buffer.addByte(_CharCode.SP);
+    write(_requestUri().codeUnits);
+    buffer[offset++] = _CharCode.SP;
     // Write HTTP/1.1.
-    buffer.add(_Const.HTTP11);
-    buffer.addByte(_CharCode.CR);
-    buffer.addByte(_CharCode.LF);
+    write(_Const.HTTP11);
+    buffer[offset++] = _CharCode.CR;
+    buffer[offset++] = _CharCode.LF;
 
     // Add the cookies to the headers.
     if (!cookies.isEmpty) {
@@ -839,11 +856,10 @@
     headers._finalize();
 
     // Write headers.
-    headers._build(buffer);
-    buffer.addByte(_CharCode.CR);
-    buffer.addByte(_CharCode.LF);
-    Uint8List headerBytes = buffer.takeBytes();
-    _outgoing.setHeader(headerBytes, headerBytes.length);
+    offset = headers._write(buffer, offset);
+    buffer[offset++] = _CharCode.CR;
+    buffer[offset++] = _CharCode.LF;
+    _outgoing.setHeader(buffer, offset);
   }
 }
 
@@ -919,6 +935,18 @@
   // Returns either a future or 'null', if it was able to write headers
   // immediately.
   Future writeHeaders({bool drainRequest: true, bool setOutgoing: true}) {
+    Future write() {
+      try {
+        outbound._writeHeader();
+      } catch (_) {
+        // Headers too large.
+        return new Future.error(new HttpException(
+            "Headers size exceeded the of '$_OUTGOING_BUFFER_SIZE'"
+            " bytes"));
+      }
+      return null;
+    }
+
     if (headersWritten) return null;
     headersWritten = true;
     Future drainFuture;
@@ -947,22 +975,22 @@
     } else {
       drainRequest = false;
     }
-    if (!ignoreBody) {
-      if (setOutgoing) {
-        int contentLength = outbound.headers.contentLength;
-        if (outbound.headers.chunkedTransferEncoding) {
-          chunked = true;
-          if (gzip) this.gzip = true;
-        } else if (contentLength >= 0) {
-          this.contentLength = contentLength;
-        }
-      }
-      if (drainFuture != null) {
-        return drainFuture.then((_) => outbound._writeHeader());
+    if (ignoreBody) {
+      return write();
+    }
+    if (setOutgoing) {
+      int contentLength = outbound.headers.contentLength;
+      if (outbound.headers.chunkedTransferEncoding) {
+        chunked = true;
+        if (gzip) this.gzip = true;
+      } else if (contentLength >= 0) {
+        this.contentLength = contentLength;
       }
     }
-    outbound._writeHeader();
-    return null;
+    if (drainFuture != null) {
+      return drainFuture.then((_) => write());
+    }
+    return write();
   }
 
 
@@ -1132,6 +1160,7 @@
 
   void setHeader(List<int> data, int length) {
     assert(_length == 0);
+    assert(data.length == _OUTGOING_BUFFER_SIZE);
     _buffer = data;
     _length = length;
   }
diff --git a/tests/standalone/io/http_client_request_test.dart b/tests/standalone/io/http_client_request_test.dart
index cd2a7be..9015b9f 100644
--- a/tests/standalone/io/http_client_request_test.dart
+++ b/tests/standalone/io/http_client_request_test.dart
@@ -108,8 +108,25 @@
 }
 
 
+void testBadHeaders() {
+  asyncStart();
+  testClientRequest((request) {
+    var value = "a";
+    for (int i = 0; i < 8 * 1024; i++) {
+      value += 'a';
+    }
+    request.headers.set('name', value);
+    request.done.catchError((error) {
+      asyncEnd();
+    }, test: (e) => e is HttpException);
+    return request.close();
+  });
+}
+
+
 void main() {
   testResponseDone();
   testBadResponseAdd();
   testBadResponseClose();
+  testBadHeaders();
 }
diff --git a/tests/standalone/io/http_server_response_test.dart b/tests/standalone/io/http_server_response_test.dart
index a6c06af..c3c0afb 100644
--- a/tests/standalone/io/http_server_response_test.dart
+++ b/tests/standalone/io/http_server_response_test.dart
@@ -281,6 +281,20 @@
 }
 
 
+void testBadHeaders() {
+  testServerRequest((server, request) {
+    var value = "a";
+    for (int i = 0; i < 8 * 1024; i++) {
+      value += 'a';
+    }
+    request.response.headers.set('name', value);
+    request.response.close().catchError((error) {
+      server.close();
+    }, test: (e) => e is HttpException);
+  });
+}
+
+
 void testWriteCharCode() {
   testServerRequest((server, request) {
     // Test that default is latin-1 (only 2 bytes).
@@ -301,5 +315,6 @@
   testBadResponseAdd();
   testBadResponseClose();
   testIgnoreRequestData();
+  testBadHeaders();
   testWriteCharCode();
 }
diff --git a/tools/VERSION b/tools/VERSION
index eaf108ed..d8a18f8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 22
 PATCH 0
 PRERELEASE 9
-PRERELEASE_PATCH 0
+PRERELEASE_PATCH 1