omq 0.9.0 → 0.10.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/lib/omq/channel.rb +3 -3
  4. data/lib/omq/client_server.rb +6 -6
  5. data/lib/omq/engine.rb +641 -0
  6. data/lib/omq/options.rb +46 -0
  7. data/lib/omq/pair.rb +2 -2
  8. data/lib/omq/peer.rb +3 -3
  9. data/lib/omq/pub_sub.rb +6 -6
  10. data/lib/omq/push_pull.rb +2 -2
  11. data/lib/omq/radio_dish.rb +2 -2
  12. data/lib/omq/reactor.rb +128 -0
  13. data/lib/omq/readable.rb +42 -0
  14. data/lib/omq/req_rep.rb +4 -4
  15. data/lib/omq/router_dealer.rb +4 -4
  16. data/lib/omq/routing/channel.rb +83 -0
  17. data/lib/omq/routing/client.rb +56 -0
  18. data/lib/omq/routing/dealer.rb +57 -0
  19. data/lib/omq/routing/dish.rb +78 -0
  20. data/lib/omq/routing/fan_out.rb +131 -0
  21. data/lib/omq/routing/gather.rb +46 -0
  22. data/lib/omq/routing/pair.rb +86 -0
  23. data/lib/omq/routing/peer.rb +101 -0
  24. data/lib/omq/routing/pub.rb +60 -0
  25. data/lib/omq/routing/pull.rb +46 -0
  26. data/lib/omq/routing/push.rb +81 -0
  27. data/lib/omq/routing/radio.rb +140 -0
  28. data/lib/omq/routing/rep.rb +101 -0
  29. data/lib/omq/routing/req.rb +65 -0
  30. data/lib/omq/routing/round_robin.rb +168 -0
  31. data/lib/omq/routing/router.rb +110 -0
  32. data/lib/omq/routing/scatter.rb +82 -0
  33. data/lib/omq/routing/server.rb +101 -0
  34. data/lib/omq/routing/sub.rb +78 -0
  35. data/lib/omq/routing/xpub.rb +72 -0
  36. data/lib/omq/routing/xsub.rb +83 -0
  37. data/lib/omq/routing.rb +66 -0
  38. data/lib/omq/scatter_gather.rb +4 -4
  39. data/lib/omq/single_frame.rb +18 -0
  40. data/lib/omq/socket.rb +24 -9
  41. data/lib/omq/transport/inproc.rb +355 -0
  42. data/lib/omq/transport/ipc.rb +117 -0
  43. data/lib/omq/transport/tcp.rb +111 -0
  44. data/lib/omq/version.rb +1 -1
  45. data/lib/omq/writable.rb +65 -0
  46. data/lib/omq.rb +60 -4
  47. metadata +32 -33
  48. data/lib/omq/zmtp/engine.rb +0 -551
  49. data/lib/omq/zmtp/options.rb +0 -48
  50. data/lib/omq/zmtp/reactor.rb +0 -131
  51. data/lib/omq/zmtp/readable.rb +0 -29
  52. data/lib/omq/zmtp/routing/channel.rb +0 -81
  53. data/lib/omq/zmtp/routing/client.rb +0 -56
  54. data/lib/omq/zmtp/routing/dealer.rb +0 -57
  55. data/lib/omq/zmtp/routing/dish.rb +0 -80
  56. data/lib/omq/zmtp/routing/fan_out.rb +0 -131
  57. data/lib/omq/zmtp/routing/gather.rb +0 -48
  58. data/lib/omq/zmtp/routing/pair.rb +0 -84
  59. data/lib/omq/zmtp/routing/peer.rb +0 -100
  60. data/lib/omq/zmtp/routing/pub.rb +0 -62
  61. data/lib/omq/zmtp/routing/pull.rb +0 -48
  62. data/lib/omq/zmtp/routing/push.rb +0 -80
  63. data/lib/omq/zmtp/routing/radio.rb +0 -139
  64. data/lib/omq/zmtp/routing/rep.rb +0 -101
  65. data/lib/omq/zmtp/routing/req.rb +0 -65
  66. data/lib/omq/zmtp/routing/round_robin.rb +0 -143
  67. data/lib/omq/zmtp/routing/router.rb +0 -109
  68. data/lib/omq/zmtp/routing/scatter.rb +0 -81
  69. data/lib/omq/zmtp/routing/server.rb +0 -100
  70. data/lib/omq/zmtp/routing/sub.rb +0 -80
  71. data/lib/omq/zmtp/routing/xpub.rb +0 -74
  72. data/lib/omq/zmtp/routing/xsub.rb +0 -86
  73. data/lib/omq/zmtp/routing.rb +0 -65
  74. data/lib/omq/zmtp/single_frame.rb +0 -20
  75. data/lib/omq/zmtp/transport/inproc.rb +0 -359
  76. data/lib/omq/zmtp/transport/ipc.rb +0 -118
  77. data/lib/omq/zmtp/transport/tcp.rb +0 -117
  78. data/lib/omq/zmtp/writable.rb +0 -61
  79. data/lib/omq/zmtp.rb +0 -81
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "io/stream"
5
+
6
+ module OMQ
7
+ module Transport
8
+ # IPC transport using Unix domain sockets.
9
+ #
10
+ # Supports both file-based paths and Linux abstract namespace
11
+ # (paths starting with @).
12
+ #
13
+ module IPC
14
+ class << self
15
+ # Binds an IPC server.
16
+ #
17
+ # @param endpoint [String] e.g. "ipc:///tmp/my.sock" or "ipc://@abstract"
18
+ # @param engine [Engine]
19
+ # @return [Listener]
20
+ #
21
+ def bind(endpoint, engine)
22
+ path = parse_path(endpoint)
23
+ sock_path = to_socket_path(path)
24
+
25
+ # Remove stale socket file for file-based paths
26
+ File.delete(sock_path) if !abstract?(path) && File.exist?(sock_path)
27
+
28
+ server = UNIXServer.new(sock_path)
29
+
30
+ Listener.new(endpoint, server, path)
31
+ end
32
+
33
+ # Connects to an IPC endpoint.
34
+ #
35
+ # @param endpoint [String]
36
+ # @param engine [Engine]
37
+ # @return [void]
38
+ #
39
+ def connect(endpoint, engine)
40
+ path = parse_path(endpoint)
41
+ sock_path = to_socket_path(path)
42
+ sock = UNIXSocket.new(sock_path)
43
+ engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: endpoint)
44
+ end
45
+
46
+ private
47
+
48
+ # Extracts path from "ipc://path".
49
+ #
50
+ def parse_path(endpoint)
51
+ endpoint.sub(%r{\Aipc://}, "")
52
+ end
53
+
54
+ # Converts @ prefix to \0 for abstract namespace.
55
+ #
56
+ def to_socket_path(path)
57
+ if abstract?(path)
58
+ "\0#{path[1..]}"
59
+ else
60
+ path
61
+ end
62
+ end
63
+
64
+ # @return [Boolean] true if abstract namespace path
65
+ #
66
+ def abstract?(path)
67
+ path.start_with?("@")
68
+ end
69
+ end
70
+
71
+ # A bound IPC listener.
72
+ #
73
+ class Listener
74
+ # @return [String] the endpoint
75
+ #
76
+ attr_reader :endpoint
77
+
78
+ # @return [UNIXServer] the server socket
79
+ #
80
+ attr_reader :server
81
+
82
+
83
+ # @param endpoint [String] the IPC endpoint URI
84
+ # @param server [UNIXServer]
85
+ # @param path [String] filesystem or abstract namespace path
86
+ #
87
+ def initialize(endpoint, server, path)
88
+ @endpoint = endpoint
89
+ @server = server
90
+ @path = path
91
+ @task = nil
92
+ end
93
+
94
+
95
+ # Registers the accept loop task owned by the engine.
96
+ #
97
+ # @param task [Async::Task]
98
+ #
99
+ def accept_task=(task)
100
+ @task = task
101
+ end
102
+
103
+
104
+ # Stops the listener.
105
+ #
106
+ def stop
107
+ @task&.stop
108
+ @server.close rescue nil
109
+ # Clean up socket file for file-based paths
110
+ unless @path.start_with?("@")
111
+ File.delete(@path) rescue nil
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "uri"
5
+ require "io/stream"
6
+
7
+ module OMQ
8
+ module Transport
9
+ # TCP transport using Ruby sockets with Async.
10
+ #
11
+ module TCP
12
+ class << self
13
+ # Binds a TCP server.
14
+ #
15
+ # @param endpoint [String] e.g. "tcp://127.0.0.1:5555" or "tcp://*:0"
16
+ # @param engine [Engine]
17
+ # @return [Listener]
18
+ #
19
+ def bind(endpoint, engine)
20
+ host, port = parse_endpoint(endpoint)
21
+ host = "0.0.0.0" if host == "*"
22
+
23
+ addrs = Addrinfo.getaddrinfo(host, port, nil, :STREAM, nil, ::Socket::AI_PASSIVE)
24
+ raise ::Socket::ResolutionError, "no addresses for #{host}" if addrs.empty?
25
+
26
+ servers = []
27
+ actual_port = nil
28
+
29
+ addrs.each do |addr|
30
+ server = TCPServer.new(addr.ip_address, actual_port || port)
31
+ actual_port ||= server.local_address.ip_port
32
+ servers << server
33
+ end
34
+
35
+ host_part = host.include?(":") ? "[#{host}]" : host
36
+ resolved = "tcp://#{host_part}:#{actual_port}"
37
+ Listener.new(resolved, servers, actual_port)
38
+ end
39
+
40
+ # Connects to a TCP endpoint.
41
+ #
42
+ # @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
43
+ # @param engine [Engine]
44
+ # @return [void]
45
+ #
46
+ def connect(endpoint, engine)
47
+ host, port = parse_endpoint(endpoint)
48
+ sock = TCPSocket.new(host, port)
49
+ engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: endpoint)
50
+ end
51
+
52
+ private
53
+
54
+ # Parses a TCP endpoint URI into host and port.
55
+ #
56
+ # @param endpoint [String]
57
+ # @return [Array(String, Integer)]
58
+ #
59
+ def parse_endpoint(endpoint)
60
+ uri = URI.parse(endpoint)
61
+ [uri.hostname, uri.port]
62
+ end
63
+ end
64
+
65
+ # A bound TCP listener.
66
+ #
67
+ class Listener
68
+ # @return [String] resolved endpoint with actual port
69
+ #
70
+ attr_reader :endpoint
71
+
72
+ # @return [Integer] bound port
73
+ #
74
+ attr_reader :port
75
+
76
+ # @return [Array<TCPServer>] bound server sockets
77
+ #
78
+ attr_reader :servers
79
+
80
+
81
+ # @param endpoint [String] resolved endpoint URI
82
+ # @param servers [Array<TCPServer>]
83
+ # @param port [Integer] bound port number
84
+ #
85
+ def initialize(endpoint, servers, port)
86
+ @endpoint = endpoint
87
+ @servers = servers
88
+ @port = port
89
+ @tasks = []
90
+ end
91
+
92
+
93
+ # Registers accept loop tasks owned by the engine.
94
+ #
95
+ # @param tasks [Array<Async::Task>]
96
+ #
97
+ def accept_tasks=(tasks)
98
+ @tasks = tasks
99
+ end
100
+
101
+
102
+ # Stops the listener.
103
+ #
104
+ def stop
105
+ @tasks.each(&:stop)
106
+ @servers.each { |s| s.close rescue nil }
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
data/lib/omq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.0"
5
5
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+
5
+ module OMQ
6
+ # Pure Ruby Writable mixin. Enqueues messages to the engine's send path.
7
+ #
8
+ module Writable
9
+ # Sends a message.
10
+ #
11
+ # @param message [String, Array<String>] message parts
12
+ # @return [self]
13
+ # @raise [IO::TimeoutError] if write_timeout exceeded
14
+ #
15
+ def send(message)
16
+ parts = freeze_message(message)
17
+ Reactor.run { with_timeout(@options.write_timeout) { @engine.enqueue_send(parts) } }
18
+ self
19
+ end
20
+
21
+ # Sends a message (chainable).
22
+ #
23
+ # @param message [String, Array<String>]
24
+ # @return [self]
25
+ #
26
+ def <<(message)
27
+ send(message)
28
+ end
29
+
30
+ private
31
+
32
+ # Converts a message into a frozen array of frozen binary strings.
33
+ #
34
+ # @param message [String, Array<String>]
35
+ # @return [Array<String>] frozen array of frozen binary strings
36
+ #
37
+ def freeze_message(message)
38
+ parts = message.is_a?(Array) ? message : [message]
39
+ raise ArgumentError, "message has no parts" if parts.empty?
40
+ if parts.frozen?
41
+ parts = parts.map { |p| frozen_binary(p) }
42
+ else
43
+ parts.map! { |p| frozen_binary(p) }
44
+ end
45
+ parts.freeze
46
+ end
47
+
48
+ def frozen_binary(str)
49
+ s = str.to_str
50
+ return s if s.frozen? && s.encoding == Encoding::BINARY
51
+ s.b.freeze
52
+ end
53
+
54
+ public
55
+
56
+ # Waits until the socket is writable.
57
+ #
58
+ # @param timeout [Numeric, nil] timeout in seconds
59
+ # @return [true]
60
+ #
61
+ def wait_writable(timeout = @options.write_timeout)
62
+ true
63
+ end
64
+ end
65
+ end
data/lib/omq.rb CHANGED
@@ -5,9 +5,9 @@
5
5
  # Socket types live directly under OMQ:: for a clean API:
6
6
  # OMQ::PUSH, OMQ::PULL, OMQ::PUB, OMQ::SUB, etc.
7
7
  #
8
- # Protocol internals live under OMQ::ZMTP:: and are not part
9
- # of the public API.
10
- #
8
+
9
+ require "protocol/zmtp"
10
+ require "io/stream"
11
11
 
12
12
  require_relative "omq/version"
13
13
 
@@ -16,9 +16,65 @@ module OMQ
16
16
  # The socket is no longer usable; the original error is available via #cause.
17
17
  #
18
18
  class SocketDeadError < RuntimeError; end
19
+
20
+ # Errors raised when a peer disconnects or resets the connection.
21
+ CONNECTION_LOST = [
22
+ EOFError,
23
+ IOError,
24
+ Errno::EPIPE,
25
+ Errno::ECONNRESET,
26
+ Errno::ECONNABORTED,
27
+ Errno::ENOTCONN,
28
+ IO::Stream::ConnectionResetError,
29
+ ].freeze
30
+
31
+ # Errors raised when a peer cannot be reached.
32
+ CONNECTION_FAILED = [
33
+ Errno::ECONNREFUSED,
34
+ Errno::ENOENT,
35
+ Errno::ETIMEDOUT,
36
+ Errno::EHOSTUNREACH,
37
+ Errno::ENETUNREACH,
38
+ Socket::ResolutionError,
39
+ ].freeze
19
40
  end
20
41
 
21
- require_relative "omq/zmtp"
42
+ # Transport
43
+ require_relative "omq/transport/inproc"
44
+ require_relative "omq/transport/tcp"
45
+ require_relative "omq/transport/ipc"
46
+
47
+ # Core
48
+ require_relative "omq/reactor"
49
+ require_relative "omq/options"
50
+ require_relative "omq/routing"
51
+ require_relative "omq/routing/round_robin"
52
+ require_relative "omq/routing/fan_out"
53
+ require_relative "omq/routing/pair"
54
+ require_relative "omq/routing/req"
55
+ require_relative "omq/routing/rep"
56
+ require_relative "omq/routing/dealer"
57
+ require_relative "omq/routing/router"
58
+ require_relative "omq/routing/pub"
59
+ require_relative "omq/routing/sub"
60
+ require_relative "omq/routing/xpub"
61
+ require_relative "omq/routing/xsub"
62
+ require_relative "omq/routing/push"
63
+ require_relative "omq/routing/pull"
64
+ require_relative "omq/routing/scatter"
65
+ require_relative "omq/routing/gather"
66
+ require_relative "omq/routing/channel"
67
+ require_relative "omq/routing/client"
68
+ require_relative "omq/routing/server"
69
+ require_relative "omq/routing/radio"
70
+ require_relative "omq/routing/dish"
71
+ require_relative "omq/routing/peer"
72
+ require_relative "omq/single_frame"
73
+ require_relative "omq/engine"
74
+ require_relative "omq/readable"
75
+ require_relative "omq/writable"
76
+
77
+ # Socket types
22
78
  require_relative "omq/socket"
23
79
  require_relative "omq/req_rep"
24
80
  require_relative "omq/router_dealer"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -67,48 +67,47 @@ files:
67
67
  - lib/omq.rb
68
68
  - lib/omq/channel.rb
69
69
  - lib/omq/client_server.rb
70
+ - lib/omq/engine.rb
71
+ - lib/omq/options.rb
70
72
  - lib/omq/pair.rb
71
73
  - lib/omq/peer.rb
72
74
  - lib/omq/pub_sub.rb
73
75
  - lib/omq/push_pull.rb
74
76
  - lib/omq/radio_dish.rb
77
+ - lib/omq/reactor.rb
78
+ - lib/omq/readable.rb
75
79
  - lib/omq/req_rep.rb
76
80
  - lib/omq/router_dealer.rb
81
+ - lib/omq/routing.rb
82
+ - lib/omq/routing/channel.rb
83
+ - lib/omq/routing/client.rb
84
+ - lib/omq/routing/dealer.rb
85
+ - lib/omq/routing/dish.rb
86
+ - lib/omq/routing/fan_out.rb
87
+ - lib/omq/routing/gather.rb
88
+ - lib/omq/routing/pair.rb
89
+ - lib/omq/routing/peer.rb
90
+ - lib/omq/routing/pub.rb
91
+ - lib/omq/routing/pull.rb
92
+ - lib/omq/routing/push.rb
93
+ - lib/omq/routing/radio.rb
94
+ - lib/omq/routing/rep.rb
95
+ - lib/omq/routing/req.rb
96
+ - lib/omq/routing/round_robin.rb
97
+ - lib/omq/routing/router.rb
98
+ - lib/omq/routing/scatter.rb
99
+ - lib/omq/routing/server.rb
100
+ - lib/omq/routing/sub.rb
101
+ - lib/omq/routing/xpub.rb
102
+ - lib/omq/routing/xsub.rb
77
103
  - lib/omq/scatter_gather.rb
104
+ - lib/omq/single_frame.rb
78
105
  - lib/omq/socket.rb
106
+ - lib/omq/transport/inproc.rb
107
+ - lib/omq/transport/ipc.rb
108
+ - lib/omq/transport/tcp.rb
79
109
  - lib/omq/version.rb
80
- - lib/omq/zmtp.rb
81
- - lib/omq/zmtp/engine.rb
82
- - lib/omq/zmtp/options.rb
83
- - lib/omq/zmtp/reactor.rb
84
- - lib/omq/zmtp/readable.rb
85
- - lib/omq/zmtp/routing.rb
86
- - lib/omq/zmtp/routing/channel.rb
87
- - lib/omq/zmtp/routing/client.rb
88
- - lib/omq/zmtp/routing/dealer.rb
89
- - lib/omq/zmtp/routing/dish.rb
90
- - lib/omq/zmtp/routing/fan_out.rb
91
- - lib/omq/zmtp/routing/gather.rb
92
- - lib/omq/zmtp/routing/pair.rb
93
- - lib/omq/zmtp/routing/peer.rb
94
- - lib/omq/zmtp/routing/pub.rb
95
- - lib/omq/zmtp/routing/pull.rb
96
- - lib/omq/zmtp/routing/push.rb
97
- - lib/omq/zmtp/routing/radio.rb
98
- - lib/omq/zmtp/routing/rep.rb
99
- - lib/omq/zmtp/routing/req.rb
100
- - lib/omq/zmtp/routing/round_robin.rb
101
- - lib/omq/zmtp/routing/router.rb
102
- - lib/omq/zmtp/routing/scatter.rb
103
- - lib/omq/zmtp/routing/server.rb
104
- - lib/omq/zmtp/routing/sub.rb
105
- - lib/omq/zmtp/routing/xpub.rb
106
- - lib/omq/zmtp/routing/xsub.rb
107
- - lib/omq/zmtp/single_frame.rb
108
- - lib/omq/zmtp/transport/inproc.rb
109
- - lib/omq/zmtp/transport/ipc.rb
110
- - lib/omq/zmtp/transport/tcp.rb
111
- - lib/omq/zmtp/writable.rb
110
+ - lib/omq/writable.rb
112
111
  homepage: https://github.com/zeromq/omq
113
112
  licenses:
114
113
  - ISC