[
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)