class WebSocketConnection extends VirtualConnection
{
    private static final Logger Log = LoggerFactory.getLogger(WebSocketConnection.class);

    private InetSocketAddress remotePeer;
    private WebSocketClientConnectionHandler socket;
    private PacketDeliverer backupDeliverer;
    private ConnectionConfiguration configuration;
    private ConnectionType connectionType;
    private WebSocketClientStanzaHandler stanzaHandler;

    public WebSocketConnection(WebSocketClientConnectionHandler socket, PacketDeliverer backupDeliverer, InetSocketAddress remotePeer) {
        this.socket = socket;
        this.backupDeliverer = backupDeliverer;
        this.remotePeer = remotePeer;
        this.connectionType = ConnectionType.SOCKET_C2S;
    }

    @Override
    public void closeVirtualConnection(@Nullable final StreamError error)
    {
        try {
            if (error != null) {
                deliverRawText0(error.toXML());
            }

            final String closeElement;
            if (STREAM_SUBSTITUTION_ENABLED.getValue()) {
                closeElement = "</stream:stream>";
            } else {
                closeElement = "<" + WebSocketClientStanzaHandler.STREAM_FOOTER + " xmlns='"+WebSocketClientStanzaHandler.FRAMING_NAMESPACE+"'/>";
            }
            deliverRawText0(closeElement);
        } finally {
            socket.getWsSession().close();
        }
    }

    @Override
    public byte[] getAddress() {
        return remotePeer.getAddress().getAddress();
    }

    @Override
    public String getHostAddress() {
        return remotePeer.getAddress().getHostAddress();
    }

    @Override
    public String getHostName()  {
        return remotePeer.getHostName();
    }

    @Override
    public void systemShutdown() {
        close(new StreamError(StreamError.Condition.system_shutdown));
    }

    @Override
    public void deliver(Packet packet) throws UnauthorizedException
    {
        if (isClosed()) {
            if (backupDeliverer != null) {
                backupDeliverer.deliver(packet);
            } else {
                Log.trace("Discarding packet that was due to be delivered on closed connection {}, for which no backup deliverer was configured.", this);
            }
        }
        else {
            boolean errorDelivering = false;
            try {
                final String xml;
                if (Namespace.NO_NAMESPACE.equals(packet.getElement().getNamespace())) {
                    // use string-based operation here to avoid cascading xmlns wonkery
                    StringBuilder packetXml = new StringBuilder(packet.toXML());
                    packetXml.insert(packetXml.indexOf(" "), " xmlns=\"jabber:client\"");
                    xml = packetXml.toString();
                } else {
                    xml = packet.toXML();
                }
                socket.getWsSession().getRemote().sendString(xml);
            } catch (Exception e) {
                Log.debug("Error delivering packet:\n" + packet, e);
                errorDelivering = true;
            }
            if (errorDelivering) {
                close();
                // Retry sending the packet again. Most probably if the packet is a
                // Message it will be stored offline
                if (backupDeliverer != null) {
                    backupDeliverer.deliver(packet);
                } else {
                    Log.trace("Discarding packet that failed to be delivered to connection {}, for which no backup deliverer was configured.", this);
                }
            }
            else {
                session.incrementServerPacketCount();
            }
        }
    }

    @Override
    public void deliverRawText(String text)
    {
        if (!isClosed()) {
            deliverRawText0(text);
        }
    }

    private void deliverRawText0(String text)
    {
        boolean errorDelivering = false;
        try {
            socket.getWsSession().getRemote().sendString(text);
        } catch (Exception e) {
            Log.debug("Error delivering raw text:\n" + text, e);
            errorDelivering = true;
        }

        // Attempt to close the connection if delivering text fails.
        if (errorDelivering) {
            close();
        }
    }

    @Override
    public boolean validate() {
        return socket.isWebSocketOpen();
    }

    @Override
    @Deprecated // Remove in Openfire 4.9 or later.
    public boolean isSecure() {
        return isEncrypted();
    }

    @Override
    public boolean isEncrypted() {
        return socket.isWebSocketEncrypted();
    }

    @Override
    @Nullable
    public PacketDeliverer getPacketDeliverer() {
        return backupDeliverer;
    }

    @Override
    public ConnectionConfiguration getConfiguration() {<FILL_FUNCTION_BODY>}

    @Override
    public boolean isCompressed() {
        return WebSocketClientConnectionHandler.isCompressionEnabled();
    }

    @Override
    public Optional<String> getTLSProtocolName() {
        return Optional.ofNullable(this.socket.getTLSProtocolName());
    }

    @Override
    public Optional<String> getCipherSuiteName() {
        return Optional.ofNullable(this.socket.getCipherSuiteName());
    }

    void setStanzaHandler(final WebSocketClientStanzaHandler stanzaHandler) {
        this.stanzaHandler = stanzaHandler;
    }

    WebSocketClientStanzaHandler getStanzaHandler() {
        return stanzaHandler;
    }

    @Override
    public void reinit(LocalSession session) {
        super.reinit(session);
        stanzaHandler.setSession(session);
    }

    @Override
    public String toString()
    {
        return "WebSocketConnection{" +
            "jid=" + (getStanzaHandler() == null ? "(null)" : getStanzaHandler().getAddress()) +
            ", remotePeer=" + remotePeer +
            ", socket=" + socket +
            ", connectionType=" + connectionType +
            '}';
    }
}

        if (configuration == null) {
            final ConnectionManager connectionManager = XMPPServer.getInstance().getConnectionManager();
            configuration = connectionManager.getListener( connectionType, true ).generateConnectionConfiguration();
        }
        return configuration;
    
<methods>public non-sealed void <init>() ,public void addCompression() ,public void close(StreamError, boolean) ,public abstract void closeVirtualConnection(StreamError) ,public java.security.cert.Certificate[] getLocalCertificates() ,public org.jivesoftware.openfire.PacketDeliverer getPacketDeliverer() ,public java.security.cert.Certificate[] getPeerCertificates() ,public boolean isClosed() ,public boolean isCompressed() ,public boolean isEncrypted() ,public boolean isInitialized() ,public boolean isSecure() ,public boolean isUsingSelfSignedCertificate() ,public void setUsingSelfSignedCertificate(boolean) ,public void startCompression() ,public void startTLS(boolean, boolean) throws java.lang.Exception,public boolean validate() <variables>private static final Logger Log,private final AtomicReference<org.jivesoftware.openfire.Connection.State> state