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
	
	 UbitUmarov
						UbitUmarov