HTTP::Client - Connection reset by peer

Hi,
Under Crystal 0.35, the code below

require “http/client”
url = “…”
response = HTTP::Client.post url

run fine for url = “http://www.abcbourse.com
but produces the following error

Unhandled exception: Error reading socket: Connection reset by peer (IO::Error)
from …/…/…/…/…/…/usr/lib/crystal/io/evented.cr:61:9 in ‘unbuffered_read’
from …/…/…/…/…/…/usr/lib/crystal/io/buffered.cr:80:16 in ‘read’
from …/…/…/…/…/…/usr/lib/crystal/openssl/bio.cr:46:13 in ‘->’
from ???
from BIO_read
from ???
from ???
from ???
from ???
from ???
from …/…/…/…/…/…/usr/lib/crystal/openssl/ssl/socket.cr:32:15 in ‘initialize’
from …/…/…/…/…/…/usr/lib/crystal/openssl/ssl/socket.cr:3:5 in ‘new:context:sync_close:hostname’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:767:5 in ‘socket’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:650:19 in ‘send_request’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:587:5 in ‘exec_internal_single’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:574:5 in ‘exec_internal’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:570:5 in ‘exec’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:692:5 in ‘exec’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:724:7 in ‘exec’
from …/…/…/…/…/…/usr/lib/crystal/http/client.cr:396:3 in ‘post’
from z.cr:17:1 in ‘__crystal_main’
from …/…/…/…/…/…/usr/lib/crystal/crystal/main.cr:105:5 in ‘main_user_code’
from …/…/…/…/…/…/usr/lib/crystal/crystal/main.cr:91:7 in ‘main’
from …/…/…/…/…/…/usr/lib/crystal/crystal/main.cr:114:3 in ‘main’
from __libc_start_main
from _start
from ???

for url = “https://www.abcbourse.com”

In a browser, both URLs are Ok.
Under Crystal 0.34, both URLs are also Ok.

Other websites I tried also work fine with http://… or https://…

So, I wonder if this is a problem from this particular website, or from a change in the http library ?

Thanks for advice

Reverting this PR fixes it. Thank you for reporting this.

/cc @straight-shoota @bcardiff @waj this is another regression in 0.35.0

As a workaround you can do this:

require "http/client"

ssl = OpenSSL::SSL::Context::Client.new
ssl.ciphers = OpenSSL::SSL::Context::CIPHERS_OLD

url = "https://www.abcbourse.com"
response = HTTP::Client.post url, tls: ssl
response.to_io(STDOUT)

This is not exactly a regression. The server at www.abcbourse.com supports only rather weak cipher suites (see https://www.ssllabs.com/ssltest/analyze.html?d=www.abcbourse.com#suitesHeading) and none of them is included in the intermediate compatibilty level of Mozillas recommendation.
Thus, by default HTTP::Client refuses to connect to such a server if it can’t offer a decent cipher suite.
You can change the SSL configuration to accept older cipher suites. So basically, this is working as expected.

What’s off here is the error message, though. I’d expect there’d be a proper error from openssl at least stating something like it couldn’t negotiate a suitable cipher with the server. That would need some investigation.

Why does my browser open it without complaining? I doubt the browser is wrong…

1 Like

The error is terrible, and we must figure out a way to make it better.
However, the Mozilla recommendations are for servers. I love the idea of Crystal pushing the world’s security forward, but I’m not sure it has to be done by default for clients. Even Mozilla browsers accept connecting to the server. Is there any other HTTP client library in other language forcing a restriction like that?

@asterite in Chrome, if you go to the Security inspector tab, it says it’s currently using an obsolete cipher suite.

1 Like

Yes, I guess we should consider using different default configurations for openssl server and client contexts. Until now it’s always been the same in both contexts. But it’s also been quite weak which is not good for server context.

PR: https://github.com/crystal-lang/crystal/pull/9459

@asterite It works, thanks.