win32-pipe 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES ADDED
@@ -0,0 +1,34 @@
1
+ == 0.2.0 - 28-May-2008
2
+ * Now pure Ruby.
3
+ * Major interface change. Pipe::Server.new and Pipe::Client.new replace the
4
+ Pipe.new_server and Pipe.new_client methods, respectively.
5
+ * An optional 3rd argument, the open mode, is now accepted which allows finer
6
+ control over how pipes are created.
7
+ * Several pipe mode and open mode constants were added.
8
+ * The asynchronous pipe server actually works now.
9
+ * Added the Pipe#name method.
10
+ * Added the Pipe#asynchronous? method.
11
+ * Added the Pipe#size method as an alias for Pipe#length.
12
+ * Added a Rakefile with tasks for testing and installation.
13
+ * Added a gemspec and uploaded a gem file to RubyForge.
14
+ * Merged the doc files into the README and/or replaced them with inlined
15
+ comments that are RDoc friendly.
16
+
17
+ == 0.1.2 - 1-Mar-2005
18
+ * Moved the 'examples' directory to the toplevel directory.
19
+ * Made the CHANGES and README files rdoc friendly.
20
+
21
+ == 0.1.1 - 25-Aug-2004
22
+ * Added many more tests to the test suite.
23
+ * Moved the example programs to doc/examples.
24
+ * Fixed minor bugs in the asynchronous client and server test programs.
25
+ * Removed the pipe.html file. You can generate your own html documentation
26
+ using rd2 on the pipe.rd file.
27
+
28
+ == 0.1.0 - 13-Feb-2004
29
+ * Asynchronous support added (thanks Park Heesob)
30
+ * Sample test programs added. See files under 'test'.
31
+ * Documentation updates.
32
+
33
+ == 0.0.1 - 20-Nov-2003
34
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,15 @@
1
+ * MANIFEST
2
+ * README
3
+ * CHANGES
4
+ * Rakefile
5
+ * win32-pipe.gemspec
6
+ * examples/test_server.rb
7
+ * examples/test_server_async.rb
8
+ * examples/test_client.rb
9
+ * examples/test_client_async.rb
10
+ * lib/win32/pipe.rb
11
+ * lib/win32/pipe/client.rb
12
+ * lib/win32/pipe/server.rb
13
+ * test/tc_pipe.rb
14
+ * test/tc_pipe_client.rb
15
+ * test/tc_pipe_server.rb
data/README ADDED
@@ -0,0 +1,56 @@
1
+ = Description
2
+ A Ruby interface for named pipes on Windows.
3
+
4
+ = Prerequisites
5
+ * windows-pr 0.8.5 or later
6
+
7
+ = Installation
8
+ == Local
9
+ rake install (non-gem) or rake install_gem (gem)
10
+
11
+ == Remote
12
+ gem install win32-pipe
13
+
14
+ = Synopsis
15
+ require 'win32/pipe'
16
+ include Win32
17
+
18
+ # In server.rb
19
+ pipe_server = Pipe::Server.new("foo_pipe")
20
+ pipe_server.connect
21
+ data = pipe_server.read
22
+ puts "Got #{data} from client"
23
+ pipe_server.close
24
+
25
+ # In client.rb (run from a different shell)
26
+ pipe_client = Pipe::Client.new("foo_pipe")
27
+ pipe_client.write("Hello World")
28
+ pipe_client.close
29
+
30
+ = What's a named pipe?
31
+ A pipe with a name - literally. In practice, it will feel more like a cross
32
+ between a socket and a pipe. At least, it does to me.
33
+
34
+ = What good is it?
35
+ My hope is that it can be used in certain circumstances where a fork might
36
+ be desirable, but which is not possible on Windows. It could also be handy
37
+ for the traditional "piping data to a server" usage. And if you come up
38
+ with anything cool, please let us all know!
39
+
40
+ = Future Plans
41
+ * Add transactions
42
+
43
+ = License
44
+ Ruby's
45
+
46
+ = Warranty
47
+ This package is provided "as is" and without any express or
48
+ implied warranties, including, without limitation, the implied
49
+ warranties of merchantability and fitness for a particular purpose.
50
+
51
+ = Copyright
52
+ (C) 2003-2008, Daniel J. Berger, All Rights Reserved.
53
+
54
+ = Authors
55
+ Daniel Berger
56
+ Park Heesob
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ include Config
6
+
7
+ desc 'Install the win32-pipe library (non-gem)'
8
+ task :install do
9
+ sitelibdir = CONFIG['sitelibdir']
10
+
11
+ pipe_installdir = File.join(sitelibdir, 'win32')
12
+ sub_installdir = File.join(sitelibdir, 'win32', 'pipe')
13
+
14
+ pipe_file = File.join('lib', 'win32', 'pipe.rb')
15
+ client_file = File.join('lib', 'win32', 'pipe', 'client.rb')
16
+ server_file = File.join('lib', 'win32', 'pipe', 'server.rb')
17
+
18
+ FileUtils.mkdir_p(sub_installdir)
19
+
20
+ FileUtils.cp(pipe_file, pipe_installdir, :verbose => true)
21
+ FileUtils.cp(client_file, sub_installdir, :verbose => true)
22
+ FileUtils.cp(server_file, sub_installdir, :verbose => true)
23
+ end
24
+
25
+ desc 'Install the win32-pipe library'
26
+ task :install_c => [:build] do
27
+ Dir.chdir('ext'){
28
+ sh 'nmake install'
29
+ }
30
+ end
31
+
32
+ desc "Clean any build files for win32-pipe"
33
+ task :clean do
34
+ Dir.chdir('ext') do
35
+ if File.exists?('pipe.so') ||
36
+ File.exists?('win32/pipe.so')
37
+ then
38
+ sh 'nmake distclean'
39
+ rm 'win32/pipe.so' if File.exists?('win32/pipe.so')
40
+ end
41
+ end
42
+ end
43
+
44
+ desc "Build win32-pipe (but don't install it)"
45
+ task :build => [:clean] do
46
+ Dir.chdir('ext') do
47
+ ruby 'extconf.rb'
48
+ sh 'nmake'
49
+ mv 'pipe.so', 'win32' # For the test suite
50
+ end
51
+ end
52
+
53
+ desc "Run the sample program"
54
+ task :example do |t|
55
+ Dir.chdir('examples'){
56
+ sh 'ruby test.rb'
57
+ }
58
+ end
59
+
60
+ Rake::TestTask.new('test') do |test|
61
+ test.libs << 'lib/win32'
62
+ test.libs << 'lib/win32/pipe'
63
+ test.test_files = FileList['test/tc*']
64
+ test.warning = true
65
+ end
@@ -0,0 +1,30 @@
1
+ #########################################################################
2
+ # test_client.rb
3
+ #
4
+ # Simple client test. Be sure to start the server first in a separate
5
+ # terminal. You can run this example via the 'rake example_client' task.
6
+ #
7
+ # Modify this code as you see fit.
8
+ #########################################################################
9
+ require 'win32/pipe'
10
+ include Win32
11
+
12
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
13
+
14
+ puts "VERSION: " + Pipe::VERSION
15
+
16
+ # Block form
17
+ Pipe::Client.new('foo') do |pipe|
18
+ puts "Connected..."
19
+ pipe.write("Ruby rocks!")
20
+ data = pipe.read
21
+ puts "Got [#{data}] back from server"
22
+ end
23
+
24
+ # Non-block form
25
+ #pclient = Pipe::Client.new('foo')
26
+ #puts "Connected..."
27
+ #pclient.write("Ruby rocks!")
28
+ #data = pclient.read
29
+ #puts "Got [#{data}] back from server"
30
+ #pclient.close
@@ -0,0 +1,82 @@
1
+ #########################################################################
2
+ # test_client_async.rb
3
+ #
4
+ # Simple client test. Be sure to start the server first in a separate
5
+ # terminal. You can run this example via the 'rake example_async_client'
6
+ # task.
7
+ #########################################################################
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ puts "VERSION: " + Pipe::VERSION
12
+
13
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
14
+
15
+ CONNECTING_STATE = 0
16
+ READING_STATE = 1
17
+ WRITING_STATE = 2
18
+
19
+ class MyPipe < Pipe::Client
20
+ def read_complete
21
+ puts "read_complete"
22
+ puts "Got [#{buffer}] back from server"
23
+ @state = WRITING_STATE
24
+ end
25
+
26
+ def write_complete
27
+ puts "write_complete"
28
+ @state = READING_STATE
29
+ end
30
+
31
+ def mainloop
32
+ @state = WRITING_STATE
33
+ while true
34
+ if wait(1) # wait for 1 second
35
+ if pending? # IO is pending
36
+ case @state
37
+ when READING_STATE
38
+ if transferred == 0
39
+ reconnect
40
+ break
41
+ end
42
+ read_complete
43
+ break
44
+ when WRITING_STATE
45
+ if transferred != length
46
+ reconnect
47
+ break
48
+ end
49
+ write_complete
50
+ end
51
+ end
52
+
53
+ case @state
54
+ when READING_STATE
55
+ if read
56
+ if not pending?
57
+ read_complete
58
+ break
59
+ end
60
+ end
61
+ when WRITING_STATE
62
+ if write("Ruby rocks!")
63
+ if not pending?
64
+ write_complete
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ sleep(1)
71
+ puts "pipe client is running"
72
+ end
73
+ end
74
+ end
75
+
76
+ flags = Pipe::DEFAULT_OPEN_MODE | Pipe::OVERLAPPED
77
+
78
+ MyPipe.new('foo', nil, flags) do |client|
79
+ puts "Connected..."
80
+ client.mainloop
81
+ end
82
+
@@ -0,0 +1,33 @@
1
+ #########################################################################
2
+ # test_server.rb
3
+ #
4
+ # A simple named pipe server. Start this up in its own terminal window.
5
+ # You may have to use the task manager to kill it if you don't connect
6
+ # with the test client program.
7
+ #
8
+ # You can start this server with the 'rake example_server' task. Modify
9
+ # this code as you see fit.
10
+ #########################################################################
11
+ require 'win32/pipe'
12
+ include Win32
13
+
14
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
15
+
16
+ puts "VERSION: " + Pipe::VERSION
17
+
18
+ # Block form
19
+ Pipe::Server.new('foo') do |pipe|
20
+ pipe.connect
21
+ data = pipe.read
22
+ puts "Got [#{data}]"
23
+ pipe.write "Thanks for the data!"
24
+ end
25
+
26
+ # Non-block form
27
+ #pserver = Pipe::Server.new('foo')
28
+ #pserver.connect # put server in wait connect
29
+ #data = pserver.read
30
+ #puts "Got [#{data}]"
31
+ #pserver.write("Thanks for the data!")
32
+ #pserver.disconnect
33
+ #pserver.close
@@ -0,0 +1,100 @@
1
+ #######################################################################
2
+ # test_server_async.rb
3
+ #
4
+ # A simple, asynchronous named pipe server. Start this up in its own
5
+ # terminal window. You can run this program via the
6
+ # 'rake example_async_server' task.
7
+ #######################################################################
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ puts "VERSION: " + Pipe::VERSION
12
+
13
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
14
+
15
+ CONNECTING_STATE = 0
16
+ READING_STATE = 1
17
+ WRITING_STATE = 2
18
+
19
+ class MyPipe < Pipe::Server
20
+ def connected
21
+ puts "connected"
22
+ @state = READING_STATE
23
+ end
24
+
25
+ def read_complete
26
+ puts "read_complete"
27
+ puts "Got [#{buffer}]"
28
+ @state = WRITING_STATE
29
+ end
30
+
31
+ def write_complete
32
+ puts "write_complete"
33
+ disconnect
34
+ @state = CONNECTING_STATE
35
+ end
36
+
37
+ def reconnect
38
+ disconnect
39
+ mainloop
40
+ end
41
+
42
+ def mainloop
43
+ @state = CONNECTING_STATE
44
+ while true
45
+ if wait(1) # wait for 1 second
46
+ if pending? # IO is pending
47
+ case @state
48
+ when CONNECTING_STATE
49
+ connected
50
+ when READING_STATE
51
+ if transferred == 0
52
+ reconnect
53
+ break
54
+ end
55
+ read_complete
56
+ when WRITING_STATE
57
+ if transferred != length
58
+ reconnect
59
+ break
60
+ end
61
+ write_complete
62
+ end
63
+ end
64
+
65
+ case @state
66
+ when CONNECTING_STATE
67
+ if connect
68
+ connected
69
+ end
70
+ when READING_STATE
71
+ if read
72
+ if !pending?
73
+ read_complete
74
+ end
75
+ else
76
+ reconnect
77
+ end
78
+ when WRITING_STATE
79
+ if write("Thanks for the data!")
80
+ if not pending?
81
+ write_complete
82
+ end
83
+ else
84
+ reconnect
85
+ break
86
+ end
87
+ end
88
+ end
89
+
90
+ sleep(1)
91
+ puts "pipe server is running"
92
+ end
93
+ end
94
+ end
95
+
96
+ flags = Pipe::ACCESS_DUPLEX | Pipe::OVERLAPPED
97
+
98
+ MyPipe.new('foo', 0, flags) do |pipe|
99
+ pipe.mainloop
100
+ end
data/lib/win32/pipe.rb ADDED
@@ -0,0 +1,253 @@
1
+ require 'windows/pipe'
2
+ require 'windows/synchronize'
3
+ require 'windows/handle'
4
+ require 'windows/file'
5
+ require 'windows/error'
6
+
7
+ # The Win32 module serves as a namespace only.
8
+ module Win32
9
+ # The Pipe class is an abstract base class for the Pipe::Server and
10
+ # Pipe::Client classes. Do not use this directly.
11
+ #
12
+ class Pipe
13
+ include Windows::Pipe
14
+ include Windows::Synchronize
15
+ include Windows::Handle
16
+ include Windows::File
17
+ include Windows::Error
18
+
19
+ class Error < StandardError; end
20
+
21
+ # The version of this library
22
+ VERSION = '0.2.0'
23
+
24
+ PIPE_BUFFER_SIZE = 512 #:nodoc:
25
+ PIPE_TIMEOUT = 5000 #:nodoc:
26
+
27
+ # Blocking mode is enabled
28
+ WAIT = PIPE_WAIT
29
+
30
+ # Nonblocking mode is enabled
31
+ NOWAIT = PIPE_NOWAIT
32
+
33
+ # The pipe is bi-directional. Both server and client processes can read
34
+ # from and write to the pipe.
35
+ ACCESS_DUPLEX = PIPE_ACCESS_DUPLEX
36
+
37
+ # The flow of data in the pipe goes from client to server only.
38
+ ACCESS_INBOUND = PIPE_ACCESS_INBOUND
39
+
40
+ # The flow of data in the pipe goes from server to client only.
41
+ ACCESS_OUTBOUND = PIPE_ACCESS_OUTBOUND
42
+
43
+ # Data is written to the pipe as a stream of bytes.
44
+ TYPE_BYTE = PIPE_TYPE_BYTE
45
+
46
+ # Data is written to the pipe as a stream of messages.
47
+ TYPE_MESSAGE = PIPE_TYPE_MESSAGE
48
+
49
+ # Data is read from the pipe as a stream of bytes.
50
+ READMODE_BYTE = PIPE_READMODE_BYTE
51
+
52
+ # Data is read from the pipe as a stream of messages.
53
+ READMODE_MESSAGE = PIPE_READMODE_MESSAGE
54
+
55
+ # All instances beyond the first will fail with access denied errors.
56
+ FIRST_PIPE_INSTANCE = FILE_FLAG_FIRST_PIPE_INSTANCE
57
+
58
+ # Functions do not return until the data is written across the network.
59
+ WRITE_THROUGH = FILE_FLAG_WRITE_THROUGH
60
+
61
+ # Overlapped mode enables asynchronous communication.
62
+ OVERLAPPED = FILE_FLAG_OVERLAPPED
63
+
64
+ # The default pipe mode
65
+ DEFAULT_PIPE_MODE = NOWAIT
66
+
67
+ # The default open mode
68
+ DEFAULT_OPEN_MODE = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH
69
+
70
+ # The data still in the pipe's buffer
71
+ attr_reader :buffer
72
+
73
+ # The number of bytes to be written to the pipe.
74
+ attr_reader :size
75
+
76
+ # The number of characters that are actually transferred over the pipe.
77
+ attr_reader :transferred
78
+
79
+ # The full name of the pipe, e.g. "\\\\.\\pipe\\my_pipe"
80
+ attr_reader :name
81
+
82
+ # The pipe mode of the pipe.
83
+ attr_reader :open_mode
84
+
85
+ # The open mode of the pipe.
86
+ attr_reader :pipe_mode
87
+
88
+ # Abstract initializer for base class. This handles automatic prepending
89
+ # of '\\.\pipe\' to each named pipe so that you don't have to. Don't
90
+ # use this directly. Add the full implementation in subclasses.
91
+ #
92
+ # The default pipe mode is PIPE_WAIT.
93
+ #
94
+ # The default open mode is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH.
95
+ #
96
+ def initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE)
97
+ @name = "\\\\.\\pipe\\" + name
98
+
99
+ @pipe_mode = pipe_mode.nil? ? DEFAULT_PIPE_MODE : pipe_mode
100
+ @open_mode = open_mode.nil? ? DEFAULT_OPEN_MODE : open_mode
101
+
102
+ @pipe = nil
103
+ @pending_io = false
104
+ @buffer = 0.chr * PIPE_BUFFER_SIZE
105
+ @size = 0
106
+ @overlapped = 0.chr * 20 # sizeof(OVERLAPPED)
107
+ @transferred = 0
108
+ @asynchronous = false
109
+
110
+ if open_mode & FILE_FLAG_OVERLAPPED > 0
111
+ @asynchronous = true
112
+ end
113
+
114
+ if @asynchronous
115
+ @event = CreateEvent(nil, true, true, nil)
116
+ @overlapped[16, 4] = [@event].pack('L')
117
+ end
118
+ end
119
+
120
+ # Disconnects the pipe.
121
+ def disconnect
122
+ DisconnectNamedPipe(@pipe)
123
+ end
124
+
125
+ # Closes the pipe.
126
+ #
127
+ def close
128
+ CloseHandle(@pipe)
129
+ end
130
+
131
+ # Returns whether or not there is a pending IO operation on the pipe.
132
+ #
133
+ def pending?
134
+ @pending_io
135
+ end
136
+
137
+ # Returns whether or not the pipe is asynchronous.
138
+ #
139
+ def asynchronous?
140
+ @asynchronous
141
+ end
142
+
143
+ # Reads data from the pipe. You can read data from either end of a named
144
+ # pipe.
145
+ #
146
+ def read
147
+ bytes = [0].pack('L')
148
+ @buffer = 0.chr * PIPE_BUFFER_SIZE
149
+
150
+ if @asynchronous
151
+ bool = ReadFile(@pipe, @buffer, @buffer.size, bytes, @overlapped)
152
+
153
+ bytes_read = bytes.unpack('L').first
154
+
155
+ if bool && bytes_read > 0
156
+ @pending_io = false
157
+ @buffer = @buffer[0, bytes_read]
158
+ return true
159
+ end
160
+
161
+ error = GetLastError()
162
+ if !bool && error == ERROR_IO_PENDING
163
+ @pending_io = true
164
+ return true
165
+ end
166
+
167
+ return false
168
+ else
169
+ unless ReadFile(@pipe, @buffer, @buffer.size, bytes, nil)
170
+ raise Error, get_last_error
171
+ end
172
+ end
173
+
174
+ @buffer.unpack("A*")
175
+ end
176
+
177
+ # Writes 'data' to the pipe. You can write data to either end of a
178
+ # named pipe.
179
+ #
180
+ def write(data)
181
+ @buffer = data
182
+ @size = data.size
183
+ bytes = [0].pack('L')
184
+
185
+ if @asynchronous
186
+ bool = WriteFile(@pipe, @buffer, @buffer.size, bytes, @overlapped)
187
+
188
+ bytes_written = bytes.unpack('L').first
189
+
190
+ if bool && bytes_written > 0
191
+ @pending_io = false
192
+ return true
193
+ end
194
+
195
+ error = GetLastError()
196
+
197
+ if !bool && error == ERROR_IO_PENDING
198
+ @pending_io = true
199
+ return true
200
+ end
201
+
202
+ return false
203
+ else
204
+ unless WriteFile(@pipe, @buffer, @buffer.size, bytes, 0)
205
+ raise Error, get_last_error
206
+ end
207
+
208
+ return true
209
+ end
210
+ end
211
+
212
+ # Returns the pipe object if an event (such as a client connection)
213
+ # occurs within the +max_time+ specified (in seconds). Otherwise, it
214
+ # returns false.
215
+ #
216
+ def wait(max_time = nil)
217
+ unless @asynchronous
218
+ raise Error, 'cannot wait in synchronous (blocking) mode'
219
+ end
220
+
221
+ max_time = max_time ? max_time * 1000 : INFINITE
222
+
223
+ wait = WaitForSingleObject(@event, max_time)
224
+
225
+ if wait == WAIT_TIMEOUT
226
+ return false
227
+ else
228
+ if wait != WAIT_OBJECT_0
229
+ raise Error, get_last_error
230
+ end
231
+ end
232
+
233
+ if @pending_io
234
+ transferred = [0].pack('L')
235
+ bool = GetOverlappedResult(@pipe, @overlapped, transferred, false)
236
+
237
+ unless bool
238
+ raise Error, get_last_error
239
+ end
240
+
241
+ @transferred = transferred.unpack('L')[0]
242
+ @buffer = @buffer[0, @transferred]
243
+ end
244
+
245
+ self
246
+ end
247
+
248
+ alias length size
249
+ end
250
+ end
251
+
252
+ require 'win32/pipe/server'
253
+ require 'win32/pipe/client'
@@ -0,0 +1,49 @@
1
+ # The Win32 module serves as a namespace only
2
+ module Win32
3
+ # The Pipe::Client class encapsulates the client side of a named pipe
4
+ # connection.
5
+ #
6
+ class Pipe::Client < Pipe
7
+ # Create and return a new Pipe::Client instance.
8
+ #
9
+ # The default pipe mode is PIPE_WAIT.
10
+ #
11
+ # The default open mode is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH.
12
+ #--
13
+ # 2147483776 is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH
14
+ def initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE)
15
+ super(name, pipe_mode, open_mode)
16
+
17
+ @pipe = CreateFile(
18
+ @name,
19
+ GENERIC_READ | GENERIC_WRITE,
20
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
21
+ nil,
22
+ OPEN_EXISTING,
23
+ @open_mode,
24
+ nil
25
+ )
26
+
27
+ error = GetLastError()
28
+
29
+ if error == ERROR_PIPE_BUSY
30
+ unless WaitNamedPipe(@name, NMPWAIT_WAIT_FOREVER)
31
+ raise Error, get_last_error
32
+ end
33
+ end
34
+
35
+ if @pipe == INVALID_HANDLE_VALUE
36
+ raise Error, get_last_error
37
+ end
38
+
39
+ if block_given?
40
+ begin
41
+ yield self
42
+ ensure
43
+ disconnect
44
+ close
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,96 @@
1
+ # The Win32 module serves as a namespace only.
2
+ module Win32
3
+ # The Pipe::Server class encapsulates the server side of a named pipe
4
+ # connection.
5
+ class Pipe::Server < Pipe
6
+
7
+ # Creates and returns a new Pipe::Server instance, using +name+ as the
8
+ # name for the pipe. Note that this does not actually connect the pipe.
9
+ # Use Pipe::Server#connect for that.
10
+ #
11
+ # The default pipe_mode is PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT.
12
+ #
13
+ # The default open_mode is Pipe::ACCESS_DUPLEX.
14
+ #--
15
+ # The default pipe_mode also happens to be 0.
16
+ #
17
+ def initialize(name, pipe_mode = 0, open_mode = Pipe::ACCESS_DUPLEX)
18
+ super(name, pipe_mode, open_mode)
19
+
20
+ @pipe = CreateNamedPipe(
21
+ @name,
22
+ @open_mode,
23
+ @pipe_mode,
24
+ PIPE_UNLIMITED_INSTANCES,
25
+ PIPE_BUFFER_SIZE,
26
+ PIPE_BUFFER_SIZE,
27
+ PIPE_TIMEOUT,
28
+ 0
29
+ )
30
+
31
+ if @pipe == INVALID_HANDLE_VALUE
32
+ raise Error, get_last_error
33
+ end
34
+
35
+ if block_given?
36
+ begin
37
+ yield self
38
+ ensure
39
+ close
40
+ end
41
+ end
42
+ end
43
+
44
+ # Enables the named pipe server process to wait for a client process
45
+ # to connect to an instance of a named pipe. In other words, it puts
46
+ # the server in 'connection wait' status.
47
+ #
48
+ # In synchronous mode always returns true on success. In asynchronous
49
+ # mode returns true if there is pending IO, or false otherwise.
50
+ #
51
+ def connect
52
+ if @asynchronous
53
+ # An overlapped ConnectNamedPipe should return 0
54
+ if ConnectNamedPipe(@pipe, @overlapped)
55
+ raise Error, get_last_error
56
+ end
57
+
58
+ error = GetLastError()
59
+
60
+ case error
61
+ when ERROR_IO_PENDING
62
+ @pending_io = true
63
+ when ERROR_PIPE_CONNECTED
64
+ unless SetEvent(@event)
65
+ raise Error, get_last_error(error)
66
+ end
67
+ when ERROR_PIPE_LISTENING
68
+ # Do nothing
69
+ else
70
+ raise Error, get_last_error(error)
71
+ end
72
+
73
+ if @pending_io
74
+ return false
75
+ else
76
+ return true
77
+ end
78
+ else
79
+ unless ConnectNamedPipe(@pipe, nil)
80
+ raise Error, get_last_error
81
+ end
82
+ end
83
+
84
+ return true
85
+ end
86
+
87
+ # Close the server. This will flush file buffers, disconnect the
88
+ # pipe, and close the pipe handle.
89
+ #
90
+ def close
91
+ FlushFileBuffers(@pipe)
92
+ DisconnectNamedPipe(@pipe)
93
+ super
94
+ end
95
+ end
96
+ end
data/test/tc_pipe.rb ADDED
@@ -0,0 +1,122 @@
1
+ ##########################################################################
2
+ # tc_pipe.rb
3
+ #
4
+ # Test suite for the win32-pipe library. This test suite should be run
5
+ # via the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = Pipe.new('foo')
14
+ end
15
+
16
+ def test_version
17
+ assert_equal('0.2.0', Pipe::VERSION)
18
+ end
19
+
20
+ def test_name
21
+ assert_respond_to(@pipe, :name)
22
+ assert_nothing_raised{ @pipe.name }
23
+ assert_equal("\\\\.\\pipe\\foo", @pipe.name)
24
+ end
25
+
26
+ def test_pipe_mode
27
+ assert_respond_to(@pipe, :pipe_mode)
28
+ assert_nothing_raised{ @pipe.pipe_mode }
29
+ assert_equal(Pipe::DEFAULT_PIPE_MODE, @pipe.pipe_mode)
30
+ end
31
+
32
+ def test_open_mode
33
+ assert_respond_to(@pipe, :open_mode)
34
+ assert_nothing_raised{ @pipe.open_mode }
35
+ assert_equal(Pipe::DEFAULT_OPEN_MODE, @pipe.open_mode)
36
+ end
37
+
38
+ def test_buffer
39
+ assert_respond_to(@pipe, :buffer)
40
+ assert_nothing_raised{ @pipe.buffer }
41
+ end
42
+
43
+ def test_size
44
+ assert_respond_to(@pipe, :size)
45
+ assert_nothing_raised{ @pipe.size }
46
+ end
47
+
48
+ def test_length_alias
49
+ assert_respond_to(@pipe, :length)
50
+ assert_equal(true, @pipe.method(:length) == @pipe.method(:size))
51
+ end
52
+
53
+ def test_pending
54
+ assert_respond_to(@pipe, :pending?)
55
+ assert_nothing_raised{ @pipe.pending? }
56
+ assert_equal(false, @pipe.pending?)
57
+ end
58
+
59
+ def test_asynchronous
60
+ assert_respond_to(@pipe, :asynchronous?)
61
+ assert_nothing_raised{ @pipe.asynchronous? }
62
+ assert_equal(false, @pipe.asynchronous?)
63
+ end
64
+
65
+ def test_read
66
+ assert_respond_to(@pipe, :read)
67
+ assert_raises(Pipe::Error){ @pipe.read } # Nothing to read
68
+ end
69
+
70
+ def test_transferred
71
+ assert_respond_to(@pipe, :transferred)
72
+ assert_nothing_raised{ @pipe.transferred }
73
+ end
74
+
75
+ def test_wait
76
+ assert_respond_to(@pipe, :wait)
77
+ assert_raises(Pipe::Error){ @pipe.wait } # Can't wait in blocking mode
78
+ end
79
+
80
+ def test_write
81
+ assert_respond_to(@pipe, :write)
82
+ assert_raises(ArgumentError){ @pipe.write } # Must have 1 argument
83
+ assert_raises(Pipe::Error){ @pipe.write("foo") } # Nothing to write to
84
+ end
85
+
86
+ def test_disconnect
87
+ assert_respond_to(@pipe, :disconnect)
88
+ assert_nothing_raised{ @pipe.disconnect }
89
+ end
90
+
91
+ def test_close
92
+ assert_respond_to(@pipe, :close)
93
+ assert_nothing_raised{ @pipe.close }
94
+ end
95
+
96
+ def test_pipe_mode_constants
97
+ assert_not_nil(Pipe::WAIT)
98
+ assert_not_nil(Pipe::NOWAIT)
99
+ assert_not_nil(Pipe::TYPE_BYTE)
100
+ assert_not_nil(Pipe::TYPE_MESSAGE)
101
+ assert_not_nil(Pipe::READMODE_BYTE)
102
+ assert_not_nil(Pipe::READMODE_MESSAGE)
103
+ end
104
+
105
+ def test_open_mode_constants
106
+ assert_not_nil(Pipe::ACCESS_DUPLEX)
107
+ assert_not_nil(Pipe::ACCESS_INBOUND)
108
+ assert_not_nil(Pipe::ACCESS_OUTBOUND)
109
+ assert_not_nil(Pipe::FIRST_PIPE_INSTANCE)
110
+ assert_not_nil(Pipe::WRITE_THROUGH)
111
+ assert_not_nil(Pipe::OVERLAPPED)
112
+ end
113
+
114
+ def test_other_constants
115
+ assert_not_nil(Pipe::INFINITE)
116
+ end
117
+
118
+ def teardown
119
+ @pipe.close
120
+ @pipe = nil
121
+ end
122
+ end
@@ -0,0 +1,28 @@
1
+ ##########################################################################
2
+ # tc_pipe_client.rb
3
+ #
4
+ # Test suite for the Pipe::Client class. This test suite should be run
5
+ # as part of the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe_Client < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = nil
14
+ end
15
+
16
+ def test_constructor_basic
17
+ assert_respond_to(Pipe::Client, :new)
18
+ end
19
+
20
+ def test_constructor_expected_errors
21
+ assert_raise(ArgumentError){ Pipe::Client.new }
22
+ assert_raise(TypeError){ Pipe::Client.new(1) }
23
+ end
24
+
25
+ def teardown
26
+ @pipe = nil
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ ##########################################################################
2
+ # tc_pipe_server.rb
3
+ #
4
+ # Test suite for the Pipe::Server class. This test suite should be run
5
+ # as part of the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe_Server < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = nil
14
+ end
15
+
16
+ def test_constructor_basic
17
+ assert_respond_to(Pipe::Server, :new)
18
+ assert_nothing_raised{ Pipe::Server.new('foo') }
19
+ end
20
+
21
+ def test_connect
22
+ assert_nothing_raised{ @pipe = Pipe::Server.new('foo') }
23
+ assert_respond_to(@pipe, :connect)
24
+ end
25
+
26
+ def test_constructor_expected_errors
27
+ assert_raise(ArgumentError){ Pipe::Server.new }
28
+ assert_raise(TypeError){ Pipe::Server.new(1) }
29
+ end
30
+
31
+ def teardown
32
+ @pipe = nil
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = "win32-pipe"
5
+ gem.version = "0.2.0"
6
+ gem.author = "Daniel J. Berger"
7
+ gem.email = "djberg96@gmail.com"
8
+ gem.homepage = "http://www.rubyforge.org/projects/win32utils"
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = "An interface for named pipes on MS Windows"
11
+ gem.description = "An interface for named pipes on MS Windows"
12
+ gem.test_files = Dir["test/tc_*.rb"]
13
+ gem.has_rdoc = true
14
+ gem.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST']
15
+ gem.rubyforge_project = "win32utils"
16
+
17
+ files = Dir["doc/*"] + Dir["examples/*"] + Dir["lib/win32/**/*.rb"]
18
+ files += Dir["test/*"] + Dir["[A-Z]*"]
19
+ files.delete_if{ |item| item.include?("CVS") }
20
+ gem.files = files
21
+ end
22
+
23
+ if $0 == __FILE__
24
+ Gem.manage_gems
25
+ Gem::Builder.new(spec).build
26
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: win32-pipe
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.0
7
+ date: 2008-05-28 00:00:00 -06:00
8
+ summary: An interface for named pipes on MS Windows
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/win32utils
13
+ rubyforge_project: win32utils
14
+ description: An interface for named pipes on MS Windows
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - examples/test_client.rb
33
+ - examples/test_client_async.rb
34
+ - examples/test_server.rb
35
+ - examples/test_server_async.rb
36
+ - lib/win32/pipe/client.rb
37
+ - lib/win32/pipe/server.rb
38
+ - lib/win32/pipe.rb
39
+ - test/tc_pipe.rb
40
+ - test/tc_pipe_client.rb
41
+ - test/tc_pipe_server.rb
42
+ - CHANGES
43
+ - examples
44
+ - ext
45
+ - lib
46
+ - MANIFEST
47
+ - Rakefile
48
+ - README
49
+ - test
50
+ - win32-pipe.gemspec
51
+ test_files:
52
+ - test/tc_pipe.rb
53
+ - test/tc_pipe_client.rb
54
+ - test/tc_pipe_server.rb
55
+ rdoc_options: []
56
+
57
+ extra_rdoc_files:
58
+ - CHANGES
59
+ - README
60
+ - MANIFEST
61
+ executables: []
62
+
63
+ extensions: []
64
+
65
+ requirements: []
66
+
67
+ dependencies: []
68
+