skip to Main Content

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.1rn"
                + $"Host: {sourceUri.Host}rn"
                + "Connection: keep-alivern"
                + "rn";

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?

2

Answers


  1. I can’t speak to the Azure implementation side of things, so perhaps someone can give you a better answer there. But, I can help you with this problem overall.

    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:

    That’s not quite true.

    The stream data being sent from your proxy stops after ~13 KB or so, which is why it can’t be played. You can see this with curl -vvv.

    The source stream is a SHOUTcast server, which does not support chunked transfer encoding. For legacy compatibility, almost none of these streaming servers do. I suspect that the authors of the .NET 6 HttpClient are confused by the HTTP/1.0-style response where there is no Content-Length response header, but also no transfer encoding. I suspect it’s assuming chunked transfer.

    If possible, your server could just handle the TLS side of the connection and proxy the HTTP request/response transparently, with no interpretation of HTTP at all.

    If this is not possible with your stack, you could still make your own lightweight HTTP client, connect to the source stream, and then relay the stream data back to your clients. That is, your server still receives HTTPS requests but the response data comes from a regular TCP socket connection.

    Login or Signup to reply.
  2. 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:

    • Use Linux AppService plan
    • Disable buffering

    Full example:

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search