using System; using System.IO; using System.Net; using System.Text; using ln.http.content; namespace ln.http; public class Http1XConnection : HttpConnection { bool _keepAlive; public Http1XConnection(Listener listener, IPEndPoint remoteEndpoint, Stream connectionStream, string method, string requestUriLine, HttpVersion httpVersion) : base(listener, remoteEndpoint, connectionStream, method, requestUriLine, httpVersion) { if (httpVersion == HttpVersion.HTTP11) _keepAlive = true; } public override void Run() { string _method = Method; HttpVersion _httpVersion = HttpVersion; HeaderContainer headerContainer = new HeaderContainer(); string _requestUriLine = RequestUriLine; while (ConnectionStream.CanRead && ConnectionStream.CanWrite) { headerContainer.Clear(); headerContainer.Read(ConnectionStream); Uri BaseURI = new Uri($"{(Listener is TlsListener ? "https" : "http")}://{headerContainer.Get("host")}"); Uri RequestUri = new Uri(BaseURI, _requestUriLine); if ( headerContainer.TryGetHeader("connection", out Header connectionHeader) ) { switch (connectionHeader.Value) { case "close": _keepAlive = false; break; case "keep-alive": _keepAlive = true; break; } } HttpContentStream contentStream = null; if (headerContainer.TryGetInteger("content-length", out int contentLength)) { if (headerContainer.TryGetValue("Expect", out string expectValue) && expectValue.Equals("100-continue")) { string statusLine = $"{HttpVersionSupport.ToString(_httpVersion)} 417 expectation failed\r\n"; byte[] statusLineBytes = Encoding.ASCII.GetBytes(statusLine); ConnectionStream.Write(statusLineBytes); break; } contentStream = new HttpContentStream(ConnectionStream, contentLength); } HttpRequestContext requestContext = new HttpRequestContext(Listener, this, ConnectionStream, new HttpRequest(_method, RequestUri, _httpVersion, false, headerContainer, contentStream)); Listener.Dispatch(requestContext); if (!ConnectionStream.CanWrite && !ConnectionStream.CanRead) break; SendResponse(requestContext); ConnectionStream.Flush(); string responseConnectionHeader = requestContext.Response.GetHeader("connection"); _keepAlive = responseConnectionHeader switch { "close" => false, "keep-alive" => true, _ => _keepAlive }; requestContext.Dispose(); if (!_keepAlive) break; if (!HttpConnection.ReadRequestLine(ConnectionStream, out _method, out _requestUriLine, out _httpVersion)) break; } } public override void SendResponse(HttpRequestContext requestContext) { foreach (var httpCookie in requestContext.Response.Cookies) requestContext.Response.Headers.Add("Set-Cookie", httpCookie.ToString()); string statusLine = $"{HttpVersionSupport.ToString(requestContext.Request.HttpVersion)} {(int)requestContext.Response.HttpStatusCode} {requestContext.Response.HttpStatusCode.ToString()}\r\n"; byte[] statusLineBytes = Encoding.ASCII.GetBytes(statusLine); requestContext.ConnectionStream.Write(statusLineBytes, 0, statusLineBytes.Length); if (!requestContext.Response.Headers.Contains("content-type")) requestContext.Response.Headers.Set("content-type", requestContext.Response.HttpContent?.ContentType); requestContext.Response.Headers.Set("content-length", requestContext.Response.HttpContent?.Length.ToString() ?? "0"); requestContext.Response.Headers.CopyTo(requestContext.ConnectionStream); requestContext.Response.HttpContent?.CopyTo(requestContext.ConnectionStream); } }