more changes on http server low level

master
UbitUmarov 2020-04-08 14:35:31 +01:00
parent f0f067d05c
commit f976d10de2
6 changed files with 175 additions and 126 deletions

View File

@ -93,17 +93,17 @@ namespace OSHttpServer
{ {
while (!m_shuttingDown) while (!m_shuttingDown)
{ {
m_processWaitEven.WaitOne(100); m_processWaitEven.WaitOne(500);
if(m_shuttingDown) if(m_shuttingDown)
return; return;
double now = GetTimeStampMS(); double now = GetTimeStamp();
if(m_contexts.Count > 0) if(m_contexts.Count > 0)
{ {
ProcessSendQueues(now); ProcessSendQueues(now);
if (now - m_lastTimeOutCheckTime > 1000) if (now - m_lastTimeOutCheckTime > 1.0)
{ {
ProcessContextTimeouts(); ProcessContextTimeouts();
m_lastTimeOutCheckTime = now; m_lastTimeOutCheckTime = now;
@ -158,8 +158,8 @@ namespace OSHttpServer
if(curConcurrentLimit > inqueues) if(curConcurrentLimit > inqueues)
curConcurrentLimit = inqueues; curConcurrentLimit = inqueues;
if (dt > 0.1) if (dt > 0.5)
dt = 0.1; dt = 0.5;
dt /= curConcurrentLimit; dt /= curConcurrentLimit;
int curbytesLimit = (int)(m_maxBandWidth * dt); int curbytesLimit = (int)(m_maxBandWidth * dt);
@ -250,68 +250,64 @@ namespace OSHttpServer
if (context.contextID < 0 || context.StopMonitoring || context.StreamPassedOff) if (context.contextID < 0 || context.StopMonitoring || context.StreamPassedOff)
return true; return true;
// Now we start checking for actual timeouts int nowMS = EnvironmentTickCount();
// First we check that we got at least one line within context.TimeoutFirstLine milliseconds // First we check first contact line
if (!context.FirstRequestLineReceived) if (!context.FirstRequestLineReceived)
{ {
if (EnvironmentTickCountAdd(context.TimeoutFirstLine, context.MonitorStartMS) <= EnvironmentTickCount()) if (EnvironmentTickCountAdd(context.TimeoutFirstLine, context.LastActivityTimeMS) < nowMS)
{ {
disconnectError = SocketError.TimedOut; disconnectError = SocketError.TimedOut;
context.MonitorStartMS = 0;
return true; return true;
} }
return false;
} }
// First we check first contact request
if (!context.FullRequestReceived) if (!context.FullRequestReceived)
{ {
if (EnvironmentTickCountAdd(context.TimeoutRequestReceived, context.MonitorStartMS) <= EnvironmentTickCount()) if (EnvironmentTickCountAdd(context.TimeoutRequestReceived, context.LastActivityTimeMS) < nowMS)
{ {
disconnectError = SocketError.TimedOut; disconnectError = SocketError.TimedOut;
context.MonitorStartMS = 0;
return true;
}
}
//
if (!context.FullRequestProcessed)
{
if (EnvironmentTickCountAdd(context.TimeoutFullRequestProcessed, context.MonitorStartMS) <= EnvironmentTickCount())
{
disconnectError = SocketError.TimedOut;
context.MonitorStartMS = 0;
return true; return true;
} }
return false;
} }
if (context.TriggerKeepalive) if (context.TriggerKeepalive)
{ {
context.TriggerKeepalive = false; context.TriggerKeepalive = false;
context.MonitorKeepaliveMS = EnvironmentTickCount(); context.MonitorKeepaliveStartMS = nowMS;
return false;
} }
if (context.FullRequestProcessed && context.MonitorKeepaliveMS == 0) if (context.MonitorKeepaliveStartMS != 0)
return true; {
if (EnvironmentTickCountAdd(context.TimeoutKeepAlive, context.MonitorKeepaliveStartMS) < nowMS)
{
disconnectError = SocketError.TimedOut;
context.MonitorKeepaliveStartMS = 0;
return true;
}
return false;
}
if (context.MonitorKeepaliveMS != 0 && if (EnvironmentTickCountAdd(context.TimeoutMaxIdle, context.LastActivityTimeMS) < nowMS)
EnvironmentTickCountAdd(context.TimeoutKeepAlive, context.MonitorKeepaliveMS) <= EnvironmentTickCount())
{ {
disconnectError = SocketError.TimedOut; disconnectError = SocketError.TimedOut;
context.MonitorStartMS = 0; context.MonitorKeepaliveStartMS = 0;
context.MonitorKeepaliveMS = 0;
return true; return true;
} }
return false; return false;
} }
public static void StartMonitoringContext(HttpClientContext context) public static void StartMonitoringContext(HttpClientContext context)
{ {
context.MonitorStartMS = EnvironmentTickCount(); context.LastActivityTimeMS = EnvironmentTickCount();
m_contexts.Enqueue(context); m_contexts.Enqueue(context);
} }
public static void EnqueueSend(HttpClientContext context, int priority) public static void EnqueueSend(HttpClientContext context, int priority, bool notThrottled = true)
{ {
switch(priority) switch(priority)
{ {
@ -327,7 +323,8 @@ namespace OSHttpServer
default: default:
return; return;
} }
m_processWaitEven.Set(); if(notThrottled)
m_processWaitEven.Set();
} }
public static void ContextEnterActiveSend() public static void ContextEnterActiveSend()

View File

@ -20,7 +20,7 @@ namespace OSHttpServer
/// </remarks> /// </remarks>
public class HttpClientContext : IHttpClientContext, IDisposable public class HttpClientContext : IHttpClientContext, IDisposable
{ {
const int MAXPIPEREQUESTS = 5; const int MAXREQUESTS = 20;
const int MAXKEEPALIVE = 60000; const int MAXKEEPALIVE = 60000;
static private int basecontextID; static private int basecontextID;
@ -36,22 +36,20 @@ namespace OSHttpServer
public bool Available = true; public bool Available = true;
public bool StreamPassedOff = false; public bool StreamPassedOff = false;
public int MonitorStartMS = 0; public int LastActivityTimeMS = 0;
public int MonitorKeepaliveMS = 0; public int MonitorKeepaliveStartMS = 0;
public bool TriggerKeepalive = false; public bool TriggerKeepalive = false;
public int TimeoutFirstLine = 70000; // 70 seconds public int TimeoutFirstLine = 10000; // 10 seconds
public int TimeoutRequestReceived = 180000; // 180 seconds 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. // The difference between this and request received is on POST more time is needed before we get the full request.
public int TimeoutFullRequestProcessed = 600000; // 10 minutes public int TimeoutMaxIdle = 180000; // 3 minutes
public int m_TimeoutKeepAlive = MAXKEEPALIVE; // 400 seconds before keepalive timeout public int m_TimeoutKeepAlive = MAXKEEPALIVE; // 400 seconds before keepalive timeout
// public int TimeoutKeepAlive = 120000; // 400 seconds before keepalive timeout
public int m_maxPipeRequests = MAXPIPEREQUESTS; public int m_maxRequests = MAXREQUESTS;
public bool FirstRequestLineReceived; public bool FirstRequestLineReceived;
public bool FullRequestReceived; public bool FullRequestReceived;
public bool FullRequestProcessed;
private bool isSendingResponse = false; private bool isSendingResponse = false;
@ -68,15 +66,15 @@ namespace OSHttpServer
} }
} }
public int MaxPipeRequests public int MaxRequests
{ {
get { return m_maxPipeRequests; } get { return m_maxRequests; }
set set
{ {
if(value <= 1) if(value <= 1)
m_maxPipeRequests = 1; m_maxRequests = 1;
else else
m_maxPipeRequests = value > MAXPIPEREQUESTS ? MAXPIPEREQUESTS : value; m_maxRequests = value > MAXREQUESTS ? MAXREQUESTS : value;
} }
} }
@ -118,7 +116,7 @@ namespace OSHttpServer
m_parser.BodyBytesReceived += OnBodyBytesReceived; m_parser.BodyBytesReceived += OnBodyBytesReceived;
m_currentRequest = new HttpRequest(this); m_currentRequest = new HttpRequest(this);
IsSecured = secured; IsSecured = secured;
_stream = stream; m_stream = stream;
m_sock = sock; m_sock = sock;
m_bufferSize = 8196; m_bufferSize = 8196;
@ -128,7 +126,7 @@ namespace OSHttpServer
SSLCommonName = ""; SSLCommonName = "";
if (secured) if (secured)
{ {
SslStream _ssl = (SslStream)_stream; SslStream _ssl = (SslStream)m_stream;
X509Certificate _cert1 = _ssl.RemoteCertificate; X509Certificate _cert1 = _ssl.RemoteCertificate;
if (_cert1 != null) if (_cert1 != null)
{ {
@ -150,7 +148,7 @@ namespace OSHttpServer
if (contextID < 0) if (contextID < 0)
return false; return false;
if (Stream == null || m_sock == null || !m_sock.Connected) if (m_stream == null || m_sock == null || !m_sock.Connected)
return false; return false;
return true; return true;
@ -191,9 +189,11 @@ namespace OSHttpServer
m_currentRequest.UriPath = e.UriPath; m_currentRequest.UriPath = e.UriPath;
m_currentRequest.AddHeader("remote_addr", RemoteAddress); m_currentRequest.AddHeader("remote_addr", RemoteAddress);
m_currentRequest.AddHeader("remote_port", RemotePort); m_currentRequest.AddHeader("remote_port", RemotePort);
FirstRequestLineReceived = true; FirstRequestLineReceived = true;
TriggerKeepalive = false; TriggerKeepalive = false;
MonitorKeepaliveMS = 0; MonitorKeepaliveStartMS = 0;
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
} }
/// <summary> /// <summary>
@ -218,10 +218,10 @@ namespace OSHttpServer
if (StreamPassedOff) if (StreamPassedOff)
return; return;
if (Stream != null) if (m_stream != null)
{ {
Stream.Close(); m_stream.Close();
Stream = null; m_stream = null;
m_sock = null; m_sock = null;
} }
@ -233,10 +233,9 @@ namespace OSHttpServer
FirstRequestLineReceived = false; FirstRequestLineReceived = false;
FullRequestReceived = false; FullRequestReceived = false;
FullRequestProcessed = false; LastActivityTimeMS = 0;
MonitorStartMS = 0;
StopMonitoring = true; StopMonitoring = true;
MonitorKeepaliveMS = 0; MonitorKeepaliveStartMS = 0;
TriggerKeepalive = false; TriggerKeepalive = false;
isSendingResponse = false; isSendingResponse = false;
@ -283,15 +282,15 @@ namespace OSHttpServer
} }
} }
private Stream _stream; private Stream m_stream;
/// <summary> /// <summary>
/// Gets or sets the network stream. /// Gets or sets the network stream.
/// </summary> /// </summary>
internal Stream Stream internal Stream Stream
{ {
get { return _stream; } get { return m_stream; }
set { _stream = value; } set { m_stream = value; }
} }
/// <summary> /// <summary>
@ -315,12 +314,12 @@ namespace OSHttpServer
{ {
try try
{ {
if (Stream != null) if (m_stream != null)
{ {
if (error == SocketError.Success) if (error == SocketError.Success)
Stream.Flush(); m_stream.Flush();
Stream.Close(); m_stream.Close();
Stream = null; m_stream = null;
} }
m_sock = null; m_sock = null;
} }
@ -339,11 +338,11 @@ namespace OSHttpServer
try try
{ {
int bytesRead = 0; int bytesRead = 0;
if (Stream == null) if (m_stream == null)
return; return;
try try
{ {
bytesRead = Stream.EndRead(ar); bytesRead = m_stream.EndRead(ar);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -357,7 +356,7 @@ namespace OSHttpServer
return; return;
} }
if(m_maxPipeRequests <= 0) if(m_maxRequests <= 0)
return; return;
m_ReceiveBytesLeft += bytesRead; m_ReceiveBytesLeft += bytesRead;
@ -367,7 +366,7 @@ namespace OSHttpServer
} }
int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft); int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
if (Stream == null) if (m_stream == null)
return; // "Connection: Close" in effect. return; // "Connection: Close" in effect.
// try again to see if we can parse another message (check parser to see if it is looking for a new message) // try again to see if we can parse another message (check parser to see if it is looking for a new message)
@ -378,7 +377,7 @@ namespace OSHttpServer
{ {
nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft); nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
if (Stream == null) if (m_stream == null)
return; // "Connection: Close" in effect. return; // "Connection: Close" in effect.
if (nextOffset == 0) if (nextOffset == 0)
@ -393,7 +392,7 @@ namespace OSHttpServer
Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset); Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
m_ReceiveBytesLeft -= offset; m_ReceiveBytesLeft -= offset;
if (Stream != null && Stream.CanRead) if (m_stream != null && m_stream.CanRead)
{ {
if (!StreamPassedOff) if (!StreamPassedOff)
Stream.BeginRead(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft, OnReceive, null); Stream.BeginRead(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft, OnReceive, null);
@ -449,10 +448,10 @@ namespace OSHttpServer
{ {
while(true) while(true)
{ {
if (_stream == null || !_stream.CanRead) if (m_stream == null || !m_stream.CanRead)
return; return;
int bytesRead = await _stream.ReadAsync(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft).ConfigureAwait(false); int bytesRead = await m_stream.ReadAsync(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft).ConfigureAwait(false);
if (bytesRead == 0) if (bytesRead == 0)
{ {
@ -465,7 +464,7 @@ namespace OSHttpServer
throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft); throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft); int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
if (Stream == null) if (m_stream == null)
return; // "Connection: Close" in effect. return; // "Connection: Close" in effect.
// try again to see if we can parse another message (check parser to see if it is looking for a new message) // try again to see if we can parse another message (check parser to see if it is looking for a new message)
@ -476,7 +475,7 @@ namespace OSHttpServer
{ {
nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft); nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
if (Stream == null) if (m_stream == null)
return; // "Connection: Close" in effect. return; // "Connection: Close" in effect.
if (nextOffset == 0) if (nextOffset == 0)
@ -536,13 +535,14 @@ namespace OSHttpServer
private void OnRequestCompleted(object source, EventArgs args) private void OnRequestCompleted(object source, EventArgs args)
{ {
TriggerKeepalive = false; TriggerKeepalive = false;
MonitorKeepaliveMS = 0; MonitorKeepaliveStartMS = 0;
FullRequestReceived = true; FullRequestReceived = true;
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
if (m_maxPipeRequests == 0) if (m_maxRequests == 0)
return; return;
if(--m_maxPipeRequests == 0) if(--m_maxRequests == 0)
m_currentRequest.Connection = ConnectionType.Close; m_currentRequest.Connection = ConnectionType.Close;
// load cookies if they exist // load cookies if they exist
@ -574,13 +574,9 @@ namespace OSHttpServer
} }
} }
public void ReqResponseAboutToSend(uint requestID)
{
isSendingResponse = true;
}
public void StartSendResponse(HttpResponse response) public void StartSendResponse(HttpResponse response)
{ {
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
isSendingResponse = true; isSendingResponse = true;
m_currentResponse = response; m_currentResponse = response;
ContextTimeoutManager.EnqueueSend(this, response.Priority); ContextTimeoutManager.EnqueueSend(this, response.Priority);
@ -595,19 +591,19 @@ namespace OSHttpServer
if(!CanSend()) if(!CanSend())
return false; return false;
m_currentResponse?.SendNextAsync(bytesLimit); m_currentResponse?.SendNextAsync(bytesLimit);
return false; return false;
} }
public void ContinueSendResponse() public void ContinueSendResponse(bool notThrottled)
{ {
if(m_currentResponse == null) if(m_currentResponse == null)
return; return;
ContextTimeoutManager.EnqueueSend(this, m_currentResponse.Priority); ContextTimeoutManager.EnqueueSend(this, m_currentResponse.Priority, notThrottled);
} }
public void ReqResponseSent(uint requestID, ConnectionType ctype) public void EndSendResponse(uint requestID, ConnectionType ctype)
{ {
isSendingResponse = false; isSendingResponse = false;
m_currentResponse?.Clear(); m_currentResponse?.Clear();
@ -626,6 +622,7 @@ namespace OSHttpServer
Disconnect(SocketError.Success); Disconnect(SocketError.Success);
else else
{ {
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
lock (requestsInServiceIDs) lock (requestsInServiceIDs)
{ {
if (requestsInServiceIDs.Count == 0) if (requestsInServiceIDs.Count == 0)
@ -645,22 +642,23 @@ namespace OSHttpServer
/// <exception cref="ArgumentException">If <paramref name="httpVersion"/> is invalid.</exception> /// <exception cref="ArgumentException">If <paramref name="httpVersion"/> is invalid.</exception>
public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType)
{ {
LastActivityTimeMS = ContextTimeoutManager.EnvironmentTickCount();
if (string.IsNullOrEmpty(contentType)) if (string.IsNullOrEmpty(contentType))
contentType = "text/html"; contentType = "text/html";
if (string.IsNullOrEmpty(reason)) if (string.IsNullOrEmpty(reason))
reason = statusCode.ToString(); reason = statusCode.ToString();
string response = string.IsNullOrEmpty(body) byte[] buffer;
? httpVersion + " " + (int)statusCode + " " + reason + "\r\n\r\n" if(string.IsNullOrEmpty(body))
: string.Format("{0} {1} {2}\r\nContent-Type: {5}\r\nContent-Length: {3}\r\n\r\n{4}", buffer = Encoding.ASCII.GetBytes(httpVersion + " " + (int)statusCode + " " + reason ?? statusCode.ToString() + "\r\n\r\n");
else
buffer = Encoding.UTF8.GetBytes(
string.Format("{0} {1} {2}\r\nContent-Type: {5}\r\nContent-Length: {3}\r\n\r\n{4}",
httpVersion, (int)statusCode, reason ?? statusCode.ToString(), httpVersion, (int)statusCode, reason ?? statusCode.ToString(),
body.Length, body, contentType); body.Length, body, contentType));
byte[] buffer = Encoding.ASCII.GetBytes(response);
Send(buffer); Send(buffer);
if (m_currentRequest.Connection == ConnectionType.Close)
FullRequestProcessed = true;
} }
/// <summary> /// <summary>
@ -671,6 +669,7 @@ namespace OSHttpServer
/// <param name="reason">reason for the status code.</param> /// <param name="reason">reason for the status code.</param>
public void Respond(string httpVersion, HttpStatusCode statusCode, string reason) public void Respond(string httpVersion, HttpStatusCode statusCode, string reason)
{ {
Respond(httpVersion, statusCode, reason, null, null); Respond(httpVersion, statusCode, reason, null, null);
} }
@ -699,7 +698,7 @@ namespace OSHttpServer
public bool Send(byte[] buffer, int offset, int size) public bool Send(byte[] buffer, int offset, int size)
{ {
if (Stream == null || m_sock == null || !m_sock.Connected) if (m_stream == null || m_sock == null || !m_sock.Connected)
return false; return false;
if (offset + size > buffer.Length) if (offset + size > buffer.Length)
@ -710,14 +709,14 @@ namespace OSHttpServer
{ {
try try
{ {
Stream.Write(buffer, offset, size); m_stream.Write(buffer, offset, size);
} }
catch catch
{ {
ok = false; ok = false;
} }
if (!ok && Stream != null) if (!ok && m_stream != null)
Disconnect(SocketError.NoRecovery); Disconnect(SocketError.NoRecovery);
return ok; return ok;
} }
@ -725,7 +724,7 @@ namespace OSHttpServer
public async Task<bool> SendAsync(byte[] buffer, int offset, int size) public async Task<bool> SendAsync(byte[] buffer, int offset, int size)
{ {
if (Stream == null || m_sock == null || !m_sock.Connected) if (m_stream == null || m_sock == null || !m_sock.Connected)
return false; return false;
if (offset + size > buffer.Length) if (offset + size > buffer.Length)
@ -735,7 +734,7 @@ namespace OSHttpServer
ContextTimeoutManager.ContextEnterActiveSend(); ContextTimeoutManager.ContextEnterActiveSend();
try try
{ {
await Stream.WriteAsync(buffer, offset, size).ConfigureAwait(false); await m_stream.WriteAsync(buffer, offset, size).ConfigureAwait(false);
} }
catch catch
{ {
@ -744,7 +743,7 @@ namespace OSHttpServer
ContextTimeoutManager.ContextLeaveActiveSend(); ContextTimeoutManager.ContextLeaveActiveSend();
if (!ok && Stream != null) if (!ok && m_stream != null)
Disconnect(SocketError.NoRecovery); Disconnect(SocketError.NoRecovery);
return ok; return ok;
} }
@ -770,7 +769,7 @@ namespace OSHttpServer
m_parser.BodyBytesReceived -= OnBodyBytesReceived; m_parser.BodyBytesReceived -= OnBodyBytesReceived;
m_parser.Clear(); m_parser.Clear();
return new HTTPNetworkContext() { Socket = m_sock, Stream = _stream as NetworkStream }; return new HTTPNetworkContext() { Socket = m_sock, Stream = m_stream as NetworkStream };
} }
public void Dispose() public void Dispose()

View File

@ -10,6 +10,8 @@ namespace OSHttpServer
{ {
public class HttpResponse : IHttpResponse public class HttpResponse : IHttpResponse
{ {
public event EventHandler<BandWitdhEventArgs> BandWitdhEvent;
private const string DefaultContentType = "text/html;charset=UTF-8"; private const string DefaultContentType = "text/html;charset=UTF-8";
private readonly IHttpClientContext m_context; private readonly IHttpClientContext m_context;
private readonly ResponseCookies m_cookies = new ResponseCookies(); private readonly ResponseCookies m_cookies = new ResponseCookies();
@ -245,9 +247,9 @@ namespace OSHttpServer
sb.Append("Server: OSWebServer\r\n"); sb.Append("Server: OSWebServer\r\n");
int keepaliveS = m_context.TimeoutKeepAlive / 1000; int keepaliveS = m_context.TimeoutKeepAlive / 1000;
if (Connection == ConnectionType.KeepAlive && keepaliveS > 0 && m_context.MaxPipeRequests > 0) if (Connection == ConnectionType.KeepAlive && keepaliveS > 0 && m_context.MaxRequests > 0)
{ {
sb.AppendFormat("Keep-Alive:timeout={0}, max={1}\r\n", keepaliveS, m_context.MaxPipeRequests); sb.AppendFormat("Keep-Alive:timeout={0}, max={1}\r\n", keepaliveS, m_context.MaxRequests);
sb.Append("Connection: Keep-Alive\r\n"); sb.Append("Connection: Keep-Alive\r\n");
} }
else else
@ -282,7 +284,7 @@ namespace OSHttpServer
if (Sent) if (Sent)
throw new InvalidOperationException("Everything have already been sent."); throw new InvalidOperationException("Everything have already been sent.");
if (m_context.MaxPipeRequests == 0 || m_keepAlive == 0) if (m_context.MaxRequests == 0 || m_keepAlive == 0)
{ {
Connection = ConnectionType.Close; Connection = ConnectionType.Close;
m_context.TimeoutKeepAlive = 0; m_context.TimeoutKeepAlive = 0;
@ -326,7 +328,12 @@ namespace OSHttpServer
{ {
if(!await m_context.SendAsync(m_headerBytes, 0, m_headerBytes.Length).ConfigureAwait(false)) if(!await m_context.SendAsync(m_headerBytes, 0, m_headerBytes.Length).ConfigureAwait(false))
{ {
if(m_body != null) if (m_context.CanSend())
{
m_context.ContinueSendResponse(true);
return;
}
if (m_body != null)
m_body.Dispose(); m_body.Dispose();
RawBuffer = null; RawBuffer = null;
Sent = true; Sent = true;
@ -336,7 +343,7 @@ namespace OSHttpServer
m_headerBytes = null; m_headerBytes = null;
if(bytesLimit <= 0) if(bytesLimit <= 0)
{ {
m_context.ContinueSendResponse(); m_context.ContinueSendResponse(true);
return; return;
} }
} }
@ -345,21 +352,34 @@ namespace OSHttpServer
{ {
if (RawBufferLen > 0) if (RawBufferLen > 0)
{ {
if(BandWitdhEvent!=null)
bytesLimit = CheckBandwidth(RawBufferLen, bytesLimit);
bool sendRes; bool sendRes;
if(RawBufferLen > bytesLimit) if(RawBufferLen > bytesLimit)
{ {
sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false); sendRes = (await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false));
RawBufferLen -= bytesLimit; if (sendRes)
RawBufferStart += bytesLimit; {
RawBufferLen -= bytesLimit;
RawBufferStart += bytesLimit;
}
} }
else else
{ {
sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false); sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false);
RawBufferLen = 0; if(sendRes)
RawBufferLen = 0;
} }
if (!sendRes) if (!sendRes)
{ {
if (m_context.CanSend())
{
m_context.ContinueSendResponse(true);
return;
}
RawBuffer = null; RawBuffer = null;
if(m_body != null) if(m_body != null)
Body.Dispose(); Body.Dispose();
@ -371,7 +391,7 @@ namespace OSHttpServer
RawBuffer = null; RawBuffer = null;
else else
{ {
m_context.ContinueSendResponse(); m_context.ContinueSendResponse(true);
return; return;
} }
} }
@ -391,17 +411,26 @@ namespace OSHttpServer
if (RawBufferLen > bytesLimit) if (RawBufferLen > bytesLimit)
{ {
sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false); sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false);
RawBufferLen -= bytesLimit; if (sendRes)
RawBufferStart += bytesLimit; {
RawBufferLen -= bytesLimit;
RawBufferStart += bytesLimit;
}
} }
else else
{ {
sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false); sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false);
RawBufferLen = 0; if (sendRes)
RawBufferLen = 0;
} }
if (!sendRes) if (!sendRes)
{ {
if (m_context.CanSend())
{
m_context.ContinueSendResponse(true);
return;
}
RawBuffer = null; RawBuffer = null;
Sent = true; Sent = true;
return; return;
@ -409,7 +438,7 @@ namespace OSHttpServer
} }
if (RawBufferLen > 0) if (RawBufferLen > 0)
{ {
m_context.ContinueSendResponse(); m_context.ContinueSendResponse(false);
return; return;
} }
} }
@ -417,7 +446,19 @@ namespace OSHttpServer
if (m_body != null) if (m_body != null)
m_body.Dispose(); m_body.Dispose();
Sent = true; Sent = true;
m_context.ReqResponseSent(requestID, Connection); m_context.EndSendResponse(requestID, Connection);
}
private int CheckBandwidth(int request, int bytesLimit)
{
if(request > bytesLimit)
request = bytesLimit;
var args = new BandWitdhEventArgs(request);
BandWitdhEvent?.Invoke(this, args);
if(args.Result > 8196)
return args.Result;
return 8196;
} }
public void Clear() public void Clear()

View File

@ -23,7 +23,7 @@ namespace OSHttpServer
int contextID {get;} int contextID {get;}
int TimeoutKeepAlive {get; set; } int TimeoutKeepAlive {get; set; }
int MaxPipeRequests{get; set; } int MaxRequests{get; set; }
bool CanSend(); bool CanSend();
bool IsSending(); bool IsSending();
@ -92,11 +92,11 @@ namespace OSHttpServer
HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing(); HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing();
void StartSendResponse(HttpResponse response); void StartSendResponse(HttpResponse response);
void ContinueSendResponse(); void ContinueSendResponse(bool notThrottled);
void ReqResponseAboutToSend(uint requestID); void EndSendResponse(uint requestID, ConnectionType connection);
void ReqResponseSent(uint requestID, ConnectionType connection);
bool TrySendResponse(int limit); bool TrySendResponse(int limit);
} }
public class HTTPNetworkContext public class HTTPNetworkContext
{ {
public NetworkStream Stream; public NetworkStream Stream;
@ -143,4 +143,6 @@ namespace OSHttpServer
} }
} }
} }

View File

@ -154,12 +154,5 @@ namespace OSHttpServer
/// </summary> /// </summary>
/// <param name="cookies">The cookies.</param> /// <param name="cookies">The cookies.</param>
void SetCookies(RequestCookies cookies); void SetCookies(RequestCookies cookies);
/// <summary>
/// Create a response object.
/// </summary>
/// <param name="context">Context for the connected client.</param>
/// <returns>A new <see cref="IHttpResponse"/>.</returns>
//IHttpResponse CreateResponse(IHttpClientContext context);
} }
} }

View File

@ -26,11 +26,13 @@ namespace OSHttpServer
/// </example> /// </example>
public interface IHttpResponse public interface IHttpResponse
{ {
event EventHandler<BandWitdhEventArgs> BandWitdhEvent;
/// <summary> /// <summary>
/// The body stream is used to cache the body contents /// The body stream is used to cache the body contents
/// before sending everything to the client. It's the simplest /// before sending everything to the client. It's the simplest
/// way to serve documents. /// way to serve documents.
/// </summary> /// </summary>
///
Stream Body { get; } Stream Body { get; }
byte[] RawBuffer { get; set; } byte[] RawBuffer { get; set; }
int RawBufferStart { get; set; } int RawBufferStart { get; set; }
@ -142,4 +144,19 @@ namespace OSHttpServer
/// </summary> /// </summary>
KeepAlive KeepAlive
} }
public class BandWitdhEventArgs : EventArgs
{
/// <summary>
/// Gets received request.
/// </summary>
public int Result;
public int Request;
public BandWitdhEventArgs(int request)
{
Request = request;
Result = request;
}
}
} }