Separate HttpPostedFile
This commit is contained in:
parent
25c2267a89
commit
fb6a901374
|
@ -6,9 +6,13 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
public class HttpFile : IHttpFile
|
public class HttpFile : IHttpFile
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public string FileName { get; set; }
|
public string FileName { get; set; }
|
||||||
|
|
||||||
public long ContentLength { get; set; }
|
public long ContentLength { get; set; }
|
||||||
|
|
||||||
public string ContentType { get; set; }
|
public string ContentType { get; set; }
|
||||||
|
|
||||||
public Stream InputStream { get; set; }
|
public Stream InputStream { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
204
Jellyfin.Server/SocketSharp/HttpPostedFile.cs
Normal file
204
Jellyfin.Server/SocketSharp/HttpPostedFile.cs
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
public sealed class HttpPostedFile : IDisposable
|
||||||
|
{
|
||||||
|
private string _name;
|
||||||
|
private string _contentType;
|
||||||
|
private Stream _stream;
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_contentType = content_type;
|
||||||
|
_stream = new ReadSubStream(base_stream, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ContentType => _contentType;
|
||||||
|
|
||||||
|
public int ContentLength => (int)_stream.Length;
|
||||||
|
|
||||||
|
public string FileName => _name;
|
||||||
|
|
||||||
|
public Stream InputStream => _stream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the unmanaged resources and disposes of the managed resources used.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream.Dispose();
|
||||||
|
_stream = null;
|
||||||
|
|
||||||
|
_name = null;
|
||||||
|
_contentType = null;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReadSubStream : Stream
|
||||||
|
{
|
||||||
|
private Stream _stream;
|
||||||
|
private long _offset;
|
||||||
|
private long _end;
|
||||||
|
private long _position;
|
||||||
|
|
||||||
|
public ReadSubStream(Stream s, long offset, long length)
|
||||||
|
{
|
||||||
|
_stream = s;
|
||||||
|
_offset = offset;
|
||||||
|
_end = offset + length;
|
||||||
|
_position = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int dest_offset, int count)
|
||||||
|
{
|
||||||
|
if (buffer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest_offset < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), "< 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = buffer.Length;
|
||||||
|
if (dest_offset > len)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// reordered to avoid possible integer overflow
|
||||||
|
if (dest_offset > len - count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Reading would overrun buffer", nameof(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > _end - _position)
|
||||||
|
{
|
||||||
|
count = (int)(_end - _position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream.Position = _position;
|
||||||
|
int result = _stream.Read(buffer, dest_offset, count);
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
_position += result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_position = _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int ReadByte()
|
||||||
|
{
|
||||||
|
if (_position >= _end)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream.Position = _position;
|
||||||
|
int result = _stream.ReadByte();
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
_position = _end;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long d, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
long real;
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case SeekOrigin.Begin:
|
||||||
|
real = _offset + d;
|
||||||
|
break;
|
||||||
|
case SeekOrigin.End:
|
||||||
|
real = _end + d;
|
||||||
|
break;
|
||||||
|
case SeekOrigin.Current:
|
||||||
|
real = _position + d;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
long virt = real - _offset;
|
||||||
|
if (virt < 0 || virt > Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid position", nameof(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
_position = _stream.Seek(real, SeekOrigin.Begin);
|
||||||
|
return _position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead => true;
|
||||||
|
|
||||||
|
public override bool CanSeek => true;
|
||||||
|
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
public override long Length => _end - _offset;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _position - _offset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value > Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_position = Seek(value, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -225,7 +225,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
|
|
||||||
if (starts_with)
|
if (starts_with)
|
||||||
{
|
{
|
||||||
return StrUtils.StartsWith(ContentType, ct, true);
|
return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
|
||||||
|
@ -324,215 +324,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
return result.ToString();
|
return result.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class HttpPostedFile
|
|
||||||
{
|
|
||||||
private string name;
|
|
||||||
private string content_type;
|
|
||||||
private Stream stream;
|
|
||||||
|
|
||||||
private class ReadSubStream : Stream
|
|
||||||
{
|
|
||||||
private Stream s;
|
|
||||||
private long offset;
|
|
||||||
private long end;
|
|
||||||
private long position;
|
|
||||||
|
|
||||||
public ReadSubStream(Stream s, long offset, long length)
|
|
||||||
{
|
|
||||||
this.s = s;
|
|
||||||
this.offset = offset;
|
|
||||||
this.end = offset + length;
|
|
||||||
position = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int dest_offset, int count)
|
|
||||||
{
|
|
||||||
if (buffer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dest_offset < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(count), "< 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = buffer.Length;
|
|
||||||
if (dest_offset > len)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
// reordered to avoid possible integer overflow
|
|
||||||
if (dest_offset > len - count)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Reading would overrun buffer", nameof(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > end - position)
|
|
||||||
{
|
|
||||||
count = (int)(end - position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Position = position;
|
|
||||||
int result = s.Read(buffer, dest_offset, count);
|
|
||||||
if (result > 0)
|
|
||||||
{
|
|
||||||
position += result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
position = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int ReadByte()
|
|
||||||
{
|
|
||||||
if (position >= end)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Position = position;
|
|
||||||
int result = s.ReadByte();
|
|
||||||
if (result < 0)
|
|
||||||
{
|
|
||||||
position = end;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long d, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
long real;
|
|
||||||
switch (origin)
|
|
||||||
{
|
|
||||||
case SeekOrigin.Begin:
|
|
||||||
real = offset + d;
|
|
||||||
break;
|
|
||||||
case SeekOrigin.End:
|
|
||||||
real = end + d;
|
|
||||||
break;
|
|
||||||
case SeekOrigin.Current:
|
|
||||||
real = position + d;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
|
|
||||||
}
|
|
||||||
|
|
||||||
long virt = real - offset;
|
|
||||||
if (virt < 0 || virt > Length)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid position", nameof(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
position = s.Seek(real, SeekOrigin.Begin);
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead => true;
|
|
||||||
|
|
||||||
public override bool CanSeek => true;
|
|
||||||
|
|
||||||
public override bool CanWrite => false;
|
|
||||||
|
|
||||||
public override long Length => end - offset;
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => position - offset;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value > Length)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
position = Seek(value, SeekOrigin.Begin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
|
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
this.content_type = content_type;
|
|
||||||
this.stream = new ReadSubStream(base_stream, offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ContentType => content_type;
|
|
||||||
|
|
||||||
public int ContentLength => (int)stream.Length;
|
|
||||||
|
|
||||||
public string FileName => name;
|
|
||||||
|
|
||||||
public Stream InputStream => stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class StrUtils
|
|
||||||
{
|
|
||||||
public static bool StartsWith(string str1, string str2, bool ignore_case)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str1))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
|
||||||
return str1.IndexOf(str2, comparison) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool EndsWith(string str1, string str2, bool ignore_case)
|
|
||||||
{
|
|
||||||
int l2 = str2.Length;
|
|
||||||
if (l2 == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int l1 = str1.Length;
|
|
||||||
if (l2 > l1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
|
||||||
return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HttpMultipart
|
private class HttpMultipart
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -606,12 +397,12 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
string header;
|
string header;
|
||||||
while ((header = ReadHeaders()) != null)
|
while ((header = ReadHeaders()) != null)
|
||||||
{
|
{
|
||||||
if (StrUtils.StartsWith(header, "Content-Disposition:", true))
|
if (header.StartsWith("Content-Disposition:", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
elem.Name = GetContentDispositionAttribute(header, "name");
|
elem.Name = GetContentDispositionAttribute(header, "name");
|
||||||
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
|
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
|
||||||
}
|
}
|
||||||
else if (StrUtils.StartsWith(header, "Content-Type:", true))
|
else if (header.StartsWith("Content-Type:", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
elem.ContentType = header.Substring("Content-Type:".Length).Trim();
|
elem.ContentType = header.Substring("Content-Type:".Length).Trim();
|
||||||
elem.Encoding = GetEncoding(elem.ContentType);
|
elem.Encoding = GetEncoding(elem.ContentType);
|
||||||
|
@ -730,13 +521,14 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StrUtils.EndsWith(line, boundary, false))
|
if (!line.EndsWith(boundary, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -55,6 +55,41 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
|
|
||||||
public QueryParamCollection Headers => _response.Headers;
|
public QueryParamCollection Headers => _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 void AddHeader(string name, string value)
|
public void AddHeader(string name, string value)
|
||||||
{
|
{
|
||||||
if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -126,41 +161,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||||
_response.Headers.Add("Set-Cookie", cookieStr);
|
_response.Headers.Add("Set-Cookie", cookieStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string AsHeaderValue(Cookie cookie)
|
|
||||||
{
|
|
||||||
var 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 bool SendChunked
|
public bool SendChunked
|
||||||
{
|
{
|
||||||
get => _response.SendChunked;
|
get => _response.SendChunked;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user