gt:                                     while ((task = sslEng.getDelegatedTask()) != null) {
context: /*
   * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  import java.net.*;
  import java.io.*;
  import java.nio.*;
  import java.nio.channels.*;
  import sun.net.www.MessageHeader;
  import java.util.*;
  import javax.net.ssl.*;
  import javax.net.ssl.SSLEngineResult.*;
  import java.security.*;
  
  /**
   * This class implements a simple HTTPS server. It uses multiple threads to
   * handle connections in parallel, and will spin off a new thread to handle
   * each request. (this is easier to implement with SSLEngine)
   * <p>
   * It must be instantiated with a {@link HttpCallback} object to which
   * requests are given and must be handled.
   * <p>
   * Simple synchronization between the client(s) and server can be done
   * using the {@link #waitForCondition(String)}, {@link #setCondition(String)} and
   * {@link #rendezvous(String,int)} methods.
   *
   * NOTE NOTE NOTE NOTE NOTE NOTE NOTE
   *
   * If you make a change in here, please don't forget to make the
   * corresponding change in the J2SE equivalent.
   *
   * NOTE NOTE NOTE NOTE NOTE NOTE NOTE
   */
  
  public class TestHttpsServer {
  
      ServerSocketChannel schan;
      int threads;
      int cperthread;
      HttpCallback cb;
      Server[] servers;
  
      // ssl related fields
      static SSLContext sslCtx;
  
      /**
       * Create a <code>TestHttpsServer<code> instance with the specified callback object
       * for handling requests. One thread is created to handle requests,
       * and up to ten TCP connections will be handled simultaneously.
       * @param cb the callback object which is invoked to handle each
       *  incoming request
       */
  
      public TestHttpsServer(HttpCallback cb) throws IOException {
          this(cb, 1, 10, 0);
      }
  
      /**
       * Create a <code>TestHttpsServer<code> instance with the specified number of
       * threads and maximum number of connections per thread. This functions
       * the same as the 4 arg constructor, where the port argument is set to zero.
       * @param cb the callback object which is invoked to handle each
       *     incoming request
       * @param threads the number of threads to create to handle requests
       *     in parallel
       * @param cperthread the number of simultaneous TCP connections to
       *     handle per thread
       */
  
      public TestHttpsServer(HttpCallback cb, int threads, int cperthread)
          throws IOException {
          this(cb, threads, cperthread, 0);
      }
  
      /**
       * Create a <code>TestHttpsServer<code> instance with the specified number
       * of threads and maximum number of connections per thread and running on
       * the specified port. The specified number of threads are created to
       * handle incoming requests, and each thread is allowed
       * to handle a number of simultaneous TCP connections.
       * @param cb the callback object which is invoked to handle
       *  each incoming request
       * @param threads the number of threads to create to handle
       *  requests in parallel
       * @param cperthread the number of simultaneous TCP connections
       *  to handle per thread
       * @param port the port number to bind the server to. <code>Zero</code>
       *  means choose any free port.
       */
      public TestHttpsServer(HttpCallback cb, int threads, int cperthread, int port)
          throws IOException {
          this(cb, threads, cperthread, null, port);
      }
  
      /**
       * Create a <code>TestHttpsServer<code> instance with the specified number
       * of threads and maximum number of connections per thread and running on
       * the specified port. The specified number of threads are created to
       * handle incoming requests, and each thread is allowed
       * to handle a number of simultaneous TCP connections.
       * @param cb the callback object which is invoked to handle
       *  each incoming request
       * @param threads the number of threads to create to handle
       *  requests in parallel
       * @param cperthread the number of simultaneous TCP connections
       *  to handle per thread
       * @param address the InetAddress to bind to. {@code Null} means the
       *  wildcard address.
       * @param port the port number to bind the server to. {@code Zero}
       *  means choose any free port.
       */
  
      public TestHttpsServer(HttpCallback cb, int threads, int cperthread, InetAddress address, int port)
          throws IOException {
          schan = ServerSocketChannel.open();
          InetSocketAddress addr = new InetSocketAddress(address, port);
          schan.socket().bind(addr);
          this.threads = threads;
          this.cb = cb;
          this.cperthread = cperthread;
  
          try {
              // create and initialize a SSLContext
              KeyStore ks = KeyStore.getInstance("JKS");
              KeyStore ts = KeyStore.getInstance("JKS");
              char[] passphrase = "passphrase".toCharArray();
  
              ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")), passphrase);
              ts.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")), passphrase);
  
              KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
              kmf.init(ks, passphrase);
  
              TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
              tmf.init(ts);
  
              sslCtx = SSLContext.getInstance("TLS");
  
              sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
  
              servers = new Server[threads];
              for (int i=0; i<threads; i++) {
                  servers[i] = new Server(cb, schan, cperthread);
                  servers[i].start();
              }
          } catch (Exception ex) {
              throw new RuntimeException("test failed. cause: "+ex.getMessage());
          }
      }
  
      /** Tell all threads in the server to exit within 5 seconds.
       *  This is an abortive termination. Just prior to the thread exiting
       *  all channels in that thread waiting to be closed are forceably closed.
       */
  
      public void terminate() {
          for (int i=0; i<threads; i++) {
              servers[i].terminate ();
          }
      }
  
      /**
       * return the local port number to which the server is bound.
       * @return the local port number
       */
  
      public int getLocalPort () {
          return schan.socket().getLocalPort ();
      }
  
      public String getAuthority() {
          InetAddress address = schan.socket().getInetAddress();
          String hostaddr = address.getHostAddress();
          if (address.isAnyLocalAddress()) hostaddr = "localhost";
          if (hostaddr.indexOf(':') > -1) hostaddr = "[" + hostaddr + "]";
          return hostaddr + ":" + getLocalPort();
      }
  
      static class Server extends Thread {
  
          ServerSocketChannel schan;
          Selector selector;
          SelectionKey listenerKey;
          SelectionKey key; /* the current key being processed */
          HttpCallback cb;
          ByteBuffer consumeBuffer;
          int maxconn;
          int nconn;
          ClosedChannelList clist;
          boolean shutdown;
  
          Server(HttpCallback cb, ServerSocketChannel schan, int maxconn) {
              this.schan = schan;
              this.maxconn = maxconn;
              this.cb = cb;
              nconn = 0;
              consumeBuffer = ByteBuffer.allocate(512);
              clist = new ClosedChannelList();
              try {
                  selector = Selector.open();
                  schan.configureBlocking(false);
                  listenerKey = schan.register(selector, SelectionKey.OP_ACCEPT);
              } catch (IOException e) {
                  System.err.println("Server could not start: " + e);
              }
          }
  
          /* Stop the thread as soon as possible */
          public synchronized void terminate() {
              shutdown = true;
          }
  
          public void run()  {
              try {
                  while (true) {
                      selector.select(1000);
                      Set selected = selector.selectedKeys();
                      Iterator iter = selected.iterator();
                      while (iter.hasNext()) {
                          key = (SelectionKey)iter.next();
                          if (key.equals (listenerKey)) {
                              SocketChannel sock = schan.accept();
                              if (sock == null) {
                                  /* false notification */
                                  iter.remove();
                                  continue;
                              }
                              sock.configureBlocking(true);
                              SSLEngine sslEng = sslCtx.createSSLEngine();
                              sslEng.setUseClientMode(false);
                              new ServerWorker(cb, sock, sslEng).start();
                              nconn ++;
                              if (nconn == maxconn) {
                                  /* deregister */
                                  listenerKey.cancel();
                                  listenerKey = null;
                              }
                          } else {
                              if (key.isReadable()) {
                                  boolean closed = false;
                                  SocketChannel chan = (SocketChannel)key.channel();
                                  if (key.attachment() != null) {
                                      closed = consume(chan);
                                  }
  
                                  if (closed) {
                                      chan.close();
                                      key.cancel();
                                      if (nconn == maxconn) {
                                          listenerKey = schan.register(selector, SelectionKey.OP_ACCEPT);
                                      }
                                      nconn --;
                                  }
                              }
                          }
                          iter.remove();
                      }
                      clist.check();
  
                      synchronized (this) {
                          if (shutdown) {
                              clist.terminate();
                              return;
                          }
                      }
                  }
              } catch (IOException e) {
                  System.out.println("Server exception: " + e);
                  // TODO finish
              }
          }
  
          /* read all the data off the channel without looking at it
           * return true if connection closed
           */
          boolean consume(SocketChannel chan) {
              try {
                  consumeBuffer.clear();
                  int c = chan.read(consumeBuffer);
                  if (c == -1)
                      return true;
              } catch (IOException e) {
                  return true;
              }
              return false;
          }
      }
  
      static class ServerWorker extends Thread {
          private ByteBuffer inNetBB;
          private ByteBuffer outNetBB;
          private ByteBuffer inAppBB;
          private ByteBuffer outAppBB;
  
          SSLEngine sslEng;
          SocketChannel schan;
          HttpCallback cb;
          HandshakeStatus currentHSStatus;
          boolean initialHSComplete;
          boolean handshakeStarted;
          /*
           * All inbound data goes through this buffer.
           *
           * It might be nice to use a cache of ByteBuffers so we're
           * not alloc/dealloc'ing all over the place.
           */
  
          /*
           * Application buffers, also used for handshaking
           */
          private int appBBSize;
  
          ServerWorker(HttpCallback cb, SocketChannel schan, SSLEngine sslEng) {
              this.sslEng = sslEng;
              this.schan = schan;
              this.cb = cb;
              currentHSStatus = HandshakeStatus.NEED_UNWRAP;
              initialHSComplete = false;
              int netBBSize = sslEng.getSession().getPacketBufferSize();
              inNetBB =  ByteBuffer.allocate(netBBSize);
              outNetBB = ByteBuffer.allocate(netBBSize);
              appBBSize = sslEng.getSession().getApplicationBufferSize();
              inAppBB = ByteBuffer.allocate(appBBSize);
              outAppBB = ByteBuffer.allocate(appBBSize);
          }
  
          public SSLEngine getSSLEngine() {
              return sslEng;
          }
  
          public ByteBuffer outNetBB() {
              return outNetBB;
          }
  
          public ByteBuffer outAppBB() {
              return outAppBB;
          }
  
          public void run () {
              try {
                  SSLEngineResult result;
  
                  while (!initialHSComplete) {
  
                      switch (currentHSStatus) {
  
                      case NEED_UNWRAP:
                          int bytes = schan.read(inNetBB);
                          if (!handshakeStarted && bytes > 0) {
                              handshakeStarted = true;
                              int byte0 = inNetBB.get(0);
                              if (byte0 != 0x16) {
                                  // first byte of a TLS connection is supposed to be
                                  // 0x16. If not it may be a plain text connection.
                                  //
                                  // Sometime a rogue client may try to open a plain
                                  // connection with our server. Calling this method
                                  // gives a chance to the test logic to ignore such
                                  // rogue connections.
                                  //
                                  if (cb.dropPlainTextConnections()) {
                                      try { schan.close(); } catch (IOException x) { };
                                      return;
                                  }
                                  // else sslEng.unwrap will throw later on...
                              }
                          }
  
  needIO:
                          while (currentHSStatus == HandshakeStatus.NEED_UNWRAP) {
                              /*
                               * Don't need to resize requestBB, since no app data should
                               * be generated here.
                               */
                              inNetBB.flip();
                              result = sslEng.unwrap(inNetBB, inAppBB);
                              inNetBB.compact();
                              currentHSStatus = result.getHandshakeStatus();
  
                              switch (result.getStatus()) {
  
                              case OK:
                                  switch (currentHSStatus) {
                                  case NOT_HANDSHAKING:
                                      throw new IOException(
                                                            "Not handshaking during initial handshake");
  
                                  case NEED_TASK:
                                      Runnable task;
  