ione 1.2.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b16fd5d35a6cd0dd75c20ea9a3febd681f5a37b
4
- data.tar.gz: 5aff84c71ae8a849858789063b528ed59b45c87c
3
+ metadata.gz: e616ec4a5f533a25efb171aa4e6ede4b1ca5de6f
4
+ data.tar.gz: 433284b5a259cb2db15f8ac95882d13d1e63507a
5
5
  SHA512:
6
- metadata.gz: b6e57d6dc85d7b329353e8d7ff2cb8ba606ff2431377fb7f1e7ca1badf6e0b1ccc7e3971d03cf28387798b307c85cbb32de4c0dbc2851664e096447b190b7215
7
- data.tar.gz: c6825f4358471a8c344f39f3473dc6c11d8f3367886d25953de255c84ebf53f214098f3deeb46471a323ef1a5b628c72e271a910d6930db5d146bba885ba29e6
6
+ metadata.gz: f80b6f25ac0192d10eac99cc8e2dd15b373944bb89e3125f378ff17df4a7a07caf0f35955e5772b322911560a8dff3cc42704c4c25dc67c952cd8c15269e5c8b
7
+ data.tar.gz: 885562d9eb7bb0cd1e6499a0b975fb2d82dee2298918fef4887ca66b5b9d4959c4bdacdd8c2aa2deb2a8e0f44187017dcc44dd9b801a5432bf0888caa720cac9
data/README.md CHANGED
@@ -27,7 +27,7 @@ Networking usually means pushing lots of bytes around and in Ruby it's easy to m
27
27
  The [examples](https://github.com/iconara/ione/tree/master/examples) directory has some examples of what you can do with Ione, for example:
28
28
 
29
29
  * [redis_client](https://github.com/iconara/ione/tree/master/examples/redis_client) is a more or less full featured Redis client that uses most of Ione's features.
30
- * [http_client](https://github.com/iconara/ione/tree/master/examples/http_client) is a simplistic HTTP client that uses Ione and [http_parser.rb](http://rubygems.org/gems/http_parser.rb) to make HTTP GET request.
30
+ * [http_client](https://github.com/iconara/ione/tree/master/examples/http_client) is a simplistic HTTP client that uses Ione and [http_parser.rb](http://rubygems.org/gems/http_parser.rb) to make HTTP GET request. It also shows how to make TLS connections.
31
31
  * [cql-rb](https://github.com/iconara/cql-rb) is a high performance Cassandra driver and where Ione was originally developed.
32
32
  * [cassandra-driver](https://github.com/datastax/ruby-driver) is the successor to cql-rb.
33
33
  * [ione-rpc](https://github.com/iconara/ione-rpc) is a RPC framework built on Ione. It makes it reasonably easy to build networked applications without having to reinvent the wheel.
@@ -14,6 +14,8 @@ module Ione
14
14
  # buffer and one is the write buffer. Writes go to the write buffer only,
15
15
  # and reads read from the read buffer until it is empty, then a new write
16
16
  # buffer is created and the old write buffer becomes the new read buffer.
17
+ #
18
+ # @since v1.0.0
17
19
  class ByteBuffer
18
20
  def initialize(initial_bytes=nil)
19
21
  @read_buffer = ''
data/lib/ione/future.rb CHANGED
@@ -11,6 +11,7 @@ module Ione
11
11
  # A promise is the write end of a Promise/Future pair. It can be fulfilled
12
12
  # with a value or failed with an error. The value can be read through the
13
13
  # future returned by {#future}.
14
+ # @since v1.0.0
14
15
  class Promise
15
16
  attr_reader :future
16
17
 
@@ -104,7 +105,7 @@ module Ione
104
105
  #
105
106
  # @example Creating a future for a blocking operation
106
107
  # def find_my_ip
107
- # promise = Promse.new
108
+ # promise = Promise.new
108
109
  # Thread.start do
109
110
  # begin
110
111
  # data = JSON.load(open('http://jsonip.org/').read)
@@ -178,7 +179,13 @@ module Ione
178
179
  # @see Ione::Future::FutureCallbacks
179
180
  # @see Ione::Future::FutureCombinators
180
181
  # @see Ione::Future::FutureFactories
182
+ # @since v1.0.0
181
183
  class Future
184
+ PENDING_STATE = 0
185
+ RESOLVED_STATE = 1
186
+ FAILED_STATE = 2
187
+
188
+ # @since v1.0.0
182
189
  module Factories
183
190
  # Combines multiple futures into a new future which resolves when all
184
191
  # constituent futures complete, or fails when one or more of them fails.
@@ -207,6 +214,33 @@ module Ione
207
214
  end
208
215
  end
209
216
 
217
+ # Combines multiple futures into a new future which resolves when all
218
+ # constituent futures complete, or fails when one or more of them fails.
219
+ #
220
+ # The resulting future has no value.
221
+ #
222
+ # @example
223
+ # future = Future.after(delete_thing(1), delete_thing(2))
224
+ # future.value # => nil
225
+ #
226
+ # @param [Array<Ione::Future>] futures the futures to combine (this argument
227
+ # can be a splatted array or a regular array passed as sole argument)
228
+ # @return [Ione::Future] with a nil value once all futures have succeeded
229
+ # @since v1.3.0
230
+ def after(*futures)
231
+ if futures.size == 1 && (fs = futures.first).is_a?(Enumerable)
232
+ *futures = *fs
233
+ end
234
+ if futures.count == 0
235
+ ResolvedFuture::NIL
236
+ elsif futures.count == 1
237
+ futures.first.map(nil)
238
+ else
239
+ CombinedNilFuture.new(futures)
240
+ end
241
+ end
242
+
243
+
210
244
  # Returns a future which will be resolved with the value of the first
211
245
  # (resolved) of the specified futures. If all of the futures fail, the
212
246
  # returned future will also fail (with the error of the last failed future).
@@ -249,6 +283,7 @@ module Ione
249
283
  # @yieldreturn [Ione::Future] a future
250
284
  # @return [Ione::Future] a future that will resolve to an array of the values
251
285
  # of the futures returned by the block
286
+ # @since v1.2.0
252
287
  def traverse(values, &block)
253
288
  all(values.map(&block))
254
289
  rescue => e
@@ -286,7 +321,8 @@ module Ione
286
321
  #
287
322
  # @param [Array<Ione::Future>] futures an array of futures whose values
288
323
  # should be reduced
289
- # @param [Object] initial_value the initial value of the accumulator
324
+ # @param [Object] initial_value the initial value of the accumulator. When
325
+ # nil (the default) the value of the first future will be used instead.
290
326
  # @param [Hash] options
291
327
  # @option options [Boolean] :ordered (true) whether or not to respect the
292
328
  # order of the input when reducing – when true the block will be called
@@ -302,6 +338,7 @@ module Ione
302
338
  # @return [Ione::Future] a future that will resolve to the value returned
303
339
  # from the last invocation of the block, or nil when the list of futures
304
340
  # is empty.
341
+ # @since v1.2.0
305
342
  def reduce(futures, initial_value=nil, options=nil, &reducer)
306
343
  if options && options[:ordered] == false
307
344
  UnorderedReducingFuture.new(futures, initial_value, reducer)
@@ -328,6 +365,7 @@ module Ione
328
365
  end
329
366
  end
330
367
 
368
+ # @since v1.0.0
331
369
  module Combinators
332
370
  # Returns a new future representing a transformation of this future's value.
333
371
  #
@@ -411,6 +449,7 @@ module Ione
411
449
  # @yieldreturn [Object, Ione::Future] the transformed value, or a future
412
450
  # that will resolve to the transformed value.
413
451
  # @return [Ione::Future] a new future representing the transformed value
452
+ # @since v1.2.0
414
453
  def then(&block)
415
454
  f = CompletableFuture.new
416
455
  on_complete do |v, e|
@@ -519,6 +558,7 @@ module Ione
519
558
  end
520
559
  end
521
560
 
561
+ # @since v1.0.0
522
562
  module Callbacks
523
563
  # Registers a listener that will be called when this future becomes
524
564
  # resolved. The listener will be called with the value of the future as
@@ -549,9 +589,10 @@ module Ione
549
589
  include Combinators
550
590
  include Callbacks
551
591
 
592
+ # @private
552
593
  def initialize
553
594
  @lock = Mutex.new
554
- @state = :pending
595
+ @state = PENDING_STATE
555
596
  @listeners = []
556
597
  end
557
598
 
@@ -595,12 +636,12 @@ module Ione
595
636
  # @see Callbacks#on_failure
596
637
  def on_complete(&listener)
597
638
  run_immediately = false
598
- if @state != :pending
639
+ if @state != PENDING_STATE
599
640
  run_immediately = true
600
641
  else
601
642
  @lock.lock
602
643
  begin
603
- if @state == :pending
644
+ if @state == PENDING_STATE
604
645
  @listeners << listener
605
646
  else
606
647
  run_immediately = true
@@ -634,13 +675,13 @@ module Ione
634
675
  # @see Callbacks#on_failure
635
676
  # @see Callbacks#on_complete
636
677
  def value
637
- raise @error if @state == :failed
638
- return @value if @state == :resolved
678
+ raise @error if @state == FAILED_STATE
679
+ return @value if @state == RESOLVED_STATE
639
680
  semaphore = nil
640
681
  @lock.lock
641
682
  begin
642
- raise @error if @state == :failed
643
- return @value if @state == :resolved
683
+ raise @error if @state == FAILED_STATE
684
+ return @value if @state == RESOLVED_STATE
644
685
  semaphore = Queue.new
645
686
  u = proc { semaphore << :unblock }
646
687
  @listeners << u
@@ -650,8 +691,8 @@ module Ione
650
691
  while true
651
692
  @lock.lock
652
693
  begin
653
- raise @error if @state == :failed
654
- return @value if @state == :resolved
694
+ raise @error if @state == FAILED_STATE
695
+ return @value if @state == RESOLVED_STATE
655
696
  ensure
656
697
  @lock.unlock
657
698
  end
@@ -662,10 +703,10 @@ module Ione
662
703
 
663
704
  # Returns true if this future is resolved or failed
664
705
  def completed?
665
- return true unless @state == :pending
706
+ return true unless @state == PENDING_STATE
666
707
  @lock.lock
667
708
  begin
668
- @state != :pending
709
+ @state != PENDING_STATE
669
710
  ensure
670
711
  @lock.unlock
671
712
  end
@@ -673,10 +714,10 @@ module Ione
673
714
 
674
715
  # Returns true if this future is resolved
675
716
  def resolved?
676
- return @state == :resolved unless @state == :pending
717
+ return @state == RESOLVED_STATE unless @state == PENDING_STATE
677
718
  @lock.lock
678
719
  begin
679
- @state == :resolved
720
+ @state == RESOLVED_STATE
680
721
  ensure
681
722
  @lock.unlock
682
723
  end
@@ -684,10 +725,10 @@ module Ione
684
725
 
685
726
  # Returns true if this future has failed
686
727
  def failed?
687
- return @state == :failed unless @state == :pending
728
+ return @state == FAILED_STATE unless @state == PENDING_STATE
688
729
  @lock.lock
689
730
  begin
690
- @state == :failed
731
+ @state == FAILED_STATE
691
732
  ensure
692
733
  @lock.unlock
693
734
  end
@@ -731,9 +772,9 @@ module Ione
731
772
  listeners = nil
732
773
  @lock.lock
733
774
  begin
734
- raise FutureError, 'Future already completed' unless @state == :pending
775
+ raise FutureError, 'Future already completed' unless @state == PENDING_STATE
735
776
  @value = v
736
- @state = :resolved
777
+ @state = RESOLVED_STATE
737
778
  listeners = @listeners
738
779
  @listeners = nil
739
780
  ensure
@@ -749,9 +790,9 @@ module Ione
749
790
  listeners = nil
750
791
  @lock.lock
751
792
  begin
752
- raise FutureError, 'Future already completed' unless @state == :pending
793
+ raise FutureError, 'Future already completed' unless @state == PENDING_STATE
753
794
  @error = error
754
- @state = :failed
795
+ @state = FAILED_STATE
755
796
  listeners = @listeners
756
797
  @listeners = nil
757
798
  ensure
@@ -793,14 +834,37 @@ module Ione
793
834
  end
794
835
  end
795
836
 
837
+ # @private
838
+ class CombinedNilFuture < CompletableFuture
839
+ def initialize(futures)
840
+ super()
841
+ @futures = futures
842
+ await_next(nil, nil)
843
+ end
844
+
845
+ def await_next(v, e)
846
+ if e
847
+ @futures = nil
848
+ fail(e)
849
+ elsif @futures.empty?
850
+ @futures = nil
851
+ resolve
852
+ else
853
+ @futures.pop.on_complete(&method(:await_next))
854
+ end
855
+ end
856
+ end
857
+
796
858
  # @private
797
859
  class ReducingFuture < CompletableFuture
860
+ NO_INITIAL_VALUE = Object.new
861
+
798
862
  def initialize(futures, initial_value, reducer)
799
863
  super()
800
864
  @futures = Array(futures)
801
865
  @remaining = @futures.size
802
866
  @initial_value = initial_value
803
- @accumulator = initial_value
867
+ @accumulator = initial_value.nil? ? NO_INITIAL_VALUE : initial_value
804
868
  @reducer = reducer
805
869
  end
806
870
 
@@ -810,10 +874,10 @@ module Ione
810
874
  unless failed?
811
875
  @lock.lock
812
876
  begin
813
- if @accumulator
814
- @accumulator = @reducer.call(@accumulator, value)
815
- else
877
+ if @accumulator.equal?(NO_INITIAL_VALUE)
816
878
  @accumulator = value
879
+ else
880
+ @accumulator = @reducer.call(@accumulator, value)
817
881
  end
818
882
  @remaining -= 1
819
883
  rescue => e
@@ -905,7 +969,7 @@ module Ione
905
969
  # @private
906
970
  class ResolvedFuture < Future
907
971
  def initialize(value=nil)
908
- @state = :resolved
972
+ @state = RESOLVED_STATE
909
973
  @value = value
910
974
  @error = nil
911
975
  end
@@ -943,7 +1007,7 @@ module Ione
943
1007
  # @private
944
1008
  class FailedFuture < Future
945
1009
  def initialize(error)
946
- @state = :failed
1010
+ @state = FAILED_STATE
947
1011
  @value = nil
948
1012
  @error = error
949
1013
  end
@@ -3,11 +3,19 @@
3
3
 
4
4
  module Ione
5
5
  module Io
6
+ # An acceptor wraps a server socket and accepts client connections.
7
+ # @since v1.1.0
6
8
  class Acceptor
9
+ # @private
7
10
  ServerSocket = RUBY_ENGINE == 'jruby' ? ::ServerSocket : Socket
8
11
 
12
+ BINDING_STATE = 0
13
+ CONNECTED_STATE = 1
14
+ CLOSED_STATE = 2
15
+
9
16
  attr_reader :backlog
10
17
 
18
+ # @private
11
19
  def initialize(host, port, backlog, unblocker, reactor, socket_impl=nil)
12
20
  @host = host
13
21
  @port = port
@@ -17,15 +25,19 @@ module Ione
17
25
  @socket_impl = socket_impl || ServerSocket
18
26
  @accept_listeners = []
19
27
  @lock = Mutex.new
20
- @state = :binding
28
+ @state = BINDING_STATE
21
29
  end
22
30
 
31
+ # Register a listener to be notified when client connections are accepted
32
+ #
33
+ # @yieldparam [Ione::Io::ServerConnection] the connection to the client
23
34
  def on_accept(&listener)
24
35
  @lock.synchronize do
25
36
  @accept_listeners << listener
26
37
  end
27
38
  end
28
39
 
40
+ # @private
29
41
  def bind
30
42
  addrinfos = @socket_impl.getaddrinfo(@host, @port, nil, Socket::SOCK_STREAM)
31
43
  begin
@@ -39,17 +51,18 @@ module Ione
39
51
  retry
40
52
  end
41
53
  end
42
- @state = :connected
54
+ @state = CONNECTED_STATE
43
55
  Future.resolved(self)
44
56
  rescue => e
45
57
  close
46
58
  Future.failed(e)
47
59
  end
48
60
 
61
+ # Stop accepting connections
49
62
  def close
50
63
  @lock.synchronize do
51
- return false if @state == :closed
52
- @state = :closed
64
+ return false if @state == CLOSED_STATE
65
+ @state = CLOSED_STATE
53
66
  end
54
67
  if @io
55
68
  begin
@@ -63,26 +76,32 @@ module Ione
63
76
  true
64
77
  end
65
78
 
79
+ # @private
66
80
  def to_io
67
81
  @io
68
82
  end
69
83
 
84
+ # Returns true if the acceptor has stopped accepting connections
70
85
  def closed?
71
- @state == :closed
86
+ @state == CLOSED_STATE
72
87
  end
73
88
 
89
+ # Returns true if the acceptor is accepting connections
74
90
  def connected?
75
- @state != :closed
91
+ @state != CLOSED_STATE
76
92
  end
77
93
 
94
+ # @private
78
95
  def connecting?
79
96
  false
80
97
  end
81
98
 
99
+ # @private
82
100
  def writable?
83
101
  false
84
102
  end
85
103
 
104
+ # @private
86
105
  def read
87
106
  client_socket, host, port = accept
88
107
  connection = ServerConnection.new(client_socket, host, port, @unblocker)
@@ -91,10 +110,12 @@ module Ione
91
110
  end
92
111
 
93
112
  if RUBY_ENGINE == 'jruby'
113
+ # @private
94
114
  def bind_socket(socket, addr, backlog)
95
115
  socket.bind(addr, backlog)
96
116
  end
97
117
  else
118
+ # @private
98
119
  def bind_socket(socket, addr, backlog)
99
120
  socket.bind(addr)
100
121
  socket.listen(backlog)
@@ -2,14 +2,21 @@
2
2
 
3
3
  module Ione
4
4
  module Io
5
+ # @since v1.0.0
5
6
  class BaseConnection
7
+ CONNECTING_STATE = 0
8
+ CONNECTED_STATE = 1
9
+ DRAINING_STATE = 2
10
+ CLOSED_STATE = 3
11
+
6
12
  attr_reader :host, :port
7
13
 
14
+ # @private
8
15
  def initialize(host, port, unblocker)
9
16
  @host = host
10
17
  @port = port
11
18
  @unblocker = unblocker
12
- @state = :connecting
19
+ @state = CONNECTING_STATE
13
20
  @writable = false
14
21
  @lock = Mutex.new
15
22
  @write_buffer = ByteBuffer.new
@@ -21,8 +28,8 @@ module Ione
21
28
  # @return [true, false] returns false if the connection was already closed
22
29
  def close(cause=nil)
23
30
  @lock.synchronize do
24
- return false if @state == :closed
25
- @state = :closed
31
+ return false if @state == CLOSED_STATE
32
+ @state = CLOSED_STATE
26
33
  @writable = false
27
34
  end
28
35
  if @io
@@ -50,30 +57,31 @@ module Ione
50
57
  #
51
58
  # @return [Ione::Future] a future that resolves to the connection when it
52
59
  # has closed
60
+ # @since v1.1.0
53
61
  def drain
54
- @state = :draining
62
+ @state = DRAINING_STATE
55
63
  close unless @writable
56
64
  @closed_promise.future
57
65
  end
58
66
 
59
67
  # @private
60
68
  def connecting?
61
- @state == :connecting
69
+ @state == CONNECTING_STATE
62
70
  end
63
71
 
64
72
  # Returns true if the connection is connected
65
73
  def connected?
66
- @state == :connected
74
+ @state == CONNECTED_STATE
67
75
  end
68
76
 
69
77
  # Returns true if the connection is closed
70
78
  def closed?
71
- @state == :closed
79
+ @state == CLOSED_STATE
72
80
  end
73
81
 
74
82
  # @private
75
83
  def writable?
76
- @writable && @state != :closed
84
+ @writable && @state != CLOSED_STATE
77
85
  end
78
86
 
79
87
  # Register to receive notifications when new data is read from the socket.
@@ -119,7 +127,7 @@ module Ione
119
127
  # @yieldparam buffer [Ione::ByteBuffer] the connection's internal buffer
120
128
  # @param bytes [String, Ione::ByteBuffer] the data to write to the socket
121
129
  def write(bytes=nil)
122
- if @state == :connected || @state == :connecting
130
+ if @state == CONNECTED_STATE || @state == CONNECTING_STATE
123
131
  @lock.lock
124
132
  begin
125
133
  if block_given?
@@ -137,7 +145,7 @@ module Ione
137
145
 
138
146
  # @private
139
147
  def flush
140
- if @state == :connected || @state == :draining
148
+ if @state == CONNECTED_STATE || @state == DRAINING_STATE
141
149
  @lock.lock
142
150
  begin
143
151
  if @writable
@@ -145,7 +153,7 @@ module Ione
145
153
  @write_buffer.discard(bytes_written)
146
154
  end
147
155
  @writable = !@write_buffer.empty?
148
- if @state == :draining && !@writable
156
+ if @state == DRAINING_STATE && !@writable
149
157
  close
150
158
  end
151
159
  ensure
@@ -158,7 +166,7 @@ module Ione
158
166
 
159
167
  # @private
160
168
  def read
161
- new_data = @io.read_nonblock(2**16)
169
+ new_data = @io.read_nonblock(65536)
162
170
  @data_listener.call(new_data) if @data_listener
163
171
  rescue => e
164
172
  close(e)
@@ -170,7 +178,11 @@ module Ione
170
178
  end
171
179
 
172
180
  def to_s
173
- %(#<#{self.class.name} #{@state} #{@host}:#{@port}>)
181
+ state_constant_name = self.class.constants.find do |name|
182
+ name.to_s.end_with?('_STATE') && self.class.const_get(name) == @state
183
+ end
184
+ state = state_constant_name.to_s.rpartition('_').first
185
+ %(#<#{self.class.name} #{state} #{@host}:#{@port}>)
174
186
  end
175
187
  end
176
188
  end