'Connection reset by peer' when replying before the end of POST data

Eric Wong normalperson at yhbt.net
Wed Feb 29 17:23:15 UTC 2012


Lunar <lunar at anargeek.net> wrote:
> Quoting the "HTTP Connection Management" document [1] which I mentioned
> previously: "Servers must therefore close each half of the connection
> independently."
> 
> After digging some more at Nginx, it looks like Nginx is doing exactly
> that. I also looked at Apache and the code responsible for half-closing
> both side of the connection is very readable. Function is called
> `ap_lingering_close()` in server/connection.c.

It's very expensive to do what Apache does with threads+Rainbows!.  It
also appears that Apache and nginx continues to completely drain the
socket if the client is sending, too (which is your case).

I don't know if there's a way around draining the socket entirely to
avoid connection resets in the client :<

I'm more interested in keeping good clients going and not wasting server
resources dealing with clients I don't want in the first place.

> It looks like there is no way to call shutdown(2) in Ruby 1.8.7, but
> from the documentation, Ruby 1.9.3 has IO#close_read and IO#close_write
> method that will call shutdown(2) for each half of a socket.

There's a shutdown method in BasicSocket for 1.8

> In any case, I think it would worth to try to add such call sequences in
> Rainbows! and see if it helps. If you can't give it a shot yourself,
> could you point me to the relevant part of the code?

You could emulate lingering close with something like this:
(valid for ThreadSpawn/ThreadPool, not EM/Cool.io stuff)

diff --git a/lib/rainbows/client.rb b/lib/rainbows/client.rb
index b456eca..9d8256a 100644
--- a/lib/rainbows/client.rb
+++ b/lib/rainbows/client.rb
@@ -6,4 +6,16 @@ class Rainbows::Client < Kgio::Socket
   include Rainbows::ProcessClient
 
   alias write kgio_write
+
+  def close
+    close_write
+    buf = ""
+    begin
+      kgio_wait_readable(2)
+      buf = kgio_tryread(512, buf)
+    rescue
+      break
+    end while buf
+    super
+  end
 end

I'm not going to apply that to Rainbows!, it's needless
overhead/complexity for dealing with clients I want to reject
anyways (and you're still draining the client socket).

> [1] <http://ftp.ics.uci.edu/pub/ietf/http/draft-ietf-http-connection-00.txt>
> 
> > Also, neither Matz Ruby 1.8 nor 1.9 can do lingering close as IO#close
> > still holds onto the GVL (blocking all clients).
> > 
> > I tried to make close(2) release the GVL for 1.9.3, but it doesn't
> > interact well if another thread was operating on that IO object.
> 
> I am not that versed in Ruby internal, but I fail to see how this could
> be related to first closing the clients write side of the connection.
> There should be a window between IO#close_write and IO#close in which in
> GVL does not matter, does it?

Having the kernel handle lingering close() should be an "easier" version
of the above patch (but I think you still need to drain the input as
long as your client is sending).


More information about the rainbows-talk mailing list