You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Roman Vottner (JIRA)" <ji...@apache.org> on 2017/11/08 15:31:00 UTC

[jira] [Updated] (CAMEL-11998) Configuring SSL via endpointProperty in restConfiguration for an Undertow component will cause a BindException for multiple endpoints

     [ https://issues.apache.org/jira/browse/CAMEL-11998?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Roman Vottner updated CAMEL-11998:
----------------------------------
    Description: 
On recreating the test case depicted in https://github.com/apache/camel/blob/master/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestApiUndertowTest.java and activating SSL configuration, I noticed that multiple endpoints exposed will configure multiple Undertow consumers which all try to bind onto the same port and thus throw a BindException.

The full Test-Code looks as follows:
{code:java}
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;

import at.rovo.utils.TestHttpClient;
import io.undertow.server.HttpServerExchange;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.net.ssl.SSLSession;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.component.undertow.RestUndertowHttpBinding;
import org.apache.camel.component.undertow.UndertowHttpBinding;
import org.apache.camel.model.rest.RestParamType;
import org.apache.camel.spring.javaconfig.CamelConfiguration;
import org.apache.camel.test.spring.CamelSpringRunner;
import org.apache.camel.test.spring.CamelTestContextBootstrapper;
import org.apache.camel.util.jsse.KeyManagersParameters;
import org.apache.camel.util.jsse.KeyStoreParameters;
import org.apache.camel.util.jsse.SSLContextParameters;
import org.apache.camel.util.jsse.TrustManagersParameters;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

@RunWith(CamelSpringRunner.class)
@BootstrapWith(CamelTestContextBootstrapper.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,
    classes = {UndertowRouteTest.ContextConfig.class})
public class UndertowRouteTest {

  private static final String REMOTE_IP = "REMOTE_IP";
  private static final String REMOTE_PORT = "REMOTE_PORT";
  private static final String USER_AGENT = "USER_AGENT";
  private static final String TLS_PROTOCOL_VERSION = "TLS_PROTOCOL_VERSION";
  private static final String TLS_CIPHER_SUITE = "TLS_CIPHER_SUITE";

  @Configuration
  @PropertySource({"classpath:test.properties"})
  public static class ContextConfig extends CamelConfiguration {

    @Resource
    private Environment env;

    @Bean(name = "undertowHttpBinding")
    public UndertowHttpBinding undertowHttpBinding() {
      return new IPResolvableRestUndertowHttpBinding();
    }

    public static class IPResolvableRestUndertowHttpBinding extends RestUndertowHttpBinding {

      @Override
      public void populateCamelHeaders(HttpServerExchange httpExchange, Map<String, Object> headersMap, Exchange exchange) throws Exception {
        super.populateCamelHeaders(httpExchange, headersMap, exchange);

        InetSocketAddress peer = httpExchange.getSourceAddress();
        headersMap.put(REMOTE_IP, peer.getAddress().getHostAddress());
        headersMap.put(REMOTE_PORT, peer.getPort());
        for (String key : headersMap.keySet()) {
          if (key.toLowerCase().equals("user-agent")) {
            headersMap.put(USER_AGENT, headersMap.get(key));
          }
        }

        if (null != httpExchange.getConnection().getSslSessionInfo()
            && null != httpExchange.getConnection().getSslSessionInfo().getSSLSession()) {

          SSLSession sslSession = httpExchange.getConnection().getSslSessionInfo().getSSLSession();
          exchange.getIn().setHeader(TLS_PROTOCOL_VERSION, sslSession.getProtocol());
          exchange.getIn().setHeader(TLS_CIPHER_SUITE, sslSession.getCipherSuite());
        }
      }
    }

    @Bean(name = "sslContextParameters")
    public SSLContextParameters sslContextParameters() {
      String keyStore = env.getProperty("ssl.keyStore.resource");
      URL keyStoreUrl = this.getClass().getResource(keyStore);

      KeyStoreParameters ksp = new KeyStoreParameters();
      ksp.setResource(keyStoreUrl.getPath());
      ksp.setPassword(env.getProperty("ssl.keyStore.password"));

      KeyManagersParameters kmp = new KeyManagersParameters();
      kmp.setKeyStore(ksp);
      kmp.setKeyPassword(env.getProperty("ssl.key.password"));

      String trustStore = env.getProperty("ssl.trustStore.resource");
      URL trustStoreUrl = this.getClass().getResource(trustStore);

      KeyStoreParameters tsp = new KeyStoreParameters();
      tsp.setResource(trustStoreUrl.getPath());
      tsp.setPassword(env.getProperty("ssl.trustStore.password"));

      TrustManagersParameters tmp = new TrustManagersParameters();
      tmp.setKeyStore(tsp);

      SSLContextParameters scp = new SSLContextParameters();
      scp.setKeyManagers(kmp);
      scp.setTrustManagers(tmp);
      return scp;
    }

    @Override
    public void setupCamelContext(CamelContext camelContext) throws Exception {
      super.setupCamelContext(camelContext);
      PropertiesComponent pc =
          new PropertiesComponent("classpath:" + env.getProperty("propertyfile"));
      camelContext.addComponent("properties", pc);
    }

    @Override
    public List<RouteBuilder> routes() {
      final List<RouteBuilder> routes = new ArrayList<>();
      routes.add(new RouteBuilder() {
        @Override
        public void configure() throws Exception {
          restConfiguration()
              .component("undertow")
              .host("localhost")
              .port(8383)
              .endpointProperty("sslContextParameters", "#sslContextParameters")
              .endpointProperty("undertowHttpBinding", "#undertowHttpBinding")
//              .apiContextPath("/api-doc")
//              .apiProperty("cors", "true")
//              .apiProperty("api.title", "The hello rest thing")
//              .apiProperty("api.version", "1.2.3")
              ;

          rest("/hello").consumes("application/json").produces("application/json")
              .get("/hi/{name}").description("Saying hi")
                .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
                .to("log:hi")
//              .get("/bye/{name}").description("Saying bye")
//                .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
//                .responseMessage().code(200).message("A reply message").endResponseMessage()
//                .to("log:bye")
//              .post("/bye").description("To update the greeting message").consumes("application/xml").produces("application/xml")
//                .param().name("greeting").type(RestParamType.body).dataType("string").description("Message to use as greeting").endParam()
//                .to("log:bye")
          ;
        }
      });
      return routes;
    }
  }

  @Test
  public void testRoute() throws Exception {

//    TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("http://localhost:8383")
//        .build();
    TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("https://localhost:8383")
        .forSSL("/ssl/clientTrust.jks", "truststorePW")
        .build();

    try {
//      TestHttpClient.Response response = client.sendGetRequest("/api-doc");
//      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
//          response.getStatusCode(), is(equalTo(200)));

      TestHttpClient.Response response = client.sendGetRequest("/hello/hi/test");
      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
          response.getStatusCode(), is(equalTo(200)));

//      TestHttpClient.Response response = client.sendGetRequest("/hello/bye/test");
//      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
//          response.getStatusCode(), is(equalTo(200)));
//      Assert.assertThat(response.getContent(), is(equalTo("No response available")));
    } catch (Exception e) {
      Assert.fail("Should not have thrown an exception");
    }
  }
}
{code}

with the TestHttpClient being convenience class that wrapps an Apache HTTP 4.5 client internally:

{code:java}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This Apache HttpClient 4.5 based test client provides convenience methods for dealing with HTTP
 * requests and responses sent and received by invoked endpoints.
 */
@SuppressWarnings("unused")
public class TestHttpClient {

  private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  private CloseableHttpClient httpClient;
  private String baseUrl;
  private HttpClientContext context = null;

  private TestHttpClient(CloseableHttpClient httpClient, HttpClientContext context, String baseUrl) {
    this.httpClient = httpClient;
    this.baseUrl = baseUrl;
    this.context = context;
  }

  public Response sendGetRequest(String path) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpGet httpGet = new HttpGet(baseUrl + path);

    LOG.debug("Sending GET request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
      return processResponse(response);
    }
  }

  public Response sendGetRequest(String path, Map<String, String> headers) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpGet httpGet = new HttpGet(baseUrl + path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpGet.setHeader(header, headers.get(header));
      }
    }

    LOG.debug("Sending GET request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
      return processResponse(response);
    }
  }

  public Response sendDeleteRequest(String path) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpDelete httpDelete = new HttpDelete(baseUrl + path);

    LOG.debug("Sending DELETE request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
      return processResponse(response);
    }
  }

  public Response sendDeleteRequest(String path, Map<String, String> headers) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpDelete httpDelete = new HttpDelete(baseUrl + path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpDelete.setHeader(header, headers.get(header));
      }
    }

    LOG.debug("Sending DELETE request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPost.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPost.setHeader(header, headers.get(header));
      }
    }
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPost.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPost.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPost.setHeader(header, headers.get(header));
      }
    }
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPost.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPut.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPut.setHeader(header, headers.get(header));
      }
    }
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPut.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPut.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPut.setHeader(header, headers.get(header));
      }
    }
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPut.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  private Response processResponse(CloseableHttpResponse response) throws IOException{
    HttpEntity entity = response.getEntity();
    Response _response = new Response();
    _response.setStatusCode(response.getStatusLine().getStatusCode());
    if (entity != null) {
      _response.setContentLength(entity.getContentLength());
    } else {
      _response.setContentLength(0L);
    }

    Map<String, String> headers = new HashMap<>();
    for (Header header : response.getAllHeaders()) {
      headers.put(header.getName(), header.getValue());
    }
    _response.setHeaders(headers);

    if (response.getStatusLine().getStatusCode() != 204 && entity != null) {
      BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()));
      StringBuilder sb = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null) {
        sb.append(line);
      }
      EntityUtils.consume(entity);

      LOG.debug("Received response: {}", sb.toString());
      _response.setContent(sb.toString());
      if (_response.getContentLength() <= 0) {
        _response.setContentLength(sb.length());
      }
    }
    return _response;
  }

  public static String decodeBase64(String base64) {
    return new String(Base64.decodeBase64(base64));
  }

  public static JSONObject convertResponseToJson(String response) throws JSONException {
    return new JSONObject(decodeBase64(response));
  }

  public class Response {

    private int statucCode = 400;
    private String content = null;
    private Map<String, String> headers = null;
    private long contentLength = 0L;

    Response() {

    }

    public int getStatusCode() {
      return statucCode;
    }

    void setStatusCode(int statucCode) {
      this.statucCode = statucCode;
    }

    public String getContent() {
      return content;
    }

    void setContent(String content) {
      this.content = content;
    }

    public Map<String, String> getHeaders() {
      return headers;
    }

    void setHeaders(Map<String, String> headers) {
      this.headers = headers;
    }

    public long getContentLength() {
      return this.contentLength;
    }

    void setContentLength(long contentLength) {
      this.contentLength = contentLength;
    }
  }

  /**
   * A test HTTP client based on Apache commons HTTP client
   */
  public static class TestHttpClientBuilder {

    private String protocol;
    private String host;
    private int port;
    private String truststoreLoc = null;
    private String truststorePW = null;
    private String user = null;
    private String password = null;

    public TestHttpClientBuilder(String protocol, String host, int port) {
      this.protocol = protocol;
      this.host = host;
      this.port = port;
    }

    public TestHttpClientBuilder(String url) {
      if (StringUtils.isBlank(url)) {
        throw new IllegalArgumentException("Invalid URL provided");
      }
      // separate the protocol - https://localhost:80/... --> protocol: https
      protocol = url.substring(0, url.indexOf(":"));
      if (StringUtils.isBlank(protocol) || !(protocol.equals("http") || protocol.equals("https"))) {
        throw new IllegalArgumentException("Invalid protocol specified: " + protocol);
      }
      int hostStart = protocol.length() + "://".length();
      // check if there is a path delimiter defined
      String hostPort;
      int hostPortEndPos = url.indexOf("/", hostStart);
      if (hostPortEndPos == -1) { // https://localhost:80 -> hostPort: localhost:80
        hostPort = url.substring(hostStart);
      } else { // https://localhost:80/... -> hostPort: localhost:80
        hostPort = url.substring(hostStart, hostPortEndPos);
      }
      // split host and port
      int hostEnd = hostPort.indexOf(":", hostStart);
      if (hostEnd == -1) { // https://localhost --> host: localhost
        host = hostPort;
        if ("http".equals(protocol)) {
          port = 80;
        } else if ("https".equals(protocol)) {
          port = 443;
        }
      } else { // https://localhost:80 --> host: localhost; port: 80
        host = hostPort.substring(0, hostEnd);
        port = Integer.parseInt(hostPort.substring(hostEnd + 1));
      }
    }

    public TestHttpClientBuilder forSSL(String truststoreLoc, String truststorePW) {
      this.truststoreLoc = truststoreLoc;
      this.truststorePW = truststorePW;
      return this;
    }

    public TestHttpClientBuilder withBasicAuth(String user, String password) {
      this.user = user;
      this.password = password;
      return this;
    }

    public TestHttpClient build() throws TestHttpClientBuilderException {

      HttpHost targetHost = new HttpHost(host, port, protocol);

      HttpClientBuilder httpClientBuilder = HttpClients.custom();
      if (StringUtils.isNotBlank(truststoreLoc) && StringUtils.isNotBlank(truststorePW)) {
        if ("http".equals(protocol)) {
          throw new TestHttpClientBuilderException(
              "Cannot setup SSL for HTTP protocol. Please change the protocol to HTTPS!");
        }
        try {
          httpClientBuilder.setSSLSocketFactory(initializeSSL(truststoreLoc, truststorePW));
        } catch (Exception ex) {
          throw new TestHttpClientBuilderException(ex);
        }
      }
      HttpClientContext context = HttpClientContext.create();
      if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password)) {
        CredentialsProvider credsProvider = initializeBasicAuthentication(targetHost, user,
                                                                          password);
        httpClientBuilder
            .setDefaultCredentialsProvider(credsProvider);
        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(targetHost, basicAuth);

        context.setCredentialsProvider(credsProvider);
        context.setAuthCache(authCache);
      }

      return new TestHttpClient(httpClientBuilder.build(), context, protocol+"://"+host+":"+port);
    }

    private SSLConnectionSocketFactory initializeSSL(String truststoreLoc, String truststorePW)
        throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
               URISyntaxException, KeyManagementException {
      KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
      try (FileInputStream instream =
               new FileInputStream(new File(getClass().getResource(truststoreLoc).toURI()))) {
        trustStore.load(instream, truststorePW.toCharArray());
      }
      HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
      SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, null).build();
      return new SSLConnectionSocketFactory(sslcontext, hostnameVerifier);
    }

    private CredentialsProvider initializeBasicAuthentication(HttpHost targetHost, String user,
                                                              String password) {
      CredentialsProvider credsProvider = new BasicCredentialsProvider();
      credsProvider.setCredentials(
          new AuthScope(targetHost.getHostName(), targetHost.getPort()),
          new UsernamePasswordCredentials(user, password));
      return credsProvider;
    }
  }
}
{code}

The `BindException` occurs if SSL is enabled and either a currently commented endpoint definition or the swagger api configuration is reenabled. As long as there is only one rest endpoint available or the SSL configuration disabled (including switching the test client to plain HTTP; the other endpoints can be enabled as well) the test succeeds

  was:
On recreating the test case depicted in https://github.com/apache/camel/blob/master/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestApiUndertowTest.java and activating SSL configuration, I noticed that multiple endpoints exposed will configure multiple Undertow consumers which all try to bind onto the same port and thus throw a BindException.

The full Test-Code looks as follows:
{code:java}
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;

import at.rovo.utils.TestHttpClient;
import io.undertow.server.HttpServerExchange;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.net.ssl.SSLSession;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.component.undertow.RestUndertowHttpBinding;
import org.apache.camel.component.undertow.UndertowHttpBinding;
import org.apache.camel.model.rest.RestParamType;
import org.apache.camel.spring.javaconfig.CamelConfiguration;
import org.apache.camel.test.spring.CamelSpringRunner;
import org.apache.camel.test.spring.CamelTestContextBootstrapper;
import org.apache.camel.util.jsse.KeyManagersParameters;
import org.apache.camel.util.jsse.KeyStoreParameters;
import org.apache.camel.util.jsse.SSLContextParameters;
import org.apache.camel.util.jsse.TrustManagersParameters;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

@RunWith(CamelSpringRunner.class)
@BootstrapWith(CamelTestContextBootstrapper.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,
    classes = {UndertowRouteTest.ContextConfig.class})
public class UndertowRouteTest {

  private static final String REMOTE_IP = "REMOTE_IP";
  private static final String REMOTE_PORT = "REMOTE_PORT";
  private static final String USER_AGENT = "USER_AGENT";
  private static final String TLS_PROTOCOL_VERSION = "TLS_PROTOCOL_VERSION";
  private static final String TLS_CIPHER_SUITE = "TLS_CIPHER_SUITE";

  @Configuration
  @PropertySource({"classpath:services-test.properties"})
  public static class ContextConfig extends CamelConfiguration {

    @Resource
    private Environment env;

    @Bean(name = "undertowHttpBinding")
    public UndertowHttpBinding undertowHttpBinding() {
      return new IPResolvableRestUndertowHttpBinding();
    }

    public static class IPResolvableRestUndertowHttpBinding extends RestUndertowHttpBinding {

      @Override
      public void populateCamelHeaders(HttpServerExchange httpExchange, Map<String, Object> headersMap, Exchange exchange) throws Exception {
        super.populateCamelHeaders(httpExchange, headersMap, exchange);

        InetSocketAddress peer = httpExchange.getSourceAddress();
        headersMap.put(REMOTE_IP, peer.getAddress().getHostAddress());
        headersMap.put(REMOTE_PORT, peer.getPort());
        for (String key : headersMap.keySet()) {
          if (key.toLowerCase().equals("user-agent")) {
            headersMap.put(USER_AGENT, headersMap.get(key));
          }
        }

        if (null != httpExchange.getConnection().getSslSessionInfo()
            && null != httpExchange.getConnection().getSslSessionInfo().getSSLSession()) {

          SSLSession sslSession = httpExchange.getConnection().getSslSessionInfo().getSSLSession();
          exchange.getIn().setHeader(TLS_PROTOCOL_VERSION, sslSession.getProtocol());
          exchange.getIn().setHeader(TLS_CIPHER_SUITE, sslSession.getCipherSuite());
        }
      }
    }

    @Bean(name = "sslContextParameters")
    public SSLContextParameters sslContextParameters() {
      String keyStore = env.getProperty("ssl.keyStore.resource");
      URL keyStoreUrl = this.getClass().getResource(keyStore);

      KeyStoreParameters ksp = new KeyStoreParameters();
      ksp.setResource(keyStoreUrl.getPath());
      ksp.setPassword(env.getProperty("ssl.keyStore.password"));

      KeyManagersParameters kmp = new KeyManagersParameters();
      kmp.setKeyStore(ksp);
      kmp.setKeyPassword(env.getProperty("ssl.key.password"));

      String trustStore = env.getProperty("ssl.trustStore.resource");
      URL trustStoreUrl = this.getClass().getResource(trustStore);

      KeyStoreParameters tsp = new KeyStoreParameters();
      tsp.setResource(trustStoreUrl.getPath());
      tsp.setPassword(env.getProperty("ssl.trustStore.password"));

      TrustManagersParameters tmp = new TrustManagersParameters();
      tmp.setKeyStore(tsp);

      SSLContextParameters scp = new SSLContextParameters();
      scp.setKeyManagers(kmp);
      scp.setTrustManagers(tmp);
      return scp;
    }

    @Override
    public void setupCamelContext(CamelContext camelContext) throws Exception {
      super.setupCamelContext(camelContext);
      PropertiesComponent pc =
          new PropertiesComponent("classpath:" + env.getProperty("propertyfile"));
      camelContext.addComponent("properties", pc);
    }

    @Override
    public List<RouteBuilder> routes() {
      final List<RouteBuilder> routes = new ArrayList<>();
      routes.add(new RouteBuilder() {
        @Override
        public void configure() throws Exception {
          restConfiguration()
              .component("undertow")
              .host("localhost")
              .port(8383)
              .endpointProperty("sslContextParameters", "#sslContextParameters")
              .endpointProperty("undertowHttpBinding", "#undertowHttpBinding")
//              .apiContextPath("/api-doc")
//              .apiProperty("cors", "true")
//              .apiProperty("api.title", "The hello rest thing")
//              .apiProperty("api.version", "1.2.3")
              ;

          rest("/hello").consumes("application/json").produces("application/json")
              .get("/hi/{name}").description("Saying hi")
                .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
                .to("log:hi")
//              .get("/bye/{name}").description("Saying bye")
//                .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
//                .responseMessage().code(200).message("A reply message").endResponseMessage()
//                .to("log:bye")
//              .post("/bye").description("To update the greeting message").consumes("application/xml").produces("application/xml")
//                .param().name("greeting").type(RestParamType.body).dataType("string").description("Message to use as greeting").endParam()
//                .to("log:bye")
          ;
        }
      });
      return routes;
    }
  }

  @Test
  public void testRoute() throws Exception {

//    TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("http://localhost:8383")
//        .build();
    TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("https://localhost:8383")
        .forSSL("/ssl/clientTrust.jks", "truststorePW")
        .build();

    try {
//      TestHttpClient.Response response = client.sendGetRequest("/api-doc");
//      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
//          response.getStatusCode(), is(equalTo(200)));

      TestHttpClient.Response response = client.sendGetRequest("/hello/hi/test");
      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
          response.getStatusCode(), is(equalTo(200)));

//      TestHttpClient.Response response = client.sendGetRequest("/hello/bye/test");
//      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
//          response.getStatusCode(), is(equalTo(200)));
//      Assert.assertThat(response.getContent(), is(equalTo("No response available")));
    } catch (Exception e) {
      Assert.fail("Should not have thrown an exception");
    }
  }
}
{code}

with the TestHttpClient being convenience class that wrapps an Apache HTTP 4.5 client internally:

{code:java}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This Apache HttpClient 4.5 based test client provides convenience methods for dealing with HTTP
 * requests and responses sent and received by invoked endpoints.
 */
@SuppressWarnings("unused")
public class TestHttpClient {

  private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  private CloseableHttpClient httpClient;
  private String baseUrl;
  private HttpClientContext context = null;

  private TestHttpClient(CloseableHttpClient httpClient, HttpClientContext context, String baseUrl) {
    this.httpClient = httpClient;
    this.baseUrl = baseUrl;
    this.context = context;
  }

  public Response sendGetRequest(String path) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpGet httpGet = new HttpGet(baseUrl + path);

    LOG.debug("Sending GET request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
      return processResponse(response);
    }
  }

  public Response sendGetRequest(String path, Map<String, String> headers) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpGet httpGet = new HttpGet(baseUrl + path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpGet.setHeader(header, headers.get(header));
      }
    }

    LOG.debug("Sending GET request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
      return processResponse(response);
    }
  }

  public Response sendDeleteRequest(String path) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpDelete httpDelete = new HttpDelete(baseUrl + path);

    LOG.debug("Sending DELETE request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
      return processResponse(response);
    }
  }

  public Response sendDeleteRequest(String path, Map<String, String> headers) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpDelete httpDelete = new HttpDelete(baseUrl + path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpDelete.setHeader(header, headers.get(header));
      }
    }

    LOG.debug("Sending DELETE request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPost.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPost.setHeader(header, headers.get(header));
      }
    }
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPost.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPost.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPostRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPost httpPost = new HttpPost(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPost.setHeader(header, headers.get(header));
      }
    }
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPost.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPut.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPut.setHeader(header, headers.get(header));
      }
    }
    StringEntity stringPayload = new StringEntity(payload, contentType);
    httpPut.setEntity(stringPayload);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPut.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  public Response sendPutRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
    if (StringUtils.isBlank(path) || !path.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path provided");
    }
    HttpPut httpPut = new HttpPut(baseUrl+path);
    if (headers != null) {
      for (String header : headers.keySet()) {
        httpPut.setHeader(header, headers.get(header));
      }
    }
    InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
    httpPut.setEntity(payloadEntity);

    LOG.debug("Sending POST request to {}", baseUrl + path);
    try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
      return processResponse(response);
    }
  }

  private Response processResponse(CloseableHttpResponse response) throws IOException{
    HttpEntity entity = response.getEntity();
    Response _response = new Response();
    _response.setStatusCode(response.getStatusLine().getStatusCode());
    if (entity != null) {
      _response.setContentLength(entity.getContentLength());
    } else {
      _response.setContentLength(0L);
    }

    Map<String, String> headers = new HashMap<>();
    for (Header header : response.getAllHeaders()) {
      headers.put(header.getName(), header.getValue());
    }
    _response.setHeaders(headers);

    if (response.getStatusLine().getStatusCode() != 204 && entity != null) {
      BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()));
      StringBuilder sb = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null) {
        sb.append(line);
      }
      EntityUtils.consume(entity);

      LOG.debug("Received response: {}", sb.toString());
      _response.setContent(sb.toString());
      if (_response.getContentLength() <= 0) {
        _response.setContentLength(sb.length());
      }
    }
    return _response;
  }

  public static String decodeBase64(String base64) {
    return new String(Base64.decodeBase64(base64));
  }

  public static JSONObject convertResponseToJson(String response) throws JSONException {
    return new JSONObject(decodeBase64(response));
  }

  public class Response {

    private int statucCode = 400;
    private String content = null;
    private Map<String, String> headers = null;
    private long contentLength = 0L;

    Response() {

    }

    public int getStatusCode() {
      return statucCode;
    }

    void setStatusCode(int statucCode) {
      this.statucCode = statucCode;
    }

    public String getContent() {
      return content;
    }

    void setContent(String content) {
      this.content = content;
    }

    public Map<String, String> getHeaders() {
      return headers;
    }

    void setHeaders(Map<String, String> headers) {
      this.headers = headers;
    }

    public long getContentLength() {
      return this.contentLength;
    }

    void setContentLength(long contentLength) {
      this.contentLength = contentLength;
    }
  }

  /**
   * A test HTTP client based on Apache commons HTTP client
   */
  public static class TestHttpClientBuilder {

    private String protocol;
    private String host;
    private int port;
    private String truststoreLoc = null;
    private String truststorePW = null;
    private String user = null;
    private String password = null;

    public TestHttpClientBuilder(String protocol, String host, int port) {
      this.protocol = protocol;
      this.host = host;
      this.port = port;
    }

    public TestHttpClientBuilder(String url) {
      if (StringUtils.isBlank(url)) {
        throw new IllegalArgumentException("Invalid URL provided");
      }
      // separate the protocol - https://localhost:80/... --> protocol: https
      protocol = url.substring(0, url.indexOf(":"));
      if (StringUtils.isBlank(protocol) || !(protocol.equals("http") || protocol.equals("https"))) {
        throw new IllegalArgumentException("Invalid protocol specified: " + protocol);
      }
      int hostStart = protocol.length() + "://".length();
      // check if there is a path delimiter defined
      String hostPort;
      int hostPortEndPos = url.indexOf("/", hostStart);
      if (hostPortEndPos == -1) { // https://localhost:80 -> hostPort: localhost:80
        hostPort = url.substring(hostStart);
      } else { // https://localhost:80/... -> hostPort: localhost:80
        hostPort = url.substring(hostStart, hostPortEndPos);
      }
      // split host and port
      int hostEnd = hostPort.indexOf(":", hostStart);
      if (hostEnd == -1) { // https://localhost --> host: localhost
        host = hostPort;
        if ("http".equals(protocol)) {
          port = 80;
        } else if ("https".equals(protocol)) {
          port = 443;
        }
      } else { // https://localhost:80 --> host: localhost; port: 80
        host = hostPort.substring(0, hostEnd);
        port = Integer.parseInt(hostPort.substring(hostEnd + 1));
      }
    }

    public TestHttpClientBuilder forSSL(String truststoreLoc, String truststorePW) {
      this.truststoreLoc = truststoreLoc;
      this.truststorePW = truststorePW;
      return this;
    }

    public TestHttpClientBuilder withBasicAuth(String user, String password) {
      this.user = user;
      this.password = password;
      return this;
    }

    public TestHttpClient build() throws TestHttpClientBuilderException {

      HttpHost targetHost = new HttpHost(host, port, protocol);

      HttpClientBuilder httpClientBuilder = HttpClients.custom();
      if (StringUtils.isNotBlank(truststoreLoc) && StringUtils.isNotBlank(truststorePW)) {
        if ("http".equals(protocol)) {
          throw new TestHttpClientBuilderException(
              "Cannot setup SSL for HTTP protocol. Please change the protocol to HTTPS!");
        }
        try {
          httpClientBuilder.setSSLSocketFactory(initializeSSL(truststoreLoc, truststorePW));
        } catch (Exception ex) {
          throw new TestHttpClientBuilderException(ex);
        }
      }
      HttpClientContext context = HttpClientContext.create();
      if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password)) {
        CredentialsProvider credsProvider = initializeBasicAuthentication(targetHost, user,
                                                                          password);
        httpClientBuilder
            .setDefaultCredentialsProvider(credsProvider);
        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(targetHost, basicAuth);

        context.setCredentialsProvider(credsProvider);
        context.setAuthCache(authCache);
      }

      return new TestHttpClient(httpClientBuilder.build(), context, protocol+"://"+host+":"+port);
    }

    private SSLConnectionSocketFactory initializeSSL(String truststoreLoc, String truststorePW)
        throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
               URISyntaxException, KeyManagementException {
      KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
      try (FileInputStream instream =
               new FileInputStream(new File(getClass().getResource(truststoreLoc).toURI()))) {
        trustStore.load(instream, truststorePW.toCharArray());
      }
      HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
      SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, null).build();
      return new SSLConnectionSocketFactory(sslcontext, hostnameVerifier);
    }

    private CredentialsProvider initializeBasicAuthentication(HttpHost targetHost, String user,
                                                              String password) {
      CredentialsProvider credsProvider = new BasicCredentialsProvider();
      credsProvider.setCredentials(
          new AuthScope(targetHost.getHostName(), targetHost.getPort()),
          new UsernamePasswordCredentials(user, password));
      return credsProvider;
    }
  }
}
{code}

The `BindException` occurs if SSL is enabled and either a currently commented endpoint definition or the swagger api configuration is reenabled. As long as there is only one rest endpoint available or the SSL configuration disabled (including switching the test client to plain HTTP; the other endpoints can be enabled as well) the test succeeds


> Configuring SSL via endpointProperty in restConfiguration for an Undertow component will cause a BindException for multiple endpoints
> -------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-11998
>                 URL: https://issues.apache.org/jira/browse/CAMEL-11998
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-undertow
>    Affects Versions: 2.19.0, 2.20.0
>         Environment: MacBookPro 15' with Mac OS X (10.12.6), Oracle Java 1.8.144
>            Reporter: Roman Vottner
>
> On recreating the test case depicted in https://github.com/apache/camel/blob/master/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestApiUndertowTest.java and activating SSL configuration, I noticed that multiple endpoints exposed will configure multiple Undertow consumers which all try to bind onto the same port and thus throw a BindException.
> The full Test-Code looks as follows:
> {code:java}
> import static org.hamcrest.CoreMatchers.equalTo;
> import static org.hamcrest.CoreMatchers.is;
> import at.rovo.utils.TestHttpClient;
> import io.undertow.server.HttpServerExchange;
> import java.net.InetSocketAddress;
> import java.net.URL;
> import java.util.ArrayList;
> import java.util.List;
> import java.util.Map;
> import javax.annotation.Resource;
> import javax.net.ssl.SSLSession;
> import org.apache.camel.CamelContext;
> import org.apache.camel.Exchange;
> import org.apache.camel.builder.RouteBuilder;
> import org.apache.camel.component.properties.PropertiesComponent;
> import org.apache.camel.component.undertow.RestUndertowHttpBinding;
> import org.apache.camel.component.undertow.UndertowHttpBinding;
> import org.apache.camel.model.rest.RestParamType;
> import org.apache.camel.spring.javaconfig.CamelConfiguration;
> import org.apache.camel.test.spring.CamelSpringRunner;
> import org.apache.camel.test.spring.CamelTestContextBootstrapper;
> import org.apache.camel.util.jsse.KeyManagersParameters;
> import org.apache.camel.util.jsse.KeyStoreParameters;
> import org.apache.camel.util.jsse.SSLContextParameters;
> import org.apache.camel.util.jsse.TrustManagersParameters;
> import org.junit.Assert;
> import org.junit.Test;
> import org.junit.runner.RunWith;
> import org.springframework.context.annotation.Bean;
> import org.springframework.context.annotation.Configuration;
> import org.springframework.context.annotation.PropertySource;
> import org.springframework.core.env.Environment;
> import org.springframework.test.context.BootstrapWith;
> import org.springframework.test.context.ContextConfiguration;
> import org.springframework.test.context.support.AnnotationConfigContextLoader;
> @RunWith(CamelSpringRunner.class)
> @BootstrapWith(CamelTestContextBootstrapper.class)
> @ContextConfiguration(loader = AnnotationConfigContextLoader.class,
>     classes = {UndertowRouteTest.ContextConfig.class})
> public class UndertowRouteTest {
>   private static final String REMOTE_IP = "REMOTE_IP";
>   private static final String REMOTE_PORT = "REMOTE_PORT";
>   private static final String USER_AGENT = "USER_AGENT";
>   private static final String TLS_PROTOCOL_VERSION = "TLS_PROTOCOL_VERSION";
>   private static final String TLS_CIPHER_SUITE = "TLS_CIPHER_SUITE";
>   @Configuration
>   @PropertySource({"classpath:test.properties"})
>   public static class ContextConfig extends CamelConfiguration {
>     @Resource
>     private Environment env;
>     @Bean(name = "undertowHttpBinding")
>     public UndertowHttpBinding undertowHttpBinding() {
>       return new IPResolvableRestUndertowHttpBinding();
>     }
>     public static class IPResolvableRestUndertowHttpBinding extends RestUndertowHttpBinding {
>       @Override
>       public void populateCamelHeaders(HttpServerExchange httpExchange, Map<String, Object> headersMap, Exchange exchange) throws Exception {
>         super.populateCamelHeaders(httpExchange, headersMap, exchange);
>         InetSocketAddress peer = httpExchange.getSourceAddress();
>         headersMap.put(REMOTE_IP, peer.getAddress().getHostAddress());
>         headersMap.put(REMOTE_PORT, peer.getPort());
>         for (String key : headersMap.keySet()) {
>           if (key.toLowerCase().equals("user-agent")) {
>             headersMap.put(USER_AGENT, headersMap.get(key));
>           }
>         }
>         if (null != httpExchange.getConnection().getSslSessionInfo()
>             && null != httpExchange.getConnection().getSslSessionInfo().getSSLSession()) {
>           SSLSession sslSession = httpExchange.getConnection().getSslSessionInfo().getSSLSession();
>           exchange.getIn().setHeader(TLS_PROTOCOL_VERSION, sslSession.getProtocol());
>           exchange.getIn().setHeader(TLS_CIPHER_SUITE, sslSession.getCipherSuite());
>         }
>       }
>     }
>     @Bean(name = "sslContextParameters")
>     public SSLContextParameters sslContextParameters() {
>       String keyStore = env.getProperty("ssl.keyStore.resource");
>       URL keyStoreUrl = this.getClass().getResource(keyStore);
>       KeyStoreParameters ksp = new KeyStoreParameters();
>       ksp.setResource(keyStoreUrl.getPath());
>       ksp.setPassword(env.getProperty("ssl.keyStore.password"));
>       KeyManagersParameters kmp = new KeyManagersParameters();
>       kmp.setKeyStore(ksp);
>       kmp.setKeyPassword(env.getProperty("ssl.key.password"));
>       String trustStore = env.getProperty("ssl.trustStore.resource");
>       URL trustStoreUrl = this.getClass().getResource(trustStore);
>       KeyStoreParameters tsp = new KeyStoreParameters();
>       tsp.setResource(trustStoreUrl.getPath());
>       tsp.setPassword(env.getProperty("ssl.trustStore.password"));
>       TrustManagersParameters tmp = new TrustManagersParameters();
>       tmp.setKeyStore(tsp);
>       SSLContextParameters scp = new SSLContextParameters();
>       scp.setKeyManagers(kmp);
>       scp.setTrustManagers(tmp);
>       return scp;
>     }
>     @Override
>     public void setupCamelContext(CamelContext camelContext) throws Exception {
>       super.setupCamelContext(camelContext);
>       PropertiesComponent pc =
>           new PropertiesComponent("classpath:" + env.getProperty("propertyfile"));
>       camelContext.addComponent("properties", pc);
>     }
>     @Override
>     public List<RouteBuilder> routes() {
>       final List<RouteBuilder> routes = new ArrayList<>();
>       routes.add(new RouteBuilder() {
>         @Override
>         public void configure() throws Exception {
>           restConfiguration()
>               .component("undertow")
>               .host("localhost")
>               .port(8383)
>               .endpointProperty("sslContextParameters", "#sslContextParameters")
>               .endpointProperty("undertowHttpBinding", "#undertowHttpBinding")
> //              .apiContextPath("/api-doc")
> //              .apiProperty("cors", "true")
> //              .apiProperty("api.title", "The hello rest thing")
> //              .apiProperty("api.version", "1.2.3")
>               ;
>           rest("/hello").consumes("application/json").produces("application/json")
>               .get("/hi/{name}").description("Saying hi")
>                 .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
>                 .to("log:hi")
> //              .get("/bye/{name}").description("Saying bye")
> //                .param().name("name").type(RestParamType.path).dataType("string").description("Who is it").endParam()
> //                .responseMessage().code(200).message("A reply message").endResponseMessage()
> //                .to("log:bye")
> //              .post("/bye").description("To update the greeting message").consumes("application/xml").produces("application/xml")
> //                .param().name("greeting").type(RestParamType.body).dataType("string").description("Message to use as greeting").endParam()
> //                .to("log:bye")
>           ;
>         }
>       });
>       return routes;
>     }
>   }
>   @Test
>   public void testRoute() throws Exception {
> //    TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("http://localhost:8383")
> //        .build();
>     TestHttpClient client = new TestHttpClient.TestHttpClientBuilder("https://localhost:8383")
>         .forSSL("/ssl/clientTrust.jks", "truststorePW")
>         .build();
>     try {
> //      TestHttpClient.Response response = client.sendGetRequest("/api-doc");
> //      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
> //          response.getStatusCode(), is(equalTo(200)));
>       TestHttpClient.Response response = client.sendGetRequest("/hello/hi/test");
>       Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
>           response.getStatusCode(), is(equalTo(200)));
> //      TestHttpClient.Response response = client.sendGetRequest("/hello/bye/test");
> //      Assert.assertThat("Unexpected status code received: " + response.getStatusCode(),
> //          response.getStatusCode(), is(equalTo(200)));
> //      Assert.assertThat(response.getContent(), is(equalTo("No response available")));
>     } catch (Exception e) {
>       Assert.fail("Should not have thrown an exception");
>     }
>   }
> }
> {code}
> with the TestHttpClient being convenience class that wrapps an Apache HTTP 4.5 client internally:
> {code:java}
> import java.io.BufferedReader;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.InputStreamReader;
> import java.lang.invoke.MethodHandles;
> import java.net.URISyntaxException;
> import java.security.KeyManagementException;
> import java.security.KeyStore;
> import java.security.KeyStoreException;
> import java.security.NoSuchAlgorithmException;
> import java.security.cert.CertificateException;
> import java.util.HashMap;
> import java.util.Map;
> import javax.net.ssl.HostnameVerifier;
> import javax.net.ssl.SSLContext;
> import org.apache.commons.codec.binary.Base64;
> import org.apache.commons.lang.StringUtils;
> import org.apache.http.Header;
> import org.apache.http.HttpEntity;
> import org.apache.http.HttpHost;
> import org.apache.http.auth.AuthScope;
> import org.apache.http.auth.UsernamePasswordCredentials;
> import org.apache.http.client.AuthCache;
> import org.apache.http.client.CredentialsProvider;
> import org.apache.http.client.methods.CloseableHttpResponse;
> import org.apache.http.client.methods.HttpDelete;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.methods.HttpPost;
> import org.apache.http.client.methods.HttpPut;
> import org.apache.http.client.protocol.HttpClientContext;
> import org.apache.http.conn.ssl.DefaultHostnameVerifier;
> import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
> import org.apache.http.entity.ContentType;
> import org.apache.http.entity.InputStreamEntity;
> import org.apache.http.entity.StringEntity;
> import org.apache.http.impl.auth.BasicScheme;
> import org.apache.http.impl.client.BasicAuthCache;
> import org.apache.http.impl.client.BasicCredentialsProvider;
> import org.apache.http.impl.client.CloseableHttpClient;
> import org.apache.http.impl.client.HttpClientBuilder;
> import org.apache.http.impl.client.HttpClients;
> import org.apache.http.ssl.SSLContexts;
> import org.apache.http.util.EntityUtils;
> import org.json.JSONException;
> import org.json.JSONObject;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> /**
>  * This Apache HttpClient 4.5 based test client provides convenience methods for dealing with HTTP
>  * requests and responses sent and received by invoked endpoints.
>  */
> @SuppressWarnings("unused")
> public class TestHttpClient {
>   private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
>   private CloseableHttpClient httpClient;
>   private String baseUrl;
>   private HttpClientContext context = null;
>   private TestHttpClient(CloseableHttpClient httpClient, HttpClientContext context, String baseUrl) {
>     this.httpClient = httpClient;
>     this.baseUrl = baseUrl;
>     this.context = context;
>   }
>   public Response sendGetRequest(String path) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpGet httpGet = new HttpGet(baseUrl + path);
>     LOG.debug("Sending GET request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendGetRequest(String path, Map<String, String> headers) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpGet httpGet = new HttpGet(baseUrl + path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpGet.setHeader(header, headers.get(header));
>       }
>     }
>     LOG.debug("Sending GET request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendDeleteRequest(String path) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpDelete httpDelete = new HttpDelete(baseUrl + path);
>     LOG.debug("Sending DELETE request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendDeleteRequest(String path, Map<String, String> headers) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpDelete httpDelete = new HttpDelete(baseUrl + path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpDelete.setHeader(header, headers.get(header));
>       }
>     }
>     LOG.debug("Sending DELETE request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpDelete, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPostRequest(String path, String payload, String contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPost httpPost = new HttpPost(baseUrl+path);
>     StringEntity stringPayload = new StringEntity(payload, contentType);
>     httpPost.setEntity(stringPayload);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPostRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPost httpPost = new HttpPost(baseUrl+path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpPost.setHeader(header, headers.get(header));
>       }
>     }
>     StringEntity stringPayload = new StringEntity(payload, contentType);
>     httpPost.setEntity(stringPayload);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPostRequest(String path, InputStream payload, ContentType contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPost httpPost = new HttpPost(baseUrl+path);
>     InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
>     httpPost.setEntity(payloadEntity);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPostRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPost httpPost = new HttpPost(baseUrl+path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpPost.setHeader(header, headers.get(header));
>       }
>     }
>     InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
>     httpPost.setEntity(payloadEntity);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPost, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPutRequest(String path, String payload, String contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPut httpPut = new HttpPut(baseUrl+path);
>     StringEntity stringPayload = new StringEntity(payload, contentType);
>     httpPut.setEntity(stringPayload);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPutRequest(String path, Map<String, String> headers, String payload, String contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPut httpPut = new HttpPut(baseUrl+path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpPut.setHeader(header, headers.get(header));
>       }
>     }
>     StringEntity stringPayload = new StringEntity(payload, contentType);
>     httpPut.setEntity(stringPayload);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPutRequest(String path, InputStream payload, ContentType contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPut httpPut = new HttpPut(baseUrl+path);
>     InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
>     httpPut.setEntity(payloadEntity);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
>       return processResponse(response);
>     }
>   }
>   public Response sendPutRequest(String path, Map<String, String> headers, InputStream payload, ContentType contentType) throws IOException {
>     if (StringUtils.isBlank(path) || !path.startsWith("/")) {
>       throw new IllegalArgumentException("Invalid path provided");
>     }
>     HttpPut httpPut = new HttpPut(baseUrl+path);
>     if (headers != null) {
>       for (String header : headers.keySet()) {
>         httpPut.setHeader(header, headers.get(header));
>       }
>     }
>     InputStreamEntity payloadEntity = new InputStreamEntity(payload, contentType);
>     httpPut.setEntity(payloadEntity);
>     LOG.debug("Sending POST request to {}", baseUrl + path);
>     try (CloseableHttpResponse response = httpClient.execute(httpPut, context)) {
>       return processResponse(response);
>     }
>   }
>   private Response processResponse(CloseableHttpResponse response) throws IOException{
>     HttpEntity entity = response.getEntity();
>     Response _response = new Response();
>     _response.setStatusCode(response.getStatusLine().getStatusCode());
>     if (entity != null) {
>       _response.setContentLength(entity.getContentLength());
>     } else {
>       _response.setContentLength(0L);
>     }
>     Map<String, String> headers = new HashMap<>();
>     for (Header header : response.getAllHeaders()) {
>       headers.put(header.getName(), header.getValue());
>     }
>     _response.setHeaders(headers);
>     if (response.getStatusLine().getStatusCode() != 204 && entity != null) {
>       BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()));
>       StringBuilder sb = new StringBuilder();
>       String line;
>       while ((line = reader.readLine()) != null) {
>         sb.append(line);
>       }
>       EntityUtils.consume(entity);
>       LOG.debug("Received response: {}", sb.toString());
>       _response.setContent(sb.toString());
>       if (_response.getContentLength() <= 0) {
>         _response.setContentLength(sb.length());
>       }
>     }
>     return _response;
>   }
>   public static String decodeBase64(String base64) {
>     return new String(Base64.decodeBase64(base64));
>   }
>   public static JSONObject convertResponseToJson(String response) throws JSONException {
>     return new JSONObject(decodeBase64(response));
>   }
>   public class Response {
>     private int statucCode = 400;
>     private String content = null;
>     private Map<String, String> headers = null;
>     private long contentLength = 0L;
>     Response() {
>     }
>     public int getStatusCode() {
>       return statucCode;
>     }
>     void setStatusCode(int statucCode) {
>       this.statucCode = statucCode;
>     }
>     public String getContent() {
>       return content;
>     }
>     void setContent(String content) {
>       this.content = content;
>     }
>     public Map<String, String> getHeaders() {
>       return headers;
>     }
>     void setHeaders(Map<String, String> headers) {
>       this.headers = headers;
>     }
>     public long getContentLength() {
>       return this.contentLength;
>     }
>     void setContentLength(long contentLength) {
>       this.contentLength = contentLength;
>     }
>   }
>   /**
>    * A test HTTP client based on Apache commons HTTP client
>    */
>   public static class TestHttpClientBuilder {
>     private String protocol;
>     private String host;
>     private int port;
>     private String truststoreLoc = null;
>     private String truststorePW = null;
>     private String user = null;
>     private String password = null;
>     public TestHttpClientBuilder(String protocol, String host, int port) {
>       this.protocol = protocol;
>       this.host = host;
>       this.port = port;
>     }
>     public TestHttpClientBuilder(String url) {
>       if (StringUtils.isBlank(url)) {
>         throw new IllegalArgumentException("Invalid URL provided");
>       }
>       // separate the protocol - https://localhost:80/... --> protocol: https
>       protocol = url.substring(0, url.indexOf(":"));
>       if (StringUtils.isBlank(protocol) || !(protocol.equals("http") || protocol.equals("https"))) {
>         throw new IllegalArgumentException("Invalid protocol specified: " + protocol);
>       }
>       int hostStart = protocol.length() + "://".length();
>       // check if there is a path delimiter defined
>       String hostPort;
>       int hostPortEndPos = url.indexOf("/", hostStart);
>       if (hostPortEndPos == -1) { // https://localhost:80 -> hostPort: localhost:80
>         hostPort = url.substring(hostStart);
>       } else { // https://localhost:80/... -> hostPort: localhost:80
>         hostPort = url.substring(hostStart, hostPortEndPos);
>       }
>       // split host and port
>       int hostEnd = hostPort.indexOf(":", hostStart);
>       if (hostEnd == -1) { // https://localhost --> host: localhost
>         host = hostPort;
>         if ("http".equals(protocol)) {
>           port = 80;
>         } else if ("https".equals(protocol)) {
>           port = 443;
>         }
>       } else { // https://localhost:80 --> host: localhost; port: 80
>         host = hostPort.substring(0, hostEnd);
>         port = Integer.parseInt(hostPort.substring(hostEnd + 1));
>       }
>     }
>     public TestHttpClientBuilder forSSL(String truststoreLoc, String truststorePW) {
>       this.truststoreLoc = truststoreLoc;
>       this.truststorePW = truststorePW;
>       return this;
>     }
>     public TestHttpClientBuilder withBasicAuth(String user, String password) {
>       this.user = user;
>       this.password = password;
>       return this;
>     }
>     public TestHttpClient build() throws TestHttpClientBuilderException {
>       HttpHost targetHost = new HttpHost(host, port, protocol);
>       HttpClientBuilder httpClientBuilder = HttpClients.custom();
>       if (StringUtils.isNotBlank(truststoreLoc) && StringUtils.isNotBlank(truststorePW)) {
>         if ("http".equals(protocol)) {
>           throw new TestHttpClientBuilderException(
>               "Cannot setup SSL for HTTP protocol. Please change the protocol to HTTPS!");
>         }
>         try {
>           httpClientBuilder.setSSLSocketFactory(initializeSSL(truststoreLoc, truststorePW));
>         } catch (Exception ex) {
>           throw new TestHttpClientBuilderException(ex);
>         }
>       }
>       HttpClientContext context = HttpClientContext.create();
>       if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password)) {
>         CredentialsProvider credsProvider = initializeBasicAuthentication(targetHost, user,
>                                                                           password);
>         httpClientBuilder
>             .setDefaultCredentialsProvider(credsProvider);
>         AuthCache authCache = new BasicAuthCache();
>         BasicScheme basicAuth = new BasicScheme();
>         authCache.put(targetHost, basicAuth);
>         context.setCredentialsProvider(credsProvider);
>         context.setAuthCache(authCache);
>       }
>       return new TestHttpClient(httpClientBuilder.build(), context, protocol+"://"+host+":"+port);
>     }
>     private SSLConnectionSocketFactory initializeSSL(String truststoreLoc, String truststorePW)
>         throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
>                URISyntaxException, KeyManagementException {
>       KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
>       try (FileInputStream instream =
>                new FileInputStream(new File(getClass().getResource(truststoreLoc).toURI()))) {
>         trustStore.load(instream, truststorePW.toCharArray());
>       }
>       HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
>       SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, null).build();
>       return new SSLConnectionSocketFactory(sslcontext, hostnameVerifier);
>     }
>     private CredentialsProvider initializeBasicAuthentication(HttpHost targetHost, String user,
>                                                               String password) {
>       CredentialsProvider credsProvider = new BasicCredentialsProvider();
>       credsProvider.setCredentials(
>           new AuthScope(targetHost.getHostName(), targetHost.getPort()),
>           new UsernamePasswordCredentials(user, password));
>       return credsProvider;
>     }
>   }
> }
> {code}
> The `BindException` occurs if SSL is enabled and either a currently commented endpoint definition or the swagger api configuration is reenabled. As long as there is only one rest endpoint available or the SSL configuration disabled (including switching the test client to plain HTTP; the other endpoints can be enabled as well) the test succeeds



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)