nats 0.4.28 → 0.5.0.beta.12
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 +15 -0
- data/HISTORY.md +16 -0
- data/README.md +8 -5
- data/bin/nats-queue +14 -5
- data/bin/nats-request +12 -3
- data/bin/nats-sub +13 -5
- data/lib/nats/client.rb +189 -35
- data/lib/nats/server.rb +27 -4
- data/lib/nats/server/cluster.rb +102 -0
- data/lib/nats/server/connection.rb +47 -17
- data/lib/nats/server/connz.rb +0 -2
- data/lib/nats/server/const.rb +14 -2
- data/lib/nats/server/options.rb +19 -2
- data/lib/nats/server/route.rb +279 -0
- data/lib/nats/server/server.rb +35 -10
- data/lib/nats/server/varz.rb +1 -0
- data/nats.gemspec +6 -4
- metadata +38 -26
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            !binary "U0hBMQ==":
         | 
| 3 | 
            +
              metadata.gz: !binary |-
         | 
| 4 | 
            +
                NTZjYWZjNDUxYmZmMzU2NTIxZjY0ODgwNTBlZWI4MGIwNTA1Y2U2NQ==
         | 
| 5 | 
            +
              data.tar.gz: !binary |-
         | 
| 6 | 
            +
                MjVkYjliNDVkMzNkNDA4ZjIxYjBhNjgxMzcxNDQ3MDQ5NzRiYjFjOA==
         | 
| 7 | 
            +
            SHA512:
         | 
| 8 | 
            +
              metadata.gz: !binary |-
         | 
| 9 | 
            +
                ODFlNmMxMjkzMzgwYmIwM2I5NWMxNzE3YWY2ZjUxYzEwNDZiMzQyYWQ5M2Q5
         | 
| 10 | 
            +
                MzQ2ZGUzYTZiYmU1NTBhZDczMmZjZjBhMzlhM2Q4ZDkzYzNhYjMzYmRlNzFk
         | 
| 11 | 
            +
                Yzc2ZjUxNjEyNWUwZDQ3NzcxMDY0NGVmMzdlMDZlN2FlYzExMWQ=
         | 
| 12 | 
            +
              data.tar.gz: !binary |-
         | 
| 13 | 
            +
                M2U4MTVjMmYyMzIxODI4NjZjZjhmNDkwOGE1MDM2ZWRmN2I4ZjJjMTYwMTQx
         | 
| 14 | 
            +
                MWY0MTA2NmJhNzQ3ZWI3NGI1YmY4ODFmMzk2YzRmMzMwOWJjYjJlMGE3MDAy
         | 
| 15 | 
            +
                N2EzNjM4MGZjOTQzOTgwMTYzNzk1YjY3ZTYzMTdjZTcyYmI0MDA=
         | 
    
        data/HISTORY.md
    CHANGED
    
    | @@ -1,5 +1,21 @@ | |
| 1 1 | 
             
            # HISTORY
         | 
| 2 2 |  | 
| 3 | 
            +
            ## v0.5.0.beta.12 (October 1, 2013)
         | 
| 4 | 
            +
              - Fixed issue #58, reconnects not stopped on auth failures
         | 
| 5 | 
            +
              - Fixed leaking ping timers on auth failures
         | 
| 6 | 
            +
              - Created AuthError
         | 
| 7 | 
            +
              - See full list @ https://github.com/derekcollison/nats/compare/v0.5.0.beta.11...v0.5.0.beta.12
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## v0.5.0.beta.11 (July 26, 2013)
         | 
| 10 | 
            +
              - Bi-directional Route designation
         | 
| 11 | 
            +
              - Upgrade to EM 1.x
         | 
| 12 | 
            +
              - See full list @ https://github.com/derekcollison/nats/compare/v0.5.0.beta.1...v0.5.0.beta.11
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ## v0.5.0.beta.1 (Sept 10, 2012)
         | 
| 15 | 
            +
              - Clustering support for nats-servers
         | 
| 16 | 
            +
              - Reconnect client logic cluster aware (explicit servers only for now)
         | 
| 17 | 
            +
              - See full list @ https://github.com/derekcollison/nats/compare/v0.4.26...v0.5.0.beta.1
         | 
| 18 | 
            +
             | 
| 3 19 | 
             
            ## v0.4.28 (September 22, 2012)
         | 
| 4 20 | 
             
              - Binary payload bug fix
         | 
| 5 21 | 
             
              - Lock EM to version 0.12.10, 1.0 does not pass tests currently.
         | 
    
        data/README.md
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            A lightweight publish-subscribe and distributed queueing messaging system.
         | 
| 4 4 |  | 
| 5 | 
            -
            [](http://travis-ci.org/derekcollison/nats)
         | 
| 5 | 
            +
            [](http://travis-ci.org/derekcollison/nats)
         | 
| 6 6 |  | 
| 7 7 | 
             
            ## Supported Platforms
         | 
| 8 8 |  | 
| @@ -12,11 +12,14 @@ This gem currently works on the following Ruby platforms: | |
| 12 12 | 
             
            - Rubinius
         | 
| 13 13 | 
             
            - JRuby
         | 
| 14 14 |  | 
| 15 | 
            +
            ## Additional Clients
         | 
| 16 | 
            +
             | 
| 15 17 | 
             
            There are several other client language bindings as well.
         | 
| 16 18 |  | 
| 17 | 
            -
            [Node.js](https://github.com/derekcollison/node_nats) | 
| 18 | 
            -
            [Go | 
| 19 | 
            -
             | 
| 19 | 
            +
            - [Node.js](https://github.com/derekcollison/node_nats)
         | 
| 20 | 
            +
            - [Go](https://github.com/apcera/nats)
         | 
| 21 | 
            +
            - [Java](https://github.com/tyagihas/java_nats)
         | 
| 22 | 
            +
            - [Java - Spring](https://github.com/mheath/jnats)
         | 
| 20 23 |  | 
| 21 24 | 
             
            ## Getting Started
         | 
| 22 25 |  | 
| @@ -112,7 +115,7 @@ See examples and benchmarks for more information.. | |
| 112 115 |  | 
| 113 116 | 
             
            (The MIT License)
         | 
| 114 117 |  | 
| 115 | 
            -
            Copyright (c) 2010- | 
| 118 | 
            +
            Copyright (c) 2010-2013 Derek Collison
         | 
| 116 119 |  | 
| 117 120 | 
             
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 118 121 | 
             
            of this software and associated documentation files (the "Software"), to
         | 
    
        data/bin/nats-queue
    CHANGED
    
    | @@ -7,14 +7,14 @@ require 'nats/client' | |
| 7 7 | 
             
            ['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }
         | 
| 8 8 |  | 
| 9 9 | 
             
            def usage
         | 
| 10 | 
            -
              puts "Usage: nats-queue <subject> <queue name> [-s server] [-t]"; exit
         | 
| 10 | 
            +
              puts "Usage: nats-queue <subject> <queue name> [-s server] [-t] [-r]"; exit
         | 
| 11 11 | 
             
            end
         | 
| 12 12 |  | 
| 13 13 | 
             
            args = ARGV.dup
         | 
| 14 14 | 
             
            opts_parser = OptionParser.new do |opts|
         | 
| 15 15 | 
             
              opts.on('-s SERVER') { |server| $nats_server = server }
         | 
| 16 | 
            -
              opts.on('-t') { $show_time = true }
         | 
| 17 | 
            -
             | 
| 16 | 
            +
              opts.on('-t','--time') { $show_time = true }
         | 
| 17 | 
            +
              opts.on('-r','--raw') { $show_raw = true }
         | 
| 18 18 | 
             
            end
         | 
| 19 19 | 
             
            args = opts_parser.parse!(args)
         | 
| 20 20 |  | 
| @@ -30,11 +30,20 @@ def header | |
| 30 30 | 
             
              "#{time_prefix}[\##{$i+=1}]"
         | 
| 31 31 | 
             
            end
         | 
| 32 32 |  | 
| 33 | 
            +
            def decorate sub, msg
         | 
| 34 | 
            +
              if $show_raw
         | 
| 35 | 
            +
                msg
         | 
| 36 | 
            +
              else
         | 
| 37 | 
            +
                "#{header} Received on [#{sub}] : '#{msg}'"
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
             | 
| 33 42 | 
             
            NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
         | 
| 34 43 |  | 
| 35 44 | 
             
            NATS.start(:uri => $nats_server,  :autostart => true) do
         | 
| 36 | 
            -
              puts "Listening on [#{subject}], queue group [#{queue_group}]"
         | 
| 45 | 
            +
              puts "Listening on [#{subject}], queue group [#{queue_group}]" unless $show_raw
         | 
| 37 46 | 
             
              NATS.subscribe(subject, :queue => queue_group) { |msg, _, sub|
         | 
| 38 | 
            -
                puts  | 
| 47 | 
            +
                puts decorate(sub, msg)
         | 
| 39 48 | 
             
              }
         | 
| 40 49 | 
             
            end
         | 
    
        data/bin/nats-request
    CHANGED
    
    | @@ -7,13 +7,14 @@ require 'nats/client' | |
| 7 7 | 
             
            ['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }
         | 
| 8 8 |  | 
| 9 9 | 
             
            def usage
         | 
| 10 | 
            -
              puts "Usage: nats-request <subject> <msg> [-s server] [-t] [-n responses]"; exit
         | 
| 10 | 
            +
              puts "Usage: nats-request <subject> <msg> [-s server] [-t] [-r] [-n responses]"; exit
         | 
| 11 11 | 
             
            end
         | 
| 12 12 |  | 
| 13 13 | 
             
            args = ARGV.dup
         | 
| 14 14 | 
             
            opts_parser = OptionParser.new do |opts|
         | 
| 15 15 | 
             
              opts.on('-s SERVER') { |server| $nats_server = server }
         | 
| 16 | 
            -
              opts.on('-t') { $show_time = true }
         | 
| 16 | 
            +
              opts.on('-t','--time') { $show_time = true }
         | 
| 17 | 
            +
              opts.on('-r','--raw') { $show_raw = true }
         | 
| 17 18 | 
             
              opts.on('-n RESPONSES') { |responses| $responses = Integer(responses) if Integer(responses) > 0 }
         | 
| 18 19 | 
             
            end
         | 
| 19 20 | 
             
            args = opts_parser.parse!(args)
         | 
| @@ -31,11 +32,19 @@ def header | |
| 31 32 | 
             
              "#{time_prefix}[\##{$i+=1}]"
         | 
| 32 33 | 
             
            end
         | 
| 33 34 |  | 
| 35 | 
            +
            def decorate msg
         | 
| 36 | 
            +
              if $show_raw
         | 
| 37 | 
            +
                msg
         | 
| 38 | 
            +
              else
         | 
| 39 | 
            +
                "#{header} Replied with : '#{msg}'"
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 34 43 | 
             
            NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
         | 
| 35 44 |  | 
| 36 45 | 
             
            NATS.start(:uri => $nats_server, :autostart => true) do
         | 
| 37 46 | 
             
              NATS.request(subject, msg) { |(msg, reply)|
         | 
| 38 | 
            -
                puts  | 
| 47 | 
            +
                puts decorate(msg)
         | 
| 39 48 | 
             
                exit! if $responses && ($responses-=1) < 1
         | 
| 40 49 | 
             
              }
         | 
| 41 50 | 
             
            end
         | 
    
        data/bin/nats-sub
    CHANGED
    
    | @@ -7,14 +7,14 @@ require 'nats/client' | |
| 7 7 | 
             
            ['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }
         | 
| 8 8 |  | 
| 9 9 | 
             
            def usage
         | 
| 10 | 
            -
              puts "Usage: nats-sub <subject> [-s server] [-t]"; exit
         | 
| 10 | 
            +
              puts "Usage: nats-sub <subject> [-s server] [-t] [-r]"; exit
         | 
| 11 11 | 
             
            end
         | 
| 12 12 |  | 
| 13 13 | 
             
            args = ARGV.dup
         | 
| 14 14 | 
             
            opts_parser = OptionParser.new do |opts|
         | 
| 15 15 | 
             
              opts.on('-s SERVER') { |server| $nats_server = server }
         | 
| 16 | 
            -
              opts.on('-t') { $show_time = true }
         | 
| 17 | 
            -
             | 
| 16 | 
            +
              opts.on('-t','--time') { $show_time = true }
         | 
| 17 | 
            +
              opts.on('-r','--raw') { $show_raw = true }
         | 
| 18 18 | 
             
            end
         | 
| 19 19 | 
             
            args = opts_parser.parse!(args)
         | 
| 20 20 |  | 
| @@ -30,9 +30,17 @@ def header | |
| 30 30 | 
             
              "#{time_prefix}[\##{$i+=1}]"
         | 
| 31 31 | 
             
            end
         | 
| 32 32 |  | 
| 33 | 
            +
            def decorate sub, msg
         | 
| 34 | 
            +
              if $show_raw
         | 
| 35 | 
            +
                msg
         | 
| 36 | 
            +
              else
         | 
| 37 | 
            +
                "#{header} Received on [#{sub}] : '#{msg}'"
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| 40 | 
            +
             | 
| 33 41 | 
             
            NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
         | 
| 34 42 |  | 
| 35 43 | 
             
            NATS.start(:uri => $nats_server, :autostart => true) do
         | 
| 36 | 
            -
              puts "Listening on [#{subject}]"
         | 
| 37 | 
            -
              NATS.subscribe(subject) { |msg, _, sub| puts  | 
| 44 | 
            +
              puts "Listening on [#{subject}]" unless $show_raw
         | 
| 45 | 
            +
              NATS.subscribe(subject) { |msg, _, sub| puts decorate(sub, msg) }
         | 
| 38 46 | 
             
            end
         | 
    
        data/lib/nats/client.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require 'uri'
         | 
| 2 | 
            +
            require 'securerandom'
         | 
| 2 3 |  | 
| 3 4 | 
             
            ep = File.expand_path(File.dirname(__FILE__))
         | 
| 4 5 |  | 
| @@ -8,7 +9,7 @@ require "#{ep}/ext/json" | |
| 8 9 |  | 
| 9 10 | 
             
            module NATS
         | 
| 10 11 |  | 
| 11 | 
            -
              VERSION =  | 
| 12 | 
            +
              VERSION = "0.5.0.beta.12".freeze
         | 
| 12 13 |  | 
| 13 14 | 
             
              DEFAULT_PORT = 4222
         | 
| 14 15 | 
             
              DEFAULT_URI = "nats://localhost:#{DEFAULT_PORT}".freeze
         | 
| @@ -21,6 +22,10 @@ module NATS | |
| 21 22 | 
             
              # Maximum outbound size per client to trigger FP, 20MB
         | 
| 22 23 | 
             
              FAST_PRODUCER_THRESHOLD = (10*1024*1024)
         | 
| 23 24 |  | 
| 25 | 
            +
              # Ping intervals
         | 
| 26 | 
            +
              DEFAULT_PING_INTERVAL = 120
         | 
| 27 | 
            +
              DEFAULT_PING_MAX = 2
         | 
| 28 | 
            +
             | 
| 24 29 | 
             
              # Protocol
         | 
| 25 30 | 
             
              # @private
         | 
| 26 31 | 
             
              MSG      = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i #:nodoc:
         | 
| @@ -66,6 +71,9 @@ module NATS | |
| 66 71 | 
             
              # When we cannot connect to the server (either initially or after a reconnect), this is raised/passed
         | 
| 67 72 | 
             
              class ConnectError < Error; end #:nodoc:
         | 
| 68 73 |  | 
| 74 | 
            +
              # When we cannot connect to the server because authorization failed.
         | 
| 75 | 
            +
              class AuthError < ConnectError; end #:nodoc:
         | 
| 76 | 
            +
             | 
| 69 77 | 
             
              class << self
         | 
| 70 78 | 
             
                attr_reader   :client, :reactor_was_running, :err_cb, :err_cb_overridden #:nodoc:
         | 
| 71 79 | 
             
                attr_reader   :reconnect_cb  #:nodoc
         | 
| @@ -87,6 +95,8 @@ module NATS | |
| 87 95 | 
             
                # @option opts [Boolean] :ssl Boolean that is sent to server for setting TLS/SSL mode.
         | 
| 88 96 | 
             
                # @option opts [Integer] :max_reconnect_attempts Integer that can be used to set the max number of reconnect tries
         | 
| 89 97 | 
             
                # @option opts [Integer] :reconnect_time_wait Integer that can be used to set the number of seconds to wait between reconnect tries
         | 
| 98 | 
            +
                # @option opts [Integer] :ping_interval Integer that can be used to set the ping interval in seconds.
         | 
| 99 | 
            +
                # @option opts [Integer] :max_outstanding_pings Integer that can be used to set the max number of outstanding pings before declaring a connection closed.
         | 
| 90 100 | 
             
                # @param [Block] &blk called when the connection is completed. Connection will be passed to the block.
         | 
| 91 101 | 
             
                # @return [NATS] connection to the server.
         | 
| 92 102 | 
             
                def connect(opts={}, &blk)
         | 
| @@ -97,6 +107,8 @@ module NATS | |
| 97 107 | 
             
                  opts[:ssl] = false if opts[:ssl].nil?
         | 
| 98 108 | 
             
                  opts[:max_reconnect_attempts] = MAX_RECONNECT_ATTEMPTS if opts[:max_reconnect_attempts].nil?
         | 
| 99 109 | 
             
                  opts[:reconnect_time_wait] = RECONNECT_TIME_WAIT if opts[:reconnect_time_wait].nil?
         | 
| 110 | 
            +
                  opts[:ping_interval] = DEFAULT_PING_INTERVAL if opts[:ping_interval].nil?
         | 
| 111 | 
            +
                  opts[:max_outstanding_pings] = DEFAULT_PING_MAX if opts[:max_outstanding_pings].nil?
         | 
| 100 112 |  | 
| 101 113 | 
             
                  # Override with ENV
         | 
| 102 114 | 
             
                  opts[:uri] ||= ENV['NATS_URI'] || DEFAULT_URI
         | 
| @@ -108,7 +120,19 @@ module NATS | |
| 108 120 | 
             
                  opts[:ssl] = ENV['NATS_SSL'].downcase == 'true' unless ENV['NATS_SSL'].nil?
         | 
| 109 121 | 
             
                  opts[:max_reconnect_attempts] = ENV['NATS_MAX_RECONNECT_ATTEMPTS'].to_i unless ENV['NATS_MAX_RECONNECT_ATTEMPTS'].nil?
         | 
| 110 122 | 
             
                  opts[:reconnect_time_wait] = ENV['NATS_RECONNECT_TIME_WAIT'].to_i unless ENV['NATS_RECONNECT_TIME_WAIT'].nil?
         | 
| 111 | 
            -
             | 
| 123 | 
            +
             | 
| 124 | 
            +
                  opts[:ping_interval] = ENV['NATS_PING_INTERVAL'].to_i unless ENV['NATS_PING_INTERVAL'].nil?
         | 
| 125 | 
            +
                  opts[:max_outstanding_pings] = ENV['NATS_MAX_OUTSTANDING_PINGS'].to_i unless ENV['NATS_MAX_OUTSTANDING_PINGS'].nil?
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  uri = opts[:uris] || opts[:servers] || opts[:uri]
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  # If they pass an array here just pass along to the real connection, and use first as the first attempt..
         | 
| 130 | 
            +
                  # Real connection will do proper walk throughs etc..
         | 
| 131 | 
            +
                  unless uri.nil?
         | 
| 132 | 
            +
                    u = uri.kind_of?(Array) ? uri.first : uri
         | 
| 133 | 
            +
                    @uri = u.is_a?(URI) ? u.dup : URI.parse(u)
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
             | 
| 112 136 | 
             
                  @err_cb = proc { |e| raise e } unless err_cb
         | 
| 113 137 | 
             
                  check_autostart(@uri) if opts[:autostart] == true
         | 
| 114 138 |  | 
| @@ -122,7 +146,7 @@ module NATS | |
| 122 146 | 
             
                def start(*args, &blk)
         | 
| 123 147 | 
             
                  @reactor_was_running = EM.reactor_running?
         | 
| 124 148 | 
             
                  unless (@reactor_was_running || blk)
         | 
| 125 | 
            -
                    raise(Error, "EM needs to be running when NATS.start called without a run block")
         | 
| 149 | 
            +
                    raise(Error, "EM needs to be running when NATS.start is called without a run block")
         | 
| 126 150 | 
             
                  end
         | 
| 127 151 | 
             
                  # Setup optimized select versions
         | 
| 128 152 | 
             
                  EM.epoll; EM.kqueue
         | 
| @@ -172,6 +196,7 @@ module NATS | |
| 172 196 | 
             
                # @param [Block] &callback called when a reconnect attempt is made.
         | 
| 173 197 | 
             
                def on_reconnect(&callback)
         | 
| 174 198 | 
             
                  @reconnect_cb = callback
         | 
| 199 | 
            +
                  @client.on_reconnect(&callback) unless @client.nil?
         | 
| 175 200 | 
             
                end
         | 
| 176 201 |  | 
| 177 202 | 
             
                # Publish a message using the default client connection.
         | 
| @@ -207,9 +232,7 @@ module NATS | |
| 207 232 | 
             
                # Returns a subject that can be used for "directed" communications.
         | 
| 208 233 | 
             
                # @return [String]
         | 
| 209 234 | 
             
                def create_inbox
         | 
| 210 | 
            -
                   | 
| 211 | 
            -
                       rand(0x0010000),rand(0x0010000),rand(0x1000000)]
         | 
| 212 | 
            -
                  "_INBOX.%04x%04x%04x%04x%04x%06x" % v
         | 
| 235 | 
            +
                  "_INBOX.#{SecureRandom.hex(13)}"
         | 
| 213 236 | 
             
                end
         | 
| 214 237 |  | 
| 215 238 | 
             
                # Flushes all messages and subscriptions in the default connection
         | 
| @@ -272,8 +295,8 @@ module NATS | |
| 272 295 |  | 
| 273 296 | 
             
              end
         | 
| 274 297 |  | 
| 275 | 
            -
              attr_reader :connected, :connect_cb, :err_cb, :err_cb_overridden #:nodoc:
         | 
| 276 | 
            -
              attr_reader :closing, :reconnecting, :options, :server_info #:nodoc
         | 
| 298 | 
            +
              attr_reader :connected, :connect_cb, :err_cb, :err_cb_overridden, :pongs_received #:nodoc:
         | 
| 299 | 
            +
              attr_reader :closing, :reconnecting, :server_pool, :options, :server_info #:nodoc
         | 
| 277 300 | 
             
              attr_reader :msgs_received, :msgs_sent, :bytes_received, :bytes_sent, :pings
         | 
| 278 301 |  | 
| 279 302 | 
             
              alias :connected? :connected
         | 
| @@ -281,16 +304,14 @@ module NATS | |
| 281 304 | 
             
              alias :reconnecting? :reconnecting
         | 
| 282 305 |  | 
| 283 306 | 
             
              def initialize(options)
         | 
| 284 | 
            -
                @uri = options[:uri]
         | 
| 285 | 
            -
                @uri.user = options[:user] if options[:user]
         | 
| 286 | 
            -
                @uri.password = options[:pass] if options[:pass]
         | 
| 287 | 
            -
                @ssl = options[:ssl] if options[:ssl]
         | 
| 288 307 | 
             
                @options = options
         | 
| 308 | 
            +
                process_uri_options
         | 
| 309 | 
            +
                @ssl = options[:ssl] if options[:ssl]
         | 
| 289 310 | 
             
                @ssid, @subs = 1, {}
         | 
| 290 311 | 
             
                @err_cb = NATS.err_cb
         | 
| 291 312 | 
             
                @reconnect_timer, @needed = nil, nil
         | 
| 292 313 | 
             
                @reconnect_cb = NATS.reconnect_cb
         | 
| 293 | 
            -
                @connected, @closing, @reconnecting = false, false, false
         | 
| 314 | 
            +
                @connected, @closing, @reconnecting, @conn_cb_called = false, false, false, false
         | 
| 294 315 | 
             
                @msgs_received = @msgs_sent = @bytes_received = @bytes_sent = @pings = 0
         | 
| 295 316 | 
             
                @pending_size = 0
         | 
| 296 317 | 
             
                send_connect_command
         | 
| @@ -418,7 +439,8 @@ module NATS | |
| 418 439 | 
             
              # Close the connection to the server.
         | 
| 419 440 | 
             
              def close
         | 
| 420 441 | 
             
                @closing = true
         | 
| 421 | 
            -
                 | 
| 442 | 
            +
                cancel_ping_timer
         | 
| 443 | 
            +
                cancel_reconnect_timer
         | 
| 422 444 | 
             
                close_connection_after_writing if connected?
         | 
| 423 445 | 
             
                process_disconnect if reconnecting?
         | 
| 424 446 | 
             
              end
         | 
| @@ -432,14 +454,22 @@ module NATS | |
| 432 454 | 
             
                err_cb_overridden || NATS.err_cb_overridden
         | 
| 433 455 | 
             
              end
         | 
| 434 456 |  | 
| 435 | 
            -
              def  | 
| 457 | 
            +
              def auth_connection?
         | 
| 458 | 
            +
                !@uri.user.nil?
         | 
| 459 | 
            +
              end
         | 
| 460 | 
            +
             | 
| 461 | 
            +
              def connect_command #:nodoc:
         | 
| 436 462 | 
             
                cs = { :verbose => @options[:verbose], :pedantic => @options[:pedantic] }
         | 
| 437 | 
            -
                if  | 
| 463 | 
            +
                if auth_connection?
         | 
| 438 464 | 
             
                  cs[:user] = @uri.user
         | 
| 439 465 | 
             
                  cs[:pass] = @uri.password
         | 
| 440 466 | 
             
                end
         | 
| 441 467 | 
             
                cs[:ssl_required] = @ssl if @ssl
         | 
| 442 | 
            -
                 | 
| 468 | 
            +
                "CONNECT #{cs.to_json}#{CR_LF}"
         | 
| 469 | 
            +
              end
         | 
| 470 | 
            +
             | 
| 471 | 
            +
              def send_connect_command #:nodoc:
         | 
| 472 | 
            +
                send_command(connect_command, true)
         | 
| 443 473 | 
             
              end
         | 
| 444 474 |  | 
| 445 475 | 
             
              def queue_server_rt(&cb) #:nodoc:
         | 
| @@ -465,8 +495,6 @@ module NATS | |
| 465 495 | 
             
                  @subs.delete(sid) if (sub[:received] == sub[:max])
         | 
| 466 496 | 
             
                end
         | 
| 467 497 |  | 
| 468 | 
            -
                return unsubscribe(sid) if (sub[:max] && (sub[:received] > sub[:max]))
         | 
| 469 | 
            -
             | 
| 470 498 | 
             
                if cb = sub[:callback]
         | 
| 471 499 | 
             
                  case cb.arity
         | 
| 472 500 | 
             
                    when 0 then cb.call
         | 
| @@ -503,7 +531,13 @@ module NATS | |
| 503 531 | 
             
                      @buf = $'
         | 
| 504 532 | 
             
                    when ERR
         | 
| 505 533 | 
             
                      @buf = $'
         | 
| 506 | 
            -
                       | 
| 534 | 
            +
                      current = server_pool.first
         | 
| 535 | 
            +
                      current[:error_received] = true
         | 
| 536 | 
            +
                      if current[:auth_required] && !current[:auth_ok]
         | 
| 537 | 
            +
                        err_cb.call(NATS::AuthError.new($1))
         | 
| 538 | 
            +
                      else
         | 
| 539 | 
            +
                        err_cb.call(NATS::ServerError.new($1))
         | 
| 540 | 
            +
                      end
         | 
| 507 541 | 
             
                    when PING
         | 
| 508 542 | 
             
                      @pings += 1
         | 
| 509 543 | 
             
                      @buf = $'
         | 
| @@ -547,6 +581,12 @@ module NATS | |
| 547 581 | 
             
                    err_cb.call(NATS::ClientError.new('TLS/SSL not supported by server'))
         | 
| 548 582 | 
             
                  end
         | 
| 549 583 | 
             
                end
         | 
| 584 | 
            +
                if @server_info[:auth_required]
         | 
| 585 | 
            +
                  current = server_pool.first
         | 
| 586 | 
            +
                  current[:auth_required] = true
         | 
| 587 | 
            +
                  queue_server_rt { current[:auth_ok] = true }
         | 
| 588 | 
            +
                  flush_pending
         | 
| 589 | 
            +
                end
         | 
| 550 590 | 
             
                @server_info
         | 
| 551 591 | 
             
              end
         | 
| 552 592 |  | 
| @@ -555,38 +595,103 @@ module NATS | |
| 555 595 | 
             
                flush_pending
         | 
| 556 596 | 
             
              end
         | 
| 557 597 |  | 
| 598 | 
            +
              def cancel_ping_timer
         | 
| 599 | 
            +
                if @ping_timer
         | 
| 600 | 
            +
                  EM.cancel_timer(@ping_timer)
         | 
| 601 | 
            +
                  @ping_timer = nil
         | 
| 602 | 
            +
                end
         | 
| 603 | 
            +
              end
         | 
| 604 | 
            +
             | 
| 558 605 | 
             
              def connection_completed #:nodoc:
         | 
| 559 606 | 
             
                @connected = true unless @ssl
         | 
| 607 | 
            +
             | 
| 608 | 
            +
                current = server_pool.first
         | 
| 609 | 
            +
                current[:was_connected] = true
         | 
| 610 | 
            +
                current[:reconnect_attempts] = 0
         | 
| 611 | 
            +
             | 
| 560 612 | 
             
                if reconnecting?
         | 
| 561 | 
            -
                   | 
| 562 | 
            -
                  send_connect_command
         | 
| 613 | 
            +
                  cancel_reconnect_timer
         | 
| 563 614 | 
             
                  @subs.each_pair { |k, v| send_command("SUB #{v[:subject]} #{v[:queue]} #{k}#{CR_LF}") }
         | 
| 564 615 | 
             
                end
         | 
| 565 | 
            -
             | 
| 616 | 
            +
             | 
| 566 617 | 
             
                unless user_err_cb? or reconnecting?
         | 
| 567 618 | 
             
                  @err_cb = proc { |e| raise e }
         | 
| 568 619 | 
             
                end
         | 
| 569 | 
            -
             | 
| 620 | 
            +
             | 
| 621 | 
            +
                flush_pending unless @ssl
         | 
| 622 | 
            +
             | 
| 623 | 
            +
                if (connect_cb and not @conn_cb_called)
         | 
| 570 624 | 
             
                  # We will round trip the server here to make sure all state from any pending commands
         | 
| 571 625 | 
             
                  # has been processed before calling the connect callback.
         | 
| 572 | 
            -
                  queue_server_rt  | 
| 626 | 
            +
                  queue_server_rt do
         | 
| 627 | 
            +
                    connect_cb.call(self)
         | 
| 628 | 
            +
                    @conn_cb_called = true
         | 
| 629 | 
            +
                  end
         | 
| 573 630 | 
             
                end
         | 
| 574 631 | 
             
                @reconnecting = false
         | 
| 575 632 | 
             
                @parse_state = AWAITING_CONTROL_LINE
         | 
| 633 | 
            +
             | 
| 634 | 
            +
                # Initialize ping timer and processing
         | 
| 635 | 
            +
                @pings_outstanding = 0
         | 
| 636 | 
            +
                @pongs_received = 0
         | 
| 637 | 
            +
                @ping_timer = EM.add_periodic_timer(@options[:ping_interval]) { send_ping }
         | 
| 638 | 
            +
              end
         | 
| 639 | 
            +
             | 
| 640 | 
            +
              def send_ping #:nodoc:
         | 
| 641 | 
            +
                return if @closing
         | 
| 642 | 
            +
                if @pings_outstanding > @options[:max_outstanding_pings]
         | 
| 643 | 
            +
                  close_connection
         | 
| 644 | 
            +
                  #close
         | 
| 645 | 
            +
                  return
         | 
| 646 | 
            +
                end
         | 
| 647 | 
            +
                @pings_outstanding += 1
         | 
| 648 | 
            +
                queue_server_rt { process_pong }
         | 
| 649 | 
            +
                flush_pending
         | 
| 650 | 
            +
              end
         | 
| 651 | 
            +
             | 
| 652 | 
            +
              def process_pong
         | 
| 653 | 
            +
                @pongs_received += 1
         | 
| 654 | 
            +
                @pings_outstanding -= 1
         | 
| 655 | 
            +
              end
         | 
| 656 | 
            +
             | 
| 657 | 
            +
              def should_delay_connect?(server)
         | 
| 658 | 
            +
                server[:was_connected] && server[:reconnect_attempts] >= 1
         | 
| 576 659 | 
             
              end
         | 
| 577 660 |  | 
| 578 | 
            -
              def schedule_reconnect | 
| 661 | 
            +
              def schedule_reconnect #:nodoc:
         | 
| 579 662 | 
             
                @reconnecting = true
         | 
| 580 | 
            -
                @reconnect_attempts = 0
         | 
| 581 663 | 
             
                @connected = false
         | 
| 582 | 
            -
                @reconnect_timer = EM. | 
| 664 | 
            +
                @reconnect_timer = EM.add_timer(@options[:reconnect_time_wait]) { attempt_reconnect }
         | 
| 583 665 | 
             
              end
         | 
| 584 666 |  | 
| 585 667 | 
             
              def unbind #:nodoc:
         | 
| 586 | 
            -
                 | 
| 587 | 
            -
             | 
| 588 | 
            -
             | 
| 589 | 
            -
             | 
| 668 | 
            +
                # If we are closing or shouldn't reconnect, go ahead and disconnect.
         | 
| 669 | 
            +
                process_disconnect and return if (closing? or should_not_reconnect?)
         | 
| 670 | 
            +
             | 
| 671 | 
            +
                @reconnecting = true if connected?
         | 
| 672 | 
            +
                @connected = false
         | 
| 673 | 
            +
                @pending = @pongs = nil
         | 
| 674 | 
            +
                cancel_ping_timer
         | 
| 675 | 
            +
             | 
| 676 | 
            +
                schedule_primary_and_connect
         | 
| 677 | 
            +
              end
         | 
| 678 | 
            +
             | 
| 679 | 
            +
              def multiple_servers_available?
         | 
| 680 | 
            +
                server_pool && server_pool.size > 1
         | 
| 681 | 
            +
              end
         | 
| 682 | 
            +
             | 
| 683 | 
            +
              def had_error?
         | 
| 684 | 
            +
                server_pool.first && server_pool.first[:error_received]
         | 
| 685 | 
            +
              end
         | 
| 686 | 
            +
             | 
| 687 | 
            +
              def should_not_reconnect?
         | 
| 688 | 
            +
                !@options[:reconnect]
         | 
| 689 | 
            +
              end
         | 
| 690 | 
            +
             | 
| 691 | 
            +
              def cancel_reconnect_timer
         | 
| 692 | 
            +
                if @reconnect_timer
         | 
| 693 | 
            +
                  EM.cancel_timer(@reconnect_timer)
         | 
| 694 | 
            +
                  @reconnect_timer = nil
         | 
| 590 695 | 
             
                end
         | 
| 591 696 | 
             
              end
         | 
| 592 697 |  | 
| @@ -597,18 +702,26 @@ module NATS | |
| 597 702 |  | 
| 598 703 | 
             
              def process_disconnect #:nodoc:
         | 
| 599 704 | 
             
                err_cb.call(NATS::ConnectError.new(disconnect_error_string)) if not closing? and @err_cb
         | 
| 705 | 
            +
                true # Chaining
         | 
| 600 706 | 
             
              ensure
         | 
| 601 | 
            -
                 | 
| 707 | 
            +
                cancel_ping_timer
         | 
| 708 | 
            +
                cancel_reconnect_timer
         | 
| 602 709 | 
             
                if (NATS.client == self)
         | 
| 603 710 | 
             
                  NATS.clear_client
         | 
| 604 711 | 
             
                  EM.stop if ((connected? || reconnecting?) and closing? and not NATS.reactor_was_running?)
         | 
| 605 712 | 
             
                end
         | 
| 606 713 | 
             
                @connected = @reconnecting = false
         | 
| 607 | 
            -
             | 
| 714 | 
            +
              end
         | 
| 715 | 
            +
             | 
| 716 | 
            +
              def can_reuse_server?(server) #:nodoc:
         | 
| 717 | 
            +
                reconnecting? && server[:was_connected] && server[:reconnect_attempts] <= @options[:max_reconnect_attempts]
         | 
| 608 718 | 
             
              end
         | 
| 609 719 |  | 
| 610 720 | 
             
              def attempt_reconnect #:nodoc:
         | 
| 611 | 
            -
                 | 
| 721 | 
            +
                @reconnect_timer = nil
         | 
| 722 | 
            +
                current = server_pool.first
         | 
| 723 | 
            +
                current[:reconnect_attempts] += 1 if current[:reconnect_attempts]
         | 
| 724 | 
            +
                send_connect_command
         | 
| 612 725 | 
             
                EM.reconnect(@uri.host, @uri.port, self)
         | 
| 613 726 | 
             
                @reconnect_cb.call unless @reconnect_cb.nil?
         | 
| 614 727 | 
             
              end
         | 
| @@ -626,6 +739,47 @@ module NATS | |
| 626 739 | 
             
                true
         | 
| 627 740 | 
             
              end
         | 
| 628 741 |  | 
| 742 | 
            +
              # Parse out URIs which can now be an array of server choices
         | 
| 743 | 
            +
              # The server pool will contain both explicit and implicit members.
         | 
| 744 | 
            +
              def process_uri_options #:nodoc
         | 
| 745 | 
            +
                @server_pool = []
         | 
| 746 | 
            +
                uri = options[:uris] || options[:servers] || options[:uri]
         | 
| 747 | 
            +
                uri = uri.kind_of?(Array) ? uri : [uri]
         | 
| 748 | 
            +
                uri.each { |u| server_pool << { :uri => u.is_a?(URI) ? u.dup : URI.parse(u) } }
         | 
| 749 | 
            +
                server_pool.shuffle! unless options[:dont_randomize_servers]
         | 
| 750 | 
            +
                bind_primary
         | 
| 751 | 
            +
              end
         | 
| 752 | 
            +
             | 
| 753 | 
            +
              def connected_server
         | 
| 754 | 
            +
                connected? ? @uri : nil
         | 
| 755 | 
            +
              end
         | 
| 756 | 
            +
             | 
| 757 | 
            +
              def bind_primary #:nodoc:
         | 
| 758 | 
            +
                first = server_pool.first
         | 
| 759 | 
            +
                @uri = first[:uri]
         | 
| 760 | 
            +
                @uri.user = options[:user] if options[:user]
         | 
| 761 | 
            +
                @uri.password = options[:pass] if options[:pass]
         | 
| 762 | 
            +
                first
         | 
| 763 | 
            +
              end
         | 
| 764 | 
            +
             | 
| 765 | 
            +
              # We have failed on an attempt at the primary (first) server, rotate and try again
         | 
| 766 | 
            +
              def schedule_primary_and_connect #:nodoc:
         | 
| 767 | 
            +
                # Dump the one we were trying if it wasn't connected
         | 
| 768 | 
            +
                current = server_pool.shift
         | 
| 769 | 
            +
                server_pool << current if (current && can_reuse_server?(current) && !current[:error_received])
         | 
| 770 | 
            +
                # If we are out of options, go ahead and disconnect.
         | 
| 771 | 
            +
                process_disconnect and return if server_pool.empty?
         | 
| 772 | 
            +
                # bind new one
         | 
| 773 | 
            +
                next_server = bind_primary
         | 
| 774 | 
            +
                # If the next one was connected and we are trying to reconnect
         | 
| 775 | 
            +
                # set up timer if we tried once already.
         | 
| 776 | 
            +
                if should_delay_connect?(next_server)
         | 
| 777 | 
            +
                  schedule_reconnect
         | 
| 778 | 
            +
                else
         | 
| 779 | 
            +
                  attempt_reconnect
         | 
| 780 | 
            +
                end
         | 
| 781 | 
            +
              end
         | 
| 782 | 
            +
             | 
| 629 783 | 
             
              def inspect #:nodoc:
         | 
| 630 784 | 
             
                "<nats client v#{NATS::VERSION}>"
         | 
| 631 785 | 
             
              end
         |