Class: Net::TNS::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/net/tns/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Connection

Returns a new instance of Connection.

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/net/tns/connection.rb', line 10

def initialize(opts={})
  @socket = nil

  @host = opts.delete(:host)
  @port = opts.delete(:port) || 1521
  @new_socket_proc = opts.delete(:new_socket_proc)

  raise ArgumentError.new("Unrecognized options: #{opts.keys}") unless opts.empty?

  if @host.nil? == @new_socket_proc.nil?
    raise ArgumentError.new("Invalid socket options. Need :host and :port, OR :new_socket_proc")
  end
end

Instance Attribute Details

#tns_protocol_versionObject (readonly)

Returns the value of attribute tns_protocol_version.



7
8
9
# File 'lib/net/tns/connection.rb', line 7

def tns_protocol_version
  @tns_protocol_version
end

#tns_sduObject (readonly)

Returns the value of attribute tns_sdu.



8
9
10
# File 'lib/net/tns/connection.rb', line 8

def tns_sdu
  @tns_sdu
end

Instance Method Details

#close_socketObject

This is a low-level function to directly close the socket for this connection. Most callers should use #disconnect instead, which will disconnect the TNS connection before closing the socket.



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/net/tns/connection.rb', line 48

def close_socket
  Net::TNS.logger.debug("Connection#close_socket called")
  begin
    unless @socket.nil? or @socket.closed?
      Net::TNS.logger.info("Closing socket")
      @socket.close
    end
  ensure
    @socket = nil
  end
end

#connect(opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/net/tns/connection.rb', line 60

def connect(opts={})
  sid = opts.delete(:sid) if opts.has_key?(:sid)
  service_name = opts.delete(:service_name) if opts.has_key?(:service_name)
  raise ArgumentError.new("Unrecognized opts: #{opts.keys}") unless opts.empty?
  raise ArgumentError.new("Must specify :sid or :service_name") unless sid.nil? != service_name.nil?

  open_socket() if @socket.nil?
  dst_host = @socket.peeraddr[3]
  dst_port = @socket.peeraddr[1]

  if sid
    Net::TNS.logger.debug("Connecting to target by SID (""#{sid}"")")
    connect_packet = ConnectPacket.make_connection_by_sid( dst_host, dst_port, sid )
  elsif service_name
    Net::TNS.logger.debug("Connecting to target by service name (""#{service_name}"")")
    connect_packet = ConnectPacket.make_connection_by_service_name( dst_host, dst_port, service_name )
  end

  response = send_and_receive(connect_packet)
  unless response.is_a?(AcceptPacket) || response.is_a?(RedirectPacket)
    raise Exceptions::ProtocolException.new("Unexpected response to Connect packet: #{response.class}")
  end
  if response.is_a?(RedirectPacket)
    # CLR extproc on 12c will end up here
    return
  end
  @tns_protocol_version = response.version.to_i
  @tns_sdu = response.sdu_size.to_i
  negotiate_ano()
end

#disconnectObject



91
92
93
94
95
96
97
98
# File 'lib/net/tns/connection.rb', line 91

def disconnect
  begin
    packet = DataPacket.make_disconnect_request
    send_tns_packet( packet )
  ensure
    close_socket()
  end
end

#open_socketObject

This is a low-level function to directly open a socket for this connection. Most callers should use #connect instead, which will open a socket and negotiate a TNS connection.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/net/tns/connection.rb', line 27

def open_socket
  Net::TNS.logger.debug("Connection#open_socket called")
  close_socket()

  if @host
    require "socket"
    Net::TNS.logger.info("Creating new TCPSocket for #{@host}:#{@port}")
    @socket = TCPSocket.new(@host, @port)
  elsif @new_socket_proc
    Net::TNS.logger.info("Calling new-socket proc for new socket")
    @socket = @new_socket_proc.call()
  else
    raise ArgumentError.new("Invalid socket options")
  end

  return
end

#receive_tns_packet(waiting_for_error_message = false) ⇒ Net::TNS::Packet

Attempts to receive a TNS message.

Parameters:

  • (Optional) (Boolean)

    Indicates a special state in which an error notification has been received, and we expect to be receiving a response to a request for the error message.

Returns:

Raises:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/net/tns/connection.rb', line 158

def receive_tns_packet( waiting_for_error_message = false )
  # This is structured as a loop in order to handle messages (e.g. Resends)
  # that need to be handled without returning to the caller. To keep a malicious
  # server from making us loop continuously, we set an arbitrary limit of
  # 10 loops without a "real" message before we throw an exception.
  receive_count = 0
  while ( true )
    raise Exceptions::ConnectionClosed if @socket.closed?

    receive_count += 1
    if ( receive_count >= 3 )
      raise Exceptions::TNSException.new( "Maximum receive attempts exceeded - too many Resends received." )
    end

    # Try to receive a TNS packet
    Net::TNS.logger.debug("Attempting to receive packet (try ##{receive_count})")
    packet = Net::TNS::Packet.from_socket(@socket)

    case packet
    when Net::TNS::RefusePacket
      Net::TNS.logger.warn("Received RefusePacket")
      raise Exceptions::RefuseMessageReceived.new( packet.data )

    # # We received a redirect request (typical of Oracle 9 and possibly previous versions)
    # when Net::TNS::RedirectPacket
    #   raise Exceptions::RedirectMessageReceived.new( packet.data )

    # We received a request to resend the last packet
    when Net::TNS::ResendPacket
      Net::TNS.logger.debug("Received ResendPacket")
      # Re-send the last packet and then loop again
      resend_last_tns_packet()

    # We received a normal response
    else
      Net::TNS.logger.debug("Received #{packet.class} (#{packet.num_bytes} bytes)")
      return packet
    end
  end
end

#send_and_receive(packet) ⇒ Object



123
124
125
126
# File 'lib/net/tns/connection.rb', line 123

def send_and_receive( packet )
  send_tns_packet( packet )
  receive_tns_packet()
end

#send_tns_packet(packet) ⇒ Object

Parameters:



129
130
131
132
133
134
135
136
137
138
# File 'lib/net/tns/connection.rb', line 129

def send_tns_packet( packet )
  if @socket.nil? || @socket.closed?
    Net::TNS.logger.warn( "Can't send packet to a closed or nil socket!" )
    return
  end
  # Store this in case we get a Resend
  @tns_last_sent_packet = packet
  Net::TNS.logger.debug( "Sending packet #{packet.class} (#{packet.num_bytes} bytes)" )
  @socket.write( packet.to_binary_s )
end