Implemented simple Session tracking
parent
ce948567fc
commit
089ce02f0b
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||||
using ln.http.threads;
|
using ln.http.threads;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using ln.http.resources.session;
|
||||||
|
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
|
@ -25,8 +26,13 @@ namespace ln.http
|
||||||
|
|
||||||
Pool threadPool = new Pool();
|
Pool threadPool = new Pool();
|
||||||
|
|
||||||
|
public SessionCache SessionCache { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public HTTPServer()
|
public HTTPServer()
|
||||||
{
|
{
|
||||||
|
SessionCache = new SessionCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEndpoint(IPEndPoint endpoint)
|
public void AddEndpoint(IPEndPoint endpoint)
|
||||||
|
@ -99,6 +105,8 @@ namespace ln.http
|
||||||
HttpRequest httpRequest = new HttpRequest(httpReader, (IPEndPoint)tcpClient.Client.LocalEndPoint);
|
HttpRequest httpRequest = new HttpRequest(httpReader, (IPEndPoint)tcpClient.Client.LocalEndPoint);
|
||||||
HttpResponse response = null;
|
HttpResponse response = null;
|
||||||
|
|
||||||
|
httpRequest.ApplySession(SessionCache);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpApplication application = DefaultApplication;
|
HttpApplication application = DefaultApplication;
|
||||||
|
@ -123,6 +131,14 @@ namespace ln.http
|
||||||
response.SetHeader("content-length", cstream.Length.ToString());
|
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 streamWriter = new StreamWriter(tcpClient.GetStream());
|
||||||
|
|
||||||
streamWriter.WriteLine("{0} {1} {2}", httpRequest.Protocol,response.StatusCode, response.StatusMessage);
|
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));
|
streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (HttpCookie httpCookie in response.Cookies)
|
||||||
|
{
|
||||||
|
streamWriter.WriteLine("Set-Cookie: {0}",httpCookie.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
streamWriter.WriteLine();
|
streamWriter.WriteLine();
|
||||||
streamWriter.Flush();
|
streamWriter.Flush();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,12 +6,14 @@ using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
|
using ln.http.resources.session;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
public class HttpRequest
|
public class HttpRequest
|
||||||
{
|
{
|
||||||
Dictionary<String, String> requestHeaders;
|
Dictionary<String, String> requestHeaders;
|
||||||
|
Dictionary<String, String> requestCookies;
|
||||||
|
|
||||||
public IPEndPoint RemoteEndpoint { get; private set; }
|
public IPEndPoint RemoteEndpoint { get; private set; }
|
||||||
public IPEndPoint LocalEndpoint { get; private set; }
|
public IPEndPoint LocalEndpoint { get; private set; }
|
||||||
|
@ -28,6 +30,8 @@ namespace ln.http
|
||||||
|
|
||||||
public QueryStringParameters Query { get; private set; }
|
public QueryStringParameters Query { get; private set; }
|
||||||
|
|
||||||
|
public Session Session { get; private set; }
|
||||||
|
|
||||||
public HttpRequest(HttpReader httpReader,IPEndPoint localEndpoint)
|
public HttpRequest(HttpReader httpReader,IPEndPoint localEndpoint)
|
||||||
{
|
{
|
||||||
LocalEndpoint = localEndpoint;
|
LocalEndpoint = localEndpoint;
|
||||||
|
@ -37,6 +41,7 @@ namespace ln.http
|
||||||
RequestURL = httpReader.URL;
|
RequestURL = httpReader.URL;
|
||||||
|
|
||||||
requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
||||||
|
requestCookies = new Dictionary<string, string>();
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
}
|
}
|
||||||
|
@ -44,6 +49,7 @@ namespace ln.http
|
||||||
private void Setup()
|
private void Setup()
|
||||||
{
|
{
|
||||||
SetupResourceURI();
|
SetupResourceURI();
|
||||||
|
SetupCookies();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,6 +76,35 @@ namespace ln.http
|
||||||
Query = new QueryStringParameters(URI.Query);
|
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()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
@ -93,6 +128,20 @@ namespace ln.http
|
||||||
|
|
||||||
public String[] RequestHeaderNames => requestHeaders.Keys.ToArray();
|
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)
|
public HttpResponse Redirect(string location,int status = 301)
|
||||||
{
|
{
|
||||||
HttpResponse httpResponse = new HttpResponse(this);
|
HttpResponse httpResponse = new HttpResponse(this);
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace ln.http
|
||||||
int statusCode;
|
int statusCode;
|
||||||
string statusMessage;
|
string statusMessage;
|
||||||
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
||||||
|
List<HttpCookie> cookies = new List<HttpCookie>();
|
||||||
|
|
||||||
public HttpResponse(HttpRequest httpRequest)
|
public HttpResponse(HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ namespace ln.http
|
||||||
ContentStream = new MemoryStream();
|
ContentStream = new MemoryStream();
|
||||||
ContentWriter = new StreamWriter(ContentStream);
|
ContentWriter = new StreamWriter(ContentStream);
|
||||||
|
|
||||||
StatusCode = 200;
|
StatusCode = 200;
|
||||||
SetHeader("content-type", "text/html");
|
SetHeader("content-type", "text/html");
|
||||||
}
|
}
|
||||||
public HttpResponse(HttpRequest httpRequest,string contentType)
|
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
|
public int StatusCode
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
|
@ -42,10 +42,14 @@
|
||||||
<Compile Include="HttpStatusCodes.cs" />
|
<Compile Include="HttpStatusCodes.cs" />
|
||||||
<Compile Include="QueryStringParameters.cs" />
|
<Compile Include="QueryStringParameters.cs" />
|
||||||
<Compile Include="HttpApplication.cs" />
|
<Compile Include="HttpApplication.cs" />
|
||||||
|
<Compile Include="HttpCookie.cs" />
|
||||||
|
<Compile Include="session\Session.cs" />
|
||||||
|
<Compile Include="session\SessionCache.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="exceptions\" />
|
<Folder Include="exceptions\" />
|
||||||
<Folder Include="threads\" />
|
<Folder Include="threads\" />
|
||||||
|
<Folder Include="session\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue