#pragma warning disable CS1591
using System ;
using System.Globalization ;
using System.IO ;
using System.Net.Http ;
using System.Net.Mime ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Threading ;
using System.Threading.Tasks ;
using System.Xml ;
using System.Xml.Linq ;
using Emby.Dlna.Common ;
using MediaBrowser.Common.Net ;
using Microsoft.Extensions.Logging ;
namespace Emby.Dlna.PlayTo
{
/// <summary>
/// Http client for Dlna PlayTo function.
/// </summary>
public partial class DlnaHttpClient
{
private readonly ILogger _logger ;
private readonly IHttpClientFactory _httpClientFactory ;
public DlnaHttpClient ( ILogger logger , IHttpClientFactory httpClientFactory )
{
_logger = logger ;
_httpClientFactory = httpClientFactory ;
}
[GeneratedRegex("(&(?![a-z] * ; ) ) ")]
private static partial Regex EscapeAmpersandRegex ( ) ;
private static string NormalizeServiceUrl ( string baseUrl , string serviceUrl )
{
// If it's already a complete url, don't stick anything onto the front of it
if ( serviceUrl . StartsWith ( "http" , StringComparison . OrdinalIgnoreCase ) )
{
return serviceUrl ;
}
if ( ! serviceUrl . StartsWith ( '/' ) )
{
serviceUrl = "/" + serviceUrl ;
}
return baseUrl + serviceUrl ;
}
private async Task < XDocument ? > SendRequestAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
{
var client = _httpClientFactory . CreateClient ( NamedClient . Dlna ) ;
using var response = await client . SendAsync ( request , cancellationToken ) . ConfigureAwait ( false ) ;
response . EnsureSuccessStatusCode ( ) ;
Stream stream = await response . Content . ReadAsStreamAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
await using ( stream . ConfigureAwait ( false ) )
{
try
{
return await XDocument . LoadAsync (
stream ,
LoadOptions . None ,
cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( XmlException )
{
// try correcting the Xml response with common errors
stream . Position = 0 ;
using StreamReader sr = new StreamReader ( stream ) ;
var xmlString = await sr . ReadToEndAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
// find and replace unescaped ampersands (&)
xmlString = EscapeAmpersandRegex ( ) . Replace ( xmlString , "&" ) ;
try
{
// retry reading Xml
using var xmlReader = new StringReader ( xmlString ) ;
return await XDocument . LoadAsync (
xmlReader ,
LoadOptions . None ,
cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( XmlException ex )
{
_logger . LogError ( ex , "Failed to parse response" ) ;
_logger . LogDebug ( "Malformed response: {Content}\n" , xmlString ) ;
return null ;
}
}
}
}
Fix regression in DlnaHttpClient
```
[18:53:50] [ERR] [25] Emby.Dlna.Main.DlnaEntryPoint: Error updating device info for 192.168.1.21 - Sonos Connect:Amp Berging
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Emby.Dlna.PlayTo.DlnaHttpClient.SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/DlnaHttpClient.cs:line 47
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 705
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 521
```
3 years ago
public async Task < XDocument ? > GetDataAsync ( string url , CancellationToken cancellationToken )
{
using var request = new HttpRequestMessage ( HttpMethod . Get , url ) ;
Fix regression in DlnaHttpClient
```
[18:53:50] [ERR] [25] Emby.Dlna.Main.DlnaEntryPoint: Error updating device info for 192.168.1.21 - Sonos Connect:Amp Berging
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Emby.Dlna.PlayTo.DlnaHttpClient.SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/DlnaHttpClient.cs:line 47
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 705
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 521
```
3 years ago
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync ( request , cancellationToken ) . ConfigureAwait ( false ) ;
}
Fix regression in DlnaHttpClient
```
[18:53:50] [ERR] [25] Emby.Dlna.Main.DlnaEntryPoint: Error updating device info for 192.168.1.21 - Sonos Connect:Amp Berging
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Emby.Dlna.PlayTo.DlnaHttpClient.SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/DlnaHttpClient.cs:line 47
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 705
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 521
```
3 years ago
public async Task < XDocument ? > SendCommandAsync (
string baseUrl ,
DeviceService service ,
string command ,
string postData ,
string? header = null ,
CancellationToken cancellationToken = default )
{
using var request = new HttpRequestMessage ( HttpMethod . Post , NormalizeServiceUrl ( baseUrl , service . ControlUrl ) )
{
Content = new StringContent ( postData , Encoding . UTF8 , MediaTypeNames . Text . Xml )
} ;
request . Headers . TryAddWithoutValidation (
"SOAPACTION" ,
string . Format (
CultureInfo . InvariantCulture ,
"\"{0}#{1}\"" ,
service . ServiceType ,
command ) ) ;
request . Headers . Pragma . ParseAdd ( "no-cache" ) ;
if ( ! string . IsNullOrEmpty ( header ) )
{
request . Headers . TryAddWithoutValidation ( "contentFeatures.dlna.org" , header ) ;
}
Fix regression in DlnaHttpClient
```
[18:53:50] [ERR] [25] Emby.Dlna.Main.DlnaEntryPoint: Error updating device info for 192.168.1.21 - Sonos Connect:Amp Berging
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Emby.Dlna.PlayTo.DlnaHttpClient.SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/DlnaHttpClient.cs:line 47
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 705
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 521
```
3 years ago
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync ( request , cancellationToken ) . ConfigureAwait ( false ) ;
}
}
}