azureaudio-streamingfilestreamresult

FileStreamResult with mimeType audio/mpeg doesn't play stream when deployed to Azure


There is an online radio published over HTTP and a custom port (e.g. http://44.44.44.44:56/stream/1) and I need to implement a proxy that exposes the same radio stream over HTTPS without a custom port (e.g. https://coolradio.azurewebsites.net/api/radio/AzureStream)

I have implemented a dotnet 6 web API and it works when running locally:

public async Task<IActionResult> AzureStream()
{
    var sourceUrl = "http://44.44.44.44:56/stream/1"
    var mimeType = "audio/mpeg";
    var client = new HttpClient();
    var sourceStream = await client.GetStreamAsync(sourceUrl);
    var fileStreamResult = new FileStreamResult(sourceStream, mimeType);
    return fileStreamResult;
}

enter image description here

When I publish this to Azure as a WebApp, I can access my other controllers and this one but instead of playing it just does nothing and the browser renders a slightly different picture, although the HTTP response, headers, and stream are identical:

enter image description here

I tried to avoid using HttpClient and instead downgrade to TcpClient but got the same result and with HttpClient (works locally, doesn't work when deployed to Azure):

var sourceUri = new Uri(sourceUrl); 
var sourceClient = new TcpClient();
string requestString = $"GET {sourceUri.PathAndQuery} HTTP/1.1\r\n"
                + $"Host: {sourceUri.Host}\r\n"
                + "Connection: keep-alive\r\n"
                + "\r\n";

await sourceClient.ConnectAsync(sourceUri.Host, sourceUri.Port);
var sourceStream = sourceClient.GetStream();
var writer = new StreamWriter(sourceStream);
writer.Write(requestString);
writer.Flush();

var fileStreamResult = new FileStreamResult(sourceStream, mimeType);
return fileStreamResult;

One important note, it used to work when I was on dotnetcore but once I switched to dotnet 6, it stopped (only the Azure part, locally works great).

How can I implement this proxy so I can publish it to Azure and expose the radio over HTTPS?


Solution

  • Locally, you are accessing your application hosted in Kestrel. In Windows AppService, there is an additional layer running IIS.

    In your case, IIS struggles with buffering the results of the stream your are passing. There are two solutions:

    Full example with disabled buffering:

    using Microsoft.AspNetCore.Http.Features;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddScoped<MyCustomMiddleware>();
    var app = builder.Build();
    app.UseMiddleware<MyCustomMiddleware>();
    app.MapGet("/audio/", async () =>
    {
        var sourceUrl = "http://44.44.44.44:56/stream/1";
        var mimeType = "audio/mpeg";
        var client = new HttpClient();
        var sourceStream = await client.GetStreamAsync(sourceUrl);
        return Results.Stream(sourceStream, mimeType, enableRangeProcessing: false);
    });
    
    app.Run();
    
    public class MyCustomMiddleware : IMiddleware
    {
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var responseBodyFeature = context.Features.Get<IHttpResponseBodyFeature>();
            // Only required for Windows/IIS
            responseBodyFeature?.DisableBuffering();
            await next(context);
        }
    }
    

    You can remove the middleware if you are running on Linux. Your provided sample code using HttpClient failed for me with a NotSupportedException. So I used the other solution I usually use.

    PS: I'm not sure if you can safely dispose HttpClient in this example. I added using var client = new HttpClient(); and the application worked just fine, but I did not check it further.