/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.carbon.transport.http.netty.util.client.http2;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpScheme;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.codec.http2.HttpConversionUtil;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.AsciiString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.http.netty.util.TestUtil;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An HTTP2 client that allows you to send HTTP2 frames to a server. Inbound and outbound frames are
* logged. When run from the command-line, sends a single HEADERS frame to the server and gets back
* a "Hello World" response.
*/
public class HTTP2Client {
private static final Logger log = LoggerFactory.getLogger(HTTP2Client.class);
private AtomicInteger streamId = new AtomicInteger(1);
private Channel channel;
private EventLoopGroup workerGroup;
private HTTP2ResponseHandler responseHandler;
private HttpScheme scheme;
private AsciiString hostName;
public HTTP2Client(boolean ssl, String host, int port) throws Exception {
try {
final SslContext sslCtx;
if (ssl) {
SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
sslCtx = SslContextBuilder.forClient()
.sslProvider(provider)
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.applicationProtocolConfig(new ApplicationProtocolConfig(
Protocol.ALPN,
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
SelectorFailureBehavior.NO_ADVERTISE,
// ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1))
.build();
} else {
sslCtx = null;
}
workerGroup = new NioEventLoopGroup();
HTTP2ClientInitializer initializer = new HTTP2ClientInitializer(sslCtx, Integer.MAX_VALUE);
// Configure the client.
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.remoteAddress(host, port);
b.handler(initializer);
// Start the client.
channel = b.connect().syncUninterruptibly().channel();
log.info("Connected to [" + host + ':' + port + ']');
// Wait for the HTTP/2 upgrade to occur.
HTTP2SettingsHandler http2SettingsHandler = initializer.settingsHandler();
http2SettingsHandler.awaitSettings(TestUtil.HTTP2_RESPONSE_TIME_OUT, TestUtil.HTTP2_RESPONSE_TIME_UNIT);
responseHandler = initializer.responseHandler();
scheme = ssl ? HttpScheme.HTTPS : HttpScheme.HTTP;
hostName = new AsciiString(host + ':' + port);
} catch (Exception ex) {
log.error("Error while initializing http2 client " + ex);
this.close();
}
}
public int send(FullHttpRequest request) throws Exception {
// Configure ssl.
int currentStreamId = streamId.addAndGet(2);
request.headers().add(HttpHeaderNames.HOST, hostName);
request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), scheme.name());
responseHandler.put(currentStreamId, channel.write(request), channel.newPromise());
channel.flush();
log.info("Finished HTTP/2 request");
return currentStreamId;
}
public void close() {
// Wait until the connection is closed.
if (channel != null && channel.isActive()) {
channel.close().syncUninterruptibly();
}
if (workerGroup != null) {
workerGroup.shutdownGracefully();
}
}
public String getResponse(int streamId) {
return responseHandler.getResponse(streamId);
}
}