From 852460b99155e015ed5f1d7ad2fab0281bfdfbec Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 25 Feb 2019 23:34:32 +0100 Subject: kestrel init --- .../SocketSharp/RequestMono.cs | 681 +++++++++++++++++++++ 1 file changed, 681 insertions(+) create mode 100644 Emby.Server.Implementations/SocketSharp/RequestMono.cs (limited to 'Emby.Server.Implementations/SocketSharp/RequestMono.cs') diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs new file mode 100644 index 000000000..a8142aef6 --- /dev/null +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -0,0 +1,681 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Model.Services; +using Microsoft.Extensions.Primitives; + +namespace Emby.Server.Implementations.SocketSharp +{ + public partial class WebSocketSharpRequest : IHttpRequest + { + internal static string GetParameter(string header, string attr) + { + int ap = header.IndexOf(attr, StringComparison.Ordinal); + if (ap == -1) + { + return null; + } + + ap += attr.Length; + if (ap >= header.Length) + { + return null; + } + + char ending = header[ap]; + if (ending != '"') + { + ending = ' '; + } + + int end = header.IndexOf(ending, ap + 1); + if (end == -1) + { + return ending == '"' ? null : header.Substring(ap); + } + + return header.Substring(ap + 1, end - ap - 1); + } + + private async Task LoadMultiPart(WebROCollection form) + { + string boundary = GetParameter(ContentType, "; boundary="); + if (boundary == null) + { + return; + } + + using (var requestStream = InputStream) + { + // DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request + // Not ending with \r\n? + var ms = new MemoryStream(32 * 1024); + await requestStream.CopyToAsync(ms).ConfigureAwait(false); + + var input = ms; + ms.WriteByte((byte)'\r'); + ms.WriteByte((byte)'\n'); + + input.Position = 0; + + // Uncomment to debug + // var content = new StreamReader(ms).ReadToEnd(); + // Console.WriteLine(boundary + "::" + content); + // input.Position = 0; + + var multi_part = new HttpMultipart(input, boundary, ContentEncoding); + + HttpMultipart.Element e; + while ((e = multi_part.ReadNextElement()) != null) + { + if (e.Filename == null) + { + byte[] copy = new byte[e.Length]; + + input.Position = e.Start; + input.Read(copy, 0, (int)e.Length); + + form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length)); + } + else + { + // We use a substream, as in 2.x we will support large uploads streamed to disk, + var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length); + files[e.Name] = sub; + } + } + } + } + + public async Task GetFormData() + { + var form = new WebROCollection(); + files = new Dictionary(); + + if (IsContentType("multipart/form-data", true)) + { + await LoadMultiPart(form).ConfigureAwait(false); + } + else if (IsContentType("application/x-www-form-urlencoded", true)) + { + await LoadWwwForm(form).ConfigureAwait(false); + } + +#if NET_4_0 + if (validateRequestNewMode && !checked_form) { + // Setting this before calling the validator prevents + // possible endless recursion + checked_form = true; + ValidateNameValueCollection("Form", query_string_nvc, RequestValidationSource.Form); + } else +#endif + if (validate_form && !checked_form) + { + checked_form = true; + ValidateNameValueCollection("Form", form); + } + + return form; + } + + public string Accept => StringValues.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"].ToString(); + + public string Authorization => StringValues.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"].ToString(); + + protected bool validate_cookies { get; set; } + protected bool validate_query_string { get; set; } + protected bool validate_form { get; set; } + protected bool checked_cookies { get; set; } + protected bool checked_query_string { get; set; } + protected bool checked_form { get; set; } + + private static void ThrowValidationException(string name, string key, string value) + { + string v = "\"" + value + "\""; + if (v.Length > 20) + { + v = v.Substring(0, 16) + "...\""; + } + + string msg = string.Format( + CultureInfo.InvariantCulture, + "A potentially dangerous Request.{0} value was detected from the client ({1}={2}).", + name, + key, + v); + + throw new Exception(msg); + } + + private static void ValidateNameValueCollection(string name, QueryParamCollection coll) + { + if (coll == null) + { + return; + } + + foreach (var pair in coll) + { + var key = pair.Name; + var val = pair.Value; + if (val != null && val.Length > 0 && IsInvalidString(val)) + { + ThrowValidationException(name, key, val); + } + } + } + + internal static bool IsInvalidString(string val) + => IsInvalidString(val, out var validationFailureIndex); + + internal static bool IsInvalidString(string val, out int validationFailureIndex) + { + validationFailureIndex = 0; + + int len = val.Length; + if (len < 2) + { + return false; + } + + char current = val[0]; + for (int idx = 1; idx < len; idx++) + { + char next = val[idx]; + + // See http://secunia.com/advisories/14325 + if (current == '<' || current == '\xff1c') + { + if (next == '!' || next < ' ' + || (next >= 'a' && next <= 'z') + || (next >= 'A' && next <= 'Z')) + { + validationFailureIndex = idx - 1; + return true; + } + } + else if (current == '&' && next == '#') + { + validationFailureIndex = idx - 1; + return true; + } + + current = next; + } + + return false; + } + + public void ValidateInput() + { + validate_cookies = true; + validate_query_string = true; + validate_form = true; + } + + private bool IsContentType(string ct, bool starts_with) + { + if (ct == null || ContentType == null) + { + return false; + } + + if (starts_with) + { + return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase); + } + + return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase); + } + + private async Task LoadWwwForm(WebROCollection form) + { + using (var input = InputStream) + { + using (var ms = new MemoryStream()) + { + await input.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + + using (var s = new StreamReader(ms, ContentEncoding)) + { + var key = new StringBuilder(); + var value = new StringBuilder(); + int c; + + while ((c = s.Read()) != -1) + { + if (c == '=') + { + value.Length = 0; + while ((c = s.Read()) != -1) + { + if (c == '&') + { + AddRawKeyValue(form, key, value); + break; + } + else + { + value.Append((char)c); + } + } + + if (c == -1) + { + AddRawKeyValue(form, key, value); + return; + } + } + else if (c == '&') + { + AddRawKeyValue(form, key, value); + } + else + { + key.Append((char)c); + } + } + + if (c == -1) + { + AddRawKeyValue(form, key, value); + } + } + } + } + } + + private static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value) + { + form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString())); + + key.Length = 0; + value.Length = 0; + } + + private Dictionary files; + + private class WebROCollection : QueryParamCollection + { + public override string ToString() + { + var result = new StringBuilder(); + foreach (var pair in this) + { + if (result.Length > 0) + { + result.Append('&'); + } + + var key = pair.Name; + if (key != null && key.Length > 0) + { + result.Append(key); + result.Append('='); + } + + result.Append(pair.Value); + } + + return result.ToString(); + } + } + private class HttpMultipart + { + + public class Element + { + public string ContentType { get; set; } + + public string Name { get; set; } + + public string Filename { get; set; } + + public Encoding Encoding { get; set; } + + public long Start { get; set; } + + public long Length { get; set; } + + public override string ToString() + { + return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " + + Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture); + } + } + + private const byte LF = (byte)'\n'; + + private const byte CR = (byte)'\r'; + + private Stream data; + + private string boundary; + + private byte[] boundaryBytes; + + private byte[] buffer; + + private bool atEof; + + private Encoding encoding; + + private StringBuilder sb; + + // See RFC 2046 + // In the case of multipart entities, in which one or more different + // sets of data are combined in a single body, a "multipart" media type + // field must appear in the entity's header. The body must then contain + // one or more body parts, each preceded by a boundary delimiter line, + // and the last one followed by a closing boundary delimiter line. + // After its boundary delimiter line, each body part then consists of a + // header area, a blank line, and a body area. Thus a body part is + // similar to an RFC 822 message in syntax, but different in meaning. + + public HttpMultipart(Stream data, string b, Encoding encoding) + { + this.data = data; + boundary = b; + boundaryBytes = encoding.GetBytes(b); + buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--' + this.encoding = encoding; + sb = new StringBuilder(); + } + + public Element ReadNextElement() + { + if (atEof || ReadBoundary()) + { + return null; + } + + var elem = new Element(); + string header; + while ((header = ReadHeaders()) != null) + { + if (header.StartsWith("Content-Disposition:", StringComparison.OrdinalIgnoreCase)) + { + elem.Name = GetContentDispositionAttribute(header, "name"); + elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename")); + } + else if (header.StartsWith("Content-Type:", StringComparison.OrdinalIgnoreCase)) + { + elem.ContentType = header.Substring("Content-Type:".Length).Trim(); + elem.Encoding = GetEncoding(elem.ContentType); + } + } + + long start = data.Position; + elem.Start = start; + long pos = MoveToNextBoundary(); + if (pos == -1) + { + return null; + } + + elem.Length = pos - start; + return elem; + } + + private string ReadLine() + { + // CRLF or LF are ok as line endings. + bool got_cr = false; + int b = 0; + sb.Length = 0; + while (true) + { + b = data.ReadByte(); + if (b == -1) + { + return null; + } + + if (b == LF) + { + break; + } + + got_cr = b == CR; + sb.Append((char)b); + } + + if (got_cr) + { + sb.Length--; + } + + return sb.ToString(); + } + + private static string GetContentDispositionAttribute(string l, string name) + { + int idx = l.IndexOf(name + "=\"", StringComparison.Ordinal); + if (idx < 0) + { + return null; + } + + int begin = idx + name.Length + "=\"".Length; + int end = l.IndexOf('"', begin); + if (end < 0) + { + return null; + } + + if (begin == end) + { + return string.Empty; + } + + return l.Substring(begin, end - begin); + } + + private string GetContentDispositionAttributeWithEncoding(string l, string name) + { + int idx = l.IndexOf(name + "=\"", StringComparison.Ordinal); + if (idx < 0) + { + return null; + } + + int begin = idx + name.Length + "=\"".Length; + int end = l.IndexOf('"', begin); + if (end < 0) + { + return null; + } + + if (begin == end) + { + return string.Empty; + } + + string temp = l.Substring(begin, end - begin); + byte[] source = new byte[temp.Length]; + for (int i = temp.Length - 1; i >= 0; i--) + { + source[i] = (byte)temp[i]; + } + + return encoding.GetString(source, 0, source.Length); + } + + private bool ReadBoundary() + { + try + { + string line; + do + { + line = ReadLine(); + } + while (line.Length == 0); + + if (line[0] != '-' || line[1] != '-') + { + return false; + } + + if (!line.EndsWith(boundary, StringComparison.Ordinal)) + { + return true; + } + } + catch + { + + } + + return false; + } + + private string ReadHeaders() + { + string s = ReadLine(); + if (s.Length == 0) + { + return null; + } + + return s; + } + + private static bool CompareBytes(byte[] orig, byte[] other) + { + for (int i = orig.Length - 1; i >= 0; i--) + { + if (orig[i] != other[i]) + { + return false; + } + } + + return true; + } + + private long MoveToNextBoundary() + { + long retval = 0; + bool got_cr = false; + + int state = 0; + int c = data.ReadByte(); + while (true) + { + if (c == -1) + { + return -1; + } + + if (state == 0 && c == LF) + { + retval = data.Position - 1; + if (got_cr) + { + retval--; + } + + state = 1; + c = data.ReadByte(); + } + else if (state == 0) + { + got_cr = c == CR; + c = data.ReadByte(); + } + else if (state == 1 && c == '-') + { + c = data.ReadByte(); + if (c == -1) + { + return -1; + } + + if (c != '-') + { + state = 0; + got_cr = false; + continue; // no ReadByte() here + } + + int nread = data.Read(buffer, 0, buffer.Length); + int bl = buffer.Length; + if (nread != bl) + { + return -1; + } + + if (!CompareBytes(boundaryBytes, buffer)) + { + state = 0; + data.Position = retval + 2; + if (got_cr) + { + data.Position++; + got_cr = false; + } + + c = data.ReadByte(); + continue; + } + + if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-') + { + atEof = true; + } + else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF) + { + state = 0; + data.Position = retval + 2; + if (got_cr) + { + data.Position++; + got_cr = false; + } + + c = data.ReadByte(); + continue; + } + + data.Position = retval + 2; + if (got_cr) + { + data.Position++; + } + + break; + } + else + { + // state == 1 + state = 0; // no ReadByte() here + } + } + + return retval; + } + + private static string StripPath(string path) + { + if (path == null || path.Length == 0) + { + return path; + } + + if (path.IndexOf(":\\", StringComparison.Ordinal) != 1 + && !path.StartsWith("\\\\", StringComparison.Ordinal)) + { + return path; + } + + return path.Substring(path.LastIndexOf('\\') + 1); + } + } + } +} -- cgit v1.2.3 From 25d3d0b731db793e4afc39ff40e0b6a1a27d6ad8 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 27 Feb 2019 08:02:32 +0100 Subject: Remove some unused stuff --- Emby.Server.Implementations/SocketSharp/RequestMono.cs | 17 ----------------- .../SocketSharp/WebSocketSharpRequest.cs | 17 ----------------- MediaBrowser.Model/Services/IRequest.cs | 10 ---------- 3 files changed, 44 deletions(-) (limited to 'Emby.Server.Implementations/SocketSharp/RequestMono.cs') diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs index a8142aef6..113f76b10 100644 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -105,14 +105,6 @@ namespace Emby.Server.Implementations.SocketSharp await LoadWwwForm(form).ConfigureAwait(false); } -#if NET_4_0 - if (validateRequestNewMode && !checked_form) { - // Setting this before calling the validator prevents - // possible endless recursion - checked_form = true; - ValidateNameValueCollection("Form", query_string_nvc, RequestValidationSource.Form); - } else -#endif if (validate_form && !checked_form) { checked_form = true; @@ -129,8 +121,6 @@ namespace Emby.Server.Implementations.SocketSharp protected bool validate_cookies { get; set; } protected bool validate_query_string { get; set; } protected bool validate_form { get; set; } - protected bool checked_cookies { get; set; } - protected bool checked_query_string { get; set; } protected bool checked_form { get; set; } private static void ThrowValidationException(string name, string key, string value) @@ -210,13 +200,6 @@ namespace Emby.Server.Implementations.SocketSharp return false; } - public void ValidateInput() - { - validate_cookies = true; - validate_query_string = true; - validate_form = true; - } - private bool IsContentType(string ct, bool starts_with) { if (ct == null || ContentType == null) diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 53dce667b..bddccf68b 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -48,8 +48,6 @@ namespace Emby.Server.Implementations.SocketSharp public string AbsoluteUri => request.GetDisplayUrl().TrimEnd('/'); - public string UserHostAddress => request.HttpContext.Connection.RemoteIpAddress.ToString(); - public string XForwardedFor => StringValues.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"].ToString(); @@ -142,19 +140,6 @@ namespace Emby.Server.Implementations.SocketSharp return name; } - internal static bool ContainsNonAsciiChars(string token) - { - for (int i = 0; i < token.Length; ++i) - { - if ((token[i] < 0x20) || (token[i] > 0x7e)) - { - return true; - } - } - - return false; - } - private string NormalizeIp(string ip) { if (!string.IsNullOrWhiteSpace(ip)) @@ -171,8 +156,6 @@ namespace Emby.Server.Implementations.SocketSharp return ip; } - public bool IsSecureConnection => request.IsHttps || XForwardedProtocol == "https"; - public string[] AcceptTypes => request.Headers.GetCommaSeparatedValues(HeaderNames.Accept); private Dictionary items; diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index c7dccdb6f..0fd4ea37b 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -61,11 +61,6 @@ namespace MediaBrowser.Model.Services string AbsoluteUri { get; } - /// - /// The Remote Ip as reported by Request.UserHostAddress - /// - string UserHostAddress { get; } - /// /// The Remote Ip as reported by X-Forwarded-For, X-Real-IP or Request.UserHostAddress /// @@ -76,11 +71,6 @@ namespace MediaBrowser.Model.Services /// string Authorization { get; } - /// - /// e.g. is https or not - /// - bool IsSecureConnection { get; } - string[] AcceptTypes { get; } string PathInfo { get; } -- cgit v1.2.3 From 27e7e792b3d95912787c613f849548809d48f6b1 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 27 Feb 2019 14:23:39 +0100 Subject: Replace some usage of QueryParamCollection --- Emby.Dlna/Api/DlnaServerService.cs | 10 +-- Emby.Dlna/ConnectionManager/ConnectionManager.cs | 3 +- Emby.Dlna/ContentDirectory/ContentDirectory.cs | 2 +- Emby.Dlna/ControlRequest.cs | 5 +- Emby.Dlna/DlnaManager.cs | 17 +++-- Emby.Dlna/IUpnpService.cs | 5 +- .../MediaReceiverRegistrar.cs | 3 +- .../HttpServer/HttpListenerHost.cs | 7 +- .../HttpServer/HttpResultFactory.cs | 19 +++-- .../HttpServer/WebSocketConnection.cs | 3 +- .../Net/WebSocketConnectEventArgs.cs | 3 +- .../Services/ServiceHandler.cs | 2 +- .../Session/SessionWebSocketListener.cs | 3 +- .../SocketSharp/RequestMono.cs | 2 - .../SocketSharp/WebSocketSharpListener.cs | 6 +- .../SocketSharp/WebSocketSharpRequest.cs | 12 ++-- .../SocketSharp/WebSocketSharpResponse.cs | 83 +--------------------- MediaBrowser.Api/Playback/BaseStreamingService.cs | 6 +- MediaBrowser.Controller/Dlna/IDlnaManager.cs | 5 +- .../Net/IWebSocketConnection.cs | 3 +- .../Net/WebSocketConnectEventArgs.cs | 6 +- MediaBrowser.Model/Services/IHttpRequest.cs | 5 -- MediaBrowser.Model/Services/IHttpResponse.cs | 12 ---- MediaBrowser.Model/Services/IRequest.cs | 21 ++---- .../Services/QueryParamCollection.cs | 20 ------ 25 files changed, 66 insertions(+), 197 deletions(-) (limited to 'Emby.Server.Implementations/SocketSharp/RequestMono.cs') diff --git a/Emby.Dlna/Api/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs index 68bf80163..8bf3797f8 100644 --- a/Emby.Dlna/Api/DlnaServerService.cs +++ b/Emby.Dlna/Api/DlnaServerService.cs @@ -136,7 +136,7 @@ namespace Emby.Dlna.Api { var url = Request.AbsoluteUri; var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); - var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress); + var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, request.UuId, serverAddress); var cacheLength = TimeSpan.FromDays(1); var cacheKey = Request.RawUrl.GetMD5(); @@ -147,21 +147,21 @@ namespace Emby.Dlna.Api public object Get(GetContentDirectory request) { - var xml = ContentDirectory.GetServiceXml(Request.Headers.ToDictionary()); + var xml = ContentDirectory.GetServiceXml(); return _resultFactory.GetResult(Request, xml, XMLContentType); } public object Get(GetMediaReceiverRegistrar request) { - var xml = MediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary()); + var xml = MediaReceiverRegistrar.GetServiceXml(); return _resultFactory.GetResult(Request, xml, XMLContentType); } public object Get(GetConnnectionManager request) { - var xml = ConnectionManager.GetServiceXml(Request.Headers.ToDictionary()); + var xml = ConnectionManager.GetServiceXml(); return _resultFactory.GetResult(Request, xml, XMLContentType); } @@ -193,7 +193,7 @@ namespace Emby.Dlna.Api return service.ProcessControlRequest(new ControlRequest { - Headers = Request.Headers.ToDictionary(), + Headers = Request.Headers, InputXml = requestStream, TargetServerUuId = id, RequestedUrl = Request.AbsoluteUri diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs index cc427f2a1..e138b91d6 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -24,7 +23,7 @@ namespace Emby.Dlna.ConnectionManager XmlReaderSettingsFactory = xmlReaderSettingsFactory; } - public string GetServiceXml(IDictionary headers) + public string GetServiceXml() { return new ConnectionManagerXmlBuilder().GetXml(); } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs index b0fec90e6..867e6112f 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs @@ -65,7 +65,7 @@ namespace Emby.Dlna.ContentDirectory } } - public string GetServiceXml(IDictionary headers) + public string GetServiceXml() { return new ContentDirectoryXmlBuilder().GetXml(); } diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index afd9a0b87..907d437f8 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using System.IO; +using Microsoft.AspNetCore.Http; namespace Emby.Dlna { public class ControlRequest { - public IDictionary Headers { get; set; } + public IHeaderDictionary Headers { get; set; } public Stream InputXml { get; set; } @@ -15,7 +16,7 @@ namespace Emby.Dlna public ControlRequest() { - Headers = new Dictionary(); + Headers = new HeaderDictionary(); } } } diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index f53d27451..770a90152 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -17,7 +17,9 @@ using MediaBrowser.Model.Drawing; using MediaBrowser.Model.IO; using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; namespace Emby.Dlna { @@ -205,16 +207,13 @@ namespace Emby.Dlna } } - public DeviceProfile GetProfile(IDictionary headers) + public DeviceProfile GetProfile(IHeaderDictionary headers) { if (headers == null) { throw new ArgumentNullException(nameof(headers)); } - // Convert to case insensitive - headers = new Dictionary(headers, StringComparer.OrdinalIgnoreCase); - var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); if (profile != null) @@ -230,12 +229,12 @@ namespace Emby.Dlna return profile; } - private bool IsMatch(IDictionary headers, DeviceIdentification profileInfo) + private bool IsMatch(IHeaderDictionary headers, DeviceIdentification profileInfo) { return profileInfo.Headers.Any(i => IsMatch(headers, i)); } - private bool IsMatch(IDictionary headers, HttpHeaderInfo header) + private bool IsMatch(IHeaderDictionary headers, HttpHeaderInfo header) { // Handle invalid user setup if (string.IsNullOrEmpty(header.Name)) @@ -243,14 +242,14 @@ namespace Emby.Dlna return false; } - if (headers.TryGetValue(header.Name, out string value)) + if (headers.TryGetValue(header.Name, out StringValues value)) { switch (header.Match) { case HeaderMatchType.Equals: return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase); case HeaderMatchType.Substring: - var isMatch = value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1; + var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1; //_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch); return isMatch; case HeaderMatchType.Regex: @@ -493,7 +492,7 @@ namespace Emby.Dlna internal string Path { get; set; } } - public string GetServerDescriptionXml(IDictionary headers, string serverUuId, string serverAddress) + public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) { var profile = GetProfile(headers) ?? GetDefaultProfile(); diff --git a/Emby.Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs index ab8aa4619..ae90e95c7 100644 --- a/Emby.Dlna/IUpnpService.cs +++ b/Emby.Dlna/IUpnpService.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Emby.Dlna { public interface IUpnpService @@ -7,9 +5,8 @@ namespace Emby.Dlna /// /// Gets the content directory XML. /// - /// The headers. /// System.String. - string GetServiceXml(IDictionary headers); + string GetServiceXml(); /// /// Processes the control request. diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs index 2b84528ea..9c6022b6c 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs @@ -3,6 +3,7 @@ using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Xml; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Dlna.MediaReceiverRegistrar @@ -19,7 +20,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar XmlReaderSettingsFactory = xmlReaderSettingsFactory; } - public string GetServiceXml(IDictionary headers) + public string GetServiceXml() { return new MediaReceiverRegistrarXmlBuilder().GetXml(); } diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 2abc6c2f4..70753e563 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -22,6 +22,7 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -154,7 +155,7 @@ namespace Emby.Server.Implementations.HttpServer { OnReceive = ProcessWebSocketMessageReceived, Url = e.Url, - QueryString = e.QueryString ?? new QueryParamCollection() + QueryString = e.QueryString ?? new QueryCollection() }; connection.Closed += Connection_Closed; @@ -606,8 +607,8 @@ namespace Emby.Server.Implementations.HttpServer } finally { - httpRes.Close(); - + // TODO + httpRes.IsClosed = true; stopWatch.Stop(); var elapsed = stopWatch.Elapsed; if (elapsed.TotalMilliseconds > 500) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 09cdbc3c2..52c8221f6 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -16,6 +16,8 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; @@ -246,9 +248,9 @@ namespace Emby.Server.Implementations.HttpServer private static string GetCompressionType(IRequest request) { - var acceptEncoding = request.Headers["Accept-Encoding"]; + var acceptEncoding = request.Headers["Accept-Encoding"].ToString(); - if (acceptEncoding != null) + if (string.IsNullOrEmpty(acceptEncoding)) { //if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1) // return "br"; @@ -424,12 +426,12 @@ namespace Emby.Server.Implementations.HttpServer /// private object GetCachedResult(IRequest requestContext, IDictionary responseHeaders, StaticResultOptions options) { - bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; + bool noCache = (requestContext.Headers[HeaderNames.CacheControl].ToString()).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified); if (!noCache) { - DateTime.TryParse(requestContext.Headers.Get("If-Modified-Since"), out var ifModifiedSinceHeader); + DateTime.TryParse(requestContext.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader); if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified)) { @@ -530,7 +532,7 @@ namespace Emby.Server.Implementations.HttpServer options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary(StringComparer.OrdinalIgnoreCase); var contentType = options.ContentType; - if (!string.IsNullOrEmpty(requestContext.Headers.Get("If-Modified-Since"))) + if (!StringValues.IsNullOrEmpty(requestContext.Headers[HeaderNames.IfModifiedSince])) { // See if the result is already cached in the browser var result = GetCachedResult(requestContext, options.ResponseHeaders, options); @@ -548,7 +550,7 @@ namespace Emby.Server.Implementations.HttpServer AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified); AddAgeHeader(responseHeaders, options.DateLastModified); - var rangeHeader = requestContext.Headers.Get("Range"); + var rangeHeader = requestContext.Headers["Range"]; if (!isHeadRequest && !string.IsNullOrEmpty(options.Path)) { @@ -609,11 +611,6 @@ namespace Emby.Server.Implementations.HttpServer } } - /// - /// The us culture - /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// /// Adds the caching responseHeaders. /// diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index e9d0bac74..2bf460bd1 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using UtfUnknown; @@ -67,7 +68,7 @@ namespace Emby.Server.Implementations.HttpServer /// Gets or sets the query string. /// /// The query string. - public QueryParamCollection QueryString { get; set; } + public IQueryCollection QueryString { get; set; } /// /// Initializes a new instance of the class. diff --git a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs index 666f1f601..e3047d392 100644 --- a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs +++ b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs @@ -1,6 +1,7 @@ using System; using System.Net.WebSockets; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; namespace Emby.Server.Implementations.Net { @@ -15,7 +16,7 @@ namespace Emby.Server.Implementations.Net /// Gets or sets the query string. /// /// The query string. - public QueryParamCollection QueryString { get; set; } + public IQueryCollection QueryString { get; set; } /// /// Gets or sets the web socket. /// diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index 7e836e22c..3c8adfc98 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.Services { if (name == null) continue; //thank you ASP.NET - var values = request.QueryString.GetValues(name); + var values = request.QueryString[name]; if (values.Count == 1) { map[name] = values[0]; diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 24903f5e8..a551433ed 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Events; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Session @@ -62,7 +63,7 @@ namespace Emby.Server.Implementations.Session } } - private SessionInfo GetSession(QueryParamCollection queryString, string remoteEndpoint) + private SessionInfo GetSession(IQueryCollection queryString, string remoteEndpoint) { if (queryString == null) { diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs index 113f76b10..f73adc5ff 100644 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -118,8 +118,6 @@ namespace Emby.Server.Implementations.SocketSharp public string Authorization => StringValues.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"].ToString(); - protected bool validate_cookies { get; set; } - protected bool validate_query_string { get; set; } protected bool validate_form { get; set; } protected bool checked_form { get; set; } diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index 77469244b..9f046c3fd 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -52,12 +52,10 @@ using Microsoft.Extensions.Logging; var endpoint = ctx.Connection.RemoteIpAddress.ToString(); var url = ctx.Request.GetDisplayUrl(); - var queryString = new QueryParamCollection(ctx.Request.Query); - var connectingArgs = new WebSocketConnectingEventArgs { Url = url, - QueryString = queryString, + QueryString = ctx.Request.Query, Endpoint = endpoint }; @@ -73,7 +71,7 @@ using Microsoft.Extensions.Logging; WebSocketConnected(new WebSocketConnectEventArgs { Url = url, - QueryString = queryString, + QueryString = ctx.Request.Query, WebSocket = socket, Endpoint = endpoint }); diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index bddccf68b..24fd36062 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -13,7 +13,6 @@ using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; -using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; using IResponse = MediaBrowser.Model.Services.IResponse; namespace Emby.Server.Implementations.SocketSharp @@ -21,7 +20,7 @@ namespace Emby.Server.Implementations.SocketSharp public partial class WebSocketSharpRequest : IHttpRequest { private readonly HttpRequest request; - private readonly IHttpResponse response; + private readonly IResponse response; public WebSocketSharpRequest(HttpRequest httpContext, HttpResponse response, string operationName, ILogger logger) { @@ -34,11 +33,9 @@ namespace Emby.Server.Implementations.SocketSharp public HttpRequest HttpRequest => request; - public object OriginalRequest => request; - public IResponse Response => response; - public IHttpResponse HttpResponse => response; + public IResponse HttpResponse => response; public string OperationName { get; set; } @@ -396,10 +393,9 @@ namespace Emby.Server.Implementations.SocketSharp public string UserAgent => request.Headers[HeaderNames.UserAgent]; - public QueryParamCollection Headers => new QueryParamCollection(request.Headers); + public IHeaderDictionary Headers => request.Headers; - private QueryParamCollection queryString; - public QueryParamCollection QueryString => queryString ?? (queryString = new QueryParamCollection(request.Query)); + public IQueryCollection QueryString => request.Query; public bool IsLocal => string.Equals(request.HttpContext.Connection.LocalIpAddress.ToString(), request.HttpContext.Connection.RemoteIpAddress.ToString()); diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs index f9ecb52a5..c4fbaddd3 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs @@ -1,23 +1,18 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net; -using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; using IRequest = MediaBrowser.Model.Services.IRequest; namespace Emby.Server.Implementations.SocketSharp { - public class WebSocketSharpResponse : IHttpResponse + public class WebSocketSharpResponse : IResponse { private readonly ILogger _logger; @@ -51,42 +46,7 @@ namespace Emby.Server.Implementations.SocketSharp set => _response.ContentType = value; } - public QueryParamCollection Headers => new QueryParamCollection(_response.Headers); - - private static string AsHeaderValue(Cookie cookie) - { - DateTime defaultExpires = DateTime.MinValue; - - var path = cookie.Expires == defaultExpires - ? "/" - : cookie.Path ?? "/"; - - var sb = new StringBuilder(); - - sb.Append($"{cookie.Name}={cookie.Value};path={path}"); - - if (cookie.Expires != defaultExpires) - { - sb.Append($";expires={cookie.Expires:R}"); - } - - if (!string.IsNullOrEmpty(cookie.Domain)) - { - sb.Append($";domain={cookie.Domain}"); - } - - if (cookie.Secure) - { - sb.Append(";Secure"); - } - - if (cookie.HttpOnly) - { - sb.Append(";HttpOnly"); - } - - return sb.ToString(); - } + public IHeaderDictionary Headers => _response.Headers; public void AddHeader(string name, string value) { @@ -111,51 +71,14 @@ namespace Emby.Server.Implementations.SocketSharp public Stream OutputStream => _response.Body; - public void Close() - { - if (!this.IsClosed) - { - this.IsClosed = true; - - try - { - var response = this._response; - - var outputStream = response.Body; - - // This is needed with compression - outputStream.Flush(); - outputStream.Dispose(); - } - catch (SocketException) - { - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in HttpListenerResponseWrapper"); - } - } - } - public bool IsClosed { get; - private set; - } - - public void SetCookie(Cookie cookie) - { - var cookieStr = AsHeaderValue(cookie); - _response.Headers.Add("Set-Cookie", cookieStr); + set; } public bool SendChunked { get; set; } - public bool KeepAlive { get; set; } - - public void ClearCookies() - { - } const int StreamCopyToBufferSize = 81920; public async Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a6be071b8..ae259a4f5 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -609,12 +609,12 @@ namespace MediaBrowser.Api.Playback { foreach (var param in Request.QueryString) { - if (char.IsLower(param.Name[0])) + if (char.IsLower(param.Key[0])) { // This was probably not parsed initially and should be a StreamOptions // TODO: This should be incorporated either in the lower framework for parsing requests // or the generated URL should correctly serialize it - request.StreamOptions[param.Name] = param.Value; + request.StreamOptions[param.Key] = param.Value; } } } @@ -867,7 +867,7 @@ namespace MediaBrowser.Api.Playback private void ApplyDeviceProfileSettings(StreamState state) { - var headers = Request.Headers.ToDictionary(); + var headers = Request.Headers; if (!string.IsNullOrWhiteSpace(state.Request.DeviceProfileId)) { diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index a6ee7c505..41a7686a3 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Dlna; +using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Dlna { @@ -17,7 +18,7 @@ namespace MediaBrowser.Controller.Dlna /// /// The headers. /// DeviceProfile. - DeviceProfile GetProfile(IDictionary headers); + DeviceProfile GetProfile(IHeaderDictionary headers); /// /// Gets the default profile. @@ -64,7 +65,7 @@ namespace MediaBrowser.Controller.Dlna /// The server uu identifier. /// The server address. /// System.String. - string GetServerDescriptionXml(IDictionary headers, string serverUuId, string serverAddress); + string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress); /// /// Gets the icon. diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index a09b2f7a2..566897b31 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { @@ -35,7 +36,7 @@ namespace MediaBrowser.Controller.Net /// Gets or sets the query string. /// /// The query string. - QueryParamCollection QueryString { get; set; } + IQueryCollection QueryString { get; set; } /// /// Gets or sets the receive action. diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs index f26b764bb..107e67421 100644 --- a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs +++ b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs @@ -1,5 +1,7 @@ using System; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Internal; namespace MediaBrowser.Controller.Net { @@ -22,7 +24,7 @@ namespace MediaBrowser.Controller.Net /// Gets or sets the query string. /// /// The query string. - public QueryParamCollection QueryString { get; set; } + public IQueryCollection QueryString { get; set; } /// /// Gets or sets a value indicating whether [allow connection]. /// @@ -31,7 +33,7 @@ namespace MediaBrowser.Controller.Net public WebSocketConnectingEventArgs() { - QueryString = new QueryParamCollection(); + QueryString = new QueryCollection(); AllowConnection = true; } } diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs index 579f80c96..50c6076f3 100644 --- a/MediaBrowser.Model/Services/IHttpRequest.cs +++ b/MediaBrowser.Model/Services/IHttpRequest.cs @@ -2,11 +2,6 @@ namespace MediaBrowser.Model.Services { public interface IHttpRequest : IRequest { - /// - /// The HttpResponse - /// - IHttpResponse HttpResponse { get; } - /// /// The HTTP Verb /// diff --git a/MediaBrowser.Model/Services/IHttpResponse.cs b/MediaBrowser.Model/Services/IHttpResponse.cs index a8b79f394..b99d12525 100644 --- a/MediaBrowser.Model/Services/IHttpResponse.cs +++ b/MediaBrowser.Model/Services/IHttpResponse.cs @@ -4,17 +4,5 @@ namespace MediaBrowser.Model.Services { public interface IHttpResponse : IResponse { - //ICookies Cookies { get; } - - /// - /// Adds a new Set-Cookie instruction to Response - /// - /// - void SetCookie(Cookie cookie); - - /// - /// Removes all pending Set-Cookie instructions - /// - void ClearCookies(); } } diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 0fd4ea37b..edb5a2509 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -1,20 +1,15 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; +using Microsoft.AspNetCore.Http; namespace MediaBrowser.Model.Services { public interface IRequest { - /// - /// The underlying ASP.NET or HttpListener HttpRequest - /// - object OriginalRequest { get; } - IResponse Response { get; } /// @@ -51,9 +46,9 @@ namespace MediaBrowser.Model.Services /// Dictionary Items { get; } - QueryParamCollection Headers { get; } + IHeaderDictionary Headers { get; } - QueryParamCollection QueryString { get; } + IQueryCollection QueryString { get; } Task GetFormData(); @@ -122,21 +117,15 @@ namespace MediaBrowser.Model.Services Stream OutputStream { get; } - /// - /// Signal that this response has been handled and no more processing should be done. - /// When used in a request or response filter, no more filters or processing is done on this request. - /// - void Close(); - /// /// Gets a value indicating whether this instance is closed. /// - bool IsClosed { get; } + bool IsClosed { get; set; } //Add Metadata to Response Dictionary Items { get; } - QueryParamCollection Headers { get; } + IHeaderDictionary Headers { get; } Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken); diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs index 9f23b2420..4631a3b63 100644 --- a/MediaBrowser.Model/Services/QueryParamCollection.cs +++ b/MediaBrowser.Model/Services/QueryParamCollection.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; -using System.Net; using MediaBrowser.Model.Dto; -using Microsoft.AspNetCore.Http; namespace MediaBrowser.Model.Services { @@ -13,23 +10,6 @@ namespace MediaBrowser.Model.Services { public QueryParamCollection() { - - } - - public QueryParamCollection(IHeaderDictionary headers) - { - foreach (var pair in headers) - { - Add(pair.Key, pair.Value); - } - } - - public QueryParamCollection(IQueryCollection queryCollection) - { - foreach (var pair in queryCollection) - { - Add(pair.Key, pair.Value); - } } private static StringComparison GetStringComparison() -- cgit v1.2.3 From 78742b8e4c658b1f02c9c040b8abb8ee5742bed4 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 5 Mar 2019 19:20:28 +0100 Subject: Switch to HeaderNames instead of hardcoded strings (and other header related fixes) --- .../HttpClientManager/HttpClientManager.cs | 5 +- .../HttpServer/FileWriter.cs | 9 +-- .../HttpServer/HttpResultFactory.cs | 41 +++++------ .../HttpServer/RangeRequestWriter.cs | 7 +- .../HttpServer/ResponseFilter.cs | 7 +- .../HttpServer/Security/AuthorizationContext.cs | 3 +- .../HttpServer/StreamWriter.cs | 5 +- .../LiveTv/Listings/SchedulesDirect.cs | 5 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 3 +- .../SocketSharp/RequestMono.cs | 5 +- MediaBrowser.Api/Images/ImageService.cs | 3 +- MediaBrowser.Api/Library/LibraryService.cs | 3 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 15 ++-- .../Progressive/BaseProgressiveStreamingService.cs | 81 +++++----------------- MediaBrowser.Common/Net/HttpRequestOptions.cs | 9 +-- 15 files changed, 82 insertions(+), 119 deletions(-) (limited to 'Emby.Server.Implementations/SocketSharp/RequestMono.cs') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index ef82d1d9e..1bebdd163 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -15,6 +15,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpClientManager { @@ -179,11 +180,11 @@ namespace Emby.Server.Implementations.HttpClientManager foreach (var header in options.RequestHeaders) { - if (string.Equals(header.Key, "Accept", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase)) { request.Accept = header.Value; } - else if (string.Equals(header.Key, "User-Agent", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase)) { SetUserAgent(request, header.Value); hasUserAgent = true; diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index 1375089e3..303db9980 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -9,6 +9,7 @@ using Emby.Server.Implementations.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer { @@ -56,10 +57,10 @@ namespace Emby.Server.Implementations.HttpServer FileSystem = fileSystem; RangeHeader = rangeHeader; - Headers["Content-Type"] = contentType; + Headers[HeaderNames.ContentType] = contentType; TotalContentLength = fileSystem.GetFileInfo(path).Length; - Headers["Accept-Ranges"] = "bytes"; + Headers[HeaderNames.AcceptRanges] = "bytes"; if (string.IsNullOrWhiteSpace(rangeHeader)) { @@ -97,8 +98,8 @@ namespace Emby.Server.Implementations.HttpServer // Content-Length is the length of what we're serving, not the original content var lengthString = RangeLength.ToString(UsCulture); - var rangeString = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); - Headers["Content-Range"] = rangeString; + var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; + Headers[HeaderNames.ContentRange] = rangeString; Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); } diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index be50719d8..f4fe6b611 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.HttpServer public object GetRedirectResult(string url) { var responseHeaders = new Dictionary(); - responseHeaders["Location"] = url; + responseHeaders[HeaderNames.Location] = url; var result = new HttpResult(Array.Empty(), "text/plain", HttpStatusCode.Redirect); @@ -96,9 +96,9 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires)) + if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string expires)) { - responseHeaders["Expires"] = "-1"; + responseHeaders[HeaderNames.Expires] = "-1"; } AddResponseHeaders(result, responseHeaders); @@ -142,9 +142,9 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _)) + if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string _)) { - responseHeaders["Expires"] = "-1"; + responseHeaders[HeaderNames.Expires] = "-1"; } AddResponseHeaders(result, responseHeaders); @@ -186,9 +186,9 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _)) + if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string _)) { - responseHeaders["Expires"] = "-1"; + responseHeaders[HeaderNames.Expires] = "-1"; } AddResponseHeaders(result, responseHeaders); @@ -213,7 +213,7 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); } - responseHeaders["Expires"] = "-1"; + responseHeaders[HeaderNames.Expires] = "-1"; return ToOptimizedResultInternal(requestContext, result, responseHeaders); } @@ -245,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer private static string GetCompressionType(IRequest request) { - var acceptEncoding = request.Headers["Accept-Encoding"].ToString(); + var acceptEncoding = request.Headers[HeaderNames.AcceptEncoding].ToString(); if (string.IsNullOrEmpty(acceptEncoding)) { @@ -325,9 +325,9 @@ namespace Emby.Server.Implementations.HttpServer } content = Compress(content, requestedCompressionType); - responseHeaders["Content-Encoding"] = requestedCompressionType; + responseHeaders[HeaderNames.ContentEncoding] = requestedCompressionType; - responseHeaders["Vary"] = "Accept-Encoding"; + responseHeaders[HeaderNames.Vary] = HeaderNames.AcceptEncoding; var contentLength = content.Length; @@ -537,7 +537,7 @@ namespace Emby.Server.Implementations.HttpServer AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified); AddAgeHeader(responseHeaders, options.DateLastModified); - var rangeHeader = requestContext.Headers["Range"]; + var rangeHeader = requestContext.Headers[HeaderNames.Range]; if (!isHeadRequest && !string.IsNullOrEmpty(options.Path)) { @@ -606,23 +606,23 @@ namespace Emby.Server.Implementations.HttpServer { if (noCache) { - responseHeaders["Cache-Control"] = "no-cache, no-store, must-revalidate"; - responseHeaders["pragma"] = "no-cache, no-store, must-revalidate"; + responseHeaders[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate"; + responseHeaders[HeaderNames.Pragma] = "no-cache, no-store, must-revalidate"; return; } if (cacheDuration.HasValue) { - responseHeaders["Cache-Control"] = "public, max-age=" + cacheDuration.Value.TotalSeconds; + responseHeaders[HeaderNames.CacheControl] = "public, max-age=" + cacheDuration.Value.TotalSeconds; } else { - responseHeaders["Cache-Control"] = "public"; + responseHeaders[HeaderNames.CacheControl] = "public"; } if (lastModifiedDate.HasValue) { - responseHeaders["Last-Modified"] = lastModifiedDate.ToString(); + responseHeaders[HeaderNames.LastModified] = lastModifiedDate.ToString(); } } @@ -635,7 +635,7 @@ namespace Emby.Server.Implementations.HttpServer { if (lastDateModified.HasValue) { - responseHeaders["Age"] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture); + responseHeaders[HeaderNames.Age] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture); } } @@ -693,9 +693,4 @@ namespace Emby.Server.Implementations.HttpServer } } } - - public interface IBrotliCompressor - { - byte[] Compress(byte[] content); - } } diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index 8904e11d3..a0bfe5d8f 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer { @@ -66,8 +67,8 @@ namespace Emby.Server.Implementations.HttpServer this._logger = logger; ContentType = contentType; - Headers["Content-Type"] = contentType; - Headers["Accept-Ranges"] = "bytes"; + Headers[HeaderNames.ContentType] = contentType; + Headers[HeaderNames.AcceptRanges] = "bytes"; StatusCode = HttpStatusCode.PartialContent; SetRangeValues(contentLength); @@ -96,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer RangeLength = 1 + RangeEnd - RangeStart; // Content-Length is the length of what we're serving, not the original content - Headers["Content-Range"] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; + Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; if (RangeStart > 0 && SourceStream.CanSeek) { diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index ae6a6576e..a53d9bf0b 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Text; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer { @@ -44,13 +45,13 @@ namespace Emby.Server.Implementations.HttpServer if (dto is IHasHeaders hasHeaders) { - if (!hasHeaders.Headers.ContainsKey("Server")) + if (!hasHeaders.Headers.ContainsKey(HeaderNames.Server)) { - hasHeaders.Headers["Server"] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50"; + hasHeaders.Headers[HeaderNames.Server] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50"; } // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy - if (hasHeaders.Headers.TryGetValue("Content-Length", out string contentLength) + if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength) && !string.IsNullOrEmpty(contentLength)) { var length = long.Parse(contentLength, UsCulture); diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index cab41e65b..276312a30 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Services; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer.Security { @@ -176,7 +177,7 @@ namespace Emby.Server.Implementations.HttpServer.Security if (string.IsNullOrEmpty(auth)) { - auth = httpReq.Headers["Authorization"]; + auth = httpReq.Headers[HeaderNames.Authorization]; } return GetAuthorization(auth); diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs index 90354385d..66a13e20d 100644 --- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs +++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer { @@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.HttpServer SourceStream = source; - Headers["Content-Type"] = contentType; + Headers[HeaderNames.ContentType] = contentType; } /// @@ -69,7 +70,7 @@ namespace Emby.Server.Implementations.HttpServer SourceBytes = source; - Headers["Content-Type"] = contentType; + Headers[HeaderNames.ContentType] = contentType; } public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 0bbffb824..4137760d0 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -17,6 +17,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.LiveTv.Listings { @@ -638,7 +639,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings #if NETSTANDARD2_0 if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - options.RequestHeaders["Accept-Encoding"] = "deflate"; + options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; } #endif @@ -676,7 +677,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings #if NETSTANDARD2_0 if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - options.RequestHeaders["Accept-Encoding"] = "deflate"; + options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; } #endif diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index fdaaf0bae..588dcb843 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -19,6 +19,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.LiveTv.TunerHosts { @@ -145,7 +146,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (protocol == MediaProtocol.Http) { // Use user-defined user-agent. If there isn't one, make it look like a browser. - httpHeaders["User-Agent"] = string.IsNullOrWhiteSpace(info.UserAgent) ? + httpHeaders[HeaderNames.UserAgent] = string.IsNullOrWhiteSpace(info.UserAgent) ? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.85 Safari/537.36" : info.UserAgent; } diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs index f73adc5ff..82bcf2f04 100644 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using MediaBrowser.Model.Services; using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.SocketSharp { @@ -114,9 +115,9 @@ namespace Emby.Server.Implementations.SocketSharp return form; } - public string Accept => StringValues.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"].ToString(); + public string Accept => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Accept]) ? null : request.Headers[HeaderNames.Accept].ToString(); - public string Authorization => StringValues.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"].ToString(); + public string Authorization => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Authorization]) ? null : request.Headers[HeaderNames.Authorization].ToString(); protected bool validate_form { get; set; } protected bool checked_form { get; set; } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 61db7b8d4..c25204bf2 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -18,6 +18,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Images { @@ -634,7 +635,7 @@ namespace MediaBrowser.Api.Images var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false); - headers["Vary"] = "Accept"; + headers[HeaderNames.Vary] = HeaderNames.Accept; return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions { diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index d44b07256..8eefbdf2c 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -29,6 +29,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Library { @@ -827,7 +828,7 @@ namespace MediaBrowser.Api.Library var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty); if (!string.IsNullOrWhiteSpace(filename)) { - headers["Content-Disposition"] = "attachment; filename=\"" + filename + "\""; + headers[HeaderNames.ContentDisposition] = "attachment; filename=\"" + filename + "\""; } return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 8fdd726b7..88ed73456 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -24,6 +24,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; +using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.LiveTv { @@ -750,9 +751,10 @@ namespace MediaBrowser.Api.LiveTv throw new FileNotFoundException(); } - var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - - outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path); + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType(path) + }; return new ProgressiveFileCopier(_fileSystem, _streamHelper, path, outputHeaders, Logger, _environment) { @@ -772,9 +774,10 @@ namespace MediaBrowser.Api.LiveTv var directStreamProvider = liveStreamInfo; - var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - - outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container); + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file." + request.Container) + }; return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger, _environment) { diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 3e74d59db..fc81e532d 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -17,6 +17,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; +using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Playback.Progressive { @@ -154,7 +155,7 @@ namespace MediaBrowser.Api.Playback.Progressive var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); // TODO: Don't hardcode this - outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts"); + outputHeaders[HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file.ts"); return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) { @@ -196,9 +197,11 @@ namespace MediaBrowser.Api.Playback.Progressive { if (state.MediaSource.IsInfiniteStream) { - var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [HeaderNames.ContentType] = contentType + }; - outputHeaders["Content-Type"] = contentType; return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) { @@ -298,16 +301,16 @@ namespace MediaBrowser.Api.Playback.Progressive if (trySupportSeek) { - if (!string.IsNullOrWhiteSpace(Request.QueryString["Range"])) + if (!string.IsNullOrWhiteSpace(Request.QueryString[HeaderNames.Range])) { - options.RequestHeaders["Range"] = Request.QueryString["Range"]; + options.RequestHeaders[HeaderNames.Range] = Request.QueryString[HeaderNames.Range]; } } var response = await HttpClient.GetResponse(options).ConfigureAwait(false); if (trySupportSeek) { - foreach (var name in new[] { "Content-Range", "Accept-Ranges" }) + foreach (var name in new[] { HeaderNames.ContentRange, HeaderNames.AcceptRanges }) { var val = response.Headers[name]; if (!string.IsNullOrWhiteSpace(val)) @@ -318,14 +321,7 @@ namespace MediaBrowser.Api.Playback.Progressive } else { - responseHeaders["Accept-Ranges"] = "none"; - } - - // Seeing cases of -1 here - if (response.ContentLength.HasValue && response.ContentLength.Value >= 0) - { - // TODO - //responseHeaders["Content-Length"] = response.ContentLength.Value.ToString(UsCulture); + responseHeaders[HeaderNames.AcceptRanges] = "none"; } if (isHeadRequest) @@ -338,7 +334,7 @@ namespace MediaBrowser.Api.Playback.Progressive var result = new StaticRemoteStreamWriter(response); - result.Headers["Content-Type"] = response.ContentType; + result.Headers[HeaderNames.ContentType] = response.ContentType; // Add the response headers to the result object foreach (var header in responseHeaders) @@ -362,42 +358,14 @@ namespace MediaBrowser.Api.Playback.Progressive // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; - responseHeaders["Accept-Ranges"] = "none"; + responseHeaders[HeaderNames.AcceptRanges] = "none"; var contentType = state.GetMimeType(outputPath); - // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response - // What we really want to do is hunt that down and remove that - var contentLength = state.EstimateContentLength || isHeadRequest ? GetEstimatedContentLength(state) : null; - - if (contentLength.HasValue) - { - responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture); - } - // Headers only if (isHeadRequest) { - var streamResult = ResultFactory.GetResult(null, new byte[] { }, contentType, responseHeaders); - - var hasHeaders = streamResult as IHasHeaders; - if (hasHeaders != null) - { - // TODO - //if (contentLength.HasValue) - //{ - // hasHeaders.Headers["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture); - //} - //else - //{ - if (hasHeaders.Headers.ContainsKey("Content-Length")) - { - hasHeaders.Headers.Remove("Content-Length"); - } - //} - } - - return streamResult; + return ResultFactory.GetResult(null, new byte[] { }, contentType, responseHeaders); } var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath); @@ -416,9 +384,11 @@ namespace MediaBrowser.Api.Playback.Progressive state.Dispose(); } - var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [HeaderNames.ContentType] = contentType + }; - outputHeaders["Content-Type"] = contentType; // Add the response headers to the result object foreach (var item in responseHeaders) @@ -433,22 +403,5 @@ namespace MediaBrowser.Api.Playback.Progressive transcodingLock.Release(); } } - - /// - /// Gets the length of the estimated content. - /// - /// The state. - /// System.Nullable{System.Int64}. - private long? GetEstimatedContentLength(StreamState state) - { - var totalBitrate = state.TotalOutputBitrate ?? 0; - - if (totalBitrate > 0 && state.RunTimeTicks.HasValue) - { - return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds / 8); - } - - return null; - } } } diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index dadac5e03..bea178517 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using Microsoft.Net.Http.Headers; namespace MediaBrowser.Common.Net { @@ -24,8 +25,8 @@ namespace MediaBrowser.Common.Net /// The accept header. public string AcceptHeader { - get => GetHeaderValue("Accept"); - set => RequestHeaders["Accept"] = value; + get => GetHeaderValue(HeaderNames.Accept); + set => RequestHeaders[HeaderNames.Accept] = value; } /// /// Gets or sets the cancellation token. @@ -45,8 +46,8 @@ namespace MediaBrowser.Common.Net /// The user agent. public string UserAgent { - get => GetHeaderValue("User-Agent"); - set => RequestHeaders["User-Agent"] = value; + get => GetHeaderValue(HeaderNames.UserAgent); + set => RequestHeaders[HeaderNames.UserAgent] = value; } /// -- cgit v1.2.3 From c5fce647defd2eace2d2431ccc52ffe1d027c184 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 7 Mar 2019 15:54:30 +0100 Subject: Cleanup/simplification * Removed useless copies/allocations * Reduced unneeded complexity --- .../Collections/CollectionImageProvider.cs | 4 +- .../IO/ManagedFileSystem.cs | 7 +- .../Images/BaseDynamicImageProvider.cs | 58 ++++++-------- .../Playlists/PlaylistImageProvider.cs | 13 +--- .../SocketSharp/RequestMono.cs | 17 ++--- .../SocketSharp/WebSocketSharpRequest.cs | 4 +- .../UserViews/CollectionFolderImageProvider.cs | 9 +-- .../UserViews/DynamicImageProvider.cs | 13 ++-- .../UserViews/FolderImageProvider.cs | 4 +- MediaBrowser.Api/Images/ImageService.cs | 89 ++++++++++++---------- MediaBrowser.Controller/Entities/BaseItem.cs | 26 ++++--- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/Entities/ItemImageInfo.cs | 16 +--- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 2 +- MediaBrowser.Controller/Entities/Movies/Movie.cs | 8 +- MediaBrowser.Controller/Entities/TV/Episode.cs | 2 +- MediaBrowser.Controller/Entities/TV/Series.cs | 3 +- MediaBrowser.Controller/Entities/Video.cs | 2 +- MediaBrowser.Model/IO/IFileSystem.cs | 3 +- 19 files changed, 126 insertions(+), 156 deletions(-) (limited to 'Emby.Server.Implementations/SocketSharp/RequestMono.cs') diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index cdfb5cadf..0244c4a68 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Collections return base.Supports(item); } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { var playlist = (BoxSet)item; @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Collections .ToList(); } - protected override string CreateImage(BaseItem item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override string CreateImage(BaseItem item, IReadOnlyCollection itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index a64dfb607..421592fad 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -7,7 +7,6 @@ using System.Text; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.IO @@ -711,20 +710,20 @@ namespace Emby.Server.Implementations.IO return GetFiles(path, null, false, recursive); } - public virtual IEnumerable GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) + public virtual IEnumerable GetFiles(string path, IReadOnlyList extensions, bool enableCaseSensitiveExtensions, bool recursive = false) { var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; // On linux and osx the search pattern is case sensitive // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method - if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1) + if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Count == 1) { return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], searchOption)); } var files = new DirectoryInfo(path).EnumerateFiles("*", searchOption); - if (extensions != null && extensions.Length > 0) + if (extensions != null && extensions.Count > 0) { files = files.Where(i => { diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 109c21f18..46f209b4b 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -20,6 +20,9 @@ namespace Emby.Server.Implementations.Images public abstract class BaseDynamicImageProvider : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider, IHasOrder where T : BaseItem { + protected virtual IReadOnlyCollection SupportedImages { get; } + = new ImageType[] { ImageType.Primary }; + protected IFileSystem FileSystem { get; private set; } protected IProviderManager ProviderManager { get; private set; } protected IApplicationPaths ApplicationPaths { get; private set; } @@ -33,18 +36,7 @@ namespace Emby.Server.Implementations.Images ImageProcessor = imageProcessor; } - protected virtual bool Supports(BaseItem item) - { - return true; - } - - public virtual ImageType[] GetSupportedImages(BaseItem item) - { - return new ImageType[] - { - ImageType.Primary - }; - } + protected virtual bool Supports(BaseItem _) => true; public async Task FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) { @@ -54,15 +46,14 @@ namespace Emby.Server.Implementations.Images } var updateType = ItemUpdateType.None; - var supportedImages = GetSupportedImages(item); - if (supportedImages.Contains(ImageType.Primary)) + if (SupportedImages.Contains(ImageType.Primary)) { var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false); updateType = updateType | primaryResult; } - if (supportedImages.Contains(ImageType.Thumb)) + if (SupportedImages.Contains(ImageType.Thumb)) { var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false); updateType = updateType | thumbResult; @@ -94,7 +85,7 @@ namespace Emby.Server.Implementations.Images } protected async Task FetchToFileInternal(BaseItem item, - List itemsWithImages, + IReadOnlyList itemsWithImages, ImageType imageType, CancellationToken cancellationToken) { @@ -119,9 +110,9 @@ namespace Emby.Server.Implementations.Images return ItemUpdateType.ImageUpdate; } - protected abstract List GetItemsWithImages(BaseItem item); + protected abstract IReadOnlyList GetItemsWithImages(BaseItem item); - protected string CreateThumbCollage(BaseItem primaryItem, List items, string outputPath) + protected string CreateThumbCollage(BaseItem primaryItem, IEnumerable items, string outputPath) { return CreateCollage(primaryItem, items, outputPath, 640, 360); } @@ -132,38 +123,38 @@ namespace Emby.Server.Implementations.Images .Select(i => { var image = i.GetImageInfo(ImageType.Primary, 0); - if (image != null && image.IsLocalFile) { return image.Path; } - image = i.GetImageInfo(ImageType.Thumb, 0); + image = i.GetImageInfo(ImageType.Thumb, 0); if (image != null && image.IsLocalFile) { return image.Path; } + return null; }) .Where(i => !string.IsNullOrEmpty(i)); } - protected string CreatePosterCollage(BaseItem primaryItem, List items, string outputPath) + protected string CreatePosterCollage(BaseItem primaryItem, IEnumerable items, string outputPath) { return CreateCollage(primaryItem, items, outputPath, 400, 600); } - protected string CreateSquareCollage(BaseItem primaryItem, List items, string outputPath) + protected string CreateSquareCollage(BaseItem primaryItem, IEnumerable items, string outputPath) { return CreateCollage(primaryItem, items, outputPath, 600, 600); } - protected string CreateThumbCollage(BaseItem primaryItem, List items, string outputPath, int width, int height) + protected string CreateThumbCollage(BaseItem primaryItem, IEnumerable items, string outputPath, int width, int height) { return CreateCollage(primaryItem, items, outputPath, width, height); } - private string CreateCollage(BaseItem primaryItem, List items, string outputPath, int width, int height) + private string CreateCollage(BaseItem primaryItem, IEnumerable items, string outputPath, int width, int height) { Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); @@ -192,7 +183,7 @@ namespace Emby.Server.Implementations.Images public string Name => "Dynamic Image Provider"; protected virtual string CreateImage(BaseItem item, - List itemsWithImages, + IReadOnlyCollection itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) @@ -211,18 +202,15 @@ namespace Emby.Server.Implementations.Images if (imageType == ImageType.Primary) { - if (item is UserView) - { - return CreateSquareCollage(item, itemsWithImages, outputPath); - } - if (item is Playlist || item is MusicGenre || item is Genre || item is PhotoAlbum) + if (item is UserView || item is Playlist || item is MusicGenre || item is Genre || item is PhotoAlbum) { return CreateSquareCollage(item, itemsWithImages, outputPath); } + return CreatePosterCollage(item, itemsWithImages, outputPath); } - throw new ArgumentException("Unexpected image type"); + throw new ArgumentException("Unexpected image type", nameof(imageType)); } protected virtual int MaxImageAgeDays => 7; @@ -234,13 +222,11 @@ namespace Emby.Server.Implementations.Images return false; } - var supportedImages = GetSupportedImages(item); - - if (supportedImages.Contains(ImageType.Primary) && HasChanged(item, ImageType.Primary)) + if (SupportedImages.Contains(ImageType.Primary) && HasChanged(item, ImageType.Primary)) { return true; } - if (supportedImages.Contains(ImageType.Thumb) && HasChanged(item, ImageType.Thumb)) + if (SupportedImages.Contains(ImageType.Thumb) && HasChanged(item, ImageType.Thumb)) { return true; } @@ -285,7 +271,7 @@ namespace Emby.Server.Implementations.Images public int Order => 0; - protected string CreateSingleImage(List itemsWithImages, string outputPathWithoutExtension, ImageType imageType) + protected string CreateSingleImage(IEnumerable itemsWithImages, string outputPathWithoutExtension, ImageType imageType) { var image = itemsWithImages .Where(i => i.HasImage(imageType) && i.GetImageInfo(imageType, 0).IsLocalFile && Path.HasExtension(i.GetImagePath(imageType))) diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs index 8a7c1492d..cad66a80f 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs @@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Playlists { } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { var playlist = (Playlist)item; @@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Playlists _libraryManager = libraryManager; } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { return _libraryManager.GetItemList(new InternalItemsQuery { @@ -89,7 +89,6 @@ namespace Emby.Server.Implementations.Playlists Recursive = true, ImageTypes = new[] { ImageType.Primary }, DtoOptions = new DtoOptions(false) - }); } } @@ -103,7 +102,7 @@ namespace Emby.Server.Implementations.Playlists _libraryManager = libraryManager; } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { return _libraryManager.GetItemList(new InternalItemsQuery { @@ -116,11 +115,5 @@ namespace Emby.Server.Implementations.Playlists DtoOptions = new DtoOptions(false) }); } - - //protected override Task CreateImage(IHasMetadata item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) - //{ - // return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); - //} } - } diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs index 5e29e4058..373f6d758 100644 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.SocketSharp byte[] copy = new byte[e.Length]; input.Position = e.Start; - input.Read(copy, 0, (int)e.Length); + await input.ReadAsync(copy, 0, (int)e.Length).ConfigureAwait(false); form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length)); } @@ -98,11 +98,11 @@ namespace Emby.Server.Implementations.SocketSharp var form = new WebROCollection(); files = new Dictionary(); - if (IsContentType("multipart/form-data", true)) + if (IsContentType("multipart/form-data")) { await LoadMultiPart(form).ConfigureAwait(false); } - else if (IsContentType("application/x-www-form-urlencoded", true)) + else if (IsContentType("application/x-www-form-urlencoded")) { await LoadWwwForm(form).ConfigureAwait(false); } @@ -200,19 +200,14 @@ namespace Emby.Server.Implementations.SocketSharp return false; } - private bool IsContentType(string ct, bool starts_with) + private bool IsContentType(string ct) { - if (ct == null || ContentType == null) + if (ContentType == null) { return false; } - if (starts_with) - { - return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase); - } - - return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase); + return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase); } private async Task LoadWwwForm(WebROCollection form) diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index bc002dc4c..10b7c4514 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.IO; using System.Net; using System.Text; -using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -405,8 +404,7 @@ namespace Emby.Server.Implementations.SocketSharp return null; } - var path = sbPathInfo.ToString(); - return path.Length > 1 ? path.TrimEnd('/') : "/"; + return sbPathInfo.Length > 1 ? sbPathInfo.ToString().TrimEnd('/') : "/"; } public string UserAgent => request.Headers[HeaderNames.UserAgent]; diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs index ce6c2cd87..a3f3f6cb4 100644 --- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Emby.Server.Implementations.Images; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; @@ -20,7 +19,7 @@ namespace Emby.Server.Implementations.UserViews { } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { var view = (CollectionFolder)item; var viewType = view.CollectionType; @@ -56,7 +55,7 @@ namespace Emby.Server.Implementations.UserViews includeItemTypes = new string[] { "Video", "Audio", "Photo", "Movie", "Series" }; } - var recursive = !new[] { CollectionType.Playlists }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + var recursive = !string.Equals(CollectionType.Playlists, viewType, StringComparison.OrdinalIgnoreCase); return view.GetItemList(new InternalItemsQuery { @@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.UserViews }, IncludeItemTypes = includeItemTypes - }).ToList(); + }); } protected override bool Supports(BaseItem item) @@ -79,7 +78,7 @@ namespace Emby.Server.Implementations.UserViews return item is CollectionFolder; } - protected override string CreateImage(BaseItem item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override string CreateImage(BaseItem item, IReadOnlyCollection itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png"); diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs index 4ec68e550..f48520443 100644 --- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.UserViews _libraryManager = libraryManager; } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { var view = (UserView)item; @@ -46,8 +46,7 @@ namespace Emby.Server.Implementations.UserViews var items = result.Select(i => { - var episode = i as Episode; - if (episode != null) + if (i is Episode episode) { var series = episode.Series; if (series != null) @@ -58,8 +57,7 @@ namespace Emby.Server.Implementations.UserViews return episode; } - var season = i as Season; - if (season != null) + if (i is Season season) { var series = season.Series; if (series != null) @@ -70,8 +68,7 @@ namespace Emby.Server.Implementations.UserViews return season; } - var audio = i as Audio; - if (audio != null) + if (i is Audio audio) { var album = audio.AlbumEntity; if (album != null && album.HasImage(ImageType.Primary)) @@ -122,7 +119,7 @@ namespace Emby.Server.Implementations.UserViews return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty); } - protected override string CreateImage(BaseItem item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override string CreateImage(BaseItem item, IReadOnlyCollection itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { if (itemsWithImages.Count == 0) { diff --git a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs b/Emby.Server.Implementations/UserViews/FolderImageProvider.cs index c810004ab..4655cd928 100644 --- a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/FolderImageProvider.cs @@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.UserViews _libraryManager = libraryManager; } - protected override List GetItemsWithImages(BaseItem item) + protected override IReadOnlyList GetItemsWithImages(BaseItem item) { return _libraryManager.GetItemList(new InternalItemsQuery { @@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.UserViews }); } - protected override string CreateImage(BaseItem item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override string CreateImage(BaseItem item, IReadOnlyCollection itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index c25204bf2..10bbc9e5d 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -312,35 +312,35 @@ namespace MediaBrowser.Api.Images private ImageInfo GetImageInfo(BaseItem item, ItemImageInfo info, int? imageIndex) { + int? width = null; + int? height = null; + long length = 0; + try { - int? width = null; - int? height = null; - long length = 0; - - try + if (info.IsLocalFile) { - if (info.IsLocalFile) - { - var fileInfo = _fileSystem.GetFileInfo(info.Path); - length = fileInfo.Length; + var fileInfo = _fileSystem.GetFileInfo(info.Path); + length = fileInfo.Length; - ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true); - width = size.Width; - height = size.Height; - - if (width <= 0 || height <= 0) - { - width = null; - height = null; - } + ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true); + width = size.Width; + height = size.Height; + if (width <= 0 || height <= 0) + { + width = null; + height = null; } } - catch - { + } + catch (Exception ex) + { + Logger.LogError(ex, "Error getting image information for {Item}", item.Name); + } - } + try + { return new ImageInfo { Path = info.Path, @@ -354,7 +354,7 @@ namespace MediaBrowser.Api.Images } catch (Exception ex) { - Logger.LogError(ex, "Error getting image information for {path}", info.Path); + Logger.LogError(ex, "Error getting image information for {Path}", info.Path); return null; } @@ -519,16 +519,16 @@ namespace MediaBrowser.Api.Images request.AddPlayedIndicator = true; } } + if (request.PercentPlayed.HasValue) { request.UnplayedCount = null; } - if (request.UnplayedCount.HasValue) + + if (request.UnplayedCount.HasValue + && request.UnplayedCount.Value <= 0) { - if (request.UnplayedCount.Value <= 0) - { - request.UnplayedCount = null; - } + request.UnplayedCount = null; } if (item == null) @@ -542,7 +542,6 @@ namespace MediaBrowser.Api.Images } var imageInfo = GetImageInfo(request, item); - if (imageInfo == null) { var displayText = item == null ? itemId.ToString() : item.Name; @@ -550,7 +549,6 @@ namespace MediaBrowser.Api.Images } IImageEnhancer[] supportedImageEnhancers; - if (_imageProcessor.ImageEnhancers.Length > 0) { if (item == null) @@ -565,13 +563,15 @@ namespace MediaBrowser.Api.Images supportedImageEnhancers = Array.Empty(); } - var cropwhitespace = request.Type == ImageType.Logo || - request.Type == ImageType.Art; - + bool cropwhitespace; if (request.CropWhitespace.HasValue) { cropwhitespace = request.CropWhitespace.Value; } + else + { + cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art; + } var outputFormats = GetOutputFormats(request); @@ -653,12 +653,10 @@ namespace MediaBrowser.Api.Images private ImageFormat[] GetOutputFormats(ImageRequest request) { - if (!string.IsNullOrWhiteSpace(request.Format)) + if (!string.IsNullOrWhiteSpace(request.Format) + && Enum.TryParse(request.Format, true, out ImageFormat format)) { - if (Enum.TryParse(request.Format, true, out ImageFormat format)) - { - return new ImageFormat[] { format }; - } + return new ImageFormat[] { format }; } return GetClientSupportedFormats(); @@ -666,8 +664,19 @@ namespace MediaBrowser.Api.Images private ImageFormat[] GetClientSupportedFormats() { - //logger.LogDebug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? Array.Empty())); - var supportedFormats = (Request.AcceptTypes ?? Array.Empty()).Select(i => i.Split(';')[0]).ToArray(); + var supportedFormats = Request.AcceptTypes ?? Array.Empty(); + if (supportedFormats.Length > 0) + { + for (int i = 0; i < supportedFormats.Length; i++) + { + int index = supportedFormats[i].IndexOf(';'); + if (index != -1) + { + supportedFormats[i] = supportedFormats[i].Substring(0, index); + } + } + } + var acceptParam = Request.QueryString["accept"]; var supportsWebP = SupportsFormat(supportedFormats, acceptParam, "webp", false); @@ -700,7 +709,7 @@ namespace MediaBrowser.Api.Images return formats.ToArray(); } - private bool SupportsFormat(string[] requestAcceptTypes, string acceptParam, string format, bool acceptAll) + private bool SupportsFormat(IEnumerable requestAcceptTypes, string acceptParam, string format, bool acceptAll) { var mimeType = "image/" + format; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 43fee79a1..39b6eedfe 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -36,10 +36,20 @@ namespace MediaBrowser.Controller.Entities /// public abstract class BaseItem : IHasProviderIds, IHasLookupInfo { - protected static MetadataFields[] EmptyMetadataFieldsArray = Array.Empty(); - protected static MediaUrl[] EmptyMediaUrlArray = Array.Empty(); - protected static ItemImageInfo[] EmptyItemImageInfoArray = Array.Empty(); - public static readonly LinkedChild[] EmptyLinkedChildArray = Array.Empty(); + private static readonly List _supportedExtensions = new List(SupportedImageExtensions) + { + ".nfo", + ".xml", + ".srt", + ".vtt", + ".sub", + ".idx", + ".txt", + ".edl", + ".bif", + ".smi", + ".ttml" + }; protected BaseItem() { @@ -49,8 +59,8 @@ namespace MediaBrowser.Controller.Entities Genres = Array.Empty(); Studios = Array.Empty(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - LockedFields = EmptyMetadataFieldsArray; - ImageInfos = EmptyItemImageInfoArray; + LockedFields = Array.Empty(); + ImageInfos = Array.Empty(); ProductionLocations = Array.Empty(); RemoteTrailers = Array.Empty(); ExtraIds = Array.Empty(); @@ -2452,10 +2462,8 @@ namespace MediaBrowser.Controller.Entities } var filename = System.IO.Path.GetFileNameWithoutExtension(Path); - var extensions = new List { ".nfo", ".xml", ".srt", ".vtt", ".sub", ".idx", ".txt", ".edl", ".bif", ".smi", ".ttml" }; - extensions.AddRange(SupportedImageExtensions); - return FileSystem.GetFiles(System.IO.Path.GetDirectoryName(Path), extensions.ToArray(), false, false) + return FileSystem.GetFiles(System.IO.Path.GetDirectoryName(Path), _supportedExtensions, false, false) .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.FullName).StartsWith(filename, StringComparison.OrdinalIgnoreCase)) .ToList(); } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index e49ff20ba..c056bc0b4 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Controller.Entities public Folder() { - LinkedChildren = EmptyLinkedChildArray; + LinkedChildren = Array.Empty(); } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs index ff6b13398..848493864 100644 --- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs +++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs @@ -25,22 +25,10 @@ namespace MediaBrowser.Controller.Entities public DateTime DateModified { get; set; } public int Width { get; set; } + public int Height { get; set; } [IgnoreDataMember] - public bool IsLocalFile - { - get - { - if (Path != null) - { - if (Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - return true; - } - } + public bool IsLocalFile => Path == null || !Path.StartsWith("http", StringComparison.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 124a943ef..a532b5ee9 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities.Movies { public BoxSet() { - RemoteTrailers = EmptyMediaUrlArray; + RemoteTrailers = Array.Empty(); LocalTrailerIds = Array.Empty(); RemoteTrailerIds = Array.Empty(); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 232d11624..20c5b3521 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -21,10 +21,10 @@ namespace MediaBrowser.Controller.Entities.Movies public Movie() { - SpecialFeatureIds = new Guid[] { }; - RemoteTrailers = EmptyMediaUrlArray; - LocalTrailerIds = new Guid[] { }; - RemoteTrailerIds = new Guid[] { }; + SpecialFeatureIds = Array.Empty(); + RemoteTrailers = Array.Empty(); + LocalTrailerIds = Array.Empty(); + RemoteTrailerIds = Array.Empty(); } public Guid[] LocalTrailerIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 072b1d89a..fb29c07b0 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Entities.TV { public Episode() { - RemoteTrailers = EmptyMediaUrlArray; + RemoteTrailers = Array.Empty(); LocalTrailerIds = Array.Empty(); RemoteTrailerIds = Array.Empty(); } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 570e9389e..eae834f6f 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -7,7 +7,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; @@ -22,7 +21,7 @@ namespace MediaBrowser.Controller.Entities.TV { public Series() { - RemoteTrailers = EmptyMediaUrlArray; + RemoteTrailers = Array.Empty(); LocalTrailerIds = Array.Empty(); RemoteTrailerIds = Array.Empty(); AirDays = Array.Empty(); diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 31cd42975..8379dcc09 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -167,7 +167,7 @@ namespace MediaBrowser.Controller.Entities AdditionalParts = Array.Empty(); LocalAlternateVersions = Array.Empty(); SubtitleFiles = Array.Empty(); - LinkedAlternateVersions = EmptyLinkedChildArray; + LinkedAlternateVersions = Array.Empty(); } public override bool CanDownload() diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index e0771245f..ca99b28ca 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; namespace MediaBrowser.Model.IO { @@ -177,7 +176,7 @@ namespace MediaBrowser.Model.IO /// IEnumerable GetFiles(string path, bool recursive = false); - IEnumerable GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive); + IEnumerable GetFiles(string path, IReadOnlyList extensions, bool enableCaseSensitiveExtensions, bool recursive); /// /// Gets the file system entries. -- cgit v1.2.3