mirror of https://github.com/apache/kafka.git
KAFKA-5863: Avoid NPE when RestClient calls expecting no-content receive content. (#13294)
Signed-off-by: Greg Harris <greg.harris@aiven.io> Reviewers: Hector Geraldino <hgeraldino@gmail.com>, Yash Mayya <yash.mayya@gmail.com>
This commit is contained in:
parent
a989329ee3
commit
72b70288eb
|
@ -1268,7 +1268,7 @@ public class DistributedHerder extends AbstractHerder implements Runnable {
|
||||||
try {
|
try {
|
||||||
String stageDescription = "Forwarding zombie fencing request to the leader at " + workerUrl;
|
String stageDescription = "Forwarding zombie fencing request to the leader at " + workerUrl;
|
||||||
try (TemporaryStage stage = new TemporaryStage(stageDescription, callback, time)) {
|
try (TemporaryStage stage = new TemporaryStage(stageDescription, callback, time)) {
|
||||||
restClient.httpRequest(fenceUrl, "PUT", null, null, null, sessionKey, requestSignatureAlgorithm);
|
restClient.httpRequest(fenceUrl, "PUT", null, null, sessionKey, requestSignatureAlgorithm);
|
||||||
}
|
}
|
||||||
callback.onCompletion(null, null);
|
callback.onCompletion(null, null);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
@ -2224,7 +2224,7 @@ public class DistributedHerder extends AbstractHerder implements Runnable {
|
||||||
log.trace("Forwarding task configurations for connector {} to leader", connName);
|
log.trace("Forwarding task configurations for connector {} to leader", connName);
|
||||||
String stageDescription = "Forwarding task configurations to the leader at " + leaderUrl;
|
String stageDescription = "Forwarding task configurations to the leader at " + leaderUrl;
|
||||||
try (TemporaryStage stage = new TemporaryStage(stageDescription, cb, time)) {
|
try (TemporaryStage stage = new TemporaryStage(stageDescription, cb, time)) {
|
||||||
restClient.httpRequest(reconfigUrl, "POST", null, rawTaskProps, null, sessionKey, requestSignatureAlgorithm);
|
restClient.httpRequest(reconfigUrl, "POST", null, rawTaskProps, sessionKey, requestSignatureAlgorithm);
|
||||||
}
|
}
|
||||||
cb.onCompletion(null, null);
|
cb.onCompletion(null, null);
|
||||||
} catch (ConnectException e) {
|
} catch (ConnectException e) {
|
||||||
|
|
|
@ -141,9 +141,14 @@ public class HerderRequestHandler {
|
||||||
return completeOrForwardRequest(cb, path, method, headers, null, body, resultType, translator, forward);
|
return completeOrForwardRequest(cb, path, method, headers, null, body, resultType, translator, forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T completeOrForwardRequest(FutureCallback<T> cb, String path, String method, HttpHeaders headers,
|
public <T> T completeOrForwardRequest(FutureCallback<T> cb, String path, String method, HttpHeaders headers, Object body,
|
||||||
Object body, Boolean forward) throws Throwable {
|
TypeReference<T> resultType, Boolean forward) throws Throwable {
|
||||||
return completeOrForwardRequest(cb, path, method, headers, body, null, new IdentityTranslator<>(), forward);
|
return completeOrForwardRequest(cb, path, method, headers, body, resultType, new IdentityTranslator<>(), forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void completeOrForwardRequest(FutureCallback<Void> cb, String path, String method, HttpHeaders headers, Object body,
|
||||||
|
Boolean forward) throws Throwable {
|
||||||
|
completeOrForwardRequest(cb, path, method, headers, body, new TypeReference<Void>() { }, new IdentityTranslator<>(), forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Translator<T, U> {
|
public interface Translator<T, U> {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
@ -67,13 +68,13 @@ public class RestClient {
|
||||||
/**
|
/**
|
||||||
* Sends HTTP request to remote REST server
|
* Sends HTTP request to remote REST server
|
||||||
*
|
*
|
||||||
* @param url HTTP connection will be established with this url.
|
* @param url HTTP connection will be established with this url, non-null.
|
||||||
* @param method HTTP method ("GET", "POST", "PUT", etc.)
|
* @param method HTTP method ("GET", "POST", "PUT", etc.), non-null
|
||||||
* @param headers HTTP headers from REST endpoint
|
* @param headers HTTP headers from REST endpoint
|
||||||
* @param requestBodyData Object to serialize as JSON and send in the request body.
|
* @param requestBodyData Object to serialize as JSON and send in the request body.
|
||||||
* @param responseFormat Expected format of the response to the HTTP request.
|
* @param responseFormat Expected format of the response to the HTTP request, non-null.
|
||||||
* @param <T> The type of the deserialized response to the HTTP request.
|
* @param <T> The type of the deserialized response to the HTTP request.
|
||||||
* @return The deserialized response to the HTTP request, or null if no data is expected.
|
* @return The deserialized response to the HTTP request, containing null if no data is expected or returned.
|
||||||
*/
|
*/
|
||||||
public <T> HttpResponse<T> httpRequest(String url, String method, HttpHeaders headers, Object requestBodyData,
|
public <T> HttpResponse<T> httpRequest(String url, String method, HttpHeaders headers, Object requestBodyData,
|
||||||
TypeReference<T> responseFormat) {
|
TypeReference<T> responseFormat) {
|
||||||
|
@ -83,21 +84,41 @@ public class RestClient {
|
||||||
/**
|
/**
|
||||||
* Sends HTTP request to remote REST server
|
* Sends HTTP request to remote REST server
|
||||||
*
|
*
|
||||||
* @param url HTTP connection will be established with this url.
|
* @param url HTTP connection will be established with this url, non-null.
|
||||||
* @param method HTTP method ("GET", "POST", "PUT", etc.)
|
* @param method HTTP method ("GET", "POST", "PUT", etc.), non-null
|
||||||
* @param headers HTTP headers from REST endpoint
|
* @param headers HTTP headers from REST endpoint
|
||||||
* @param requestBodyData Object to serialize as JSON and send in the request body.
|
* @param requestBodyData Object to serialize as JSON and send in the request body.
|
||||||
* @param responseFormat Expected format of the response to the HTTP request.
|
* @param sessionKey The key to sign the request with (intended for internal requests only);
|
||||||
|
* may be null if the request doesn't need to be signed
|
||||||
|
* @param requestSignatureAlgorithm The algorithm to sign the request with (intended for internal requests only);
|
||||||
|
* may be null if the request doesn't need to be signed
|
||||||
|
*/
|
||||||
|
public void httpRequest(String url, String method, HttpHeaders headers, Object requestBodyData,
|
||||||
|
SecretKey sessionKey, String requestSignatureAlgorithm) {
|
||||||
|
httpRequest(url, method, headers, requestBodyData, new TypeReference<Void>() { }, sessionKey, requestSignatureAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends HTTP request to remote REST server
|
||||||
|
*
|
||||||
|
* @param url HTTP connection will be established with this url, non-null.
|
||||||
|
* @param method HTTP method ("GET", "POST", "PUT", etc.), non-null
|
||||||
|
* @param headers HTTP headers from REST endpoint
|
||||||
|
* @param requestBodyData Object to serialize as JSON and send in the request body.
|
||||||
|
* @param responseFormat Expected format of the response to the HTTP request, non-null.
|
||||||
* @param <T> The type of the deserialized response to the HTTP request.
|
* @param <T> The type of the deserialized response to the HTTP request.
|
||||||
* @param sessionKey The key to sign the request with (intended for internal requests only);
|
* @param sessionKey The key to sign the request with (intended for internal requests only);
|
||||||
* may be null if the request doesn't need to be signed
|
* may be null if the request doesn't need to be signed
|
||||||
* @param requestSignatureAlgorithm The algorithm to sign the request with (intended for internal requests only);
|
* @param requestSignatureAlgorithm The algorithm to sign the request with (intended for internal requests only);
|
||||||
* may be null if the request doesn't need to be signed
|
* may be null if the request doesn't need to be signed
|
||||||
* @return The deserialized response to the HTTP request, or null if no data is expected.
|
* @return The deserialized response to the HTTP request, containing null if no data is expected or returned.
|
||||||
*/
|
*/
|
||||||
public <T> HttpResponse<T> httpRequest(String url, String method, HttpHeaders headers, Object requestBodyData,
|
public <T> HttpResponse<T> httpRequest(String url, String method, HttpHeaders headers, Object requestBodyData,
|
||||||
TypeReference<T> responseFormat,
|
TypeReference<T> responseFormat,
|
||||||
SecretKey sessionKey, String requestSignatureAlgorithm) {
|
SecretKey sessionKey, String requestSignatureAlgorithm) {
|
||||||
|
Objects.requireNonNull(url, "url must be non-null");
|
||||||
|
Objects.requireNonNull(method, "method must be non-null");
|
||||||
|
Objects.requireNonNull(responseFormat, "response format must be non-null");
|
||||||
// Only try to load SSL configs if we have to (see KAFKA-14816)
|
// Only try to load SSL configs if we have to (see KAFKA-14816)
|
||||||
SslContextFactory sslContextFactory = url.startsWith("https://")
|
SslContextFactory sslContextFactory = url.startsWith("https://")
|
||||||
? SSLUtils.createClientSideSslContextFactory(config)
|
? SSLUtils.createClientSideSslContextFactory(config)
|
||||||
|
|
|
@ -330,7 +330,7 @@ public class ConnectorsResource {
|
||||||
FutureCallback<Void> cb = new FutureCallback<>();
|
FutureCallback<Void> cb = new FutureCallback<>();
|
||||||
ConnectorTaskId taskId = new ConnectorTaskId(connector, task);
|
ConnectorTaskId taskId = new ConnectorTaskId(connector, task);
|
||||||
herder.restartTask(taskId, cb);
|
herder.restartTask(taskId, cb);
|
||||||
requestHandler.completeOrForwardRequest(cb, "/connectors/" + connector + "/tasks/" + task + "/restart", "POST", headers, null, forward);
|
requestHandler.completeOrForwardRequest(cb, "/connectors/" + connector + "/tasks/" + task + "/restart", "POST", headers, null, new TypeReference<Void>() { }, forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
|
@ -341,7 +341,7 @@ public class ConnectorsResource {
|
||||||
final @Parameter(hidden = true) @QueryParam("forward") Boolean forward) throws Throwable {
|
final @Parameter(hidden = true) @QueryParam("forward") Boolean forward) throws Throwable {
|
||||||
FutureCallback<Herder.Created<ConnectorInfo>> cb = new FutureCallback<>();
|
FutureCallback<Herder.Created<ConnectorInfo>> cb = new FutureCallback<>();
|
||||||
herder.deleteConnectorConfig(connector, cb);
|
herder.deleteConnectorConfig(connector, cb);
|
||||||
requestHandler.completeOrForwardRequest(cb, "/connectors/" + connector, "DELETE", headers, null, forward);
|
requestHandler.completeOrForwardRequest(cb, "/connectors/" + connector, "DELETE", headers, null, new TypeReference<Herder.Created<ConnectorInfo>>() { }, forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|
|
@ -78,7 +78,6 @@ import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.mockito.stubbing.OngoingStubbing;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -2695,15 +2694,14 @@ public class DistributedHerderTest {
|
||||||
expectRebalance(1, Collections.emptyList(), Collections.emptyList());
|
expectRebalance(1, Collections.emptyList(), Collections.emptyList());
|
||||||
expectMemberPoll();
|
expectMemberPoll();
|
||||||
|
|
||||||
OngoingStubbing<RestClient.HttpResponse<Object>> expectRequest = when(restClient.httpRequest(
|
doAnswer(invocation -> {
|
||||||
any(), eq("PUT"), isNull(), isNull(), isNull(), any(), any()
|
if (!succeed) {
|
||||||
));
|
throw new ConnectRestException(409, "Rebalance :(");
|
||||||
|
}
|
||||||
if (succeed) {
|
return null;
|
||||||
expectRequest.thenReturn(null);
|
}).when(restClient).httpRequest(
|
||||||
} else {
|
any(), eq("PUT"), isNull(), isNull(), any(), any()
|
||||||
expectRequest.thenThrow(new ConnectRestException(409, "Rebalance :("));
|
);
|
||||||
}
|
|
||||||
|
|
||||||
ArgumentCaptor<Runnable> forwardRequest = ArgumentCaptor.forClass(Runnable.class);
|
ArgumentCaptor<Runnable> forwardRequest = ArgumentCaptor.forClass(Runnable.class);
|
||||||
|
|
||||||
|
@ -3291,10 +3289,10 @@ public class DistributedHerderTest {
|
||||||
changedTaskConfigs.add(TASK_CONFIG);
|
changedTaskConfigs.add(TASK_CONFIG);
|
||||||
when(worker.connectorTaskConfigs(CONN1, sinkConnectorConfig)).thenReturn(changedTaskConfigs);
|
when(worker.connectorTaskConfigs(CONN1, sinkConnectorConfig)).thenReturn(changedTaskConfigs);
|
||||||
|
|
||||||
when(restClient.httpRequest(any(), eq("POST"), any(), any(), any(), any(), any()))
|
doThrow(new ConnectException("Request to leader to reconfigure connector tasks failed"))
|
||||||
.thenThrow(new ConnectException("Request to leader to reconfigure connector tasks failed"))
|
.doThrow(new ConnectException("Request to leader to reconfigure connector tasks failed"))
|
||||||
.thenThrow(new ConnectException("Request to leader to reconfigure connector tasks failed"))
|
.doNothing()
|
||||||
.thenReturn(null);
|
.when(restClient).httpRequest(any(), eq("POST"), any(), any(), any(), any());
|
||||||
|
|
||||||
expectAndVerifyTaskReconfigurationRetries();
|
expectAndVerifyTaskReconfigurationRetries();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,13 @@ import static org.mockito.Mockito.when;
|
||||||
public class RestClientTest {
|
public class RestClientTest {
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
private static final String MOCK_URL = "http://localhost:1234/api/endpoint";
|
||||||
|
private static final String TEST_METHOD = "GET";
|
||||||
|
private static final TestDTO TEST_DTO = new TestDTO("requestBodyData");
|
||||||
private static final TypeReference<TestDTO> TEST_TYPE = new TypeReference<TestDTO>() {
|
private static final TypeReference<TestDTO> TEST_TYPE = new TypeReference<TestDTO>() {
|
||||||
};
|
};
|
||||||
private static final SecretKey MOCK_SECRET_KEY = getMockSecretKey();
|
private static final SecretKey MOCK_SECRET_KEY = getMockSecretKey();
|
||||||
|
private static final String TEST_SIGNATURE_ALGORITHM = "HmacSHA1";
|
||||||
|
|
||||||
private static void assertIsInternalServerError(ConnectRestException e) {
|
private static void assertIsInternalServerError(ConnectRestException e) {
|
||||||
assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.statusCode());
|
assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.statusCode());
|
||||||
|
@ -78,31 +82,26 @@ public class RestClientTest {
|
||||||
return mockKey;
|
return mockKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RestClient.HttpResponse<TestDTO> httpRequest(HttpClient httpClient, String requestSignatureAlgorithm, boolean https) {
|
private static <T> RestClient.HttpResponse<T> httpRequest(
|
||||||
|
HttpClient httpClient,
|
||||||
|
String url,
|
||||||
|
String method,
|
||||||
|
TypeReference<T> responseFormat,
|
||||||
|
String requestSignatureAlgorithm
|
||||||
|
) {
|
||||||
RestClient client = spy(new RestClient(null));
|
RestClient client = spy(new RestClient(null));
|
||||||
doReturn(httpClient).when(client).httpClient(any());
|
doReturn(httpClient).when(client).httpClient(any());
|
||||||
String protocol = https ? "https" : "http";
|
|
||||||
String url = protocol + "://localhost:1234/api/endpoint";
|
|
||||||
return client.httpRequest(
|
return client.httpRequest(
|
||||||
url,
|
url,
|
||||||
"GET",
|
method,
|
||||||
null,
|
null,
|
||||||
new TestDTO("requestBodyData"),
|
TEST_DTO,
|
||||||
TEST_TYPE,
|
responseFormat,
|
||||||
MOCK_SECRET_KEY,
|
MOCK_SECRET_KEY,
|
||||||
requestSignatureAlgorithm
|
requestSignatureAlgorithm
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RestClient.HttpResponse<TestDTO> httpRequest(HttpClient httpClient, String requestSignatureAlgorithm) {
|
|
||||||
return httpRequest(httpClient, requestSignatureAlgorithm, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RestClient.HttpResponse<TestDTO> httpRequest(HttpClient httpClient) throws Exception {
|
|
||||||
String validRequestSignatureAlgorithm = "HmacSHA1";
|
|
||||||
return httpRequest(httpClient, validRequestSignatureAlgorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public static class RequestFailureParameterizedTest {
|
public static class RequestFailureParameterizedTest {
|
||||||
|
@ -136,7 +135,9 @@ public class RestClientTest {
|
||||||
public void testFailureDuringRequestCausesInternalServerError() throws Exception {
|
public void testFailureDuringRequestCausesInternalServerError() throws Exception {
|
||||||
Request request = buildThrowingMockRequest(requestException);
|
Request request = buildThrowingMockRequest(requestException);
|
||||||
when(httpClient.newRequest(anyString())).thenReturn(request);
|
when(httpClient.newRequest(anyString())).thenReturn(request);
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
assertIsInternalServerError(e);
|
assertIsInternalServerError(e);
|
||||||
assertEquals(requestException, e.getCause());
|
assertEquals(requestException, e.getCause());
|
||||||
}
|
}
|
||||||
|
@ -166,15 +167,46 @@ public class RestClientTest {
|
||||||
when(httpClient.newRequest(anyString())).thenReturn(req);
|
when(httpClient.newRequest(anyString())).thenReturn(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullUrl() throws Exception {
|
||||||
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
|
|
||||||
|
assertThrows(NullPointerException.class, () -> httpRequest(
|
||||||
|
httpClient, null, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullMethod() throws Exception {
|
||||||
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
|
|
||||||
|
assertThrows(NullPointerException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, null, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullResponseType() throws Exception {
|
||||||
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
|
|
||||||
|
assertThrows(NullPointerException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, null, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess() throws Exception {
|
public void testSuccess() throws Exception {
|
||||||
int statusCode = Response.Status.OK.getStatusCode();
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
TestDTO expectedResponse = new TestDTO("someContent");
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
setupHttpClient(statusCode, toJsonString(expectedResponse));
|
|
||||||
|
|
||||||
RestClient.HttpResponse<TestDTO> httpResp = httpRequest(httpClient);
|
RestClient.HttpResponse<TestDTO> httpResp = httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
);
|
||||||
assertEquals(statusCode, httpResp.status());
|
assertEquals(statusCode, httpResp.status());
|
||||||
assertEquals(expectedResponse, httpResp.body());
|
assertEquals(TEST_DTO, httpResp.body());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -182,7 +214,9 @@ public class RestClientTest {
|
||||||
int statusCode = Response.Status.NO_CONTENT.getStatusCode();
|
int statusCode = Response.Status.NO_CONTENT.getStatusCode();
|
||||||
setupHttpClient(statusCode, null);
|
setupHttpClient(statusCode, null);
|
||||||
|
|
||||||
RestClient.HttpResponse<TestDTO> httpResp = httpRequest(httpClient);
|
RestClient.HttpResponse<TestDTO> httpResp = httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
);
|
||||||
assertEquals(statusCode, httpResp.status());
|
assertEquals(statusCode, httpResp.status());
|
||||||
assertNull(httpResp.body());
|
assertNull(httpResp.body());
|
||||||
}
|
}
|
||||||
|
@ -193,19 +227,36 @@ public class RestClientTest {
|
||||||
ErrorMessage errorMsg = new ErrorMessage(Response.Status.GONE.getStatusCode(), "Some Error Message");
|
ErrorMessage errorMsg = new ErrorMessage(Response.Status.GONE.getStatusCode(), "Some Error Message");
|
||||||
setupHttpClient(statusCode, toJsonString(errorMsg));
|
setupHttpClient(statusCode, toJsonString(errorMsg));
|
||||||
|
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
assertEquals(statusCode, e.statusCode());
|
assertEquals(statusCode, e.statusCode());
|
||||||
assertEquals(errorMsg.errorCode(), e.errorCode());
|
assertEquals(errorMsg.errorCode(), e.errorCode());
|
||||||
assertEquals(errorMsg.message(), e.getMessage());
|
assertEquals(errorMsg.message(), e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonEmptyResponseWithVoidResponseType() throws Exception {
|
||||||
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
|
|
||||||
|
TypeReference<Void> voidResponse = new TypeReference<Void>() { };
|
||||||
|
RestClient.HttpResponse<Void> httpResp = httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, voidResponse, TEST_SIGNATURE_ALGORITHM
|
||||||
|
);
|
||||||
|
assertEquals(statusCode, httpResp.status());
|
||||||
|
assertNull(httpResp.body());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnexpectedHttpResponseCausesInternalServerError() throws Exception {
|
public void testUnexpectedHttpResponseCausesInternalServerError() throws Exception {
|
||||||
int statusCode = Response.Status.NOT_MODIFIED.getStatusCode(); // never thrown explicitly -
|
int statusCode = Response.Status.NOT_MODIFIED.getStatusCode(); // never thrown explicitly -
|
||||||
// should be treated as an unexpected error and translated into 500 INTERNAL_SERVER_ERROR
|
// should be treated as an unexpected error and translated into 500 INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
setupHttpClient(statusCode, null);
|
setupHttpClient(statusCode, null);
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
assertIsInternalServerError(e);
|
assertIsInternalServerError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +264,9 @@ public class RestClientTest {
|
||||||
public void testRuntimeExceptionCausesInternalServerError() {
|
public void testRuntimeExceptionCausesInternalServerError() {
|
||||||
when(httpClient.newRequest(anyString())).thenThrow(new RuntimeException());
|
when(httpClient.newRequest(anyString())).thenThrow(new RuntimeException());
|
||||||
|
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
assertIsInternalServerError(e);
|
assertIsInternalServerError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +275,9 @@ public class RestClientTest {
|
||||||
setupHttpClient(0, null);
|
setupHttpClient(0, null);
|
||||||
|
|
||||||
String invalidRequestSignatureAlgorithm = "Foo";
|
String invalidRequestSignatureAlgorithm = "Foo";
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient, invalidRequestSignatureAlgorithm));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, invalidRequestSignatureAlgorithm
|
||||||
|
));
|
||||||
assertIsInternalServerError(e);
|
assertIsInternalServerError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +286,9 @@ public class RestClientTest {
|
||||||
String invalidJsonString = "Invalid";
|
String invalidJsonString = "Invalid";
|
||||||
setupHttpClient(201, invalidJsonString);
|
setupHttpClient(201, invalidJsonString);
|
||||||
|
|
||||||
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(httpClient));
|
ConnectRestException e = assertThrows(ConnectRestException.class, () -> httpRequest(
|
||||||
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
assertIsInternalServerError(e);
|
assertIsInternalServerError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,12 +297,15 @@ public class RestClientTest {
|
||||||
// See KAFKA-14816; we want to make sure that even if the worker is configured with invalid SSL properties,
|
// See KAFKA-14816; we want to make sure that even if the worker is configured with invalid SSL properties,
|
||||||
// REST requests only fail if we try to contact a URL using HTTPS (but not HTTP)
|
// REST requests only fail if we try to contact a URL using HTTPS (but not HTTP)
|
||||||
int statusCode = Response.Status.OK.getStatusCode();
|
int statusCode = Response.Status.OK.getStatusCode();
|
||||||
TestDTO expectedResponse = new TestDTO("someContent");
|
setupHttpClient(statusCode, toJsonString(TEST_DTO));
|
||||||
setupHttpClient(statusCode, toJsonString(expectedResponse));
|
|
||||||
|
|
||||||
String requestSignatureAlgorithm = "HmacSHA1";
|
assertDoesNotThrow(() -> httpRequest(
|
||||||
assertDoesNotThrow(() -> httpRequest(httpClient, requestSignatureAlgorithm, false));
|
httpClient, MOCK_URL, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
assertThrows(RuntimeException.class, () -> httpRequest(httpClient, requestSignatureAlgorithm, true));
|
));
|
||||||
|
String httpsUrl = "https://localhost:1234/api/endpoint";
|
||||||
|
assertThrows(RuntimeException.class, () -> httpRequest(
|
||||||
|
httpClient, httpsUrl, TEST_METHOD, TEST_TYPE, TEST_SIGNATURE_ALGORITHM
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -428,7 +428,7 @@ public class ConnectorsResourceTest {
|
||||||
expectAndCallbackNotLeaderException(cb).when(herder)
|
expectAndCallbackNotLeaderException(cb).when(herder)
|
||||||
.deleteConnectorConfig(eq(CONNECTOR_NAME), cb.capture());
|
.deleteConnectorConfig(eq(CONNECTOR_NAME), cb.capture());
|
||||||
// Should forward request
|
// Should forward request
|
||||||
when(restClient.httpRequest(LEADER_URL + "connectors/" + CONNECTOR_NAME + "?forward=false", "DELETE", NULL_HEADERS, null, null))
|
when(restClient.httpRequest(eq(LEADER_URL + "connectors/" + CONNECTOR_NAME + "?forward=false"), eq("DELETE"), isNull(), any(), any()))
|
||||||
.thenReturn(new RestClient.HttpResponse<>(204, new HashMap<>(), null));
|
.thenReturn(new RestClient.HttpResponse<>(204, new HashMap<>(), null));
|
||||||
connectorsResource.destroyConnector(CONNECTOR_NAME, NULL_HEADERS, FORWARD);
|
connectorsResource.destroyConnector(CONNECTOR_NAME, NULL_HEADERS, FORWARD);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue