On OS X I am successfully sending an initial request via a xojo.net.httpsocket. I’ve been using the pagereceived event to handle the response. When I go to make a another send request to the socket, I always get a “a request is already in progress” error. I was taking the fact that pagereceived had been fired then the request was finished. Apparently not. Do I need to do something to either end the current request or send another one?
Yeah, this bit me as well. You can’t start a new request on a socket from within the pageReceived event. There are any number of ways to avoid this. For my specific cases, I went ahead and built a state machine that manages all the socket communication.
Essentially: Subclass a timer. Give it some properties such as what state it is in, or what task it is waiting on. In its action event, have it inspect the state it is in and decide what to do next (make a new request with a socket, or whatever). In the events of a socket, have it notify the state machine about what was just received. That way the state machine can either re-use a socket or construct new ones as needed.
I went about this by using a Thread subclass. I send a request into the thread which starts the run event and fires an event (along with the received data) so I can do the next thing. So far it’s worked well.
The thread itself is based on the Task thread example. So it has an internal timer that fires on the main thread to UI can be updated.
In my case I’m not sending the next socket request in pagereceived directly. Rather the info that comes out of the initial request passes through about 3 methods before another send request is generated. But, if I were to use a timer to monitor the state of the socket, what am I looking for that would tell me that it’s ok to send the next request?
I believe the PageReceived event doesn’t fire until it’s complete.
If that call sequence is initiated within PageReceived, then the request is for all intents and purposes being generated directly by PageReceived.
I created a little example. http is a subclassed xojo.net.httpsocket with this in the pagereceived event:
Sub PageReceived(URL as Text, HTTPStatus as Integer, Content as xojo.Core.MemoryBlock)
send_request consists of:
The first run of send_request goes through, the second run gets “a request is already in progress.”
Your request is still within the PageReceived event processing.
But when does it end and how can you tell?
It ends when the code in PageReceived returns. That’s why you have to use a Timer or Thread, to detach the code from PageReceived and give PageReceived a chance to return before the code is executed.
In my actual code I was doing something like pagereceived > another method > another method > another method > send another request. So it’s more of a time issue rather than just leaving pagereceived and coming back later?
No. You must leave PageReceived. It doesn’t matter how deep the call stack is, if it was initiated by PageReceived, it is part of PageReceived. No amount of elapsed time will change that. The next request must be moved completely outside the PageReceived event and anything called by the PageReceived code.
Now I see. Thank you Kimball, Bob and Tim. Now to go mess with timers.
I instantiate per request. Not sure why you need timers. When they’re done, they go away because they’re out of scope.
Create a new class ‘MyHTTPSocket’ with Super Xojo.Net.HttpSocket
Put whatever you need to do in the Pagereceived of this Class. (Or call some other method from the Pagereceived and pass on the values)
Then whenever you want to do a Http request, you instantiate a new one like this:
Dim httpGet As New MyHTTPSocket
It’s asynchronous so this way you can do as many requests at the same time as you want.
I only need a timer to drive the state machine. In my case, my network requests are dependent on the results of a prior request. If all my requests could be unrelated to each other, there would be no need to track state between requests and no need for a timer.
I don’t follow why you need a timer and can’t create a new HTTPSocket instead of reusing the one that’s firing the event.
Let’s say I need to have a conversation with a JSON-based API somewhere. This conversation will be more than just one request/response loop, and may go on for hundreds of requests / responses. For instance, I may have an interaction with the API that looks something like the following:
All the items circled in red represent states that my app has to transition through. There may be many many other states, but this particular run through the machine this is the path it took. Each of the arrows represent a request and subsequent response from the server. I wanted to have a single “overlord” or “manager” for the state transitions, and didn’t want to put all my state logic into a subclass of the httpsocket, because some of the state transitions may need to interact with other parts of the application. In this example, all the states are simple transitions, but more complex examples may involve writing files out to disk locally, looking up things in my local database, creating Pictures in memory, parsing JSON structures into objects, updating the UI, etc. I prefer to keep all that out of the event handlers of a socket subclass.
So, instead I subclass Timer, and it manages all the state transitions. Based on the state I’m in, from the action of the timer I can update UI, make a network request (either with a new socket or by re-using one that is not busy), dump files to disk, introduce a long delay (by altering the period of the timer), etc, etc, etc.
This is one of those interesting problems that can have many possible solutions. Ultimately I see advantages of using a timer to drive the state machine, but it is certainly not the only way.
I use Xojo.Core.Timer.Calllater with a 1ms delay see http://developer.xojo.com/xojo-core-timer$CallLater
A delay of zero is more appropriate if you just want it to be the next event loop iteration.
FWIW, I’ve implemented pretty much the exact scenario you’re talking about. You want to treat the new socket (which is asynchronous) as synchronous so that you don’t start the next thing until the last thing is complete.
If you want to do a screen share tomorrow I could walk you through what I did. PM me if you want to do that.