Why does my session object go away?

From a Container Control on a WebPage, I send a POST request to an API on a service I have subscribed to. The response comes back in the PageReceived event of the HTTPSecureSocket instance on the WebPage. In the PageReceived event, I call a public method of the Container Control to get the response data back into the Container. When the ccRegisterNewAccount1.PageReceived method is called from the HTTPSSignin.PageReceived event, the data is passed correctly but I lose the Session object. Why?

WebPage

ccRegisterNewAccount1 (Container Control)

SendHTTPSPostRequest (Event)
HTTPSSignIn (HTTPSecureSocket)
PageReceived (Event)

ccRegisterNewAccount1.SendHTTPSPostRequest (Event)

[code] Dim Request As String
Dim bSend As Boolean = True

HTTPSSignIn.SetFormData(form)
HTTPSSignIn.SetRequestHeader(“Authorization”, "Bearer " + App.StripeKey)

Select Case Uppercase(Object)
Case “NEWCUSTOMER”
Request = “customers”

Case Else
bSend = False
End Select

if bSend then HTTPSSignIn.Post(AppOption(enumAppOption.StripeURL).StringValue + Request)
[/code]

HTTPSSignin.PageReceived (Event)

Self.ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8))

ccRegisterNewAccount.PageReceived (Public method)

[code] MsgBox "PageReceived: " + CStr(Session.ClientTime.GMTOffset)

CreateStripeAccount(httpStatus, data)[/code]

I get a NilObjectException on Session.ClientTime.GMTOffset

My understanding is that the code in the PageReceived event is running in a different thread and is not aware of which Session to reference. To address this, use WebSessionContext or store a reference to the Session in a Property on the WebPage that has your HTTPSecureSocket instance.

If storing a reference, make it a weakref. Otherwise, you’re setting yourself up for a memory leak.

Greg, what do you mean by “weakref”?

http://documentation.xojo.com/index.php/WeakRef

Reading through the document, I understand what the WeakRef but am left confused at how to use it in my scenario.

I have added a refNewAccount As WeakRef property to my WebPage that contains the Container Control (ccRegisterNewAccount1). I then added refNewAccount = new WeakRef(me) to the Open event of ccRegisterNewAccount1. I assume this will set the “Value” property to a reference of the ccRegisterNewAccount1 Container Control. If this is correct, the thing I don’t understand is how to reference the ccRegisterNewAccount1.PageReceived public method using the refNewAccount object reference.

My comment was in response to Fred’s suggestion about keeping a reference to a Session on the socket. Basically the problem is that you have a reference to the socket in your session and a reference to the Session in the socket. This is called a circular reference and because of them your app “leaks memory”. Basically, neither of the objects (the Session or the Socket) can ever be destroyed because they both hold onto the last reference to the other. If Sessions don’t die, neither do any other properties, pages or controls that were open when the user leaves your app.

The solution I like to use is to use a Computed Property which sets a WeakRef in the background. On your Socket:

[code]Property mSession as WeakRef

Computed Property MySession as Session
Sub Set (value as WebSession)
mSession = New WeakRef(value)
End Sub

Function Get() as WebSession
If mSession = nil or mSession.value = nil then
return nil
else
return WebSession(mSession.Value)
End If
End Function[/code]

When you create the socket, set the socket’s MySession property. Whenever you want to reference the session, make a copy, check to see if it’s nil and if it’s not, then go ahead and use it. if it’s nil, you can probably stop what you’re doing with the socket because the Session is already gone:

Dim sess as WebSession = MySession if sess<>nil then sess.webpage1.progressbar1.value = xxxxx end if

If I am understanding you correctly, I

  1. On the HTTPS1 Super, create the private property mSession as well as the Computer Property MySession
  2. Set HTTPSSignIn.MySession = Session in the ccRegisterNewAccount1.SendHTTPSPostRequest event
  3. Call HTTPSSignIn.Post
  4. Then in HTTPSSignIn.PageReceived…

Dim sess as WebSession = MySession if sess<>nil then sess.pgSignin.ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8)) end if

When I do this I receive the following errors when trying to run the project.

  1. Type mismatch error. Expected class Session.Session, but got class WebSession -> Return WebSession(mSession.Value)
  2. This item does not exist -> Dim sess as WebSession = MySession
  3. Type “WebSession” has no member named “pgSignin” -> sess.pgSignin.ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8))

On error 1 & 2, change WebSession to Session. Sorry about that, I was typing from memory.

Thanx. I figured that is what you meant on 1 & 2. However, I am still receiving #3.

Oh, right. If pgSignIn is the current page, you could use CurrentPage to get at it, but you’ll need to cast it. Something like:

If session.currentpage is a pgSignIn then pgSignIn(session.currentpage).... End if

Dim sess as WebSession = me.MySession if sess<>nil then if sess.CurrentPage Isa pgSignin then pgSignin(sess.CurrentPage).ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8)) end if end if

This allows me to compile and run but I still receive a NilObjectException on Session.ClientTime.GMTOffset as stated in my initial post. I’m thinking it has something to do with HTTPSSignIn.MySession = Session in the ccRegisterNewAccount1.SendHTTPSPostRequest event. Am I assigning the wrong session/object?

If I change HTTPSSignIn.MySession = Session to HTTPSSignIn.MySession = WebSession I get [quote]Expected a value of type class WebSession, but found a static namespace reference to class WebSession.[/quote]

Dim sess as WebSession = me.MySession if sess<>nil then if sess.CurrentPage Isa pgSignin then pgSignin(sess.CurrentPage).ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8)) end if end if

Doesn’t seem to call the PageReceived method in the calling session even though stepping through the code shows the data as entered before the call to HTTPSSignin.Post. I am assuming from the comments above that this code should pass the variables to the PageReceived method from the calling ccRegisterNewAccount1 container control and code execution should continue as normal within the original websession. So when MsgBox "PageReceived: " + CStr(Session.ClientTime.GMTOffset) is executed in the ccRegisterNewAccount1.PageReceived public method, Session.ClientTime should be referencing the original Session.

Am I not understanding something here?

That looks right to me. Put a breakpoint on that Dim statement and step through and see where it’s failing.

It steps through just fine to the ccRegisterNewAccount1.PageReceived event.

Sub PageReceived(url As String, httpStatus As Integer, data As String) MsgBox Session.Identifier MsgBox "PageReceived: " + CStr(Session.ClientTime.GMTOffset) End Sub

I get the NilObjectException on the “Session.” object identifier yet the session was there in the previous step.

[quote]WARNING: Using pgSignin inside pgSignin is bad form and can lead to hard-to-find errors. Use Self instead.
Warning: No Session object in context[/quote]

Yes. You still can’t call Session.xxx in there because it’s being called from an event that’s not connected to a session. You can however, bring a “session” into existence using the other method Fred talked about… using WebSessionContext thusly:

Assuming your code is running on a method within a WebPage…

dim ctx as new WebSessionContext(App.SessionForControlID(self))

See this blog post that talks about WebSessionContexts:

http://www.xojo.com/blog/en/2013/06/websessioncontext-changes-in-2013.php

Now on compile I receive the error

[quote]Parameter “ControlID” expects type String, but this is class pgSignin.pgSignin
Dim ctx As new WebSessionContext(App.SessionForControlID(self))[/quote]

pgSignin.ccRegisterNewAccount1.SendHTTPSPostRequest

Dim ctx As new WebSessionContext(App.SessionForControlID(self)) HTTPSSignIn.SessionContext = ctx

pgSignin.HTTPSSignin.PageReceived

Dim ctx As WebSessionContext = me.SessionContext if ctx <> nil then if ctx.Session.CurrentPage IsA pgSignin then pgSignin(ctx.Session.CurrentPage).ccRegisterNewAccount1.PageReceived(url, httpStatus, DefineEncoding(content, Encodings.UTF8)) end if end if

HTTPS1

[code]Private mSession As WeakRef
Property SessionContext As WebSessionContext

Get
If mSession = nil or mSession.value = nil then
Return nil
else
Return WebSessionContext(mSession.Value)
End If
End Get
Set
mSession = new WeakRef(value)
End Set[/code]

Dim ctx As new WebSessionContext(App.SessionForControlID(self.ControlID)) HTTPSSignIn.SessionContext = ctx

I added .ControlID to self and now when I step through, ctx is being created at the current WebSessionContext and is successfully assigned to HTTPSSignIn.SessionContext. However, when the HTTPSSignIn.PageReceived is fired, me.SessionContext is Nil.

In HTTPSSignIn.PageReceived try replacing Dim ctx As WebSessionContext = me.SessionContext with Dim ctx As New WebSessionContext(App.SessionForControl(Self)).