Skip to content

Commit

Permalink
Make sure only authentication errors stop host iteration
Browse files Browse the repository at this point in the history
Fixes ged#476
  • Loading branch information
larskanis committed Oct 2, 2022
1 parent baac805 commit 452ae53
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 4 deletions.
5 changes: 2 additions & 3 deletions lib/pg/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,6 @@ def cancel

# Check to see if it's finished or failed yet
poll_status = send( poll_meth )
@last_status = status unless [PG::CONNECTION_BAD, PG::CONNECTION_OK].include?(status)
end

unless status == PG::CONNECTION_OK
Expand Down Expand Up @@ -760,13 +759,13 @@ def new(*args)

conn.send(:async_connect_or_reset, :connect_poll)
rescue PG::ConnectionBad => err
if errors && !(conn && [PG::CONNECTION_AWAITING_RESPONSE].include?(conn.instance_variable_get(:@last_status)))
if errors && /authenticat/ !~ err.message
# Seems to be no authentication error -> try next host
errors << err
return nil
else
# Probably an authentication error
raise
raise err
end
end
conn
Expand Down
10 changes: 10 additions & 0 deletions spec/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ def initialize( name, port: 54321, postgresql_conf: '' )
EOT
end

# Enable MD5 authentication in hba config
hba_content = File.read(@test_pgdata+"pg_hba.conf")
File.open(@test_pgdata+"pg_hba.conf", "w") do |fd|
fd.puts <<-EOT
# TYPE DATABASE USER ADDRESS METHOD
host all testusermd5 ::1/128 md5
EOT
fd.puts hba_content
end

trace "Generate certificates"
generate_ssl_certs(@test_pgdata.to_s)
end
Expand Down
49 changes: 48 additions & 1 deletion spec/pg/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,55 @@
end
end

context "with multiple PostgreSQL servers", :without_transaction do
before :all do
@port_ro = @port + 1
@port_down = @port + 2
@dbms = PG::TestingHelpers::PostgresServer.new("read-only",
port: @port_ro,
postgresql_conf: "default_transaction_read_only=on"
)
end

after :all do
@dbms&.teardown
end

it "honors target_session_attrs requirements" do
uri = "postgres://localhost:#{@port_ro},localhost:#{@port}/postgres?target_session_attrs=read-write"
PG.connect(uri) do |conn|
expect( conn.port ).to eq( @port )
end

uri = "postgres://localhost:#{@port_ro},localhost:#{@port}/postgres?target_session_attrs=any"
PG.connect(uri) do |conn|
expect( conn.port ).to eq( @port_ro )
end
end
end

it "stops hosts iteration on authentication errors", :without_transaction do
@conn.exec("DROP USER IF EXISTS testusermd5")
@conn.exec("CREATE USER testusermd5 PASSWORD 'secret'")

uri = "host=::1,::1,127.0.0.1 port=#{@port_down},#{@port},#{@port} dbname=postgres user=testusermd5 password=wrong"
expect { PG.connect(uri) }.to raise_error(/authenti.*testusermd5/i)

uri = "host=::1,::1,127.0.0.1 port=#{@port_down},#{@port},#{@port} dbname=postgres user=testusermd5 password=secret"
PG.connect(uri) do |conn|
expect( conn.hostaddr ).to eq( "::1" )
expect( conn.port ).to eq( @port )
end

uri = "host=::1,::1,127.0.0.1 port=#{@port_down},#{@port_down},#{@port} dbname=postgres user=testusermd5 password=wrong"
PG.connect(uri) do |conn|
expect( conn.hostaddr ).to eq( "127.0.0.1" )
expect( conn.port ).to eq( @port )
end
end

it "connects using URI with multiple hosts", :postgresql_12 do
uri = "postgres://localhost:#{@port+1},127.0.0.1:#{@port}/test?keepalives=1"
uri = "postgres://localhost:#{@port+2},127.0.0.1:#{@port}/test?keepalives=1"
tmpconn = described_class.connect( uri )
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
expect( tmpconn.port ).to eq( @port )
Expand Down

0 comments on commit 452ae53

Please sign in to comment.