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

RestTemplate reading Json prohibits JDK HttpClient connection reuse (keep-alive) #27969

Closed
apinske opened this issue Jan 22, 2022 · 4 comments
Closed
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@apinske
Copy link

apinske commented Jan 22, 2022

Affects: 5.3.14

There is an issue regarding https connection reuse (keep-alive) when using jackson unmarshaller, chunked transfer encoding and RestTemplate. The connections are not reused, which leads to more TLS handshakes for subsequent calls.

  • When the Jackson AUTO_CLOSE_SOURCE-Feature is enabled (which is the default), jackson closes the HttpInputStream in UTF8StreamJsonParser._closeInput() after the json doc is fully read.
  • The stream is not at EOF yet, though. The last-chunk (as of RFC7230 4.1) is still "waiting on the wire".
  • This leads to a call to sun.net.www.http.ChunkedInputStream.hurry() which aims to read all the remaining bytes.
  • This does not succeed fully, because in sun.net.www.http.ChunkedInputStream.readAheadNonBlocking() in.available() may not return all remaining bytes.
  • This can lead to a ChunkedInputStream that is not in state STATE_DONE, which will lead to it being closed, instead of kept alive.

This is a combination of multiple unrelated things, that may not be issues in their own rights.

  • JDK-HttpClient hurry() may leave unread bytes in the stream, which is probably okay, as close() was called prematurely.
  • Jackson by default calls close() prematurely, having not read all input.
  • Spring provides Jackson in its default configuration on top of JDK-HttpClient.
    • Spring has its own feature to drain http streams, and should probably not rely on Jackson closing the stream.

I open this bug against Spring, because I think it is the best place to fix the issue.

Find a reproducing project at: https://github.com/apinske/playground-http/tree/spring-framework-issues-27969

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 22, 2022
@jhoeller jhoeller added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Jan 24, 2022
@jhoeller jhoeller added this to the Triage Queue milestone Jan 24, 2022
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Jan 26, 2022

There was a commit a while ago b947bfe along these lines, ensuring the response stream is drained first when ClientHttpResponse#close() is called. Maybe we need to do the same for the close on the InputStream itself.

What do you think @bclozel?

@rstoyanchev rstoyanchev self-assigned this Jan 26, 2022
@bclozel
Copy link
Member

bclozel commented Jan 26, 2022

Right now I'm missing the difference between this use case and the one that was fixed in b947bfe.

The sample project calls restTemplate.getForObject which internally calls doExecute and closes the response. So the response body should be in fact drained. Would draining as soon as the input stream is closed change the behavior here?

Note that I have yet to run the sample locally, so this comment is probably missing a key fact.

@apinske
Copy link
Author

apinske commented Jan 26, 2022

The test in b947bfe does not use jackson, which closes the stream before the spring mechanism (drain+close) can do it. The subsequent IOException (during spring's drain attempt) is swallowed.

@rstoyanchev rstoyanchev removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 1, 2022
@rstoyanchev rstoyanchev modified the milestones: Triage Queue, 5.3.16 Feb 1, 2022
@rstoyanchev rstoyanchev added the type: enhancement A general enhancement label Feb 1, 2022
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Feb 1, 2022

Team decision: Recently we reviewed a request #27773 to ensure the InputStream is closed but decided that converters do not have enough context to do that. Here we'll ensure that they consistently don't close the InputStream by using StreamUtils#nonClosing. This is something that was done a long time ago in 6661788 but not applied the the Jackson converter at the time. Later, the writing side of Jackson was included 7be7e5b, but not for reading.

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: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants