mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
CE-775 add timeouts to outbound http calls
This commit is contained in:
@ -81,6 +81,7 @@ import org.apache.http.HttpEntity;
|
|||||||
import org.apache.http.HttpEntityEnclosingRequest;
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpDelete;
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
@ -1048,7 +1049,7 @@ public class BaseAPIActionUtil
|
|||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// make sure to use closeable client to avoid leaks //
|
// make sure to use closeable client to avoid leaks //
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
try(CloseableHttpClient httpClient = HttpClientBuilder.create().build())
|
try(CloseableHttpClient httpClient = buildHttpClient())
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// call utility methods that populate data in the request //
|
// call utility methods that populate data in the request //
|
||||||
@ -1153,6 +1154,25 @@ public class BaseAPIActionUtil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Build the default HttpClient used by the makeRequest method
|
||||||
|
*******************************************************************************/
|
||||||
|
protected CloseableHttpClient buildHttpClient()
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// do we want this?? .setConnectionManager(new PoolingHttpClientConnectionManager()) //
|
||||||
|
// needs some good scrutiny. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
return HttpClientBuilder.create()
|
||||||
|
.setDefaultRequestConfig(RequestConfig.custom()
|
||||||
|
.setConnectTimeout(getConnectionTimeoutMillis())
|
||||||
|
.setConnectionRequestTimeout(getConnectionRequestTimeoutMillis())
|
||||||
|
.setSocketTimeout(getSocketTimeoutMillis()).build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1439,6 +1459,51 @@ public class BaseAPIActionUtil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For the HttpClientBuilder RequestConfig, specify its ConnectionTimeout. See
|
||||||
|
** - https://www.baeldung.com/httpclient-timeout
|
||||||
|
** - https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/org/apache/hc/client5/http/config/RequestConfig.Builder.html
|
||||||
|
*******************************************************************************/
|
||||||
|
protected int getConnectionTimeoutMillis()
|
||||||
|
{
|
||||||
|
//////////////
|
||||||
|
// 1 minute //
|
||||||
|
//////////////
|
||||||
|
return (60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For the HttpClientBuilder RequestConfig, specify its ConnectionRequestTimeout. See
|
||||||
|
** - https://www.baeldung.com/httpclient-timeout
|
||||||
|
** - https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/org/apache/hc/client5/http/config/RequestConfig.Builder.html
|
||||||
|
*******************************************************************************/
|
||||||
|
protected int getConnectionRequestTimeoutMillis()
|
||||||
|
{
|
||||||
|
//////////////
|
||||||
|
// 1 minute //
|
||||||
|
//////////////
|
||||||
|
return (60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For the HttpClientBuilder RequestConfig, specify its ConnectionRequestTimeout. See
|
||||||
|
** - https://www.baeldung.com/httpclient-timeout
|
||||||
|
** - https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/org/apache/hc/client5/http/config/RequestConfig.Builder.html
|
||||||
|
*******************************************************************************/
|
||||||
|
protected int getSocketTimeoutMillis()
|
||||||
|
{
|
||||||
|
///////////////
|
||||||
|
// 3 minutes //
|
||||||
|
///////////////
|
||||||
|
return (3 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -22,11 +22,18 @@
|
|||||||
package com.kingsrook.qqq.backend.module.api.actions;
|
package com.kingsrook.qqq.backend.module.api.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||||
@ -36,6 +43,7 @@ import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
@ -82,6 +90,8 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class BaseAPIActionUtilTest extends BaseTest
|
class BaseAPIActionUtilTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(BaseAPIActionUtilTest.class);
|
||||||
|
|
||||||
private static MockApiUtilsHelper mockApiUtilsHelper = new MockApiUtilsHelper();
|
private static MockApiUtilsHelper mockApiUtilsHelper = new MockApiUtilsHelper();
|
||||||
|
|
||||||
|
|
||||||
@ -822,6 +832,103 @@ class BaseAPIActionUtilTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testTimeouts() throws QException
|
||||||
|
{
|
||||||
|
ShortTimeoutActionUtil shortTimeoutActionUtil = new ShortTimeoutActionUtil();
|
||||||
|
shortTimeoutActionUtil.setBackendMetaData((APIBackendMetaData) QContext.getQInstance().getBackend(TestUtils.MOCK_BACKEND_NAME));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// make sure we work correctly with a large enough timeout //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
{
|
||||||
|
startSimpleHttpServer(8888);
|
||||||
|
HttpGet request = new HttpGet("http://localhost:8888");
|
||||||
|
shortTimeoutActionUtil.setTimeoutMillis(3000);
|
||||||
|
|
||||||
|
shortTimeoutActionUtil.makeRequest(QContext.getQInstance().getTable(TestUtils.MOCK_TABLE_NAME), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// make sure we fail with a too-small timeout //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
{
|
||||||
|
startSimpleHttpServer(8889);
|
||||||
|
HttpGet request = new HttpGet("http://localhost:8889");
|
||||||
|
shortTimeoutActionUtil.setTimeoutMillis(1);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> shortTimeoutActionUtil.makeRequest(QContext.getQInstance().getTable(TestUtils.MOCK_TABLE_NAME), request))
|
||||||
|
.hasRootCauseInstanceOf(SocketTimeoutException.class)
|
||||||
|
.rootCause().hasMessageContaining("timed out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void startSimpleHttpServer(int port)
|
||||||
|
{
|
||||||
|
Executors.newSingleThreadExecutor().submit(() ->
|
||||||
|
{
|
||||||
|
LOG.info("Listening on " + port);
|
||||||
|
try(ServerSocket serverSocket = new ServerSocket(port))
|
||||||
|
{
|
||||||
|
Socket clientSocket = serverSocket.accept();
|
||||||
|
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||||
|
String greeting = in.readLine();
|
||||||
|
LOG.info("Read: " + greeting);
|
||||||
|
SleepUtils.sleep(1, TimeUnit.SECONDS);
|
||||||
|
out.println("HTTP/1.1 200 OK");
|
||||||
|
out.close();
|
||||||
|
clientSocket.close();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.info("Exception in simple http server", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
static class ShortTimeoutActionUtil extends BaseAPIActionUtil
|
||||||
|
{
|
||||||
|
private int timeoutMillis = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for timeoutMillis
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTimeoutMillis(int timeoutMillis)
|
||||||
|
{
|
||||||
|
this.timeoutMillis = timeoutMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
protected int getSocketTimeoutMillis()
|
||||||
|
{
|
||||||
|
return (timeoutMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
Reference in New Issue
Block a user