/** javax.net.ssl.SSLSession#getValue(String name) */
  public void test_getValue() {
    SSLSession s = clientSession;
    mySSLSessionBindingListener sbl = new mySSLSessionBindingListener();

    try {
      s.getValue(null);
      fail("IllegalArgumentException wasn't thrown");
    } catch (IllegalArgumentException expected) {
      // expected
    }

    s.putValue("Name", sbl);
    Object obj = s.getValue("Name");
    assertTrue(obj instanceof SSLSessionBindingListener);
  }
  /**
   * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
   * This allows the required attributes to be set for SSL requests. <br>
   * The requirements of the Servlet specs are:
   *
   * <ul>
   *   <li>an attribute named "javax.servlet.request.cipher_suite" of type String.
   *   <li>an attribute named "javax.servlet.request.key_size" of type Integer.
   *   <li>an attribute named "javax.servlet.request.X509Certificate" of type
   *       java.security.cert.X509Certificate[]. This is an array of objects of type
   *       X509Certificate, the order of this array is defined as being in ascending order of trust.
   *       The first certificate in the chain is the one set by the client, the next is the one used
   *       to authenticate the first, and so on.
   * </ul>
   *
   * @param socket The Socket the request arrived on. This should be a javax.net.ssl.SSLSocket.
   * @param request HttpRequest to be customised.
   */
  protected void customizeRequest(Socket socket, HttpRequest request) {
    super.customizeRequest(socket, request);

    if (!(socket instanceof javax.net.ssl.SSLSocket))
      return; // I'm tempted to let it throw an exception...

    try {
      SSLSocket sslSocket = (SSLSocket) socket;
      SSLSession sslSession = sslSocket.getSession();
      String cipherSuite = sslSession.getCipherSuite();
      Integer keySize;
      X509Certificate[] certs;

      CachedInfo cachedInfo = (CachedInfo) sslSession.getValue(CACHED_INFO_ATTR);
      if (cachedInfo != null) {
        keySize = cachedInfo.getKeySize();
        certs = cachedInfo.getCerts();
      } else {
        keySize = new Integer(ServletSSL.deduceKeyLength(cipherSuite));
        certs = getCertChain(sslSession);
        cachedInfo = new CachedInfo(keySize, certs);
        sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
      }

      if (certs != null) request.setAttribute("javax.servlet.request.X509Certificate", certs);
      else if (_needClientAuth) // Sanity check
      throw new HttpException(HttpResponse.__403_Forbidden);

      request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
      request.setAttribute("javax.servlet.request.key_size", keySize);
    } catch (Exception e) {
      log.warn(LogSupport.EXCEPTION, e);
    }
  }
  /**
   * Customizes the request attributes to be set for SSL requests.
   *
   * <p>The requirements of the Servlet specs are:
   *
   * <ul>
   *   <li>an attribute named "javax.servlet.request.ssl_session_id" of type String (since Servlet
   *       Spec 3.0).
   *   <li>an attribute named "javax.servlet.request.cipher_suite" of type String.
   *   <li>an attribute named "javax.servlet.request.key_size" of type Integer.
   *   <li>an attribute named "javax.servlet.request.X509Certificate" of type
   *       java.security.cert.X509Certificate[]. This is an array of objects of type
   *       X509Certificate, the order of this array is defined as being in ascending order of trust.
   *       The first certificate in the chain is the one set by the client, the next is the one used
   *       to authenticate the first, and so on.
   * </ul>
   *
   * @param sslEngine the sslEngine to be customized.
   * @param request HttpRequest to be customized.
   */
  protected void customize(SSLEngine sslEngine, Request request) {
    SSLSession sslSession = sslEngine.getSession();

    if (_sniHostCheck) {
      String name = request.getServerName();
      X509 x509 = (X509) sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509);

      if (x509 != null && !x509.matches(name)) {
        LOG.warn("Host {} does not match SNI {}", name, x509);
        throw new BadMessageException(400, "Host does not match SNI");
      }

      if (LOG.isDebugEnabled()) LOG.debug("Host {} matched SNI {}", name, x509);
    }

    try {
      String cipherSuite = sslSession.getCipherSuite();
      Integer keySize;
      X509Certificate[] certs;
      String idStr;

      CachedInfo cachedInfo = (CachedInfo) sslSession.getValue(CACHED_INFO_ATTR);
      if (cachedInfo != null) {
        keySize = cachedInfo.getKeySize();
        certs = cachedInfo.getCerts();
        idStr = cachedInfo.getIdStr();
      } else {
        keySize = SslContextFactory.deduceKeyLength(cipherSuite);
        certs = SslContextFactory.getCertChain(sslSession);
        byte[] bytes = sslSession.getId();
        idStr = TypeUtil.toHexString(bytes);
        cachedInfo = new CachedInfo(keySize, certs, idStr);
        sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
      }

      if (certs != null) request.setAttribute("javax.servlet.request.X509Certificate", certs);

      request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
      request.setAttribute("javax.servlet.request.key_size", keySize);
      request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
    } catch (Exception e) {
      LOG.warn(Log.EXCEPTION, e);
    }
  }