Rearrange HttpHeaders adapters
This commit moves HttpHeaders that are used in multiple places (client and server, reactive and non-reactive) to a new, separate http.support package. Closes gh-30823
This commit is contained in:
parent
8e9528de13
commit
3d2befc84a
|
|
@ -20,13 +20,14 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.support.HttpComponentsHeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation based on
|
||||
|
|
@ -65,10 +66,8 @@ final class HttpComponentsClientHttpResponse implements ClientHttpResponse {
|
|||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
if (this.headers == null) {
|
||||
this.headers = new HttpHeaders();
|
||||
for (Header header : this.httpResponse.getHeaders()) {
|
||||
this.headers.add(header.getName(), header.getValue());
|
||||
}
|
||||
MultiValueMap<String, String> adapter = new HttpComponentsHeadersAdapter(this.httpResponse);
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(adapter);
|
||||
}
|
||||
return this.headers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ import org.eclipse.jetty.client.api.Response;
|
|||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation based on based on Jetty's
|
||||
* {@link ClientHttpResponse} implementation based on Jetty's
|
||||
* {@link org.eclipse.jetty.client.HttpClient}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import org.springframework.core.io.buffer.DataBufferFactory;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.support.HttpComponentsHeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.springframework.core.io.buffer.DataBufferFactory;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.support.HttpComponentsHeadersAdapter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import org.springframework.core.io.buffer.DataBufferUtils;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.JettyHeadersAdapter;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation for the Jetty ReactiveStreams HTTP client.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import org.springframework.core.io.buffer.DataBuffer;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.client.JettyHeadersAdapter;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
|
|||
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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.reactive;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import io.netty5.handler.codec.http.headers.HttpHeaders;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Netty HTTP headers.
|
||||
*
|
||||
* <p>There is a duplicate of this class in the server package!
|
||||
*
|
||||
* <p>This class is based on {@link NettyHeadersAdapter}.
|
||||
*
|
||||
* @author Violeta Georgieva
|
||||
* @since 6.0
|
||||
*/
|
||||
class Netty5HeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
Netty5HeadersAdapter(HttpHeaders headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFirst(String key) {
|
||||
CharSequence value = this.headers.get(key);
|
||||
return (value != null ? value.toString() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, @Nullable String value) {
|
||||
if (value != null) {
|
||||
this.headers.add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(String key, List<? extends String> values) {
|
||||
this.headers.add(key, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(MultiValueMap<String, String> values) {
|
||||
values.forEach(this.headers::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, @Nullable String value) {
|
||||
if (value != null) {
|
||||
this.headers.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAll(Map<String, String> values) {
|
||||
values.forEach(this.headers::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toSingleValueMap() {
|
||||
Map<String, String> singleValueMap = CollectionUtils.newLinkedHashMap(this.headers.size());
|
||||
this.headers.forEach(entry -> singleValueMap.putIfAbsent(
|
||||
entry.getKey().toString(), entry.getValue().toString()));
|
||||
return singleValueMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.headers.names().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.headers.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return (key instanceof String headerName && this.headers.contains(headerName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return (value instanceof CharSequence &&
|
||||
StreamSupport.stream(this.headers.spliterator(), false)
|
||||
.anyMatch(entry -> value.equals(entry.getValue())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<String> get(Object key) {
|
||||
Iterator<CharSequence> iterator = this.headers.valuesIterator((CharSequence) key);
|
||||
if (iterator.hasNext()) {
|
||||
List<String> result = new ArrayList<>();
|
||||
iterator.forEachRemaining(value -> result.add(value.toString()));
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> put(String key, @Nullable List<String> value) {
|
||||
List<String> previousValues = get(key);
|
||||
this.headers.set(key, value);
|
||||
return previousValues;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
if (key instanceof String headerName) {
|
||||
List<String> previousValues = get(headerName);
|
||||
this.headers.remove(headerName);
|
||||
return previousValues;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> map) {
|
||||
map.forEach(this.headers::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.headers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return new HeaderNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
List<List<String>> result = new ArrayList<>(this.headers.size());
|
||||
forEach((key, value) -> result.add(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return new AbstractSet<>() {
|
||||
@Override
|
||||
public Iterator<Entry<String, List<String>>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return org.springframework.http.HttpHeaders.formatHeaders(this);
|
||||
}
|
||||
|
||||
|
||||
private class EntryIterator implements Iterator<Entry<String, List<String>>> {
|
||||
|
||||
private final Iterator<CharSequence> names = headers.names().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.names.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, List<String>> next() {
|
||||
return new HeaderEntry(this.names.next());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class HeaderEntry implements Entry<String, List<String>> {
|
||||
|
||||
private final CharSequence key;
|
||||
|
||||
HeaderEntry(CharSequence key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.key.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getValue() {
|
||||
List<String> values = get(this.key);
|
||||
return (values != null ? values : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> setValue(List<String> value) {
|
||||
List<String> previousValues = getValue();
|
||||
headers.set(this.key, value);
|
||||
return previousValues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class HeaderNames extends AbstractSet<String> {
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new HeaderNamesIterator(headers.names().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.names().size();
|
||||
}
|
||||
}
|
||||
|
||||
private final class HeaderNamesIterator implements Iterator<String> {
|
||||
|
||||
private final Iterator<CharSequence> iterator;
|
||||
|
||||
@Nullable
|
||||
private CharSequence currentName;
|
||||
|
||||
private HeaderNamesIterator(Iterator<CharSequence> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
this.currentName = this.iterator.next();
|
||||
return this.currentName.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
if (!headers.contains(this.currentName)) {
|
||||
throw new IllegalStateException("Header not present: " + this.currentName);
|
||||
}
|
||||
headers.remove(this.currentName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ZeroCopyHttpOutputMessage;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation for the Reactor-Netty HTTP client.
|
||||
|
|
@ -136,7 +137,7 @@ class ReactorClientHttpRequest extends AbstractClientHttpRequest implements Zero
|
|||
|
||||
@Override
|
||||
protected HttpHeaders initReadOnlyHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(new NettyHeadersAdapter(this.request.requestHeaders()));
|
||||
return HttpHeaders.readOnlyHttpHeaders(new Netty4HeadersAdapter(this.request.requestHeaders()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
@ -74,7 +75,7 @@ class ReactorClientHttpResponse implements ClientHttpResponse {
|
|||
*/
|
||||
public ReactorClientHttpResponse(HttpClientResponse response, Connection connection) {
|
||||
this.response = response;
|
||||
MultiValueMap<String, String> adapter = new NettyHeadersAdapter(response.responseHeaders());
|
||||
MultiValueMap<String, String> adapter = new Netty4HeadersAdapter(response.responseHeaders());
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(adapter);
|
||||
this.inbound = connection.inbound();
|
||||
this.bufferFactory = new NettyDataBufferFactory(connection.outbound().alloc());
|
||||
|
|
@ -87,7 +88,7 @@ class ReactorClientHttpResponse implements ClientHttpResponse {
|
|||
@Deprecated
|
||||
public ReactorClientHttpResponse(HttpClientResponse response, NettyInbound inbound, ByteBufAllocator alloc) {
|
||||
this.response = response;
|
||||
MultiValueMap<String, String> adapter = new NettyHeadersAdapter(response.responseHeaders());
|
||||
MultiValueMap<String, String> adapter = new Netty4HeadersAdapter(response.responseHeaders());
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(adapter);
|
||||
this.inbound = inbound;
|
||||
this.bufferFactory = new NettyDataBufferFactory(alloc);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import org.springframework.core.io.buffer.Netty5DataBufferFactory;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ZeroCopyHttpOutputMessage;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation for the Reactor Netty 2 (Netty 5) HTTP client.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.server.reactive;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Jetty HTTP headers.
|
||||
*
|
||||
* <p>There is a duplicate of this class in the client package!
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.1.1
|
||||
*/
|
||||
class JettyHeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpFields.Mutable headers;
|
||||
|
||||
|
||||
JettyHeadersAdapter(HttpFields.Mutable headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getFirst(String key) {
|
||||
return this.headers.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, @Nullable String value) {
|
||||
this.headers.add(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(String key, List<? extends String> values) {
|
||||
values.forEach(value -> add(key, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(MultiValueMap<String, String> values) {
|
||||
values.forEach(this::addAll);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, @Nullable String value) {
|
||||
this.headers.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAll(Map<String, String> values) {
|
||||
values.forEach(this::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toSingleValueMap() {
|
||||
Map<String, String> singleValueMap = CollectionUtils.newLinkedHashMap(this.headers.size());
|
||||
Iterator<HttpField> iterator = this.headers.iterator();
|
||||
iterator.forEachRemaining(field -> {
|
||||
if (!singleValueMap.containsKey(field.getName())) {
|
||||
singleValueMap.put(field.getName(), field.getValue());
|
||||
}
|
||||
});
|
||||
return singleValueMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.headers.getFieldNamesCollection().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return (this.headers.size() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return (key instanceof String headerName && this.headers.contains(headerName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return (value instanceof String searchString &&
|
||||
this.headers.stream().anyMatch(field -> field.contains(searchString)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> get(Object key) {
|
||||
if (containsKey(key)) {
|
||||
return this.headers.getValuesList((String) key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value) {
|
||||
List<String> oldValues = get(key);
|
||||
this.headers.put(key, value);
|
||||
return oldValues;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
if (key instanceof String headerName) {
|
||||
List<String> oldValues = get(key);
|
||||
this.headers.remove(headerName);
|
||||
return oldValues;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> map) {
|
||||
map.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.headers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return new HeaderNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
return this.headers.getFieldNamesCollection().stream()
|
||||
.map(this.headers::getValuesList).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return new AbstractSet<>() {
|
||||
@Override
|
||||
public Iterator<Entry<String, List<String>>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return HttpHeaders.formatHeaders(this);
|
||||
}
|
||||
|
||||
|
||||
private class EntryIterator implements Iterator<Entry<String, List<String>>> {
|
||||
|
||||
private final Iterator<String> names = headers.getFieldNamesCollection().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.names.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, List<String>> next() {
|
||||
return new HeaderEntry(this.names.next());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class HeaderEntry implements Entry<String, List<String>> {
|
||||
|
||||
private final String key;
|
||||
|
||||
HeaderEntry(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getValue() {
|
||||
return headers.getValuesList(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> setValue(List<String> value) {
|
||||
List<String> previousValues = headers.getValuesList(this.key);
|
||||
headers.put(this.key, value);
|
||||
return previousValues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class HeaderNames extends AbstractSet<String> {
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new HeaderNamesIterator(headers.getFieldNamesCollection().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.getFieldNamesCollection().size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final class HeaderNamesIterator implements Iterator<String> {
|
||||
|
||||
private final Iterator<String> iterator;
|
||||
|
||||
@Nullable
|
||||
private String currentName;
|
||||
|
||||
private HeaderNamesIterator(Iterator<String> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
this.currentName = this.iterator.next();
|
||||
return this.currentName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
if (!headers.contains(this.currentName)) {
|
||||
throw new IllegalStateException("Header not present: " + this.currentName);
|
||||
}
|
||||
headers.remove(this.currentName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.server.Response;
|
|||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.server.reactive;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Netty HTTP headers.
|
||||
*
|
||||
* <p>There is a duplicate of this class in the client package!
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class NettyHeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
NettyHeadersAdapter(HttpHeaders headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFirst(String key) {
|
||||
return this.headers.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, @Nullable String value) {
|
||||
if (value != null) {
|
||||
this.headers.add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(String key, List<? extends String> values) {
|
||||
this.headers.add(key, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(MultiValueMap<String, String> values) {
|
||||
values.forEach(this.headers::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, @Nullable String value) {
|
||||
if (value != null) {
|
||||
this.headers.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAll(Map<String, String> values) {
|
||||
values.forEach(this.headers::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toSingleValueMap() {
|
||||
Map<String, String> singleValueMap = CollectionUtils.newLinkedHashMap(this.headers.size());
|
||||
this.headers.entries()
|
||||
.forEach(entry -> {
|
||||
if (!singleValueMap.containsKey(entry.getKey())) {
|
||||
singleValueMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
});
|
||||
return singleValueMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.headers.names().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.headers.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return (key instanceof String headerName && this.headers.contains(headerName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return (value instanceof String &&
|
||||
this.headers.entries().stream()
|
||||
.anyMatch(entry -> value.equals(entry.getValue())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<String> get(Object key) {
|
||||
if (containsKey(key)) {
|
||||
return this.headers.getAll((String) key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> put(String key, @Nullable List<String> value) {
|
||||
List<String> previousValues = this.headers.getAll(key);
|
||||
this.headers.set(key, value);
|
||||
return previousValues;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
if (key instanceof String headerName) {
|
||||
List<String> previousValues = this.headers.getAll(headerName);
|
||||
this.headers.remove(headerName);
|
||||
return previousValues;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> map) {
|
||||
map.forEach(this.headers::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.headers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return new HeaderNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
return this.headers.names().stream()
|
||||
.map(this.headers::getAll).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return new AbstractSet<>() {
|
||||
@Override
|
||||
public Iterator<Entry<String, List<String>>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return org.springframework.http.HttpHeaders.formatHeaders(this);
|
||||
}
|
||||
|
||||
|
||||
private class EntryIterator implements Iterator<Entry<String, List<String>>> {
|
||||
|
||||
private final Iterator<String> names = headers.names().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.names.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, List<String>> next() {
|
||||
return new HeaderEntry(this.names.next());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class HeaderEntry implements Entry<String, List<String>> {
|
||||
|
||||
private final String key;
|
||||
|
||||
HeaderEntry(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getValue() {
|
||||
return headers.getAll(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> setValue(List<String> value) {
|
||||
List<String> previousValues = headers.getAll(this.key);
|
||||
headers.set(this.key, value);
|
||||
return previousValues;
|
||||
}
|
||||
}
|
||||
|
||||
private class HeaderNames extends AbstractSet<String> {
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new HeaderNamesIterator(headers.names().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return headers.names().size();
|
||||
}
|
||||
}
|
||||
|
||||
private final class HeaderNamesIterator implements Iterator<String> {
|
||||
|
||||
private final Iterator<String> iterator;
|
||||
|
||||
@Nullable
|
||||
private String currentName;
|
||||
|
||||
private HeaderNamesIterator(Iterator<String> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
this.currentName = this.iterator.next();
|
||||
return this.currentName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
if (!headers.contains(this.currentName)) {
|
||||
throw new IllegalStateException("Header not present: " + this.currentName);
|
||||
}
|
||||
headers.remove(this.currentName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ import org.springframework.core.io.buffer.Netty5DataBufferFactory;
|
|||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpLogging;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ZeroCopyHttpOutputMessage;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
|||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpLogging;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
@ -65,7 +66,7 @@ class ReactorServerHttpRequest extends AbstractServerHttpRequest {
|
|||
throws URISyntaxException {
|
||||
|
||||
super(HttpMethod.valueOf(request.method().name()), ReactorUriHelper.createUri(request), "",
|
||||
new NettyHeadersAdapter(request.requestHeaders()));
|
||||
new Netty4HeadersAdapter(request.requestHeaders()));
|
||||
Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
|
||||
this.request = request;
|
||||
this.bufferFactory = bufferFactory;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ZeroCopyHttpOutputMessage;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
|
||||
/**
|
||||
* Adapt {@link ServerHttpResponse} to the {@link HttpServerResponse}.
|
||||
|
|
@ -55,7 +56,7 @@ class ReactorServerHttpResponse extends AbstractServerHttpResponse implements Ze
|
|||
|
||||
|
||||
public ReactorServerHttpResponse(HttpServerResponse response, DataBufferFactory bufferFactory) {
|
||||
super(bufferFactory, new HttpHeaders(new NettyHeadersAdapter(Objects.requireNonNull(response,
|
||||
super(bufferFactory, new HttpHeaders(new Netty4HeadersAdapter(Objects.requireNonNull(response,
|
||||
"HttpServerResponse must not be null").responseHeaders())));
|
||||
this.response = response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client.reactive;
|
||||
package org.springframework.http.support;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -32,6 +32,7 @@ import org.apache.hc.core5.http.HttpMessage;
|
|||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
|
|
@ -40,14 +41,19 @@ import org.springframework.util.MultiValueMap;
|
|||
* HttpClient headers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.3
|
||||
* @since 6.1
|
||||
*/
|
||||
class HttpComponentsHeadersAdapter implements MultiValueMap<String, String> {
|
||||
public final class HttpComponentsHeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpMessage message;
|
||||
|
||||
|
||||
HttpComponentsHeadersAdapter(HttpMessage message) {
|
||||
/**
|
||||
* Create a new {@code HttpComponentsHeadersAdapter} based on the given
|
||||
* {@code HttpMessage}.
|
||||
*/
|
||||
public HttpComponentsHeadersAdapter(HttpMessage message) {
|
||||
Assert.notNull(message, "Message must not be null");
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client;
|
||||
package org.springframework.http.support;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
|
|
@ -35,12 +35,10 @@ import org.springframework.util.MultiValueMap;
|
|||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Jetty HTTP headers.
|
||||
*
|
||||
* <p>There is a duplicate of this class in the server package!
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 5.3
|
||||
* @since 6.1
|
||||
*/
|
||||
public final class JettyHeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client.reactive;
|
||||
package org.springframework.http.support;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
|
|
@ -26,24 +26,28 @@ import java.util.Set;
|
|||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Netty HTTP headers.
|
||||
*
|
||||
* <p>There is a duplicate of this class in the server package!
|
||||
* {@code MultiValueMap} implementation for wrapping Netty 4 HTTP headers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 5.3
|
||||
* @since 6.1
|
||||
*/
|
||||
class NettyHeadersAdapter implements MultiValueMap<String, String> {
|
||||
public final class Netty4HeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
NettyHeadersAdapter(HttpHeaders headers) {
|
||||
/**
|
||||
* Creates a new {@code Netty4HeadersAdapter} based on the given
|
||||
* {@code HttpHeaders}.
|
||||
*/
|
||||
public Netty4HeadersAdapter(HttpHeaders headers) {
|
||||
Assert.notNull(headers, "Headers must not be null");
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.server.reactive;
|
||||
package org.springframework.http.support;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -29,23 +29,27 @@ import java.util.stream.StreamSupport;
|
|||
import io.netty5.handler.codec.http.headers.HttpHeaders;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@code MultiValueMap} implementation for wrapping Netty HTTP headers.
|
||||
*
|
||||
* <p>This class is based on {@link NettyHeadersAdapter}.
|
||||
*
|
||||
* @author Violeta Georgieva
|
||||
* @since 6.0
|
||||
* @since 6.1
|
||||
*/
|
||||
final class Netty5HeadersAdapter implements MultiValueMap<String, String> {
|
||||
public final class Netty5HeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
Netty5HeadersAdapter(HttpHeaders headers) {
|
||||
/**
|
||||
* Create a new {@code Netty5HeadersAdapter} based on the given
|
||||
* {@code HttpHeaders}.
|
||||
*/
|
||||
public Netty5HeadersAdapter(HttpHeaders headers) {
|
||||
Assert.notNull(headers, "Headers must not be null");
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* This package provides internal HTTP support classes,
|
||||
* to be used by higher-level client and server classes.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.http.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
|
@ -33,6 +33,9 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
@ -134,7 +137,7 @@ class HeadersAdaptersTests {
|
|||
static Stream<Arguments> headers() {
|
||||
return Stream.of(
|
||||
arguments(named("Map", CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH)))),
|
||||
arguments(named("Netty", new NettyHeadersAdapter(new DefaultHttpHeaders()))),
|
||||
arguments(named("Netty", new Netty4HeadersAdapter(new DefaultHttpHeaders()))),
|
||||
arguments(named("Netty", new Netty5HeadersAdapter(io.netty5.handler.codec.http.headers.HttpHeaders.newHeaders()))),
|
||||
arguments(named("Tomcat", new TomcatHeadersAdapter(new MimeHeaders()))),
|
||||
arguments(named("Undertow", new UndertowHeadersAdapter(new HeaderMap()))),
|
||||
|
|
|
|||
Loading…
Reference in New Issue