My architect is wondering why we need to persist each user's session to the DB instead of just using cookies. I have never seen anyone use just cookies.
Usually I hold the Session Id in a cookie then use it to perform CRUDS on the session record keyed off the ID.
I mean I don't see how you can do session state without in proc. I've always done session state via a custom table holding routine fields like IsLoggedIn, IsClosed, IsNew, IPAddress, Browser, and so on.
Has anyone done session state on an e-commerce site without persisting it in a DB?
UPDATED
So here is kinda how we did things at another place I worked for an e-commerce site that got 500+ page hits a month:
public USession CreateNewSession(int userID)
{
string ipAddress = GetCurrentRequestIPAddress(context.Request);
USession newSession = USession.NewSession();
newSession.IpAddress = ipAddress;
newSession.UserID = customerID;
newSession.Browser = context.Request.UserAgent ?? string.Empty;
if (context.Request.UrlReferrer != null)
newSession.Referer = context.Request.UrlReferrer.ToString();
else
newSession.Referer = string.Empty;
InsertSession(newSession);
return newSession;
}
public USession CreateNewSession(int userID)
{
string ipAddress = GetCurrentRequestIPAddress(context.Request);
USession newSession = USession.NewSession();
newSession.IpAddress = ipAddress;
newSession.UserID = customerID;
newSession.Browser = context.Request.UserAgent ?? string.Empty;
if (context.Request.UrlReferrer != null)
newSession.Referer = context.Request.UrlReferrer.ToString();
else
newSession.Referer = string.Empty;
InsertSession(newSession);
return newSession;
}
public USession GetSession()
{
// existing sessionId this session?
HttpCookie cookie = context.Request.Cookies["usessionId"];
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
session = CreateNewSession(0);
else
{
string sessionID = cookie.Value;
session = GetSession(sessionID);
if (session == null)
session = CreateNewSession(0);
else if (session.IsClosed > 0)
session = CreateNewSession(session.UserID);
}
if (session.LastAccessed < DateTime.Now.AddHours(-1)) session.LoggedIn = false;
if (session.LastDestination.Equals("lesson"))
session.LastDestPageDestinationID = ContextValue(context, "lessonid");
else
session.LastDestPageDestinationID = 0;
if (session.IsNew) session.FirstDestination = session.LastDestination;
SaveSession();
return session;
}
private void SaveSession()
{
session.LastAccess = DateTime.Now;
session.LastDest = string.Empty;
db.UpdateSession(session);
if (!cookieIsSet)
{
// add a session cookie for this current session
HttpCookie cookie = CreateSessionCookie("usessionId", session.SessionID, 365);
if (session.LastDest.Equals("logout", StringComparison.OrdinalIgnoreCase))
cookie.Value = string.Empty;
if (session.LastDest.Equals("lessonOrder")) return;
context.Response.SetCookie(cookie);
}
}
internal void UpdateSession(USession s)
{
using (ourConnection conn = CreateConnection("UpdateSession"))
{
conn.CommandText = #"update csession set
closed = #closed,
userID = #customerID,
lastAccess = #lastAccess,
lastDestination = #lastDest,
orderId = #OrderId,
IsloggedIn = #isLoggedIn;
conn.AddParam("#id", s.Id);
conn.AddParam("#closed", s.Closed);
conn.AddParam("#userID", s.UserID);
conn.AddParam("#lastAccess", s.LastAccess);
conn.AddParam("#firstDestination", s.FirstDestination);
conn.AddParam("#lastDestination", s.LastDestination);
conn.AddParam("#isLoggedIn", s.IsLoggedIn);
conn.AddParam("#orderID", s.OrderID);
try
{
conn.ExecuteNonQuery();
}
catch (Exception ex)
{
LogException(ex);
}
}
}
public HttpCookie CreateSessionCookie(string cookieValue, string uSessionID, double daysTillExpiration)
{
HttpCookie cookie = new HttpCookie("usessionid", uSessionID);
cookie.Expires = DateTime.Now.AddDays(daysTillExpiration);
cookie.Path = "/";
return cookie;
}
So we'd work with the USession custom object in memory throughout our code for checking for loggedIn, to force close their session, and all sorts of stuff based on their current session.
Also in our Application_Error in global.asax we would log the current sessionId for tracking purposes on error.
HttpCookie cookie = Request.Cookies["usessionid"];
if (cookie != null)
{
logger.Variables = "<b>uSessionID:</b> " + cookie.Value;
if (cookie.Value.Length > 0)
logger.USessionID = GetUSessionID(cookie.Value);
}
The ASP.NET session has 3 possible states:
Off (my preferred) - don't use any session at all
InProc - the session is stored in the memory of the web server. Fast, but not convinient if you are running in a web farm because each node of the web farm will have a different copy of the session and you might get conflicts.
Out-of-Proc - the session is stored in memory of a specially dedicated server running the ASP.NET Session state Windows service. Good for webfarms scenarios but not reliable enough as the session is still persisted in the memory of some server which might not survive crashes.
SQL Server - the session is persisted in SQL server. Very reliable and suitable for web farms. Problem is with performance. It will be slower than the previous modes as the session is now persisted in the database.
The 3 modes are covered in depth in the following article.
The mode you choose is configured in web.config and it is completely transparent to your code in which you simply use the Session["someKey"] to work with the session, it's just the underlying storage mechanism that differs.
Related
I have an Asp.Net MVC 5 web application which has an error handling mechanism for log error in DB and showing the errorId to the user who raised that error in a separate form which is called ErrorPage. When an error occurs in the web app I store that errorId in session and I read it from the session in ErrorPage to showing that errorId to the user who faced this error in order to be able for backup operations. The web server of this web application is processing requests with only one worker process currently, so all generated sessions are valid and accessible in the whole of the web app.
I am going to increase the number of worker processes from 1 to 4 for this web app but I have some problem with my web app. Also in IIS, I set the session state mode to In Process mode because of in the web app I used session in many cases and I can't set it to SQL Server mode because of it will increase performance overhead.
The problem is where a request goes in worker process A (for example) and a session will generate for this request in the worker process A and suppose this request encounters an error in web application, I will redirect the user to the ErrorPage and it is possible this new request (redirecting user to ErrorPage's action in ErrorController) goes in another worker process B (for example). But in the worker process B, I can't access that session which is generated for the first request because of that sessions is defined at the worker process level and they are valid only in that worker process.
So after a lot of search for this, I decided to save session info in DB instead of Ram and load it from DB when I need that info. But I have no idea about saving this info in DB with which key ID?
Imagine this scenario to find out my real problem easier:
let's have:
WorkerProcessId1 = W1;
WorkerProcessId2 = W2;
SessionId1 = S1;
SessionId2 = S2;
RequestId1 = R1;
RequestId2 = R2;
and the scenario:
R1 comes to web server
==> web server passes R1 to W1
==> W1 generates S1 for R1
==> R1 faces an error
==> for the user who sends R1 (it is possible the user has not logged in yet so I don't know the userId), I will save the error in DB using the combination of S1 and userId in a specific pattern as a unique identifier in Error table in DB
==> the user will redirect to ErrorPage with another request R2
==> web server passes R2 to W2
==> W2 generates S2 for R2
==> after the redirect is done, in the ErrorPage I need the errorId of that error which I save it to DB, for showing it to the user for backup operations
==> I don't know which error belongs to this user and which error should be load from DB????
If this is not possible to do that, is there any way to have a shared identifier across all worker processes of the web server?
Edit:
In this edit, I will explain where and how I used from the session in my ErrorHandling mechanism. At the end of the target line there is a commented phrase where it is written "Here I am using session":
namespace MyDomain.UI.Infrastructure.Attributes
{
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public CustomHandleErrorAttribute()
{
}
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
{
return;
}
var errorid = 0;
try
{
errorid = SaveErrorToDatabase(filterContext);
}
catch (Exception e)
{
//Console.WriteLine(e);
//throw;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = "Error Message....",
errorid,
}
};
}
else
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Controller.TempData.Clear();
filterContext.Controller.TempData.Add("ErrorCode", errorid);//Here I am using session
filterContext.Result = new ViewResult
{
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
}
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
private int SaveErrorToDatabase(ExceptionContext exception)
{
MyDomainDBContext dbContext = new MyDomainDBContext();
var browserType = exception.HttpContext.Request.Browser.Capabilities["type"];
var error = new Error
{
ErrorURL = exception.HttpContext.Request.Url.ToString(),
ExceptionType = exception.Exception.GetType().Name,
IsGlobalError = false,
Message = exception.Exception.Message,
StackTrace = exception.Exception.StackTrace,
ThrownTime = DateTime.Now,
UserIP = IPAddress.Parse(exception.HttpContext.Request.UserHostAddress).ToString(),
BrowserName = browserType.ToString() + "," +
GetUserPlatform(exception.HttpContext.Request)
};
AddRequestDetails(exception.Exception, exception.HttpContext.Request, error);
if (exception.Exception.InnerException != null)
{
error.Message += "\n Inner Excpetion : \n " + exception.Exception.InnerException.Message;
if (exception.Exception.InnerException.InnerException != null)
{
error.Message += "\n \t Inner Excpetion : \n " + exception.Exception.InnerException.InnerException.Message;
}
}
if (exception.HttpContext.User.Identity.IsAuthenticated)
{
error.UserID = exception.HttpContext.User.Identity.GetUserId<int>();
}
dbContext.Errors.Add(error);
dbContext.SaveChanges();
return error.ErrorID;
}
private void AddRequestDetails(Exception exception, HttpRequestBase request, Error err)
{
if (exception.GetType().Name == "HttpAntiForgeryException" && exception.Message == "The anti-forgery cookie token and form field token do not match.")
{
if (request.Form != null)
{
if (request.Cookies["__RequestVerificationToken"] != null)
{
err.RequestDetails = "Form : " + request.Form["__RequestVerificationToken"] +
" \n Cookie : " + request.Cookies["__RequestVerificationToken"].Value;
}
else
{
err.RequestDetails = "Does not have cookie for forgery";
}
}
}
}
private String GetUserPlatform(HttpRequestBase request)
{
var ua = request.UserAgent;
if (ua.Contains("Android"))
return $"Android";
if (ua.Contains("iPad"))
return $"iPad OS";
if (ua.Contains("iPhone"))
return $"iPhone OS";
if (ua.Contains("Linux") && ua.Contains("KFAPWI"))
return "Kindle Fire";
if (ua.Contains("RIM Tablet") || (ua.Contains("BB") && ua.Contains("Mobile")))
return "Black Berry";
if (ua.Contains("Windows Phone"))
return $"Windows Phone";
if (ua.Contains("Mac OS"))
return "Mac OS";
if (ua.Contains("Windows NT 5.1") || ua.Contains("Windows NT 5.2"))
return "Windows XP";
if (ua.Contains("Windows NT 6.0"))
return "Windows Vista";
if (ua.Contains("Windows NT 6.1"))
return "Windows 7";
if (ua.Contains("Windows NT 6.2"))
return "Windows 8";
if (ua.Contains("Windows NT 6.3"))
return "Windows 8.1";
if (ua.Contains("Windows NT 10"))
return "Windows 10";
//fallback to basic platform:
return request.Browser.Platform + (ua.Contains("Mobile") ? " Mobile " : "");
}
}
public class IgnoreErrorPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (new[]{
"InputStream",
"Filter",
"Length",
"Position",
"ReadTimeout",
"WriteTimeout",
"LastActivityDate",
"LastUpdatedDate",
"Session"
}.Contains(property.PropertyName))
{
property.Ignored = true;
}
return property;
}
}
}
As you can see I filled TempData which will be stored in the session for passing errorId by ErrorCode key to ErrorPage for showing to the user.
I found a temporary solution for passing errorId to ErrorPage by creating a new class which is inherited from HandleErrorInfo with below structure, and using this errorId in ErrorPage:
public class HandleErrorInfoExtension : HandleErrorInfo
{
public HandleErrorInfoExtension(Exception exception, string controllerName, string actionName, int errorId) : base(exception, controllerName, actionName)
{
ErrorId = errorId;
}
public int ErrorId { get; set; }
}
But I don't accept my own answer because of that still I am looking for a real solution to resolve the main problem of this question which is being able to share a data (or data structure) between all worker processes of the application. You should know I used session in some other places of my application that some of these places are vital (like a payment module) so I don't find the main solution for removing the using of the session (except using DB data storing because of performance overhead) yet. So I ask the community of developers of the StackOverflow.com to help me solve this problem.
Thanks to all of you dear colleagues.
Hi I am trying to create a putty like interface in my web application.
1. It will have a text box to enter a command (Temporarily hard coding the server user credentials) and If the user hits return key, it will send ajax request to the server.
2. Server will creates jsch & session and channle object and execute that user command in the remote shell.
3. And I will populate the response in user browser screen.
I don’t want the point number two as above for further request. I want it as “Server will check for existing channel and using that channel it will execute”.
To achieve this I tried storing the channel object in session. But I need to execute the .connect() method of the channel object on every request (Which return Last Login time…., It seem it is doing login process using the older credentials) ie, Only the state is store in terms of user name & password and not the connect and server session.
Could someone suggest me a way to get a solution for my problem with JSch.
Or suggest me any other way to achieve my requirement. (Putty like interface in Browser window)
ie, I am looking for a way to create asynchronous & stateless ssh connection using JSch ?
This is my code
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String createSession = request.getParameter("createSession");
String logOff = request.getParameter("logOff");
userVoice = request.getParameter("string");
userVoice = userVoice == null ? "" : userVoice;
userVoice = userVoice + "\n";
writer = response.getWriter();
try {
HttpSession httpSession = request.getSession();
//channel = (Channel) httpSession.getAttribute("channel");
if(channel!= null && channel.isConnected())
{
/*
* channelOutput = (InputStream) httpSession
* .getAttribute("channelOutput"); channelInput = (OutputStream)
* httpSession .getAttribute("channelInput");
*/
channelOutput = channel.getInputStream();
channelInput = channel.getOutputStream();
}
if (createSession != null && logOff == null) {
String username = request.getParameter("username"); // "bninet";
String password = request.getParameter("password"); // "password";
String host = request.getParameter("host"); // "10.77.246.120";
// // sample ip
// address
int port = Integer.parseInt(request.getParameter("port"));
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(password);
Properties properties = new Properties();
properties.put("StrictHostKeyChecking", "no");
session.setConfig(properties);
session.setPassword(password);
session.connect(30000);
channel = session.openChannel("shell");
setIOforChannel(channel, httpSession);
// httpSession.setAttribute("channel", channel);
} else if (channelOutput != null && channelInput != null) {
if (logOff != null) {
userVoice = "exit";
}
channelInput.write((userVoice + "\n").getBytes());
//channel.connect();
if (logOff != null) {
channel.disconnect();
// httpSession.removeAttribute("channelOutput");
// httpSession.removeAttribute("channelInput");
}
} else {
writer.write("No session Available.\n Please create a session using createSession tool ");
return;
}
Thread.sleep(1000);
String returnData = streamToString(channelOutput);
int i = 0;
while (!returnData.isEmpty() && i < 5) {
writer.write(returnData);
Thread.sleep(1000);
returnData = streamToString(channelOutput);
i++;
}
} catch (Exception e) {
writer.write("Error Occured -- " + e.getMessage());
} finally {
writer.flush();
writer.close();
}
}
If you reuse a Channel, it reuses the session, which holds your credentials.
In order to use different credentials, you would need to disconnect the session, change its settings and reconnect it.
How to disconnect the session.
If you are wanting to reuse the session, you dont need to reconect the channel each time. Connect it once as a shell, plugging an input and output stream into it. Use the streams to pass commands and capture output.
See the JSCH example on the JCraft website.
I have an old system that was developed, not by me, in classic ASP.
I have a new system, developed by me in ASP.NET
How can I pass a session variable (not complex types, just a simple string or int) TO that classic ASP page? I don't need anything back from it.
To add a spanner to the works - how can I do the "hand off" or transfer if the classic ASP site is on a different domain?
Update:
Cannot use the option of passing items via querystring OR storing it in a DB and letting the classic ASP read it from the DB.
Thank you
you could use a classic asp page that sets session variables out of e.g. post parameters.
then call that classic asp page from your asp.net page.
example (not complete) session.asp:
if session("userIsloggedIn") = true and request.form("act") = "setSessionVar" then
session(request.form("name")) = request.form("value")
end if
of course this is some kind of hack but we are talking about classic asp...
I had a different direction. I exchanged the session states via a cookie. Adding these methods. So now instead of calling Session directly, I use these methods instead.
ASP.NET
public static void AddSessionCookie(string key, string value)
{
var cookie = HttpContext.Current.Request.Cookies["SessionCookie"];
if (cookie == null)
{
cookie = new HttpCookie("SessionCookie");
cookie.Expires = DateTime.Now.AddHours(12);
HttpContext.Current.Response.Cookies.Add(cookie);
HttpContext.Current.Request.Cookies.Add(cookie);
}
HttpContext.Current.Session[key] = value;
cookie[key] = value;
}
public static string GetSessionCookie(string key)
{
if (HttpContext.Current.Session[key] == null)
return string.Empty;
string cook = HttpContext.Current.Session[key].ToString();
if (!String.IsNullOrEmpty(cook))
{
var cookie = HttpContext.Current.Request.Cookies["SessionCookie"];
if (cookie == null)
{
cookie = new HttpCookie("SessionCookie");
cookie.Expires = DateTime.Now.AddHours(12);
HttpContext.Current.Response.Cookies.Add(cookie);
HttpContext.Current.Request.Cookies.Add(cookie);
}
if (cookie != null)
cookie[key] = cook;
return cook;
}
return cook;
}
Then for Classic
Function AddSessionCookie(key, value)
Response.Cookies("SessionCookie")(key)= value
Response.Cookies("SessionCookie").Expires = DATE + 1
Session(key) = value
End Function
Function GetSessionCookie(key)
If Session(key) <> "" Then
Response.Write(Session(key))
ELSEIF Response.Cookies("SessionCookie")(key) <> "" THEN
Session(key)=Response.Cookies("SessionCookie")(key)
Set GetSessionCookie = Session(key)
End If
End Function
I am creating cookies with following lines:
HttpCookie userid = new HttpCookie("userid", objUser.id.ToString());
userid.Expires.AddYears(1);
Response.Cookies.Add(userid);
Now how can I make it persistent?
If I visit the same page again after closing the browser, I'm unable to get it back.
Here's how you can do that.
Writing the persistent cookie.
//create a cookie
HttpCookie myCookie = new HttpCookie("myCookie");
//Add key-values in the cookie
myCookie.Values.Add("userid", objUser.id.ToString());
//set cookie expiry date-time. Made it to last for next 12 hours.
myCookie.Expires = DateTime.Now.AddHours(12);
//Most important, write the cookie to client.
Response.Cookies.Add(myCookie);
Reading the persistent cookie.
//Assuming user comes back after several hours. several < 12.
//Read the cookie from Request.
HttpCookie myCookie = Request.Cookies["myCookie"];
if (myCookie == null)
{
//No cookie found or cookie expired.
//Handle the situation here, Redirect the user or simply return;
}
//ok - cookie is found.
//Gracefully check if the cookie has the key-value as expected.
if (!string.IsNullOrEmpty(myCookie.Values["userid"]))
{
string userId = myCookie.Values["userid"].ToString();
//Yes userId is found. Mission accomplished.
}
Although the accepted answer is correct, it does not state why the original code failed to work.
Bad code from your question:
HttpCookie userid = new HttpCookie("userid", objUser.id.ToString());
userid.Expires.AddYears(1);
Response.Cookies.Add(userid);
Take a look at the second line. The basis for expiration is on the Expires property which contains the default of 1/1/0001. The above code is evaluating to 1/1/0002. Furthermore the evaluation is not being saved back to the property. Instead the Expires property should be set with the basis on the current date.
Corrected code:
HttpCookie userid = new HttpCookie("userid", objUser.id.ToString());
userid.Expires = DateTime.Now.AddYears(1);
Response.Cookies.Add(userid);
FWIW be very careful with storing something like a userid in a cookie unencrypted. Doing this makes your site very prone to cookie poisoning where users can easily impersonate another user. If you are considering something like this I would highly recommend using the forms authentication cookie directly.
bool persist = true;
var cookie = FormsAuthentication.GetAuthCookie(loginUser.ContactId, persist);
cookie.Expires = DateTime.Now.AddMonths(3);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var userData = "store any string values you want inside the ticket
extra than user id that will be encrypted"
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name,
ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData);
cookie.Value = FormsAuthentication.Encrypt(newTicket);
Response.Cookies.Add(cookie);
Then you can read this at any time from an ASP.NET page by doing
string userId = null;
if (this.Context.User.Identity.IsAuthenticated)
{
userId = this.Context.User.Identity.Name;
}
As I understand you use ASP.NET authentication and to set cookies persistent you need to set FormsAuthenticationTicket.IsPersistent = true
It is the main idea.
bool isPersisted = true;
var authTicket = new FormsAuthenticationTicket(
1,
user_name,
DateTime.Now,
DateTime.Now.AddYears(1),//Expiration (you can set it to 1 year)
isPersisted,//THIS IS THE MAIN FLAG
addition_data);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, authTicket );
if (isPersisted)
authCookie.Expires = authTicket.Expiration;
HttpContext.Current.Response.Cookies.Add(authCookie);
You need to add this as the last line...
HttpContext.Current.Response.Cookies.Add(userid);
When you need to read the value of the cookie, you'd use a method similar to this:
string cookieUserID= String.Empty;
try
{
if (HttpContext.Current.Request.Cookies["userid"] != null)
{
cookieUserID = HttpContext.Current.Request.Cookies["userid"];
}
}
catch (Exception ex)
{
//handle error
}
return cookieUserID;
//add cookie
var panelIdCookie = new HttpCookie("panelIdCookie");
panelIdCookie.Values.Add("panelId", panelId.ToString(CultureInfo.InvariantCulture));
panelIdCookie.Expires = DateTime.Now.AddMonths(2);
Response.Cookies.Add(panelIdCookie);
//read cookie
var httpCookie = Request.Cookies["panelIdCookie"];
if (httpCookie != null)
{
panelId = Convert.ToInt32(httpCookie["panelId"]);
}
I want to be able to get the SessionID of the currently authenticated session in a WebMethod function where EnableSession = false.
I cannot set EnableSession=true on this request, because another (long running) request on a different page is keeping the SessionState locked (EnableSessionState == "True" not "Readonly").
Is there a consistent way of getting the SessionID from either the ASP.NET Session cookie or the Url for cookieless sessions? I can code it myself but I would rather use a function that is already documented and tested.
Thank you very much,
Florin.
There seems to be no ASP.NET function that can do this, so I made up a hack of my own, which works... for now ;):
private string GetSessionID(HttpContext context)
{
var cookieless = context.Request.Params["HTTP_ASPFILTERSESSIONID"];
if (!string.IsNullOrEmpty(cookieless))
{
int start = cookieless.LastIndexOf("(");
int finish = cookieless.IndexOf(")");
if (start != -1 && finish != -1)
return cookieless.Substring(start + 1, finish - start - 1);
}
var cookie = context.Request.Cookies["ASP.NET_SessionId"];
if (cookie != null)
return cookie.Value;
return null;
}
HttpContext.Current.Session.SessionID