Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions multiapps-controller-core-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.cloudfoundry.multiapps</groupId>
Expand Down
4 changes: 2 additions & 2 deletions multiapps-controller-core-test/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

exports org.cloudfoundry.multiapps.controller.core.test;

requires transitive org.apache.httpcomponents.httpcore;
requires transitive org.cloudfoundry.multiapps.mta;

requires org.apache.httpcomponents.httpclient;
requires org.apache.httpcomponents.client5.httpclient5;
requires org.apache.httpcomponents.core5.httpcore5;
requires org.cloudfoundry.multiapps.common;
requires org.cloudfoundry.multiapps.common.test;
requires org.junit.jupiter.api;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import java.io.IOException;
import java.util.List;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.ResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HttpContext;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.cloudfoundry.multiapps.common.Nullable;
import org.immutables.value.Value;
import org.mockito.ArgumentMatchers;
Expand All @@ -23,9 +23,6 @@ public abstract class HttpClientMock {
@Nullable
public abstract Throwable getException();

@Nullable
public abstract String getResponseHandlerReturnValue();

@Value.Derived
public CloseableHttpClient getMock() {
try {
Expand All @@ -49,12 +46,10 @@ public CloseableHttpClient getMock() {
Mockito.when(httpClient.execute(ArgumentMatchers.<HttpHost> any(), ArgumentMatchers.any(),
ArgumentMatchers.<HttpContext> any()))
.thenAnswer(answer);
Mockito.when(
httpClient.execute(ArgumentMatchers.<ClassicHttpRequest> any(), ArgumentMatchers.<HttpClientResponseHandler<Object>> any()))
.thenAnswer(answer);

if (getResponseHandlerReturnValue() != null) {
Mockito.when(httpClient.execute(ArgumentMatchers.any(), ArgumentMatchers.any(),
ArgumentMatchers.<ResponseHandler<Object>> any()))
.thenReturn(getResponseHandlerReturnValue());
}
return httpClient;
} catch (IOException e) {
throw new IllegalStateException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.cloudfoundry.multiapps.controller.core.test;

import java.util.function.UnaryOperator;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;

public class HttpMocks {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.immutables.value.Value;
import org.mockito.Mockito;

Expand All @@ -28,18 +24,14 @@ public abstract class HttpResponseMock {
@Value.Derived
public CloseableHttpResponse getMock() {
CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class);
Mockito.when(response.getStatusLine())
.thenReturn(createStatusLine(getStatusCode()));
Mockito.when(response.getCode())
.thenReturn(getStatusCode());
Mockito.when(response.getEntity())
.thenReturn(createHttpEntity(getBody()));
mockHeaders(response);
return response;
}

private static StatusLine createStatusLine(int statusCode) {
return new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), statusCode, null);
}

private static HttpEntity createHttpEntity(String body) {
return new StringEntity(body, StandardCharsets.UTF_8);
}
Expand Down
4 changes: 4 additions & 0 deletions multiapps-controller-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,9 @@
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
</dependencies>
</project>
4 changes: 2 additions & 2 deletions multiapps-controller-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
requires org.apache.commons.collections4;
requires org.apache.commons.io;
requires org.apache.commons.lang3;
requires org.apache.httpcomponents.httpclient;
requires org.apache.httpcomponents.httpcore;
requires org.apache.httpcomponents.client5.httpclient5;
requires org.apache.httpcomponents.core5.httpcore5;
requires org.cloudfoundry.multiapps.common;
requires org.cloudfoundry.multiapps.controller.api;
requires org.slf4j;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package org.cloudfoundry.multiapps.controller.core.cf;

import java.util.Map;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHeaders;
import org.cloudfoundry.multiapps.controller.core.Constants;

public class CloudControllerHeaderConfiguration {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,105 +1,54 @@
package org.cloudfoundry.multiapps.controller.core.http;

import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

public class CsrfHttpClient implements HttpClient, Closeable {
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpHead;
import org.apache.hc.client5.http.classic.methods.HttpOptions;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.io.CloseMode;

public class CsrfHttpClient extends CloseableHttpClient {

public static final String CSRF_TOKEN_HEADER_NAME = "X-CSRF-TOKEN";
public static final String CSRF_TOKEN_HEADER_FETCH_VALUE = "Fetch";
public static final String CSRF_TOKEN_HEADER_REQUIRED_VALUE = "Required";
private static final List<String> NON_PROTECTED_METHODS = Arrays.asList(HttpGet.METHOD_NAME, HttpOptions.METHOD_NAME,
HttpHead.METHOD_NAME);
private static final List<String> NON_PROTECTED_METHODS = List.of(HttpGet.METHOD_NAME, HttpOptions.METHOD_NAME,
HttpHead.METHOD_NAME);

private final HttpClient delegate;
private final CloseableHttpClient delegate;
private String csrfToken;
private final String csrfGetTokenUrl;
private final Map<String, String> httpRequestHeaders;
private boolean isTokenInitialized;

public CsrfHttpClient(HttpClient httpClient, String csrfGetTokenUrl, Map<String, String> httpRequestHeaders) {
public CsrfHttpClient(CloseableHttpClient httpClient, String csrfGetTokenUrl, Map<String, String> httpRequestHeaders) {
this.delegate = httpClient;
this.csrfGetTokenUrl = csrfGetTokenUrl;
this.httpRequestHeaders = httpRequestHeaders;
}

@Override
public HttpParams getParams() {
return delegate.getParams();
}

@Override
public ClientConnectionManager getConnectionManager() {
return delegate.getConnectionManager();
}

@Override
public HttpResponse execute(HttpUriRequest request) throws IOException {
return executeRequest(request, () -> delegate.execute(request));
}

@Override
public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException {
return executeRequest(request, () -> delegate.execute(request, context));
}

@Override
public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException {
return executeRequest(request, () -> delegate.execute(target, request));
}

@Override
public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException {
protected CloseableHttpResponse doExecute(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException {
return executeRequest(request, () -> delegate.execute(target, request, context));
}

@Override
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException {
return executeRequest(request, () -> delegate.execute(request, responseHandler));
}

@Override
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException {
return executeRequest(request, () -> delegate.execute(request, responseHandler, context));
}

@Override
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException {
return executeRequest(request, () -> delegate.execute(target, request, responseHandler));
}

@Override
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
throws IOException {
return executeRequest(request, () -> delegate.execute(target, request, responseHandler, context));
}

private <T> T executeRequest(HttpRequest request, Executor<T> executionSupplier) throws IOException {
T result = executeWithCsrfTokenSetting(request, executionSupplier);
if (!(result instanceof HttpResponse)) {
if (!(result instanceof ClassicHttpResponse response)) {
return result;
}
HttpResponse response = (HttpResponse) result;
if (isRetryNeeded(request, response)) {
result = executeWithCsrfTokenSetting(request, executionSupplier);
}
Expand Down Expand Up @@ -130,8 +79,7 @@ private void setCrsfToken(HttpRequest request) throws IOException {
}

private boolean isProtectionRequired(HttpRequest request) {
return !NON_PROTECTED_METHODS.contains(request.getRequestLine()
.getMethod());
return !NON_PROTECTED_METHODS.contains(request.getMethod());
}

private void initializeToken(boolean force) throws IOException {
Expand All @@ -151,20 +99,20 @@ private String fetchNewCsrfToken() throws IOException {
HttpGet fetchTokenRequest = new HttpGet(csrfGetTokenUrl);
fetchTokenRequest.addHeader(CSRF_TOKEN_HEADER_NAME, CSRF_TOKEN_HEADER_FETCH_VALUE);
setHttpRequestHeaders(fetchTokenRequest);
HttpResponse response = delegate.execute(fetchTokenRequest);
EntityUtils.consume(response.getEntity());
if (response.containsHeader(CSRF_TOKEN_HEADER_NAME)) {
return response.getFirstHeader(CSRF_TOKEN_HEADER_NAME)
.getValue();
}

return null;
return delegate.execute(fetchTokenRequest, response -> {
EntityUtils.consume(response.getEntity());
if (response.containsHeader(CSRF_TOKEN_HEADER_NAME)) {
return response.getFirstHeader(CSRF_TOKEN_HEADER_NAME)
.getValue();
}
return null;
});
}

/**
* Checks if a request has failed due to an expired session(token is not valid anymore) and regenerates the token if needed.
*/
private boolean isRetryNeeded(HttpRequest request, HttpResponse response) throws IOException {
private boolean isRetryNeeded(HttpRequest request, ClassicHttpResponse response) throws IOException {
if (!isProtectionRequired(request)) {
// The request was not protected so the error was not caused by
// missing token.
Expand All @@ -173,8 +121,7 @@ private boolean isRetryNeeded(HttpRequest request, HttpResponse response) throws

// The token was initialized but probably the session has expired. If it
// is so, then the token needs to be regenerated and request retried.
if (isTokenInitialized && (response.getStatusLine()
.getStatusCode() == HttpStatus.SC_FORBIDDEN)) {
if (isTokenInitialized && (response.getCode() == HttpStatus.SC_FORBIDDEN)) {

Header csrfTokenHeader = response.getFirstHeader(CSRF_TOKEN_HEADER_NAME);

Expand All @@ -195,9 +142,12 @@ private boolean isRetryNeeded(HttpRequest request, HttpResponse response) throws

@Override
public void close() throws IOException {
if (delegate instanceof Closeable) {
((Closeable) delegate).close();
}
delegate.close();
}

@Override
public void close(CloseMode closeMode) {
delegate.close(closeMode);
}

private interface Executor<T> {
Expand Down
Loading