require "socket" | |
class Client | |
def initialize(addr, port, user, db) | |
@user = user | |
@db = db | |
@socket = TCPSocket.new(addr, port) | |
end | |
def login_packet | |
null = "\x0" | |
packet = [ | |
[0].pack("N"), [0x30000].pack("N"), | |
"user", null, | |
@user, null, | |
"database", null, | |
@database, null, | |
null | |
] | |
# Set the packet size at pos 0. The null was a placeholder. | |
packet_size = packet.join.size | |
packet[0] = [packet_size].pack("N") | |
packet.join | |
end | |
def parse_uint32(buffer) | |
num = buffer[0..3].unpack("N").first | |
buffer = buffer[4..-1] | |
[num, buffer] | |
end | |
def parse_string(buffer) | |
null = "\x0" | |
pos = buffer.index(null) | |
fail "no valid string to parse" if pos.nil? | |
str = buffer[0...pos] | |
buffer = buffer[(pos+1)..-1] | |
[str, buffer] | |
end | |
def parse_message(buffer) | |
type = buffer[0] | |
size = buffer[1..4].unpack("N").first - 4 | |
message = buffer[5...(5+size)] | |
buffer = buffer[(5 + size)..-1] | |
[type, message, buffer] | |
end | |
def connect | |
@socket.write(login_packet) | |
buffer = @socket.recv(1024) | |
loop do | |
type, message, buffer = parse_message(buffer) | |
case type | |
when "Z" then return :ok | |
when "R" then auth(message) | |
when "S" then params(message) | |
when "K" then backend_key_data(message) | |
else fail "Idk what this message is: #{type}" | |
end | |
end | |
ensure | |
@socket.close rescue nil | |
end | |
def backend_key_data(message) | |
process_id, message = parse_uint32(message) | |
puts "process_id: #{process_id}" | |
secret_key, message = parse_uint32(message) | |
puts "secret_key: #{secret_key}" | |
end | |
def params(message) | |
key, message = parse_string(message) | |
value, message = parse_string(message) | |
puts "param: #{key}" | |
puts "value: #{value}" | |
end | |
def auth(message) | |
code, _ = parse_uint32(message) | |
case code | |
when 0 | |
puts "auth: ok" | |
else | |
fail "New auth code #{code}" | |
end | |
nil | |
end | |
end | |
host = "localhost" | |
port = 5432 | |
user = "postgres" | |
database = "some_db" | |
client = Client.new(host, port, user, database) | |
puts client.connect | |
# $ ruby pg.rb | |
# auth: ok | |
# param: application_name | |
# value: | |
# param: client_encoding | |
# value: UTF8 | |
# param: DateStyle | |
# value: ISO, MDY | |
# param: integer_datetimes | |
# value: on | |
# param: IntervalStyle | |
# value: postgres | |
# param: is_superuser | |
# value: on | |
# param: server_encoding | |
# value: UTF8 | |
# param: server_version | |
# value: 9.6.1 | |
# param: session_authorization | |
# value: postgres | |
# param: standard_conforming_strings | |
# value: on | |
# param: TimeZone | |
# value: US/Eastern | |
# process_id: 26819 | |
# secret_key: 1782894957 | |
# ok |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment