remove some useless code form http low level; delay normal connection close, to let client do it instead

master
UbitUmarov 2020-04-21 00:13:02 +01:00
parent c097f148dd
commit 43fdbf87d4
7 changed files with 61 additions and 92 deletions

View File

@ -277,7 +277,7 @@ namespace OSHttpServer
if (context.TriggerKeepalive)
{
context.TriggerKeepalive = false;
context.MonitorKeepaliveStartMS = nowMS;
context.MonitorKeepaliveStartMS = nowMS + 1;
return false;
}
@ -285,7 +285,10 @@ namespace OSHttpServer
{
if (EnvironmentTickCountAdd(context.TimeoutKeepAlive, context.MonitorKeepaliveStartMS) < nowMS)
{
disconnectError = SocketError.TimedOut;
if(context.IsClosing)
disconnectError = SocketError.Success;
else
disconnectError = SocketError.TimedOut;
context.MonitorKeepaliveStartMS = 0;
return true;
}

View File

@ -21,13 +21,13 @@ namespace OSHttpServer
public class HttpClientContext : IHttpClientContext, IDisposable
{
const int MAXREQUESTS = 20;
const int MAXKEEPALIVE = 60000;
const int MAXKEEPALIVE = 120000;
static private int basecontextID;
private readonly byte[] m_ReceiveBuffer;
private int m_ReceiveBytesLeft;
private ILogWriter _log;
private ILogWriter m_log;
private readonly IHttpRequestParser m_parser;
private HashSet<uint> requestsInServiceIDs;
private Socket m_sock;
@ -42,8 +42,8 @@ namespace OSHttpServer
public int TimeoutRequestReceived = 30000; // 30 seconds
// The difference between this and request received is on POST more time is needed before we get the full request.
public int TimeoutMaxIdle = 600000; // 3 minutes
public int m_TimeoutKeepAlive = MAXKEEPALIVE; // 400 seconds before keepalive timeout
public int TimeoutMaxIdle = 600000; // 10 minutes
public int m_TimeoutKeepAlive = 60000;
public int m_maxRequests = MAXREQUESTS;
@ -51,6 +51,7 @@ namespace OSHttpServer
public bool FullRequestReceived;
private bool isSendingResponse = false;
private bool m_isClosing = false;
private HttpRequest m_currentRequest;
private HttpResponse m_currentResponse;
@ -65,6 +66,11 @@ namespace OSHttpServer
}
}
public bool IsClosing
{
get { return m_isClosing;}
}
public int MaxRequests
{
get { return m_maxRequests; }
@ -102,14 +108,15 @@ namespace OSHttpServer
/// <exception cref="SocketException">If <see cref="Socket.BeginReceive(byte[],int,int,SocketFlags,AsyncCallback,object)"/> fails</exception>
/// <exception cref="ArgumentException">Stream must be writable and readable.</exception>
public HttpClientContext(bool secured, IPEndPoint remoteEndPoint,
Stream stream, IRequestParserFactory parserFactory, Socket sock)
Stream stream, ILogWriter m_logWriter, Socket sock)
{
if (!stream.CanWrite || !stream.CanRead)
throw new ArgumentException("Stream must be writable and readable.");
LocalIPEndPoint = remoteEndPoint;
_log = NullLogWriter.Instance;
m_parser = parserFactory.CreateParser(_log);
m_log = m_logWriter;
m_isClosing = false;
m_parser = new HttpRequestParser(m_log);
m_parser.RequestCompleted += OnRequestCompleted;
m_parser.RequestLineReceived += OnRequestLine;
m_parser.HeaderReceived += OnHeaderReceived;
@ -145,7 +152,7 @@ namespace OSHttpServer
public bool CanSend()
{
if (contextID < 0)
if (contextID < 0 || m_isClosing)
return false;
if (m_stream == null || m_sock == null || !m_sock.Connected)
@ -273,11 +280,11 @@ namespace OSHttpServer
/// </summary>
public ILogWriter LogWriter
{
get { return _log; }
get { return m_log; }
set
{
_log = value ?? NullLogWriter.Instance;
m_parser.LogWriter = _log;
m_log = value ?? NullLogWriter.Instance;
m_parser.LogWriter = m_log;
}
}
@ -309,9 +316,10 @@ namespace OSHttpServer
{
try
{
m_stream.Flush(); // we should be on a work task so hold on it
m_stream.Flush();
}
catch { }
}
m_stream.Close();
m_stream = null;
@ -346,6 +354,9 @@ namespace OSHttpServer
return;
}
if(m_isClosing)
continue;
m_ReceiveBytesLeft += bytesRead;
if (m_ReceiveBytesLeft > m_ReceiveBuffer.Length)
throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
@ -506,7 +517,7 @@ namespace OSHttpServer
ContextTimeoutManager.EnqueueSend(this, m_currentResponse.Priority, notThrottled);
}
public void EndSendResponse(uint requestID, ConnectionType ctype)
public async Task EndSendResponse(uint requestID, ConnectionType ctype)
{
isSendingResponse = false;
m_currentResponse?.Clear();
@ -522,19 +533,28 @@ namespace OSHttpServer
}
if (doclose)
Disconnect(SocketError.Success);
{
m_isClosing = true;
lock (requestsInServiceIDs)
requestsInServiceIDs.Clear();
TriggerKeepalive = true;
return;
}
else
{
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
if(Stream!=null && Stream.CanWrite)
{
ContextTimeoutManager.ContextEnterActiveSend();
try
{
Stream.Flush();
await Stream.FlushAsync().ConfigureAwait(false);
}
catch
{
};
ContextTimeoutManager.ContextLeaveActiveSend();
}
lock (requestsInServiceIDs)

View File

@ -16,7 +16,6 @@ namespace OSHttpServer
public class HttpContextFactory : IHttpContextFactory
{
private readonly ConcurrentDictionary<int, HttpClientContext> m_activeContexts = new ConcurrentDictionary<int, HttpClientContext>();
private readonly IRequestParserFactory m_parserfactory;
private readonly ILogWriter m_logWriter;
/// <summary>
@ -30,10 +29,9 @@ namespace OSHttpServer
/// <param name="writer">The writer.</param>
/// <param name="bufferSize">Amount of bytes to read from the incoming socket stream.</param>
/// <param name="factory">Used to create a request parser.</param>
public HttpContextFactory(ILogWriter writer, IRequestParserFactory factory)
public HttpContextFactory(ILogWriter writer)
{
m_logWriter = writer;
m_parserfactory = factory;
ContextTimeoutManager.Start();
}
@ -46,33 +44,16 @@ namespace OSHttpServer
/// <returns>A context.</returns>
protected HttpClientContext CreateContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
{
HttpClientContext context;
context = CreateNewContext(isSecured, endPoint, stream, sock);
var context = new HttpClientContext(isSecured, endPoint, stream, m_logWriter, sock);
context.Disconnected += OnFreeContext;
context.RequestReceived += OnRequestReceived;
context.Stream = stream;
context.IsSecured = isSecured;
context.LocalIPEndPoint = endPoint;
ContextTimeoutManager.StartMonitoringContext(context);
m_activeContexts[context.contextID] = context;
context.Start();
return context;
}
/// <summary>
/// Create a new context.
/// </summary>
/// <param name="isSecured">true if HTTPS is used.</param>
/// <param name="endPoint">Remote client</param>
/// <param name="stream">Network stream, <see cref="HttpClientContext"/></param>
/// <returns>A new context (always).</returns>
protected virtual HttpClientContext CreateNewContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
{
return new HttpClientContext(isSecured, endPoint, stream, m_parserfactory, sock);
}
private void OnRequestReceived(object sender, RequestEventArgs e)
{
RequestReceived?.Invoke(sender, e);

View File

@ -34,11 +34,11 @@ namespace OSHttpServer
/// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
/// <exception cref="ArgumentNullException"><c>address</c> is null.</exception>
/// <exception cref="ArgumentException">Port must be a positive number.</exception>
protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory)
protected OSHttpListener(IPAddress address, int port)
{
m_address = address;
m_port = port;
m_contextFactory = factory;
m_contextFactory = new HttpContextFactory(m_logWriter);
m_contextFactory.RequestReceived += OnRequestReceived;
}
@ -49,12 +49,10 @@ namespace OSHttpServer
/// <param name="port">TCP Port to listen on, default HTTPS port is 443</param>
/// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
/// <param name="certificate">Certificate to use</param>
/// <param name="protocol">which HTTPS protocol to use, default is TLS.</param>
protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory, X509Certificate certificate,
SslProtocols protocol)
: this(address, port, factory, certificate)
protected OSHttpListener(IPAddress address, int port, X509Certificate certificate)
: this(address, port)
{
m_sslProtocol = protocol;
m_certificate = certificate;
}
/// <summary>
@ -64,31 +62,28 @@ namespace OSHttpServer
/// <param name="port">TCP Port to listen on, default HTTPS port is 443</param>
/// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
/// <param name="certificate">Certificate to use</param>
protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory, X509Certificate certificate)
: this(address, port, factory)
/// <param name="protocol">which HTTPS protocol to use, default is TLS.</param>
protected OSHttpListener(IPAddress address, int port, X509Certificate certificate,
SslProtocols protocol)
: this(address, port)
{
m_certificate = certificate;
m_sslProtocol = protocol;
}
public static OSHttpListener Create(IPAddress address, int port)
{
RequestParserFactory requestFactory = new RequestParserFactory();
HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
return new OSHttpListener(address, port, factory);
return new OSHttpListener(address, port);
}
public static OSHttpListener Create(IPAddress address, int port, X509Certificate certificate)
{
RequestParserFactory requestFactory = new RequestParserFactory();
HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
return new OSHttpListener(address, port, factory, certificate);
return new OSHttpListener(address, port, certificate);
}
public static OSHttpListener Create(IPAddress address, int port, X509Certificate certificate, SslProtocols protocol)
{
RequestParserFactory requestFactory = new RequestParserFactory();
HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
return new OSHttpListener(address, port, factory, certificate, protocol);
return new OSHttpListener(address, port, certificate, protocol);
}
private void OnRequestReceived(object sender, RequestEventArgs e)
@ -96,7 +91,6 @@ namespace OSHttpServer
RequestReceived?.Invoke(sender, e);
}
public RemoteCertificateValidationCallback CertificateValidationCallback
{
set { m_clientCertValCallback = value; }

View File

@ -281,6 +281,9 @@ namespace OSHttpServer
public void Send()
{
if(m_context.IsClosing)
return;
if (Sent)
throw new InvalidOperationException("Everything have already been sent.");
@ -446,7 +449,7 @@ namespace OSHttpServer
if (m_body != null)
m_body.Dispose();
Sent = true;
m_context.EndSendResponse(requestID, Connection);
await m_context.EndSendResponse(requestID, Connection).ConfigureAwait(false);
}
private int CheckBandwidth(int request, int bytesLimit)

View File

@ -29,6 +29,7 @@ namespace OSHttpServer
bool CanSend();
bool IsSending();
bool IsClosing {get ;}
/// <summary>
/// Disconnect from client
@ -95,7 +96,7 @@ namespace OSHttpServer
void StartSendResponse(HttpResponse response);
void ContinueSendResponse(bool notThrottled);
void EndSendResponse(uint requestID, ConnectionType connection);
Task EndSendResponse(uint requestID, ConnectionType connection);
bool TrySendResponse(int limit);
}

View File

@ -1,33 +0,0 @@
using OSHttpServer.Parser;
namespace OSHttpServer
{
/// <summary>
/// Creates request parsers when needed.
/// </summary>
public class RequestParserFactory : IRequestParserFactory
{
/// <summary>
/// Create a new request parser.
/// </summary>
/// <param name="logWriter">Used when logging should be enabled.</param>
/// <returns>A new request parser.</returns>
public IHttpRequestParser CreateParser(ILogWriter logWriter)
{
return new HttpRequestParser(logWriter);
}
}
/// <summary>
/// Creates request parsers when needed.
/// </summary>
public interface IRequestParserFactory
{
/// <summary>
/// Create a new request parser.
/// </summary>
/// <param name="logWriter">Used when logging should be enabled.</param>
/// <returns>A new request parser.</returns>
IHttpRequestParser CreateParser(ILogWriter logWriter);
}
}