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;
}
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:
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
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.
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.
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:
You can remove the middleware if you are running on Linux. Your provided sample code using
HttpClient
failed for me with aNotSupportedException
. So I used the other solution I usually use.PS: I’m not sure if you can safely dispose
HttpClient
in this example. I addedusing var client = new HttpClient();
and the application worked just fine, but I did not check it further.