Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LinkedCaseInsensitiveMap does not track removals via keySet, entrySet, or values #22821

Closed
ghost opened this issue Apr 19, 2019 · 10 comments
Closed
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Milestone

Comments

@ghost
Copy link

ghost commented Apr 19, 2019

I'm learning spring boot (using Spring-boot 2.1.3) while playing with REST API calls i found something interesting with HttpHeaders object

Suppose if i have an HttpHeaders object with some values

      HttpHeaders headers = new HttpHeaders();
      headers.add("Connection", "keep-alive");

But now if i try to delete and add them again it throws NullPointerException

      headers.entrySet().removeIf(entry -> entry.getKey().equals("Connection"));
      headers.add("Connection", "keep-alive");

Error:

       Caused by: java.lang.NullPointerException: null
	at org.springframework.util.CollectionUtils$MultiValueMapAdapter.add(CollectionUtils.java:460) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.http.HttpHeaders.add(HttpHeaders.java:1559) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at com.demo.DemoMain.run(DemoMain.java:27) ~[main/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
	... 5 common frames omitted
@philwebb
Copy link
Member

Thanks. This looks like a bug in LinkedCaseInsensitiveMap:

@Override
@Nullable
public V computeIfAbsent(String key, Function<? super String, ? extends V> mappingFunction) {
	String oldKey = this.caseInsensitiveKeys.putIfAbsent(convertKey(key), key);
	if (oldKey != null) {
		return this.targetMap.get(oldKey);
	}
	return this.targetMap.computeIfAbsent(key, mappingFunction);
}

The oldKey variable is not null but the target map doesn't contain the value. I guess we either need to track removals done via entrySet or that method should change.

@philwebb philwebb transferred this issue from spring-projects/spring-boot Apr 19, 2019
@philwebb philwebb added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 19, 2019
@rhamedy
Copy link

rhamedy commented Apr 20, 2019

@philwebb should this be a bug to be fixed, I offer to work on it 👍

@ghost
Copy link
Author

ghost commented Apr 20, 2019

@philwebb @rhamedy How this works? any one can contribute to this? i thought only spring people are allowed to fix these and outsiders are just to report bugs, correct me please if i'm wrong and if i'm allowed to work on it i feel it's a great opportunity for me

@philwebb
Copy link
Member

@Deadpool1111 @rhamedy Thanks for the offer of help. Anyone is free to contribute to Spring as long as they are happy to sign a CLA. Submissions are usually handled via pull-requests which are reviewed by a member of the team.

For this specific issue, I think we should wait until someone from the Framework team has reviewed it. It looks like the fix might be quite involved as we'll probably need to return specialized types from getValues() getKeys() and getKeySet() that can track removals and update the caseInsensitiveKeys.

@jhoeller jhoeller self-assigned this Apr 20, 2019
@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug labels Apr 21, 2019
sbrannen added a commit that referenced this issue Apr 21, 2019
@sbrannen
Copy link
Member

FYI: I introduced failing tests that reproduce this bug in e187a42.

@nkjackzhang
Copy link
Contributor

FYI: I introduced failing tests that reproduce this bug in e187a42.

headers.get() returns List<String>, so headers.getFirst() should be used.

@philwebb philwebb removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 25, 2019
@philwebb philwebb changed the title HttpHeaders add method throws null pointer exception when deleting and adding same headers LinkedCaseInsensitiveMap track removals via keySet, entrySet or values Apr 25, 2019
philwebb added a commit to philwebb/spring-framework that referenced this issue Apr 25, 2019
Ensure that results returned from keySet, entrySet & values are tracked
to remove case insensitive keys from the source map.

Closes spring-projectsgh-22821
@philwebb
Copy link
Member

@jhoeller I have a fix here that you can cherry-pick to the appropriate branch if you're happy with it.

@jhoeller jhoeller added this to the 5.2 M2 milestone Apr 26, 2019
@jhoeller jhoeller assigned philwebb and unassigned jhoeller Apr 26, 2019
@jhoeller
Copy link
Contributor

@philwebb This looks good to me; please simply merge it to master right away! Since the changes are quite involved (and the use cases rather limited), I'd rather keep this as 5.2 only.

As for the example above, why not simply call headers.remove("Connection") which works reliably on existing versions and is way more efficient than entrySet() iteration?

@wilkinsona
Copy link
Member

Can we please consider backporting this, or at least the parts of it that are necessary to fix #23633, to 5.1? As things stand Spring REST Docs' support for removing headers from a request or response before they're documented does not work with Framework 5.1.x.

wilkinsona added a commit to spring-projects/spring-restdocs that referenced this issue Sep 14, 2019
This updates REST Docs to test against Framework's latest 5.1
snapshot. Due to a bug in the behaviour of HttpHeaders' key set [1],
HeaderRemovingOperationPreprocessor has been updated to no longer use
it when removing headers from a request or response prior to it being
documented.

[1] spring-projects/spring-framework#22821
@wilkinsona
Copy link
Member

Nevermind. I've worked around it in a way that works with 5.0, 5.1, and (hopefully) 5.2.

@sbrannen sbrannen changed the title LinkedCaseInsensitiveMap track removals via keySet, entrySet or values LinkedCaseInsensitiveMap does not track removals via keySet, entrySet, or values Sep 15, 2019
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Sep 15, 2019
@sbrannen sbrannen added type: regression A bug that is also a regression status: backported An issue that has been backported to maintenance branches and removed type: bug A general bug labels Sep 16, 2019
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Sep 16, 2019
sbrannen pushed a commit that referenced this issue Sep 16, 2019
Ensure that results returned from keySet, entrySet & values are tracked
to remove case insensitive keys from the source map.

Closes gh-22821
sbrannen pushed a commit to sbrannen/spring-framework that referenced this issue Sep 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

6 participants