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

Reactor Netty can only get the first cookie value when multiple cookies with the same name exist #28490

Closed
jsonwan opened this issue May 20, 2022 · 9 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@jsonwan
Copy link

jsonwan commented May 20, 2022

Affects: main

The way to confirm:

Modify two lines in CookieIntegrationTests and the test will fail.

	@ParameterizedHttpServerTest
	public void basicTest(HttpServer httpServer) throws Exception {
		startServer(httpServer);

		URI url = new URI("http://localhost:" + port);
		// Modified line 1:add lang=zh-CN
		String header = "SID=31d4d96e407aad42; lang=en-US; lang=zh-CN";
		ResponseEntity<Void> response = new RestTemplate().exchange(
				RequestEntity.get(url).header("Cookie", header).build(), Void.class);

		Map<String, List<HttpCookie>> requestCookies = this.cookieHandler.requestCookies;
		assertThat(requestCookies.size()).isEqualTo(2);

		List<HttpCookie> list = requestCookies.get("SID");
		assertThat(list.size()).isEqualTo(1);
		assertThat(list.iterator().next().getValue()).isEqualTo("31d4d96e407aad42");

		list = requestCookies.get("lang");
		// Modified line 2:add this line
		assertThat(list.size()).isEqualTo(2);
		assertThat(list.iterator().next().getValue()).isEqualTo("en-US");

		List<String> headerValues = response.getHeaders().get("Set-Cookie");
		assertThat(headerValues.size()).isEqualTo(2);

		List<String> cookie0 = splitCookie(headerValues.get(0));
		assertThat(cookie0.remove("SID=31d4d96e407aad42")).as("SID").isTrue();
		assertThat(cookie0.stream().map(String::toLowerCase))
				.containsExactlyInAnyOrder("path=/", "secure", "httponly");
		List<String> cookie1 = splitCookie(headerValues.get(1));
		assertThat(cookie1.remove("lang=en-US")).as("lang").isTrue();
		assertThat(cookie1.stream().map(String::toLowerCase))
				.containsExactlyInAnyOrder("path=/", "domain=example.com");
	}

According to rfc 6265,server should be able to handle cookies with the same name instead of parsing the first one and ignoring the others. I think it is more reasonable for spring-web to give all values to application and let application decide how to handle multiple values.

By the way, the bottom frameworks Reactor-netty, Jetty, and Tomcat can parse multiple cookie values with the same name in their newer versions, wish spring can support soon.

Related Issues

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 20, 2022
@aooohan
Copy link
Contributor

aooohan commented May 20, 2022

I don't think this is an issue with the spring framework.You can debug it at org.springframework.http.server.reactive.AbstractServerHttpRequest#initCookies

@jsonwan
Copy link
Author

jsonwan commented May 20, 2022

I don't think this is an issue with the spring framework.You can debug it at org.springframework.http.server.reactive.AbstractServerHttpRequest#initCookies

This method only get the first cookie value when multi cookies with the same name exist, because the implementation class ReactorServerHttpRequest.initCookies() use HttpServerRequest.cookies() to get cookies, which can be replaced by HttpServerRequest.allCookies() to get all the values.
Besides, the return type of this method is MultiValueMap, which can be used to save cookies with same name. I guess may be designed so.

@aooohan
Copy link
Contributor

aooohan commented May 20, 2022

I don't think this is an issue with the spring framework.You can debug it at org.springframework.http.server.reactive.AbstractServerHttpRequest#initCookies

This method only get the first cookie value when multi cookies with the same name exist, because the implementation class ReactorServerHttpRequest.initCookies() use HttpServerRequest.cookies() to get cookies, which can be replaced by HttpServerRequest.allCookies() to get all the values. Besides, the return type of this method is MultiValueMap, which can be used to save cookies with same name. I guess may be designed so.

Yes, you're right, I just took a closer look at the ReactorServerHttpRequest implementation and reactor-netty's HttpServerRequest does provide a way to get all cookies, HttpServerRequest#allCookies().

@aooohan
Copy link
Contributor

aooohan commented May 20, 2022

@jsonwan Can I propose a pr to solve this issue?

@jsonwan
Copy link
Author

jsonwan commented May 20, 2022

@jsonwan Can I propose a pr to solve this issue?

Yes, I tried to solve this problem by using HttpServerRequest.allCookies() instead of cookies() and completing the test cases, however, Jetty, Reactor-netty and Tomcat test cases passed but the undertow test cases failed because undertow does not provide a method to get all cookies. Wish for your help.
Related undertow issue: https://issues.redhat.com/browse/UNDERTOW-1676#section-4.2.2

@aooohan
Copy link
Contributor

aooohan commented May 20, 2022

@jsonwan Can I propose a pr to solve this issue?

Yes, I tried to solve this problem by using HttpServerRequest.allCookies() instead of cookies() and completing the test cases, however, Jetty, Reactor-netty and Tomcat test cases passed but the undertow test cases failed because undertow does not provide a method to get all cookies. Wish for your help. Related undertow issue: https://issues.redhat.com/browse/UNDERTOW-1676#section-4.2.2

Undertow does not offer the ability to parse multiple cookies, there is nothing we can do about it. I think we just need to solve the ReactorServerHttpRequest problem, but of course, I think it's a problem, and it's up to the maintainers of the spring framework to decide if it ends up being what we think it is

@sbrannen sbrannen changed the title spring-web ServerHttpRequest.getCookies() can only get the first cookie value when multi cookies with the same name exist ServerHttpRequest.getCookies() can only get the first cookie value when multiple cookies with the same name exist May 20, 2022
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label May 20, 2022
@akvone
Copy link

akvone commented Jun 1, 2022

We also stumbled upon this. Currently we fixed that by using nativeRequest:

HttpServerRequest nativeRequest = ((AbstractServerHttpRequest) serverWebExchange.getRequest()).getNativeRequest();
Map<CharSequence, List<Cookie>> allCookies = nativeRequest.allCookies();

By the way, it seems that default implementation of HttpServletRequest#getCookies returns all cookies.

@jhoeller
Copy link
Contributor

jhoeller commented Nov 6, 2022

Note that we are upgrading to the Servlet 6.0 based Undertow 2.3 for Spring Framework 6.0 RC4 (#29435), aiming to retain runtime compatibility with Undertow 2.2. This allows us to rely on the new Undertow 2.2 cookies API, hopefully helping here.

@sdeleuze sdeleuze self-assigned this Nov 24, 2023
@sdeleuze sdeleuze added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 6, 2023
@sdeleuze sdeleuze changed the title ServerHttpRequest.getCookies() can only get the first cookie value when multiple cookies with the same name exist Reactor Netty can only get the first cookie value when multiple cookies with the same name exist Dec 6, 2023
@sdeleuze sdeleuze added this to the 6.1.2 milestone Dec 6, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Dec 6, 2023

I confirm @aooohan analysis, Undertow handling of multiple cookies with the same name looks incorrect. They may have mistakenly interpreted "e.g., that were set with different Path or Domain attributes" as "i.e. , that were set with different Path or Domain attributes" ("for example" in the spec versus "that is" in their implementation description).

As a consequence, I am going to fix this bug with Reactor Netty and ignore the related test with Undertow (until they fix this bug).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

7 participants