File: Listener\ServerOnExistingQueueTests.cs
Web Access
Project: src\src\Servers\HttpSys\test\FunctionalTests\Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj (Microsoft.AspNetCore.Server.HttpSys.FunctionalTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.HttpSys.Internal;
using Microsoft.AspNetCore.InternalTesting;
using Windows.Win32;
using Xunit;
 
namespace Microsoft.AspNetCore.Server.HttpSys.Listener;
 
public class ServerOnExistingQueueTests
{
    [ConditionalFact]
    public async Task Server_200OK_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
 
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_SendHelloWorld_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
 
        Task<string> responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Response.ContentLength = 11;
        await using (var writer = new StreamWriter(context.Response.Body))
        {
            await writer.WriteAsync("Hello World");
        }
 
        string response = await responseTask;
        Assert.Equal("Hello World", response);
    }
 
    [ConditionalFact]
    public async Task Server_EchoHelloWorld_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
 
        var responseTask = SendRequestAsync(address, "Hello World");
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        string input = await new StreamReader(context.Request.Body).ReadToEndAsync();
        Assert.Equal("Hello World", input);
        context.Response.ContentLength = 11;
        await using (var writer = new StreamWriter(context.Response.Body))
        {
            await writer.WriteAsync("Hello World");
        }
 
        var response = await responseTask;
        Assert.Equal("Hello World", response);
    }
 
    [ConditionalFact]
    // No-ops if you did not create the queue
    public async Task Server_SetQueueLimit_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.RequestQueueLimit = 1001;
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_PathBase_Success()
    {
        using var baseServer = Utilities.CreateDynamicHttpServer("/PathBase", out var root, out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.UrlPrefixes.Add(address); // Need to mirror the setting so we can parse out PathBase
 
        var responseTask = SendRequestAsync(root + "/pathBase/paTh");
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal("/pathBase", context.Request.PathBase);
        Assert.Equal("/paTh", context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_PathBaseMismatch_Success()
    {
        using var baseServer = Utilities.CreateDynamicHttpServer("/PathBase", out var root, out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
 
        var responseTask = SendRequestAsync(root + "/pathBase/paTh");
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal(string.Empty, context.Request.PathBase);
        Assert.Equal("/pathBase/paTh", context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalTheory]
    [InlineData("/", "/", "", "/")]
    [InlineData("/basepath/", "/basepath", "/basepath", "")]
    [InlineData("/basepath/", "/basepath/", "/basepath", "/")]
    [InlineData("/basepath/", "/basepath/subpath", "/basepath", "/subpath")]
    [InlineData("/base path/", "/base%20path/sub%20path", "/base path", "/sub path")]
    [InlineData("/base葉path/", "/base%E8%91%89path/sub%E8%91%89path", "/base葉path", "/sub葉path")]
    [InlineData("/basepath/", "/basepath/sub%2Fpath", "/basepath", "/sub%2Fpath")]
    public async Task Server_PathSplitting(string pathBase, string requestPath, string expectedPathBase, string expectedPath)
    {
        using var baseServer = Utilities.CreateDynamicHttpServer(pathBase, out var root, out var baseAddress);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.UrlPrefixes.Add(baseAddress); // Keep them in sync
 
        var responseTask = SendRequestAsync(root + requestPath);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal(expectedPathBase, context.Request.PathBase);
        Assert.Equal(expectedPath, context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_LongestPathSplitting()
    {
        using var baseServer = Utilities.CreateDynamicHttpServer("/basepath", out var root, out var baseAddress);
        baseServer.Options.UrlPrefixes.Add(baseAddress + "secondTier");
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.UrlPrefixes.Add(baseAddress); // Keep them in sync
        server.Options.UrlPrefixes.Add(baseAddress + "secondTier");
 
        var responseTask = SendRequestAsync(root + "/basepath/secondTier/path/thing");
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal("/basepath/secondTier", context.Request.PathBase);
        Assert.Equal("/path/thing", context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    // Changes to the base server are reflected
    public async Task Server_HotAddPrefix_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.UrlPrefixes.Add(address); // Keep them in sync
 
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal(string.Empty, context.Request.PathBase);
        Assert.Equal("/", context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
 
        address += "pathbase/";
        baseServer.Options.UrlPrefixes.Add(address);
        server.Options.UrlPrefixes.Add(address);
 
        responseTask = SendRequestAsync(address);
 
        context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal("/pathbase", context.Request.PathBase);
        Assert.Equal("/", context.Request.Path);
        context.Dispose();
 
        response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    // Changes to the base server are reflected
    public async Task Server_HotRemovePrefix_Success()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServerOnExistingQueue(baseServer.Options.RequestQueueName);
        server.Options.UrlPrefixes.Add(address); // Keep them in sync
 
        address += "pathbase/";
        baseServer.Options.UrlPrefixes.Add(address);
        server.Options.UrlPrefixes.Add(address);
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal("/pathbase", context.Request.PathBase);
        Assert.Equal("/", context.Request.Path);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
 
        Assert.True(baseServer.Options.UrlPrefixes.Remove(address));
        Assert.True(server.Options.UrlPrefixes.Remove(address));
 
        responseTask = SendRequestAsync(address);
 
        context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
        Assert.Equal(string.Empty, context.Request.PathBase);
        Assert.Equal("/pathbase/", context.Request.Path);
        context.Dispose();
 
        response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_CreateOrAttach_NoUrlPrefix_NewUrlPrefixWorks()
    {
        var queueName = Guid.NewGuid().ToString();
 
        // Create a queue without a UrlGroup or any UrlPrefixes
        var statusCode = PInvoke.HttpCreateRequestQueue(
                HttpApi.Version,
                queueName,
                default,
                0,
                out var requestQueueHandle);
 
        Assert.True(statusCode == ErrorCodes.ERROR_SUCCESS);
 
        using var server = Utilities.CreateServer(options =>
        {
            options.RequestQueueName = queueName;
            options.RequestQueueMode = RequestQueueMode.CreateOrAttach;
            options.UrlPrefixes.Add("http://localhost:0");
        });
 
        var address = server.Options.UrlPrefixes.First().FullPrefix;
 
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_CreateOrAttach_UrlPrefixExist_ExistingUrlPrefixWorks()
    {
        using var baseServer = Utilities.CreateHttpServer(out var address);
        using var server = Utilities.CreateServer(options =>
        {
            options.RequestQueueName = baseServer.Options.RequestQueueName;
            options.RequestQueueMode = RequestQueueMode.CreateOrAttach;
            options.UrlPrefixes.Add(address);
        });
 
        var responseTask = SendRequestAsync(address);
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    [ConditionalFact]
    public async Task Server_CreateOrAttach_UrlPrefixExist_NewAndExistingUrlPrefixsWork()
    {
        using var baseServer = Utilities.CreateHttpServerReturnRoot("/baseServer", out string rootAddress);
        using var server = Utilities.CreateServer(options =>
        {
            options.RequestQueueName = baseServer.Options.RequestQueueName;
            options.RequestQueueMode = RequestQueueMode.CreateOrAttach;
            options.UrlPrefixes.Add(rootAddress + "/server");
        });
 
        var responseTask = SendRequestAsync(rootAddress + "/baseServer");
 
        var context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        var response = await responseTask;
        Assert.Equal(string.Empty, response);
 
        responseTask = SendRequestAsync(rootAddress + "/server");
 
        context = await server.AcceptAsync(Utilities.DefaultTimeout);
        context.Dispose();
 
        response = await responseTask;
        Assert.Equal(string.Empty, response);
    }
 
    private async Task<string> SendRequestAsync(string uri)
    {
        using HttpClient client = new HttpClient();
        return await client.GetStringAsync(uri);
    }
 
    private async Task<string> SendRequestAsync(string uri, string upload)
    {
        using HttpClient client = new HttpClient();
        HttpResponseMessage response = await client.PostAsync(uri, new StringContent(upload));
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}