diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 09077ead0e..41323981f7 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -126,6 +126,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemdTestApp", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlaintextApp", "samples\PlaintextApp\PlaintextApp.csproj", "{CE5523AE-6E38-4E20-998F-C64E02C5CC51}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformBenchmarks", "benchmarkapps\PlatformBenchmarks\PlatformBenchmarks.csproj", "{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -364,6 +366,18 @@ Global {CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x64.Build.0 = Release|Any CPU {CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x86.ActiveCfg = Release|Any CPU {CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x86.Build.0 = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x64.Build.0 = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x86.Build.0 = Debug|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|Any CPU.Build.0 = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x64.ActiveCfg = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x64.Build.0 = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x86.ActiveCfg = Release|Any CPU + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -390,6 +404,7 @@ Global {7BC22A4A-15D2-44C2-AB45-049F0FB562FA} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE} {A7994A41-CAF8-47A7-8975-F101F75B5BC1} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE} {CE5523AE-6E38-4E20-998F-C64E02C5CC51} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE} + {7C24EAB8-57A9-4613-A8A6-4C21BB7D260D} = {A95C3BE1-B850-4265-97A0-777ADCCD437F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2D10D020-6770-47CA-BB8D-2C23FE3AE071} diff --git a/benchmarkapps/PlatformBenchmarks/AsciiString.cs b/benchmarkapps/PlatformBenchmarks/AsciiString.cs new file mode 100644 index 0000000000..f84d42c978 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/AsciiString.cs @@ -0,0 +1,50 @@ +// 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; +using System.Text; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; + +namespace PlatformBenchmarks +{ + public struct AsciiString : IEquatable + { + private readonly byte[] _data; + + public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s); + + public int Length => _data.Length; + + public ReadOnlySpan AsSpan() => _data; + + public static implicit operator ReadOnlySpan(AsciiString str) => str._data; + public static implicit operator byte[] (AsciiString str) => str._data; + + public static implicit operator AsciiString(string str) => new AsciiString(str); + + public override string ToString() => HttpUtilities.GetAsciiStringNonNullCharacters(_data); + public static explicit operator string(AsciiString str) => str.ToString(); + + public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data); + private bool SequenceEqual(byte[] data1, byte[] data2) => new Span(data1).SequenceEqual(data2); + + public static bool operator ==(AsciiString a, AsciiString b) => a.Equals(b); + public static bool operator !=(AsciiString a, AsciiString b) => !a.Equals(b); + public override bool Equals(object other) => (other is AsciiString) && Equals((AsciiString)other); + + public override int GetHashCode() + { + // Copied from x64 version of string.GetLegacyNonRandomizedHashCode() + // https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/String.Comparison.cs + var data = _data; + int hash1 = 5381; + int hash2 = hash1; + foreach (int b in data) + { + hash1 = ((hash1 << 5) + hash1) ^ b; + } + return hash1 + (hash2 * 1566083941); + } + + } +} diff --git a/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs new file mode 100644 index 0000000000..6551bee358 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs @@ -0,0 +1,156 @@ +// 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; +using System.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Utf8Json; + +namespace PlatformBenchmarks +{ + public class BenchmarkApplication : HttpConnection + { + private static AsciiString _crlf = "\r\n"; + private static AsciiString _eoh = "\r\n\r\n"; // End Of Headers + private static AsciiString _http11OK = "HTTP/1.1 200 OK\r\n"; + private static AsciiString _headerServer = "Server: Custom"; + private static AsciiString _headerContentLength = "Content-Length: "; + private static AsciiString _headerContentLengthZero = "Content-Length: 0\r\n"; + private static AsciiString _headerContentTypeText = "Content-Type: text/plain\r\n"; + private static AsciiString _headerContentTypeJson = "Content-Type: application/json\r\n"; + + + private static AsciiString _plainTextBody = "Hello, World!"; + + private static class Paths + { + public static AsciiString Plaintext = "/plaintext"; + public static AsciiString Json = "/json"; + } + + private bool _isPlainText; + private bool _isJson; + + public override void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) + { + if (path.StartsWith(Paths.Plaintext) && method == HttpMethod.Get) + { + _isPlainText = true; + } + else if (path.StartsWith(Paths.Json) && method == HttpMethod.Get) + { + _isJson = true; + } + else + { + _isPlainText = false; + _isJson = false; + } + } + + public override void OnHeader(Span name, Span value) + { + } + + public override ValueTask ProcessRequestAsync() + { + if (_isPlainText) + { + PlainText(Writer); + } + else if (_isJson) + { + Json(Writer); + } + else + { + Default(Writer); + } + + return default; + } + + public override async ValueTask OnReadCompletedAsync() + { + await Writer.FlushAsync(); + } + private static void PlainText(PipeWriter pipeWriter) + { + var writer = new BufferWriter(pipeWriter); + // HTTP 1.1 OK + writer.Write(_http11OK); + + // Server headers + writer.Write(_headerServer); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + // Content-Type header + writer.Write(_headerContentTypeText); + + // Content-Length header + writer.Write(_headerContentLength); + writer.WriteNumeric((ulong)_plainTextBody.Length); + + // End of headers + writer.Write(_eoh); + + // Body + writer.Write(_plainTextBody); + writer.Commit(); + } + + private static void Json(PipeWriter pipeWriter) + { + var writer = new BufferWriter(pipeWriter); + + // HTTP 1.1 OK + writer.Write(_http11OK); + + // Server headers + writer.Write(_headerServer); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + // Content-Type header + writer.Write(_headerContentTypeJson); + + // Content-Length header + writer.Write(_headerContentLength); + var jsonPayload = JsonSerializer.SerializeUnsafe(new { message = "Hello, World!" }); + writer.WriteNumeric((ulong)jsonPayload.Count); + + // End of headers + writer.Write(_eoh); + + // Body + writer.Write(jsonPayload); + writer.Commit(); + } + + private static void Default(PipeWriter pipeWriter) + { + var writer = new BufferWriter(pipeWriter); + + // HTTP 1.1 OK + writer.Write(_http11OK); + + // Server headers + writer.Write(_headerServer); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + // Content-Length 0 + writer.Write(_headerContentLengthZero); + + // End of headers + writer.Write(_crlf); + writer.Commit(); + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs b/benchmarkapps/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs new file mode 100644 index 0000000000..c6d2cdbdf9 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs @@ -0,0 +1,81 @@ +// 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; +using System.Net; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Configuration; + +namespace PlatformBenchmarks +{ + public static class BenchmarkConfigurationHelpers + { + public static IWebHostBuilder UseBenchmarksConfiguration(this IWebHostBuilder builder, IConfiguration configuration) + { + builder.UseConfiguration(configuration); + + // Handle the transport type + var webHost = builder.GetSetting("KestrelTransport"); + + // Handle the thread count + var threadCountRaw = builder.GetSetting("threadCount"); + int? theadCount = null; + + if (!string.IsNullOrEmpty(threadCountRaw) && + Int32.TryParse(threadCountRaw, out var value)) + { + theadCount = value; + } + + if (string.Equals(webHost, "Libuv", StringComparison.OrdinalIgnoreCase)) + { + builder.UseLibuv(options => + { + if (theadCount.HasValue) + { + options.ThreadCount = theadCount.Value; + } + }); + } + else if (string.Equals(webHost, "Sockets", StringComparison.OrdinalIgnoreCase)) + { + builder.UseSockets(options => + { + if (theadCount.HasValue) + { + options.IOQueueCount = theadCount.Value; + } + }); + } + + return builder; + } + + public static IPEndPoint CreateIPEndPoint(this IConfiguration config) + { + var url = config["server.urls"] ?? config["urls"]; + + if (string.IsNullOrEmpty(url)) + { + return new IPEndPoint(IPAddress.Loopback, 8080); + } + + var address = ServerAddress.FromUrl(url); + + IPAddress ip; + + if (string.Equals(address.Host, "localhost", StringComparison.OrdinalIgnoreCase)) + { + ip = IPAddress.Loopback; + } + else if (!IPAddress.TryParse(address.Host, out ip)) + { + ip = IPAddress.IPv6Any; + } + + return new IPEndPoint(ip, address.Port); + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/BufferWriterExtensions.cs b/benchmarkapps/PlatformBenchmarks/BufferWriterExtensions.cs new file mode 100644 index 0000000000..0da6b24fd4 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BufferWriterExtensions.cs @@ -0,0 +1,38 @@ +// 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.Text; +using System.Diagnostics; +using System.IO.Pipelines; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + internal static class BufferWriterExtensions + { + private const int MaxULongByteLength = 20; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void WriteNumeric(ref this BufferWriter buffer, ulong number) + { + // Try to format directly + if (Utf8Formatter.TryFormat(number, buffer.Span, out int bytesWritten)) + { + buffer.Advance(bytesWritten); + } + else + { + // Ask for at least 20 bytes + buffer.Ensure(MaxULongByteLength); + + Debug.Assert(buffer.Span.Length >= 20, "Buffer is < 20 bytes"); + + // Try again + if (Utf8Formatter.TryFormat(number, buffer.Span, out bytesWritten)) + { + buffer.Advance(bytesWritten); + } + } + } + } +} \ No newline at end of file diff --git a/benchmarkapps/PlatformBenchmarks/DateHeader.cs b/benchmarkapps/PlatformBenchmarks/DateHeader.cs new file mode 100644 index 0000000000..1d03d78b53 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/DateHeader.cs @@ -0,0 +1,59 @@ +// 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; +using System.Buffers.Text; +using System.Diagnostics; +using System.Text; +using System.Threading; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http +{ + /// + /// Manages the generation of the date header value. + /// + internal static class DateHeader + { + const int prefixLength = 8; // "\r\nDate: ".Length + const int dateTimeRLength = 29; // Wed, 14 Mar 2018 14:20:00 GMT + const int suffixLength = 2; // crlf + const int suffixIndex = dateTimeRLength + prefixLength; + + private static readonly Timer s_timer = new Timer((s) => { + SetDateValues(DateTimeOffset.UtcNow); + }, null, 1000, 1000); + + private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + suffixLength]; + private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + suffixLength]; + + static DateHeader() + { + var utf8 = Encoding.ASCII.GetBytes("\r\nDate: ").AsSpan(); + utf8.CopyTo(s_headerBytesMaster); + utf8.CopyTo(s_headerBytesScratch); + s_headerBytesMaster[suffixIndex] = (byte)'\r'; + s_headerBytesMaster[suffixIndex + 1] = (byte)'\n'; + s_headerBytesScratch[suffixIndex] = (byte)'\r'; + s_headerBytesScratch[suffixIndex + 1] = (byte)'\n'; + SetDateValues(DateTimeOffset.UtcNow); + } + + public static ReadOnlySpan HeaderBytes => s_headerBytesMaster; + + private static void SetDateValues(DateTimeOffset value) + { + lock (s_headerBytesScratch) + { + if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan().Slice(prefixLength), out int written, 'R')) + { + throw new Exception("date time format failed"); + } + Debug.Assert(written == dateTimeRLength); + var temp = s_headerBytesMaster; + s_headerBytesMaster = s_headerBytesScratch; + s_headerBytesScratch = temp; + } + } + } +} \ No newline at end of file diff --git a/benchmarkapps/PlatformBenchmarks/HttpApplication.cs b/benchmarkapps/PlatformBenchmarks/HttpApplication.cs new file mode 100644 index 0000000000..e9b20f5aaa --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/HttpApplication.cs @@ -0,0 +1,161 @@ +// 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; +using System.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; + +namespace PlatformBenchmarks +{ + public static class HttpApplicationConnectionBuilderExtensions + { + public static IConnectionBuilder UseHttpApplication(this IConnectionBuilder builder) where TConnection : HttpConnection, new() + { + return builder.Use(next => new HttpApplication().ExecuteAsync); + } + } + + public class HttpApplication where TConnection : HttpConnection, new() + { + public Task ExecuteAsync(ConnectionContext connection) + { + var parser = new HttpParser(); + + var httpConnection = new TConnection + { + Parser = parser, + Reader = connection.Transport.Input, + Writer = connection.Transport.Output + }; + return httpConnection.ExecuteAsync(); + } + } + + public class HttpConnection : IHttpHeadersHandler, IHttpRequestLineHandler + { + private State _state; + + public PipeReader Reader { get; set; } + public PipeWriter Writer { get; set; } + + internal HttpParser Parser { get; set; } + + public virtual void OnHeader(Span name, Span value) + { + + } + + public virtual void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) + { + + } + + public virtual ValueTask ProcessRequestAsync() + { + return default; + } + + public virtual ValueTask OnReadCompletedAsync() + { + return default; + } + + public async Task ExecuteAsync() + { + try + { + await ProcessRequestsAsync(); + + Reader.Complete(); + } + catch (Exception ex) + { + Reader.Complete(ex); + } + finally + { + Writer.Complete(); + } + } + + private async Task ProcessRequestsAsync() + { + while (true) + { + var task = Reader.ReadAsync(); + + if (!task.IsCompleted) + { + // No more data in the input + await OnReadCompletedAsync(); + } + + var result = await task; + var buffer = result.Buffer; + var consumed = buffer.Start; + var examined = buffer.End; + + if (!buffer.IsEmpty) + { + ParseHttpRequest(buffer, out consumed, out examined); + + if (_state != State.Body && result.IsCompleted) + { + ThrowUnexpectedEndOfData(); + } + } + else if (result.IsCompleted) + { + break; + } + + Reader.AdvanceTo(consumed, examined); + + if (_state == State.Body) + { + await ProcessRequestAsync(); + + _state = State.StartLine; + } + } + } + + private void ParseHttpRequest(in ReadOnlySequence buffer, out SequencePosition consumed, out SequencePosition examined) + { + consumed = buffer.Start; + examined = buffer.End; + + var parsingStartLine = _state == State.StartLine; + if (parsingStartLine) + { + if (Parser.ParseRequestLine(this, buffer, out consumed, out examined)) + { + _state = State.Headers; + } + } + + if (_state == State.Headers) + { + if (Parser.ParseHeaders(this, parsingStartLine ? buffer.Slice(consumed) : buffer, out consumed, out examined, out int consumedBytes)) + { + _state = State.Body; + } + } + } + + private static void ThrowUnexpectedEndOfData() + { + throw new InvalidOperationException("Unexpected end of data!"); + } + + private enum State + { + StartLine, + Headers, + Body + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj b/benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj new file mode 100644 index 0000000000..a883710816 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.1 + Exe + latest + true + + + + + + + + + + + + + + + + + + + + + diff --git a/benchmarkapps/PlatformBenchmarks/Program.cs b/benchmarkapps/PlatformBenchmarks/Program.cs new file mode 100644 index 0000000000..7776cbd1ce --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/Program.cs @@ -0,0 +1,46 @@ +// 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; +using System.Net; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace PlatformBenchmarks +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) + { + var config = new ConfigurationBuilder() + .AddEnvironmentVariables(prefix: "ASPNETCORE_") + .AddCommandLine(args) + .Build(); + + var host = new WebHostBuilder() + .UseBenchmarksConfiguration(config) + .UseKestrel((context, options) => + { + IPEndPoint endPoint = context.Configuration.CreateIPEndPoint(); + + options.Listen(endPoint, builder => + { + builder.UseHttpApplication(); + }); + }) + .UseStartup() + .Build(); + + return host; + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/Startup.cs b/benchmarkapps/PlatformBenchmarks/Startup.cs new file mode 100644 index 0000000000..bfc1f70375 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/Startup.cs @@ -0,0 +1,19 @@ +// 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; +using System.Net.WebSockets; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; + +namespace PlatformBenchmarks +{ + public class Startup + { + public void Configure(IApplicationBuilder app) + { + + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/benchmarks.json.json b/benchmarkapps/PlatformBenchmarks/benchmarks.json.json new file mode 100644 index 0000000000..157cee28dc --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/benchmarks.json.json @@ -0,0 +1,15 @@ +{ + "Default": { + "Client": "Wrk", + "PresetHeaders": "Json", + + "Source": { + "Repository": "https://github.com/aspnet/KestrelHttpServer.git", + "BranchOrCommit": "dev", + "Project": "benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj" + } + }, + "JsonPlatform": { + "Path": "/json" + } +} \ No newline at end of file diff --git a/benchmarkapps/PlatformBenchmarks/benchmarks.plaintext.json b/benchmarkapps/PlatformBenchmarks/benchmarks.plaintext.json new file mode 100644 index 0000000000..5ad58aee15 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/benchmarks.plaintext.json @@ -0,0 +1,26 @@ +{ + "Default": { + "Client": "Wrk", + "PresetHeaders": "Plaintext", + "ClientProperties": { + "ScriptName": "pipeline", + "PipelineDepth": 16 + }, + "Source": { + "Repository": "https://github.com/aspnet/KestrelHttpServer.git", + "BranchOrCommit": "dev", + "Project": "benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj" + }, + "Port": 8080 + }, + "PlaintextPlatform": { + "Path": "/plaintext" + }, + "PlaintextNonPipelinedPlatform": { + "Path": "/plaintext", + "ClientProperties": { + "ScriptName": "", + "PipelineDepth": 0 + } + } +} diff --git a/build/dependencies.props b/build/dependencies.props index 9831c511ea..ace613e29c 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -7,6 +7,7 @@ 0.10.13 2.1.0-preview3-17002 1.10.0 + 2.1.0-preview3-32110 2.1.0-preview3-32110 2.1.0-preview3-32110 2.1.0-preview3-32110 @@ -20,6 +21,7 @@ 2.1.0-preview3-32110 2.1.0-preview3-32110 2.1.0-preview3-32110 + 2.1.0-preview3-32037 2.1.0-preview3-32110 2.1.0-preview3-32110 2.1.0-preview3-32110 @@ -40,6 +42,7 @@ 4.5.0-preview3-26331-02 4.5.0-preview3-26331-02 4.5.0-preview3-26331-02 + 1.3.7 0.8.0 2.3.1 2.4.0-beta.1.build3945