Rob Schoenaker - 7 - Mar 7, 2015

I am trying to get ServerEvents to work with an Android client. This starts off fine, but stops after some time. The exact behaviour can be seen here in an older issue: https://github.com/ServiceStack/Issues/issues/170

This does not seem fixed in 4.0.38. From what I can tell, it seems that OnHeartbeat is never fired and I suspect the heartbeat is not starting, which would be an obvious reason to close the connections.

I am testing this with the latest Xamarin on a Nexus 5, all latest updates. Connecting works, data comes in, but after a random interval, the connections just drops.

I can provide a test case if necessary, but I would be glad to check this out myself. Can you point me on how to get a source-based version up and running? (I use the ServiceClient from NuGet at the moment)

Rob Schoenaker:

I got all the sources from GitHub, but it does not compile. One thing lacking is MemoryStreamFactory in ServiceStack.Text. It does not seem to be added to the project. Any ideas?

Specifically concerning ServiceStack.Text.Android

MemoryStreamFactory is here: https://github.com/ServiceStack/ServiceStack.Text/blob/master/src/ServiceStack.Text/RecyclableMemoryStream.cs#L37

Note: the Android client uses the PCL SS.Client and SS.Text projects.

Rob Schoenaker:

OK, thx. Will try andere let you know

Rob Schoenaker:

Step 1: I can now debug this. Keep you posted.

Rob Schoenaker:

The error is in the

public void ProcessResponse(Stream stream)

method. I will see if I can fix this. In short: the parser keeps on parsing without actually processing messages. This causes the connection to never receive an onConnect (it is skipped) and thus never fires a heartbeat.

Rob Schoenaker:

Nailed it! Fix in next post. The method needs to change o this

Rob Schoenaker:

        public void ProcessResponse(Stream stream)
        {
            if (!stream.CanRead) return;

            var task = stream.ReadAsync(buffer, 0, 2048, cancel.Token);
            task.ContinueWith(t =>
            {
                if (cancel.IsCancellationRequested || t.IsCanceled)
                {
                    httpReq = null;
                    return;
                }

                if (t.IsFaulted)
                {
                    OnExceptionReceived(t.Exception);
                    httpReq = null;
                    return;
                }

                errorsCount = 0;

                int len = task.Result;
                if (len > 0)
                {
                    var text = overflowText + encoding.GetString(buffer, 0, len);
                    int pos;
                    while ((pos = text.IndexOf(’\n’)) >= 0)
                    {
                        if (text.StartsWith("\n"))
                        {
                            if (currentMsg != null) 
                                ProcessEventMessage(currentMsg);
                            currentMsg = null;
                           
                            text = text.Substring(1);
                            if (text == “”)
                            {
                              break;
                            }
                        }

                        var line = text.Substring(0, pos);
                        if (!string.IsNullOrWhiteSpace(line)) 
                            ProcessLine(line);
                        if (text.Length > pos + 1)
                            text = text.Substring(pos + 1);
                    }

                    overflowText = text;

                    ProcessResponse(stream);
                }
                else
                {
                    if (log.IsDebugEnabled)
                        log.DebugFormat(“Connection ended on {0}”, ConnectionDisplayName);
                }
            });
        }

Rob Schoenaker:

Further testing proves that this does not 100% fix the issue.
I will test more and see if I can come up with a solid solution

Do you have a small stand-alone app that you’re testing this on?

Rob Schoenaker:

Not het, but i will make one.

Rob Schoenaker:

I have a test app ready. It’s probabl too big to attach. Where do you want me to send it? I will provide details with it.

Add it to a new GitHub repo.

Rob Schoenaker:

Here you are:
https://github.com/RobSchoenaker/SSTest

Rob Schoenaker:

If you need any info,  I am on Hangouts

Rob Schoenaker:

Fyi, I did not include the SS libraries in the commit. They are identical to the SS git sources, with the exception of the method I modified.

I’ve moved some of your code to the PCL Test Android project and added debug logging that output to the screen so it’s easier to see what’s going on in this commit: https://github.com/ServiceStack/ServiceStack/commit/da112619749eadd4dd8b11923cff203941417f12

The issue was that Mono grouped multiple SSE data events together in the same socket read buffer which wasn’t being handled properly, this should be resolved now and I also added a fallback to auto-reconnect on failed connections.

These changes is now available on MyGet at: https://github.com/ServiceStack/ServiceStack/wiki/MyGet
If you already have v4.0.39 installed you’ll need to clear your NuGet cache and /packages folder.

Rob Schoenaker:

Perfect. I will await the release of 4.0.39 with the actual fix in place. For the time being, this is fine.

The grouping was indeed the cause of this issue. 

Thanks again for your quick response!