remove some useless code form http low level; delay normal connection close, to let client do it instead
parent
c097f148dd
commit
43fdbf87d4
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue