Skip to content

Enh pooling #2528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 28, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -50,6 +50,8 @@
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.restcomm.connect.commons.common.http.CustomHttpClientBuilder;
import scala.concurrent.ExecutionContext;

/**
@@ -72,6 +74,7 @@ public Bootstrapper() {

@Override
public void destroy() {
CustomHttpClientBuilder.stopDefaultClient();
system.shutdown();
system.awaitTermination();
}
@@ -363,6 +366,9 @@ public void servletInitialized(SipServletContextEvent event) {
logger.error("Monitoring Service is null");
}

CloseableHttpClient buildDefaultClient = CustomHttpClientBuilder.buildDefaultClient(RestcommConfiguration.getInstance().getMain());
context.setAttribute(CustomHttpClientBuilder.class.getName(), buildDefaultClient);

//Initialize Extensions
Configuration extensionConfiguration = null;
try {
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.HttpClientUtils;
import org.restcomm.connect.commons.common.http.CustomHttpClientBuilder;
import org.restcomm.connect.commons.configuration.RestcommConfiguration;

@@ -30,7 +29,7 @@ public URI download(URI requestUri, File pathToSave) throws IOException, URISynt
try {
if (requestUri.getScheme().equalsIgnoreCase("https")) {
//Handle the HTTPS URIs
client = CustomHttpClientBuilder.build(RestcommConfiguration.getInstance().getMain());
client = CustomHttpClientBuilder.buildDefaultClient(RestcommConfiguration.getInstance().getMain());
/*URI result = new URIBuilder()
.setScheme(requestUri.getScheme())
.setHost(requestUri.getHost())
@@ -76,10 +75,6 @@ public URI download(URI requestUri, File pathToSave) throws IOException, URISynt
((CloseableHttpResponse) httpResponse).close();
httpResponse = null;
}
if (client != null) {
HttpClientUtils.closeQuietly(client);
client = null;
}
}

return pathToSave.toURI();
Original file line number Diff line number Diff line change
@@ -17,26 +17,35 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

package org.restcomm.connect.commons.common.http;

import org.apache.http.client.HttpClient;
import java.net.InetSocketAddress;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.restcomm.connect.commons.HttpConnector;
import org.restcomm.connect.commons.HttpConnectorList;
import org.restcomm.connect.commons.configuration.sets.MainConfigurationSet;
import org.restcomm.connect.commons.util.UriUtils;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpHost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.util.PublicSuffixMatcher;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;

/**
*
@@ -46,44 +55,90 @@
public class CustomHttpClientBuilder {

private CustomHttpClientBuilder() {
// TODO Auto-generated constructor stub
}

private static CloseableHttpClient defaultClient = null;

public static synchronized void stopDefaultClient() {
if (defaultClient != null) {
HttpClientUtils.closeQuietly(defaultClient);
defaultClient = null;
}
}

public static synchronized CloseableHttpClient buildDefaultClient(MainConfigurationSet config) {
if (defaultClient == null) {
defaultClient = build(config);
}
return defaultClient;
}

public static HttpClient build(MainConfigurationSet config) {
public static CloseableHttpClient build(MainConfigurationSet config) {
int timeoutConnection = config.getResponseTimeout();
return build(config, timeoutConnection);
}

public static HttpClient build(MainConfigurationSet config, int timeout) {
SslMode mode = config.getSslMode();
public static CloseableHttpClient build(MainConfigurationSet config, int timeout) {
HttpClientBuilder builder = HttpClients.custom();

RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setConnectionRequestTimeout(config.getDefaultHttpConnectionRequestTimeout())
.setSocketTimeout(timeout)
.setCookieSpec(CookieSpecs.STANDARD).build();
if ( mode == SslMode.strict ) {
SSLConnectionSocketFactory sslsf = null;
try {
builder.setDefaultRequestConfig(requestConfig);

SslMode mode = config.getSslMode();
SSLConnectionSocketFactory sslsf = null;
if (mode == SslMode.strict) {
sslsf = buildStrictFactory();
} else {
sslsf = buildAllowallFactory();
}
builder.setSSLSocketFactory(sslsf);

builder.setMaxConnPerRoute(config.getDefaultHttpMaxConnsPerRoute());
builder.setMaxConnTotal(config.getDefaultHttpMaxConns());
builder.setConnectionTimeToLive(config.getDefaultHttpTTL(), TimeUnit.MILLISECONDS);
if (config.getDefaultHttpRoutes() != null
&& config.getDefaultHttpRoutes().size() > 0) {
if (sslsf == null) {
//strict mode with no system https properties
//taken from apache buider code
PublicSuffixMatcher publicSuffixMatcherCopy = PublicSuffixMatcherLoader.getDefault();
DefaultHostnameVerifier hostnameVerifierCopy = new DefaultHostnameVerifier(publicSuffixMatcherCopy);
sslsf = new SSLConnectionSocketFactory(
SSLContextBuilder.create().build(),
getSSLPrototocolsFromSystemProperties(),
null,
// new String[]{"TLS_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException("Error creating HttpClient", e);
SSLContexts.createDefault(),
hostnameVerifierCopy);
}
return HttpClients.custom().setDefaultRequestConfig(requestConfig).setSSLSocketFactory(sslsf).build();
} else {
return buildAllowallClient(requestConfig);
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslsf)
.build();
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
reg,
null,
null,
null,
config.getDefaultHttpTTL(),
TimeUnit.MILLISECONDS);
//ensure conn configuration is set again for new conn manager
poolingmgr.setMaxTotal(config.getDefaultHttpMaxConns());
poolingmgr.setDefaultMaxPerRoute(config.getDefaultHttpMaxConnsPerRoute());
for (InetSocketAddress addr : config.getDefaultHttpRoutes().keySet()) {
HttpRoute r = new HttpRoute(new HttpHost(addr.getHostName(), addr.getPort()));
poolingmgr.setMaxPerRoute(r, config.getDefaultHttpRoutes().get(addr));
}
builder.setConnectionManager(poolingmgr);
}
return builder.build();
}

private static String[] getSSLPrototocolsFromSystemProperties() {
String protocols = System.getProperty("jdk.tls.client.protocols");
if (protocols == null)
if (protocols == null) {
protocols = System.getProperty("https.protocols");
}

if (protocols != null) {
String[] protocolsArray = protocols.split(",");
@@ -92,39 +147,34 @@ private static String[] getSSLPrototocolsFromSystemProperties() {
return null;
}

private static HttpClient buildAllowallClient(RequestConfig requestConfig) {
HttpConnectorList httpConnectorList = UriUtils.getHttpConnectorList();
HttpClient httpClient = null;
//Enable SSL only if we have HTTPS connector
List<HttpConnector> connectors = httpConnectorList.getConnectors();
Iterator<HttpConnector> iterator = connectors.iterator();
while (iterator.hasNext()) {
HttpConnector connector = iterator.next();
if (connector.isSecure()) {
SSLConnectionSocketFactory sslsf;
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
// sslsf = new SSLConnectionSocketFactory(builder.build());

sslsf = new SSLConnectionSocketFactory(
builder.build(),
getSSLPrototocolsFromSystemProperties(),
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());


httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setSSLSocketFactory(sslsf).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
throw new RuntimeException("Error creating HttpClient", e);
}
break;
}
}
if (httpClient == null) {
httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
private static SSLConnectionSocketFactory buildStrictFactory() {
try {
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
SSLContextBuilder.create().build(),
getSSLPrototocolsFromSystemProperties(),
null,
// new String[]{"TLS_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
return sslsf;
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException("Error creating HttpClient", e);
}
}

private static SSLConnectionSocketFactory buildAllowallFactory() {
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());

return httpClient;
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build(),
getSSLPrototocolsFromSystemProperties(),
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());

return sslsf;
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
throw new RuntimeException("Error creating HttpClient", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@

package org.restcomm.connect.commons.configuration.sets;

import java.net.InetSocketAddress;
import java.util.Map;
import org.restcomm.connect.commons.common.http.SslMode;

/**
@@ -30,6 +32,16 @@ public interface MainConfigurationSet {

int getResponseTimeout();

Integer getDefaultHttpConnectionRequestTimeout();

Integer getDefaultHttpMaxConns();

Integer getDefaultHttpMaxConnsPerRoute();

Integer getDefaultHttpTTL();

Map<InetSocketAddress,Integer> getDefaultHttpRoutes();

boolean isUseHostnameToResolveRelativeUrls();

String getHostname();
Original file line number Diff line number Diff line change
@@ -17,9 +17,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

package org.restcomm.connect.commons.configuration.sets.impl;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import org.restcomm.connect.commons.annotations.concurrency.Immutable;
import org.restcomm.connect.commons.configuration.sets.MainConfigurationSet;
import org.restcomm.connect.commons.configuration.sources.ConfigurationSource;
@@ -30,8 +32,8 @@
* Provides a typed interface to a set of configuration options retrieved from a
* configuration source.
*
* To add a new option in this set define its name as static fields and then initialize,
* validate it in the constructor.
* To add a new option in this set define its name as static fields and then
* initialize, validate it in the constructor.
*
* @author orestis.tsakiridis@telestax.com (Orestis Tsakiridis)
*
@@ -41,9 +43,21 @@ public class MainConfigurationSetImpl extends ConfigurationSet implements MainCo

private static final String SSL_MODE_KEY = "http-client.ssl-mode";
private static final String HTTP_RESPONSE_TIMEOUT = "http-client.response-timeout";
private static final String HTTP_CONNECTION_REQUEST_TIMEOUT = "http-client.connection-request-timeout";
private static final String HTTP_MAX_CONN_TOTAL = "http-client.max-conn-total";
private static final String HTTP_MAX_CONN_PER_ROUTE = "http-client.max-conn-per-route";
private static final String HTTP_CONNECTION_TIME_TO_LIVE = "http-client.connection-time-to-live";
private static final String HTTP_ROUTES_HOST = "http-client.routes-host";
private static final String HTTP_ROUTES_PORT = "http-client.routes-port";
private static final String HTTP_ROUTES_CONN = "http-client.routes-conn";
private static final SslMode SSL_MODE_DEFAULT = SslMode.strict;
private SslMode sslMode;
private int responseTimeout;
private Integer connectionRequestTimeout;
private Integer defaultHttpMaxConns;
private Integer defaultHttpMaxConnsPerRoute;
private Integer defaultHttpTTL;
private Map<InetSocketAddress, Integer> defaultHttpRoutes = new HashMap();
private static final String USE_HOSTNAME_TO_RESOLVE_RELATIVE_URL_KEY = "http-client.use-hostname-to-resolve-relative-url";
private static final String HOSTNAME_TO_USE_FOR_RELATIVE_URLS_KEY = "http-client.hostname";
private static final boolean RESOLVE_RELATIVE_URL_WITH_HOSTNAME_DEFAULT = true;
@@ -62,21 +76,63 @@ public MainConfigurationSetImpl(ConfigurationSource source) {
boolean resolveRelativeUrlWithHostname;
String resolveRelativeUrlHostname;
boolean bypassLb = false;
int timeout = 5000;

try {
timeout = Integer.parseInt(source.getProperty(HTTP_RESPONSE_TIMEOUT));
responseTimeout = Integer.parseInt(source.getProperty(HTTP_RESPONSE_TIMEOUT, "5000"));
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + HTTP_RESPONSE_TIMEOUT + "' configuration setting", e);
}
try {
connectionRequestTimeout = Integer.parseInt(source.getProperty(HTTP_CONNECTION_REQUEST_TIMEOUT,String.valueOf(responseTimeout)));
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + HTTP_RESPONSE_TIMEOUT + "' configuration setting", e);
}
try {
defaultHttpMaxConns = Integer.parseInt(source.getProperty(HTTP_MAX_CONN_TOTAL, "2000"));
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + HTTP_MAX_CONN_TOTAL + "' configuration setting", e);
}
try {
defaultHttpMaxConnsPerRoute = Integer.parseInt(source.getProperty(HTTP_MAX_CONN_PER_ROUTE, "100"));
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + HTTP_MAX_CONN_PER_ROUTE + "' configuration setting", e);
}
try {
defaultHttpTTL = Integer.parseInt(source.getProperty(HTTP_CONNECTION_TIME_TO_LIVE, "30000"));
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + HTTP_CONNECTION_TIME_TO_LIVE + "' configuration setting", e);
}

try {
String delimiter = ",";
String routesHostProp = source.getProperty(HTTP_ROUTES_HOST, "");
if (!routesHostProp.isEmpty()) {
String[] routesHostList = routesHostProp.split(delimiter);
String routesPortProp = source.getProperty(HTTP_ROUTES_PORT, "");
String[] routesPortList = routesPortProp.split(delimiter);
String routesConnProp = source.getProperty(HTTP_ROUTES_CONN, "");
String[] routesConnList = routesConnProp.split(delimiter);
for (int i = 0; i < routesHostList.length; i++) {
Integer port = Integer.valueOf(routesPortList[i]);
Integer conn = Integer.valueOf(routesConnList[i]);
InetSocketAddress addr = new InetSocketAddress(routesHostList[i], port);
defaultHttpRoutes.put(addr, conn);
}
}

} catch (Throwable e) {//to catch array index out of bounds
throw new RuntimeException("Error initializing '" + HTTP_ROUTES_CONN + "' configuration setting", e);
}

// http-client.ssl-mode
try {
sslMode = SSL_MODE_DEFAULT;
String sslModeRaw = source.getProperty(SSL_MODE_KEY);
if ( ! StringUtils.isEmpty(sslModeRaw) )
if (!StringUtils.isEmpty(sslModeRaw)) {
sslMode = SslMode.valueOf(sslModeRaw);
}
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + SSL_MODE_KEY + "' configuration setting", e);
throw new RuntimeException("Error initializing '" + SSL_MODE_KEY + "' configuration setting", e);
}
this.sslMode = sslMode;

@@ -90,7 +146,6 @@ public MainConfigurationSetImpl(ConfigurationSource source) {
} catch (Exception e) {
throw new RuntimeException("Error initializing '" + USE_HOSTNAME_TO_RESOLVE_RELATIVE_URL_KEY + "' configuration setting", e);
}
this.responseTimeout = timeout;
this.useHostnameToResolveRelativeUrls = resolveRelativeUrlWithHostname;
this.hostname = resolveRelativeUrlHostname;
bypassLbForClients = bypassLb;
@@ -130,15 +185,19 @@ public String getHostname() {
}

@Override
public boolean getBypassLbForClients() { return bypassLbForClients; }
public boolean getBypassLbForClients() {
return bypassLbForClients;
}

@Override
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}

@Override
public String getInstanceId() { return this.instanceId; }
public String getInstanceId() {
return this.instanceId;
}

public void setSslMode(SslMode sslMode) {
this.sslMode = sslMode;
@@ -165,7 +224,32 @@ public String getApiVersion() {
}

@Override
public int getRecordingMaxDelay () {
public int getRecordingMaxDelay() {
return recordingMaxDelay;
}

@Override
public Integer getDefaultHttpMaxConns() {
return defaultHttpMaxConns;
}

@Override
public Integer getDefaultHttpMaxConnsPerRoute() {
return defaultHttpMaxConnsPerRoute;
}

@Override
public Integer getDefaultHttpTTL() {
return defaultHttpTTL;
}

@Override
public Map<InetSocketAddress, Integer> getDefaultHttpRoutes() {
return defaultHttpRoutes;
}

@Override
public Integer getDefaultHttpConnectionRequestTimeout() {
return connectionRequestTimeout;
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package org.restcomm.connect.commons.configuration;


import java.net.InetSocketAddress;
import static org.junit.Assert.*;

import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.restcomm.connect.commons.configuration.RestcommConfiguration;
import org.restcomm.connect.commons.configuration.sets.MainConfigurationSet;
import org.restcomm.connect.commons.common.http.SslMode;

public class RestcommConfigurationTest {
private RestcommConfiguration conf;
private Configuration xml;
private XMLConfiguration xml;

public RestcommConfigurationTest() {
super();
@@ -27,7 +26,10 @@ public RestcommConfigurationTest() {
public void before() throws ConfigurationException, MalformedURLException {
URL url = this.getClass().getResource("/restcomm.xml");
// String relativePath = "../../../../../../../../restcomm.application/src/main/webapp/WEB-INF/conf/restcomm.xml";
xml = new XMLConfiguration(url);
xml = new XMLConfiguration();
xml.setDelimiterParsingDisabled(true);
xml.setAttributeSplittingDisabled(true);
xml.load(url);
conf = new RestcommConfiguration(xml);
}

@@ -42,9 +44,17 @@ public void allConfiguraitonSetsAreAvailable() {
@Test
public void mainSetConfigurationOptionsAreValid() {
MainConfigurationSet main = conf.getMain();
assertTrue( main.getSslMode().equals(SslMode.strict));
assertTrue( main.getHostname().equals("127.0.0.1"));
assertTrue( main.isUseHostnameToResolveRelativeUrls() == true );
assertEquals(SslMode.strict, main.getSslMode());
assertEquals("127.0.0.1", main.getHostname());
assertTrue(main.isUseHostnameToResolveRelativeUrls());
assertEquals(Integer.valueOf(200), main.getDefaultHttpMaxConns());
assertEquals(Integer.valueOf(20), main.getDefaultHttpMaxConnsPerRoute());
assertEquals(Integer.valueOf(30000), main.getDefaultHttpTTL());
assertEquals(2, main.getDefaultHttpRoutes().size());
InetSocketAddress addr1= new InetSocketAddress("127.0.0.1", 8080);
assertEquals(Integer.valueOf(10), main.getDefaultHttpRoutes().get(addr1));
InetSocketAddress addr2= new InetSocketAddress("192.168.1.1", 80);
assertEquals(Integer.valueOf(60), main.getDefaultHttpRoutes().get(addr2));
}

@Test
7 changes: 7 additions & 0 deletions restcomm/restcomm.commons/src/test/resources/restcomm.xml
Original file line number Diff line number Diff line change
@@ -312,6 +312,13 @@
<use-hostname-to-resolve-relative-url>true</use-hostname-to-resolve-relative-url>
<!-- Optionally provide the hostname to be used, otherwise Java will try to get the hostname of the machine JVM is running -->
<hostname>127.0.0.1</hostname>

<max-conn-total>200</max-conn-total>
<max-conn-per-route>20</max-conn-per-route>
<connection-time-to-live>30000</connection-time-to-live>
<routes-host>127.0.0.1,192.168.1.1</routes-host>
<routes-port>8080,80</routes-port>
<routes-conn>10,60</routes-conn>
</http-client>

<!-- The SMS aggregator is responsible for the handling of SMS messages
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@
package org.restcomm.connect.dao.mybatis;

import static org.junit.Assert.assertTrue;

import static org.junit.Assert.assertEquals;

import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
@@ -42,11 +43,15 @@
import org.restcomm.connect.dao.entities.CallDetailRecordFilter;

import junit.framework.Assert;
import org.junit.Rule;
import org.junit.rules.TestName;

/**
* @author quintana.thomas@gmail.com (Thomas Quintana)
*/
public class CallDetailRecordsDaoTest extends DaoTest {
@Rule public TestName name = new TestName();

private static MybatisDaoManager manager;

public CallDetailRecordsDaoTest() {
@@ -55,7 +60,8 @@ public CallDetailRecordsDaoTest() {

@Before
public void before() throws Exception {
sandboxRoot = createTempDir("cdrTest");
//use testmethod name to further ensure uniqueness of tmp dir
sandboxRoot = createTempDir("cdrTest" + name.getMethodName());
String mybatisFilesPath = getClass().getResource("/callDetailRecordsDao").getFile();
setupSandbox(mybatisFilesPath, sandboxRoot);

@@ -364,11 +370,11 @@ public void testReadByEndTime() {
builder.setUri(url);
CallDetailRecord cdr = builder.build();
final CallDetailRecordsDao cdrs = manager.getCallDetailRecordsDao();
int beforeAdding = cdrs.getCallDetailRecordsByEndTime(now).size();
// Create a new CDR in the data store.
cdrs.addCallDetailRecord(cdr);
// Validate the results.
assertTrue(cdrs.getCallDetailRecordsByEndTime(now).size() == 1);
assertTrue(cdrs.getCallDetailRecordsByStarTimeAndEndTime(now).size() == 1);
// Validate the results, including the ones matching in the script(10)
assertEquals(beforeAdding + 1, cdrs.getCallDetailRecordsByEndTime(now).size());
// Delete the CDR.
cdrs.removeCallDetailRecord(sid);
// Validate that the CDRs were removed.
3 changes: 2 additions & 1 deletion restcomm/restcomm.dao/src/test/resources/mybatis.xml
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:./src/test/resources/data/restcomm;create=true;hsqldb.write_delay=false;shutdown=true"/>
<!--keep data in maven target so git ignores changes-->
<property name="url" value="jdbc:hsqldb:file:./target/test-classes/data/restcomm;create=true;hsqldb.write_delay=false;shutdown=true"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</dataSource>
20 changes: 10 additions & 10 deletions restcomm/restcomm.http/pom.xml
Original file line number Diff line number Diff line change
@@ -17,16 +17,6 @@

<dependencies>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
@@ -250,6 +240,16 @@
<version>2.0.99-beta</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>1.57</version>
<!-- Include this if you have dependency conflicts for Guava, Jetty, Jackson
or Apache HTTP Client -->
<classifier>standalone</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.restcomm</groupId>
<artifactId>restcomm-connect.extension.api</artifactId>
Original file line number Diff line number Diff line change
@@ -34,7 +34,6 @@
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.restcomm.connect.commons.common.http.CustomHttpClientBuilder;
@@ -53,6 +52,10 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

/**
* @author quintana.thomas@gmail.com (Thomas Quintana)
@@ -61,31 +64,39 @@ public final class Downloader extends RestcommUntypedActor {

public static final int LOGGED_RESPONSE_MAX_SIZE = 100;

private CloseableHttpClient client = null;

// Logger.
private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);

public Downloader () {
super();
client = (CloseableHttpClient) CustomHttpClientBuilder.buildDefaultClient(RestcommConfiguration.getInstance().getMain());
}


public HttpResponseDescriptor fetch (final HttpRequestDescriptor descriptor) throws IllegalArgumentException, IOException,
URISyntaxException, XMLStreamException {
int code = -1;
HttpRequest request = null;
CloseableHttpResponse response = null;
HttpRequestDescriptor temp = descriptor;
CloseableHttpClient client = null;
HttpResponseDescriptor responseDescriptor = null;
HttpResponseDescriptor rawResponseDescriptor = null;
try {
do {
client = (CloseableHttpClient) CustomHttpClientBuilder.build(RestcommConfiguration.getInstance().getMain());
// client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
// client.getParams().setParameter("http.protocol.content-charset", "UTF-8");
request = request(temp);
request.setHeader("http.protocol.content-charset", "UTF-8");

response = client.execute((HttpUriRequest) request);
if (descriptor.getTimeout() > 0){
HttpContext httpContext = new BasicHttpContext();
httpContext.setAttribute(HttpClientContext.REQUEST_CONFIG, RequestConfig.custom().
setConnectTimeout(descriptor.getTimeout()).
setSocketTimeout(descriptor.getTimeout()).
setConnectionRequestTimeout(descriptor.getTimeout()).build());
response = client.execute((HttpUriRequest) request, httpContext);
} else {
response = client.execute((HttpUriRequest) request);
}
code = response.getStatusLine().getStatusCode();
if (isRedirect(code)) {
final Header header = response.getFirstHeader(HttpHeaders.LOCATION);
@@ -128,9 +139,9 @@ public HttpResponseDescriptor fetch (final HttpRequestDescriptor descriptor) thr
logger.warning(String.format("Problem while trying to download RCML. URL: %s, Status: %s, Response: %s ", request.getRequestLine(), statusInfo, responseInfo));
throw e; // re-throw
} finally {
response.close();
HttpClientUtils.closeQuietly(client);
client = null;
if (response != null) {
response.close();
}
}
return responseDescriptor;
}
Original file line number Diff line number Diff line change
@@ -38,9 +38,13 @@ public final class HttpRequestDescriptor {
private final URI uri;
private final String method;
private final List<NameValuePair> parameters;
private final Integer timeout;

public HttpRequestDescriptor(final URI uri, final String method, final List<NameValuePair> parameters) {
public HttpRequestDescriptor(final URI uri, final String method,
final List<NameValuePair> parameters,
final Integer timeout) {
super();
this.timeout = timeout;
this.uri = base(uri);
this.method = method;
if (parameters != null) {
@@ -53,6 +57,11 @@ public HttpRequestDescriptor(final URI uri, final String method, final List<Name
final List<NameValuePair> other = URLEncodedUtils.parse(uri, "UTF-8");
parameters.addAll(other);
}

}

public HttpRequestDescriptor(final URI uri, final String method, final List<NameValuePair> parameters) {
this(uri,method,parameters, -1);
}

public HttpRequestDescriptor(final URI uri, final String method) throws UnsupportedEncodingException, URISyntaxException {
@@ -92,4 +101,9 @@ public String getParametersAsString() {
public URI getUri() {
return uri;
}

public Integer getTimeout() {
return timeout;
}

}
Original file line number Diff line number Diff line change
@@ -41,6 +41,10 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

/**
* Utility class that handles notification submission to rcmlserver (typically RVD)
@@ -87,10 +91,15 @@ public void transmitNotifications(List<JsonObject> notifications, String notifie
String json = gson.toJson(notifications);
request.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
Integer totalTimeout = rcmlserverConfig.getTimeout() + notifications.size() * rcmlserverConfig.getTimeoutPerNotification();
HttpClient httpClient = CustomHttpClientBuilder.build(mainConfig, totalTimeout);
HttpClient httpClient = CustomHttpClientBuilder.buildDefaultClient(mainConfig);
try {
logger.info("Will transmit a set of " + notifications.size() + " notifications and wait at most for " + totalTimeout);
HttpResponse response = httpClient.execute(request);
HttpContext httpContext = new BasicHttpContext();
httpContext.setAttribute(HttpClientContext.REQUEST_CONFIG, RequestConfig.custom().
setConnectTimeout(totalTimeout).
setSocketTimeout(totalTimeout).
setConnectionRequestTimeout(totalTimeout).build());
HttpResponse response = httpClient.execute(request, httpContext);
if (response.getStatusLine().getStatusCode() != 200) {
throw new RcmlserverNotifyError();
}
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@

package org.restcomm.connect.http;

import javax.ws.rs.core.MediaType;
import org.apache.commons.configuration.ConfigurationException;
import org.junit.Test;
import org.restcomm.connect.http.exceptions.NotAuthenticated;
@@ -45,6 +46,8 @@ public void requestMissingAuthorizationIsRejected() {
when(request.getHeader("Authorization")).thenReturn(null); // override Authorization header to null
AccountsEndpoint endpoint = new AccountsEndpoint(servletContext,request);
endpoint.init();
//use endpoint to cause the exception
endpoint.getAccounts(MediaType.TEXT_XML_TYPE);
}

}
Original file line number Diff line number Diff line change
@@ -23,53 +23,77 @@
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import com.github.tomakehurst.wiremock.junit.WireMockRule;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.http.conn.ConnectionPoolTimeoutException;

import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.restcomm.connect.commons.configuration.RestcommConfiguration;

import org.restcomm.connect.http.client.Downloader;
import org.restcomm.connect.http.client.DownloaderResponse;
import org.restcomm.connect.http.client.HttpRequestDescriptor;
import org.restcomm.connect.http.client.HttpResponseDescriptor;
import scala.concurrent.duration.FiniteDuration;

/**
* @author quintana.thomas@gmail.com (Thomas Quintana)
*/
public final class DownloaderTest {

private ActorSystem system;
private ActorRef downloader;

private static int MOCK_PORT = 8099;
//use localhost instead of 127.0.0.1 to match the route rule
private static String PATH = "http://localhost:" + MOCK_PORT + "/";

@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().bindAddress("127.0.0.1").port(MOCK_PORT));

public DownloaderTest() {
super();
}

@Before
public void before() throws Exception {
URL url = this.getClass().getResource("/restcomm.xml");
Configuration xml = new XMLConfiguration(url);
RestcommConfiguration.createOnce(xml);
system = ActorSystem.create();
downloader = system.actorOf(new Props(Downloader.class));
}

@After
public void after() throws Exception {
system.shutdown();
wireMockRule.resetRequests();
}

@Test
@Ignore
public void testGet() throws URISyntaxException, IOException {
stubFor(get(urlMatching("/testGet")).willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("expectedBody")));
new JavaTestKit(system) {
{
final ActorRef observer = getRef();
final URI uri = URI.create("http://www.restcomm.org");
final URI uri = URI.create(PATH + "testGet");
final String method = "GET";
final HttpRequestDescriptor request = new HttpRequestDescriptor(uri, method);
downloader.tell(request, observer);
@@ -78,45 +102,90 @@ public void testGet() throws URISyntaxException, IOException {
assertTrue(response.succeeded());
final HttpResponseDescriptor descriptor = response.get();
System.out.println("Result: " + descriptor.getContentAsString());
assertTrue(descriptor.getContentAsString().contains("<title>RestComm - Home</title>"));
assertTrue(descriptor.getContentAsString().contains("expectedBody"));
}
};
}

@Test
@Ignore
public void testPost() throws URISyntaxException, IOException {
stubFor(post(urlMatching("/testPost")).willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("expectedBody")));
new JavaTestKit(system) {
{
final ActorRef observer = getRef();
final URI uri = URI.create("http://www.restcomm.org");
final URI uri = URI.create(PATH + "testPost");
final String method = "POST";
final HttpRequestDescriptor request = new HttpRequestDescriptor(uri, method);
downloader.tell(request, observer);
final FiniteDuration timeout = FiniteDuration.create(30, TimeUnit.SECONDS);
final DownloaderResponse response = expectMsgClass(timeout, DownloaderResponse.class);
assertTrue(response.succeeded());
final HttpResponseDescriptor descriptor = response.get();
assertTrue(descriptor.getContentAsString().contains("<title>RestComm - Home</title>"));
assertTrue(descriptor.getContentAsString().contains("expectedBody"));
}
};
}

@Test
@Ignore
public void testNotFound() throws URISyntaxException, IOException {
stubFor(get(urlMatching("/testNotFound")).willReturn(aResponse()
.withStatus(404)
.withHeader("Content-Type", "application/json")
.withBody("{}")));
new JavaTestKit(system) {
{
final ActorRef observer = getRef();
final URI uri = URI.create("http://www.telestax.com/not-found.html");
final URI uri = URI.create(PATH + "testNotFound");
final String method = "GET";
final HttpRequestDescriptor request = new HttpRequestDescriptor(uri, method);
downloader.tell(request, observer);
final FiniteDuration timeout = FiniteDuration.create(30, TimeUnit.SECONDS);
final DownloaderResponse response = expectMsgClass(timeout, DownloaderResponse.class);
assertTrue(response.succeeded());
final HttpResponseDescriptor descriptor = response.get();
assertTrue(descriptor.getStatusCode() == 404);
assertEquals(404, descriptor.getStatusCode());
}
};
}


/**
* configuration comes from restcomm.xml file
*
* @throws Exception
*/
@Test()
public void testDownloaderWithRouteconfiguration() throws Exception {
stubFor(get(urlMatching("/testDownloaderWithRouteconfiguration")).willReturn(aResponse()
.withFixedDelay(5000 * 2)//delay will cause read timeout to happen
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{}")));
new JavaTestKit(system) {
{
int connsPerRoute = 5;
final URI uri = URI.create(PATH + "testDownloaderWithRouteconfiguration");
final String method = "GET";
final HttpRequestDescriptor request = new HttpRequestDescriptor(uri, method);
final ActorRef observer = getRef();

for (int i =0; i < connsPerRoute; i ++)
{
downloader = system.actorOf(new Props(Downloader.class));
downloader.tell(request, observer);
}
Thread.sleep(1000);
downloader = system.actorOf(new Props(Downloader.class));
downloader.tell(request, observer);
final FiniteDuration timeout = FiniteDuration.create(30, TimeUnit.SECONDS);
final DownloaderResponse response = expectMsgClass(timeout, DownloaderResponse.class);
assertFalse(response.succeeded());
//JavaTestKit dont allow to throw exception and use "expected"
assertEquals(ConnectionPoolTimeoutException.class, response.cause().getClass());
}
};
}
7 changes: 7 additions & 0 deletions restcomm/restcomm.http/src/test/resources/restcomm.xml
Original file line number Diff line number Diff line change
@@ -270,6 +270,13 @@
<use-hostname-to-resolve-relative-url>true</use-hostname-to-resolve-relative-url>
<!-- Optionally provide the hostname to be used, otherwise Java will try to get the hostname of the machine JVM is running -->
<hostname></hostname>
<connection-request-timeout>2000</connection-request-timeout>
<max-conn-total>200</max-conn-total>
<max-conn-per-route>2</max-conn-per-route>
<connection-time-to-live>30000</connection-time-to-live>
<routes-host>127.0.0.1,www.restcomm.org</routes-host>
<routes-port>8099,80</routes-port>
<routes-conn>5,10</routes-conn>
</http-client>

<!-- The SMS aggregator is responsible for the handling of SMS messages
Original file line number Diff line number Diff line change
@@ -2572,7 +2572,7 @@ private long sendPushNotificationIfNeeded(final String pushClientIdentity) {
logger.debug("Push server notification to client with identity: '" + pushClientIdentity + "' added to queue.");
}
if (httpClient == null) {
httpClient = CustomHttpClientBuilder.build(RestcommConfiguration.getInstance().getMain());
httpClient = CustomHttpClientBuilder.buildDefaultClient(RestcommConfiguration.getInstance().getMain());
}
Futures.future(new Callable<Void>() {