[ruby-core:81316] [Ruby trunk Feature#13396] Net::HTTP has no write timeout

From: naruse@...
Date: 2017-05-20 22:01:07 UTC
List: ruby-core #81316
Issue #13396 has been updated by naruse (Yui NARUSE).


Only supports Linux:

```diff
@@ -10372,11 +10383,23 @@ static int
 nogvl_wait_for_single_fd(int fd, short events)
 {
     struct pollfd fds;
+    struct timespec ts, *tsp = NULL;

     fds.fd = fd;
     fds.events = events;

-    return poll(&fds, 1, -1);
+       if (is_socket(fd, Qnil)) {
+           struct timeval timeout;
+           socklen_t sz = sizeof(timeout);
+           if (getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, &sz) == 0 &&
+                   timeout.tv_sec != 0 || timeout.tv_usec != 0) {
+               ts.tv_sec = timeout.tv_sec;
+               ts.tv_nsec = timeout.tv_usec * 1000;
+              tsp = &ts;
+           }
+       }
+
+    return ppoll(&fds, 1, tsp, NULL);
 }

 static int
@@ -10445,6 +10468,9 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
 #endif
     } while (ret == -1 && maygvl_copy_stream_continue_p(0, stp));

+    if (ret == 0) {
+       return -1;
+    }
     if (ret == -1) {
         stp->syserr = IOWAIT_SYSCALL;
         stp->error_no = errno;
```

----------------------------------------
Feature #13396: Net::HTTP has no write timeout
https://bugs.ruby-lang.org/issues/13396#change-65001

* Author: byroot (Jean Boussier)
* Status: Assigned
* Priority: Normal
* Assignee: naruse (Yui NARUSE)
* Target version: 
----------------------------------------
When sending a large request to an unresponsive server, `Net::HTTP` can hang pretty much forever.

```ruby
# server.rb
require 'socket'

server = TCPServer.new('localhost', 2345)
loop do
  socket = server.accept
end

```

```ruby
# client.rb
require 'net/http'

connection = Net::HTTP.new('localhost', 2345)
connection.open_timeout = 1
connection.read_timeout = 3
connection.start

post = Net::HTTP::Post.new('/')
body = (('a' * 1023) + "\n") * 5_000
post.body = body

puts "Sending #{body.bytesize} bytes"
connection.request(post)
```

The above code will hang forever on all system I tested it on (OSX / Linux 3.19).

The issue only trigger once the request body is above a certain threshold. That threshold depends on the system, I assume it's due to the system's TCP settings, but a request over 4MB will trigger the issue consistently.

I assume it happens when the request is bigger than the socket buffer. 

It's stuck on the following path:

```
/net/protocol.rb:211:in `write': Interrupt
/net/protocol.rb:211:in `write0'
/net/protocol.rb:185:in `block in write'
/net/protocol.rb:202:in `writing'
/net/protocol.rb:184:in `write'
/net/http/generic_request.rb:188:in `send_request_with_body'
/net/http/generic_request.rb:121:in `exec'
/net/http.rb:1435:in `block in transport_request'
/net/http.rb:1434:in `catch'
/net/http.rb:1434:in `transport_request'
/net/http.rb:1407:in `request'
```

I tried setting `setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, ...)` on the client socket, but without success. However adding a `Timeout.timeout` call around `req.exec` did work.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread

Prev Next