Issue with Vaadin Push Long Poll

This code is taken from AthmospherePushConnection.java of the flow server. I’m having issues with the long polling transport. In order to track the issue down, I have a question regarding the part “for(;;)” of the message. What’s the purpose of this “for(;;)” ?

/**
     * Pushes pending state changes and client RPC calls to the client. If
     * {@code isConnected()} is false, defers the push until a connection is
     * established.
     *
     * @param async
     *            True if this push asynchronously originates from the server,
     *            false if it is a response to a client request.
     */
    public void push(boolean async) {
        if (!isConnected()) {
            if (async && state != State.RESPONSE_PENDING) {
                state = State.PUSH_PENDING;
            } else {
                state = State.RESPONSE_PENDING;
            }
        } else {
            try {
                JsonObject response = new UidlWriter().createUidl(getUI(),
                        async);
                sendMessage("for(;;);[" + response.toJson() + "]
");
            } catch (Exception e) {
                throw new RuntimeException("Push failed", e);
            }
        }
    }

During using long polling the client ends up receiving four messages at once, failing with invalid json. The example below was one string, I added the linebreaks manually.

Invalid JSON from server: 
for(;;);[{"syncId":17,"clientId":0,"meta":{"async":true},"changes":[{"node":8,"type":"clear","feat":2},{"node":8,"type":"splice","feat":2,"index":0,"addNodes":[125]
},{"node":8,"type":"splice","feat":2,"index":1,"addNodes":[127]
},{"node":120,"type":"detach"},{"node":121,"type":"detach"},{"node":122,"type":"detach"},{"node":123,"type":"detach"},{"node":124,"type":"attach"},{"node":124,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":125,"type":"attach"},{"node":125,"type":"clear","feat":2},{"node":125,"type":"splice","feat":2,"index":0,"addNodes":[124]
},{"node":125,"type":"put","key":"tag","feat":0,"value":"label"},{"node":126,"type":"attach"},{"node":126,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":127,"type":"attach"},{"node":127,"type":"clear","feat":2},{"node":127,"type":"splice","feat":2,"index":0,"addNodes":[126]
},{"node":127,"type":"put","key":"tag","feat":0,"value":"label"}],"timings":[246774,1]
}]
for(;;);[{"syncId":18,"clientId":0,"meta":{"async":true},"changes":[{"node":8,"type":"clear","feat":2},{"node":8,"type":"splice","feat":2,"index":0,"addNodes":[129]
},{"node":8,"type":"splice","feat":2,"index":1,"addNodes":[131]
},{"node":124,"type":"detach"},{"node":125,"type":"detach"},{"node":126,"type":"detach"},{"node":127,"type":"detach"},{"node":128,"type":"attach"},{"node":128,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":129,"type":"attach"},{"node":129,"type":"clear","feat":2},{"node":129,"type":"splice","feat":2,"index":0,"addNodes":[128]
},{"node":129,"type":"put","key":"tag","feat":0,"value":"label"},{"node":130,"type":"attach"},{"node":130,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":131,"type":"attach"},{"node":131,"type":"clear","feat":2},{"node":131,"type":"splice","feat":2,"index":0,"addNodes":[130]
},{"node":131,"type":"put","key":"tag","feat":0,"value":"label"}],"timings":[246774,1]
}]
for(;;);[{"syncId":19,"clientId":0,"meta":{"async":true},"changes":[{"node":8,"type":"clear","feat":2},{"node":8,"type":"splice","feat":2,"index":0,"addNodes":[133]
},{"node":8,"type":"splice","feat":2,"index":1,"addNodes":[135]
},{"node":128,"type":"detach"},{"node":129,"type":"detach"},{"node":130,"type":"detach"},{"node":131,"type":"detach"},{"node":132,"type":"attach"},{"node":132,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":133,"type":"attach"},{"node":133,"type":"clear","feat":2},{"node":133,"type":"splice","feat":2,"index":0,"addNodes":[132]
},{"node":133,"type":"put","key":"tag","feat":0,"value":"label"},{"node":134,"type":"attach"},{"node":134,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":135,"type":"attach"},{"node":135,"type":"clear","feat":2},{"node":135,"type":"splice","feat":2,"index":0,"addNodes":[134]
},{"node":135,"type":"put","key":"tag","feat":0,"value":"label"}],"timings":[246774,1]
}]
for(;;);[{"syncId":20,"clientId":0,"meta":{"async":true},"changes":[{"node":8,"type":"clear","feat":2},{"node":8,"type":"splice","feat":2,"index":0,"addNodes":[137]
},{"node":8,"type":"splice","feat":2,"index":1,"addNodes":[139]
},{"node":132,"type":"detach"},{"node":133,"type":"detach"},{"node":134,"type":"detach"},{"node":135,"type":"detach"},{"node":136,"type":"attach"},{"node":136,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":137,"type":"attach"},{"node":137,"type":"clear","feat":2},{"node":137,"type":"splice","feat":2,"index":0,"addNodes":[136]
},{"node":137,"type":"put","key":"tag","feat":0,"value":"label"},{"node":138,"type":"attach"},{"node":138,"type":"put","key":"text","feat":7,"value":"anonymous"},{"node":139,"type":"attach"},{"node":139,"type":"clear","feat":2},{"node":139,"type":"splice","feat":2,"index":0,"addNodes":[138]
},{"node":139,"type":"put","key":"tag","feat":0,"value":"label"}],"timings":[246774,1]
}]

In the message it can be seen that the client received multiple updates at once. This can be seen by having the for(;:wink: more than once in the message.
Is this expected behaviour? I guess not. But what leads to the issue? And - shouldn’t the client be able to parse it? I believe it fails because the next for(;:wink: is not separated from the previous message.

Please clarify whats going on here please.

Further investigation shows that messages are wrapped into

public static final String JSON_COMMUNICATION_PREFIX = "for(;;);[";
public static final String JSON_COMMUNICATION_SUFFIX = "]";

flow-client/src/main/java/com/vaadin/client/communication/MessageHandler.java

    public static String stripJSONWrapping(String jsonWithWrapping) {
        if (jsonWithWrapping == null) {
            return null;
        }

        if (!jsonWithWrapping.startsWith(JSON_COMMUNICATION_PREFIX)
                || !jsonWithWrapping.endsWith(JSON_COMMUNICATION_SUFFIX)) {
            return null;
        }
        return jsonWithWrapping.substring(JSON_COMMUNICATION_PREFIX.length(),
                jsonWithWrapping.length() - JSON_COMMUNICATION_SUFFIX.length());
    }

While this works for one message it fails if two messages are concatinated together.