You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jellyfin/SocketHttpListener.Portable/Net/RequestStream.cs

232 lines
7.3 KiB

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace SocketHttpListener.Net
{
class RequestStream : Stream
{
byte[] buffer;
int offset;
int length;
long remaining_body;
bool disposed;
Stream stream;
internal RequestStream(Stream stream, byte[] buffer, int offset, int length)
: this(stream, buffer, offset, length, -1)
{
}
internal RequestStream(Stream stream, byte[] buffer, int offset, int length, long contentlength)
{
this.stream = stream;
this.buffer = buffer;
this.offset = offset;
this.length = length;
this.remaining_body = contentlength;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
protected override void Dispose(bool disposing)
{
disposed = true;
}
public override void Flush()
{
}
// Returns 0 if we can keep reading from the base stream,
// > 0 if we read something from the buffer.
// -1 if we had a content length set and we finished reading that many bytes.
int FillFromBuffer(byte[] buffer, int off, int count)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (off < 0)
throw new ArgumentOutOfRangeException("offset", "< 0");
if (count < 0)
throw new ArgumentOutOfRangeException("count", "< 0");
int len = buffer.Length;
if (off > len)
throw new ArgumentException("destination offset is beyond array size");
if (off > len - count)
throw new ArgumentException("Reading would overrun buffer");
if (this.remaining_body == 0)
return -1;
if (this.length == 0)
return 0;
int size = Math.Min(this.length, count);
if (this.remaining_body > 0)
size = (int)Math.Min(size, this.remaining_body);
if (this.offset > this.buffer.Length - size)
{
size = Math.Min(size, this.buffer.Length - this.offset);
}
if (size == 0)
return 0;
Buffer.BlockCopy(this.buffer, this.offset, buffer, off, size);
this.offset += size;
this.length -= size;
if (this.remaining_body > 0)
remaining_body -= size;
return size;
}
public override int Read([In, Out] byte[] buffer, int offset, int count)
{
if (disposed)
throw new ObjectDisposedException(typeof(RequestStream).ToString());
// Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
int nread = FillFromBuffer(buffer, offset, count);
if (nread == -1)
{ // No more bytes available (Content-Length)
return 0;
}
else if (nread > 0)
{
return nread;
}
nread = stream.Read(buffer, offset, count);
if (nread > 0 && remaining_body > 0)
remaining_body -= nread;
return nread;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (disposed)
throw new ObjectDisposedException(typeof(RequestStream).ToString());
int nread = FillFromBuffer(buffer, offset, count);
if (nread > 0 || nread == -1)
{
return Math.Max(0, nread);
}
// Avoid reading past the end of the request to allow
// for HTTP pipelining
if (remaining_body >= 0 && count > remaining_body)
count = (int)Math.Min(Int32.MaxValue, remaining_body);
nread = await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
if (remaining_body > 0 && nread > 0)
remaining_body -= nread;
return nread;
}
//public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
// AsyncCallback cback, object state)
//{
// if (disposed)
// throw new ObjectDisposedException(typeof(RequestStream).ToString());
// int nread = FillFromBuffer(buffer, offset, count);
// if (nread > 0 || nread == -1)
// {
// HttpStreamAsyncResult ares = new HttpStreamAsyncResult();
// ares.Buffer = buffer;
// ares.Offset = offset;
// ares.Count = count;
// ares.Callback = cback;
// ares.State = state;
// ares.SynchRead = Math.Max(0, nread);
// ares.Complete();
// return ares;
// }
// // Avoid reading past the end of the request to allow
// // for HTTP pipelining
// if (remaining_body >= 0 && count > remaining_body)
// count = (int)Math.Min(Int32.MaxValue, remaining_body);
// return stream.BeginRead(buffer, offset, count, cback, state);
//}
//public override int EndRead(IAsyncResult ares)
//{
// if (disposed)
// throw new ObjectDisposedException(typeof(RequestStream).ToString());
// if (ares == null)
// throw new ArgumentNullException("async_result");
// if (ares is HttpStreamAsyncResult)
// {
// HttpStreamAsyncResult r = (HttpStreamAsyncResult)ares;
// if (!ares.IsCompleted)
// ares.AsyncWaitHandle.WaitOne();
// return r.SynchRead;
// }
// // Close on exception?
// int nread = stream.EndRead(ares);
// if (remaining_body > 0 && nread > 0)
// remaining_body -= nread;
// return nread;
//}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
//public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
// AsyncCallback cback, object state)
//{
// throw new NotSupportedException();
//}
//public override void EndWrite(IAsyncResult async_result)
//{
// throw new NotSupportedException();
//}
}
}