Skip to content

Commit e4dc863

Browse files
committed
Fix headers keySet in WebFlux adapters
Prior to this commit, WebFlux native headers adapters would delegate the `httpHeaders.keySet` to underlying implementations that do not honor the `remove*` methods. This commit fixes the `Set` implementation backing the `httpHeaders.keySet` and ensures that headers can be safely removed from the set. Fixes gh-26361
1 parent 689b556 commit e4dc863

File tree

7 files changed

+328
-21
lines changed

7 files changed

+328
-21
lines changed

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -154,7 +154,7 @@ public void clear() {
154154

155155
@Override
156156
public Set<String> keySet() {
157-
return this.headers.getFieldNamesCollection();
157+
return new HeaderNames();
158158
}
159159

160160
@Override
@@ -227,4 +227,52 @@ public List<String> setValue(List<String> value) {
227227
}
228228
}
229229

230+
231+
private class HeaderNames extends AbstractSet<String> {
232+
233+
@Override
234+
public Iterator<String> iterator() {
235+
return new HeaderNamesIterator(headers.getFieldNamesCollection().iterator());
236+
}
237+
238+
@Override
239+
public int size() {
240+
return headers.getFieldNamesCollection().size();
241+
}
242+
}
243+
244+
private final class HeaderNamesIterator implements Iterator<String> {
245+
246+
private final Iterator<String> iterator;
247+
248+
@Nullable
249+
private String currentName;
250+
251+
private HeaderNamesIterator(Iterator<String> iterator) {
252+
this.iterator = iterator;
253+
}
254+
255+
@Override
256+
public boolean hasNext() {
257+
return this.iterator.hasNext();
258+
}
259+
260+
@Override
261+
public String next() {
262+
this.currentName = this.iterator.next();
263+
return this.currentName;
264+
}
265+
266+
@Override
267+
public void remove() {
268+
if (this.currentName == null) {
269+
throw new IllegalStateException("No current Header in iterator");
270+
}
271+
if (!headers.containsKey(this.currentName)) {
272+
throw new IllegalStateException("Header not present: " + this.currentName);
273+
}
274+
headers.remove(this.currentName);
275+
}
276+
}
277+
230278
}

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -157,7 +157,7 @@ public void clear() {
157157

158158
@Override
159159
public Set<String> keySet() {
160-
return this.headers.names();
160+
return new HeaderNames();
161161
}
162162

163163
@Override
@@ -230,4 +230,52 @@ public List<String> setValue(List<String> value) {
230230
}
231231
}
232232

233+
234+
private class HeaderNames extends AbstractSet<String> {
235+
236+
@Override
237+
public Iterator<String> iterator() {
238+
return new HeaderNamesIterator(headers.names().iterator());
239+
}
240+
241+
@Override
242+
public int size() {
243+
return headers.names().size();
244+
}
245+
}
246+
247+
private final class HeaderNamesIterator implements Iterator<String> {
248+
249+
private final Iterator<String> iterator;
250+
251+
@Nullable
252+
private String currentName;
253+
254+
private HeaderNamesIterator(Iterator<String> iterator) {
255+
this.iterator = iterator;
256+
}
257+
258+
@Override
259+
public boolean hasNext() {
260+
return this.iterator.hasNext();
261+
}
262+
263+
@Override
264+
public String next() {
265+
this.currentName = this.iterator.next();
266+
return this.currentName;
267+
}
268+
269+
@Override
270+
public void remove() {
271+
if (this.currentName == null) {
272+
throw new IllegalStateException("No current Header in iterator");
273+
}
274+
if (!headers.contains(this.currentName)) {
275+
throw new IllegalStateException("Header not present: " + this.currentName);
276+
}
277+
headers.remove(this.currentName);
278+
}
279+
}
280+
233281
}

spring-web/src/main/java/org/springframework/http/server/reactive/JettyHeadersAdapter.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -154,7 +154,7 @@ public void clear() {
154154

155155
@Override
156156
public Set<String> keySet() {
157-
return this.headers.getFieldNamesCollection();
157+
return new HeaderNames();
158158
}
159159

160160
@Override
@@ -227,4 +227,52 @@ public List<String> setValue(List<String> value) {
227227
}
228228
}
229229

230+
231+
private class HeaderNames extends AbstractSet<String> {
232+
233+
@Override
234+
public Iterator<String> iterator() {
235+
return new HeaderNamesIterator(headers.getFieldNamesCollection().iterator());
236+
}
237+
238+
@Override
239+
public int size() {
240+
return headers.getFieldNamesCollection().size();
241+
}
242+
}
243+
244+
private final class HeaderNamesIterator implements Iterator<String> {
245+
246+
private final Iterator<String> iterator;
247+
248+
@Nullable
249+
private String currentName;
250+
251+
private HeaderNamesIterator(Iterator<String> iterator) {
252+
this.iterator = iterator;
253+
}
254+
255+
@Override
256+
public boolean hasNext() {
257+
return this.iterator.hasNext();
258+
}
259+
260+
@Override
261+
public String next() {
262+
this.currentName = this.iterator.next();
263+
return this.currentName;
264+
}
265+
266+
@Override
267+
public void remove() {
268+
if (this.currentName == null) {
269+
throw new IllegalStateException("No current Header in iterator");
270+
}
271+
if (!headers.containsKey(this.currentName)) {
272+
throw new IllegalStateException("Header not present: " + this.currentName);
273+
}
274+
headers.remove(this.currentName);
275+
}
276+
}
277+
230278
}

spring-web/src/main/java/org/springframework/http/server/reactive/NettyHeadersAdapter.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -157,7 +157,7 @@ public void clear() {
157157

158158
@Override
159159
public Set<String> keySet() {
160-
return this.headers.names();
160+
return new HeaderNames();
161161
}
162162

163163
@Override
@@ -230,4 +230,51 @@ public List<String> setValue(List<String> value) {
230230
}
231231
}
232232

233+
private class HeaderNames extends AbstractSet<String> {
234+
235+
@Override
236+
public Iterator<String> iterator() {
237+
return new HeaderNamesIterator(headers.names().iterator());
238+
}
239+
240+
@Override
241+
public int size() {
242+
return headers.names().size();
243+
}
244+
}
245+
246+
private final class HeaderNamesIterator implements Iterator<String> {
247+
248+
private final Iterator<String> iterator;
249+
250+
@Nullable
251+
private String currentName;
252+
253+
private HeaderNamesIterator(Iterator<String> iterator) {
254+
this.iterator = iterator;
255+
}
256+
257+
@Override
258+
public boolean hasNext() {
259+
return this.iterator.hasNext();
260+
}
261+
262+
@Override
263+
public String next() {
264+
this.currentName = this.iterator.next();
265+
return this.currentName;
266+
}
267+
268+
@Override
269+
public void remove() {
270+
if (this.currentName == null) {
271+
throw new IllegalStateException("No current Header in iterator");
272+
}
273+
if (!headers.contains(this.currentName)) {
274+
throw new IllegalStateException("Header not present: " + this.currentName);
275+
}
276+
headers.remove(this.currentName);
277+
}
278+
}
279+
233280
}

spring-web/src/main/java/org/springframework/http/server/reactive/TomcatHeadersAdapter.java

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -20,7 +20,6 @@
2020
import java.util.Collection;
2121
import java.util.Collections;
2222
import java.util.Enumeration;
23-
import java.util.HashSet;
2423
import java.util.Iterator;
2524
import java.util.List;
2625
import java.util.Map;
@@ -167,12 +166,7 @@ public void clear() {
167166

168167
@Override
169168
public Set<String> keySet() {
170-
Set<String> result = new HashSet<>(8);
171-
Enumeration<String> names = this.headers.names();
172-
while (names.hasMoreElements()) {
173-
result.add(names.nextElement());
174-
}
175-
return result;
169+
return new HeaderNames();
176170
}
177171

178172
@Override
@@ -247,4 +241,59 @@ public List<String> setValue(List<String> value) {
247241
}
248242
}
249243

244+
245+
private class HeaderNames extends AbstractSet<String> {
246+
247+
@Override
248+
public Iterator<String> iterator() {
249+
return new HeaderNamesIterator(headers.names());
250+
}
251+
252+
@Override
253+
public int size() {
254+
Enumeration<String> names = headers.names();
255+
int size = 0;
256+
while (names.hasMoreElements()) {
257+
names.nextElement();
258+
size++;
259+
}
260+
return size;
261+
}
262+
}
263+
264+
private final class HeaderNamesIterator implements Iterator<String> {
265+
266+
private final Enumeration<String> enumeration;
267+
268+
@Nullable
269+
private String currentName;
270+
271+
private HeaderNamesIterator(Enumeration<String> enumeration) {
272+
this.enumeration = enumeration;
273+
}
274+
275+
@Override
276+
public boolean hasNext() {
277+
return this.enumeration.hasMoreElements();
278+
}
279+
280+
@Override
281+
public String next() {
282+
this.currentName = this.enumeration.nextElement();
283+
return this.currentName;
284+
}
285+
286+
@Override
287+
public void remove() {
288+
if (this.currentName == null) {
289+
throw new IllegalStateException("No current Header in iterator");
290+
}
291+
int index = headers.findHeader(this.currentName, 0);
292+
if (index == -1) {
293+
throw new IllegalStateException("Header not present: " + this.currentName);
294+
}
295+
headers.removeHeader(index);
296+
}
297+
}
298+
250299
}

0 commit comments

Comments
 (0)