Example usage for io.netty.handler.ssl ApplicationProtocolNegotiationHandler ApplicationProtocolNegotiationHandler

List of usage examples for io.netty.handler.ssl ApplicationProtocolNegotiationHandler ApplicationProtocolNegotiationHandler

Introduction

In this page you can find the example usage for io.netty.handler.ssl ApplicationProtocolNegotiationHandler ApplicationProtocolNegotiationHandler.

Prototype

protected ApplicationProtocolNegotiationHandler(String fallbackProtocol) 

Source Link

Document

Creates a new instance with the specified fallback protocol name.

Usage

From source file:ccwihr.client.t2.Http2ClientInitializer.java

License:Apache License

/**
 * Configure the pipeline for TLS NPN negotiation to HTTP/2.
 *///  ww w.  j ava2s  . c o m
private void configureSsl(SocketChannel ch) {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(sslCtx.newHandler(ch.alloc()));
    // We must wait for the handshake to finish and the protocol to be negotiated before configuring
    // the HTTP/2 components of the pipeline.
    pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
        @Override
        protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                ChannelPipeline p = ctx.pipeline();
                p.addLast(connectionHandler);
                configureEndOfPipeline(p);
                return;
            }
            ctx.close();
            throw new IllegalStateException("unknown protocol: " + protocol);
        }
    });
}

From source file:com.relayrides.pushy.apns.ApnsClient.java

License:Open Source License

protected ApnsClient(final SslContext sslContext, final EventLoopGroup eventLoopGroup) {
    this.bootstrap = new Bootstrap();

    if (eventLoopGroup != null) {
        this.bootstrap.group(eventLoopGroup);
        this.shouldShutDownEventLoopGroup = false;
    } else {/*  w w  w.j ava 2 s . com*/
        this.bootstrap.group(new NioEventLoopGroup(1));
        this.shouldShutDownEventLoopGroup = true;
    }

    this.bootstrap.channel(SocketChannelClassUtil.getSocketChannelClass(this.bootstrap.config().group()));
    this.bootstrap.option(ChannelOption.TCP_NODELAY, true);
    this.bootstrap.handler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) throws Exception {
            final ChannelPipeline pipeline = channel.pipeline();

            final ProxyHandlerFactory proxyHandlerFactory = ApnsClient.this.proxyHandlerFactory;

            if (proxyHandlerFactory != null) {
                pipeline.addFirst(proxyHandlerFactory.createProxyHandler());
            }

            if (ApnsClient.this.writeTimeoutMillis > 0) {
                pipeline.addLast(
                        new WriteTimeoutHandler(ApnsClient.this.writeTimeoutMillis, TimeUnit.MILLISECONDS));
            }

            pipeline.addLast(sslContext.newHandler(channel.alloc()));
            pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
                @Override
                protected void configurePipeline(final ChannelHandlerContext context, final String protocol) {
                    if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                        final ApnsClientHandler apnsClientHandler = new ApnsClientHandler.ApnsClientHandlerBuilder()
                                .server(false).apnsClient(ApnsClient.this)
                                .authority(
                                        ((InetSocketAddress) context.channel().remoteAddress()).getHostName())
                                .encoderEnforceMaxConcurrentStreams(true).build();

                        synchronized (ApnsClient.this.bootstrap) {
                            if (ApnsClient.this.gracefulShutdownTimeoutMillis != null) {
                                apnsClientHandler.gracefulShutdownTimeoutMillis(
                                        ApnsClient.this.gracefulShutdownTimeoutMillis);
                            }
                        }

                        context.pipeline().addLast(
                                new IdleStateHandler(0, 0, PING_IDLE_TIME_MILLIS, TimeUnit.MILLISECONDS));
                        context.pipeline().addLast(apnsClientHandler);

                        final ChannelPromise connectionReadyPromise = ApnsClient.this.connectionReadyPromise;

                        if (connectionReadyPromise != null) {
                            connectionReadyPromise.trySuccess();
                        }
                    } else {
                        throw new IllegalArgumentException("Unexpected protocol: " + protocol);
                    }
                }
            });
        }
    });
}

From source file:com.turo.pushy.apns.BenchmarkApnsServer.java

License:Open Source License

public BenchmarkApnsServer(final InputStream certificateChainInputStream,
        final InputStream privateKeyPkcs8InputStream, final NioEventLoopGroup eventLoopGroup)
        throws SSLException {
    final SslContext sslContext;
    {/*from   www .j av  a 2s . c  om*/
        final SslProvider sslProvider;

        if (OpenSsl.isAvailable()) {
            if (OpenSsl.isAlpnSupported()) {
                sslProvider = SslProvider.OPENSSL;
            } else {
                sslProvider = SslProvider.JDK;
            }
        } else {
            sslProvider = SslProvider.JDK;
        }

        sslContext = SslContextBuilder.forServer(certificateChainInputStream, privateKeyPkcs8InputStream, null)
                .sslProvider(sslProvider)
                .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                .clientAuth(ClientAuth.OPTIONAL)
                .applicationProtocolConfig(
                        new ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN,
                                ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                                ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                                ApplicationProtocolNames.HTTP_2))
                .build();
    }

    this.bootstrap = new ServerBootstrap();
    this.bootstrap.group(eventLoopGroup);

    this.bootstrap.channel(NioServerSocketChannel.class);
    this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) throws Exception {
            final SslHandler sslHandler = sslContext.newHandler(channel.alloc());
            channel.pipeline().addLast(sslHandler);
            channel.pipeline()
                    .addLast(new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) {

                        @Override
                        protected void configurePipeline(final ChannelHandlerContext context,
                                final String protocol) throws Exception {
                            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                                context.pipeline().addLast(
                                        new BenchmarkApnsServerHandler.BenchmarkApnsServerHandlerBuilder()
                                                .initialSettings(new Http2Settings()
                                                        .maxConcurrentStreams(MAX_CONCURRENT_STREAMS))
                                                .build());

                                BenchmarkApnsServer.this.allChannels.add(context.channel());
                            } else {
                                throw new IllegalStateException("Unexpected protocol: " + protocol);
                            }
                        }
                    });
        }
    });
}

From source file:com.turo.pushy.apns.MockApnsServer.java

License:Open Source License

protected MockApnsServer(final SslContext sslContext, final EventLoopGroup eventLoopGroup) {
    this.bootstrap = new ServerBootstrap();

    if (eventLoopGroup != null) {
        this.bootstrap.group(eventLoopGroup);
        this.shouldShutDownEventLoopGroup = false;
    } else {/* w w w . jav  a 2 s  . c o m*/
        this.bootstrap.group(new NioEventLoopGroup(1));
        this.shouldShutDownEventLoopGroup = true;
    }

    this.bootstrap.channel(SocketChannelClassUtil.getServerSocketChannelClass(this.bootstrap.config().group()));
    this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) throws Exception {
            final SslHandler sslHandler = sslContext.newHandler(channel.alloc());
            channel.pipeline().addLast(sslHandler);
            channel.pipeline()
                    .addLast(new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) {

                        @Override
                        protected void configurePipeline(final ChannelHandlerContext context,
                                final String protocol) throws Exception {
                            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                                AbstractMockApnsServerHandler.AbstractMockApnsServerHandlerBuilder handlerBuilder;

                                try {
                                    final SSLSession sslSession = sslHandler.engine().getSession();

                                    // This will throw an exception if the peer hasn't authenticated (i.e. we're expecting
                                    // token authentication).
                                    final String principalName = sslSession.getPeerPrincipal().getName();

                                    final Pattern pattern = Pattern.compile(".*UID=([^,]+).*");
                                    final Matcher matcher = pattern.matcher(principalName);

                                    final String baseTopic;

                                    if (matcher.matches()) {
                                        baseTopic = matcher.group(1);
                                    } else {
                                        throw new IllegalArgumentException(
                                                "Client certificate does not specify a base topic.");
                                    }

                                    final TlsAuthenticationMockApnsServerHandler.TlsAuthenticationMockApnsServerHandlerBuilder tlsAuthenticationHandlerBuilder = new TlsAuthenticationMockApnsServerHandler.TlsAuthenticationMockApnsServerHandlerBuilder();

                                    tlsAuthenticationHandlerBuilder.baseTopic(baseTopic);

                                    handlerBuilder = tlsAuthenticationHandlerBuilder;
                                } catch (final SSLPeerUnverifiedException e) {
                                    // No need for alarm; this is an expected case
                                    final TokenAuthenticationMockApnsServerHandler.TokenAuthenticationMockApnsServerHandlerBuilder tokenAuthenticationHandlerBuilder = new TokenAuthenticationMockApnsServerHandler.TokenAuthenticationMockApnsServerHandlerBuilder();

                                    tokenAuthenticationHandlerBuilder.verificationKeysByKeyId(
                                            MockApnsServer.this.verificationKeysByKeyId);
                                    tokenAuthenticationHandlerBuilder.topicsByVerificationKey(
                                            MockApnsServer.this.topicsByVerificationKey);
                                    tokenAuthenticationHandlerBuilder.emulateExpiredFirstToken(
                                            MockApnsServer.this.emulateExpiredFirstToken);

                                    handlerBuilder = tokenAuthenticationHandlerBuilder;
                                }

                                context.pipeline().addLast(handlerBuilder
                                        .initialSettings(new Http2Settings().maxConcurrentStreams(8))
                                        .emulateInternalErrors(MockApnsServer.this.emulateInternalErrors)
                                        .deviceTokenExpirationsByTopic(
                                                MockApnsServer.this.deviceTokenExpirationsByTopic)
                                        .build());

                                MockApnsServer.this.allChannels.add(context.channel());
                            } else {
                                throw new IllegalStateException("Unexpected protocol: " + protocol);
                            }
                        }
                    });
        }
    });
}

From source file:io.gatling.http.client.impl.DefaultHttpClient.java

License:Apache License

private Future<Channel> installHttp2Handler(HttpTx tx, Channel channel, ChannelPool channelPool) {

    Promise<Channel> whenAlpn = channel.eventLoop().newPromise();

    channel.pipeline().addAfter(SSL_HANDLER, ALPN_HANDLER,
            new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) {
                @Override// ww w  . j a  va2 s . c om
                protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {

                    switch (protocol) {
                    case ApplicationProtocolNames.HTTP_2:
                        LOGGER.debug("ALPN led to HTTP/2 with remote {}", tx.request.getUri().getHost());
                        tx.listener.onProtocolAwareness(true);
                        Http2Connection connection = new DefaultHttp2Connection(false);

                        HttpToHttp2ConnectionHandler http2Handler = new HttpToHttp2ConnectionHandlerBuilder()
                                .initialSettings(Http2Settings.defaultSettings()) // FIXME override?
                                .connection(connection)
                                .frameListener(new DelegatingDecompressorFrameListener(connection,
                                        new ChunkedInboundHttp2ToHttpAdapter(connection, false, true,
                                                whenAlpn)))
                                .build();

                        ctx.pipeline().addLast(HTTP2_HANDLER, http2Handler).addLast(APP_HTTP2_HANDLER,
                                new Http2AppHandler(connection, http2Handler, channelPool, config));

                        channelPool.offer(channel);

                        SslHandler sslHandler = (SslHandler) ctx.pipeline().get(SSL_HANDLER);
                        Set<String> subjectAlternativeNames = Tls
                                .extractSubjectAlternativeNames(sslHandler.engine());
                        if (!subjectAlternativeNames.isEmpty()) {
                            channelPool.addCoalescedChannel(subjectAlternativeNames,
                                    (InetSocketAddress) channel.remoteAddress(), channel, tx.key);
                        }
                        break;

                    case ApplicationProtocolNames.HTTP_1_1:
                        LOGGER.debug("ALPN led to HTTP/1 with remote {}", tx.request.getUri().getHost());
                        if (tx.request.isHttp2PriorKnowledge()) {
                            IllegalStateException e = new IllegalStateException(
                                    "HTTP/2 Prior knowledge was set on host " + tx.request.getUri().getHost()
                                            + " but it only supports HTTP/1");
                            whenAlpn.setFailure(e);
                            throw e;
                        }
                        tx.listener.onProtocolAwareness(false);
                        ctx.pipeline()
                                .addBefore(CHUNKED_WRITER_HANDLER, HTTP_CLIENT_CODEC, newHttpClientCodec())
                                .addBefore(CHUNKED_WRITER_HANDLER, INFLATER_HANDLER,
                                        newHttpContentDecompressor())
                                .addAfter(CHUNKED_WRITER_HANDLER, APP_HTTP_HANDLER,
                                        new HttpAppHandler(DefaultHttpClient.this, channelPool, config));
                        whenAlpn.setSuccess(ctx.channel());
                        break;

                    default:
                        IllegalStateException e = new IllegalStateException("Unknown protocol: " + protocol);
                        whenAlpn.setFailure(e);
                        ctx.close();
                        // FIXME do we really need to throw?
                        throw e;
                    }
                }
            });

    whenAlpn.addListener(f -> {
        if (!f.isSuccess()) {
            tx.listener.onThrowable(f.cause());
        }
    });

    return whenAlpn;
}

From source file:io.netty.example.http2.helloworld.client.Http2ClientInitializer.java

License:Apache License

/**
 * Configure the pipeline for TLS NPN negotiation to HTTP/2.
 *//*from  w  ww. j av a 2 s .co m*/
private void configureSsl(SocketChannel ch) {
    ChannelPipeline pipeline = ch.pipeline();
    // Specify Host in SSLContext New Handler to add TLS SNI Extension
    pipeline.addLast(sslCtx.newHandler(ch.alloc(), Http2Client.HOST, Http2Client.PORT));
    // We must wait for the handshake to finish and the protocol to be negotiated before configuring
    // the HTTP/2 components of the pipeline.
    pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
        @Override
        protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                ChannelPipeline p = ctx.pipeline();
                p.addLast(connectionHandler);
                configureEndOfPipeline(p);
                return;
            }
            ctx.close();
            throw new IllegalStateException("unknown protocol: " + protocol);
        }
    });
}

From source file:io.vertx.core.http.Http2ClientTest.java

License:Open Source License

private ServerBootstrap createH2Server(
        BiFunction<Http2ConnectionDecoder, Http2ConnectionEncoder, Http2FrameListener> handler) {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.channel(NioServerSocketChannel.class);
    NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    eventLoopGroups.add(eventLoopGroup);
    bootstrap.group(eventLoopGroup);// w w  w.  ja v  a  2  s . co  m
    bootstrap.childHandler(new ChannelInitializer<Channel>() {
        @Override
        protected void initChannel(Channel ch) throws Exception {
            SSLHelper sslHelper = new SSLHelper(serverOptions, Cert.SERVER_JKS.get(), null);
            SslHandler sslHandler = new SslHandler(
                    sslHelper.setApplicationProtocols(Arrays.asList(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1))
                            .createEngine((VertxInternal) vertx, DEFAULT_HTTPS_HOST, DEFAULT_HTTPS_PORT));
            ch.pipeline().addLast(sslHandler);
            ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("whatever") {
                @Override
                protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
                    if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                        ChannelPipeline p = ctx.pipeline();
                        Http2ConnectionHandler clientHandler = createHttpConnectionHandler(handler);
                        p.addLast("handler", clientHandler);
                        return;
                    }
                    ctx.close();
                    throw new IllegalStateException("unknown protocol: " + protocol);
                }
            });
        }
    });
    return bootstrap;
}

From source file:io.vertx.core.http.impl.HttpServerImpl.java

License:Open Source License

public synchronized HttpServer listen(int port, String host, Handler<AsyncResult<HttpServer>> listenHandler) {
    if (requestStream.handler() == null && wsStream.handler() == null) {
        throw new IllegalStateException("Set request or websocket handler first");
    }/*from ww  w  . j  ava  2  s .c  o  m*/
    if (listening) {
        throw new IllegalStateException("Already listening");
    }
    listenContext = vertx.getOrCreateContext();
    listening = true;
    serverOrigin = (options.isSsl() ? "https" : "http") + "://" + host + ":" + port;
    List<HttpVersion> applicationProtocols = options.getAlpnVersions();
    if (listenContext.isWorkerContext()) {
        applicationProtocols = applicationProtocols.stream().filter(v -> v != HttpVersion.HTTP_2)
                .collect(Collectors.toList());
    }
    sslHelper.setApplicationProtocols(applicationProtocols);
    synchronized (vertx.sharedHttpServers()) {
        this.actualPort = port; // Will be updated on bind for a wildcard port
        id = new ServerID(port, host);
        HttpServerImpl shared = vertx.sharedHttpServers().get(id);
        if (shared == null || port == 0) {
            serverChannelGroup = new DefaultChannelGroup("vertx-acceptor-channels",
                    GlobalEventExecutor.INSTANCE);
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(vertx.getAcceptorEventLoopGroup(), availableWorkers);
            bootstrap.channel(NioServerSocketChannel.class);
            applyConnectionOptions(bootstrap);
            sslHelper.validate(vertx);
            bootstrap.childHandler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel ch) throws Exception {
                    if (requestStream.isPaused() || wsStream.isPaused()) {
                        ch.close();
                        return;
                    }
                    ChannelPipeline pipeline = ch.pipeline();
                    if (sslHelper.isSSL()) {
                        pipeline.addLast("ssl", sslHelper.createSslHandler(vertx));
                        if (options.isUseAlpn()) {
                            pipeline.addLast("alpn", new ApplicationProtocolNegotiationHandler("http/1.1") {
                                @Override
                                protected void configurePipeline(ChannelHandlerContext ctx, String protocol)
                                        throws Exception {
                                    if (protocol.equals("http/1.1")) {
                                        configureHttp1(pipeline);
                                    } else {
                                        handleHttp2(ch);
                                    }
                                }
                            });
                        } else {
                            configureHttp1(pipeline);
                        }
                    } else {
                        if (DISABLE_HC2) {
                            configureHttp1(pipeline);
                        } else {
                            pipeline.addLast(new Http1xOrHttp2Handler());
                        }
                    }
                }
            });

            addHandlers(this, listenContext);
            try {
                bindFuture = AsyncResolveConnectHelper.doBind(vertx, port, host, bootstrap);
                bindFuture.addListener(res -> {
                    if (res.failed()) {
                        vertx.sharedHttpServers().remove(id);
                    } else {
                        Channel serverChannel = res.result();
                        HttpServerImpl.this.actualPort = ((InetSocketAddress) serverChannel.localAddress())
                                .getPort();
                        serverChannelGroup.add(serverChannel);
                        metrics = vertx.metricsSPI().createMetrics(this, new SocketAddressImpl(port, host),
                                options);
                    }
                });
            } catch (final Throwable t) {
                // Make sure we send the exception back through the handler (if any)
                if (listenHandler != null) {
                    vertx.runOnContext(v -> listenHandler.handle(Future.failedFuture(t)));
                } else {
                    // No handler - log so user can see failure
                    log.error(t);
                }
                listening = false;
                return this;
            }
            vertx.sharedHttpServers().put(id, this);
            actualServer = this;
        } else {
            // Server already exists with that host/port - we will use that
            actualServer = shared;
            this.actualPort = shared.actualPort;
            addHandlers(actualServer, listenContext);
            metrics = vertx.metricsSPI().createMetrics(this, new SocketAddressImpl(port, host), options);
        }
        actualServer.bindFuture.addListener(future -> {
            if (listenHandler != null) {
                final AsyncResult<HttpServer> res;
                if (future.succeeded()) {
                    res = Future.succeededFuture(HttpServerImpl.this);
                } else {
                    res = Future.failedFuture(future.cause());
                    listening = false;
                }
                listenContext.runOnContext((v) -> listenHandler.handle(res));
            } else if (future.failed()) {
                listening = false;
                // No handler - log so user can see failure
                log.error(future.cause());
            }
        });
    }
    return this;
}

From source file:io.vertx.test.core.Http2ClientTest.java

License:Open Source License

private ServerBootstrap createH2Server(
        BiFunction<Http2ConnectionDecoder, Http2ConnectionEncoder, Http2FrameListener> handler) {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.channel(NioServerSocketChannel.class);
    NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    eventLoopGroups.add(eventLoopGroup);
    bootstrap.group(eventLoopGroup);/*from  w w  w.ja  va 2  s.c o  m*/
    bootstrap.childHandler(new ChannelInitializer<Channel>() {
        @Override
        protected void initChannel(Channel ch) throws Exception {
            SSLHelper sslHelper = new SSLHelper(serverOptions, Cert.SERVER_JKS.get(), null);
            SslHandler sslHandler = sslHelper
                    .setApplicationProtocols(Arrays.asList(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1))
                    .createSslHandler((VertxInternal) vertx, DEFAULT_HTTPS_HOST, DEFAULT_HTTPS_PORT);
            ch.pipeline().addLast(sslHandler);
            ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("whatever") {
                @Override
                protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
                    if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                        ChannelPipeline p = ctx.pipeline();
                        Http2ConnectionHandler clientHandler = createHttpConnectionHandler(handler);
                        p.addLast("handler", clientHandler);
                        return;
                    }
                    ctx.close();
                    throw new IllegalStateException("unknown protocol: " + protocol);
                }
            });
        }
    });
    return bootstrap;
}

From source file:netty.http2.NettyHttp2ClientInitializer.java

License:Apache License

/**
 * Configure the pipeline for TLS NPN negotiation to HTTP/2.
 *//*from   www . j a v  a  2  s .co  m*/
private void configureSsl(SocketChannel ch) {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(sslCtx.newHandler(ch.alloc()));
    // We must wait for the handshake to finish and the protocol to be negotiated before configuring
    // the HTTP/2 components of the pipeline.
    pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
        @Override
        protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                System.out.println("HTTP/2 Negotiated");
                ChannelPipeline p = ctx.pipeline();
                p.addLast(connectionHandler);
                configureEndOfPipeline(p);
                return;
            }
            ctx.close();
            throw new IllegalStateException("unknown protocol: " + protocol);
        }
    });
}