diff --git a/HTTPServer.cs b/HTTPServer.cs index aef50b4..3bba2b7 100644 --- a/HTTPServer.cs +++ b/HTTPServer.cs @@ -6,6 +6,7 @@ using System.Threading; using ln.http.threads; using System.IO; using System.Text; +using ln.http.resources.session; namespace ln.http @@ -25,8 +26,13 @@ namespace ln.http Pool threadPool = new Pool(); + public SessionCache SessionCache { get; set; } + + + public HTTPServer() { + SessionCache = new SessionCache(); } public void AddEndpoint(IPEndPoint endpoint) @@ -99,6 +105,8 @@ namespace ln.http HttpRequest httpRequest = new HttpRequest(httpReader, (IPEndPoint)tcpClient.Client.LocalEndPoint); HttpResponse response = null; + httpRequest.ApplySession(SessionCache); + try { HttpApplication application = DefaultApplication; @@ -123,6 +131,14 @@ namespace ln.http response.SetHeader("content-length", cstream.Length.ToString()); } + if (SessionCache != null) + { + SessionCache.ApplySessionID(response,httpRequest.Session); + } + + response.AddCookie("LN_SEEN", DateTime.Now.ToString()); + + StreamWriter streamWriter = new StreamWriter(tcpClient.GetStream()); streamWriter.WriteLine("{0} {1} {2}", httpRequest.Protocol,response.StatusCode, response.StatusMessage); @@ -131,6 +147,12 @@ namespace ln.http streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName)); } + foreach (HttpCookie httpCookie in response.Cookies) + { + streamWriter.WriteLine("Set-Cookie: {0}",httpCookie.ToString()); + } + + streamWriter.WriteLine(); streamWriter.Flush(); diff --git a/HttpCookie.cs b/HttpCookie.cs new file mode 100644 index 0000000..f836f94 --- /dev/null +++ b/HttpCookie.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using System.Text; +namespace ln.http +{ + public class HttpCookie + { + public String Name { get; set; } + public String Value { get; set; } + + public bool Secure { get; set; } + public DateTime Expires { get; set; } + public String Path { get; set; } + public String Domain { get; set; } + + public HttpCookie(String name) + { + Name = name; + } + public HttpCookie(String name,String value) + { + Name = name; + Value = value; + } + + public override string ToString() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendFormat("{0}={1}", Name, Value); + if (Secure) + stringBuilder.Append(";SECURE"); + + if (Path != null) + stringBuilder.AppendFormat(";Path={0}", Path); + + return stringBuilder.ToString(); + } + + + } +} diff --git a/HttpRequest.cs b/HttpRequest.cs index 713e58a..960b745 100644 --- a/HttpRequest.cs +++ b/HttpRequest.cs @@ -6,12 +6,14 @@ using System.Linq; using System.Net; using System.Net.Sockets; using ln.http.exceptions; +using ln.http.resources.session; namespace ln.http { public class HttpRequest { Dictionary requestHeaders; + Dictionary requestCookies; public IPEndPoint RemoteEndpoint { get; private set; } public IPEndPoint LocalEndpoint { get; private set; } @@ -28,6 +30,8 @@ namespace ln.http public QueryStringParameters Query { get; private set; } + public Session Session { get; private set; } + public HttpRequest(HttpReader httpReader,IPEndPoint localEndpoint) { LocalEndpoint = localEndpoint; @@ -37,6 +41,7 @@ namespace ln.http RequestURL = httpReader.URL; requestHeaders = new Dictionary(httpReader.Headers); + requestCookies = new Dictionary(); Setup(); } @@ -44,6 +49,7 @@ namespace ln.http private void Setup() { SetupResourceURI(); + SetupCookies(); } /* @@ -70,6 +76,35 @@ namespace ln.http Query = new QueryStringParameters(URI.Query); } + private void SetupCookies() + { + string cookies = GetRequestHeader("COOKIE"); + foreach (String cookie in cookies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + string[] c = cookie.Split(new char[] { '=' }, 2); + string cn, cv; + cn = c[0].Trim(); + if (c.Length > 1) + cv = c[1].Trim(); + else + cv = ""; + + if (!this.requestCookies.ContainsKey(cn)) + this.requestCookies.Add(cn, cv); + } + } + + public void ApplySession(SessionCache sessionCache) + { + Session session = sessionCache.ApplySession(this); + Session = session; + if (Session == null) + { + requestHeaders.Add("X-LNH-Session-Failed", "true"); + Session = new Session(); + } + } + public override string ToString() { @@ -93,6 +128,20 @@ namespace ln.http public String[] RequestHeaderNames => requestHeaders.Keys.ToArray(); + public String[] CookieNames => requestCookies.Keys.ToArray(); + public bool ContainsCookie(String name) + { + return this.requestCookies.ContainsKey(name); + } + public String GetCookie(String name) + { + return requestCookies[name]; + } + + + + + public HttpResponse Redirect(string location,int status = 301) { HttpResponse httpResponse = new HttpResponse(this); diff --git a/HttpResponse.cs b/HttpResponse.cs index b4a3c42..b0f2c6b 100644 --- a/HttpResponse.cs +++ b/HttpResponse.cs @@ -16,6 +16,7 @@ namespace ln.http int statusCode; string statusMessage; Dictionary> headers = new Dictionary>(); + List cookies = new List(); public HttpResponse(HttpRequest httpRequest) { @@ -23,7 +24,7 @@ namespace ln.http ContentStream = new MemoryStream(); ContentWriter = new StreamWriter(ContentStream); - StatusCode = 200; + StatusCode = 200; SetHeader("content-type", "text/html"); } public HttpResponse(HttpRequest httpRequest,string contentType) @@ -84,6 +85,22 @@ namespace ln.http } + public void AddCookie(string name,string value) + { + AddCookie(new HttpCookie(name, value)); + } + public void AddCookie(HttpCookie httpCookie) + { + cookies.Add(httpCookie); + } + public void RemoveCookie(HttpCookie httpCookie) + { + cookies.Remove(httpCookie); + } + public HttpCookie[] Cookies => cookies.ToArray(); + + + public int StatusCode { get diff --git a/ln.http.csproj b/ln.http.csproj index 93067a3..4de2c3a 100644 --- a/ln.http.csproj +++ b/ln.http.csproj @@ -42,10 +42,14 @@ + + + + \ No newline at end of file diff --git a/session/Session.cs b/session/Session.cs new file mode 100644 index 0000000..0accd0b --- /dev/null +++ b/session/Session.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ln.http.resources.session +{ + public class Session + { + public Guid SessionID { get; private set; } + public long SessionAge => DateTimeOffset.Now.ToUnixTimeSeconds() - LastTouch; + public long LastTouch { get; private set; } + + private readonly Dictionary elements = new Dictionary(); + + public Session() + { + SessionID = Guid.NewGuid(); + } + + public object this[string name] + { + get => this.elements[name]; + set => this.elements[name] = value; + } + + public String[] ElementNames => this.elements.Keys.ToArray(); + + public void Touch() + { + LastTouch = DateTimeOffset.Now.ToUnixTimeSeconds(); + } + } +} diff --git a/session/SessionCache.cs b/session/SessionCache.cs new file mode 100644 index 0000000..e125256 --- /dev/null +++ b/session/SessionCache.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +namespace ln.http.resources.session +{ + public class SessionCache + { + Dictionary sessions = new Dictionary(); + + public SessionCache() + { + } + + public bool Contains(Guid sessionID) + { + return this.sessions.ContainsKey(sessionID); + } + + /* + * Create a new Session instance + * + */ + public virtual Session CreateSession() + { + return new Session(); + } + + /* + * Find and return SessionID from given HttpRequest + * + */ + public virtual Guid FindSessionID(HttpRequest httpRequest) + { + if (httpRequest.ContainsCookie("SID_LN")) + { + return Guid.Parse(httpRequest.GetCookie("SID_LN")); + } + return Guid.Empty; + } + + /* + * Apply SessionID of the given Session to the given Response + * + */ + public virtual void ApplySessionID(HttpResponse httpResponse,Session session) + { + HttpCookie sessionCookie = new HttpCookie("SID_LN",session.SessionID.ToString()); + sessionCookie.Path = "/"; + httpResponse.AddCookie(sessionCookie); + } + + public Session ApplySession(HttpRequest httpRequest) + { + Guid sessionID = FindSessionID(httpRequest); + if (!Guid.Empty.Equals(sessionID) && Contains(sessionID)) + { + Session session = this.sessions[sessionID]; + session.Touch(); + return session; + } + else + { + Session session = CreateSession(); + this.sessions.Add(session.SessionID, session); + return session; + } + } + + + + + } +}