Skip to content

Commit

Permalink
Add ClientHttpRequestInitializer support
Browse files Browse the repository at this point in the history
Add `ClientHttpRequestInitializer` interface that can be used with any
`HttpAccessor` to initialize each `ClientHttpRequest` before it's used.

This provides a useful alternative to `ClientHttpRequestInterceptor`
when users need to configure things like `HttpHeaders`.

Closes spring-projectsgh-22002
  • Loading branch information
philwebb committed Sep 10, 2019
1 parent 333711f commit 9c0901b
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 1 deletion.
@@ -0,0 +1,45 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client;

import org.springframework.http.client.support.HttpAccessor;

/**
* Callback interface for initializing a {@link ClientHttpRequest} prior to it
* being used.
*
* <p> Typically used with {@link HttpAccessor} and subclasses such as
* {@link org.springframework.web.client.RestTemplate RestTemplate} to apply
* consistent settings or headers to each request.
*
* <p>Unlike {@link ClientHttpRequestInterceptor}, this interface can apply
* customizations without needing to read the entire request body into memory.
*
* @author Phillip Webb
* @since 5.2
* @see HttpAccessor#getClientHttpRequestInitializers()
*/
@FunctionalInterface
public interface ClientHttpRequestInitializer {

/**
* Initialize the given client HTTP request.
* @param request the request to configure
*/
void initialize(ClientHttpRequest request);

}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,13 +18,17 @@

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpLogging;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Assert;

Expand All @@ -49,6 +53,8 @@ public abstract class HttpAccessor {

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

private List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();


/**
* Set the request factory that this accessor uses for obtaining client request handles.
Expand All @@ -74,6 +80,28 @@ public ClientHttpRequestFactory getRequestFactory() {
}


/**
* Set the request initializers the this accessor should use.
* <p>The initializers will get sorted according to their order
* before the {@link ClientHttpRequest} is initialized.
*/
public void setClientHttpRequestInitializers(
List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {
if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
this.clientHttpRequestInitializers.clear();
this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
}
}

/**
* Return the request initializers that this accessor uses.
* <p>The returned {@link List} is active and may get appended to.
*/
public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
return this.clientHttpRequestInitializers;
}

/**
* Create a new {@link ClientHttpRequest} via this template's {@link ClientHttpRequestFactory}.
* @param url the URL to connect to
Expand All @@ -85,10 +113,16 @@ public ClientHttpRequestFactory getRequestFactory() {
*/
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}

private void initialize(ClientHttpRequest request) {
this.clientHttpRequestInitializers.forEach(
initializer -> initializer.initialize(request));
}

}
Expand Up @@ -40,6 +40,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.GenericHttpMessageConverter;
Expand Down Expand Up @@ -679,6 +680,32 @@ public void requestInterceptorCanAddExistingHeaderValueWithBody() throws Excepti
verify(response).close();
}

@Test
public void clientHttpRequestInitializerAndRequestInterceptorAreBothApplied() throws Exception {
ClientHttpRequestInitializer initializer = request ->
request.getHeaders().add("MyHeader", "MyInitializerValue");
ClientHttpRequestInterceptor interceptor = (request, body, execution) -> {
request.getHeaders().add("MyHeader", "MyInterceptorValue");
return execution.execute(request, body);
};
template.setClientHttpRequestInitializers(Collections.singletonList(initializer));
template.setInterceptors(Collections.singletonList(interceptor));

MediaType contentType = MediaType.TEXT_PLAIN;
given(converter.canWrite(String.class, contentType)).willReturn(true);
HttpHeaders requestHeaders = new HttpHeaders();
mockSentRequest(POST, "https://example.com", requestHeaders);
mockResponseStatus(HttpStatus.OK);

HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(contentType);
HttpEntity<String> entity = new HttpEntity<>("Hello World", entityHeaders);
template.exchange("https://example.com", POST, entity, Void.class);
assertThat(requestHeaders.get("MyHeader")).contains("MyInterceptorValue", "MyInitializerValue");

verify(response).close();
}

private void mockSentRequest(HttpMethod method, String uri) throws Exception {
mockSentRequest(method, uri, new HttpHeaders());
}
Expand Down

0 comments on commit 9c0901b

Please sign in to comment.