https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112019-04-19T01:32:33ZRuby Issue Tracking SystemRuby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=776682019-04-19T01:32:33Znaruse (Yui NARUSE)naruse@airemix.jp
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li></ul><p>In general resolving DNS is done by libc (getaddrinfo) and Ruby just uses their result.<br>
Therefore it is not a bug and won't be a small feature.</p> Ruby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=777572019-04-24T15:31:15Znicolasnoble (Nicolas Noble)
<ul></ul><p>I thought long and hard about how to reply to this.</p>
<p>Let's put it this way: the fact that you are using glibc isn't the problem at all. The fact that you are badly using its results is.</p>
<p>Any other big piece of code out there that is doing any sort of abstraction is going to be establishing an internet connection will retry on all the entries returned by the DNS, in a round robin fashion. Ruby is the only one that I know of that doesn't do that. In fact, if you run my reproduction case on jruby, it will work correctly. If anything, the bug should be that jruby and ruby are acting differently.</p>
<p>It is important to realize that when one is attempting to write any abstraction layer, there's an expectation of correctness. In this one case, the current chosen model of abstraction means that the http downloader system will catastrophically fail to work in certain conditions without any sort of recourse by the user of the abstraction.</p> Ruby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=867852020-07-28T23:59:57Zguss77 (Oded Arbel)
<ul></ul><p>I've encountered this issue as well. My workaround - in case someone else has the same problem:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'net/http'</span>
<span class="nb">require</span> <span class="s1">'resolv'</span>
<span class="c1"># ...</span>
<span class="n">url</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="k">unless</span> <span class="n">url</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">URI</span><span class="p">)</span>
<span class="n">addrs</span> <span class="o">=</span> <span class="no">Resolv</span><span class="o">::</span><span class="no">DNS</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">getaddresses</span><span class="p">(</span><span class="n">url</span><span class="p">.</span><span class="nf">host</span><span class="p">).</span><span class="nf">sort</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="o">|</span> <span class="n">b</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">to_s</span> <span class="o"><=></span> <span class="n">a</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">to_s</span> <span class="p">}.</span><span class="nf">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">ip</span><span class="o">|</span> <span class="n">ip</span><span class="p">.</span><span class="nf">to_s</span> <span class="p">}</span>
<span class="k">begin</span>
<span class="n">response</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">start</span><span class="p">(</span><span class="n">url</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="n">url</span><span class="p">.</span><span class="nf">port</span><span class="p">,</span> <span class="ss">use_ssl: </span><span class="n">url</span><span class="p">.</span><span class="nf">scheme</span> <span class="o">==</span> <span class="s1">'https'</span><span class="p">,</span> <span class="ss">open_timeout: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">ipaddr: </span><span class="n">addrs</span><span class="p">.</span><span class="nf">shift</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">http</span><span class="o">|</span>
<span class="n">http</span><span class="p">.</span><span class="nf">request</span><span class="p">(</span><span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Get</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">url</span><span class="p">.</span><span class="nf">request_uri</span><span class="p">))</span>
<span class="k">end</span>
<span class="k">rescue</span> <span class="no">Timeout</span><span class="o">::</span><span class="no">Error</span> <span class="o">=></span> <span class="n">e</span>
<span class="k">retry</span> <span class="k">unless</span> <span class="n">addrs</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">end</span>
</code></pre> Ruby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=868762020-07-31T18:46:26Zbenlangfeld (Ben Langfeld)ben@langfeld.me
<ul></ul><p>naruse (Yui NARUSE) wrote in <a href="#note-1">#note-1</a>:</p>
<blockquote>
<p>In general resolving DNS is done by libc (getaddrinfo) and Ruby just uses their result.<br>
Therefore it is not a bug and won't be a small feature.</p>
</blockquote>
<p>RFC3484 (<a href="https://www.ietf.org/rfc/rfc3484.txt" class="external">https://www.ietf.org/rfc/rfc3484.txt</a>) very clearly states:</p>
<pre><code>Well-behaved applications SHOULD iterate through the list of
addresses returned from getaddrinfo() until they find a working
address.
</code></pre>
<p>That Ruby does not do this should be considered a bug, and I would ask that you please re-open this ticket on that basis. Ruby is in contravention of the RFC and the documentation of <code>getaddrinfo(3)</code>. This is not a missing feature; Ruby is broken. Note that I am not asking that anyone fix this; I will prepare a fix. All I ask is that this ticket be acknowledged as valid and not rejected based on misunderstanding it.</p> Ruby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=868772020-07-31T22:51:27Zphluid61 (Matthew Kerwin)matthew@kerwin.net.au
<ul></ul><p>benlangfeld (Ben Langfeld) wrote in <a href="#note-4">#note-4</a>:</p>
<blockquote>
<p>RFC3484 (<a href="https://www.ietf.org/rfc/rfc3484.txt" class="external">https://www.ietf.org/rfc/rfc3484.txt</a>) very clearly states:</p>
</blockquote>
<p>Minor point, I don't think it affects much, but RFC 3484 is obsolete and is replaced by RFC 6724 <a href="https://tools.ietf.org/html/rfc6724" class="external">https://tools.ietf.org/html/rfc6724</a></p>
<p>Cheers</p> Ruby master - Bug #15773: Net::HTTP doesn't try next IP address in case of timeouthttps://bugs.ruby-lang.org/issues/15773?journal_id=869132020-08-03T16:23:23Zbenlangfeld (Ben Langfeld)ben@langfeld.me
<ul></ul><p>phluid61 (Matthew Kerwin) wrote in <a href="#note-5">#note-5</a>:</p>
<blockquote>
<p>benlangfeld (Ben Langfeld) wrote in <a href="#note-4">#note-4</a>:</p>
<blockquote>
<p>RFC3484 (<a href="https://www.ietf.org/rfc/rfc3484.txt" class="external">https://www.ietf.org/rfc/rfc3484.txt</a>) very clearly states:</p>
</blockquote>
<p>Minor point, I don't think it affects much, but RFC 3484 is obsolete and is replaced by RFC 6724 <a href="https://tools.ietf.org/html/rfc6724" class="external">https://tools.ietf.org/html/rfc6724</a></p>
<p>Cheers</p>
</blockquote>
<p>Thank you for pointing that out. RFC 6724 is even more explicit:</p>
<pre><code>Well-behaved applications SHOULD NOT simply use the first address
returned from an API such as getaddrinfo() and then give up if it
fails. For many applications, it is appropriate to iterate through
the list of addresses returned from getaddrinfo() until a working
address is found. For other applications, it might be appropriate to
try multiple addresses in parallel (e.g., with some small delay in
between) and use the first one to succeed.
</code></pre>