em-ruby-sockets 0.0.1

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.
@@ -0,0 +1,28 @@
1
+ require "eventmachine"
2
+ require "kgio"
3
+ require "iobuffer"
4
+
5
+ require "em-ruby-sockets/version"
6
+ require "em-ruby-sockets/errors"
7
+ require "em-ruby-sockets/tcp_connection"
8
+ require "em-ruby-sockets/tcp_client"
9
+ require "em-ruby-sockets/file_streamer"
10
+
11
+
12
+
13
+ module EventMachine::RubySockets
14
+
15
+ def self.tcp_connect ip, port, klass=EM::RubySockets::TcpClient
16
+ raise EM::RubySockets::Error, "EventMachine is not running" unless EM.reactor_running?
17
+
18
+ sock = ::Kgio::TCPSocket.start(ip, port)
19
+
20
+ conn = EM.watch sock, klass
21
+ conn.post_init
22
+ conn.send :init
23
+
24
+ block_given? and yield conn
25
+ conn
26
+ end
27
+
28
+ end
@@ -0,0 +1,3 @@
1
+ module EventMachine::RubySockets
2
+ class Error < ::StandardError ; end
3
+ end
@@ -0,0 +1,223 @@
1
+ module EventMachine::RubySockets
2
+
3
+ class TcpClient < ::EM::Connection
4
+
5
+ include TcpConnection
6
+
7
+ def init
8
+ self.notify_writable = true
9
+ self.notify_readable = true
10
+ @buffer = ::IO::Buffer.new
11
+ @connected = false
12
+ @error = false
13
+ end
14
+ private :init
15
+
16
+ def notify_writable
17
+ return if @error
18
+ self.notify_writable = false
19
+
20
+ unless @connected
21
+ begin
22
+ case @io.kgio_trywrite("")
23
+ when nil
24
+ @connected = true
25
+ on_connected()
26
+ end
27
+
28
+ rescue => e
29
+ connection_terminated e
30
+ return
31
+ end
32
+ end
33
+
34
+ try_send_data nil unless @error or @buffer.empty?
35
+ end
36
+ private :notify_writable
37
+
38
+ def notify_readable
39
+ return if @error
40
+
41
+ case buf = @io.kgio_tryread(4096)
42
+ # Reads at most maxlen bytes from the stream socket.
43
+ when ::String
44
+ receive_data buf
45
+ # Returns nil on EOF.
46
+ when nil
47
+ connection_terminated :remote_close
48
+ end
49
+
50
+ rescue => e
51
+ connection_terminated e
52
+ end
53
+ private :notify_readable
54
+
55
+ def try_send_data data
56
+ if data
57
+ if @buffer.empty?
58
+ @buffer << data
59
+ # If the buffer is not empty then there is a pending EM tick to send it,
60
+ # so don't add a new one and just append new data to the buffer.
61
+ else
62
+ @buffer << data
63
+ return
64
+ end
65
+ end
66
+
67
+ # Don't send the data now but in next EM iteration.
68
+ EM.next_tick do
69
+ unless @error
70
+ begin
71
+ case buf = @io.kgio_trywrite(@buffer.read)
72
+ # Returns nil if the write was completed in full.
73
+ when nil
74
+ @buffer.clear
75
+ self.notify_writable = false if self.notify_writable?
76
+ # Returns :wait_writable if EAGAIN is encountered and nothing
77
+ # was written. It could occur *after* the connection is
78
+ # established.
79
+ when :wait_writable
80
+ self.notify_writable = true unless self.notify_writable?
81
+ # Returns a String containing the unwritten portion if EAGAIN was
82
+ # encountered, but some portion was successfully written.
83
+ when ::String
84
+ @buffer << buf
85
+ self.notify_writable = true unless self.notify_writable?
86
+ end
87
+
88
+ rescue => e
89
+ connection_terminated e
90
+ end
91
+ end
92
+ end
93
+ end
94
+ private :try_send_data
95
+
96
+ def connection_terminated cause
97
+ return if @error
98
+
99
+ @error = true
100
+ detach
101
+ @io.close unless @io.closed?
102
+ @buffer.clear
103
+ if @connected
104
+ on_disconnected cause
105
+ else
106
+ on_connection_error cause
107
+ end
108
+ end
109
+ private :connection_terminated
110
+
111
+ # Access to the Ruby socket itself. A good place for socket settings is
112
+ # the post_init method.
113
+ def io
114
+ @io
115
+ end
116
+
117
+ # Sends the given _data_ to the remote peer. It can be safely called
118
+ # before the connection is established (the data is then stored within
119
+ # an internal buffer).
120
+ # Returns true if the connection is established or trying to connect.
121
+ # False if the connection has been closed or if an unrecoverable error
122
+ # occurred while writting into the socket.
123
+ #
124
+ # Arguments:
125
+ # - [data] a String.
126
+ def send_data data
127
+ raise ::ArgumentError, "argument must be a String" unless data.is_a?(::String)
128
+ return false if @error
129
+
130
+ if @connected
131
+ try_send_data data
132
+ else
133
+ @buffer << data
134
+ end
135
+ true
136
+ end
137
+
138
+ # Terminates the connection regardless it's been sucessfuly connected
139
+ # or not. The callback on_disconnected will be called with argument
140
+ # _nil_.
141
+ def disconnect
142
+ return if @error
143
+ @error = true
144
+ detach
145
+ @io.close unless @io.closed?
146
+ @buffer.clear
147
+ on_disconnected nil
148
+ end
149
+ alias :close_connection :disconnect
150
+ alias :close_connection_after_writing :disconnect
151
+
152
+ # Sets the duration after which a TCP connection in a connecting state
153
+ # will fail. After the given duration on_connection_error() is called
154
+ # with argument :pending_connect_timeout.
155
+ #
156
+ # Arguments:
157
+ # - [duration] Value in seconds (float).
158
+ def pending_connect_timeout= duration
159
+ return false if @pending_connect_timeout
160
+
161
+ EM.add_timer(@pending_connect_timeout = duration.to_f) do
162
+ unless @error or @connected
163
+ connection_terminated :pending_connect_timeout
164
+ end
165
+ end
166
+ @pending_connect_timeout
167
+ end
168
+ alias :set_pending_connect_timeout :pending_connect_timeout=
169
+
170
+ # Gets the duration after which a TCP connection in a connecting state
171
+ # will fail. Returns nil if not set.
172
+ def pending_connect_timeout
173
+ @pending_connect_timeout
174
+ end
175
+
176
+ # Returns the number of bytes to be sent by the socket.
177
+ def get_outbound_data_size
178
+ @buffer.size
179
+ end
180
+
181
+ # Executed upon instance initialization and before the connection
182
+ # is attempted. Good place for defining custom attributes.
183
+ #
184
+ # This method must be defined in the user's inherinted class.
185
+ def post_init
186
+ end
187
+
188
+ # Called upon connection is established.
189
+ #
190
+ # This method must be defined in the user's inherinted class.
191
+ def on_connected
192
+ end
193
+
194
+ # Called when the connection attemp failed. _cause_ can be:
195
+ # - [:pending_connect_timeout] Connection not established within pending_connect_timeout value.
196
+ # - [exception] An exception.
197
+ #
198
+ # This method must be defined in the user's inherinted class.
199
+ def on_connection_error cause
200
+ end
201
+
202
+ # Called when TCP data is received in the socket.
203
+ #
204
+ # This method must be defined in the user's inherinted class.
205
+ def receive_data data
206
+ end
207
+
208
+ # Called when the connection is terminated by the peer (and it was
209
+ # already established) or when it is terminated locally (by calling
210
+ # disconnect so cause becomes _nil_).
211
+ #
212
+ # _cause_ can be:
213
+ # - [nil] Local disconnection via disconnect method.
214
+ # - [:remote_close] Remote normal close.
215
+ # - [exception] Some other cause.
216
+ #
217
+ # This method must be defined in the user's inherinted class.
218
+ def on_disconnected cause
219
+ end
220
+
221
+ end
222
+
223
+ end
@@ -0,0 +1,48 @@
1
+ #$debug = true
2
+
3
+ module EventMachine::RubySockets
4
+
5
+ module TcpConnection
6
+
7
+ # Access to the Ruby socket itself. A good place for socket settings is
8
+ # the post_init() method.
9
+ def io
10
+ @io
11
+ end
12
+
13
+ # Returns true if the socket has been closed or failed to connect.
14
+ def error?
15
+ @error
16
+ end
17
+
18
+ # Returns the number of bytes to be sent by the socket.
19
+ def get_outbound_data_size
20
+ @buffer.size
21
+ end
22
+
23
+ # Returns an array of [ port, ip ] with the local address used
24
+ # for this socket.
25
+ # It returns nil in case the socket has been closed or failed to connect.
26
+ def local_address
27
+ return nil if @error
28
+ ::Socket.unpack_sockaddr_in @io.getsockname
29
+ end
30
+
31
+ # Returns an array of [ port, ip ] with the remote connected address.
32
+ # It returns nil in case the socket is not connected.
33
+ def remote_address
34
+ return nil unless @connected
35
+ ::Socket.unpack_sockaddr_in @io.getpeername
36
+ end
37
+
38
+ # Streams the given _filename_ through this connection. It does not set callback or
39
+ # errback. For a more powerful usage create a EventMachine::RubySockets::FileStreamer
40
+ # instance.
41
+ def stream_file_data filename, args={}
42
+ EM::RubySockets::FileStreamer.new(self, filename, args).run
43
+ end
44
+ alias :send_file_data :stream_file_data
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ module RubySockets
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-ruby-sockets
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - "I\xC3\xB1aki Baz Castillo"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-04-14 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: kgio
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 7
31
+ - 4
32
+ version: 2.7.4
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: iobuffer
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 1
45
+ - 1
46
+ - 2
47
+ version: 1.1.2
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: em-ruby-sockets uses Ruby sockets within EventMachine::watch()
51
+ email: ibc@aliax.net
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - lib/em-ruby-sockets.rb
60
+ - lib/em-ruby-sockets/version.rb
61
+ - lib/em-ruby-sockets/errors.rb
62
+ - lib/em-ruby-sockets/tcp_connection.rb
63
+ - lib/em-ruby-sockets/tcp_client.rb
64
+ has_rdoc: true
65
+ homepage: https://github.com/ibc/em-ruby-sockets
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 1
80
+ - 8
81
+ - 7
82
+ version: 1.8.7
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ requirements: []
92
+
93
+ rubyforge_project:
94
+ rubygems_version: 1.3.7
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Ruby sockets integrated within EventMachine
98
+ test_files: []
99
+