@ -6,19 +6,16 @@ using System;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.IO.Compression ;
using System.Net ;
using System.Runtime.Serialization ;
using System.Text ;
using System.Threading.Tasks ;
using System.Xml ;
using Emby.Server.Implementations.HttpServer ;
using Emby.Server.Implementations.Services ;
using MediaBrowser.Model.IO ;
using MediaBrowser.Model.Services ;
using IRequest = MediaBrowser . Model . Services . IRequest ;
using MimeTypes = MediaBrowser . Model . Net . MimeTypes ;
using StreamWriter = Emby . Server . Implementations . HttpServer . StreamWriter ;
namespace Emby.Server.Implementations.HttpServer
{
@ -193,50 +190,37 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public object ToOptimizedResult < T > ( IRequest request , T dto )
{
var compressionType = GetCompressionType ( request ) ;
if ( compressionType = = null )
{
var contentType = request . ResponseContentType ;
switch ( GetRealContentType ( contentType ) )
{
case "application/xml" :
case "text/xml" :
case "text/xml; charset=utf-8" : //"text/xml; charset=utf-8" also matches xml
return SerializeToXmlString ( dto ) ;
case "application/json" :
case "text/json" :
return _jsonSerializer . SerializeToString ( dto ) ;
}
}
var contentType = request . ResponseContentType ;
// Do not use the memoryStreamFactory here, they don't place nice with compression
using ( var ms = new MemoryStream ( ) )
switch ( GetRealContentType ( contentType ) )
{
var contentType = request . ResponseContentType ;
var writerFn = RequestHelper . GetResponseWriter ( HttpListenerHost . Instance , contentType ) ;
case "application/xml" :
case "text/xml" :
case "text/xml; charset=utf-8" : //"text/xml; charset=utf-8" also matches xml
return SerializeToXmlString ( dto ) ;
writerFn ( dto , ms ) ;
case "application/json" :
case "text/json" :
return _jsonSerializer . SerializeToString ( dto ) ;
default :
{
var ms = new MemoryStream ( ) ;
var writerFn = RequestHelper . GetResponseWriter ( HttpListenerHost . Instance , contentType ) ;
ms . Position = 0 ;
writerFn ( dto , ms ) ;
ms . Position = 0 ;
var responseHeaders = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
if ( string . Equals ( request . Verb , "head" , StringComparison . OrdinalIgnoreCase ) )
{
return GetHttpResult ( new byte [ ] { } , contentType , true ) ;
}
return GetCompressedResult ( ms , compressionType , responseHeaders , false , request . ResponseContentType ) . Result ;
return GetHttpResult ( ms , contentType , true ) ;
}
}
}
private static Stream GetCompressionStream ( Stream outputStream , string compressionType )
{
if ( compressionType = = "deflate" )
return new DeflateStream ( outputStream , CompressionMode . Compress , true ) ;
if ( compressionType = = "gzip" )
return new GZipStream ( outputStream , CompressionMode . Compress , true ) ;
throw new NotSupportedException ( compressionType ) ;
}
public static string GetRealContentType ( string contentType )
{
return contentType = = null
@ -568,123 +552,47 @@ namespace Emby.Server.Implementations.HttpServer
var contentType = options . ContentType ;
var responseHeaders = options . ResponseHeaders ;
var requestedCompressionType = GetCompressionType ( requestContext ) ;
//var requestedCompressionType = GetCompressionType(requestContext) ;
if ( ! compress | | string . IsNullOrEmpty ( requestedCompressionType ) )
{
var rangeHeader = requestContext . Headers . Get ( "Range" ) ;
if ( ! isHeadRequest & & ! string . IsNullOrWhiteSpace ( options . Path ) )
{
return new FileWriter ( options . Path , contentType , rangeHeader , _logger , _fileSystem )
{
OnComplete = options . OnComplete ,
OnError = options . OnError ,
FileShare = options . FileShare
} ;
}
if ( ! string . IsNullOrWhiteSpace ( rangeHeader ) )
{
var stream = await factoryFn ( ) . ConfigureAwait ( false ) ;
return new RangeRequestWriter ( rangeHeader , stream , contentType , isHeadRequest , _logger )
{
OnComplete = options . OnComplete
} ;
}
else
{
var stream = await factoryFn ( ) . ConfigureAwait ( false ) ;
responseHeaders [ "Content-Length" ] = stream . Length . ToString ( UsCulture ) ;
if ( isHeadRequest )
{
stream . Dispose ( ) ;
return GetHttpResult ( new byte [ ] { } , contentType , true ) ;
}
return new StreamWriter ( stream , contentType , _logger )
{
OnComplete = options . OnComplete ,
OnError = options . OnError
} ;
}
}
var rangeHeader = requestContext . Headers . Get ( "Range" ) ;
using ( var stream = await factoryFn ( ) . ConfigureAwait ( false ) )
if ( ! isHeadRequest & & ! string . IsNullOrWhiteSpace ( options . Path ) )
{
return await GetCompressedResult ( stream , requestedCompressionType , responseHeaders , isHeadRequest , contentType ) . ConfigureAwait ( false ) ;
return new FileWriter ( options . Path , contentType , rangeHeader , _logger , _fileSystem )
{
OnComplete = options . OnComplete ,
OnError = options . OnError ,
FileShare = options . FileShare
} ;
}
}
private async Task < IHasHeaders > GetCompressedResult ( Stream stream ,
string requestedCompressionType ,
IDictionary < string , string > responseHeaders ,
bool isHeadRequest ,
string contentType )
{
using ( var reader = new MemoryStream ( ) )
if ( ! string . IsNullOrWhiteSpace ( rangeHeader ) )
{
await stream . CopyToAsync ( reader ) . ConfigureAwait ( false ) ;
reader . Position = 0 ;
var content = reader . ToArray ( ) ;
var stream = await factoryFn ( ) . ConfigureAwait ( false ) ;
if ( content . Length > = 1024 )
return new RangeRequestWriter ( rangeHeader , stream , contentType , isHeadRequest , _logger )
{
content = Compress ( content , requestedCompressionType ) ;
responseHeaders [ "Content-Encoding" ] = requestedCompressionType ;
}
OnComplete = options . OnComplete
} ;
}
else
{
var stream = await factoryFn ( ) . ConfigureAwait ( false ) ;
responseHeaders [ "Vary" ] = "Accept-Encoding" ;
responseHeaders [ "Content-Length" ] = content . Length . ToString ( UsCulture ) ;
responseHeaders [ "Content-Length" ] = stream . Length . ToString ( UsCulture ) ;
if ( isHeadRequest )
{
stream . Dispose ( ) ;
return GetHttpResult ( new byte [ ] { } , contentType , true ) ;
}
return GetHttpResult ( content , contentType , true , responseHeaders ) ;
}
}
private byte [ ] Compress ( byte [ ] bytes , string compressionType )
{
if ( compressionType = = "deflate" )
return Deflate ( bytes ) ;
if ( compressionType = = "gzip" )
return GZip ( bytes ) ;
throw new NotSupportedException ( compressionType ) ;
}
private byte [ ] Deflate ( byte [ ] bytes )
{
// In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
// Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
using ( var ms = new MemoryStream ( ) )
using ( var zipStream = new DeflateStream ( ms , CompressionMode . Compress ) )
{
zipStream . Write ( bytes , 0 , bytes . Length ) ;
zipStream . Dispose ( ) ;
return ms . ToArray ( ) ;
}
}
private byte [ ] GZip ( byte [ ] buffer )
{
using ( var ms = new MemoryStream ( ) )
using ( var zipStream = new GZipStream ( ms , CompressionMode . Compress ) )
{
zipStream . Write ( buffer , 0 , buffer . Length ) ;
zipStream . Dispose ( ) ;
return ms . ToArray ( ) ;
return new StreamWriter ( stream , contentType , _logger )
{
OnComplete = options . OnComplete ,
OnError = options . OnError
} ;
}
}