summaryrefslogtreecommitdiff
path: root/test/socket/test_tcp.rb
blob: 7f9dc53cae266bc83b0b92bb001d91735e2c6ded (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
59
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# frozen_string_literal: true

begin
  require "socket"
  require "test/unit"
rescue LoadError
end


class TestSocket_TCPSocket < Test::Unit::TestCase
  def test_inspect
    TCPServer.open("localhost", 0) {|server|
      assert_match(/AF_INET/, server.inspect)
      TCPSocket.open("localhost", server.addr[1]) {|client|
        assert_match(/AF_INET/, client.inspect)
      }
    }
  end

  def test_initialize_failure
    # These addresses are chosen from TEST-NET-1, TEST-NET-2, and TEST-NET-3.
    # [RFC 5737]
    # They are chosen because probably they are not used as a host address.
    # Anyway the addresses are used for bind() and should be failed.
    # So no packets should be generated.
    test_ip_addresses = [
      '192.0.2.1', '192.0.2.42', # TEST-NET-1
      '198.51.100.1', '198.51.100.42', # TEST-NET-2
      '203.0.113.1', '203.0.113.42', # TEST-NET-3
    ]
    begin
      list = Socket.ip_address_list
    rescue NotImplementedError
      return
    end
    test_ip_addresses -= list.reject {|ai| !ai.ipv4? }.map {|ai| ai.ip_address }
    if test_ip_addresses.empty?
      return
    end
    client_addr = test_ip_addresses.first
    client_port = 8000

    server_addr = '127.0.0.1'
    server_port = 80

    begin
      # Since client_addr is not an IP address of this host,
      # bind() in TCPSocket.new should fail as EADDRNOTAVAIL.
      t = TCPSocket.new(server_addr, server_port, client_addr, client_port)
      flunk "expected SystemCallError"
    rescue SystemCallError => e
      assert_match "for \"#{client_addr}\" port #{client_port}", e.message
    end
  ensure
    t.close if t && !t.closed?
  end

  def test_initialize_resolv_timeout
    TCPServer.open("localhost", 0) do |svr|
      th = Thread.new {
        c = svr.accept
        c.close
      }
      addr = svr.addr
      s = TCPSocket.new(addr[3], addr[1], resolv_timeout: 10)
      th.join
    ensure
      s.close()
    end
  end

  def test_initialize_connect_timeout
    assert_raise(IO::TimeoutError, Errno::ENETUNREACH) do
      TCPSocket.new("192.0.2.1", 80, connect_timeout: 0)
    end
  end

  def test_recvfrom
    TCPServer.open("localhost", 0) {|svr|
      th = Thread.new {
        c = svr.accept
        c.write "foo"
        c.close
      }
      addr = svr.addr
      TCPSocket.open(addr[3], addr[1]) {|sock|
        assert_equal(["foo", nil], sock.recvfrom(0x10000))
      }
      th.join
    }
  end

  def test_encoding
    TCPServer.open("localhost", 0) {|svr|
      th = Thread.new {
        c = svr.accept
        c.write "foo\r\n"
        c.close
      }
      addr = svr.addr
      TCPSocket.open(addr[3], addr[1]) {|sock|
        assert_equal(true, sock.binmode?)
        s = sock.gets
        assert_equal("foo\r\n", s)
        assert_equal(Encoding.find("ASCII-8BIT"), s.encoding)
      }
      th.join
    }
  end

  def test_accept_nonblock
    TCPServer.open("localhost", 0) {|svr|
      assert_raise(IO::WaitReadable) { svr.accept_nonblock }
      assert_equal :wait_readable, svr.accept_nonblock(exception: false)
      assert_raise(IO::WaitReadable) { svr.accept_nonblock(exception: true) }
    }
  end

  def test_accept_multithread
    attempts_count       = 5
    server_threads_count = 3
    client_threads_count = 3

    attempts_count.times do
      server_threads = Array.new(server_threads_count) do
        Thread.new do
          TCPServer.open("localhost", 0) do |server|
            accept_threads = Array.new(client_threads_count) do
              Thread.new { server.accept.close }
            end
            client_threads = Array.new(client_threads_count) do
              Thread.new { TCPSocket.open(server.addr[3], server.addr[1]) {} }
            end
            client_threads.each(&:join)
            accept_threads.each(&:join)
          end
        end
      end

      server_threads.each(&:join)
    end
  end
end if defined?(TCPSocket)