/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.server.protocol.http2;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.alpn.ALPN;
import org.xnio.ChannelListener;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import org.xnio.Pool;
import org.xnio.Pooled;
import org.xnio.StreamConnection;
import org.xnio.channels.StreamSourceChannel;
import org.xnio.conduits.PushBackStreamSourceConduit;
import org.xnio.ssl.JsseXnioSsl;
import org.xnio.ssl.SslConnection;

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.UndertowOptions;
import io.undertow.protocols.http2.Http2Channel;
import io.undertow.server.HttpHandler;
import io.undertow.server.OpenListener;
import io.undertow.server.protocol.http.HttpOpenListener;
import io.undertow.util.ImmediatePooled;


/**
 * Open listener for HTTP2 server
 *
 * @author Stuart Douglas
 */
public final class Http2OpenListener implements ChannelListener<StreamConnection>, OpenListener {

    private static final String PROTOCOL_KEY = Http2OpenListener.class.getName() + ".protocol";

    private static final String HTTP2 = "h2-14";
    private static final String HTTP_1_1 = "http/1.1";
    private final Pool<ByteBuffer> bufferPool;
    private final int bufferSize;

    private volatile HttpHandler rootHandler;

    private volatile OptionMap undertowOptions;
    private final HttpOpenListener delegate;

    public Http2OpenListener(final Pool<ByteBuffer> pool, final int bufferSize) {
        this(pool, OptionMap.EMPTY, bufferSize, null);
    }

    public Http2OpenListener(final Pool<ByteBuffer> pool, final OptionMap undertowOptions, final int bufferSize) {
        this(pool, undertowOptions, bufferSize, null);
    }

    public Http2OpenListener(final Pool<ByteBuffer> pool, final int bufferSize, HttpOpenListener httpDelegate) {
        this(pool, OptionMap.EMPTY, bufferSize, httpDelegate);
    }

    public Http2OpenListener(final Pool<ByteBuffer> pool, final OptionMap undertowOptions, final int bufferSize, HttpOpenListener httpDelegate) {
        this.undertowOptions = undertowOptions;
        this.bufferPool = pool;
        this.bufferSize = bufferSize;
        this.delegate = httpDelegate;
    }

    public void handleEvent(final StreamConnection channel) {
        if (UndertowLogger.REQUEST_LOGGER.isTraceEnabled()) {
            UndertowLogger.REQUEST_LOGGER.tracef("Opened connection with %s", channel.getPeerAddress());
        }
        final PotentialHttp2Connection potentialConnection = new PotentialHttp2Connection(channel);
        channel.getSourceChannel().setReadListener(potentialConnection);
        final SSLEngine sslEngine = JsseXnioSsl.getSslEngine((SslConnection) channel);
        String existing = null;// (String) sslEngine.getSession().getValue(PROTOCOL_KEY);
        //resuming an existing session, no need for ALPN
        if (existing != null) {
            UndertowLogger.REQUEST_LOGGER.debug("Resuming existing session, not doing NPN negotiation");
            if (existing.equals(HTTP2)) {
                Http2Channel sc = new Http2Channel(channel, bufferPool, new ImmediatePooled<>(ByteBuffer.wrap(new byte[0])), false, false, undertowOptions);
                sc.getReceiveSetter().set(new Http2ReceiveListener(rootHandler, getUndertowOptions(), bufferSize));
                sc.resumeReceives();
            } else {
                if (delegate == null) {
                    UndertowLogger.REQUEST_IO_LOGGER.couldNotInitiateHttp2Connection();
                    IoUtils.safeClose(channel);
                    return;
                }
                channel.getSourceChannel().setReadListener(null);
                delegate.handleEvent(channel);
            }
        } else {
            ALPN.put(sslEngine, new ALPN.ServerProvider() {
                @Override
                public void unsupported() {
                    potentialConnection.selected = HTTP_1_1;
                }

                @Override
                public String select(List<String> strings) {
                    ALPN.remove(sslEngine);
                    for (String s : strings) {
                        if (s.equals(HTTP2)) {
                            potentialConnection.selected = s;
                            sslEngine.getSession().putValue(PROTOCOL_KEY, s);
                            return s;
                        }
                    }
                    sslEngine.getSession().putValue(PROTOCOL_KEY, HTTP_1_1);
                    potentialConnection.selected = HTTP_1_1;
                    return HTTP_1_1;
                }
            });
            potentialConnection.handleEvent(channel.getSourceChannel());
        }
    }

    @Override
    public HttpHandler getRootHandler() {
        return rootHandler;
    }

    @Override
    public void setRootHandler(final HttpHandler rootHandler) {
        this.rootHandler = rootHandler;
        if (delegate != null) {
            delegate.setRootHandler(rootHandler);
        }
    }

    @Override
    public OptionMap getUndertowOptions() {
        return undertowOptions;
    }

    @Override
    public void setUndertowOptions(final OptionMap undertowOptions) {
        if (undertowOptions == null) {
            throw UndertowMessages.MESSAGES.argumentCannotBeNull("undertowOptions");
        }
        this.undertowOptions = undertowOptions;
    }

    @Override
    public Pool<ByteBuffer> getBufferPool() {
        return bufferPool;
    }

    private class PotentialHttp2Connection implements ChannelListener<StreamSourceChannel> {
        private String selected;
        private final StreamConnection channel;

        private PotentialHttp2Connection(StreamConnection channel) {
            this.channel = channel;
        }

        @Override
        public void handleEvent(StreamSourceChannel source) {
            Pooled<ByteBuffer> buffer = bufferPool.allocate();
            boolean free = true;
            try {
                while (true) {
                    int res = channel.getSourceChannel().read(buffer.getResource());
                    if (res == -1) {
                        IoUtils.safeClose(channel);
                        return;
                    }
                    buffer.getResource().flip();
                    if (HTTP2.equals(selected)) {

                        //cool, we have a Http2 connection.
                        Http2Channel channel = new Http2Channel(this.channel, bufferPool, buffer, false, false, undertowOptions);
                        Integer idleTimeout = undertowOptions.get(UndertowOptions.IDLE_TIMEOUT);
                        if (idleTimeout != null && idleTimeout > 0) {
                            channel.setIdleTimeout(idleTimeout);
                        }
                        free = false;
                        channel.getReceiveSetter().set(new Http2ReceiveListener(rootHandler, getUndertowOptions(), bufferSize));
                        channel.resumeReceives();
                        return;
                    } else if (HTTP_1_1.equals(selected) || res > 0) {
                        if (delegate == null) {
                            UndertowLogger.REQUEST_IO_LOGGER.couldNotInitiateHttp2Connection();
                            IoUtils.safeClose(channel);
                            return;
                        }
                        channel.getSourceChannel().setReadListener(null);
                        if (res > 0) {
                            PushBackStreamSourceConduit pushBackStreamSourceConduit = new PushBackStreamSourceConduit(channel.getSourceChannel().getConduit());
                            channel.getSourceChannel().setConduit(pushBackStreamSourceConduit);
                            pushBackStreamSourceConduit.pushBack(buffer);
                            free = false;
                        }
                        delegate.handleEvent(channel);
                        return;
                    } else if (res == 0) {
                        channel.getSourceChannel().resumeReads();
                        return;
                    }
                }

            } catch (IOException e) {
                UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
                IoUtils.safeClose(channel);
            } finally {
                if (free) {
                    buffer.free();
                }
            }
        }
    }
}

@@ -3503,16 +3503,8 @@
 ng =
- null;//
  (St

Add back commented out code