java - 使用嵌入式 tomcat 服务器进行 JUnit 测试,如何为 http 和 https 连接器指定自动端口?

标签 java http tomcat ssl jenkins

描述

我做了一个 JUnit 测试,专注于尝试测试对 SOAP 网络服务的调用。

我正在使用嵌入式 tomcat 服务器进行测试,以便使用模拟服务器运行我的测试。

我同时使用 http 和 https 连接器。

我需要为这两个连接器使用自动端口,因为测试是在 Jenkins 服务器上运行的,我不能只使用端口 443 或 8443,因为它们已经被占用。

我知道使用端口 0 作为标准端口会导致 tomcat 使用自动端口分配,但我无法设法将它与两个连接器一起使用。

预期行为

我也想为我的自定义 ssl 连接器使用自动端口分配。

是否有可能以某种方式这样做?

示例代码

这是我的 tomcat 实例的代码:

@Before
public void setup() throws Throwable {

    File tomcatWorkingDir = new File(mWorkingDir);

    //Empty the target/tomcat-working-dir directory if it exist
    //Create the directory otherwise
    if(tomcatWorkingDir.exists() && tomcatWorkingDir.isDirectory()){
        LOGGER.info("cleaning tomcat-working-dir directory");
        FileUtils.cleanDirectory(new File(mWorkingDir)); 
    } else {
        LOGGER.info("create tomcat-working-dir directory");
        tomcatWorkingDir.mkdir();
    }

    LOGGER.info("disabling ssl certification validation");
    //Disable JVM ssl sockets connection
    disableJVMCertificate();

    //Add server certificate
    createServerCertificate();

    //Custom SSL Connector
    Connector SSLConnector = getSSLConnector();

    mTomcat = new Tomcat();

    //Standard http startup port
    mTomcat.setPort(0);

    //Set up base directory 
    //Otherwise, tomcat would use the current directory
    mTomcat.setBaseDir(mWorkingDir);

    LOGGER.info("setting the ssl connector in TOMCAT");
    Service service = mTomcat.getService();
    service.addConnector(SSLConnector);

    //Redirect current port
    Connector defaultConnector = mTomcat.getConnector();
    defaultConnector.setRedirectPort(SERVER_HTTPS_PORT);

    //Configure the way WAR are managed by the engine
    mTomcat.getHost().setAutoDeploy(true);
    mTomcat.getHost().setDeployOnStartup(true);

    //Add mock server into our webApp
    String servletName = "/server";
    File webApp = new File(mWorkingDir,"../../../ws-mock-server/src/main/webapp");

    mTomcat.addWebapp(mTomcat.getHost(), servletName, webApp.getAbsolutePath());

    //start tomcat
    LOGGER.info("starting TOMCAT");

    mTomcat.start();
  }

这里是我的自定义 ssl 连接器。

    private static Connector getSSLConnector(){
    Connector connector = new Connector();
    connector.setPort(SERVER_HTTPS_PORT);
    connector.setSecure(true);

    //Http protocol Http11AprProtocol
    connector.setAttribute("protocol", "org.apache.coyote.http11.Http11AprProtocol");

    //Maximum threads allowedd on this instance of tomcat
    connector.setAttribute("maxThreads","200");
    connector.setAttribute("SSLEnabled", true);

    //No client Authentification is required in order to connect
    connector.setAttribute("clientAuth", false);

    //SSL TLSv1 protocol
    connector.setAttribute("sslProtocol","TLS");

    //Ciphers configuration describing how server will encrypt his messages
    //A common cipher suite need to exist between server and client in an ssl
    //communication in order for the handshake to succeed
    connector.setAttribute("ciphers","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");

    LOGGER.info("setting keystore file");
    //Here an absolute file path is needed in order to properly set up the keystore attribute
    connector.setAttribute("keystoreFile",new File(".").getAbsolutePath().replace("\\", "/")+"/"+mWorkingDir+"/server.jks");

    LOGGER.info("setting keystore pass");
    connector.setAttribute("keystorePass","changeit");

    return connector;
}

最佳答案

对于这个问题我有两个解决方案:

手动选择SSL端口

ServerSocket(0) constructor自动选择一个空闲端口。 Tomcat也是采用这种方式。

try (ServerSocket testSocket = new ServerSocket(0)) {
    int randomFreePort = testSocket.getLocalPort(); 
    sslConnector.setPort(randomFreePort);
    defaultConnector.setRedirectPort( randomFreePort);
} // At this point the testSocket.close() called
tomcat.start();

我知道,另一个进程有可能在 testSocket.close()tomcat.start() 之间分配相同的端口,但您可以使用 LifecycleState.FAILED.equals(sslConnector.getState()) 测试检测这种情况。

使用生命周期监听器

Tomcat 连接器具有生命周期感知能力,因此您会收到有关“before_init”和“after_init”事件的通知。 Tomcat 按照您将它们添加到服务的顺序初始化连接器。

  1. 添加 ssl 连接器。
  2. 添加一个 http 连接器。 (这将是“默认”连接器。不要调用 mTomcat.getConnector(),因为它获取第一个连接器或创建一个新连接器。)
  3. 当 ssl 连接器初始化完成后,您可以使用 getLocalPort() 获取所选端口。打电话。
  4. 在 http 连接器初始化之前,调用 setRedirectPort

完整示例:

    Tomcat mTomcat = new Tomcat();
    Connector sslConnector = getSSLConnector(); 
    mTomcat.getService().addConnector(sslConnector);    
    Connector defaultConnector = new Connector();
    defaultConnector.setPort(0);
    mTomcat.getService().addConnector(defaultConnector);

    // Do the rest of the Tomcat setup

    AtomicInteger sslPort = new AtomicInteger();
    sslConnector.addLifecycleListener(event->{
        if( "after_init".equals(event.getType()) )
            sslPort.set(sslConnector.getLocalPort());
    });
    defaultConnector.addLifecycleListener(event->{
        if( "before_init".equals(event.getType()) )
            defaultConnector.setRedirectPort(sslPort.get());
    });

    mTomcat.start();

关于java - 使用嵌入式 tomcat 服务器进行 JUnit 测试,如何为 http 和 https 连接器指定自动端口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42298835/

相关文章:

java - 如何知道 httpservletrequest 是否包含一些文件/图像?

apache - 如何强制增加 Apache 中视频的范围字节响应的大小?

java - 通过 Maven 部署 Solr 到 Tomcat

java - 声明已关闭

Java - JSON 到 URL

java - 使用泛型多个边界的未经检查/未经确认的强制转换

java - 通过 Cmd 行设置 Tomcat

java - 如何将 jar 文件添加到项目而不是添加到 Tomcat lib 文件夹?

java - 使用 JAAS 登录模块注销

Java:如何使用socket发送和接收数据?