From bd60507dcd317f2c1f42da37afd9dbc928f50928 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 28 Mar 2016 13:57:08 -0700 Subject: [PATCH] #553 Use System.Buffers for temporary arrays --- src/Microsoft.AspNetCore.Http/project.json | 3 +- .../WebSockets/OwinWebSocketAdapter.cs | 17 +++-- .../BufferedReadStream.cs | 21 ++++-- .../FileBufferingReadStream.cs | 71 ++++++++++++++++--- .../FormReader.cs | 69 ++++++++++++------ .../HttpRequestStreamReader.cs | 31 +------- .../HttpResponseStreamWriter.cs | 30 +------- .../MultipartReaderStream.cs | 40 ++++++++--- .../StreamHelperExtensions.cs | 25 +++++-- .../ContentDispositionHeaderValue.cs | 11 ++- src/Microsoft.Net.Http.Headers/project.json | 7 +- 11 files changed, 211 insertions(+), 114 deletions(-) diff --git a/src/Microsoft.AspNetCore.Http/project.json b/src/Microsoft.AspNetCore.Http/project.json index b5ba767975..3027e66a12 100644 --- a/src/Microsoft.AspNetCore.Http/project.json +++ b/src/Microsoft.AspNetCore.Http/project.json @@ -21,7 +21,8 @@ "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-*", "Microsoft.AspNetCore.WebUtilities": "1.0.0-*", "Microsoft.Extensions.ObjectPool": "1.0.0-*", - "Microsoft.Net.Http.Headers": "1.0.0-*" + "Microsoft.Net.Http.Headers": "1.0.0-*", + "System.Buffers": "4.0.0-*" }, "frameworks": { "net451": { }, diff --git a/src/Microsoft.AspNetCore.Owin/WebSockets/OwinWebSocketAdapter.cs b/src/Microsoft.AspNetCore.Owin/WebSockets/OwinWebSocketAdapter.cs index 17bd2ede40..e7eed159ea 100644 --- a/src/Microsoft.AspNetCore.Owin/WebSockets/OwinWebSocketAdapter.cs +++ b/src/Microsoft.AspNetCore.Owin/WebSockets/OwinWebSocketAdapter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -33,6 +34,7 @@ namespace Microsoft.AspNetCore.Owin public class OwinWebSocketAdapter : WebSocket { + private const int _rentedBufferSize = 1024; private IDictionary _websocketContext; private WebSocketSendAsync _sendAsync; private WebSocketReceiveAsync _receiveAsync; @@ -126,11 +128,18 @@ namespace Microsoft.AspNetCore.Owin await CloseOutputAsync(closeStatus, statusDescription, cancellationToken); } - byte[] buffer = new byte[1024]; - while (State == WebSocketState.CloseSent) + var buffer = ArrayPool.Shared.Rent(_rentedBufferSize); + try { - // Drain until close received - await ReceiveAsync(new ArraySegment(buffer), cancellationToken); + while (State == WebSocketState.CloseSent) + { + // Drain until close received + await ReceiveAsync(new ArraySegment(buffer), cancellationToken); + } + } + finally + { + ArrayPool.Shared.Return(buffer); } } diff --git a/src/Microsoft.AspNetCore.WebUtilities/BufferedReadStream.cs b/src/Microsoft.AspNetCore.WebUtilities/BufferedReadStream.cs index 7a1e2448e3..f9ea281d05 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/BufferedReadStream.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/BufferedReadStream.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.IO; using System.Text; using System.Threading; @@ -16,11 +17,17 @@ namespace Microsoft.AspNetCore.WebUtilities private readonly Stream _inner; private readonly byte[] _buffer; + private readonly ArrayPool _bytePool; private int _bufferOffset = 0; private int _bufferCount = 0; private bool _disposed; public BufferedReadStream(Stream inner, int bufferSize) + : this(inner, bufferSize, ArrayPool.Shared) + { + } + + public BufferedReadStream(Stream inner, int bufferSize, ArrayPool bytePool) { if (inner == null) { @@ -28,7 +35,8 @@ namespace Microsoft.AspNetCore.WebUtilities } _inner = inner; - _buffer = new byte[bufferSize]; + _bytePool = bytePool; + _buffer = bytePool.Rent(bufferSize); } public ArraySegment BufferedData @@ -128,10 +136,15 @@ namespace Microsoft.AspNetCore.WebUtilities protected override void Dispose(bool disposing) { - _disposed = true; - if (disposing) + if (!_disposed) { - _inner.Dispose(); + _disposed = true; + _bytePool.Return(_buffer); + + if (disposing) + { + _inner.Dispose(); + } } } diff --git a/src/Microsoft.AspNetCore.WebUtilities/FileBufferingReadStream.cs b/src/Microsoft.AspNetCore.WebUtilities/FileBufferingReadStream.cs index a80f02a5fd..06f9de2644 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/FileBufferingReadStream.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/FileBufferingReadStream.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Diagnostics; using System.IO; using System.Threading; @@ -16,12 +17,15 @@ namespace Microsoft.AspNetCore.WebUtilities /// public class FileBufferingReadStream : Stream { + private const int _maxRentedBufferSize = 1024 * 1024; // 1MB private readonly Stream _inner; + private readonly ArrayPool _bytePool; private readonly int _memoryThreshold; private string _tempFileDirectory; private readonly Func _tempFileDirectoryAccessor; - private Stream _buffer = new MemoryStream(); // TODO: We could have a more efficiently expanding buffer stream. + private Stream _buffer; + private byte[] _rentedBuffer; private bool _inMemory = true; private bool _completelyBuffered; @@ -32,6 +36,15 @@ namespace Microsoft.AspNetCore.WebUtilities Stream inner, int memoryThreshold, Func tempFileDirectoryAccessor) + : this(inner, memoryThreshold, tempFileDirectoryAccessor, ArrayPool.Shared) + { + } + + public FileBufferingReadStream( + Stream inner, + int memoryThreshold, + Func tempFileDirectoryAccessor, + ArrayPool bytePool) { if (inner == null) { @@ -43,6 +56,18 @@ namespace Microsoft.AspNetCore.WebUtilities throw new ArgumentNullException(nameof(tempFileDirectoryAccessor)); } + _bytePool = bytePool; + if (memoryThreshold < _maxRentedBufferSize) + { + _rentedBuffer = bytePool.Rent(memoryThreshold); + _buffer = new MemoryStream(_rentedBuffer); + _buffer.SetLength(0); + } + else + { + _buffer = new MemoryStream(); + } + _inner = inner; _memoryThreshold = memoryThreshold; _tempFileDirectoryAccessor = tempFileDirectoryAccessor; @@ -50,6 +75,15 @@ namespace Microsoft.AspNetCore.WebUtilities // TODO: allow for an optional buffer size limit to prevent filling hard disks. 1gb? public FileBufferingReadStream(Stream inner, int memoryThreshold, string tempFileDirectory) + : this(inner, memoryThreshold, tempFileDirectory, ArrayPool.Shared) + { + } + + public FileBufferingReadStream( + Stream inner, + int memoryThreshold, + string tempFileDirectory, + ArrayPool bytePool) { if (inner == null) { @@ -61,6 +95,18 @@ namespace Microsoft.AspNetCore.WebUtilities throw new ArgumentNullException(nameof(tempFileDirectory)); } + _bytePool = bytePool; + if (memoryThreshold < _maxRentedBufferSize) + { + _rentedBuffer = bytePool.Rent(memoryThreshold); + _buffer = new MemoryStream(_rentedBuffer); + _buffer.SetLength(0); + } + else + { + _buffer = new MemoryStream(); + } + _inner = inner; _memoryThreshold = memoryThreshold; _tempFileDirectory = tempFileDirectory; @@ -145,11 +191,11 @@ namespace Microsoft.AspNetCore.WebUtilities if (_inMemory && _buffer.Length + read > _memoryThreshold) { - var oldBuffer = _buffer; - _buffer = CreateTempFile(); _inMemory = false; - oldBuffer.Position = 0; - oldBuffer.CopyTo(_buffer, 1024 * 16); + _buffer = CreateTempFile(); + _buffer.Write(_rentedBuffer, 0, (int)_buffer.Length); + _bytePool.Return(_rentedBuffer); + _rentedBuffer = null; } if (read > 0) @@ -216,11 +262,11 @@ namespace Microsoft.AspNetCore.WebUtilities if (_inMemory && _buffer.Length + read > _memoryThreshold) { - var oldBuffer = _buffer; - _buffer = CreateTempFile(); _inMemory = false; - oldBuffer.Position = 0; - await oldBuffer.CopyToAsync(_buffer, 1024 * 16, cancellationToken); + _buffer = CreateTempFile(); + await _buffer.WriteAsync(_rentedBuffer, 0, (int)_buffer.Length, cancellationToken); + _bytePool.Return(_rentedBuffer); + _rentedBuffer = null; } if (read > 0) @@ -270,6 +316,11 @@ namespace Microsoft.AspNetCore.WebUtilities if (!_disposed) { _disposed = true; + if (_rentedBuffer != null) + { + _bytePool.Return(_rentedBuffer); + } + if (disposing) { _buffer.Dispose(); @@ -285,4 +336,4 @@ namespace Microsoft.AspNetCore.WebUtilities } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.WebUtilities/FormReader.cs b/src/Microsoft.AspNetCore.WebUtilities/FormReader.cs index ca378895ef..df12872c99 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/FormReader.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/FormReader.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Text; @@ -14,25 +15,40 @@ namespace Microsoft.AspNetCore.WebUtilities /// /// Used to read an 'application/x-www-form-urlencoded' form. /// - public class FormReader + public class FormReader : IDisposable { + private const int _rentedCharPoolLength = 8192; private readonly TextReader _reader; - private readonly char[] _buffer = new char[1024]; + private readonly char[] _buffer; + private readonly ArrayPool _charPool; private readonly StringBuilder _builder = new StringBuilder(); private int _bufferOffset; private int _bufferCount; + private bool _disposed; public FormReader(string data) + : this(data, ArrayPool.Shared) + { + } + + public FormReader(string data, ArrayPool charPool) { if (data == null) { throw new ArgumentNullException(nameof(data)); } + _buffer = charPool.Rent(_rentedCharPoolLength); + _charPool = charPool; _reader = new StringReader(data); } public FormReader(Stream stream, Encoding encoding) + : this(stream, encoding, ArrayPool.Shared) + { + } + + public FormReader(Stream stream, Encoding encoding, ArrayPool charPool) { if (stream == null) { @@ -44,6 +60,8 @@ namespace Microsoft.AspNetCore.WebUtilities throw new ArgumentNullException(nameof(encoding)); } + _buffer = charPool.Rent(_rentedCharPoolLength); + _charPool = charPool; _reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024 * 2, leaveOpen: true); } @@ -167,17 +185,18 @@ namespace Microsoft.AspNetCore.WebUtilities /// The collection containing the parsed HTTP form body. public static Dictionary ReadForm(string text) { - var reader = new FormReader(text); - - var accumulator = new KeyValueAccumulator(); - var pair = reader.ReadNextPair(); - while (pair.HasValue) + using (var reader = new FormReader(text)) { - accumulator.Append(pair.Value.Key, pair.Value.Value); - pair = reader.ReadNextPair(); - } + var accumulator = new KeyValueAccumulator(); + var pair = reader.ReadNextPair(); + while (pair.HasValue) + { + accumulator.Append(pair.Value.Key, pair.Value.Value); + pair = reader.ReadNextPair(); + } - return accumulator.GetResults(); + return accumulator.GetResults(); + } } /// @@ -200,17 +219,27 @@ namespace Microsoft.AspNetCore.WebUtilities /// The collection containing the parsed HTTP form body. public static async Task> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken()) { - var reader = new FormReader(stream, encoding); - - var accumulator = new KeyValueAccumulator(); - var pair = await reader.ReadNextPairAsync(cancellationToken); - while (pair.HasValue) + using (var reader = new FormReader(stream, encoding)) { - accumulator.Append(pair.Value.Key, pair.Value.Value); - pair = await reader.ReadNextPairAsync(cancellationToken); - } + var accumulator = new KeyValueAccumulator(); + var pair = await reader.ReadNextPairAsync(cancellationToken); + while (pair.HasValue) + { + accumulator.Append(pair.Value.Key, pair.Value.Value); + pair = await reader.ReadNextPairAsync(cancellationToken); + } - return accumulator.GetResults(); + return accumulator.GetResults(); + } + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + _charPool.Return(_buffer); + } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.WebUtilities/HttpRequestStreamReader.cs b/src/Microsoft.AspNetCore.WebUtilities/HttpRequestStreamReader.cs index 15e98d7eaa..67977ca6f0 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/HttpRequestStreamReader.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/HttpRequestStreamReader.cs @@ -34,40 +34,13 @@ namespace Microsoft.AspNetCore.WebUtilities private bool _isBlocked; public HttpRequestStreamReader(Stream stream, Encoding encoding) - : this(stream, encoding, DefaultBufferSize) + : this(stream, encoding, DefaultBufferSize, ArrayPool.Shared, ArrayPool.Shared) { } public HttpRequestStreamReader(Stream stream, Encoding encoding, int bufferSize) + : this(stream, encoding, bufferSize, ArrayPool.Shared, ArrayPool.Shared) { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!stream.CanRead) - { - throw new ArgumentException(Resources.HttpRequestStreamReader_StreamNotReadable, nameof(stream)); - } - - if (encoding == null) - { - throw new ArgumentNullException(nameof(encoding)); - } - - _stream = stream; - _encoding = encoding; - _decoder = encoding.GetDecoder(); - - if (bufferSize < MinBufferSize) - { - bufferSize = MinBufferSize; - } - - _byteBufferSize = bufferSize; - _byteBuffer = new byte[bufferSize]; - var maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize); - _charBuffer = new char[maxCharsPerBuffer]; } public HttpRequestStreamReader( diff --git a/src/Microsoft.AspNetCore.WebUtilities/HttpResponseStreamWriter.cs b/src/Microsoft.AspNetCore.WebUtilities/HttpResponseStreamWriter.cs index 30f6423fef..9ec8da2338 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/HttpResponseStreamWriter.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/HttpResponseStreamWriter.cs @@ -34,39 +34,13 @@ namespace Microsoft.AspNetCore.WebUtilities private int _charBufferCount; public HttpResponseStreamWriter(Stream stream, Encoding encoding) - : this(stream, encoding, DefaultBufferSize) + : this(stream, encoding, DefaultBufferSize, ArrayPool.Shared, ArrayPool.Shared) { } public HttpResponseStreamWriter(Stream stream, Encoding encoding, int bufferSize) + : this(stream, encoding, bufferSize, ArrayPool.Shared, ArrayPool.Shared) { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!stream.CanWrite) - { - throw new ArgumentException(Resources.HttpResponseStreamWriter_StreamNotWritable, nameof(stream)); - } - - if (encoding == null) - { - throw new ArgumentNullException(nameof(encoding)); - } - - _stream = stream; - Encoding = encoding; - _charBufferSize = bufferSize; - - if (bufferSize < MinBufferSize) - { - bufferSize = MinBufferSize; - } - - _encoder = encoding.GetEncoder(); - _byteBuffer = new byte[encoding.GetMaxByteCount(bufferSize)]; - _charBuffer = new char[bufferSize]; } public HttpResponseStreamWriter( diff --git a/src/Microsoft.AspNetCore.WebUtilities/MultipartReaderStream.cs b/src/Microsoft.AspNetCore.WebUtilities/MultipartReaderStream.cs index fdd84a5c6d..c94b27b003 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/MultipartReaderStream.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/MultipartReaderStream.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Diagnostics; using System.IO; using System.Threading; @@ -13,6 +14,8 @@ namespace Microsoft.AspNetCore.WebUtilities { private readonly MultipartBoundary _boundary; private readonly BufferedReadStream _innerStream; + private readonly ArrayPool _bytePool; + private readonly long _innerOffset; private long _position; private long _observedLength; @@ -24,6 +27,17 @@ namespace Microsoft.AspNetCore.WebUtilities /// The . /// The boundary pattern to use. public MultipartReaderStream(BufferedReadStream stream, MultipartBoundary boundary) + : this(stream, boundary, ArrayPool.Shared) + { + } + + /// + /// Creates a stream that reads until it reaches the given boundary pattern. + /// + /// The . + /// The boundary pattern to use. + /// The ArrayPool pool to use for temporary byte arrays. + public MultipartReaderStream(BufferedReadStream stream, MultipartBoundary boundary, ArrayPool bytePool) { if (stream == null) { @@ -35,6 +49,7 @@ namespace Microsoft.AspNetCore.WebUtilities throw new ArgumentNullException(nameof(boundary)); } + _bytePool = bytePool; _innerStream = stream; _innerOffset = _innerStream.CanSeek ? _innerStream.Position : 0; _boundary = boundary; @@ -212,14 +227,18 @@ namespace Microsoft.AspNetCore.WebUtilities read = _innerStream.Read(buffer, offset, Math.Min(count, matchOffset - bufferedData.Offset)); return UpdatePosition(read); } - Debug.Assert(matchCount == _boundary.BoundaryBytes.Length); + + var length = _boundary.BoundaryBytes.Length; + Debug.Assert(matchCount == length); // "The boundary may be followed by zero or more characters of // linear whitespace. It is then terminated by either another CRLF" // or -- for the final boundary. - byte[] boundary = new byte[_boundary.BoundaryBytes.Length]; - read = _innerStream.Read(boundary, 0, boundary.Length); - Debug.Assert(read == boundary.Length); // It should have all been buffered + var boundary = _bytePool.Rent(length); + read = _innerStream.Read(boundary, 0, length); + _bytePool.Return(boundary); + Debug.Assert(read == length); // It should have all been buffered + var remainder = _innerStream.ReadLine(lengthLimit: 100); // Whitespace may exceed the buffer. remainder = remainder.Trim(); if (string.Equals("--", remainder, StringComparison.Ordinal)) @@ -227,7 +246,6 @@ namespace Microsoft.AspNetCore.WebUtilities FinalBoundaryFound = true; } Debug.Assert(FinalBoundaryFound || string.Equals(string.Empty, remainder, StringComparison.Ordinal), "Un-expected data found on the boundary line: " + remainder); - _finished = true; return 0; } @@ -264,14 +282,18 @@ namespace Microsoft.AspNetCore.WebUtilities read = _innerStream.Read(buffer, offset, Math.Min(count, matchOffset - bufferedData.Offset)); return UpdatePosition(read); } - Debug.Assert(matchCount == _boundary.BoundaryBytes.Length); + + var length = _boundary.BoundaryBytes.Length; + Debug.Assert(matchCount == length); // "The boundary may be followed by zero or more characters of // linear whitespace. It is then terminated by either another CRLF" // or -- for the final boundary. - byte[] boundary = new byte[_boundary.BoundaryBytes.Length]; - read = _innerStream.Read(boundary, 0, boundary.Length); - Debug.Assert(read == boundary.Length); // It should have all been buffered + var boundary = _bytePool.Rent(length); + read = _innerStream.Read(boundary, 0, length); + _bytePool.Return(boundary); + Debug.Assert(read == length); // It should have all been buffered + var remainder = await _innerStream.ReadLineAsync(lengthLimit: 100, cancellationToken: cancellationToken); // Whitespace may exceed the buffer. remainder = remainder.Trim(); if (string.Equals("--", remainder, StringComparison.Ordinal)) diff --git a/src/Microsoft.AspNetCore.WebUtilities/StreamHelperExtensions.cs b/src/Microsoft.AspNetCore.WebUtilities/StreamHelperExtensions.cs index ac5d4c43a3..90830e897e 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/StreamHelperExtensions.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/StreamHelperExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -9,14 +10,28 @@ namespace Microsoft.AspNetCore.WebUtilities { public static class StreamHelperExtensions { - public static async Task DrainAsync(this Stream stream, CancellationToken cancellationToken) + private const int _maxReadBufferSize = 1024 * 4; + + public static Task DrainAsync(this Stream stream, CancellationToken cancellationToken) + { + return stream.DrainAsync(ArrayPool.Shared, cancellationToken); + } + + public static async Task DrainAsync(this Stream stream, ArrayPool bytePool, CancellationToken cancellationToken) { - byte[] buffer = new byte[1024]; cancellationToken.ThrowIfCancellationRequested(); - while (await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken) > 0) + var buffer = bytePool.Rent(_maxReadBufferSize); + try { - // Not all streams support cancellation directly. - cancellationToken.ThrowIfCancellationRequested(); + while (await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken) > 0) + { + // Not all streams support cancellation directly. + cancellationToken.ThrowIfCancellationRequested(); + } + } + finally + { + bytePool.Return(buffer); } } } diff --git a/src/Microsoft.Net.Http.Headers/ContentDispositionHeaderValue.cs b/src/Microsoft.Net.Http.Headers/ContentDispositionHeaderValue.cs index 5ee589440a..7ceb8728d4 100644 --- a/src/Microsoft.Net.Http.Headers/ContentDispositionHeaderValue.cs +++ b/src/Microsoft.Net.Http.Headers/ContentDispositionHeaderValue.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; @@ -578,12 +579,13 @@ namespace Microsoft.Net.Http.Headers } var decoded = new StringBuilder(); + byte[] unescapedBytes = null; try { var encoding = Encoding.GetEncoding(parts[0]); var dataString = parts[2]; - var unescapedBytes = new byte[dataString.Length]; + unescapedBytes = ArrayPool.Shared.Rent(dataString.Length); var unescapedBytesCount = 0; for (var index = 0; index < dataString.Length; index++) { @@ -615,6 +617,13 @@ namespace Microsoft.Net.Http.Headers { return false; // Unknown encoding or bad characters } + finally + { + if (unescapedBytes != null) + { + ArrayPool.Shared.Return(unescapedBytes); + } + } output = decoded.ToString(); return true; diff --git a/src/Microsoft.Net.Http.Headers/project.json b/src/Microsoft.Net.Http.Headers/project.json index 3fa2366ec5..c3fa7a950f 100644 --- a/src/Microsoft.Net.Http.Headers/project.json +++ b/src/Microsoft.Net.Http.Headers/project.json @@ -18,7 +18,7 @@ }, "dependencies": { }, "frameworks": { - "netstandard1.0": { + "netstandard1.1": { "dependencies": { "System.Collections": "4.0.11-*", "System.Diagnostics.Contracts": "4.0.1-*", @@ -26,10 +26,11 @@ "System.Linq": "4.1.0-*", "System.Resources.ResourceManager": "4.0.1-*", "System.Runtime.Extensions": "4.1.0-*", - "System.Text.Encoding": "4.0.11-*" + "System.Text.Encoding": "4.0.11-*", + "System.Buffers": "4.0.0-*" }, "imports": [ - "dotnet5.1" + "dotnet5.2" ] } }