/
message_channel_spec.rb
196 lines (166 loc) · 5.25 KB
/
message_channel_spec.rb
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
require 'socket'
PhusionPassenger.require_passenger_lib 'message_channel'
module PhusionPassenger
describe MessageChannel do
describe "scenarios with a single channel" do
before :each do
@reader_pipe, @writer_pipe = IO.pipe
@reader = MessageChannel.new(@reader_pipe)
@writer = MessageChannel.new(@writer_pipe)
end
after :each do
@reader_pipe.close unless @reader_pipe.closed?
@writer_pipe.close unless @writer_pipe.closed?
end
it "can read a single written array message" do
@writer.write("hello")
@reader.read.should == ["hello"]
end
it "can handle array messages that contain spaces" do
@writer.write("hello world", "! ")
@reader.read.should == ["hello world", "! "]
end
it "can handle array messages that have only a single empty string" do
@writer.write("")
@reader.read.should == [""]
end
it "can handle array messages with empty arguments" do
@writer.write("hello", "", "world")
@reader.read.should == ["hello", "", "world"]
@writer.write("")
@reader.read.should == [""]
@writer.write(nil, "foo")
@reader.read.should == ["", "foo"]
end
it "properly detects end-of-file when reading an array message" do
@writer.close
@reader.read.should be_nil
end
specify "#read_hash works" do
@writer.write("hello", "world")
@reader.read_hash.should == { "hello" => "world" }
@writer.write("hello", "world", "foo", "bar", "", "...")
@reader.read_hash.should == { "hello" => "world", "foo" => "bar", "" => "..." }
end
specify "#read_hash throws an exception if the array message doesn't have an even number of items" do
@writer.write("foo")
lambda { @reader.read_hash }.should raise_error(MessageChannel::InvalidHashError)
@writer.write("foo", "bar", "baz")
lambda { @reader.read_hash }.should raise_error(MessageChannel::InvalidHashError)
end
it "can read a single written scalar message" do
@writer.write_scalar("hello world")
@reader.read_scalar.should == "hello world"
end
it "can handle empty scalar messages" do
@writer.write_scalar("")
@reader.read_scalar.should == ""
end
it "properly detects end-of-file when reading a scalar message" do
@writer.close
@reader.read_scalar.should be_nil
end
it "puts the data into the given buffer" do
buffer = ''
@writer.write_scalar("x" * 100)
result = @reader.read_scalar(buffer)
result.object_id.should == buffer.object_id
buffer.should == "x" * 100
end
it "raises SecurityError when a received scalar message's size is larger than a specified maximum" do
@writer.write_scalar(" " * 100)
lambda { @reader.read_scalar('', 99) }.should raise_error(SecurityError)
end
end
describe "scenarios with 2 channels and 2 concurrent processes" do
after :each do
@parent_socket.close
Process.waitpid(@pid) rescue nil
end
def spawn_process
@parent_socket, @child_socket = UNIXSocket.pair
@pid = fork do
@parent_socket.close
@channel = MessageChannel.new(@child_socket)
begin
yield
rescue Exception => e
print_exception("child", e)
ensure
@child_socket.close
exit!
end
end
@child_socket.close
@channel = MessageChannel.new(@parent_socket)
end
it "both processes can read and write a single array message" do
spawn_process do
x = @channel.read
@channel.write("#{x[0]}!")
end
@channel.write("hello")
@channel.read.should == ["hello!"]
end
it "can handle scalar messages with arbitrary binary data" do
garbage_files = ["garbage1.dat", "garbage2.dat", "garbage3.dat"]
spawn_process do
garbage_files.each do |name|
data = File.binread("stub/#{name}")
@channel.write_scalar(data)
end
end
garbage_files.each do |name|
data = File.binread("stub/#{name}")
@channel.read_scalar.should == data
end
end
it "supports IO object (file descriptor) passing" do
spawn_process do
writer = @channel.recv_io
writer.write("it works")
writer.close
end
reader, writer = IO.pipe
@channel.send_io(writer)
writer.close
reader.read.should == "it works"
reader.close
end
it "supports large amounts of data" do
iterations = 1000
blob = "123" * 1024
spawn_process do
iterations.times do |i|
@channel.write(blob)
end
end
iterations.times do
@channel.read.should == [blob]
end
end
it "has stream properties" do
garbage = File.binread("stub/garbage1.dat")
spawn_process do
@channel.write("hello", "world")
@channel.write_scalar(garbage)
@channel.send_io(STDIN)
@channel.write_scalar(":-)")
a = @channel.read_scalar
b = @channel.read
b << a
@channel.write(*b)
end
@channel.read.should == ["hello", "world"]
@channel.read_scalar.should == garbage
@channel.recv_io.close
@channel.read_scalar.should == ":-)"
@channel.write_scalar("TASTE MY WRATH! ULTIMATE SWORD TECHNIQUE!! DRAGON'S BREATH SL--")
@channel.write("Uhm, watch your step.", "WAAHH?!", "Calm down, Motoko!!")
@channel.read.should == ["Uhm, watch your step.", "WAAHH?!", "Calm down, Motoko!!",
"TASTE MY WRATH! ULTIMATE SWORD TECHNIQUE!! DRAGON'S BREATH SL--"]
end
end
end
end # module PhusionPassenger