@Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof SessionProtocol) {
            assert protocol == null;
            assert responseDecoder == null;

            // Set the current protocol and its associated WaitsHolder implementation.
            final SessionProtocol protocol = (SessionProtocol) evt;
            this.protocol = protocol;
            if (protocol == H1 || protocol == H1C) {
                final HttpResponseDecoder responseDecoder;
                if (isWebSocket()) {
                    responseDecoder = ctx.pipeline().get(WebSocketHttp1ClientChannelHandler.class);
                } else {
                    responseDecoder = ctx.pipeline().get(Http1ResponseDecoder.class);
                }
                final KeepAliveHandler keepAliveHandler = responseDecoder.keepAliveHandler();
                keepAliveHandler.initialize(ctx);

                final ClientHttp1ObjectEncoder requestEncoder =
                        new ClientHttp1ObjectEncoder(channel, protocol, clientFactory.http1HeaderNaming(),
                                                     keepAliveHandler,
                                                     isWebSocket());
                if (keepAliveHandler instanceof Http1ClientKeepAliveHandler) {
                    ((Http1ClientKeepAliveHandler) keepAliveHandler).setEncoder(requestEncoder);
                }

                this.requestEncoder = requestEncoder;
                this.responseDecoder = responseDecoder;
            } else if (protocol == H2 || protocol == H2C) {
                final ChannelHandlerContext connectionHandlerCtx =
                        ctx.pipeline().context(Http2ClientConnectionHandler.class);
                final Http2ClientConnectionHandler connectionHandler =
                        (Http2ClientConnectionHandler) connectionHandlerCtx.handler();
                requestEncoder = new ClientHttp2ObjectEncoder(connectionHandlerCtx,
                                                              connectionHandler, protocol);
                responseDecoder = connectionHandler.responseDecoder();
            } else {
                throw new Error(); // Should never reach here.
            }

            tryCompleteSessionPromise(ctx);
            return;
        }

        if (evt instanceof SessionProtocolNegotiationException) {
            tryFailSessionPromise((Throwable) evt);
            ctx.close();
            return;
        }

        if (evt instanceof SslCompletionEvent) {
            final SslCompletionEvent sslCompletionEvent = (SslCompletionEvent) evt;
            if (sslCompletionEvent.isSuccess()) {
                // Expected event
            } else {
                Throwable handshakeException = sslCompletionEvent.cause();
                final Throwable pendingException = getPendingException(ctx);
                if (pendingException != null && handshakeException != pendingException) {
                    // Use pendingException as the primary cause.
                    pendingException.addSuppressed(handshakeException);
                    handshakeException = pendingException;
                }
                tryFailSessionPromise(handshakeException);
                ctx.close();
            }
            return;
        }

        if (evt instanceof Http2ConnectionPrefaceAndSettingsFrameWrittenEvent ||
            evt instanceof ChannelInputShutdownReadComplete) {
            // Expected events
            return;
        }

        if (evt instanceof ProxyConnectionEvent) {
            proxyDestinationAddress = ((ProxyConnectionEvent) evt).destinationAddress();
            if (!tryCompleteSessionPromise(ctx)) {
                // A session has not been created yet. Additional handshakes will be done by HTTP/1 or HTTP/2.
                if (poolKey.proxyConfig.proxyType().isForwardProxy()) {
                    scheduleSessionTimeout(channel, sessionPromise, connectionTimeoutMillis, desiredProtocol);
                }
            }
            return;
        }

        logger.warn("{} Unexpected user event: {}", channel, evt);
    }
{@code true} if an {@link Http2Settings} has been received from the remote endpoint.
core/src/main/java/com/linecorp/armeria/client/HttpSessionHandler.java