Class: Net::NATPMP::Request

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/net/natpmp/requests.rb

Overview

Main request class

Direct Known Subclasses

ExternalAddressRequest, MappingRequest

Constant Summary

Constants included from Constants

Constants::BASE_DELAY, Constants::DEFAULT_INSIDE_PORT, Constants::DEFAULT_LIFETIME, Constants::DEFAULT_OUTSIDE_PORT, Constants::DEFAULT_PROTO, Constants::MAX_WAIT, Constants::OP_CODES, Constants::PROTO_CODES, Constants::RESULT_CODES, Constants::RESULT_CODES_DESC, Constants::VERSION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Request

Returns a new instance of Request.



14
15
16
17
18
19
20
# File 'lib/net/natpmp/requests.rb', line 14

def initialize(config)
  @config = config

  @socket = UDPSocket.new
  @socket.bind(config.bind_address.to_s, config.bind_port)
  @socket.connect(config.gw.to_s, config.port)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



12
13
14
# File 'lib/net/natpmp/requests.rb', line 12

def config
  @config
end

#socketObject (readonly)

Returns the value of attribute socket.



12
13
14
# File 'lib/net/natpmp/requests.rb', line 12

def socket
  @socket
end

Class Method Details

.req(config, _opts = {}) ⇒ Object



22
23
24
# File 'lib/net/natpmp/requests.rb', line 22

def self.req(config, _opts = {})
  new(config)
end

Instance Method Details

#check_reply(reply, sent_op) ⇒ Object

Raises:



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/net/natpmp/requests.rb', line 60

def check_reply(reply, sent_op)
  # Check the first 4 bytes only (the rest are variable)
  version, opcode, result = reply.unpack('CCn')

  # Check the version in the reply
  raise InvalidVersion, "Invalid version #{version}" unless version == VERSION

  # Check the operation code in the reply. Always (128 + sent opcode)
  expected_opcode = 128 + sent_op
  if opcode != expected_opcode
    raise InvalidReply,
          "Invalid reply opcode. Was expecting #{expected_opcode}, got: #{opcode}"
  end

  # Check the result code in the reply
  raise RequestFailed, result_code: result unless result == RESULT_CODES[:success]
end

#send(msg, expected_response_size = 16) ⇒ Object

Send a message to the NAT-PMP server. Takes a message and an optional response size



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/net/natpmp/requests.rb', line 27

def send(msg, expected_response_size = 16)
  sent_op = msg.unpack1('xC') # To verify the response
  size_sent = @socket.send(msg, 0)

  raise RequestFailed unless size_sent == msg.size

  delay = Constants::BASE_DELAY
  attempts = 1

  begin
    sleep delay
    reply, = @socket.recvfrom_nonblock(expected_response_size)

    check_reply(reply, sent_op)

    reply
  rescue IO::WaitReadable
    if delay < MAX_WAIT
      delay *= 2
      puts "Timeout, retrying after #{delay} seconds"
      attempts += 1
      retry
    end

    raise TimeoutException, "Timeout after #{attempts} attempts"
  rescue Errno::ECONNREFUSED
    raise ConnectionRefused, 'Connection refused'
  end
ensure
  sleep 0.25 # Sleep for a bit to make sure the socket is not closed too soon
  @socket.close # Close the socket because we don't need it anymore
end