qpid_proton 0.21.0 → 0.22.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.
- checksums.yaml +4 -4
- data/examples/README.md +26 -10
- data/examples/broker.rb +99 -91
- data/examples/example_test.rb +11 -11
- data/ext/cproton/cproton.c +3 -3
- data/lib/core/connection.rb +18 -12
- data/lib/core/connection_driver.rb +13 -3
- data/lib/core/container.rb +276 -120
- data/lib/core/disposition.rb +1 -1
- data/lib/core/endpoint.rb +3 -0
- data/lib/core/listener.rb +6 -0
- data/lib/core/message.rb +5 -9
- data/lib/core/messaging_handler.rb +5 -1
- data/lib/core/receiver.rb +1 -2
- data/lib/core/sasl.rb +1 -1
- data/lib/core/session.rb +6 -4
- data/lib/core/terminus.rb +10 -4
- data/lib/core/transfer.rb +3 -0
- data/lib/core/transport.rb +1 -0
- data/lib/core/uri.rb +9 -10
- data/lib/core/work_queue.rb +76 -0
- data/lib/qpid_proton.rb +2 -8
- data/lib/types/array.rb +1 -0
- data/lib/types/hash.rb +0 -11
- data/lib/util/schedule.rb +79 -0
- data/lib/util/wrapper.rb +4 -0
- data/tests/old_examples/old_example_test.rb +0 -8
- data/tests/test_container.rb +203 -126
- data/tests/test_container_sasl.rb +141 -0
- data/tests/test_interop.rb +1 -1
- data/tests/test_messaging_adapter.rb +1 -1
- data/tests/test_tools.rb +30 -5
- data/tests/test_uri.rb +2 -0
- metadata +5 -9
- data/lib/messenger/messenger.rb +0 -703
- data/lib/messenger/subscription.rb +0 -36
- data/lib/messenger/tracker.rb +0 -37
- data/lib/messenger/tracker_status.rb +0 -68
- data/lib/util/timeout.rb +0 -49
- data/tests/old_examples/recv.rb +0 -23
- data/tests/old_examples/send.rb +0 -21
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 606167f90e6d8e39827fd3166b073468735f6cb9
         | 
| 4 | 
            +
              data.tar.gz: 8a98a50b98eebc56846228ed843780d9c139fc06
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d5850008fb8352a0773900878d0bc1afba63e8b8d6caacb9602fa4ff501086ed5b7fcf07edc978fd446866bce66e432698dee49c639fafb3eaf47097b4701b9c
         | 
| 7 | 
            +
              data.tar.gz: dcd40be0c4ef4cf579e906606030120cd36e6bba9d59e9850e9443454ec6ff4c5da6a9678b1e11c726fd2cc6b65b5033bcbef953a057255b9bef851df756f024
         | 
    
        data/examples/README.md
    CHANGED
    
    | @@ -58,19 +58,35 @@ In this set of examples we see the following event occurring, in addition to wha | |
| 58 58 |  | 
| 59 59 | 
             
            ## Now About That Broker example
         | 
| 60 60 |  | 
| 61 | 
            -
            The **broker.rb** example application is a nice demonstration of doing something more interesting in Ruby with Proton.
         | 
| 61 | 
            +
            The **broker.rb** example application is a nice demonstration of doing something more interesting in Ruby with Proton, and shows how to use multiple threads.
         | 
| 62 62 |  | 
| 63 | 
            -
            The  | 
| 63 | 
            +
            The broker listens for incoming connections and sender/receiver links. It uses the source and target address of senders and receivers to identify a queue. Messages from receivers go on the queue, and are sent via senders.
         | 
| 64 64 |  | 
| 65 65 | 
             
            The components of the broker example include:
         | 
| 66 | 
            -
             * **Broker**  | 
| 67 | 
            -
             * ** | 
| 68 | 
            -
             | 
| 69 | 
            -
            The Broker manages a map connecting a queue address to the instance of Exchange that holds references to the endpoints of interest.
         | 
| 66 | 
            +
             * **Broker** is a Listener::Handler that accepts connections, and manages the set of named queues.
         | 
| 67 | 
            +
             * **BrokerHandler** extends MessagingHandler to accept incoming connections, senders and receivers and transfers messages between them and the Broker's queues.
         | 
| 68 | 
            +
             * **MessageQueue** - A queue of messages that keeps track of waiting senders.
         | 
| 70 69 |  | 
| 71 70 | 
             
            The broker application demonstrates a new set of events:
         | 
| 72 71 |  | 
| 73 | 
            -
             * ** | 
| 74 | 
            -
             * ** | 
| 75 | 
            -
             * **on_connection_close** - Fired when  | 
| 76 | 
            -
             * **on_transport_close** - Fired when the  | 
| 72 | 
            +
             * **on_sender_open** - Fired when a sender link is opened, the broker gets the address and starts sending messages from the corresponding queue.
         | 
| 73 | 
            +
             * **on_sender_close** - Fired when a sender link is closed, remove the sender from the queue so no more messages are sent.
         | 
| 74 | 
            +
             * **on_connection_close** - Fired when the remote connection is closes, close all senders.
         | 
| 75 | 
            +
             * **on_transport_close** - Fired when the transport (socket) has closed, close all senders.
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            It also demonstrates aspects of multi-threaded proton:
         | 
| 78 | 
            +
             | 
| 79 | 
            +
             * **Thread safe MessageQueue** Uses a Mutex to make actions atomic when called concurrently.
         | 
| 80 | 
            +
             | 
| 81 | 
            +
             * **Using WorkQueue** Proton objects like Sender are not thread safe.  They are
         | 
| 82 | 
            +
               normally only used in MessagingHandler#on_ callbacks.  To request work from a
         | 
| 83 | 
            +
               different thread you can add a code block to a WorkQueue, as shown in
         | 
| 84 | 
            +
               MessageQueue#push.
         | 
| 85 | 
            +
             | 
| 86 | 
            +
             * **Listener::Handler** The broker creates a new BrokerHandler instance for
         | 
| 87 | 
            +
               each accepted connection. The container ensures that calls on each handler instance
         | 
| 88 | 
            +
               are serialized even if there are multiple threads in the container.
         | 
| 89 | 
            +
             | 
| 90 | 
            +
             * **Calling Container#run in multiple threads** The Container uses threads that call
         | 
| 91 | 
            +
               #run as a thread pool to dispatch calls to MessagingHandler instances. Even
         | 
| 92 | 
            +
               if there are multiple threads, calls to handler instance are serialized.
         | 
    
        data/examples/broker.rb
    CHANGED
    
    | @@ -21,139 +21,142 @@ require 'qpid_proton' | |
| 21 21 | 
             
            require 'optparse'
         | 
| 22 22 | 
             
            require 'pathname'
         | 
| 23 23 |  | 
| 24 | 
            +
            # Thread safe message queue that notifies waiting senders when messages arrive.
         | 
| 24 25 | 
             
            class MessageQueue
         | 
| 25 26 |  | 
| 26 | 
            -
              def initialize | 
| 27 | 
            -
                @ | 
| 28 | 
            -
                @ | 
| 29 | 
            -
                @ | 
| 30 | 
            -
              end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
               | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 27 | 
            +
              def initialize
         | 
| 28 | 
            +
                @lock = Mutex.new           # Make ations on the queue atomic
         | 
| 29 | 
            +
                @messages = []              # Messages on the queue
         | 
| 30 | 
            +
                @waiting = []               # Senders that are waiting for messages
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              # Push a message onto the queue and notify any waiting senders
         | 
| 34 | 
            +
              def push(message)
         | 
| 35 | 
            +
                @lock.synchronize do
         | 
| 36 | 
            +
                  @messages << message
         | 
| 37 | 
            +
                  unless @waiting.empty?    # Notify waiting senders
         | 
| 38 | 
            +
                    # NOTE: the call to self.send_to is added to the sender's work_queue,
         | 
| 39 | 
            +
                    # and will be executed in the sender's thread
         | 
| 40 | 
            +
                    @waiting.each { |s| s.work_queue.add { self.send_to(s); } }
         | 
| 41 | 
            +
                    @waiting.clear
         | 
| 42 | 
            +
                  end
         | 
| 39 43 | 
             
                end
         | 
| 40 | 
            -
                @consumers.empty? && (@dynamic || @queue.empty?)
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
              def publish(message)
         | 
| 44 | 
            -
                @queue << message
         | 
| 45 | 
            -
                self.dispatch
         | 
| 46 44 | 
             
              end
         | 
| 47 45 |  | 
| 48 | 
            -
               | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                 | 
| 52 | 
            -
                   | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 46 | 
            +
              # Pop a message off the queue.
         | 
| 47 | 
            +
              # If no messages available, record sender as waiting and return nil.
         | 
| 48 | 
            +
              def pop(sender)
         | 
| 49 | 
            +
                @lock.synchronize do
         | 
| 50 | 
            +
                  if @messages.empty?
         | 
| 51 | 
            +
                    @waiting << sender
         | 
| 52 | 
            +
                    nil
         | 
| 53 | 
            +
                  else
         | 
| 54 | 
            +
                    @messages.shift
         | 
| 55 | 
            +
                  end
         | 
| 56 56 | 
             
                end
         | 
| 57 57 | 
             
              end
         | 
| 58 58 |  | 
| 59 | 
            -
               | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                  end
         | 
| 59 | 
            +
              # NOTE: Called in sender's thread.
         | 
| 60 | 
            +
              # Pull messages from the queue as long as sender has credit.
         | 
| 61 | 
            +
              # If queue runs out of messages, record sender as waiting.
         | 
| 62 | 
            +
              def send_to(sender)
         | 
| 63 | 
            +
                while sender.credit > 0 && (message = pop(sender))
         | 
| 64 | 
            +
                  sender.send(message)
         | 
| 66 65 | 
             
                end
         | 
| 67 | 
            -
                return result
         | 
| 68 66 | 
             
              end
         | 
| 69 67 |  | 
| 68 | 
            +
              def forget(sender)
         | 
| 69 | 
            +
                @lock.synchronize { @waiting.delete(sender) }
         | 
| 70 | 
            +
              end
         | 
| 70 71 | 
             
            end
         | 
| 71 72 |  | 
| 72 | 
            -
            class Broker < Qpid::Proton::MessagingHandler
         | 
| 73 | 
            -
             | 
| 74 | 
            -
              def initialize(url)
         | 
| 75 | 
            -
                super()
         | 
| 76 | 
            -
                @url = url
         | 
| 77 | 
            -
                @queues = {}
         | 
| 78 | 
            -
                begin          # Optional SSL setup, ignore if we don't find cert files etc.
         | 
| 79 | 
            -
                  @ssl_domain = Qpid::Proton::SSLDomain.new(Qpid::Proton::SSLDomain::MODE_SERVER)
         | 
| 80 | 
            -
                  cert_passsword = "tserverpw"
         | 
| 81 | 
            -
                  if Gem.win_platform?       # Use P12 certs for windows schannel
         | 
| 82 | 
            -
                    @ssl_domain.credentials("ssl_certs/tserver-certificate.p12", "", cert_passsword)
         | 
| 83 | 
            -
                  else
         | 
| 84 | 
            -
                    @ssl_domain.credentials("ssl_certs/tserver-certificate.pem", "ssl_certs/tserver-private-key.pem", cert_passsword)
         | 
| 85 | 
            -
                  end
         | 
| 86 | 
            -
                  @ssl_domain.allow_unsecured_client # SSL is optional, this is not secure.
         | 
| 87 | 
            -
                rescue
         | 
| 88 | 
            -
                  @ssl_domain = nil # Don't worry if we can't set up SSL.
         | 
| 89 | 
            -
                end
         | 
| 90 | 
            -
              end
         | 
| 91 73 |  | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
                @listener = container.listen(@url, Qpid::Proton::Listener::Handler.new(opts))
         | 
| 96 | 
            -
                STDOUT.puts "Listening on #{@url.inspect}"; STDOUT.flush
         | 
| 97 | 
            -
              end
         | 
| 74 | 
            +
            # Handler for broker connections. In a multi-threaded application you should
         | 
| 75 | 
            +
            # normally create a separate handler instance for each connection.
         | 
| 76 | 
            +
            class BrokerHandler < Qpid::Proton::MessagingHandler
         | 
| 98 77 |  | 
| 99 | 
            -
              def  | 
| 100 | 
            -
                 | 
| 101 | 
            -
                  @queues[address] = MessageQueue.new
         | 
| 102 | 
            -
                end
         | 
| 103 | 
            -
                @queues[address]
         | 
| 78 | 
            +
              def initialize(broker)
         | 
| 79 | 
            +
                @broker = broker
         | 
| 104 80 | 
             
              end
         | 
| 105 81 |  | 
| 106 82 | 
             
              def on_sender_open(sender)
         | 
| 107 83 | 
             
                if sender.remote_source.dynamic?
         | 
| 108 | 
            -
                  address = SecureRandom.uuid
         | 
| 109 | 
            -
                  sender.source.address = address
         | 
| 110 | 
            -
                  q = MessageQueue.new(true)
         | 
| 111 | 
            -
                  @queues[address] = q
         | 
| 112 | 
            -
                  q.subscribe(sender)
         | 
| 84 | 
            +
                  sender.source.address = SecureRandom.uuid
         | 
| 113 85 | 
             
                elsif sender.remote_source.address
         | 
| 114 86 | 
             
                  sender.source.address = sender.remote_source.address
         | 
| 115 | 
            -
             | 
| 87 | 
            +
                else
         | 
| 88 | 
            +
                  sender.connection.close("no source address")
         | 
| 89 | 
            +
                  return
         | 
| 116 90 | 
             
                end
         | 
| 91 | 
            +
                q = @broker.queue(sender.source.address)
         | 
| 92 | 
            +
                q.send_to(sender)
         | 
| 117 93 | 
             
              end
         | 
| 118 94 |  | 
| 119 95 | 
             
              def on_receiver_open(receiver)
         | 
| 120 96 | 
             
                if receiver.remote_target.address
         | 
| 121 97 | 
             
                  receiver.target.address = receiver.remote_target.address
         | 
| 122 | 
            -
                 | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
              def unsubscribe(link)
         | 
| 126 | 
            -
                if @queues.has_key?(link.source.address)
         | 
| 127 | 
            -
                  if @queues[link.source.address].unsubscribe(link)
         | 
| 128 | 
            -
                    @queues.delete(link.source.address)
         | 
| 129 | 
            -
                  end
         | 
| 98 | 
            +
                else
         | 
| 99 | 
            +
                  receiver.connection.close("no target address")
         | 
| 130 100 | 
             
                end
         | 
| 131 101 | 
             
              end
         | 
| 132 102 |  | 
| 133 103 | 
             
              def on_sender_close(sender)
         | 
| 134 | 
            -
                 | 
| 104 | 
            +
                q = @broker.queue(sender.source.address)
         | 
| 105 | 
            +
                q.forget(sender) if q
         | 
| 135 106 | 
             
              end
         | 
| 136 107 |  | 
| 137 108 | 
             
              def on_connection_close(connection)
         | 
| 138 | 
            -
                 | 
| 109 | 
            +
                connection.each_sender { |s| on_sender_close(s) }
         | 
| 139 110 | 
             
              end
         | 
| 140 111 |  | 
| 141 112 | 
             
              def on_transport_close(transport)
         | 
| 142 | 
            -
                 | 
| 143 | 
            -
              end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
              def remove_stale_consumers(connection)
         | 
| 146 | 
            -
                connection.each_sender { |s| unsubscribe(s) }
         | 
| 113 | 
            +
                transport.connection.each_sender { |s| on_sender_close(s) }
         | 
| 147 114 | 
             
              end
         | 
| 148 115 |  | 
| 149 116 | 
             
              def on_sendable(sender)
         | 
| 150 | 
            -
                 | 
| 151 | 
            -
                q.dispatch(sender)
         | 
| 117 | 
            +
                @broker.queue(sender.source.address).send_to(sender)
         | 
| 152 118 | 
             
              end
         | 
| 153 119 |  | 
| 154 120 | 
             
              def on_message(delivery, message)
         | 
| 155 | 
            -
                 | 
| 156 | 
            -
             | 
| 121 | 
            +
                @broker.queue(delivery.receiver.target.address).push(message)
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
            end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            # Broker manages the queues and accepts incoming connections.
         | 
| 126 | 
            +
            class Broker < Qpid::Proton::Listener::Handler
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              def initialize
         | 
| 129 | 
            +
                @queues = {}
         | 
| 130 | 
            +
                @connection_options = {}
         | 
| 131 | 
            +
                ssl_setup
         | 
| 132 | 
            +
              end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
              def ssl_setup
         | 
| 135 | 
            +
                # Optional SSL setup
         | 
| 136 | 
            +
                ssl = Qpid::Proton::SSLDomain.new(Qpid::Proton::SSLDomain::MODE_SERVER)
         | 
| 137 | 
            +
                cert_passsword = "tserverpw"
         | 
| 138 | 
            +
                if Gem.win_platform?       # Use P12 certs for windows schannel
         | 
| 139 | 
            +
                  ssl.credentials("ssl_certs/tserver-certificate.p12", "", cert_passsword)
         | 
| 140 | 
            +
                else
         | 
| 141 | 
            +
                  ssl.credentials("ssl_certs/tserver-certificate.pem", "ssl_certs/tserver-private-key.pem", cert_passsword)
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
                ssl.allow_unsecured_client # SSL is optional, this is not secure.
         | 
| 144 | 
            +
                @connection_options[:ssl_domain] = ssl if ssl
         | 
| 145 | 
            +
              rescue
         | 
| 146 | 
            +
                # Don't worry if we can't set up SSL.
         | 
| 147 | 
            +
              end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
              def on_open(l)
         | 
| 150 | 
            +
                STDOUT.puts "Listening on #{l}\n"; STDOUT.flush
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              # Create a new BrokerHandler instance for each connection we accept
         | 
| 154 | 
            +
              def on_accept(l)
         | 
| 155 | 
            +
                { :handler => BrokerHandler.new(self) }.update(@connection_options)
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              def queue(address)
         | 
| 159 | 
            +
                @queues[address] ||= MessageQueue.new
         | 
| 157 160 | 
             
              end
         | 
| 158 161 |  | 
| 159 162 | 
             
            end
         | 
| @@ -164,4 +167,9 @@ Start an example broker listening on URL" | |
| 164 167 | 
             
              return 1
         | 
| 165 168 | 
             
            end
         | 
| 166 169 | 
             
            url, = ARGV
         | 
| 167 | 
            -
            Qpid::Proton::Container.new | 
| 170 | 
            +
            container = Qpid::Proton::Container.new
         | 
| 171 | 
            +
            container.listen(url, Broker.new)
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            # Run the container in multiple threads.
         | 
| 174 | 
            +
            threads = 4.times.map { Thread.new {  container.run }}
         | 
| 175 | 
            +
            threads.each { |t| t.join }
         | 
    
        data/examples/example_test.rb
    CHANGED
    
    | @@ -46,7 +46,7 @@ class ExampleTest < MiniTest::Test | |
| 46 46 | 
             
              end
         | 
| 47 47 |  | 
| 48 48 | 
             
              def test_helloworld
         | 
| 49 | 
            -
                assert_output("Hello world!", "helloworld.rb", $url,  | 
| 49 | 
            +
                assert_output("Hello world!", "helloworld.rb", $url, "examples")
         | 
| 50 50 | 
             
              end
         | 
| 51 51 |  | 
| 52 52 | 
             
              def test_client_server
         | 
| @@ -60,40 +60,40 @@ class ExampleTest < MiniTest::Test | |
| 60 60 | 
             
            -> And the mome raths outgrabe.
         | 
| 61 61 | 
             
            <- AND THE MOME RATHS OUTGRABE.
         | 
| 62 62 | 
             
            EOS
         | 
| 63 | 
            -
                server = run_script("server.rb", $url,  | 
| 64 | 
            -
                assert_output(want.strip, "client.rb", $url,  | 
| 63 | 
            +
                server = run_script("server.rb", $url, "examples")
         | 
| 64 | 
            +
                assert_output(want.strip, "client.rb", $url, "examples")
         | 
| 65 65 | 
             
              ensure
         | 
| 66 66 | 
             
                Process.kill :TERM, server.pid if server
         | 
| 67 67 | 
             
              end
         | 
| 68 68 |  | 
| 69 69 | 
             
              def test_send_recv
         | 
| 70 | 
            -
                assert_output("All 10 messages confirmed!", "simple_send.rb", $url,  | 
| 70 | 
            +
                assert_output("All 10 messages confirmed!", "simple_send.rb", $url, "examples")
         | 
| 71 71 | 
             
                want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
         | 
| 72 | 
            -
                assert_output(want.strip, "simple_recv.rb", $url,  | 
| 72 | 
            +
                assert_output(want.strip, "simple_recv.rb", $url, "examples")
         | 
| 73 73 | 
             
              end
         | 
| 74 74 |  | 
| 75 75 | 
             
              def test_ssl_send_recv
         | 
| 76 | 
            -
                out = run_script("ssl_send.rb", $url,  | 
| 76 | 
            +
                out = run_script("ssl_send.rb", $url, "examples").read.strip
         | 
| 77 77 | 
             
                assert_match(/Connection secured with "...*\"\nAll 10 messages confirmed!/, out)
         | 
| 78 78 | 
             
                want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
         | 
| 79 | 
            -
                assert_output(want.strip, "simple_recv.rb", $url,  | 
| 79 | 
            +
                assert_output(want.strip, "simple_recv.rb", $url, "examples")
         | 
| 80 80 | 
             
              end
         | 
| 81 81 |  | 
| 82 82 | 
             
              def test_direct_recv
         | 
| 83 83 | 
             
                url = test_url
         | 
| 84 | 
            -
                  p = run_script("direct_recv.rb", url,  | 
| 84 | 
            +
                  p = run_script("direct_recv.rb", url, "examples")
         | 
| 85 85 | 
             
                  p.readline                # Wait till ready
         | 
| 86 | 
            -
                  assert_output("All 10 messages confirmed!", "simple_send.rb", url,  | 
| 86 | 
            +
                  assert_output("All 10 messages confirmed!", "simple_send.rb", url, "examples")
         | 
| 87 87 | 
             
                  want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
         | 
| 88 88 | 
             
                  assert_equal(want.strip, p.read.strip)
         | 
| 89 89 | 
             
              end
         | 
| 90 90 |  | 
| 91 91 | 
             
              def test_direct_send
         | 
| 92 92 | 
             
                url = test_url
         | 
| 93 | 
            -
                p = run_script("direct_send.rb", url,  | 
| 93 | 
            +
                p = run_script("direct_send.rb", url, "examples")
         | 
| 94 94 | 
             
                p.readline                # Wait till ready
         | 
| 95 95 | 
             
                want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
         | 
| 96 | 
            -
                assert_output(want.strip, "simple_recv.rb", url,  | 
| 96 | 
            +
                assert_output(want.strip, "simple_recv.rb", url, "examples")
         | 
| 97 97 | 
             
                assert_equal("All 10 messages confirmed!", p.read.strip)
         | 
| 98 98 | 
             
              end
         | 
| 99 99 | 
             
            end
         | 
    
        data/ext/cproton/cproton.c
    CHANGED
    
    | @@ -19329,7 +19329,7 @@ _wrap_pn_data_put_decimal32(int argc, VALUE *argv, VALUE self) { | |
| 19329 19329 | 
             
              }
         | 
| 19330 19330 | 
             
              arg1 = (pn_data_t *)(argp1);
         | 
| 19331 19331 | 
             
              {
         | 
| 19332 | 
            -
                arg2 =  | 
| 19332 | 
            +
                arg2 = NUM2UINT(argv[1]);
         | 
| 19333 19333 | 
             
              }
         | 
| 19334 19334 | 
             
              result = (int)pn_data_put_decimal32(arg1,arg2);
         | 
| 19335 19335 | 
             
              vresult = SWIG_From_int((int)(result));
         | 
| @@ -20044,7 +20044,7 @@ _wrap_pn_data_get_decimal32(int argc, VALUE *argv, VALUE self) { | |
| 20044 20044 | 
             
              arg1 = (pn_data_t *)(argp1);
         | 
| 20045 20045 | 
             
              result = (pn_decimal32_t)pn_data_get_decimal32(arg1);
         | 
| 20046 20046 | 
             
              {
         | 
| 20047 | 
            -
                vresult =  | 
| 20047 | 
            +
                vresult = UINT2NUM(result);
         | 
| 20048 20048 | 
             
              }
         | 
| 20049 20049 | 
             
              return vresult;
         | 
| 20050 20050 | 
             
            fail:
         | 
| @@ -23711,7 +23711,7 @@ SWIGEXPORT void Init_cproton(void) { | |
| 23711 23711 | 
             
              rb_define_module_function(mCproton, "pni_connection_driver", _wrap_pni_connection_driver, -1);
         | 
| 23712 23712 | 
             
              rb_define_const(mCproton, "PROTON_IMPORT_EXPORT_H", SWIG_From_int((int)(1)));
         | 
| 23713 23713 | 
             
              rb_define_const(mCproton, "PN_VERSION_MAJOR", SWIG_From_int((int)(0)));
         | 
| 23714 | 
            -
              rb_define_const(mCproton, "PN_VERSION_MINOR", SWIG_From_int((int)( | 
| 23714 | 
            +
              rb_define_const(mCproton, "PN_VERSION_MINOR", SWIG_From_int((int)(22)));
         | 
| 23715 23715 | 
             
              rb_define_const(mCproton, "PN_VERSION_POINT", SWIG_From_int((int)(0)));
         | 
| 23716 23716 | 
             
              rb_define_const(mCproton, "PROTON_TYPES_H", SWIG_From_int((int)(1)));
         | 
| 23717 23717 | 
             
              rb_define_const(mCproton, "PN_MILLIS_MAX", SWIG_From_unsigned_SS_int((unsigned int)((~0U))));
         | 
    
        data/lib/core/connection.rb
    CHANGED
    
    | @@ -124,9 +124,8 @@ module Qpid::Proton | |
| 124 124 |  | 
| 125 125 | 
             
                # @private
         | 
| 126 126 | 
             
                def apply opts
         | 
| 127 | 
            -
                  # NOTE: Only connection options are set here. | 
| 128 | 
            -
                  # with {Transport#apply} | 
| 129 | 
            -
                  # on_connection_bound if not using a connection_driver)
         | 
| 127 | 
            +
                  # NOTE: Only connection options are set here.
         | 
| 128 | 
            +
                  # Transport options must be applied with {Transport#apply}
         | 
| 130 129 | 
             
                  @container = opts[:container]
         | 
| 131 130 | 
             
                  cid = opts[:container_id] || (@container && @container.id) || SecureRandom.uuid
         | 
| 132 131 | 
             
                  cid = cid.to_s if cid.is_a? Symbol # Allow symbols as container name
         | 
| @@ -135,12 +134,9 @@ module Qpid::Proton | |
| 135 134 | 
             
                  Cproton.pn_connection_set_password(@impl, opts[:password]) if opts[:password]
         | 
| 136 135 | 
             
                  Cproton.pn_connection_set_hostname(@impl, opts[:virtual_host]) if opts[:virtual_host]
         | 
| 137 136 | 
             
                  @link_prefix = opts[:link_prefix] || cid
         | 
| 138 | 
            -
                  Codec::Data.from_object(Cproton.pn_connection_offered_capabilities(@impl),
         | 
| 139 | 
            -
             | 
| 140 | 
            -
                  Codec::Data.from_object(Cproton. | 
| 141 | 
            -
                                          Types.symbol_array(opts[:desired_capabilities]))
         | 
| 142 | 
            -
                  Codec::Data.from_object(Cproton.pn_connection_properties(@impl),
         | 
| 143 | 
            -
                                          Types.symbol_keys(opts[:properties]))
         | 
| 137 | 
            +
                  Codec::Data.from_object(Cproton.pn_connection_offered_capabilities(@impl), opts[:offered_capabilities])
         | 
| 138 | 
            +
                  Codec::Data.from_object(Cproton.pn_connection_desired_capabilities(@impl), opts[:desired_capabilities])
         | 
| 139 | 
            +
                  Codec::Data.from_object(Cproton.pn_connection_properties(@impl), opts[:properties])
         | 
| 144 140 | 
             
                end
         | 
| 145 141 |  | 
| 146 142 | 
             
                # Idle-timeout advertised by the remote peer, in seconds.
         | 
| @@ -253,17 +249,24 @@ module Qpid::Proton | |
| 253 249 | 
             
                  return enum_for(:each_link) unless block_given?
         | 
| 254 250 | 
             
                  l = Cproton.pn_link_head(@impl, 0);
         | 
| 255 251 | 
             
                  while l
         | 
| 256 | 
            -
                    yield  | 
| 252 | 
            +
                    l2 = l                  #  get next before yield, in case yield closes l and unlinks it
         | 
| 257 253 | 
             
                    l = Cproton.pn_link_next(l, 0)
         | 
| 254 | 
            +
                    yield Link.wrap(l2)
         | 
| 258 255 | 
             
                  end
         | 
| 259 256 | 
             
                  self
         | 
| 260 257 | 
             
                end
         | 
| 261 258 |  | 
| 262 259 | 
             
                # Get the {Sender} links - see {#each_link}
         | 
| 263 | 
            -
                def each_sender() | 
| 260 | 
            +
                def each_sender()
         | 
| 261 | 
            +
                  return enum_for(:each_sender) unless block_given?
         | 
| 262 | 
            +
                  each_link.select { |l| yield l if l.sender? }
         | 
| 263 | 
            +
                end
         | 
| 264 264 |  | 
| 265 265 | 
             
                # Get the {Receiver} links - see {#each_link}
         | 
| 266 | 
            -
             | 
| 266 | 
            +
                  def each_receiver()
         | 
| 267 | 
            +
                    return enum_for(:each_receiver) unless block_given?
         | 
| 268 | 
            +
                    each_link.select { |l| yield l if l.receiver? }
         | 
| 269 | 
            +
                  end
         | 
| 267 270 |  | 
| 268 271 | 
             
                # @deprecated use {#MessagingHandler} to handle work
         | 
| 269 272 | 
             
                def work_head
         | 
| @@ -282,6 +285,9 @@ module Qpid::Proton | |
| 282 285 | 
             
                  @link_prefix + "/" +  (@link_count += 1).to_s(32)
         | 
| 283 286 | 
             
                end
         | 
| 284 287 |  | 
| 288 | 
            +
                # @return [WorkQueue] work queue to execute code serialized correctly for this connection
         | 
| 289 | 
            +
                attr_reader :work_queue
         | 
| 290 | 
            +
             | 
| 285 291 | 
             
                protected
         | 
| 286 292 |  | 
| 287 293 | 
             
                def _local_condition
         | 
| @@ -118,9 +118,17 @@ module Qpid::Proton | |
| 118 118 | 
             
                def tick(now=Time.now)
         | 
| 119 119 | 
             
                  transport = Cproton.pni_connection_driver_transport(@impl)
         | 
| 120 120 | 
             
                  ms = Cproton.pn_transport_tick(transport, (now.to_r * 1000).to_i)
         | 
| 121 | 
            -
                   | 
| 121 | 
            +
                  @next_tick = ms.zero? ? nil : Time.at(ms.to_r / 1000);
         | 
| 122 | 
            +
                  unless @next_tick
         | 
| 123 | 
            +
                    idle = Cproton.pn_transport_get_idle_timeout(transport);
         | 
| 124 | 
            +
                    @next_tick = now + (idle.to_r / 1000) unless idle.zero?
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
                  @next_tick
         | 
| 122 127 | 
             
                end
         | 
| 123 128 |  | 
| 129 | 
            +
                # Time returned by the last call to {#tick}
         | 
| 130 | 
            +
                attr_accessor :next_tick
         | 
| 131 | 
            +
             | 
| 124 132 | 
             
                # Disconnect the write side of the transport, *without* sending an AMQP
         | 
| 125 133 | 
             
                # close frame. To close politely, you should use {Connection#close}, the
         | 
| 126 134 | 
             
                # transport will close itself once the protocol close is complete.
         | 
| @@ -181,6 +189,7 @@ module Qpid::Proton | |
| 181 189 | 
             
                    case e.method           # Events that affect the driver
         | 
| 182 190 | 
             
                    when :on_transport_tail_closed then close_read
         | 
| 183 191 | 
             
                    when :on_transport_head_closed then close_write
         | 
| 192 | 
            +
                    when :on_transport_closed then @io.close rescue nil # Allow double-close
         | 
| 184 193 | 
             
                    end
         | 
| 185 194 | 
             
                    e.dispatch @adapter
         | 
| 186 195 | 
             
                  end
         | 
| @@ -192,10 +201,11 @@ module Qpid::Proton | |
| 192 201 | 
             
                #   or nil if there are no scheduled events
         | 
| 193 202 | 
             
                def process(now=Time.now)
         | 
| 194 203 | 
             
                  read
         | 
| 204 | 
            +
                  dispatch
         | 
| 195 205 | 
             
                  next_tick = tick(now)
         | 
| 196 | 
            -
                  dispatch | 
| 206 | 
            +
                  dispatch
         | 
| 197 207 | 
             
                  write
         | 
| 198 | 
            -
                  dispatch | 
| 208 | 
            +
                  dispatch
         | 
| 199 209 | 
             
                  return next_tick
         | 
| 200 210 | 
             
                end
         | 
| 201 211 | 
             
              end
         |