Implemented simple Session tracking

master
Harald Wolff 2019-02-26 22:00:37 +01:00
parent ce948567fc
commit 089ce02f0b
7 changed files with 239 additions and 1 deletions

View File

@ -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();

41
HttpCookie.cs 100644
View File

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

View File

@ -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<String, String> requestHeaders;
Dictionary<String, String> 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<string, string>(httpReader.Headers);
requestCookies = new Dictionary<string, string>();
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);

View File

@ -16,6 +16,7 @@ namespace ln.http
int statusCode;
string statusMessage;
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
List<HttpCookie> cookies = new List<HttpCookie>();
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

View File

@ -42,10 +42,14 @@
<Compile Include="HttpStatusCodes.cs" />
<Compile Include="QueryStringParameters.cs" />
<Compile Include="HttpApplication.cs" />
<Compile Include="HttpCookie.cs" />
<Compile Include="session\Session.cs" />
<Compile Include="session\SessionCache.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="exceptions\" />
<Folder Include="threads\" />
<Folder Include="session\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

33
session/Session.cs 100644
View File

@ -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<string, object> elements = new Dictionary<string, object>();
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();
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace ln.http.resources.session
{
public class SessionCache
{
Dictionary<Guid, Session> sessions = new Dictionary<Guid, Session>();
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;
}
}
}
}