Skip to content

Commit

Permalink
Avoided repeated creation of ReadOnlyHttpHeaders wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed May 11, 2020
1 parent 3276f81 commit df99889
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1769,7 +1769,9 @@ public String toString() {


/**
* Apply a read-only {@code HttpHeaders} wrapper around the given headers.
* Apply a read-only {@code HttpHeaders} wrapper around the given headers
* that also caches the parsed representations of the "Accept" and
* "Content-Type" headers.
*/
public static HttpHeaders readOnlyHttpHeaders(MultiValueMap<String, String> headers) {
Assert.notNull(headers, "HttpHeaders must not be null");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2020 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 @@ -20,6 +20,7 @@
import java.io.OutputStream;

import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
Expand All @@ -35,10 +36,22 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {

private boolean executed = false;

@Nullable
private HttpHeaders readOnlyHeaders;


@Override
public final HttpHeaders getHeaders() {
return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
if (this.readOnlyHeaders != null) {
return this.readOnlyHeaders;
}
else if (this.executed) {
this.readOnlyHeaders = HttpHeaders.readOnlyHttpHeaders(this.headers);
return this.readOnlyHeaders;
}
else {
return this.headers;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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 Down Expand Up @@ -60,6 +60,9 @@ private enum State {NEW, COMMITTING, COMMITTED}

private final List<Supplier<? extends Publisher<Void>>> commitActions = new ArrayList<>(4);

@Nullable
private HttpHeaders readOnlyHeaders;


public AbstractClientHttpRequest() {
this(new HttpHeaders());
Expand All @@ -74,10 +77,16 @@ public AbstractClientHttpRequest(HttpHeaders headers) {

@Override
public HttpHeaders getHeaders() {
if (State.COMMITTED.equals(this.state.get())) {
return HttpHeaders.readOnlyHttpHeaders(this.headers);
if (this.readOnlyHeaders != null) {
return this.readOnlyHeaders;
}
else if (State.COMMITTED.equals(this.state.get())) {
this.readOnlyHeaders = HttpHeaders.readOnlyHttpHeaders(this.headers);
return this.readOnlyHeaders;
}
else {
return this.headers;
}
return this.headers;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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 Down Expand Up @@ -47,6 +47,9 @@ public class ServletServerHttpResponse implements ServerHttpResponse {

private boolean bodyUsed = false;

@Nullable
private HttpHeaders readOnlyHeaders;


/**
* Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
Expand Down Expand Up @@ -74,7 +77,16 @@ public void setStatusCode(HttpStatus status) {

@Override
public HttpHeaders getHeaders() {
return (this.headersWritten ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
if (this.readOnlyHeaders != null) {
return this.readOnlyHeaders;
}
else if (this.headersWritten) {
this.readOnlyHeaders = HttpHeaders.readOnlyHttpHeaders(this.headers);
return this.readOnlyHeaders;
}
else {
return this.headers;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ private enum State {NEW, COMMITTING, COMMITTED}

private final List<Supplier<? extends Mono<Void>>> commitActions = new ArrayList<>(4);

@Nullable
private HttpHeaders readOnlyHeaders;


public AbstractServerHttpResponse(DataBufferFactory dataBufferFactory) {
this(dataBufferFactory, new HttpHeaders());
Expand Down Expand Up @@ -155,8 +158,16 @@ public Integer getStatusCodeValue() {

@Override
public HttpHeaders getHeaders() {
return (this.state.get() == State.COMMITTED ?
HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
if (this.readOnlyHeaders != null) {
return this.readOnlyHeaders;
}
else if (this.state.get() == State.COMMITTED) {
this.readOnlyHeaders = HttpHeaders.readOnlyHttpHeaders(this.headers);
return this.readOnlyHeaders;
}
else {
return this.headers;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,29 +234,28 @@ HttpRequest request() {

private class DefaultHeaders implements Headers {

private HttpHeaders delegate() {
return response.getHeaders();
}
private final HttpHeaders httpHeaders =
HttpHeaders.readOnlyHttpHeaders(response.getHeaders());

@Override
public OptionalLong contentLength() {
return toOptionalLong(delegate().getContentLength());
return toOptionalLong(this.httpHeaders.getContentLength());
}

@Override
public Optional<MediaType> contentType() {
return Optional.ofNullable(delegate().getContentType());
return Optional.ofNullable(this.httpHeaders.getContentType());
}

@Override
public List<String> header(String headerName) {
List<String> headerValues = delegate().get(headerName);
List<String> headerValues = this.httpHeaders.get(headerName);
return (headerValues != null ? headerValues : Collections.emptyList());
}

@Override
public HttpHeaders asHttpHeaders() {
return HttpHeaders.readOnlyHttpHeaders(delegate());
return this.httpHeaders;
}

private OptionalLong toOptionalLong(long value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ public WebClient build() {
.map(filter -> filter.apply(exchange))
.orElse(exchange) : exchange);
return new DefaultWebClient(filteredExchange, initUriBuilderFactory(),
this.defaultHeaders != null ? unmodifiableCopy(this.defaultHeaders) : null,
this.defaultCookies != null ? unmodifiableCopy(this.defaultCookies) : null,
this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null,
this.defaultCookies != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultCookies) : null,
this.defaultRequest, new DefaultWebClientBuilder(this));
}

Expand Down Expand Up @@ -308,10 +308,6 @@ private UriBuilderFactory initUriBuilderFactory() {
return factory;
}

private static HttpHeaders unmodifiableCopy(HttpHeaders headers) {
return HttpHeaders.readOnlyHttpHeaders(headers);
}

private static <K, V> MultiValueMap<K, V> unmodifiableCopy(MultiValueMap<K, V> map) {
return CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>(map));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,61 +260,60 @@ public String toString() {


private class DefaultHeaders implements Headers {

private HttpHeaders delegate() {
return request().getHeaders();
}

private final HttpHeaders httpHeaders =
HttpHeaders.readOnlyHttpHeaders(request().getHeaders());

@Override
public List<MediaType> accept() {
return delegate().getAccept();
return this.httpHeaders.getAccept();
}

@Override
public List<Charset> acceptCharset() {
return delegate().getAcceptCharset();
return this.httpHeaders.getAcceptCharset();
}

@Override
public List<Locale.LanguageRange> acceptLanguage() {
return delegate().getAcceptLanguage();
return this.httpHeaders.getAcceptLanguage();
}

@Override
public OptionalLong contentLength() {
long value = delegate().getContentLength();
long value = this.httpHeaders.getContentLength();
return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty());
}

@Override
public Optional<MediaType> contentType() {
return Optional.ofNullable(delegate().getContentType());
return Optional.ofNullable(this.httpHeaders.getContentType());
}

@Override
public InetSocketAddress host() {
return delegate().getHost();
return this.httpHeaders.getHost();
}

@Override
public List<HttpRange> range() {
return delegate().getRange();
return this.httpHeaders.getRange();
}

@Override
public List<String> header(String headerName) {
List<String> headerValues = delegate().get(headerName);
List<String> headerValues = this.httpHeaders.get(headerName);
return (headerValues != null ? headerValues : Collections.emptyList());
}

@Override
public HttpHeaders asHttpHeaders() {
return HttpHeaders.readOnlyHttpHeaders(delegate());
return this.httpHeaders;
}

@Override
public String toString() {
return delegate().toString();
return this.httpHeaders.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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 Down Expand Up @@ -60,6 +60,8 @@ public class DefaultClientResponseTests {

private ClientHttpResponse mockResponse;

private final HttpHeaders httpHeaders = new HttpHeaders();

private ExchangeStrategies mockExchangeStrategies;

private DefaultClientResponse defaultClientResponse;
Expand All @@ -68,6 +70,7 @@ public class DefaultClientResponseTests {
@BeforeEach
public void createMocks() {
mockResponse = mock(ClientHttpResponse.class);
given(mockResponse.getHeaders()).willReturn(this.httpHeaders);
mockExchangeStrategies = mock(ExchangeStrategies.class);
defaultClientResponse = new DefaultClientResponse(mockResponse, mockExchangeStrategies, "", "", () -> null);
}
Expand All @@ -91,7 +94,6 @@ public void rawStatusCode() {

@Test
public void header() {
HttpHeaders httpHeaders = new HttpHeaders();
long contentLength = 42L;
httpHeaders.setContentLength(contentLength);
MediaType contentType = MediaType.TEXT_PLAIN;
Expand Down Expand Up @@ -233,7 +235,6 @@ public void toEntityWithUnknownStatusCode() throws Exception {
= factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
given(mockResponse.getHeaders()).willReturn(httpHeaders);
given(mockResponse.getStatusCode()).willThrow(new IllegalArgumentException("999"));
Expand Down Expand Up @@ -293,13 +294,12 @@ public void toEntityList() {
}

@Test
public void toEntityListWithUnknownStatusCode() throws Exception {
public void toEntityListWithUnknownStatusCode() {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
given(mockResponse.getHeaders()).willReturn(httpHeaders);
given(mockResponse.getStatusCode()).willThrow(new IllegalArgumentException("999"));
Expand All @@ -321,8 +321,7 @@ public void toEntityListWithUnknownStatusCode() throws Exception {
@Test
public void toEntityListTypeReference() {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
DefaultDataBuffer dataBuffer = factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);

mockTextPlainResponse(body);
Expand All @@ -332,8 +331,7 @@ public void toEntityListTypeReference() {
given(mockExchangeStrategies.messageReaders()).willReturn(messageReaders);

ResponseEntity<List<String>> result = defaultClientResponse.toEntityList(
new ParameterizedTypeReference<String>() {
}).block();
new ParameterizedTypeReference<String>() {}).block();
assertThat(result.getBody()).isEqualTo(Collections.singletonList("foo"));
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
Expand All @@ -342,9 +340,7 @@ public void toEntityListTypeReference() {


private void mockTextPlainResponse(Flux<DataBuffer> body) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
given(mockResponse.getHeaders()).willReturn(httpHeaders);
given(mockResponse.getStatusCode()).willReturn(HttpStatus.OK);
given(mockResponse.getRawStatusCode()).willReturn(HttpStatus.OK.value());
given(mockResponse.getBody()).willReturn(body);
Expand Down
Loading

0 comments on commit df99889

Please sign in to comment.