Skip to content

Commit 3f7d3cb

Browse files
committed
Add chaining methods to ClientHttpRequestInterceptor
See gh-34169
1 parent c970a60 commit 3f7d3cb

File tree

2 files changed

+40
-28
lines changed

2 files changed

+40
-28
lines changed

spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestInterceptor.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020

2121
import org.springframework.http.HttpRequest;
22+
import org.springframework.util.Assert;
2223

2324
/**
2425
* Contract to intercept client-side HTTP requests. Implementations can be
@@ -60,4 +61,32 @@ public interface ClientHttpRequestInterceptor {
6061
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
6162
throws IOException;
6263

64+
/**
65+
* Return a new interceptor that invokes {@code this} interceptor first, and
66+
* then the one that's passed in.
67+
* @param interceptor the next interceptor
68+
* @return a new interceptor that chains the two
69+
* @since 7.0
70+
*/
71+
default ClientHttpRequestInterceptor andThen(ClientHttpRequestInterceptor interceptor) {
72+
Assert.notNull(interceptor, "ClientHttpRequestInterceptor must not be null");
73+
return (request, body, execution) -> {
74+
ClientHttpRequestExecution nextExecution =
75+
(nextRequest, nextBody) -> interceptor.intercept(nextRequest, nextBody, execution);
76+
return intercept(request, body, nextExecution);
77+
};
78+
}
79+
80+
/**
81+
* Return a new execution that invokes {@code this} interceptor, and then
82+
* delegates to the given execution.
83+
* @param execution the execution to delegate to
84+
* @return a new execution instance
85+
* @since 7.0
86+
*/
87+
default ClientHttpRequestExecution apply(ClientHttpRequestExecution execution) {
88+
Assert.notNull(execution, "ClientHttpRequestExecution must not be null");
89+
return (request, body) -> intercept(request, body, execution);
90+
}
91+
6392
}

spring-web/src/main/java/org/springframework/http/client/InterceptingClientHttpRequest.java

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.io.OutputStream;
2121
import java.net.URI;
2222
import java.util.List;
23-
import java.util.ListIterator;
2423

2524
import org.springframework.http.HttpHeaders;
2625
import org.springframework.http.HttpMethod;
@@ -69,39 +68,23 @@ public URI getURI() {
6968

7069
@Override
7170
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
72-
ClientHttpRequestExecution requestExecution = new DelegatingRequestExecution(this.requestFactory);
73-
ListIterator<ClientHttpRequestInterceptor> iterator = this.interceptors.listIterator(this.interceptors.size());
74-
while (iterator.hasPrevious()) {
75-
ClientHttpRequestInterceptor interceptor = iterator.previous();
76-
requestExecution = new InterceptingRequestExecution(interceptor, requestExecution);
77-
}
78-
return requestExecution.execute(this, bufferedOutput);
71+
return getExecution().execute(this, bufferedOutput);
7972
}
8073

81-
82-
private static class InterceptingRequestExecution implements ClientHttpRequestExecution {
83-
84-
private final ClientHttpRequestInterceptor interceptor;
85-
86-
private final ClientHttpRequestExecution nextExecution;
87-
88-
public InterceptingRequestExecution(ClientHttpRequestInterceptor interceptor, ClientHttpRequestExecution nextExecution) {
89-
this.interceptor = interceptor;
90-
this.nextExecution = nextExecution;
91-
}
92-
93-
@Override
94-
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
95-
return this.interceptor.intercept(request, body, this.nextExecution);
96-
}
97-
74+
private ClientHttpRequestExecution getExecution() {
75+
ClientHttpRequestExecution execution = new EndOfChainRequestExecution(this.requestFactory);
76+
return this.interceptors.stream()
77+
.reduce(ClientHttpRequestInterceptor::andThen)
78+
.map(interceptor -> interceptor.apply(execution))
79+
.orElse(execution);
9880
}
9981

100-
private static class DelegatingRequestExecution implements ClientHttpRequestExecution {
82+
83+
private static class EndOfChainRequestExecution implements ClientHttpRequestExecution {
10184

10285
private final ClientHttpRequestFactory requestFactory;
10386

104-
public DelegatingRequestExecution(ClientHttpRequestFactory requestFactory) {
87+
public EndOfChainRequestExecution(ClientHttpRequestFactory requestFactory) {
10588
this.requestFactory = requestFactory;
10689
}
10790

0 commit comments

Comments
 (0)