protected void processStanza(String stanza, XMPPPacketReader reader) throws Exception {
        // Verify if end of stream was requested
        if (isEndOfStream(stanza)) {
            if (session != null) {
                session.getStreamManager().formalClose();
                Log.debug( "Closing session as an end-of-stream was received: {}", session );
                session.close();
            }
            return;
        }
        // Ignore <?xml version="1.0"?> stanzas sent by clients
        if (stanza.startsWith("<?xml")) {
            return;
        }

        Element doc;
        final Set<Namespace> namespaces = connection.getAdditionalNamespaces();
        if (namespaces.isEmpty()) {
            doc = reader.read(new StringReader(stanza)).getRootElement();
        } else {
            // When the peer defined namespace prefixes on the 'stream' element, other than the default namespaces, then
            // these need to be 'put back' for the parser to be able to parse any data that is prefixed with those. The
            // only known case occurring 'in the wild' for this is Server Dialback, but it's valid XML / XMPP regardless.
            // Reestablishing those prefixes is achieved by wrapping the data-to-be-parsed in a dummy root element on which
            // the prefixes are defined. After the data has been parsed, the dummy root element is discarded. See OF-2556.
            Log.trace("Connection '{}' defined namespace prefixes on its original 'stream' element: {}", connection.getAddress(), namespaces.stream().map(Namespace::asXML).collect(Collectors.joining(", ")));
            final StringBuilder sb = new StringBuilder();
            sb.append("<stream:stream");
            namespaces.forEach(namespace -> sb.append(" ").append(namespace.asXML()));
            if (namespaces.stream().noneMatch(namespace -> namespace.getPrefix().equals("stream"))) {
                sb.append(" ").append(Namespace.get("stream", "http://etherx.jabber.org/streams").asXML());
            }
            sb.append(">").append(stanza).append("</stream:stream>");

            doc = reader.read(new StringReader(sb.toString())).getRootElement().elementIterator().next();
            doc.detach();
        }

        if (doc == null) {
            // No document found.
            return;
        }
        String tag = doc.getName();
        if ("starttls".equals(tag)) {
            // Negotiate TLS
            if (negotiateTLS()) {
                startedTLS = true;
            }
            else {
                connection.close();
                session = null;
            }
        }
        else if ("auth".equals(tag)) {
            // User is trying to authenticate using SASL
            startedSASL = true;
            // Process authentication stanza
            saslStatus = SASLAuthentication.handle(session, doc);
        } else if (startedSASL && "response".equals(tag) || "abort".equals(tag)) {
            // User is responding to SASL challenge. Process response
            saslStatus = SASLAuthentication.handle(session, doc);
        }
        else if ("compress".equals(tag)) {
            // Client is trying to initiate compression
            if (compressClient(doc)) {
                // Compression was successful so open a new stream and offer
                // resource binding and session establishment (to client sessions only)
                waitingCompressionACK = true;
            }
        } else if (isStreamManagementStanza(doc)) {
            Log.trace("Client '{}' is sending stream management stanza.", session.getAddress());
            session.getStreamManager().process( doc );
        }
        else {
            process(doc);
        }
    }
Creates a dedicated reader for a socket.

@param router     the router for sending packets that were read.
@param connection the connection being read.
xmppserver/src/main/java/org/jivesoftware/openfire/net/StanzaHandler.java