diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs index abd3a1d4e3..9dcc13df41 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs @@ -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; } diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs index 6059afcb2a..4e0bee5933 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs @@ -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 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 /// If fails /// Stream must be writable and readable. 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 /// 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) diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs index 447beb532f..bba7640ee2 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs @@ -16,7 +16,6 @@ namespace OSHttpServer public class HttpContextFactory : IHttpContextFactory { private readonly ConcurrentDictionary m_activeContexts = new ConcurrentDictionary(); - private readonly IRequestParserFactory m_parserfactory; private readonly ILogWriter m_logWriter; /// @@ -30,10 +29,9 @@ namespace OSHttpServer /// The writer. /// Amount of bytes to read from the incoming socket stream. /// Used to create a request parser. - public HttpContextFactory(ILogWriter writer, IRequestParserFactory factory) + public HttpContextFactory(ILogWriter writer) { m_logWriter = writer; - m_parserfactory = factory; ContextTimeoutManager.Start(); } @@ -46,33 +44,16 @@ namespace OSHttpServer /// A context. 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; } - /// - /// Create a new context. - /// - /// true if HTTPS is used. - /// Remote client - /// Network stream, - /// A new context (always). - 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); diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs index 77431bae23..d53e89b6f7 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs @@ -34,11 +34,11 @@ namespace OSHttpServer /// Factory used to create es. /// address is null. /// Port must be a positive number. - 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 /// TCP Port to listen on, default HTTPS port is 443 /// Factory used to create es. /// Certificate to use - /// which HTTPS protocol to use, default is TLS. - 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; } /// @@ -64,31 +62,28 @@ namespace OSHttpServer /// TCP Port to listen on, default HTTPS port is 443 /// Factory used to create es. /// Certificate to use - protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory, X509Certificate certificate) - : this(address, port, factory) + /// which HTTPS protocol to use, default is TLS. + 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; } diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs index ec6d98fee3..76ad1979c6 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs @@ -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) diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs index 60e7527dc3..a11fad2c47 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs @@ -29,6 +29,7 @@ namespace OSHttpServer bool CanSend(); bool IsSending(); + bool IsClosing {get ;} /// /// 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); } diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestParserFactory.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestParserFactory.cs deleted file mode 100644 index 03762efafb..0000000000 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestParserFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using OSHttpServer.Parser; - -namespace OSHttpServer -{ - /// - /// Creates request parsers when needed. - /// - public class RequestParserFactory : IRequestParserFactory - { - /// - /// Create a new request parser. - /// - /// Used when logging should be enabled. - /// A new request parser. - public IHttpRequestParser CreateParser(ILogWriter logWriter) - { - return new HttpRequestParser(logWriter); - } - } - - /// - /// Creates request parsers when needed. - /// - public interface IRequestParserFactory - { - /// - /// Create a new request parser. - /// - /// Used when logging should be enabled. - /// A new request parser. - IHttpRequestParser CreateParser(ILogWriter logWriter); - } -}