summaryrefslogtreecommitdiff
path: root/spec/ruby/library/socket/tcpserver/new_spec.rb
blob: 8d9696c9d86b70c4506cee0c5eeac7174d26eca7 (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
require_relative '../spec_helper'
require_relative '../fixtures/classes'

describe "TCPServer.new" do
  after :each do
    @server.close if @server && !@server.closed?
  end

  it "binds to a host and a port" do
    @server = TCPServer.new('127.0.0.1', 0)
    addr = @server.addr
    addr[0].should == 'AF_INET'
    addr[1].should be_kind_of(Integer)
    # on some platforms (Mac), MRI
    # returns comma at the end.
    addr[2].should =~ /^#{SocketSpecs.hostname}\b/
    addr[3].should == '127.0.0.1'
  end

  it "binds to localhost and a port with either IPv4 or IPv6" do
    @server = TCPServer.new(SocketSpecs.hostname, 0)
    addr = @server.addr
    addr[1].should be_kind_of(Integer)
    if addr[0] == 'AF_INET'
      addr[2].should =~ /^#{SocketSpecs.hostname}\b/
      addr[3].should == '127.0.0.1'
    else
      addr[2].should =~ /^#{SocketSpecs.hostname('::1')}\b/
      addr[3].should == '::1'
    end
  end

  it "binds to INADDR_ANY if the hostname is empty" do
    @server = TCPServer.new('', 0)
    addr = @server.addr
    addr[0].should == 'AF_INET'
    addr[1].should be_kind_of(Integer)
    addr[2].should == '0.0.0.0'
    addr[3].should == '0.0.0.0'
  end

  it "binds to INADDR_ANY if the hostname is empty and the port is a string" do
    @server = TCPServer.new('', '0')
    addr = @server.addr
    addr[0].should == 'AF_INET'
    addr[1].should be_kind_of(Integer)
    addr[2].should == '0.0.0.0'
    addr[3].should == '0.0.0.0'
  end

  it "binds to a port if the port is explicitly nil" do
    @server = TCPServer.new('', nil)
    addr = @server.addr
    addr[0].should == 'AF_INET'
    addr[1].should be_kind_of(Integer)
    addr[2].should == '0.0.0.0'
    addr[3].should == '0.0.0.0'
  end

  it "binds to a port if the port is an empty string" do
    @server = TCPServer.new('', '')
    addr = @server.addr
    addr[0].should == 'AF_INET'
    addr[1].should be_kind_of(Integer)
    addr[2].should == '0.0.0.0'
    addr[3].should == '0.0.0.0'
  end

  it "coerces port to string, then determines port from that number or service name" do
    -> { TCPServer.new(SocketSpecs.hostname, Object.new) }.should raise_error(TypeError)

    port = Object.new
    port.should_receive(:to_str).and_return("0")

    @server = TCPServer.new(SocketSpecs.hostname, port)
    addr = @server.addr
    addr[1].should be_kind_of(Integer)

    # TODO: This should also accept strings like 'https', but I don't know how to
    # pick such a service port that will be able to reliably bind...
  end

  it "has a single argument form and treats it as a port number" do
    @server = TCPServer.new(0)
    addr = @server.addr
    addr[1].should be_kind_of(Integer)
  end

  it "coerces port to a string when it is the only argument" do
    -> { TCPServer.new(Object.new) }.should raise_error(TypeError)

    port = Object.new
    port.should_receive(:to_str).and_return("0")

    @server = TCPServer.new(port)
    addr = @server.addr
    addr[1].should be_kind_of(Integer)
  end

  it "raises Errno::EADDRNOTAVAIL when the address is unknown" do
    -> { TCPServer.new("1.2.3.4", 0) }.should raise_error(Errno::EADDRNOTAVAIL)
  end

  # There is no way to make this fail-proof on all machines, because
  # DNS servers like opendns return A records for ANY host, including
  # traditionally invalidly named ones.
  quarantine! do
    it "raises a SocketError when the host is unknown" do
      -> {
        TCPServer.new("--notavalidname", 0)
      }.should raise_error(SocketError)
    end
  end

  it "raises Errno::EADDRINUSE when address is already in use" do
    @server = TCPServer.new('127.0.0.1', 0)
    -> {
      @server = TCPServer.new('127.0.0.1', @server.addr[1])
    }.should raise_error(Errno::EADDRINUSE)
  end

  platform_is_not :windows, :aix do
    # A known bug in AIX.  getsockopt(2) does not properly set
    # the fifth argument for SO_REUSEADDR.
    it "sets SO_REUSEADDR on the resulting server" do
      @server = TCPServer.new('127.0.0.1', 0)
      @server.getsockopt(:SOCKET, :REUSEADDR).data.should_not == "\x00\x00\x00\x00"
      @server.getsockopt(:SOCKET, :REUSEADDR).int.should_not == 0
    end
  end
end