All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xipki.ocsp.qa.benchmark.HttpClient Maven / Gradle / Ivy

The newest version!
/*
 *
 * Copyright (c) 2013 - 2018 Lijun Liao
 *
 * 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 org.xipki.ocsp.qa.benchmark;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.ocsp.client.api.OcspRequestorException;
import org.xipki.util.LogUtil;
import org.xipki.util.ParamUtil;
import org.xipki.util.concurrent.CountLatch;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;

/**
 * TODO.
 * @author Lijun Liao
 * @since 2.2.0
 */

final class HttpClient {

  private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);

  private class HttpClientInitializer extends ChannelInitializer {

    public HttpClientInitializer() {
    }

    @Override
    public void initChannel(SocketChannel ch) {
      ChannelPipeline pipeline = ch.pipeline();
      pipeline.addLast(new ReadTimeoutHandler(60, TimeUnit.SECONDS))
        .addLast(new WriteTimeoutHandler(60, TimeUnit.SECONDS))
        .addLast(new HttpClientCodec())
        .addLast(new HttpObjectAggregator(65536))
        .addLast(new HttpClientHandler());
    }
  }

  private class HttpClientHandler extends SimpleChannelInboundHandler {

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpResponse resp) {
      try {
        decrementPendingRequests();
        responseHandler.onComplete(resp);
      } catch (Throwable th) {
        LOG.error("unexpected error", th);
      }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
      decrementPendingRequests();
      ctx.close();
      LOG.warn("error", cause);
      responseHandler.onError();
    }
  }

  private static Boolean epollAvailable;

  private static Boolean kqueueAvailable;

  private final CountLatch latch = new CountLatch(0, 0);

  private int queueSize = 1000;

  private String uri;

  private OcspBenchmark responseHandler;

  private EventLoopGroup workerGroup;

  private Channel channel;

  private int pendingRequests = 0;

  static {
    String os = System.getProperty("os.name").toLowerCase();
    ClassLoader loader = HttpClient.class.getClassLoader();
    if (os.contains("linux")) {
      try {
        Class checkClazz = Class.forName("io.netty.channel.epoll.Epoll", false, loader);
        Method mt = checkClazz.getMethod("isAvailable");
        Object obj = mt.invoke(null);

        if (obj instanceof Boolean) {
          epollAvailable = (Boolean) obj;
        }
      } catch (Throwable th) {
        if (th instanceof ClassNotFoundException) {
          LOG.info("epoll linux is not in classpath");
        } else {
          LogUtil.warn(LOG, th, "could not use Epoll transport");
        }
      }
    } else if (os.contains("mac os") || os.contains("os x")) {
      try {
        Class checkClazz = Class.forName("io.netty.channel.epoll.kqueue.KQueue", false, loader);
        Method mt = checkClazz.getMethod("isAvailable");
        Object obj = mt.invoke(null);
        if (obj instanceof Boolean) {
          kqueueAvailable = (Boolean) obj;
        }
      } catch (Throwable th) {
        LogUtil.warn(LOG, th, "could not use KQueue transport");
      }
    }
  }

  public HttpClient(String uri, OcspBenchmark responseHandler, int queueSize) {
    this.uri = ParamUtil.requireNonNull("uri", uri);
    if (queueSize > 0) {
      this.queueSize = queueSize;
    }
    this.responseHandler = ParamUtil.requireNonNull("responseHandler", responseHandler);
    this.workerGroup = new NioEventLoopGroup(1);
  }

  @SuppressWarnings("unchecked")
  public void start() throws Exception {
    URI uri = new URI(this.uri);
    String scheme = (uri.getScheme() == null) ? "http" : uri.getScheme();

    if (!"http".equalsIgnoreCase(scheme)) {
      System.err.println("Only HTTP is supported.");
      return;
    }

    int port = uri.getPort();
    if (port == -1) {
      port = 80;
    }

    Class channelClass = NioSocketChannel.class;

    final int numThreads = 1;

    ClassLoader loader = getClass().getClassLoader();
    if (epollAvailable != null && epollAvailable.booleanValue()) {
      try {
        channelClass = (Class)
            Class.forName("io.netty.channel.epoll.EpollSocketChannel", false, loader);

        Class clazz = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup", true, loader);
        Constructor constructor = clazz.getConstructor(int.class);
        this.workerGroup = (EventLoopGroup) constructor.newInstance(numThreads);
        LOG.info("use Epoll Transport");
      } catch (Throwable th) {
        if (th instanceof ClassNotFoundException) {
          LOG.info("epoll linux is not in classpath");
        } else {
          LogUtil.warn(LOG, th, "could not use Epoll transport");
        }
        channelClass = null;
        this.workerGroup = null;
      }
    } else if (kqueueAvailable != null && kqueueAvailable.booleanValue()) {
      try {
        channelClass = (Class)
                Class.forName("io.netty.channel.kqueue.KQueueSocketChannel", false, loader);

        Class clazz = Class.forName("io.netty.channel.kqueue.KQueueEventLoopGroup",
                    true, loader);
        Constructor constructor = clazz.getConstructor(int.class);
        this.workerGroup = (EventLoopGroup) constructor.newInstance(numThreads);
        LOG.info("Use KQueue Transport");
      } catch (Exception ex) {
        LogUtil.warn(LOG, ex, "could not use KQueue transport");
        channelClass = null;
        this.workerGroup = null;
      }
    }

    if (this.workerGroup == null) {
      channelClass = NioSocketChannel.class;
      this.workerGroup = new NioEventLoopGroup(numThreads);
    }

    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(workerGroup)
      .option(ChannelOption.SO_KEEPALIVE, true)
      .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
      .channel(channelClass)
      .handler(new HttpClientInitializer());

    String host = (uri.getHost() == null) ? "127.0.0.1" : uri.getHost();
    // Make the connection attempt.
    this.channel = bootstrap.connect(host, port).syncUninterruptibly().channel();
  }

  public void send(FullHttpRequest request) throws OcspRequestorException {
    if (!channel.isActive()) {
      throw new OcspRequestorException("channel is not active");
    }

    try {
      latch.await(5, TimeUnit.SECONDS);
    } catch (InterruptedException ex) {
      throw new OcspRequestorException("sending poll is full");
    }
    incrementPendingRequests();
    ChannelFuture future = this.channel.writeAndFlush(request);
    future.awaitUninterruptibly();
  }

  public void shutdown() {
    if (channel != null) {
      channel = null;
    }
    this.workerGroup.shutdownGracefully();
  }

  private void incrementPendingRequests() {
    synchronized (latch) {
      if (++pendingRequests >= queueSize) {
        if (latch.getCount() == 0) {
          latch.countUp();
        }
      }
    }
  }

  private void decrementPendingRequests() {
    synchronized (latch) {
      if (--pendingRequests < queueSize) {
        final int count = (int) latch.getCount();
        if (count > 0) {
          while (latch.getCount() != 0) {
            latch.countDown();
          }
        } else if (count < 0) {
          while (latch.getCount() != 0) {
            latch.countUp();
          }
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy