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

TCP RST was sent with large response body #459

Closed
keimoon opened this issue Nov 2, 2016 · 19 comments · Fixed by #708
Closed

TCP RST was sent with large response body #459

keimoon opened this issue Nov 2, 2016 · 19 comments · Fixed by #708
Labels
3 - in progress Someone is working on this ticket bug t:core Issues related to the akka-http-core module t:server Issues related to the HTTP server
Projects
Milestone

Comments

@keimoon
Copy link

keimoon commented Nov 2, 2016

Akka HTTP version: 3.0.0-RC1

How to reproduce:

  • Use small MTU for network interface (for example 1500). You can set MTU for loopback interface with sudo ifconfig lo0 mtu 1500
  • Response with large body content (should be larger than 128KiB) using complete and ToResponseMarshallable
  • Make sure client send header Connection: close.

Expected result:

Client should receive normally

Actual result:

Client got Recv failure: Connection reset by peer

This causes problems with some proxy like nginx or kong because they send Connection: close to upstream by default.

@ktoso ktoso added bug 1 - triaged Tickets that are safe to pick up for contributing in terms of likeliness of being accepted labels Nov 2, 2016
@ktoso
Copy link
Member

ktoso commented Nov 2, 2016

Thanks for reporting. I'll mark as bug though I'm not sure about it.

@jrudolph
Copy link
Member

jrudolph commented Nov 2, 2016

It could be like this: when the server sees Connection: close on a request it fully closes the connection directly after having sent out the last bit of the response which might close the socket too soon?

@keimoon which client did you use? Could you try to capture a network trace using tcpdump and attach it here? Thanks ;)

@jrudolph jrudolph added this to the backlog milestone Nov 2, 2016
@jrudolph jrudolph added t:server Issues related to the HTTP server t:http:server t:core Issues related to the akka-http-core module and removed t:http:server labels Nov 2, 2016
@keimoon
Copy link
Author

keimoon commented Nov 2, 2016

I used curl. Here is network trace that you can load into wireshark

akka.pcap.tar.gz

screen shot 2016-11-02 at 7 34 33 pm

@keimoon
Copy link
Author

keimoon commented Nov 2, 2016

Also I can only reproduce the bug with HttpEntity.Strict

@jrudolph
Copy link
Member

jrudolph commented Nov 2, 2016

Great, thanks @keimoon. That will help a lot.

@jrudolph
Copy link
Member

jrudolph commented Nov 2, 2016

I had a quick try, this issue can be reproduced using your instructions.

The problem is that when the HTTP server decides to close the connection, it does so by completely killing the HTTP stack which will complete the write-side of the TCP connection stream and cancel the read-side. If cancel arrives first (which seems to be the case), the Tcp stream implementation will abort the connection (= socket.close with soLinger(0)). The result is that the OS just doesn't know about the connection any more and if any data was not sent yet this data is lost and any further packets will be responded with RST.

We've seen issues like that for a long time (IIRC also with spray). I guess the solution could be just to keep cancellation from reaching the TCP connection. (I don't think that this creates leaks, hopefully leaks would be prevented by the idle-timeout on the connection but it might make sense to think a bit about it.)

@keimoon
Copy link
Author

keimoon commented Nov 2, 2016

Great. Currently we are implementing an work around that will use HttpEntity.Chunked if the response body is large.

@mariszin
Copy link

mariszin commented Dec 5, 2016

We migrated our app to akka-http 10.0.0, and received the same error - when using anything, that sends header "Connection: close" (nginx, curl), the request will fail on Strict entities, that are larger than 128K.
Error in curl:
curl: (56) Recv failure: Connection reset by peer

@guntiso
Copy link
Contributor

guntiso commented Dec 5, 2016

Chunked does not help - changed response entity to HttpEntity.Chunked(mediaType, Source(Seq(ByteString(prettyJsonString)))), and now it works with curl, but still fails with nginx. Is there any reliable workaround?

@daandi
Copy link

daandi commented Dec 9, 2016

Same here :( Would really appreciate a way to solve this.

@ktoso
Copy link
Member

ktoso commented Dec 12, 2016

So if I understand correctly, we'd need a "CancellationBarrier" that can be "opened" by external signal, which we could use to resolve the race (making it always "cancel" after the completion went "all the way"). Reasonable @jrudolph ?

@jrudolph
Copy link
Member

We might not need to send cancellation to the tcp stream at all but I haven't checked if that would work.

@jrudolph
Copy link
Member

I reproduced it with this code jrudolph@a929a03 and curl:

curl -vvv -H "connection:close" http://localhost:9001/big > /dev/null

@gisql
Copy link

gisql commented Dec 30, 2016

Workaround with nginx here: akka/akka#19542

@jrudolph jrudolph added 3 - in progress Someone is working on this ticket and removed 1 - triaged Tickets that are safe to pick up for contributing in terms of likeliness of being accepted labels Jan 3, 2017
jrudolph added a commit to jrudolph/akka-http that referenced this issue Jan 3, 2017
…ction closure

A race between completion and cancellation towards the TCP stream endpoints
might lead to the connection being canceled (= RST frames sent) when an HTTP
connection is regularly closed (e.g. when the client sets `Connection: close`
in the request).

We now prevent cancellation from reaching the TCP stream at all and rely
on closing the connection from the write side.

Fixes akka#459.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Jan 3, 2017
…ction closure

A race between completion and cancellation towards the TCP stream endpoints
might lead to the connection being canceled (= RST frames sent) when an HTTP
connection is regularly closed (e.g. when the client sets `Connection: close`
in the request).

We now prevent cancellation from reaching the TCP stream at all and rely
on closing the connection from the write side.

Fixes akka#459.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Jan 3, 2017
…ction closure

A race between completion and cancellation towards the TCP stream endpoints
might lead to the connection being canceled (= RST frames sent) when an HTTP
connection is regularly closed (e.g. when the client sets `Connection: close`
in the request).

We now prevent cancellation from reaching the TCP stream at all and rely
on closing the connection from the write side.

Fixes akka#459.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Jan 3, 2017
…ction closure

A race between completion and cancellation towards the TCP stream endpoints
might lead to the connection being canceled (= RST frames sent) when an HTTP
connection is regularly closed (e.g. when the client sets `Connection: close`
in the request).

We now prevent cancellation from reaching the TCP stream at all and rely
on closing the connection from the write side.

Fixes akka#459.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Jan 4, 2017
…ction closure

A race between completion and cancellation towards the TCP stream endpoints
might lead to the connection being canceled (= RST frames sent) when an HTTP
connection is regularly closed (e.g. when the client sets `Connection: close`
in the request).

We now prevent cancellation from reaching the TCP stream at all and rely
on closing the connection from the write side.

Fixes akka#459.
@ktoso ktoso closed this as completed in #708 Jan 4, 2017
ktoso added a commit that referenced this issue Jan 4, 2017
=htp #459 prevent "Connection closed by peer" errors during connection closure
@jonas
Copy link
Member

jonas commented Jan 10, 2017

Tested a locally published version of Akka HTTP and I no longer see connection resets.

@ktoso
Copy link
Member

ktoso commented Jan 10, 2017

Cool, thanks for testing!

jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 8, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 8, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 9, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 9, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 16, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
jrudolph added a commit to jrudolph/akka-http that referenced this issue Feb 16, 2017
Under circumstances that I could not reproduce so far,
the AbsorbCancellation stage introduced in akka#459 may keep the graph running
if data was already buffered but never read. The reason is that the stage
so far requires completion to reach the stage after cancellation was
absorbed which might be blocked by incoming elements that were never
pulled.

The solution is two-fold. After cancellation:

 1) pull in and ignore all incoming elements to eventually fetch completion
 2) complete the stage after a configurable time

In the HTTP use case, we delay the cancellation by no more than the time specified
in the new linger-timeout setting.
@antonkatz
Copy link

I am perhaps seeing this again with nginx.
I'm serving an html page with Akka-Http 10.0.9 with getFromFile().
The file contains an tag with src containing base64 encoded image.

Nginx has this in the error logs.
upstream prematurely closed connection while reading upstream

The error is resolved by using the workaround in akka/akka#19542

@jonas
Copy link
Member

jonas commented Aug 31, 2017

@antonkatz Is the traffic served over HTTPS?

@jrudolph
Copy link
Member

jrudolph commented Sep 4, 2017

@antonkatz we fixed issue #1219 in 10.0.10 which was similar to this bug when running on HTTPS. Can you check if 10.0.10 still has the issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 - in progress Someone is working on this ticket bug t:core Issues related to the akka-http-core module t:server Issues related to the HTTP server
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

9 participants