Files
crtr/Assets/Cryville/Common/Network/Http11/Http11ResponseStream.cs

130 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace Cryville.Common.Network.Http11 {
public abstract class Http11ResponseStream : Stream {
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(); }
}
public override void Flush() { }
public abstract byte[] ReadToEnd();
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();
}
}
internal sealed class Http11ResponseBlockStream : Http11ResponseStream {
readonly BinaryReader _reader;
readonly int _length;
int _pos = 0;
internal Http11ResponseBlockStream(BinaryReader reader, int length) {
_reader = reader;
_length = length;
}
public override int Read(byte[] buffer, int offset, int count) {
int recv = 0;
int recv_len = System.Math.Min(count, _length - _pos);
if (recv_len == 0) return 0;
while (recv < recv_len) {
recv += _reader.Read(buffer, offset + recv, count - recv);
}
_pos += recv_len;
return recv_len;
}
public override byte[] ReadToEnd() {
byte[] buffer = new byte[_length - _pos];
Read(buffer, 0, buffer.Length);
return buffer;
}
}
internal sealed class Http11ResponseChunkedStream : Http11ResponseStream {
readonly BinaryReader _reader;
byte[] _chunk = null;
int _pos = 0;
internal Http11ResponseChunkedStream(BinaryReader reader) {
_reader = reader;
ReadChunk();
}
public void ReadChunk() {
if (_chunk != null && _chunk.Length == 0) return;
string[] chunkHeader = Http11Response.ReadLine(_reader).Split(';');
if (!int.TryParse(chunkHeader[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int chunkSize))
throw new IOException("Corrupted chunk received");
if (chunkSize == 0) {
_chunk = new byte[0];
// TODO TE Header, now just discard
var headers = new Dictionary<string, string>();
while (Http11Response.ParseHeader(_reader, headers)) ;
return;
}
_chunk = new byte[chunkSize];
int recv = 0;
while (recv < chunkSize) {
recv += _reader.Read(_chunk, recv, chunkSize - recv);
}
_pos = 0;
if (Http11Response.ReadLine(_reader) != "")
throw new IOException("Corrupted chunk received");
}
public override int Read(byte[] buffer, int offset, int count) {
if (_chunk.Length == 0) return 0;
int recv = 0;
while (true) {
if (count - recv <= _chunk.Length - _pos) break;
Array.Copy(_chunk, _pos, buffer, recv, _chunk.Length - _pos);
recv += _chunk.Length - _pos;
ReadChunk();
if (_chunk.Length == 0) return recv;
}
Array.Copy(_chunk, _pos, buffer, recv, count - recv);
return count;
}
public override byte[] ReadToEnd() {
if (_chunk.Length == 0) return new byte[0];
List<byte[]> segs = new();
while (true) {
if (_pos != 0) {
var buffer = new byte[_chunk.Length - _pos];
Array.Copy(_chunk, _pos, buffer, 0, buffer.Length);
segs.Add(buffer);
}
else segs.Add(_chunk);
ReadChunk();
if (_chunk.Length == 0) {
var result = new byte[segs.Sum(i => i.Length)];
int p = 0;
foreach (var i in segs) {
Array.Copy(i, 0, result, p, i.Length);
p += i.Length;
}
return result;
}
}
}
}
}