Added working ln.http.HttpClient
parent
eb122b944b
commit
35517e431c
|
@ -2,9 +2,9 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageVersion>0.9.0-test1</PackageVersion>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<LangVersion>9</LangVersion>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<PackageVersion>1.0.1</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using ln.http.client;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ln.http.tests
|
||||
{
|
||||
public class ClientTests
|
||||
{
|
||||
private HttpClient HttpClient;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
HttpClient = new HttpClient();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClient()
|
||||
{
|
||||
var o = HttpClient.Get("http://l--n.de");
|
||||
if (o && o.Value is HttpClientResponse response)
|
||||
{
|
||||
Assert.AreEqual(HttpStatusCode.Found, response.StatusCode);
|
||||
Assert.AreEqual("https://l--n.de/", response.Headers.Get("Location"));
|
||||
}
|
||||
|
||||
HttpClient.FollowRedirects = true;
|
||||
o = HttpClient.Get("http://l--n.de");
|
||||
if (o && o.Value is HttpClientResponse response2)
|
||||
{
|
||||
Assert.AreEqual(HttpStatusCode.OK, response2.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<LangVersion>9</LangVersion>
|
||||
|
||||
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using ln.collections;
|
||||
|
||||
|
@ -13,7 +14,7 @@ public class CertificateStore
|
|||
|
||||
public virtual void AddCertificate(X509Certificate certificate) => _cache.Add(certificate.Subject, certificate);
|
||||
public virtual bool TryGetCertificate(string hostname, out X509Certificate certificate) =>
|
||||
_cache.TryGetValue(hostname, out certificate);
|
||||
_cache.TryGetValue(String.Format("CN={0}", hostname), out certificate);
|
||||
|
||||
public virtual void RemoveCertificate(string hostname) => _cache.Remove(hostname);
|
||||
}
|
|
@ -14,6 +14,11 @@ namespace ln.http
|
|||
public HeaderContainer()
|
||||
{
|
||||
}
|
||||
public HeaderContainer(IEnumerable<Header> headers)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
Add(new Header(header));
|
||||
}
|
||||
|
||||
public HeaderContainer(Stream stream)
|
||||
{
|
||||
|
@ -52,6 +57,8 @@ namespace ln.http
|
|||
_headers.Add(lowerHeaderName, new Header(headerName, headerValue));
|
||||
}
|
||||
|
||||
public void Add(Header header) => Add(header.Name, header.Value);
|
||||
|
||||
public void Remove(string headerName) => _headers.Remove(headerName.ToLower());
|
||||
public void Clear() => _headers.Clear();
|
||||
|
||||
|
@ -206,6 +213,12 @@ namespace ln.http
|
|||
Value = headerValue;
|
||||
}
|
||||
|
||||
public Header(Header src)
|
||||
{
|
||||
Name = src.Name;
|
||||
Value = src.Value;
|
||||
}
|
||||
|
||||
public bool TryGetParameter(string parameterName, out string parameterValue)
|
||||
{
|
||||
if (_parameters is null)
|
||||
|
@ -249,7 +262,8 @@ namespace ln.http
|
|||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString() => String.Format("{0}: {1}", Name, Value);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using ln.http.content;
|
||||
|
||||
namespace ln.http;
|
||||
|
||||
|
@ -46,7 +47,7 @@ public class Http1XConnection : HttpConnection
|
|||
}
|
||||
}
|
||||
|
||||
HttpRequestStream requestStream = null;
|
||||
HttpContentStream contentStream = null;
|
||||
if (headerContainer.TryGetInteger("content-length", out int contentLength))
|
||||
{
|
||||
if (headerContainer.TryGetValue("Expect", out string expectValue) && expectValue.Equals("100-continue"))
|
||||
|
@ -56,10 +57,10 @@ public class Http1XConnection : HttpConnection
|
|||
ConnectionStream.Write(statusLineBytes);
|
||||
break;
|
||||
}
|
||||
requestStream = new HttpRequestStream(ConnectionStream, contentLength);
|
||||
contentStream = new HttpContentStream(ConnectionStream, contentLength);
|
||||
}
|
||||
|
||||
HttpRequestContext requestContext = new HttpRequestContext(Listener, this, ConnectionStream, new HttpRequest(_method, RequestUri, _httpVersion, false, headerContainer, requestStream));
|
||||
HttpRequestContext requestContext = new HttpRequestContext(Listener, this, ConnectionStream, new HttpRequest(_method, RequestUri, _httpVersion, false, headerContainer, contentStream));
|
||||
Listener.Dispatch(requestContext);
|
||||
|
||||
if (!ConnectionStream.CanWrite && !ConnectionStream.CanRead)
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.IO;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using ln.http.content;
|
||||
using ln.json;
|
||||
|
||||
namespace ln.http
|
||||
|
@ -24,16 +25,16 @@ namespace ln.http
|
|||
|
||||
|
||||
Dictionary<String, String> requestCookies;
|
||||
public HttpRequestStream ContentStream { get; }
|
||||
public HttpContentStream ContentStream { get; }
|
||||
|
||||
public HttpRequest(string requestMethodName, Uri requestUri, HttpVersion httpVersion, bool viaTLS, HeaderContainer headerContainer, HttpRequestStream requestStream)
|
||||
public HttpRequest(string requestMethodName, Uri requestUri, HttpVersion httpVersion, bool viaTLS, HeaderContainer headerContainer, HttpContentStream contentStream)
|
||||
{
|
||||
RequestMethodName = requestMethodName;
|
||||
RequestUri = requestUri;
|
||||
HttpVersion = httpVersion;
|
||||
Headers = headerContainer;
|
||||
ViaTLS = viaTLS;
|
||||
ContentStream = requestStream;
|
||||
ContentStream = contentStream;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
|
|
@ -34,4 +34,17 @@ public static class HttpVersionSupport
|
|||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static HttpVersion Parse(string httpVersion)
|
||||
{
|
||||
switch (httpVersion)
|
||||
{
|
||||
case "HTTP/1.0":
|
||||
return HttpVersion.HTTP10;
|
||||
case "HTTP/1.1":
|
||||
return HttpVersion.HTTP10;
|
||||
default:
|
||||
return HttpVersion.None;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,217 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using System.Text;
|
||||
using ln.http.content;
|
||||
using ln.http.exceptions;
|
||||
using ln.logging;
|
||||
using ln.patterns;
|
||||
using ln.type;
|
||||
namespace ln.http.client
|
||||
{
|
||||
public class HttpClient
|
||||
{
|
||||
CookieContainer Cookies { get; set; } = new CookieContainer();
|
||||
private CookieContainer _cookies = new CookieContainer();
|
||||
private HeaderContainer _defaultHeaders = new HeaderContainer();
|
||||
private ILogWriter _logWriter;
|
||||
|
||||
public HeaderContainer DefaultHeaders => _defaultHeaders;
|
||||
public CookieContainer Cookies => _cookies;
|
||||
|
||||
public HttpClient() :this(null) { }
|
||||
|
||||
public bool FollowRedirects { get; set; }
|
||||
|
||||
public HttpClient()
|
||||
public HttpClient(ILogWriter logWriter)
|
||||
{
|
||||
_logWriter = logWriter ?? new LogWriter(new ConsoleLogSink(true));
|
||||
_defaultHeaders.Add("User-Agent", "Mozilla/5.0 (ln.http.client.HttpClient)");
|
||||
}
|
||||
|
||||
public HttpClientRequest CreateRequest(URI uri)
|
||||
private delegate bool TryConnectDelegate(Uri uri, IPAddress ip, int port, out IDisposable disposable,
|
||||
out Stream connectionStream);
|
||||
public Optional<HttpClientResponse> Request(HttpMethod method, Uri uri, IEnumerable<Header> headers,
|
||||
Stream body)
|
||||
{
|
||||
return new HttpClientRequest(this, uri);
|
||||
TryConnectDelegate tryConnectDelegate = null;
|
||||
|
||||
switch (uri.Scheme)
|
||||
{
|
||||
case "http":
|
||||
tryConnectDelegate = this.TryConnect;
|
||||
break;
|
||||
case "https":
|
||||
tryConnectDelegate = this.TryConnectTLS;
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"URI scheme { uri.Scheme } not supported by HttpClient");
|
||||
}
|
||||
|
||||
foreach (var address in GetIPAddresses(uri))
|
||||
{
|
||||
if (tryConnectDelegate(uri, address, uri.Port, out IDisposable disposable, out Stream connectionStream))
|
||||
{
|
||||
try
|
||||
{
|
||||
return _Request(connectionStream, method, uri, headers, body);
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionStream?.Dispose();
|
||||
disposable?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new SocketException();
|
||||
}
|
||||
|
||||
private IPAddress[] GetIPAddresses(Uri uri)
|
||||
{
|
||||
switch (uri.HostNameType)
|
||||
{
|
||||
case UriHostNameType.Dns:
|
||||
return Dns.GetHostAddresses(uri.Host);
|
||||
case UriHostNameType.IPv4:
|
||||
case UriHostNameType.IPv6:
|
||||
return new[] { IPAddress.Parse(uri.Host), };
|
||||
default:
|
||||
return new IPAddress[0];
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryConnect(Uri uri, IPAddress ip, int port, out IDisposable disposable, out Stream connectionStream)
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpClient tcpClient = new TcpClient();
|
||||
tcpClient.Connect(new IPEndPoint(ip, port));
|
||||
disposable = tcpClient;
|
||||
connectionStream = tcpClient.GetStream();
|
||||
return true;
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
}
|
||||
|
||||
disposable = null;
|
||||
connectionStream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryConnectTLS(Uri uri, IPAddress ip, int port, out IDisposable disposable, out Stream connectionStream)
|
||||
{
|
||||
if (TryConnect(uri, ip, port, out IDisposable rawDisposable, out Stream rawConnectionStream))
|
||||
{
|
||||
SslStream sslStream = new SslStream(rawConnectionStream, false);
|
||||
sslStream.AuthenticateAsClient(uri.Host);
|
||||
|
||||
disposable = rawDisposable;
|
||||
connectionStream = sslStream;
|
||||
return true;
|
||||
}
|
||||
|
||||
disposable = null;
|
||||
connectionStream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<HttpClientResponse> _Request(Stream connectionStream, HttpMethod method, Uri uri,
|
||||
IEnumerable<Header> headers, Stream body)
|
||||
{
|
||||
HeaderContainer requestHeaders = new HeaderContainer(_defaultHeaders);
|
||||
requestHeaders.Set("Host", uri.Host);
|
||||
if (body is Stream)
|
||||
requestHeaders.Set("Content-Length", body.Length.ToString());
|
||||
|
||||
connectionStream.WriteBytes(Encoding.ASCII.GetBytes(String.Format("{0} {1} HTTP/1.1\r\n",
|
||||
method.ToString(),
|
||||
uri.PathAndQuery
|
||||
)));
|
||||
requestHeaders.CopyTo(connectionStream);
|
||||
|
||||
if ((method != HttpMethod.HEAD) && (body is Stream))
|
||||
body.CopyTo(connectionStream);
|
||||
|
||||
connectionStream.Flush();
|
||||
|
||||
if (!ReadResponseLine(connectionStream, out HttpVersion httpVersion, out HttpStatusCode statusCode,
|
||||
out string reason))
|
||||
return new ProtocolViolationException();
|
||||
|
||||
HeaderContainer responseHeaders = new HeaderContainer(connectionStream);
|
||||
Stream contentStream = null;
|
||||
|
||||
if (method != HttpMethod.HEAD)
|
||||
{
|
||||
if (responseHeaders.TryGetValue("Transfer-Encoding", out string transferEncoding) &&
|
||||
(transferEncoding.Equals("chunked")))
|
||||
contentStream = new HttpContentStreamChunked(connectionStream);
|
||||
else if (responseHeaders.TryGetInteger("Content-Length", out int contentLength))
|
||||
contentStream = new HttpContentStream(connectionStream, contentLength);
|
||||
}
|
||||
|
||||
if (FollowRedirects)
|
||||
{
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Found:
|
||||
case HttpStatusCode.MovedPermanently:
|
||||
case HttpStatusCode.TemporaryRedirect:
|
||||
case HttpStatusCode.PermanentRedirect:
|
||||
if (body != null)
|
||||
body.Position = 0;
|
||||
return Request(method, new Uri(responseHeaders.Get("Location")), headers, body);
|
||||
case HttpStatusCode.SeeOther:
|
||||
return Get(responseHeaders.Get("Location"), headers);
|
||||
}
|
||||
}
|
||||
return new HttpClientResponse(method, uri, statusCode, reason, responseHeaders, contentStream);
|
||||
}
|
||||
|
||||
public Optional<HttpClientResponse> Get(Uri uri) => Request(HttpMethod.GET, uri, null, null);
|
||||
public Optional<HttpClientResponse> Get(Uri uri, IEnumerable<Header> headers) => Request(HttpMethod.GET, uri, headers, null);
|
||||
public Optional<HttpClientResponse> Post(Uri uri, Stream body) => Request(HttpMethod.POST, uri, null, body);
|
||||
public Optional<HttpClientResponse> Post(Uri uri, IEnumerable<Header> headers, Stream body) => Request(HttpMethod.POST, uri, headers, body);
|
||||
public Optional<HttpClientResponse> Get(string uri) => Request(HttpMethod.GET, new Uri(uri), null, null);
|
||||
public Optional<HttpClientResponse> Get(string uri, IEnumerable<Header> headers) => Request(HttpMethod.GET, new Uri(uri), headers, null);
|
||||
public Optional<HttpClientResponse> Post(string uri, Stream body) => Request(HttpMethod.POST, new Uri(uri), null, body);
|
||||
public Optional<HttpClientResponse> Post(string uri, IEnumerable<Header> headers, Stream body) => Request(HttpMethod.POST, new Uri(uri), headers, body);
|
||||
|
||||
|
||||
public static bool ReadResponseLine(Stream stream, out HttpVersion httpVersion, out HttpStatusCode statusCode,
|
||||
out string reason)
|
||||
{
|
||||
string requestLine = HttpConnection.ReadLine(stream);
|
||||
if (requestLine is null)
|
||||
{
|
||||
httpVersion = HttpVersion.None;
|
||||
statusCode = 0;
|
||||
reason = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
int idxSpace1 = requestLine.IndexOf(' ');
|
||||
int idxSpace2 = requestLine.IndexOf(' ', idxSpace1 + 1);
|
||||
|
||||
if ((idxSpace1 > 0) && (idxSpace2 > 0))
|
||||
{
|
||||
httpVersion = HttpVersionSupport.Parse(requestLine.Substring(0, idxSpace1));
|
||||
statusCode = (HttpStatusCode)int.Parse(requestLine.Substring(idxSpace1 + 1, (idxSpace2 - idxSpace1 - 1)));
|
||||
reason = requestLine.Substring(idxSpace2 + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
httpVersion = HttpVersion.None;
|
||||
statusCode = 0;
|
||||
reason = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
using System;
|
||||
using ln.type;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.Security;
|
||||
|
||||
namespace ln.http.client
|
||||
{
|
||||
public class HttpClientRequest
|
||||
{
|
||||
public HttpClient HttpClient { get; }
|
||||
public URI URI
|
||||
{
|
||||
get => uri;
|
||||
set
|
||||
{
|
||||
if (!value.Scheme.Equals("http") && !value.Scheme.Equals("https"))
|
||||
throw new ArgumentOutOfRangeException(nameof(value), String.Format("unsupported url scheme: {0}", value.Scheme));
|
||||
uri = value;
|
||||
}
|
||||
}
|
||||
|
||||
public HeaderContainer Headers { get; } = new HeaderContainer();
|
||||
public String Method { get; set; }
|
||||
|
||||
URI uri;
|
||||
MemoryStream contentStream;
|
||||
HttpClientResponse clientResponse;
|
||||
|
||||
public HttpClientRequest(HttpClient httpClient, URI uri)
|
||||
{
|
||||
HttpClient = httpClient;
|
||||
URI = uri;
|
||||
|
||||
Headers.Add("user-agent", "ln.http.client");
|
||||
|
||||
}
|
||||
|
||||
public Stream GetContentStream()
|
||||
{
|
||||
if (clientResponse != null)
|
||||
throw new NotSupportedException("Request has already been executed, content stream has been disposed");
|
||||
|
||||
if (contentStream == null)
|
||||
contentStream = new MemoryStream();
|
||||
|
||||
return contentStream;
|
||||
}
|
||||
|
||||
public HttpClientResponse GetResponse()
|
||||
{
|
||||
if (clientResponse == null)
|
||||
{
|
||||
executeRequest();
|
||||
}
|
||||
return clientResponse;
|
||||
}
|
||||
|
||||
private Stream OpenConnection()
|
||||
{
|
||||
TcpClient tcpClient = null;
|
||||
SslStream sslStream = null;
|
||||
|
||||
try
|
||||
{
|
||||
tcpClient = new TcpClient();
|
||||
tcpClient.ExclusiveAddressUse = false;
|
||||
|
||||
tcpClient.Connect(URI.Host, int.Parse(URI.Port));
|
||||
if (!tcpClient.Connected)
|
||||
throw new IOException(String.Format("could not connect to host {0}",uri.Host));
|
||||
|
||||
if (uri.Scheme.Equals("https"))
|
||||
{
|
||||
sslStream = new SslStream(tcpClient.GetStream());
|
||||
return sslStream;
|
||||
}
|
||||
return tcpClient.GetStream();
|
||||
} catch (Exception)
|
||||
{
|
||||
if (sslStream != null)
|
||||
sslStream.Dispose();
|
||||
if (tcpClient != null)
|
||||
tcpClient.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeRequest()
|
||||
{
|
||||
if (contentStream.Length > 0)
|
||||
{
|
||||
byte[] requestContent = contentStream.ToArray();
|
||||
Headers.Add("content-length", requestContent.Length.ToString());
|
||||
}
|
||||
|
||||
Stream connectionStream = OpenConnection();
|
||||
|
||||
|
||||
clientResponse = new HttpClientResponse(this);
|
||||
|
||||
|
||||
contentStream.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,13 +1,35 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using ln.http.content;
|
||||
|
||||
namespace ln.http.client
|
||||
{
|
||||
public class HttpClientResponse
|
||||
{
|
||||
public HttpClientRequest ClientRequest { get; }
|
||||
public HttpMethod Method { get; }
|
||||
public Uri Uri { get; }
|
||||
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
public string Reason { get; }
|
||||
|
||||
public HttpClientResponse(HttpClientRequest clientRequest)
|
||||
private HeaderContainer _responseHeaders;
|
||||
private Stream _contentStream;
|
||||
|
||||
public HeaderContainer Headers => _responseHeaders;
|
||||
public Stream ContentStream => _contentStream;
|
||||
|
||||
public HttpClientResponse(HttpMethod method, Uri uri, HttpStatusCode statusCode, string reason, HeaderContainer responseHeaders, Stream contentStream)
|
||||
{
|
||||
ClientRequest = clientRequest;
|
||||
Method = method;
|
||||
Uri = uri;
|
||||
|
||||
StatusCode = statusCode;
|
||||
Reason = reason;
|
||||
|
||||
_responseHeaders = responseHeaders;
|
||||
_contentStream = contentStream;
|
||||
}
|
||||
|
||||
public override string ToString() => String.Format("{0} {1}", (int)StatusCode, StatusCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ln.http;
|
||||
namespace ln.http.content;
|
||||
|
||||
public class HttpRequestStream : Stream
|
||||
public class HttpContentStream : Stream
|
||||
{
|
||||
public int MemoryLimit { get; set; } = 1024 * 1024 * 10;
|
||||
|
||||
private Stream _stream;
|
||||
private string _tempFileName;
|
||||
|
||||
public HttpRequestStream(Stream connectionStream, int length)
|
||||
public HttpContentStream(Stream connectionStream, int length)
|
||||
{
|
||||
byte[] transferBuffer;
|
||||
// if (length <= MemoryLimit)
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ln.http.content;
|
||||
|
||||
public class HttpContentStreamChunked : Stream
|
||||
{
|
||||
private Stream _stream;
|
||||
private string _tempFileName;
|
||||
|
||||
public HttpContentStreamChunked(Stream connectionStream)
|
||||
{
|
||||
byte[] transferBuffer;
|
||||
_tempFileName = Path.GetTempFileName();
|
||||
_stream = new FileStream(_tempFileName, FileMode.Open, FileAccess.ReadWrite);
|
||||
|
||||
transferBuffer = new byte[1024 * 1024];
|
||||
|
||||
int nChunkSize;
|
||||
do
|
||||
{
|
||||
string chunkSize = HttpConnection.ReadLine(connectionStream);
|
||||
nChunkSize = int.Parse(chunkSize, System.Globalization.NumberStyles.HexNumber);
|
||||
int length = nChunkSize;
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
int size = length > transferBuffer.Length ? transferBuffer.Length : length;
|
||||
int nread = connectionStream.Read(transferBuffer, 0, size);
|
||||
_stream.Write(transferBuffer, 0, nread);
|
||||
length -= nread;
|
||||
}
|
||||
|
||||
HttpConnection.ReadLine(connectionStream);
|
||||
} while (nChunkSize != 0);
|
||||
|
||||
_stream.Position = 0;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count);
|
||||
public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
|
||||
public override void SetLength(long value) => throw new System.NotImplementedException();
|
||||
public override void Write(byte[] buffer, int offset, int count) => throw new System.NotImplementedException();
|
||||
public override void Flush() => throw new System.NotImplementedException();
|
||||
|
||||
public override bool CanRead { get; } = true;
|
||||
public override bool CanSeek { get; } = true;
|
||||
public override bool CanWrite { get; } = false;
|
||||
public override long Length => _stream.Length;
|
||||
public override long Position { get => _stream.Position; set => _stream.Position = value; }
|
||||
|
||||
public override int Read(Span<byte> buffer) => _stream.Read(buffer);
|
||||
public override void Close() => _stream.Close();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_stream.Dispose();
|
||||
if (_tempFileName is not null)
|
||||
File.Delete(_tempFileName);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
||||
<PackageTags>http server</PackageTags>
|
||||
<LangVersion>default</LangVersion>
|
||||
<PackageVersion>0.9.8</PackageVersion>
|
||||
<PackageVersion>0.9.9-preview0</PackageVersion>
|
||||
<AssemblyVersion>0.6.2.0</AssemblyVersion>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
@ -18,6 +18,7 @@
|
|||
<PackageReference Include="ln.collections" Version="0.2.2" />
|
||||
<PackageReference Include="ln.json" Version="1.2.1" />
|
||||
<PackageReference Include="ln.mime" Version="1.1.1" />
|
||||
<PackageReference Include="ln.patterns" Version="0.1.0-preview7" />
|
||||
<PackageReference Include="ln.threading" Version="0.2.2" />
|
||||
<PackageReference Include="ln.type" Version="0.1.9" />
|
||||
</ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue