#nullable disable
#pragma warning disable CS1591
using System ;
using System.Globalization ;
using System.Net.Http ;
using System.Net.Mime ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
using System.Xml.Linq ;
using Emby.Dlna.Common ;
using MediaBrowser.Common.Net ;
namespace Emby.Dlna.PlayTo
{
public class SsdpHttpClient
{
private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50" ;
private const string FriendlyName = "Jellyfin" ;
private readonly IHttpClientFactory _httpClientFactory ;
public SsdpHttpClient ( IHttpClientFactory httpClientFactory )
{
_httpClientFactory = httpClientFactory ;
}
public async Task < XDocument > SendCommandAsync (
string baseUrl ,
DeviceService service ,
string command ,
string postData ,
string header = null ,
CancellationToken cancellationToken = default )
{
var url = NormalizeServiceUrl ( baseUrl , service . ControlUrl ) ;
using var response = await PostSoapDataAsync (
url ,
$"\" { service . ServiceType } # { command } \ "" ,
postData ,
header ,
cancellationToken )
. ConfigureAwait ( false ) ;
response . EnsureSuccessStatusCode ( ) ;
await using var stream = await response . Content . ReadAsStreamAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
return await XDocument . LoadAsync (
stream ,
LoadOptions . None ,
cancellationToken ) . ConfigureAwait ( false ) ;
}
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 ;
}
public async Task SubscribeAsync (
string url ,
string ip ,
int port ,
string localIp ,
int eventport ,
int timeOut = 3600 )
{
using var options = new HttpRequestMessage ( new HttpMethod ( "SUBSCRIBE" ) , url ) ;
options . Headers . UserAgent . ParseAdd ( USERAGENT ) ;
options . Headers . TryAddWithoutValidation ( "HOST" , ip + ":" + port . ToString ( CultureInfo . InvariantCulture ) ) ;
options . Headers . TryAddWithoutValidation ( "CALLBACK" , "<" + localIp + ":" + eventport . ToString ( CultureInfo . InvariantCulture ) + ">" ) ;
options . Headers . TryAddWithoutValidation ( "NT" , "upnp:event" ) ;
options . Headers . TryAddWithoutValidation ( "TIMEOUT" , "Second-" + timeOut . ToString ( CultureInfo . InvariantCulture ) ) ;
using var response = await _httpClientFactory . CreateClient ( NamedClient . Default )
. SendAsync ( options , HttpCompletionOption . ResponseHeadersRead )
. ConfigureAwait ( false ) ;
response . EnsureSuccessStatusCode ( ) ;
}
public async Task < XDocument > GetDataAsync ( string url , CancellationToken cancellationToken )
{
using var options = new HttpRequestMessage ( HttpMethod . Get , url ) ;
options . Headers . UserAgent . ParseAdd ( USERAGENT ) ;
options . Headers . TryAddWithoutValidation ( "FriendlyName.DLNA.ORG" , FriendlyName ) ;
using var response = await _httpClientFactory . CreateClient ( NamedClient . Default ) . SendAsync ( options , HttpCompletionOption . ResponseHeadersRead , cancellationToken ) . ConfigureAwait ( false ) ;
response . EnsureSuccessStatusCode ( ) ;
await using var stream = await response . Content . ReadAsStreamAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
try
{
return await XDocument . LoadAsync (
stream ,
LoadOptions . None ,
cancellationToken ) . ConfigureAwait ( false ) ;
}
catch
{
return null ;
}
}
Fix ObjectDisposedException
```
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, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, 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.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Emby.Dlna.PlayTo.SsdpHttpClient.SendCommandAsync(String baseUrl, DeviceService service, String command, String postData, String header, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/SsdpHttpClient.cs:line 41
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 629
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 445
```
5 years ago
private async Task < HttpResponseMessage > PostSoapDataAsync (
string url ,
string soapAction ,
string postData ,
string header ,
CancellationToken cancellationToken )
{
if ( soapAction [ 0 ] ! = '\"' )
{
soapAction = $"\" { soapAction } \ "" ;
}
using var options = new HttpRequestMessage ( HttpMethod . Post , url ) ;
options . Headers . UserAgent . ParseAdd ( USERAGENT ) ;
options . Headers . TryAddWithoutValidation ( "SOAPACTION" , soapAction ) ;
options . Headers . TryAddWithoutValidation ( "Pragma" , "no-cache" ) ;
options . Headers . TryAddWithoutValidation ( "FriendlyName.DLNA.ORG" , FriendlyName ) ;
if ( ! string . IsNullOrEmpty ( header ) )
{
options . Headers . TryAddWithoutValidation ( "contentFeatures.dlna.org" , header ) ;
}
options . Content = new StringContent ( postData , Encoding . UTF8 , MediaTypeNames . Text . Xml ) ;
Fix ObjectDisposedException
```
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, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, 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.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Emby.Dlna.PlayTo.SsdpHttpClient.SendCommandAsync(String baseUrl, DeviceService service, String command, String postData, String header, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/SsdpHttpClient.cs:line 41
at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 629
at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 445
```
5 years ago
return await _httpClientFactory . CreateClient ( NamedClient . Default ) . SendAsync ( options , HttpCompletionOption . ResponseHeadersRead , cancellationToken ) . ConfigureAwait ( false ) ;
}
}
}