HandleURL cannot see sessions

I have a scenario where I am running a web app but need to update the web page with data submitted through HandlURL from different app.

This works consistently and flawlessly when running on my Mac.
When I deploy to Xojo Cloud, it stops.

I made a simple test to demonstrate. The web page has an text field and a button, which assigns the content of the text field to session.myid

In the HandleURL I have the following code:

If request.Path <> “test” Then Return False

Var s() As String
For i As Integer = 1 To app.SessionCount
s.add i.ToString()+“:”+app.SessionAt(i-1).myid
Next

response.Write "There are “+app.SessionCount.ToString()+” sessions: "+String.FromArray( s, ", ")

Return True

And in a desktop app, I’m sending a request to the server as so (changing the URL when testing remote or local):

Var u As New URLConnection
Var s As String = u.SendSync(“GET”,“https://MYURL.xojocloud.net/test”)

TextArea1.Text=s

Each time I run this against the Xojo cloud deployment I get the following response:

There are 0 sessions:

When I run the web app on my Mac, I get:

There are 2 sessions: 1:Session 1, 2:Session 2

I have no doubt that I’m doing something stupid. I just can’t figure out what it is.
The fact that it works one way on Mac and differently on XojoCloud is a bit distressing.
Is Xojo Cloud spewing off additional instances of the app that cannot see each other?

Any help would be greatly appreciated.
Thanks in advance.

@Chris_Halford This doesn’t solve the specific issue that you’re describing, but while working on my code injection technique I had a similar need (i.e. to associate sessions with requests coming into the HandleURL event). The technique is designed to help developers add custom controls without the need for the Web SDK.

I ended up using a combination of WebSessionContext and introspection to pull it off.

Details about the technique, and a downloadable Xojo project file, are available here:
https://timdietrich.me/blog/xojo-web-code-injection/

I hope it helps.

Thanks for the response, Tim.
I do think what you are doing is a bit different.
The fact that I can’t see any sessions at all is a show stopper for me.
Your blog is interesting anyway, and I may ping you offline with a few questions.
Hopefully there is a simple solution.

Chris,

I was curious about this, so I created a test project and uploaded to my Xojo Cloud server. And it appears to be working.

The code in my HandleURL event handler is slightly different from yours. I’ve included it below.

If you want to give the app a try, here it is:
http://sessions.mnmlsw.com

To see a list of active sessions, go here:
http://sessions.mnmlsw.com/sessions

I hope this helps. Let me know if you have any questions, or if you’d like me to test anything else.

Good luck!

If Request.Path <> "sessions" Then
  Return False
End If

Response.Write( App.SessionCount.ToString + " sessions exist:<br>" + EndOfLine )

For i As Integer = 0 to ( App.SessionCount - 1 )
  
  Var ThisSession As WebSession = App.SessionAt( i )
  
  Response.Write( "&bull; Session " + i.ToString + ": " + ThisSession.Identifier + "<br>" + EndOfLine )
  
Next

Return True
1 Like

I only know bits and pieces about Xojo Cloud, so you’ll have to reach out to Jason to get specifics for your server.

From what I glean from forum posts, I think they always spin up two instances of your app. Multiple instances can’t always find someone’s session. A session on one instance is inaccessible to the other(s) via the SessionCount / SessionAt functionality.

It is possible that you’re getting 0 sessions as the HandleURL response because your browser session is running on the other instance. HandleURL does not spin up sessions, so you must be connected with a browser to see anything at all.

To reliably communicate with multiple instances of your web app, you’ll need to find some method for every instance be able to share information. How you do this really differs for what you need to do and how many instances you are running.

You should check out Lifeboat. It will give you complete control over your deployment while still being super simple. It’s how I deploy TPLM for myself and my customers.

Just guessing, but with multiple app instances, you will probably need another piece (like a DB, IPCSocket, a helper app, …) to achieve what you want to do.

For example, once a request lands at your HandleURL event handler, you can process it and store the outcome into your database. Then, in the WebPage you want to display that information, add a WebTimer and poll the database to check for those changes.

Instead of polling, you could create something more elaborated, to avoid unnecessary lookups to the Database.

3 Likes

You need to run the session check from a separate application to see this.

That’s what I think too. It’s a shame that it behaves differently than it does on Mac.

@Jason_Parsley Can you confirm that Xojo Cloud automatically load balances our apps?

I think XOJO Cloud runs 1 app for each Virtual CPU. So for Small server (2 vCPU), you have two distinct copies of your app running instantaneously.

1 Like

From: Web app optimization — Xojo documentation

Xojo Cloud manages this automatically via load balancing (which means deploying an instance of your app for every core your server has) and a reverse proxy. If you find your app is getting too sluggish because you now have significantly more users than in the past, simply upgrading your Xojo Cloud server to one with more cores will likely solve the problem.

As Hanif said, Small server (2 vCPU).

2 Likes

That explains it. Thanks.

1 Like

Yes, Hanif is correct.

Thanks Jason.

1 Like

Well an approach that was recommended to me that would actually perform better, is to open a custom socket to the remote application.
That would allow much better bi-directional communication (pushed in both directions).
But I am concerned about implications of end-users having to worry about non-standard ports.

Has anyone used a server socket on a WebApp?
Any tips or tricks to make this behave well?

@Ricardo_Cruz what would be an example of something more elaborate rather than polling ?

Using EasyTCPSocket for example. Both instances will try to Listen. One of them will raise the Error event with a 105 code (“AddressInUseError”). In this case, that instance will then set the Address to “localhost” and Connect.

Whenever an instance receives the request you want to spread through every instance, you process it, update this instance sessions, and then broadcast the message by writing to the socket. The other end will listen to changes in ReceivedMessage event, and also update its sessions whenever a message arrives.

So, for example, create a new class, “ChannelSocket” with its super set to EasyTCPSocket.

In the Error event, you could have this code:

Select Case code
Case EasyTCPSocket.AddressInUseError
  // We're trying to Listen, but another instance is already listening on that
  // port, so we will try to connect to it instead.
  Address = "127.0.0.1" // Assuming it runs on the same machine in this example
  Connect
Else
  // Something else happened, check other possible error values at:
  // https://documentation.xojo.com/api/networking/easytcpsocket.html#constants
  Break
End Select

In the ReceivedMessage event:

Select Case command
Case 100
  // We will use this arbitrary command code to fire a
  // MessageBox on every session.
  App.ShowMessageOnEverySession(data)
End Select

Create this method in your App class:

Public Sub ShowMessageOnEverySession(message As String)
  For Each session As WebSession In App.Sessions
    Var s As New WebSessionContext(session.Identifier)
    MessageBox(message)
  Next
End Sub

Then, whenever you receive a request in App.HandleURL:

Select Case request.Path
Case "api/v1/test"
  Var message As String = "Received a request on /" + request.Path + "."
  
  // First, notify the event to sessions in this instance
  ShowMessageOnEverySession(message)
  
  // Then, broadcast the message to the other instances
  Channel.SendMessage(100, message)
  
  response.Write("ACK")
  response.Status = 200
  Return True
End Select

Something like that should do the trick. But there are cases to handle, like when the instance that is listening crashes, or when you want to roll an update.