ln.http/ln.http/Http1XConnection.cs

105 lines
4.3 KiB
C#

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);
}
}