diff --git a/eng/pipelines/libraries/deploy-networking-echo-test-servers.yml b/eng/pipelines/libraries/deploy-networking-echo-test-servers.yml
new file mode 100644
index 00000000000000..c1bd5f5ffb4daa
--- /dev/null
+++ b/eng/pipelines/libraries/deploy-networking-echo-test-servers.yml
@@ -0,0 +1,99 @@
+# This pipeline deploys the test servers we are using to run (outerloop) Functional tests for various functionality in System.Net namespace.
+# The pipeline deploys to staging slots, which are available at
+# - corefx-net-http11-staging.azurewebsites.net
+# - corefx-net-http2-staging.azurewebsites.net
+#
+# Usage instructions:
+# 1) Start pipeline at https://dev.azure.com/dnceng/internal/_build?definitionId=1419 (
+deploy-networking-echo-test-servers), pick the correct branch and start the build.
+# 2) After pipeline succeeds, check your changes against the staging slots, two possible ways to do this are:
+# - create a no-merge PR where you overwrite the default urls (e.g. https://github.com/dotnet/runtime/pull/111396)
+# - test locally, either by same changes as in above PR, or by using the override environment variables
+#
+# DOTNET_TEST_HTTPHOST=http://corefx-net-http11-staging.azurewebsites.net
+# DOTNET_TEST_SECUREHTTPHOST=https://corefx-net-http11-staging.azurewebsites.net
+# DOTNET_TEST_WEBSOKETHOST=http://corefx-net-http11-staging.azurewebsites.net
+# DOTNET_TEST_SECUREWEBSOKETHOST=http://corefx-net-http11-staging.azurewebsites.net
+#
+# DOTNET_TEST_HTTP2HOST=http://corefx-net-http2-staging.azurewebsites.net
+# 3) If there are no issues, you can swap the staging slots with production slots via the Azure portal.
+# Only the members of the .NET Networking team have access to the Azure Subscription which hosts the
+# servers. Look for the "Deployment slots" section at corefx-net-http11 and corefx-net-http2 Web Apps
+
+trigger: none
+
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+
+variables:
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+ - name: FrameworkVersion
+ value: 8.x
+ - name: TargetFramework
+ value: net8.0
+
+extends:
+ template: v1/1ES.Unofficial.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ customBuildTags:
+ - 1ES.PT.ViaStartRight
+ pool:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+
+ stages:
+ - stage: stage
+ displayName: Building in a VM
+ jobs:
+ - job: job
+ displayName: Build
+ steps:
+ - checkout: self
+
+ - task: UseDotNet@2
+ inputs:
+ packageType: "sdk"
+ version: $(FrameworkVersion)
+
+ - script: echo {} > ./global.json
+ displayName: Bypass repository global.json
+ workingDirectory: ./src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer
+
+ - script: dotnet publish -c Release /p:GenevaTelemetry=true /p:_TargetFrameworkForXHarness=$(TargetFramework)
+ workingDirectory: ./src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer
+ displayName: Run dotnet publish
+
+ - task: zip@0
+ displayName: Zip artifacts
+ inputs:
+ pathToZipFolder: 'artifacts\bin\NetCoreServer\Release\$(TargetFramework)\publish\'
+ pathToZipFile: 'artifacts\bin\NetCoreServer\Release\$(TargetFramework)\publish.zip'
+
+ - task: AzureRmWebAppDeployment@4
+ displayName: Deploy to corefx-net-http11-staging
+ inputs:
+ ConnectionType: "AzureRM"
+ azureSubscription: .NET Libraries Network Testing
+ appType: "webApp"
+ WebAppName: "corefx-net-http11"
+ deployToSlotOrASE: true
+ ResourceGroupName: "Production-WestUS"
+ SlotName: "staging"
+ package: 'artifacts\bin\NetCoreServer\Release\$(TargetFramework)\publish.zip'
+
+ - task: AzureRmWebAppDeployment@4
+ displayName: Deploy to corefx-net-http2-staging
+ inputs:
+ ConnectionType: "AzureRM"
+ azureSubscription: .NET Libraries Network Testing
+ appType: "webApp"
+ WebAppName: "corefx-net-http2"
+ deployToSlotOrASE: true
+ ResourceGroupName: "Production-WestUS"
+ SlotName: "staging"
+ package: 'artifacts\bin\NetCoreServer\Release\$(TargetFramework)\publish.zip'
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Directory.Build.props b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Directory.Build.props
index 65a9a7c3bf4783..8b6a62bf6d7d27 100644
--- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Directory.Build.props
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Directory.Build.props
@@ -4,4 +4,5 @@
+
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/GenericHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/GenericHandler.cs
index c433a9edbcb76c..3731385df3bb27 100644
--- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/GenericHandler.cs
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/GenericHandler.cs
@@ -19,6 +19,16 @@ public GenericHandler(RequestDelegate next)
public async Task Invoke(HttpContext context)
{
PathString path = context.Request.Path;
+ if (path.Equals(new PathString("/swagger.json")))
+ {
+ using (var stream = typeof(GenericHandler).Assembly.GetManifestResourceStream("NetCoreServer.swagger.json"))
+ {
+ context.Response.ContentType = "application/json";
+ await stream.CopyToAsync(context.Response.Body);
+ }
+ return;
+ }
+
if (path.Equals(new PathString("/deflate.ashx")))
{
await DeflateHandler.InvokeAsync(context);
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/NetCoreServer.csproj b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/NetCoreServer.csproj
index a458eb3eeec5ed..d1c331b0aabcdb 100644
--- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/NetCoreServer.csproj
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/NetCoreServer.csproj
@@ -16,11 +16,16 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Startup.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Startup.cs
index 5eade747eb9d34..0dec43c28f5b82 100644
--- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Startup.cs
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Startup.cs
@@ -10,10 +10,6 @@
using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;
using OpenTelemetry.Exporter.Geneva;
-using System.Diagnostics.Metrics;
-using Microsoft.AspNetCore.Http;
-using System.Threading.Tasks;
-using System.Diagnostics;
using System.Collections.Generic;
#endif
@@ -31,7 +27,8 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
#if GENEVA_TELEMETRY
- services.AddOpenTelemetryMetrics((builder) => builder
+ services.AddOpenTelemetry()
+ .WithMetrics((builder) => builder
.AddAspNetCoreInstrumentation()
.AddGenevaMetricExporter(options =>
{
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/swagger.json b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/swagger.json
new file mode 100644
index 00000000000000..8472a602b9bdc8
--- /dev/null
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/swagger.json
@@ -0,0 +1,294 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": ".NET Networking Echo Test Servers",
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ },
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "https://corefx-net-http11.azurewebsites.net/"
+ }
+ ],
+ "paths": {
+ "/deflate.ashx": {
+ "get": {
+ "summary": "Get DEFLATE compressed content",
+ "description": "Get DEFLATE compressed content",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "text/plain": {
+ "example": "Sending DEFLATE compressed"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/echo.ashx": {
+ "get": {
+ "summary": "Get request data echoed back",
+ "description": "Get request data echoed back",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RequestInformation"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/emptycontent.ashx": {
+ "get": {
+ "summary": "Get empty content back",
+ "description": "Get empty content back",
+ "responses": {
+ "200": {
+ "description": "Successful operation"
+ }
+ }
+ }
+ },
+ "/gzip.ashx": {
+ "get": {
+ "summary": "Get gzip-compressed content",
+ "description": "Get gzip-compressed content",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "text/plain": {
+ "example": "Sending GZIP compressed"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/redirect.ashx": {
+ "get": {
+ "summary": "Get a redirect response",
+ "description": "get a redirect response",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "uri",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RequestInformation"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Failed operation"
+ }
+ }
+ }
+ },
+ "/statuscode.ashx": {
+ "get": {
+ "summary": "Get a specific status code in response",
+ "description": "Get a specific status code in response",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "statuscode",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "statusdescription",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "delay",
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful operation"
+ },
+ "400": {
+ "description": "Error parsing status code"
+ }
+ }
+ }
+ },
+ "/version": {
+ "get": {
+ "summary": "Get version information",
+ "description": "Get version information",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "text/plain": {
+ "example": "Information for: NetCoreServer.dll\nLocation: C:\\home\\site\\wwwroot\nFramework: .NET 6.0.32\nFile Version: 1.0.0.0\nProduct Version: 1.0.0\nCreation Date: 4/11/2019 9:05:08 PM\nLast Modified: 2/28/2022 2:06:00 PM\n"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test.ashx": {
+ "get": {
+ "summary": "Test endpoint",
+ "description": "Test endpoint",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "text/plain": {
+ "example": {
+ "Method": "GET",
+ "Url": "/test.ashx",
+ "Headers": {
+ "Accept": "text/plain",
+ "Accept-Encoding": "gzip, deflate, br, zstd",
+ "Accept-Language": "en-US,en;q=0.9,cs;q=0.8",
+ "Host": "corefx-net-http11.azurewebsites.net",
+ "Max-Forwards": "10",
+ "Referer": "https://editor-next.swagger.io/",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
+ "Origin": "https://editor-next.swagger.io",
+ "sec-ch-ua-platform": "\"Windows\"",
+ "sec-ch-ua": "\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
+ "sec-ch-ua-mobile": "?0",
+ "Sec-Fetch-Site": "cross-site",
+ "Sec-Fetch-Mode": "cors",
+ "Sec-Fetch-Dest": "empty",
+ "X-ARR-LOG-ID": "46cb3aaa-9ad7-44b1-b284-a492ded947ed",
+ "CLIENT-IP": "167.220.196.35:19180",
+ "DISGUISED-HOST": "corefx-net-http11.azurewebsites.net",
+ "X-SITE-DEPLOYMENT-ID": "corefx-net-http11",
+ "WAS-DEFAULT-HOSTNAME": "corefx-net-http11.azurewebsites.net",
+ "X-Forwarded-Proto": "https",
+ "X-AppService-Proto": "https",
+ "X-ARR-SSL": "2048|256|CN=Microsoft Azure RSA TLS Issuing CA 08, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US",
+ "X-Forwarded-TlsVersion": "1.3",
+ "X-Forwarded-For": "167.220.196.35:19180",
+ "X-Original-URL": "/test.ashx",
+ "X-WAWS-Unencoded-URL": "/test.ashx"
+ },
+ "Cookies": {},
+ "BodyContent": "",
+ "BodyLength": 0,
+ "SecureConnection": true,
+ "ClientCertificatePresent": false,
+ "ClientCertificate": null
+ }
+ }
+ },
+ "headers": {
+ "Content-MD5": {
+ "schema": {
+ "type": "string"
+ },
+ "description": "Base64-encoded MD5 hash of the response content"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/large.ashx": {
+ "get": {
+ "summary": "Get a large response",
+ "description": "Get a large response",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "size",
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/octet-stream": {
+ "schema": {
+ "type": "array"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "RequestInformation": {
+ "type": "object",
+ "properties": {
+ "Method": {
+ "type": "string",
+ "example": "GET"
+ },
+ "Url": {
+ "type": "string",
+ "format": "uri",
+ "example": "/echo.ashx"
+ },
+ "Headers": {
+ "type": "object"
+ },
+ "Cookies": {
+ "type": "object"
+ },
+ "BodyContent": {
+ "type": "string"
+ },
+ "BodyLength": {
+ "type": "integer"
+ },
+ "SecureConnection": {
+ "type": "boolean"
+ },
+ "ClientCertificatePresent": {
+ "type": "boolean"
+ },
+ "ClientCertificate": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file