--- /home/cmeng/source/smack/Smack/trunk/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java	2019-09-26 05:45:29.000000000 
+++ /home/cmeng/workspace/android/atalk-android/aTalk/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java	2019-10-24 13:13:28.000000000 
@@ -37,12 +37,13 @@
 import org.jivesoftware.smack.packet.IQ;
 import org.jivesoftware.smack.packet.Message;
 import org.jivesoftware.smack.packet.Nonza;
 import org.jivesoftware.smack.packet.Presence;
 import org.jivesoftware.smack.packet.Stanza;
 import org.jivesoftware.smack.packet.StanzaError;
+import org.jivesoftware.smack.packet.XmlEnvironment;
 import org.jivesoftware.smack.util.CloseableUtil;
 import org.jivesoftware.smack.util.PacketParserUtils;
 import org.jivesoftware.smack.xml.XmlPullParser;
 
 import org.igniterealtime.jbosh.AbstractBody;
 import org.igniterealtime.jbosh.BOSHClient;
@@ -137,12 +138,14 @@
     }
 
     @Override
     protected void connectInternal() throws SmackException, InterruptedException {
         done = false;
         notified = false;
+        isFirstInitialization = true;
+
         try {
             // Ensure a clean starting state
             if (client != null) {
                 client.close();
                 client = null;
             }
@@ -160,19 +163,20 @@
             for (Map.Entry<String, String> h : config.getHttpHeaders().entrySet()) {
                 cfgBuilder.addHttpHeader(h.getKey(), h.getValue());
             }
 
             client = BOSHClient.create(cfgBuilder.build());
 
-            client.addBOSHClientConnListener(new BOSHConnectionListener());
-            client.addBOSHClientResponseListener(new BOSHPacketReader());
-
-            // Initialize the debugger
+            // Initialize the debugger before addBOSHClientResponseListener(new BOSHPacketReader());
+            // BOSHPacketReader may hold and send response prior to display of the request i.e. <response/> before <challenge/>
             if (debugger != null) {
                 initDebugger();
             }
+
+            client.addBOSHClientConnListener(new BOSHConnectionListener());
+            client.addBOSHClientResponseListener(new BOSHPacketReader());
 
             // Send the session creation request
             client.send(ComposableBody.builder()
                     .setNamespaceDefinition("xmpp", XMPP_BOSH_NS)
                     .setAttribute(BodyQName.createWithPrefix(XMPP_BOSH_NS, "version", "xmpp"), "1.0")
                     .build());
@@ -272,16 +276,14 @@
     }
 
     @Override
     public void instantShutdown() {
         setWasAuthenticated();
         sessionID = null;
-        done = true;
         authenticated = false;
         connected = false;
-        isFirstInitialization = false;
 
         // Close down the readers and writers.
         CloseableUtil.maybeClose(readerPipe, LOGGER);
         CloseableUtil.maybeClose(reader, LOGGER);
         CloseableUtil.maybeClose(writer, LOGGER);
 
@@ -363,12 +365,13 @@
         client.addBOSHClientRequestListener(new BOSHClientRequestListener() {
             @Override
             public void requestSent(BOSHMessageEvent event) {
                 if (event.getBody() != null) {
                     try {
                         writer.write(event.getBody().toXML());
+                        writer.flush();
                     } catch (Exception e) {
                         // Ignore
                     }
                 }
             }
         });
@@ -397,17 +400,18 @@
     @Override
     protected void afterSaslAuthenticationSuccess()
                     throws NotConnectedException, InterruptedException, SmackWrappedException {
         // XMPP over BOSH is unusual when it comes to SASL authentication: Instead of sending a new stream open, it
         // requires a special XML element ot be send after successful SASL authentication.
         // See XEP-0206 § 5., especially the following is example 5 of XEP-0206.
-        ComposableBody composeableBody = ComposableBody.builder().setNamespaceDefinition("xmpp",
-                        XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
-                        BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
-                                        "xmpp"), "true").setAttribute(
-                        BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build();
+        ComposableBody composeableBody = ComposableBody.builder()
+                .setNamespaceDefinition("xmpp", XMPP_BOSH_NS)
+                .setAttribute(BodyQName.createWithPrefix(XMPP_BOSH_NS, "lang", "xmpp"), "en")
+                .setAttribute(BodyQName.createWithPrefix(XMPP_BOSH_NS, "restart", "xmpp"), "true")
+                .setAttribute(BodyQName.create(BOSH_URI, "to"), getXMPPServiceDomain().toString())
+                .build();
 
         try {
             send(composeableBody);
         } catch (BOSHException e) {
             // jbosh's exception API does not really match the one of Smack.
             throw new SmackException.SmackWrappedException(e);
@@ -446,13 +450,13 @@
                             }
                     }
                 }
                 else {
                     if (connEvent.isError()) {
                         // TODO Check why jbosh's getCause returns Throwable here. This is very
-                        // unusual and should be avoided if possible
+                        // unusual and should be avoided if possible; stanza send disrupted gets <h1>400 Bad Request</h1>
                         Throwable cause = connEvent.getCause();
                         Exception e;
                         if (cause instanceof Exception) {
                             e = (Exception) cause;
                         } else {
                             e = new Exception(cause);
@@ -501,19 +505,22 @@
                     do {
                         eventType = parser.next();
                         switch (eventType) {
                         case START_ELEMENT:
                             String name = parser.getName();
                             switch (name) {
+                            case IQ.IQ_ELEMENT:
+                                // cmeng: temporary fix to init incomingStreamXmlEnvironment for BOSHConnection (no stream:stream)
+                                if ((incomingStreamXmlEnvironment == null) || (incomingStreamXmlEnvironment.getLanguage() == null))
+                                    incomingStreamXmlEnvironment = XmlEnvironment.from(parser);
                             case Message.ELEMENT:
-                            case IQ.IQ_ELEMENT:
                             case Presence.ELEMENT:
                                 parseAndProcessStanza(parser);
                                 break;
                             case "features":
-                                parseFeatures(parser);
+                                parseFeaturesAndNotify(parser);
                                 break;
                             case "error":
                                 // Some BOSH error isn't stream error.
                                 if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
                                     throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
                                 } else {
