[
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Discussion\n    url: https://github.com/nats-io/nats.rb/discussions\n    about: Ideal for ideas, feedback, or longer form questions.\n  - name: Chat\n    url: https://slack.nats.io\n    about: Ideal for short, one-off questions, general conversation, and meeting other NATS users!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/defect.yml",
    "content": "---\nname: Defect\ndescription: Report a defect, such as a bug or regression.\nlabels:\n  - defect\nbody:\n  - type: textarea\n    id: versions\n    attributes:\n      label: What version were you using?\n      description: Include the server version (`nats-server --version`) and any client versions when observing the issue.\n    validations:\n      required: true\n  - type: textarea\n    id: environment\n    attributes:\n      label: What environment was the server running in?\n      description: This pertains to the operating system, CPU architecture, and/or Docker image that was used.\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Is this defect reproducible?\n      description: Provide best-effort steps to showcase the defect.\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Given the capability you are leveraging, describe your expectation?\n      description: This may be the expected behavior or performance characteristics.\n    validations:\n      required: true\n  - type: textarea\n    id: actual\n    attributes:\n      label: Given the expectation, what is the defect you are observing?\n      description: This may be an unexpected behavior or regression in performance.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/proposal.yml",
    "content": "---\nname: Proposal\ndescription: Propose an enhancement or new feature.\nlabels:\n  - proposal\nbody:\n  - type: textarea\n    id: usecase\n    attributes:\n      label: What motivated this proposal?\n      description: Describe the use case justifying this request.\n    validations:\n      required: true\n  - type: textarea\n    id: change\n    attributes:\n      label: What is the proposed change?\n      description: This could be a behavior change, enhanced API, or a branch new feature.\n    validations:\n      required: true\n  - type: textarea\n    id: benefits\n    attributes:\n      label: Who benefits from this change?\n      description: Describe how this not only benefits you.\n    validations:\n      required: false\n  - type: textarea\n    id: alternates\n    attributes:\n      label: What alternatives have you evaluated?\n      description: This could be using existing features or relying on an external dependency.\n    validations:\n      required: false\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n\\#*\\#\n.\\#*\n*.rbc\n.rbx\n.bundle\n*.gem\n.DS_Store\n.idea\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: ruby\n\nrvm:\n  - 2.7\n\ncache:\n  directories:\n  - $HOME/nats-server\n\nbefore_install:\n  - bash ./scripts/install_gnatsd.sh\n\nbefore_script:\n  - export PATH=$HOME/nats-server:$PATH\n\nsudo: required\ndist: xenial\nbundler_args: --without server\n"
  },
  {
    "path": "CODE-OF-CONDUCT.md",
    "content": "## Community Code of Conduct\n\nNATS follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).\n"
  },
  {
    "path": "GOVERNANCE.md",
    "content": "# NATS Ruby Client Governance\n\nNATS Ruby Client is part of the NATS project and is subject to the [NATS Governance](https://github.com/nats-io/nats-general/blob/master/GOVERNANCE.md)."
  },
  {
    "path": "Gemfile",
    "content": "source \"http://rubygems.org\"\n\ngemspec\n\ngroup :test do\n  gem 'rake'\n  gem 'rspec'\nend\n\ngroup :server do\n  gem 'daemons'\n  gem 'json_pure'\n  gem 'thin'\n  gem 'rack', \">= 2.0.6\"\nend\n\ngroup :v2 do\n  gem 'nkeys'\nend\n"
  },
  {
    "path": "HISTORY.md",
    "content": "# HISTORY\n\n## v0.11.0 (June 10, 2019)\n  - NATS v2.0 support! (#162)\n\n## v0.8.4 (Feb 23, 2018)\n  - Support to include connection `name` as part of CONNECT options (#145)\n  - Fixed support for Ruby 2.5 due to missing OpenSSL `require` (#144)\n\n## v0.8.2 (March 14, 2017)\n  - Allow setting name from client on connect (#129)\n  - Add discovered servers helper for servers announced via async INFO (#136)\n  - Add time based reconnect backoff (#139)\n  - Modify lang sent on connect when using jruby (#135)\n  - Update eventmachine dependencies (#134)\n\n## v0.8.0 (August 10, 2016)\n  - Added cluster auto discovery handling which is supported on v0.9.2 server release (#125)\n  - Added jruby part of the build (both in openjdk and oraclejdk runtimes) (#122 #123)\n  - Fixed ping interval accounting (#120)\n\n## v0.7.1 (July 8, 2016)\n  - Remove dependencies which are no longer needed for ruby-client\n  - See full list @ https://github.com/nats-io/ruby-nats/compare/v0.7.0...v0.7.1\n\n## v0.7.0 (July 8, 2016)\n  - Enhanced TLS support: certificates and verify peer functionality added\n  - Bumped version of Eventmachine to 1.2 series\n  - See full list @ https://github.com/nats-io/ruby-nats/compare/v0.6.0...v0.7.0\n\n## v0.6.0 (March 22, 2016)\n  - Removed distributing `nats-server` along with the gem\n  - Fixed issue with subscriptions not being sent on first reconnect (#94)\n  - Added loading Oj gem for JSON when supported (#91)\n  - Fixed removing warning message introduced by EM 1.0.8 (#90)\n  - Changed to testing spec with `gnatsd` (#95)\n  - See full list @ https://github.com/nats-io/ruby-nats/compare/v0.5.1...v0.6.0\n\n## v0.5.1 (August 7, 2015)\n  - Changed to never remove servers when configured as such (#88)\n  - See full list @ https://github.com/nats-io/ruby-nats/compare/v0.5.0...v0.5.1\n\n## v0.5.0 (June 19, 2015)\n  - See full list @ https://github.com/nats-io/ruby-nats/compare/v0.5.0.beta.16...v0.5.0\n\n## v0.5.0.beta.16 (December 7, 2014)\n  - Resolved major issue on cluster connects to non-first server, issue #78\n  - Official Support for Ruby 2.1\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.5.0.beta.12...v0.5.0.beta.16\n\n## v0.5.0.beta.12 (October 1, 2013)\n  - Fixed issue #58, reconnects not stopped on auth failures\n  - Fixed leaking ping timers on auth failures\n  - Created AuthError\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.5.0.beta.11...v0.5.0.beta.12\n\n## v0.5.0.beta.11 (July 26, 2013)\n  - Bi-directional Route designation\n  - Upgrade to EM 1.x\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.5.0.beta.1...v0.5.0.beta.11\n\n## v0.5.0.beta.1 (Sept 10, 2012)\n  - Clustering support for nats-servers\n  - Reconnect client logic cluster aware (explicit servers only for now)\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.26...v0.5.0.beta.1\n\n## v0.4.28 (September 22, 2012)\n  - Binary payload bug fix\n  - Lock EM to version 0.12.10, 1.0 does not pass tests currently.\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.26...v0.4.28\n\n## v0.4.26 (July 30, 2012)\n  - Syslog support\n  - Fixed reconnect bug to authorized servers\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.24...v0.4.26\n\n## v0.4.24 (May 24, 2012)\n\n  - Persist queue groups across reconnects\n  - Proper exit codes for nats-server\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.22...v0.4.24\n\n## v0.4.22 (Mar 5, 2012)\n\n  - HTTP based server monitoring (/varz, /connz, /healthz)\n  - Perfomance and Stability improvements\n  - Client monitoring\n  - Server to Client pings\n  - Multiple Auth users\n  - SSL/TSL support\n  - nats-top utility\n  - Connection state dump on SIGUSR2\n  - Client Server information support\n  - Client Fast Producer support\n  - Client reconenct callbacks\n  - Server Max Connections support\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.10...v0.4.22\n\n## v0.4.10 (Apr 21, 2011)\n\n  - Minor bug fixes\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.8...v0.4.10\n\n## v0.4.8 (Apr 2, 2011)\n\n  - Minor bug fixes\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.4.2...v0.4.8\n\n## v0.4.2 (Feb 21, 2011)\n\n  - Queue group support\n  - Auto-unsubscribe support\n  - Time expiration on subscriptions\n  - Jruby initial support\n  - Performance enhancements\n  - Complete config file support\n  - See full list @ https://github.com/derekcollison/nats/compare/v0.3.12...v0.4.2\n\n## v0.3.12 (Nov 21, 2010)\n\n  - Initial Release\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nMaintainership is on a per project basis.\n\n### Maintainers\n  - Derek Collison <derek@nats.io> [@derekcollison](https://github.com/derekcollison)\n  - Waldemar Quevedo <wally@nats.io> [@wallyqs](https://github.com/wallyqs)\n"
  },
  {
    "path": "README.md",
    "content": "# NATS - Ruby Client\n\nA [Ruby](http://ruby-lang.org) client for the [NATS messaging system](https://nats.io).\n\n[![License Apache 2.0](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n[![Build Status](https://app.travis-ci.com/nats-io/nats.rb.svg?branch=master)](https://app.travis-ci.com/nats-io/nats.rb)\n[![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&type=5&v=0.11.0)](https://rubygems.org/gems/nats/versions/0.11.0)\n[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/nats)\n\n## Getting Started\n\n```bash\ngem install nats\n\nnats-sub foo &\nnats-pub foo 'Hello World!'\n```\n\nStarting from [v0.11.0](https://github.com/nats-io/nats.rb/releases/tag/v0.11.0) release,\nyou can also optionally install [NKEYS](https://github.com/nats-io/nkeys.rb) in order to use\nthe new NATS v2.0 auth features:\n\n```bash\ngem install nkeys\n```\n\nIf you're looking for a non-EventMachine alternative, check out the [nats-pure](https://github.com/nats-io/nats-pure.rb) gem.\n\n## Basic Usage\n\n```ruby\nrequire \"nats/client\"\n\nNATS.start do\n\n  # Simple Subscriber\n  NATS.subscribe('foo') { |msg| puts \"Msg received : '#{msg}'\" }\n\n  # Simple Publisher\n  NATS.publish('foo.bar.baz', 'Hello World!')\n\n  # Unsubscribing\n  sid = NATS.subscribe('bar') { |msg| puts \"Msg received : '#{msg}'\" }\n  NATS.unsubscribe(sid)\n\n  # Requests\n  NATS.request('help') { |response| puts \"Got a response: '#{response}'\" }\n\n  # Replies\n  NATS.subscribe('help') { |msg, reply| NATS.publish(reply, \"I'll help!\") }\n\n  # Stop using NATS.stop, exits EM loop if NATS.start started the loop\n  NATS.stop\n\nend\n```\n\n## Wildcard Subscriptions\n\n```ruby\n# \"*\" matches any token, at any level of the subject.\nNATS.subscribe('foo.*.baz') { |msg, reply, sub| puts \"Msg received on [#{sub}] : '#{msg}'\" }\nNATS.subscribe('foo.bar.*') { |msg, reply, sub| puts \"Msg received on [#{sub}] : '#{msg}'\" }\nNATS.subscribe('*.bar.*')   { |msg, reply, sub| puts \"Msg received on [#{sub}] : '#{msg}'\" }\n\n# \">\" matches any length of the tail of a subject and can only be the last token\n# E.g. 'foo.>' will match 'foo.bar', 'foo.bar.baz', 'foo.foo.bar.bax.22'\nNATS.subscribe('foo.>') { |msg, reply, sub| puts \"Msg received on [#{sub}] : '#{msg}'\" }\n```\n\n## Queues Groups\n\n```ruby\n# All subscriptions with the same queue name will form a queue group\n# Each message will be delivered to only one subscriber per queue group, queuing semantics\n# You can have as many queue groups as you wish\n# Normal subscribers will continue to work as expected.\nNATS.subscribe(subject, :queue => 'job.workers') { |msg| puts \"Received '#{msg}'\" }\n```\n\n## Clustered Usage\n\n```ruby\nNATS.start(:servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223']) do |nc|\n  puts \"NATS is connected to #{nc.connected_server}\"\n\n  nc.on_reconnect do\n    puts \"Reconnected to server at #{nc.connected_server}\"\n  end\n\n  nc.on_disconnect do |reason|\n    puts \"Disconnected: #{reason}\"\n  end\n\n  nc.on_close do\n    puts \"Connection to NATS closed\"\n  end\nend\n\nopts = {\n  :dont_randomize_servers => true,\n  :reconnect_time_wait => 0.5,\n  :max_reconnect_attempts => 10,\n  :servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223', 'nats://127.0.0.1:4224']\n}\n\nNATS.connect(opts) do |c|\n  puts \"NATS is connected!\"\nend\n```\n\n### Auto discovery\n\nThe client also auto discovers new nodes announced by the server as\nthey attach to the cluster.  Reconnection logic parameters such as\ntime to back-off on failure and max attempts apply the same to both\ndiscovered nodes and those defined explicitly on connect:\n\n```ruby\nopts = {\n  :dont_randomize_servers => true,\n  :reconnect_time_wait => 0.5,\n  :max_reconnect_attempts => 10,\n  :servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'],\n  :user => 'secret',\n  :pass => 'deadbeef'\n}\n\nNATS.connect(opts) do |c|\n  # Confirm number of available servers in cluster.\n  puts \"Connected to NATS! Servers in pool: #{c.server_pool.count}\"\nend\n```\n\n## Advanced Usage\n\n```ruby\n# Publish with closure, callback fires when server has processed the message\nNATS.publish('foo', 'You done?') { puts 'msg processed!' }\n\n# Timeouts for subscriptions\nsid = NATS.subscribe('foo') { received += 1 }\nNATS.timeout(sid, TIMEOUT_IN_SECS) { timeout_recvd = true }\n\n# Timeout unless a certain number of messages have been received\nNATS.timeout(sid, TIMEOUT_IN_SECS, :expected => 2) { timeout_recvd = true }\n\n# Auto-unsubscribe after MAX_WANTED messages received\nNATS.unsubscribe(sid, MAX_WANTED)\n\n# Multiple connections\nNATS.subscribe('test') do |msg|\n  puts \"received msg\"\n  \n  # Gracefully disconnect from NATS after handling\n  # messages that have already been delivered by server.\n  NATS.drain\nend\n\n# Form second connection to send message on\nNATS.connect { NATS.publish('test', 'Hello World!') }\n```\n\nSee examples and benchmarks for more information..\n\n### TLS\n\nAdvanced customizations options for setting up a secure connection can\nbe done by including them on connect:\n\n```ruby\noptions = {\n  :servers => [\n   'nats://secret:deadbeef@127.0.0.1:4443',\n   'nats://secret:deadbeef@127.0.0.1:4444'\n  ],\n  :max_reconnect_attempts => 10,\n  :reconnect_time_wait => 2,\n  :tls => {\n    :private_key_file => './spec/configs/certs/key.pem',\n    :cert_chain_file  => './spec/configs/certs/server.pem'\n    # Can enable verify_peer functionality optionally by passing\n    # the location of a ca_file.\n    # :verify_peer => true,\n    # :ca_file => './spec/configs/certs/ca.pem'\n  }\n}\n\n# Set default callbacks\nNATS.on_error do |e|\n  puts \"Error: #{e}\"\nend\n\nNATS.on_disconnect do |reason|\n  puts \"Disconnected: #{reason}\"\nend\n\nNATS.on_reconnect do |nats|\n  puts \"Reconnected to NATS server at #{nats.connected_server}\"\nend\n\nNATS.on_close do\n  puts \"Connection to NATS closed\"\n  EM.stop\nend\n\nNATS.start(options) do |nats|\n  puts \"Connected to NATS at #{nats.connected_server}\"\n\n  nats.subscribe(\"hello\") do |msg|\n    puts \"Received: #{msg}\"\n  end\n\n  nats.flush do\n    nats.publish(\"hello\", \"world\")\n  end\nend\n```\n\n### Fibers\n\nRequests without a callback can be made to work synchronously and return \nthe result when running in a Fiber.  For these type of requests, it is\npossible to set a timeout of how long to wait for a single or multiple\nresponses.\n\n```ruby\nNATS.start {\n\n  NATS.subscribe('help') do |msg, reply|\n    puts \"[Received]: <<- #{msg}\"\n    NATS.publish(reply, \"I'll help! - #{msg}\")\n  end\n\n  NATS.subscribe('slow') do |msg, reply|\n    puts \"[Received]: <<- #{msg}\"\n    EM.add_timer(1) { NATS.publish(reply, \"I'll help! - #{msg}\") }\n  end\n\n  10.times do |n|\n    NATS.subscribe('hi') do |msg, reply|\n      NATS.publish(reply, \"Hello World! - id:#{n}\")\n    end\n  end\n\n  Fiber.new do\n    # Requests work synchronously within the same Fiber\n    # returning the message when done.\n    response = NATS.request('help', 'foo')\n    puts \"[Response]: ->> '#{response}'\"\n\n    # Specifying a custom timeout to give up waiting for\n    # a response.\n    response = NATS.request('slow', 'bar', timeout: 2)\n    if response.nil?\n      puts \"No response after 2 seconds...\"\n    else\n      puts \"[Response]: ->> '#{response}'\"\n    end\n\n    # Can gather multiple responses with the same request\n    # which will then return a collection with the responses\n    # that were received before the timeout.\n    responses = NATS.request('hi', 'quux', max: 10, timeout: 1)\n    responses.each_with_index do |response, i|\n      puts \"[Response# #{i}]: ->> '#{response}'\"\n    end\n    \n    # If no replies then an empty collection is returned.\n    responses = NATS.request('nowhere', '', max: 10, timeout: 2)\n    if responses.any?\n      puts \"Got #{responses.count} responses\"\n    else\n      puts \"No response after 2 seconds...\"\n    end\n\n    NATS.stop\n  end.resume\n\n  # Multiple fibers can make requests concurrently\n  # under the same Eventmachine loop.\n  Fiber.new do\n    10.times do |n|\n      response = NATS.request('help', \"help.#{n}\")\n      puts \"[Response]: ->> '#{response}'\"\n    end\n  end.resume\n}\n```\n\n### New Authentication (Nkeys and User Credentials)\n\nThis requires server with version >= 2.0.0\n\nNATS servers have a new security and authentication mechanism to authenticate with user credentials and NKEYS. A single file containing the JWT and NKEYS to authenticate against a NATS v2 server can be set with the `user_credentials` option:\n\n```ruby\nrequire 'nats/client'\n\nNATS.start(\"tls://connect.ngs.global\", user_credentials: \"/path/to/creds\") do |nc|\n  nc.subscribe(\"hello\") do |msg|\n    puts \"[Received] #{msg}\"\n  end\n  nc.publish('hello', 'world')\nend\n```\n\nThis will create two callback handlers to present the user JWT and sign the nonce challenge from the server. The core client library never has direct access to your private key and simply performs the callback for signing the server challenge. The library will load and wipe and clear the objects it uses for each connect or reconnect.\n\nBare NKEYS are also supported. The nkey seed should be in a read only file, e.g. `seed.txt`.\n\n```bash\n> cat seed.txt\n# This is my seed nkey!\nSUAGMJH5XLGZKQQWAWKRZJIGMOU4HPFUYLXJMXOO5NLFEO2OOQJ5LPRDPM\n```\n\nThen in the client specify the path to the seed using the `nkeys_seed` option:\n\n```ruby\nrequire 'nats/client'\n\nNATS.start(\"tls://connect.ngs.global\", nkeys_seed: \"path/to/seed.txt\") do |nc|\n  nc.subscribe(\"hello\") do |msg|\n    puts \"[Received] #{msg}\"\n  end\n  nc.publish('hello', 'world')\nend\n\n```\n\n## License\n\nUnless otherwise noted, the NATS source files are distributed under\nthe Apache Version 2.0 license found in the LICENSE file.\n"
  },
  {
    "path": "Rakefile",
    "content": "#!/usr/bin/env rake\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'rspec/core'\nrequire 'rspec/core/rake_task'\n\ntask :default => 'spec:client'\n\ndesc 'Run specs from client and server'\nRSpec::Core::RakeTask.new(:spec) do |spec|\n  spec.pattern = FileList['spec/**/*_spec.rb']\n  spec.rspec_opts = [\"--format\", \"documentation\", \"--colour\", \"--profile\"]\nend\n\ndesc 'Run spec from client using gnatsd as the server'\nRSpec::Core::RakeTask.new('spec:client') do |spec|\n  spec.pattern = FileList['spec/client/*_spec.rb']\n  spec.rspec_opts = [\"--format\", \"documentation\", \"--colour\", \"--profile\"]\nend\n\ndesc 'Run spec from client on jruby using gnatsd as the server'\nRSpec::Core::RakeTask.new('spec:client:jruby') do |spec|\n  spec.pattern = FileList['spec/client/*_spec.rb']\n  spec.rspec_opts = [\"--format\", \"documentation\", \"--colour\", \"--tag\", \"~jruby_excluded\", \"--profile\"]\nend\n\ndesc 'Run spec from server'\nRSpec::Core::RakeTask.new('spec:server') do |spec|\n  spec.pattern = FileList['spec/server/*_spec.rb']\n  spec.rspec_opts = [\"--format\", \"documentation\", \"--colour\"]\nend\n\ndesc \"Build the gem\"\ntask :gem do\n  sh 'gem build *.gemspec'\nend\n\ndesc \"Install the gem\"\ntask :geminstall do\n  sh 'gem build *.gemspec'\n  sh 'gem install *.gem'\n  sh 'rm *.gem'\nend\n\ndesc \"Synonym for spec\"\ntask :test => :spec\ndesc \"Synonym for spec\"\ntask :tests => :spec\n\ndesc \"Synonym for gem\"\ntask :pkg => :gem\ndesc \"Synonym for gem\"\ntask :package => :gem\n"
  },
  {
    "path": "TODO",
    "content": "\n- [DONE] Contributing guidelines\n- [DONE] cluster tests for travis-ci\n\n- [DONE] clustering/routing\n  - [DONE] Allow implicit route information to propagate to clients?\n  - [DONE] Check with EM 1.0 not working\n  - [DONE] Client ping timers\n  - [DONE] Retry timers on routes\n  - [DONE] Random on cluster client server pool\n\n- do client blowup detection from sending in tight loop\n- allow port to also be listen_port in config file\n- queue groups allow selection, e.g. round robin vs random?\n- Logger rotation should work\n\n- [DONE] Allow clients to specify app/client name\n- [DONE] tests!\n- [DONE] JRuby support - works if you hand run servers for tests\n- [DONE] Add a disconnect cb\n- [DONE] balance receive perf with send perf (EM issue, iovec)\n- [DONE] Time interval Pings from server..\n- [DONE] Ping/Pong time window\n- [DONE] monitoring\n- [DONE] Allow monitoring to specify on net param\n- [DONE] /connz info\n- [DONE] Don't truncate log on startup\n- [DONE] Connection queue size dump to log for SIGURS2\n- [DONE] Fixed # of replies? sub foo [2], server closes subscription automatically, avoid overwhelming client connection?\n- [DONE] Request timeout option.\n- [DONE] proper flow control, EM and PING/PONG\n- [DONE] request/response automated, just use UUID now.\n- [DONE] better reply semantics in protocol.\n- [DONE] daemon mode\n- [DONE] autolaunch\n- [DONE] Queue groups\n"
  },
  {
    "path": "benchmark/latency_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\n\n$LOAD_PATH << File.expand_path('../../lib', __FILE__)\nrequire 'nats/client'\n\n$loop = 10000\n$hash = 1000\n$sub  = 'test'\n\nSTDOUT.sync = true\n\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: latency_perf [options]\"\n\n  opts.separator \"\"\n  opts.separator \"options:\"\n\n  opts.on(\"-n ITERATIONS\", \"iterations to expect (default: #{$loop})\") { |iter| $loop = iter.to_i }\nend\n\nparser.parse(ARGV)\n$drain = $loop\n\ntrap(\"TERM\") { exit! }\ntrap(\"INT\")  { exit! }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n\n  def done\n    ms = \"%.2f\" % (((Time.now-$start)/$loop)*1000.0)\n    puts \"\\nTest completed : #{ms} ms avg request/response latency\\n\"\n    NATS.stop\n  end\n\n  def send_request\n    s = NATS.request('test') {\n      $drain-=1\n      if $drain == 0\n        done\n      else\n        send_request\n        printf('+') if $drain.modulo($hash) == 0\n      end\n      NATS.unsubscribe(s)\n    }\n  end\n\n  s_conn = NATS.connect\n  s_conn.subscribe('test') do |msg, reply|\n    s_conn.publish(reply)\n  end\n\n\n  # Send first request when we are connected with subscriber\n  s_conn.on_connect {\n    puts \"Sending #{$loop} request/responses\"\n    $start = Time.now\n    send_request\n  }\n\nend\n"
  },
  {
    "path": "benchmark/pub_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\n\n$:.unshift File.expand_path('../../lib', __FILE__)\nrequire 'nats/client'\n\n$count = 100000\n$batch = 100\n\n$delay = 0.00001\n$dmin  = 0.00001\n\nTRIP  = (2*1024*1024)\nTSIZE = 4*1024\n\n$sub  = 'test'\n$data_size = 16\n\n$hash  = 2500\n\nSTDOUT.sync = true\n\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: pub_perf [options]\"\n\n  opts.separator \"\"\n  opts.separator \"options:\"\n\n  opts.on(\"-n COUNT\",   \"Messages to send (default: #{$count}}\") { |count| $count = count.to_i }\n  opts.on(\"-s SIZE\",    \"Message size (default: #{$data_size})\") { |size| $data_size = size.to_i }\n  opts.on(\"-S SUBJECT\", \"Send subject (default: (#{$sub})\")      { |sub| $sub = sub }\n  opts.on(\"-b BATCH\",   \"Batch size (default: (#{$batch})\")      { |batch| $batch = batch.to_i }\nend\n\nparser.parse(ARGV)\n\ntrap(\"TERM\") { exit! }\ntrap(\"INT\")  { exit! }\n\nNATS.on_error { |err| puts \"Error: #{err}\"; exit! }\n\n$data = Array.new($data_size) { \"%01x\" % rand(16) }.join('').freeze\n\nNATS.start(:fast_producer_error => true) do\n\n  $start   = Time.now\n  $to_send = $count\n\n  $batch = 10 if $data_size >= TSIZE\n\n  def send_batch\n    (0..$batch).each do\n      $to_send -= 1\n      if $to_send == 0\n        NATS.publish($sub, $data) { display_final_results }\n        return\n      else\n        NATS.publish($sub, $data)\n      end\n      printf('+') if $to_send.modulo($hash) == 0\n    end\n\n    if (NATS.pending_data_size > TRIP)\n      $delay *= 2\n    elsif $delay > $dmin\n      $delay /= 2\n    end\n\n    EM.add_timer($delay) { send_batch }\n  end\n\n  def display_final_results\n    elapsed = Time.now - $start\n    mbytes = sprintf(\"%.1f\", (($data_size*$count)/elapsed)/(1024*1024))\n    puts \"\\nTest completed : #{($count/elapsed).ceil} msgs/sec (#{mbytes} MB/sec)\\n\"\n    NATS.stop\n  end\n\n  if false\n    EM.add_periodic_timer(0.25) do\n      puts \"Outstanding data size is #{NATS.client.get_outbound_data_size}\"\n    end\n  end\n\n  puts \"Sending #{$count} messages of size #{$data.size} bytes on [#{$sub}]\"\n\n  # kick things off..\n  send_batch\n\nend\n"
  },
  {
    "path": "benchmark/pub_sub_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\n\n$:.unshift File.expand_path('../../lib', __FILE__)\nrequire 'nats/client'\n\n$count = 100000\n$batch = 100\n\n$delay = 0.00001\n$dmin  = 0.00001\n\nTRIP  = (2*1024*1024)\nTSIZE = 4*1024\n\n$sub  = 'test'\n$data_size = 16\n\n$hash  = 2500\n\nSTDOUT.sync = true\n\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: pub_perf [options]\"\n\n  opts.separator \"\"\n  opts.separator \"options:\"\n\n  opts.on(\"-n COUNT\",   \"Messages to send (default: #{$count}}\") { |count| $count = count.to_i }\n  opts.on(\"-s SIZE\",    \"Message size (default: #{$data_size})\") { |size| $data_size = size.to_i }\n  opts.on(\"-S SUBJECT\", \"Send subject (default: (#{$sub})\")      { |sub| $sub = sub }\n  opts.on(\"-b BATCH\",   \"Batch size (default: (#{$batch})\")      { |batch| $batch = batch.to_i }\nend\n\nparser.parse(ARGV)\n\ntrap(\"TERM\") { exit! }\ntrap(\"INT\")  { exit! }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\n$data = Array.new($data_size) { \"%01x\" % rand(16) }.join('').freeze\n\nNATS.start(:fast_producer => true) do\n\n  $batch = 10 if $data_size >= TSIZE\n\n  $received = 0\n  NATS.subscribe($sub) { $received += 1 }\n\n  $start   = Time.now\n  $to_send = $count\n\n  def send_batch\n    (0..$batch).each do\n      $to_send -= 1\n      if $to_send == 0\n        NATS.publish($sub, $data) { display_final_results }\n        return\n      else\n        NATS.publish($sub, $data)\n      end\n      printf('+') if $to_send.modulo($hash) == 0\n    end\n\n    if (NATS.pending_data_size > TRIP)\n      $delay *= 2\n    elsif $delay > $dmin\n      $delay /= 2\n    end\n\n    EM.add_timer($delay) { send_batch }\n  end\n\n  def display_final_results\n    elapsed = Time.now - $start\n    mbytes = sprintf(\"%.1f\", (($data_size*$count)/elapsed)/(1024*1024))\n    puts \"\\nTest completed : #{($count/elapsed).ceil} sent/received msgs/sec (#{mbytes} MB/sec)\\n\"\n    puts \"Received #{$received} messages\\n\"\n    NATS.stop\n  end\n\n  if false\n    EM.add_periodic_timer(0.25) do\n      puts \"Outstanding data size is #{NATS.client.get_outbound_data_size}\"\n    end\n  end\n\n  puts \"Sending #{$count} messages of size #{$data.size} bytes on [#{$sub}]\"\n\n  # kick things off..\n  send_batch\n\nend\n"
  },
  {
    "path": "benchmark/queues_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\n\n$:.unshift File.expand_path('../../lib', __FILE__)\nrequire 'nats/client'\n\n$expected = 100000\n$hash = 2500\n$sub  = 'test'\n$qs = 5\n$qgroup = 'mycoolgroup'\n\nSTDOUT.sync = true\n\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: queues_perf [options]\"\n\n  opts.separator \"\"\n  opts.separator \"options:\"\n\n  opts.on(\"-n ITERATIONS\", \"iterations to expect (default: #{$expected})\") { |iter| $expected = iter.to_i }\n  opts.on(\"-s SUBJECT\", \"Send subject (default: #{$sub})\")                 { |nsub| $sub = nsub }\n  opts.on(\"-q QUEUE SUBSCRIBERS\", \"# subscribers (default: #{$qs})\")        { |qs| $qs = qs }\nend\n\nparser.parse(ARGV)\n\ntrap(\"TERM\") { exit! }\ntrap(\"INT\")  { exit! }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n  received = 1\n  (0...$qs).each do\n    NATS.subscribe($sub, :queue => $qgroup) do\n      ($start = Time.now and puts \"Started Receiving..\") if (received == 1)\n      if ((received+=1) == $expected)\n        puts \"\\nTest completed : #{($expected/(Time.now-$start)).ceil} msgs/sec.\\n\"\n        NATS.stop\n      end\n      printf('+') if received.modulo($hash) == 0\n    end\n  end\n  puts \"Waiting for #{$expected} messages on [#{$sub}] on #{$qs} queue receivers on group: [#{$qgroup}]\"\nend\n"
  },
  {
    "path": "benchmark/sub_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\n\n$:.unshift File.expand_path('../../lib', __FILE__)\nrequire 'nats/client'\n\n$expected = 100000\n$hash = 2500\n$sub  = 'test'\n\nSTDOUT.sync = true\n\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: sub_perf [options]\"\n\n  opts.separator \"\"\n  opts.separator \"options:\"\n\n  opts.on(\"-n COUNT\", \"Messages to expect (default: #{$expected})\") { |count| $expected = count.to_i }\n  opts.on(\"-s SUBJECT\", \"Send subject (default: #{$sub})\")          { |sub| $sub = sub }\nend\n\nparser.parse(ARGV)\n\ntrap(\"TERM\") { exit! }\ntrap(\"INT\")  { exit! }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n\n  received = 1\n  NATS.subscribe($sub) {\n    ($start = Time.now and puts \"Started Receiving..\") if (received == 1)\n    if ((received += 1) == $expected)\n      puts \"\\nTest completed : #{($expected/(Time.now-$start)).ceil} msgs/sec.\\n\"\n      NATS.stop\n    end\n    printf('+') if received.modulo($hash) == 0\n  }\n\n  puts \"Waiting for #{$expected} messages on [#{$sub}]\"\nend\n"
  },
  {
    "path": "benchmark/sublist_perf.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$:.unshift File.expand_path('../../lib', __FILE__)\nrequire 'nats/server/sublist'\n\nclass PerfSublist\n  @@levels  = 5\n  @@targets = ['derek', 'ruth', 'sam', 'meg', 'brett', 'ben', 'miles', 'bella', 'rex', 'diamond']\n  @@sublist = Sublist.new()\n  @@subs    = []\n\n  def PerfSublist.subsInit(pre=nil)\n    @@targets.each {|t|\n        sub = pre ? (pre + \".\" + t) : t\n        @@sublist.insert(sub, sub)\n        @@subs << sub\n        subsInit(sub) if sub.split(\".\").size < @@levels\n      }\n  end\n\n  def PerfSublist.disableSublistCache\n    @@sublist.disable_cache\n  end\n\n  def PerfSublist.addWildcards\n    @@sublist.insert(\"ruth.>\", \"honey\")\n    @@sublist.insert(\"ruth.sam.meg.>\", \"honey\")\n    @@sublist.insert(\"ruth.*.meg.*\", \"honey\")\n  end\n\n  def PerfSublist.matchTest(subject, loop)\n    start = Time.now\n    loop.times {@@sublist.match(subject)}\n    stop = Time.now\n    puts \"Matched #{subject} #{loop} times in #{ms_time(stop-start)} ms @ #{(loop/(stop-start)).to_i}/sec\"\n  end\n\n  def PerfSublist.reset\n    @@sublist = Sublist.new()\n  end\n\n  def PerfSublist.removeAll\n    @@subs.each do |sub|\n      @@sublist.remove(sub, sub)\n    end\n  end\n\n  def PerfSublist.subscriptionCount\n    @@sublist.count\n  end\n\n  def PerfSublist.totalCount\n    @@subs.count\n  end\n\nend\n\ndef ms_time(t)\n  \"%0.2f\" % (t*1000)\nend\n\n# setup the subscription list.\nstart = Time.now\nPerfSublist.subsInit\nPerfSublist.addWildcards\nstop = Time.now\nputs\nputs \"Sublist holding #{PerfSublist.subscriptionCount} subscriptions\"\nputs \"Insert rate of #{(PerfSublist.subscriptionCount/(stop-start)).to_i}/sec\"\n\n#require 'profiler'\n\nputs\nputs \"cache test\"\n\n#Profiler__::start_profile\nPerfSublist.matchTest(\"derek.sam.meg.ruth\", 100000)\nPerfSublist.matchTest(\"ruth.sam.meg.derek\", 100000) # multiple returns w/ wc\nPerfSublist.matchTest(\"derek.sam.meg.billybob\", 100000) # worst case miss\n#Profiler__::stop_profile\n#Profiler__::print_profile($stdout)\n\nputs\nputs \"Hit any key to continue w/ cache disabled\"\nSTDIN.getc\n\n#Profiler__::start_profile\nPerfSublist.disableSublistCache\nPerfSublist.matchTest(\"derek.sam.meg.ruth\", 50000)\nPerfSublist.matchTest(\"ruth.sam.meg.derek\", 50000) # multiple returns w/ wc\nPerfSublist.matchTest(\"derek.sam.meg.billybob\", 50000) # worst case miss\n#Profiler__::stop_profile\n#Profiler__::print_profile($stdout)\n\n#Run multiple times to see Jruby speedup\n0.times do\n  puts \"\\n\\n\"\n  #PerfSublist.reset\n  #PerfSublist.subsInit\n  #PerfSublist.disableSublistCache\n  PerfSublist.matchTest(\"derek.sam.meg.ruth\", 50000)\n  PerfSublist.matchTest(\"ruth.sam.meg.derek\", 50000) # multiple returns w/ wc\n  PerfSublist.matchTest(\"derek.sam.meg.billybob\", 50000) # worst case miss\nend\n\nstart = Time.now\nPerfSublist.removeAll\nstop = Time.now\nputs\nputs \"Sublist now holding #{PerfSublist.subscriptionCount} subscriptions\"\nputs \"Removal rate of #{(PerfSublist.totalCount/(stop-start)).to_i}/sec\"\n\n# Allows you to see memory usage, etc\nputs\nputs \"Hit any key to quit\"\nSTDIN.getc\n"
  },
  {
    "path": "bin/nats-pub",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\nrequire 'rubygems'\nrequire 'nats/client'\n\ndef usage\n  puts \"Usage: nats-pub <subject> <msg> [-s server] [--creds CREDS]\"; exit\nend\n\nargs = ARGV.dup\nopts_parser = OptionParser.new do |opts|\n  opts.on('-s SERVER') { |server| $nats_server = server }\n  opts.on('--creds CREDS') { |creds| $creds = creds }\nend\nargs = opts_parser.parse!(args)\n\nsubject, msg = args\nusage unless subject\nmsg ||= 'Hello World'\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start($nats_server, user_credentials: $creds) do\n  NATS.publish(subject, msg) { NATS.stop }\nend\n\nputs \"Published [#{subject}] : '#{msg}'\"\n"
  },
  {
    "path": "bin/nats-queue",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\nrequire 'rubygems'\nrequire 'nats/client'\n\n['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }\n\ndef usage\n  puts \"Usage: nats-queue <subject> <queue name> [-s server] [-t] [-r]\"; exit\nend\n\nargs = ARGV.dup\nopts_parser = OptionParser.new do |opts|\n  opts.on('-s SERVER') { |server| $nats_server = server }\n  opts.on('-t','--time') { $show_time = true }\n  opts.on('-r','--raw') { $show_raw = true }\n  opts.on('--creds CREDS') { |creds| $creds = creds }\nend\nargs = opts_parser.parse!(args)\n\nsubject, queue_group = args\nusage unless subject and queue_group\n\ndef time_prefix\n  \"[#{Time.now}] \" if $show_time\nend\n\ndef header\n  $i=0 unless $i\n  \"#{time_prefix}[\\##{$i+=1}]\"\nend\n\ndef decorate sub, msg\n  if $show_raw\n    msg\n  else\n    \"#{header} Received on [#{sub}] : '#{msg}'\"\n  end\nend\n\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start($nats_server, user_credentials: $creds) do\n  puts \"Listening on [#{subject}], queue group [#{queue_group}]\" unless $show_raw\n  NATS.subscribe(subject, :queue => queue_group) { |msg, _, sub|\n    puts decorate(sub, msg)\n  }\nend\n"
  },
  {
    "path": "bin/nats-request",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\nrequire 'rubygems'\nrequire 'nats/client'\n\n['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }\n\ndef usage\n  puts \"Usage: nats-request <subject> <msg> [-s server] [-t] [-r] [-n responses]\"; exit\nend\n\nargs = ARGV.dup\nopts_parser = OptionParser.new do |opts|\n  opts.on('-s SERVER') { |server| $nats_server = server }\n  opts.on('-t','--time') { $show_time = true }\n  opts.on('-r','--raw') { $show_raw = true }\n  opts.on('-n RESPONSES') { |responses| $responses = Integer(responses) if Integer(responses) > 0 }\n  opts.on('--creds CREDS') { |creds| $creds = creds }\nend\nargs = opts_parser.parse!(args)\n\nsubject, msg = args\nusage unless subject\nmsg ||= 'Hello World'\n\ndef time_prefix\n  \"[#{Time.now}] \" if $show_time\nend\n\ndef header\n  $i=0 unless $i\n  \"#{time_prefix}[\\##{$i+=1}]\"\nend\n\ndef decorate msg\n  if $show_raw\n    msg\n  else\n    \"#{header} Replied with : '#{msg}'\"\n  end\nend\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start($nats_server, user_credentials: $creds) do\n  NATS.request(subject, msg) { |(msg, reply)|\n    puts decorate(msg)\n    exit! if $responses && ($responses-=1) < 1\n  }\nend\n"
  },
  {
    "path": "bin/nats-server",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# NATS command line interface script.\n# Run <tt>nats-server -h</tt> to get more usage.\n\nrequire 'nats/server'\n"
  },
  {
    "path": "bin/nats-sub",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\nrequire 'rubygems'\nrequire 'nats/client'\n\n['TERM', 'INT'].each { |s| trap(s) {  puts; exit! } }\n\ndef usage\n  puts \"Usage: nats-sub <subject> [-s server] [--creds CREDS] [-t] [-r]\"; exit\nend\n\nargs = ARGV.dup\nopts_parser = OptionParser.new do |opts|\n  opts.on('-s SERVER') { |server| $nats_server = server }\n  opts.on('-t','--time') { $show_time = true }\n  opts.on('-r','--raw') { $show_raw = true }\n  opts.on('--creds CREDS') { |creds| $creds = creds }\nend\nargs = opts_parser.parse!(args)\n\nsubject = args.shift\nusage unless subject\n\ndef time_prefix\n  \"[#{Time.now}] \" if $show_time\nend\n\ndef header\n  $i=0 unless $i\n  \"#{time_prefix}[\\##{$i+=1}]\"\nend\n\ndef decorate sub, msg\n  if $show_raw\n    msg\n  else\n    \"#{header} Received on [#{sub}] : '#{msg}'\"\n  end\nend\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start($nats_server, user_credentials: $creds) do\n  puts \"Listening on [#{subject}]\" unless $show_raw\n  NATS.subscribe(subject) { |msg, _, sub| puts decorate(sub, msg) }\nend\n"
  },
  {
    "path": "bin/nats-top",
    "content": "#!/usr/bin/env ruby\n# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'optparse'\nrequire 'net/http'\nrequire 'uri'\nrequire 'io/wait'\n\nrequire 'rubygems'\nrequire 'json'\n\ndef usage\n  puts \"Usage: nats-top [-s server_uri] [-m local monitor port] [-n num_connections] [-d delay_secs] [--sort sort_by]\"\n  puts \"--sort_options for more help\"\n  exit\nend\n\n$valid_sort_options = ['pending_size', 'msgs_to', 'msgs_from', 'bytes_to', 'bytes_from', 'subs']\n\ndef sort_options_help\n  puts \"Available sort_by options: #{$valid_sort_options.join(', ')}.\"\n  puts \"E.g. #{$0} -s bytes_to\"\n  exit\nend\n\nargs = ARGV.dup\nopts_parser = OptionParser.new do |opts|\n  opts.on('-s server_uri')      { |server|   $nats_server = server }\n  opts.on('-m local_port')      { |port|     $nats_port = port.to_i }\n  opts.on('-n num_connections') { |num|      $num_connections = num.to_i }\n  opts.on('-d delay')           { |delay|    $delay = delay.to_f }\n  opts.on('--sort sort_by')     { |sort_key| $sort_key = sort_key }\n  opts.on('--sort_options')     { sort_options_help }\n  opts.on('-h')     { usage }\n  opts.on('--help') { usage }\nend\nargs = opts_parser.parse!(args)\n\nDEFAULT_MONITOR_PORT = 9222\nDEFAULT_NUM_CONNECTIONS = 10\nDEFAULT_DELAY = 1 #sec\nDEFAULT_SORT = 'pending_size'\n\n$nats_port = DEFAULT_MONITOR_PORT if $nats_port.nil?\n$num_connections = DEFAULT_NUM_CONNECTIONS if $num_connections.nil?\n$nats_server = \"http://localhost:#{$nats_port}\" if $nats_server.nil?\n\n$nats_server = \"http://#{$nats_server}\" unless $nats_server.start_with?('http')\n\n$delay = DEFAULT_DELAY if $delay.nil?\n$sort_key = DEFAULT_SORT if $sort_key.nil?\n$sort_key.downcase!\n\nunless $valid_sort_options.include?($sort_key)\n  puts \"Invalid sort_by argument: #{$sort_key}\"\n  sort_options_help\nend\n\nvarz_uri = URI.parse(\"#{$nats_server}/varz\")\nconnz_uri = URI.parse(\"#{$nats_server}/connz?n=#{$num_connections}&s=#{$sort_key}\")\n\ndef psize(size, prec=1)\n  return 'NA' unless size\n  return sprintf(\"%.#{prec}f\", size) if size < 1024\n  return sprintf(\"%.#{prec}fK\", size/1024.0) if size < (1024*1024)\n  return sprintf(\"%.#{prec}fM\", size/(1024.0*1024.0)) if size < (1024*1024*1024)\n  return sprintf(\"%.#{prec}fG\", size/(1024.0*1024.0*1024.0))\nend\n\ndef clear_screen\n  print \"\\e[H\\e[2J\"\nend\n\n['TERM', 'INT'].each { |s| trap(s) {  clear_screen; exit! } }\n\nin_last_msgs  = in_last_bytes = 0\nout_last_msgs = out_last_bytes = 0\n\npoll = Time.now\nfirst = true\n\nwhile true\n  begin\n\n    varz_response = Net::HTTP::get_response(varz_uri)\n    varz = JSON.parse(varz_response.body, :symbolize_keys => true, :symbolize_names => true)\n\n    # Simple rates\n    delta_in_msgs, in_last_msgs     = varz[:in_msgs] - in_last_msgs, varz[:in_msgs]\n    delta_in_bytes, in_last_bytes   = varz[:in_bytes] - in_last_bytes, varz[:in_bytes]\n    delta_out_msgs, out_last_msgs   = varz[:out_msgs] - out_last_msgs, varz[:out_msgs]\n    delta_out_bytes, out_last_bytes = varz[:out_bytes] - out_last_bytes, varz[:out_bytes]\n\n    now = Time.now\n    tdelta, poll = now - poll, now\n\n    unless first\n      rate_in_msgs = delta_in_msgs / tdelta\n      rate_in_bytes = delta_in_bytes / tdelta\n      rate_out_msgs = delta_out_msgs / tdelta\n      rate_out_bytes = delta_out_bytes / tdelta\n    end\n\n    connz_response = Net::HTTP::get_response(connz_uri)\n    connz = JSON.parse(connz_response.body, :symbolize_keys => true, :symbolize_names => true)\n\n    clear_screen\n\n    puts  \"\\nServer:\"\n    puts  \"  Load: CPU: #{varz[:cpu]}%  Memory: #{psize(varz[:mem])}\"\n    print \"  In:   Msgs: #{psize(varz[:in_msgs])}  Bytes: #{psize(varz[:in_bytes])}\"\n    puts  \"  Msgs/Sec: #{psize(rate_in_msgs)}  Bytes/Sec: #{psize(rate_in_bytes)}\"\n\n    print \"  Out:  Msgs: #{psize(varz[:out_msgs])}  Bytes: #{psize(varz[:out_bytes])}\"\n    puts  \"  Msgs/Sec: #{psize(rate_out_msgs)}  Bytes/Sec: #{psize(rate_out_bytes)}\"\n\n    puts \"\\nConnections: #{psize(connz[:num_connections], 0)}\"\n\n    conn_t = \"  %-20s %-8s %-6s  %-10s  %-10s  %-10s  %-10s  %-10s\\n\"\n    printf(conn_t, 'HOST', 'CID', 'SUBS', 'PENDING', 'MSGS_TO', 'MSGS_FROM', 'BYTES_TO', 'BYTES_FROM')\n\n    connz[:connections].each do |conn|\n      printf(conn_t, \"#{conn[:ip]}:#{conn[:port]}\",\n                     conn[:cid],\n                     psize(conn[:subscriptions]),\n                     psize(conn[:pending_size]),\n                     psize(conn[:out_msgs]),\n                     psize(conn[:in_msgs]),\n                     psize(conn[:out_bytes]),\n                     psize(conn[:in_bytes])\n             )\n    end\n    puts\n\n    first = false\n\n    sleep($delay)\n\n  rescue => e\n    puts \"Error: #{e}\"\n    exit(1)\n  end\n\nend\n\n\n\n"
  },
  {
    "path": "dependencies.md",
    "content": "# External Dependencies\n\nThis file lists the dependencies used in this repository.\n\n| Dependency | License |\n|-|-|\n| github.com/eventmachine/eventmachine | Ruby License |\n"
  },
  {
    "path": "examples/auth_pub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\ndef usage\n  puts \"Usage: pub.rb <user> <pass> <subject> <msg>\"; exit\nend\n\nuser, pass, subject, msg = ARGV\nusage unless user and pass and subject\n\n# Default\nmsg ||= 'Hello World'\n\nuri = \"nats://#{user}:#{pass}@localhost:#{NATS::DEFAULT_PORT}\"\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start(:uri => uri) do\n  NATS.publish(subject, msg) { NATS.stop }\nend\n\nputs \"Published on [#{subject}] : '#{msg}'\"\n"
  },
  {
    "path": "examples/auth_sub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: auth_sub <user> <pass> <subject>\"; exit\nend\n\nuser, pass, subject = ARGV\nusage unless user and pass and subject\n\nuri = \"nats://#{user}:#{pass}@localhost:#{NATS::DEFAULT_PORT}\"\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start(:uri => uri) do\n  puts \"Listening on [#{subject}]\"\n  NATS.subscribe(subject) { |msg, _, sub| puts \"Received on [#{sub}] : '#{msg}'\" }\nend\n"
  },
  {
    "path": "examples/auto_unsub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: ruby auto_unsub.rb <subject> [wanted=5] [send=10]\"; exit\nend\n\nsubject = ARGV.shift\nwanted  = ARGV.shift || 5\nsend    = ARGV.shift || 10\n\nusage unless subject\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nreceived = 0\n\nNATS.start do\n  puts \"Listening on [#{subject}], auto unsubscribing after #{wanted} messages, but will send #{send}.\"\n  NATS.subscribe(subject, :max => wanted) { |msg|\n    puts \"Received '#{msg}'\"\n    received += 1\n  }\n  (0...send).each { NATS.publish(subject, 'hello') }\n  NATS.publish('done') { NATS.stop }\nend\n\nputs \"Received #{received} messages\"\n"
  },
  {
    "path": "examples/busy_body.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n# This is an example to show off nats-top. Run busy_body on a monitor enabled\n# server and exec nats-top.\n\n['TERM', 'INT'].each { |sig| trap(sig) { exit! } }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\ndef create_subscribers(sub='foo.bar', num_subs=10, num_connections=20)\n  (1..num_connections).each do\n    NATS.connect do |nc|\n      (1..num_subs).each { nc.subscribe(sub) }\n    end\n  end\nend\n\ndef create_publishers(sub='foo.bar', body='Hello World!', num_connections=20, num_sends=100)\n  (1..num_connections).each do\n    NATS.connect do |nc|\n      (1..num_sends).each { nc.publish(sub, body) }\n    end\n  end\nend\n\ndef timed_publish(sub='foo.bar', body='Hello World!', delay=1, burst=500)\n  EM.add_periodic_timer(1) do\n    b = (burst * rand).to_i\n    (1..b).each { NATS.publish(sub, body) }\n  end\nend\n\nNATS.start {\n  create_subscribers\n  create_publishers\n  timed_publish\n}\n"
  },
  {
    "path": "examples/drain_connection.rb",
    "content": "require 'nats/client'\n\nnc1 = nil\nnc2 = nil\nresponses = []\ninbox = NATS.create_inbox\n[\"TERM\", \"INT\"].each { |sig| trap(sig) {\n    EM.stop\n  }\n}\n\nsubscribers = []\nEM.run do\n  5.times do |n|\n    subscribers << NATS.connect(drain_timeout: 30, name: \"client-#{n}\") do |nc|\n      nc.on_error { |err| puts \"#{Time.now} - Error: #{err}\" }\n      nc.on_close { |err| puts \"#{Time.now} - Connection drained and closed!\" }\n      puts \"#{Time.now} - Started Connection #{n}...\"\n\n      nc.flush do\n        nc.subscribe('foo', queue: \"workers\") do |msg, reply, sub|\n          nc.publish(reply, \"ACK1:#{msg}\")\n        end\n\n        nc.subscribe('bar', queue: \"workers\") do |msg, reply, sub|\n          nc.publish(reply, \"ACK1:#{msg}\")\n        end\n\n        nc.subscribe('quux', queue: \"workers\") do |msg, reply, sub|\n          nc.publish(reply, \"ACK1:#{msg}\")\n        end\n      end\n    end\n  end\n\n  pub_client = NATS.connect do |nc|\n    EM.add_periodic_timer(0.001) do\n      Fiber.new do\n        response = nc.request(\"foo\", \"A\")\n        puts \"Dropped request!!!\" if response.nil?\n      end.resume\n    end\n\n    EM.add_periodic_timer(0.001) do\n      Fiber.new do\n        response = nc.request(\"bar\", \"B\")\n        puts \"Dropped request!!!\" if response.nil?\n        # puts \"Response on 'bar' : #{response}\"\n      end.resume\n    end\n\n    EM.add_periodic_timer(0.001) do\n      Fiber.new do\n        response = nc.request(\"quux\", \"C\")\n        puts \"Dropped request!!!\" if response.nil?\n      end.resume\n    end\n  end\n\n  EM.add_timer(1) do\n    # Drain is like stop but gracefully closes the connection.\n    subs = subscribers[0..3]\n\n    subs.each_with_index do |nc, i|\n      if nc.draining?\n        puts \"Already draining... #{responses.count}\"\n        next\n      end\n\n      # Just using close will cause some requests to fail\n      # nc.close\n\n      # Drain is more graceful and allow clients to process requests\n      # that have already been delivered by the server to the subscriber.\n      puts \"#{Time.now} - Start draining  #{nc.options[:name]}... (pending_data: #{nc.pending_data_size})\"\n      nc.drain do\n        puts \"#{Time.now} - Done draining #{nc.options[:name]}!\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "examples/expected.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: ruby expected.rb <subject> [timeout (default 5 secs)] [expected (default 5)]\"\n  exit\nend\n\nsubject  = ARGV.shift\ntimeout  = ARGV.shift || 5\nexpected = ARGV.shift || 5\n\nusage unless subject\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n  received = 0\n  puts \"Listening on [#{subject}]\"\n  puts \"Will timeout in #{timeout} seconds unless #{expected} messages are received.\"\n  sid = NATS.subscribe(subject) { |msg|\n    puts \"Received '#{msg}'\"\n    received += 1\n    if received >= expected\n      puts \"All #{expected} messages received, exiting..\"\n      NATS.stop\n    end\n  }\n  NATS.timeout(sid, timeout, :expected => expected) {\n    puts \"Timedout waiting for a message!\"\n    NATS.stop\n  }\nend\n"
  },
  {
    "path": "examples/fiber_request.rb",
    "content": "require 'fiber'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { EM.stop } }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start {\n\n  NATS.subscribe('help') do |msg, reply|\n    puts \"[Received]: <<- #{msg}\"\n    NATS.publish(reply, \"I'll help! - #{msg}\")\n  end\n\n  NATS.subscribe('slow') do |msg, reply|\n    puts \"[Received]: <<- #{msg}\"\n    EM.add_timer(1) { NATS.publish(reply, \"I'll help! - #{msg}\") }\n  end\n\n  10.times do |n|\n    NATS.subscribe('hi') do |msg, reply|\n      NATS.publish(reply, \"Hello World! - id:#{n}\")\n    end\n  end\n\n  Fiber.new do\n    # Requests work synchronously within the same Fiber\n    # returning the message when done.\n    response = NATS.request('help', 'foo')\n    puts \"[Response]: ->> '#{response}'\"\n\n    # Specifying a custom timeout to give up waiting for\n    # a response.\n    response = NATS.request('slow', 'bar', timeout: 2)\n    if response.nil?\n      puts \"No response after 2 seconds...\"\n    else\n      puts \"[Response]: ->> '#{response}'\"\n    end\n\n    # Can gather multiple responses with the same request\n    # which will then return a collection with the responses\n    # that were received before the timeout.\n    responses = NATS.request('hi', 'quux', max: 10, timeout: 1)\n    responses.each_with_index do |response, i|\n      puts \"[Response# #{i}]: ->> '#{response}'\"\n    end\n    \n    # If no replies then an empty collection is returned.\n    responses = NATS.request('nowhere', '', max: 10, timeout: 2)\n    if responses.any?\n      puts \"Got #{responses.count} responses\"\n    else\n      puts \"No response after 2 seconds...\"\n    end\n\n    NATS.stop\n  end.resume\n\n  # Multiple fibers can make requests concurrently\n  # under the same Eventmachine loop.\n  Fiber.new do\n    10.times do |n|\n      response = NATS.request('help', \"help.#{n}\")\n      puts \"[Response]: ->> '#{response}'\"\n    end\n  end.resume\n}\n"
  },
  {
    "path": "examples/multi_connection.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start {\n  NATS.subscribe('test') do |msg, reply, sub|\n    puts \"received data on sub:#{sub} - #{msg}\"\n    NATS.stop\n  end\n\n  # Form a second connection to send message on\n  NATS.connect { |nc| nc.publish('test', 'Hello World!') }\n}\n"
  },
  {
    "path": "examples/pub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\ndef usage\n  puts \"Usage: ruby pub.rb <subject> <msg>\"; exit\nend\n\nsubject, msg = ARGV\nusage unless subject\nmsg ||= 'Hello World'\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start { NATS.publish(subject, msg) { NATS.stop } }\n\nputs \"Published [#{subject}] : '#{msg}'\"\n"
  },
  {
    "path": "examples/queue_sub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: ruby queue_sub.rb <subject> <queue name>\"; exit\nend\n\nsubject, queue_group = ARGV\nusage unless subject and queue_group\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n  puts \"Listening on [#{subject}], queue group [#{queue_group}]\"\n  NATS.subscribe(subject, :queue => queue_group) { |msg| puts \"Received '#{msg}'\" }\nend\n"
  },
  {
    "path": "examples/request.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start {\n\n  # The helper\n  NATS.subscribe('help') do |msg, reply|\n    NATS.publish(reply, \"I'll help!\")\n  end\n\n  # Help request\n  NATS.request('help') { |response|\n    puts \"Got a response: '#{response}'\"\n    NATS.stop\n  }\n}\n"
  },
  {
    "path": "examples/server_config.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./server_config.yml\n#\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\nssl: false\n\npid_file: '/tmp/nats_test.pid'\n# log_file: '/tmp/nats_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "examples/server_config_cluster.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./server_config.yml\n#\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n#\n# NATS can support both full mesh and directive\n# acyclic graphs setups. Its up to the configuration\n# setup to avoid cycles.\n#\n# The port definition allows us to receive incoming connections.\n# Comment out if you want to suppress incoming connections.\n#\n# The server can solicit active connections via the routes definitions below.\n#\n# authorization is similar to client connection definitions.\n\ncluster:\n  port: 4244\n\n  authorization:\n    user: route_user\n    password: cafebabe\n    token: deadbeef\n    timeout: 1\n\n  # These are actively connected from this server. Other servers\n  # can connect to us if they supply the correct credentials from\n  # above.\n\n  routes:\n    nats-route://foo:bar@127.0.0.1:4220\n    nats-route://foo:bar@127.0.0.1:4221\n\npid_file: '/tmp/nats_test.pid'\n# log_file: '/tmp/nats_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "examples/simple.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start {\n  NATS.subscribe('test') do |msg, reply, sub|\n    puts \"received data on sub:#{sub} - #{msg}\"\n    NATS.stop\n  end\n\n  NATS.publish('test', 'Hello World!')\n}\n"
  },
  {
    "path": "examples/sub.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: ruby sub.rb <subject>\"; exit\nend\n\nsubject = ARGV.shift\nusage unless subject\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n  puts \"Listening on [#{subject}]\"\n  NATS.subscribe(subject) { |msg| puts \"Received '#{msg}'\" }\nend\n"
  },
  {
    "path": "examples/sub_timeout.rb",
    "content": "require 'rubygems'\nrequire 'nats/client'\n\n[\"TERM\", \"INT\"].each { |sig| trap(sig) { NATS.stop } }\n\ndef usage\n  puts \"Usage: ruby subtimeout.rb <subject> [timeout (default 5 secs)]\"; exit\nend\n\nsubject = ARGV.shift\ntimeout = ARGV.shift || 5\n\nusage unless subject\n\nNATS.on_error { |err| puts \"Server Error: #{err}\"; exit! }\n\nNATS.start do\n  puts \"Listening on [#{subject}]\"\n  puts \"Will timeout in #{timeout} seconds.\"\n  sid = NATS.subscribe(subject) { |msg|\n    puts \"Received '#{msg}'\"\n    NATS.stop\n  }\n  NATS.timeout(sid, timeout) {\n    puts \"Timedout waiting for a message!\"\n    NATS.stop\n  }\nend\n"
  },
  {
    "path": "examples/tls-connect.rb",
    "content": "require 'nats/client'\n\nEM.run do\n\n  options = {\n    :servers => [\n      'nats://secret:deadbeef@127.0.0.1:4443',\n      'nats://secret:deadbeef@127.0.0.1:4444'\n    ],\n    :max_reconnect_attempts => 10,\n    :reconnect_time_wait => 2,\n    :tls => {\n      :private_key_file => './spec/configs/certs/key.pem',\n      :cert_chain_file  => './spec/configs/certs/server.pem'\n    }\n  }\n\n  NATS.connect(options) do |nc|\n    puts \"#{Time.now.to_f} - Connected to NATS at #{nc.connected_server}\"\n\n    nc.subscribe(\"hello\") do |msg|\n      puts \"#{Time.now.to_f} - Received: #{msg}\"\n    end\n\n    nc.flush do\n      nc.publish(\"hello\", \"world\")\n    end\n\n    EM.add_periodic_timer(0.1) do\n      next unless nc.connected?\n      nc.publish(\"hello\", \"hello\")\n    end\n\n    # Set default callbacks\n    nc.on_error do |e|\n      puts \"#{Time.now.to_f } - Error: #{e}\"\n    end\n\n    nc.on_disconnect do |reason|\n      puts \"#{Time.now.to_f} - Disconnected: #{reason}\"\n    end\n\n    nc.on_reconnect do |nc|\n      puts \"#{Time.now.to_f} - Reconnected to NATS server at #{nc.connected_server}\"\n    end\n\n    nc.on_close do\n      puts \"#{Time.now.to_f} - Connection to NATS closed\"\n      EM.stop\n    end\n  end\nend\n"
  },
  {
    "path": "examples/tls.rb",
    "content": "require 'nats/client'\n\noptions = {\n  :servers => [\n   'nats://secret:deadbeef@127.0.0.1:4443',\n   'nats://secret:deadbeef@127.0.0.1:4444'\n  ],\n  :max_reconnect_attempts => 10,\n  :reconnect_time_wait => 2,\n  :tls => {\n    :ssl_version => :TLSv1_2,\n    :protocols => [:tlsv1_2],\n    :private_key_file => './spec/configs/certs/key.pem',\n    :cert_chain_file  => './spec/configs/certs/server.pem'\n  }\n}\n\n# Set default callbacks\nNATS.on_error do |e|\n  puts \"#{Time.now.to_f } - Error: #{e}\"\nend\n\nNATS.on_disconnect do |reason|\n  puts \"#{Time.now.to_f} - Disconnected: #{reason}\"\nend\n\nNATS.on_reconnect do |nats|\n  puts \"#{Time.now.to_f} - Reconnected to NATS server at #{nats.connected_server}\"\nend\n\nNATS.on_close do\n  puts \"#{Time.now.to_f} - Connection to NATS closed\"\n  EM.stop\nend\n\nNATS.start(options) do |nats|\n  puts \"#{Time.now.to_f} - Connected to NATS at #{nats.connected_server}\"\n\n  nats.subscribe(\"hello\") do |msg|\n    puts \"#{Time.now.to_f} - Received: #{msg}\"\n  end\n\n  nats.flush do\n    nats.publish(\"hello\", \"world\")\n  end\n\n  EM.add_periodic_timer(0.1) do\n    next unless nats.connected?\n    nats.publish(\"hello\", \"hello\")\n  end\nend\n"
  },
  {
    "path": "lib/nats/client.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'uri'\nrequire 'securerandom'\nrequire 'fiber'\nrequire 'openssl' unless defined?(OpenSSL)\n\nep = File.expand_path(File.dirname(__FILE__))\n\nrequire \"#{ep}/ext/em\"\nrequire \"#{ep}/ext/bytesize\"\nrequire \"#{ep}/ext/json\"\nrequire \"#{ep}/version\"\nrequire \"#{ep}/nuid\"\n\nmodule NATS\n\n  DEFAULT_PORT = 4222\n  DEFAULT_URI = \"nats://localhost:#{DEFAULT_PORT}\".freeze\n\n  MAX_RECONNECT_ATTEMPTS = 10\n  RECONNECT_TIME_WAIT = 2\n\n  MAX_PENDING_SIZE = 32768\n\n  # Maximum outbound size per client to trigger FP, 20MB\n  FAST_PRODUCER_THRESHOLD = (10*1024*1024)\n\n  # Ping intervals\n  DEFAULT_PING_INTERVAL = 120\n  DEFAULT_PING_MAX = 2\n\n  # Drain mode support\n  DEFAULT_DRAIN_TIMEOUT = 30\n\n  # Protocol\n  # @private\n  MSG      = /\\AMSG\\s+([^\\s]+)\\s+([^\\s]+)\\s+(([^\\s]+)[^\\S\\r\\n]+)?(\\d+)\\r\\n/i #:nodoc:\n  OK       = /\\A\\+OK\\s*\\r\\n/i #:nodoc:\n  ERR      = /\\A-ERR\\s+('.+')?\\r\\n/i #:nodoc:\n  PING     = /\\APING\\s*\\r\\n/i #:nodoc:\n  PONG     = /\\APONG\\s*\\r\\n/i #:nodoc:\n  INFO     = /\\AINFO\\s+([^\\r\\n]+)\\r\\n/i  #:nodoc:\n  UNKNOWN  = /\\A(.*)\\r\\n/  #:nodoc:\n\n  # Responses\n  CR_LF = (\"\\r\\n\".freeze) #:nodoc:\n  CR_LF_SIZE = (CR_LF.bytesize) #:nodoc:\n\n  PING_REQUEST  = (\"PING#{CR_LF}\".freeze) #:nodoc:\n  PONG_RESPONSE = (\"PONG#{CR_LF}\".freeze) #:nodoc:\n\n  SUB_OP = ('SUB'.freeze) #:nodoc:\n  EMPTY_MSG = (''.freeze) #:nodoc:\n\n  # Used for future pedantic Mode\n  SUB = /^([^\\.\\*>\\s]+|>$|\\*)(\\.([^\\.\\*>\\s]+|>$|\\*))*$/ #:nodoc:\n  SUB_NO_WC = /^([^\\.\\*>\\s]+)(\\.([^\\.\\*>\\s]+))*$/ #:nodoc:\n\n  # Parser\n  AWAITING_CONTROL_LINE = 1 #:nodoc:\n  AWAITING_MSG_PAYLOAD  = 2 #:nodoc:\n  AWAITING_INFO_LINE = 3 # :nodoc:\n\n  class Error < StandardError; end #:nodoc:\n\n  # When the NATS server sends us an ERROR message, this is raised/passed by default\n  class ServerError < Error; end #:nodoc:\n\n  # When we detect error on the client side (e.g. Fast Producer, TLS required)\n  class ClientError < Error; end #:nodoc:\n\n  # When we cannot connect to the server (either initially or after a reconnect), this is raised/passed\n  class ConnectError < Error; end #:nodoc:\n\n  # When we cannot connect to the server because authorization failed.\n  class AuthError < ConnectError; end #:nodoc:\n\n  class << self\n    attr_reader   :client, :reactor_was_running, :err_cb, :err_cb_overridden #:nodoc:\n    attr_reader   :reconnect_cb, :close_cb, :disconnect_cb  #:nodoc\n\n    alias :reactor_was_running? :reactor_was_running\n\n    # Create and return a connection to the server with the given options.\n    # The optional block will be called when the connection has been completed.\n    #\n    # @param [String] uri The URI or comma separated list of URIs of NATS servers to connect to.\n    # @param [Hash] opts\n    # @option opts [String|URI] :uri The URI to connect to, example nats://localhost:4222\n    # @option opts [Boolean] :reconnect Boolean that can be used to suppress reconnect functionality.\n    # @option opts [Boolean] :debug Boolean that can be used to output additional debug information.\n    # @option opts [Boolean] :verbose Boolean that is sent to server for setting verbose protocol mode.\n    # @option opts [Boolean] :pedantic Boolean that is sent to server for setting pedantic mode.\n    # @option opts [Boolean] :ssl Boolean that is sent to server for setting TLS/SSL mode.\n    # @option opts [Hash]    :tls Map of options for configuring secure connection handled to EM#start_tls directly.\n    # @option opts [Integer] :max_reconnect_attempts Integer that can be used to set the max number of reconnect tries\n    # @option opts [Integer] :reconnect_time_wait Integer that can be used to set the number of seconds to wait between reconnect tries\n    # @option opts [Integer] :ping_interval Integer that can be used to set the ping interval in seconds.\n    # @option opts [Integer] :max_outstanding_pings Integer that can be used to set the max number of outstanding pings before declaring a connection closed.\n    # @param [Block] &blk called when the connection is completed. Connection will be passed to the block.\n    # @return [NATS] connection to the server.\n    #\n    # @example Connect to local NATS server.\n    #  NATS.connect do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Setting custom server URI to connect.\n    #  NATS.connect(\"nats://localhost:4222\") do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Setting username and password to authenticate.\n    #  NATS.connect(\"nats://user:password@localhost:4222\") do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Specifying explicit list of servers via options.\n    #  NATS.connect(servers: [\"nats://127.0.0.1:4222\",\"nats://127.0.0.1:4223\",\"nats://127.0.0.1:4224\"]) do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Using comma separated array to define list of servers.\n    #  NATS.connect(\"nats://localhost:4223,nats://localhost:4224\") do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Only specifying endpoint uses NATS default scheme and port.\n    #  NATS.connect(\"demo.nats.io\") do |nc|\n    #    # ...\n    #  end\n    #\n    # @example Setting infinite reconnect retries with 2 seconds back off against custom URI.\n    #  NATS.connect(\"demo.nats.io:4222\", max_reconnect_attempts: -1, reconnect_time_wait: 2) do |nc|\n    #    # ...\n    #  end\n    #\n    def connect(uri=nil, opts={}, &blk)\n      case uri\n      when String\n        # Initialize TLS defaults in case any url is using it.\n        uris = opts[:uri] = process_uri(uri)\n        opts[:tls] ||= {} if uris.any? {|u| u.scheme == 'tls'}\n      when Hash\n        opts = uri\n      end\n\n      # Defaults\n      opts[:verbose] = false if opts[:verbose].nil?\n      opts[:pedantic] = false if opts[:pedantic].nil?\n      opts[:reconnect] = true if opts[:reconnect].nil?\n      opts[:ssl] = false if opts[:ssl].nil?\n      opts[:max_reconnect_attempts] = MAX_RECONNECT_ATTEMPTS if opts[:max_reconnect_attempts].nil?\n      opts[:reconnect_time_wait] = RECONNECT_TIME_WAIT if opts[:reconnect_time_wait].nil?\n      opts[:ping_interval] = DEFAULT_PING_INTERVAL if opts[:ping_interval].nil?\n      opts[:max_outstanding_pings] = DEFAULT_PING_MAX if opts[:max_outstanding_pings].nil?\n      opts[:drain_timeout] = DEFAULT_DRAIN_TIMEOUT if opts[:drain_timeout].nil?\n\n      # Override with ENV\n      opts[:uri] ||= ENV['NATS_URI'] || DEFAULT_URI\n      opts[:verbose] = ENV['NATS_VERBOSE'].downcase == 'true' unless ENV['NATS_VERBOSE'].nil?\n      opts[:pedantic] = ENV['NATS_PEDANTIC'].downcase == 'true' unless ENV['NATS_PEDANTIC'].nil?\n      opts[:debug] = ENV['NATS_DEBUG'].downcase == 'true' unless ENV['NATS_DEBUG'].nil?\n      opts[:reconnect] = ENV['NATS_RECONNECT'].downcase == 'true' unless ENV['NATS_RECONNECT'].nil?\n      opts[:fast_producer_error] = ENV['NATS_FAST_PRODUCER'].downcase == 'true' unless ENV['NATS_FAST_PRODUCER'].nil?\n      opts[:ssl] = ENV['NATS_SSL'].downcase == 'true' unless ENV['NATS_SSL'].nil?\n      opts[:max_reconnect_attempts] = ENV['NATS_MAX_RECONNECT_ATTEMPTS'].to_i unless ENV['NATS_MAX_RECONNECT_ATTEMPTS'].nil?\n      opts[:reconnect_time_wait] = ENV['NATS_RECONNECT_TIME_WAIT'].to_i unless ENV['NATS_RECONNECT_TIME_WAIT'].nil?\n      opts[:name] ||= ENV['NATS_CONNECTION_NAME']\n      opts[:no_echo] ||= ENV['NATS_NO_ECHO'] || false\n      opts[:ping_interval] = ENV['NATS_PING_INTERVAL'].to_i unless ENV['NATS_PING_INTERVAL'].nil?\n      opts[:max_outstanding_pings] = ENV['NATS_MAX_OUTSTANDING_PINGS'].to_i unless ENV['NATS_MAX_OUTSTANDING_PINGS'].nil?\n      opts[:drain_timeout] ||= ENV['NATS_DRAIN_TIMEOUT'].to_i unless ENV['NATS_DRAIN_TIMEOUT'].nil?\n\n      uri = opts[:uris] || opts[:servers] || opts[:uri]\n\n      if opts[:tls]\n        case\n        when opts[:tls][:ca_file]\n          # Ensure that the file exists before going further\n          # in order to report configuration errors during\n          # connect synchronously.\n          if !File.readable?(opts[:tls][:ca_file])\n            raise(Error, \"TLS Verification is enabled but ca_file %s is not readable\" % opts[:tls][:ca_file])\n          end\n\n          # Certificate is supplied so assume we mean verification by default,\n          # but still allow disabling explicitly by setting to false.\n          opts[:tls][:verify_peer] ||= true\n        when (opts[:tls][:verify_peer] && !opts[:tls][:ca_file])\n          raise(Error, \"TLS Verification is enabled but ca_file is not set\")\n        else\n          # Otherwise, disable verifying peer by default,\n          # thus never reaching EM#ssl_verify_peer\n          opts[:tls][:verify_peer] = false\n        end\n\n        # Allow overriding directly but default to those which server supports.\n        opts[:tls][:ssl_version] ||= %w(tlsv1 tlsv1_1 tlsv1_2)\n        opts[:tls][:protocols]   ||= %w(tlsv1 tlsv1_1 tlsv1_2)\n      end\n\n      # If they pass an array here just pass along to the real connection, and use first as the first attempt..\n      # Real connection will do proper walk throughs etc..\n      unless uri.nil?\n        uris = uri.kind_of?(Array) ? uri : [uri]\n        uris.shuffle! unless opts[:dont_randomize_servers]\n        u = uris.first\n        @uri = u.is_a?(URI) ? u.dup : URI.parse(u)\n      end\n\n      @err_cb = proc { |e| raise e } unless err_cb\n      @close_cb = proc { } unless close_cb\n      @disconnect_cb = proc { } unless disconnect_cb\n\n      client = EM.connect(@uri.host, @uri.port, self, opts)\n      client.on_connect(&blk) if blk\n      return client\n    end\n\n    # Create a default client connection to the server.\n    # @see NATS::connect\n    def start(*args, &blk)\n      @reactor_was_running = EM.reactor_running?\n      unless (@reactor_was_running || blk)\n        raise(Error, \"EM needs to be running when NATS.start is called without a run block\")\n      end\n      # Setup optimized select versions\n      if EM.epoll?\n        EM.epoll\n      elsif EM.kqueue?\n        EM.kqueue\n      elsif EM.library_type == :java\n        # No warning needed, we're using Java NIO\n      else\n        Kernel.warn('Neither epoll nor kqueue are supported, performance may be impacted')\n      end\n      EM.run { @client = connect(*args, &blk) }\n    end\n\n    # Close the default client connection and optionally call the associated block.\n    # @param [Block] &blk called when the connection is closed.\n    def stop(&blk)\n      client.close if (client and (client.connected? || client.reconnecting?))\n      blk.call if blk\n      @err_cb = nil\n      @close_cb = nil\n      @reconnect_cb = nil\n      @disconnect_cb = nil\n    end\n\n    # Drain gracefully disconnects from the server, letting\n    # subscribers process pending messages already sent by server and\n    # optionally calls the associated block.\n    # @param [Block] &blk called when drain is done and connection is closed.\n    def drain(&blk)\n      if (client and !client.draining? and (client.connected? || client.reconnecting?))\n        client.drain { blk.call if blk }\n      end\n    end\n\n    # @return [URI] Connected server\n    def connected_server\n      return nil unless client\n      client.connected_server\n    end\n\n    # @return [Boolean] Connected state\n    def connected?\n      return false unless client\n      client.connected?\n    end\n\n    # @return [Boolean] Reconnecting state\n    def reconnecting?\n      return false unless client\n      client.reconnecting?\n    end\n\n    # @return [Boolean] Draining state\n    def draining?\n      return false unless client\n      client.draining?\n    end\n\n    # @return [Hash] Options\n    def options\n      return {} unless client\n      client.options\n    end\n\n    # @return [Hash] Server information\n    def server_info\n      return nil unless client\n      client.server_info\n    end\n\n    # Set the default on_error callback.\n    # @param [Block] &callback called when an error has been detected.\n    def on_error(&callback)\n      @err_cb, @err_cb_overridden = callback, true\n    end\n\n    # Set the default on_reconnect callback.\n    # @param [Block] &callback called when a reconnect attempt is made.\n    def on_reconnect(&callback)\n      @reconnect_cb = callback\n      @client.on_reconnect(&callback) unless @client.nil?\n    end\n\n    # Set the default on_disconnect callback.\n    # @param [Block] &callback called whenever client disconnects from a server.\n    def on_disconnect(&callback)\n      @disconnect_cb = callback\n      @client.on_disconnect(&callback) unless @client.nil?\n    end\n\n    # Set the default on_closed callback.\n    # @param [Block] &callback called when will reach a state when will no longer be connected.\n    def on_close(&callback)\n      @close_cb = callback\n      @client.on_close(&callback) unless @client.nil?\n    end\n\n    # Publish a message using the default client connection.\n    # @see NATS#publish\n    def publish(*args, &blk)\n      (@client ||= connect).publish(*args, &blk)\n    end\n\n    # Subscribe using the default client connection.\n    # @see NATS#subscribe\n    def subscribe(*args, &blk)\n      (@client ||= connect).subscribe(*args, &blk)\n    end\n\n    # Cancel a subscription on the default client connection.\n    # @see NATS#unsubscribe\n    def unsubscribe(*args)\n      (@client ||= connect).unsubscribe(*args)\n    end\n\n    # Set a timeout for receiving messages for the subscription.\n    # @see NATS#timeout\n    def timeout(*args, &blk)\n      (@client ||= connect).timeout(*args, &blk)\n    end\n\n    # Publish a message and wait for a response on the default client connection.\n    # @see NATS#request\n    def request(*args, &blk)\n      (@client ||= connect).request(*args, &blk)\n    end\n\n    # Returns a subject that can be used for \"directed\" communications.\n    # @return [String]\n    def create_inbox\n      \"_INBOX.#{SecureRandom.hex(13)}\"\n    end\n\n    # Flushes all messages and subscriptions in the default connection\n    # @see NATS#flush\n    def flush(*args, &blk)\n      (@client ||= connect).flush(*args, &blk)\n    end\n\n    # Return bytes outstanding for the default client connection.\n    # @see NATS#pending_data_size\n    def pending_data_size(*args)\n      (@client ||= connect).pending_data_size(*args)\n    end\n\n    def wait_for_server(uri, max_wait = 5) # :nodoc:\n      start = Time.now\n      while (Time.now - start < max_wait) # Wait max_wait seconds max\n        break if server_running?(uri)\n        sleep(0.1)\n      end\n    end\n\n    def server_running?(uri) # :nodoc:\n      require 'socket'\n      s = TCPSocket.new(uri.host, uri.port)\n      s.close\n      return true\n    rescue\n      return false\n    end\n\n    def clear_client # :nodoc:\n      @client = nil\n    end\n\n    private\n\n    def uri_is_remote?(uri)\n      uri.host != 'localhost' && uri.host != '127.0.0.1'\n    end\n\n    def process_uri(uris)\n      connect_uris = []\n      uris.split(',').each do |uri|\n        opts = {}\n\n        # Scheme\n        if uri.include?(\"://\")\n          scheme, uri = uri.split(\"://\")\n          opts[:scheme] = scheme\n        else\n          opts[:scheme] = 'nats'\n        end\n\n        # UserInfo\n        if uri.include?(\"@\")\n          userinfo, endpoint = uri.split(\"@\")\n          host, port = endpoint.split(\":\")\n          opts[:userinfo] = userinfo\n        else\n          host, port = uri.split(\":\")\n        end\n\n        # Host and Port\n        opts[:host] = host || \"localhost\"\n        opts[:port] = port || DEFAULT_PORT\n\n        connect_uris << URI::Generic.build(opts)\n      end\n\n      connect_uris\n    end\n  end\n\n  attr_reader :connected, :connect_cb, :err_cb, :err_cb_overridden, :pongs_received #:nodoc:\n  attr_reader :closing, :reconnecting, :draining, :server_pool, :options, :server_info #:nodoc\n  attr_reader :msgs_received, :msgs_sent, :bytes_received, :bytes_sent, :pings\n  attr_reader :disconnect_cb, :close_cb\n\n  alias :connected? :connected\n  alias :closing? :closing\n  alias :reconnecting? :reconnecting\n  alias :draining? :draining\n\n  def initialize(options)\n    @options = options\n    process_uri_options\n\n    @buf = nil\n    @ssid, @subs = 1, {}\n    @err_cb = NATS.err_cb\n    @close_cb = NATS.close_cb\n    @reconnect_cb = NATS.reconnect_cb\n    @disconnect_cb = NATS.disconnect_cb\n    @reconnect_timer, @needed = nil, nil\n    @connected, @closing, @reconnecting, @conn_cb_called = false, false, false, false\n    @msgs_received = @msgs_sent = @bytes_received = @bytes_sent = @pings = 0\n    @pending_size = 0\n    @server_info = { }\n\n    # Mark whether we should be connecting securely, try best effort\n    # in being compatible with present ssl support.\n    @ssl = false\n    @tls = nil\n    @tls = options[:tls] if options[:tls]\n    @ssl = options[:ssl] if options[:ssl] or @tls\n\n    # New style request/response implementation.\n    @resp_sub = nil\n    @resp_map = nil\n    @resp_sub_prefix = nil\n    @nuid = NATS::NUID.new\n\n    # Drain mode\n    @draining = false\n    @drained_subs = false\n\n    # NKEYS\n    @user_credentials = options[:user_credentials] if options[:user_credentials]\n    @nkeys_seed = options[:nkeys_seed] if options[:nkeys_seed]\n    @user_nkey_cb = nil\n    @user_jwt_cb = nil\n    @signature_cb = nil\n\n    # NKEYS\n    setup_nkeys_connect if @user_credentials or @nkeys_seed\n  end\n\n  # Publish a message to a given subject, with optional reply subject and completion block\n  # @param [String] subject\n  # @param [Object, #to_s] msg\n  # @param [String] opt_reply\n  # @param [Block] blk, closure called when publish has been processed by the server.\n  def publish(subject, msg=EMPTY_MSG, opt_reply=nil, &blk)\n    return unless subject and not @drained_subs\n    msg = msg.to_s\n\n    # Accounting\n    @msgs_sent += 1\n    @bytes_sent += msg.bytesize if msg\n\n    send_command(\"PUB #{subject} #{opt_reply} #{msg.bytesize}#{CR_LF}#{msg}#{CR_LF}\")\n    queue_server_rt(&blk) if blk\n  end\n\n  # Subscribe to a subject with optional wildcards.\n  # Messages will be delivered to the supplied callback.\n  # Callback can take any number of the supplied arguments as defined by the list: msg, reply, sub.\n  # Returns subscription id which can be passed to #unsubscribe.\n  # @param [String] subject, optionally with wilcards.\n  # @param [Hash] opts, optional options hash, e.g. :queue, :max.\n  # @param [Block] callback, called when a message is delivered.\n  # @return [Object] sid, Subject Identifier\n  def subscribe(subject, opts={}, &callback)\n    return unless subject and not draining?\n    sid = (@ssid += 1)\n    sub = @subs[sid] = { :subject => subject, :callback => callback, :received => 0 }\n    sub[:queue] = opts[:queue] if opts[:queue]\n    sub[:max] = opts[:max] if opts[:max]\n    send_command(\"SUB #{subject} #{opts[:queue]} #{sid}#{CR_LF}\")\n    # Setup server support for auto-unsubscribe\n    unsubscribe(sid, opts[:max]) if opts[:max]\n    sid\n  end\n\n  # Cancel a subscription.\n  # @param [Object] sid\n  # @param [Number] opt_max, optional number of responses to receive before auto-unsubscribing\n  def unsubscribe(sid, opt_max=nil)\n    return if draining?\n    opt_max_str = \" #{opt_max}\" unless opt_max.nil?\n    send_command(\"UNSUB #{sid}#{opt_max_str}#{CR_LF}\")\n    return unless sub = @subs[sid]\n    sub[:max] = opt_max\n    @subs.delete(sid) unless (sub[:max] && (sub[:received] < sub[:max]))\n  end\n\n  # Drain gracefully closes the connection.\n  # @param [Block] blk called when drain is done and connection is closed.\n  def drain(&blk)\n    return if draining? or closing?\n    @draining = true\n\n    # Remove interest in all subjects to stop receiving messages.\n    @subs.each do |sid, _|\n      send_command(\"UNSUB #{sid} #{CR_LF}\")\n    end\n\n    # Roundtrip to ensure no more messages are received.\n    flush do\n      drain_timeout_timer, draining_timer = nil, nil\n      drain_timeout_timer = EM.add_timer(options[:drain_timeout]) do\n        EM.cancel_timer(draining_timer)\n\n        # Report the timeout via the error callback and just close\n        err_cb.call(NATS::ClientError.new(\"Drain Timeout\"))\n        @draining = false\n        close unless closing?\n        blk.call if blk\n      end\n\n      # Periodically check for the pending data to be empty.\n      draining_timer = EM.add_periodic_timer(0.1) do\n        next unless closing? or @buf.nil? or @buf.empty?\n\n        # Subscriptions have been drained already so disallow publishing.\n        @drained_subs = true\n        next unless pending_data_size == 0\n        EM.cancel_timer(draining_timer)\n        EM.cancel_timer(drain_timeout_timer)\n\n        # We're done draining and can close now.\n        @draining = false\n        close unless closing?\n        blk.call if blk\n      end\n    end\n  end\n\n  # Return the active subscription count.\n  # @return [Number]\n  def subscription_count\n    @subs.size\n  end\n\n  # Setup a timeout for receiving messages for the subscription.\n  # @param [Object] sid\n  # @param [Number] timeout, float in seconds\n  # @param [Hash] opts, options, :auto_unsubscribe(true), :expected(1)\n  def timeout(sid, timeout, opts={}, &callback)\n    # Setup a timeout if requested\n    return unless sub = @subs[sid]\n\n    auto_unsubscribe, expected = true, 1\n    auto_unsubscribe = opts[:auto_unsubscribe] if opts.key?(:auto_unsubscribe)\n    expected = opts[:expected] if opts.key?(:expected)\n\n    EM.cancel_timer(sub[:timeout]) if sub[:timeout]\n\n    sub[:timeout] = EM.add_timer(timeout) do\n      unsubscribe(sid) if auto_unsubscribe\n      callback.call(sid) if callback\n    end\n    sub[:expected] = expected\n  end\n\n  # Send a request and have the response delivered to the supplied callback.\n  # @param [String] subject\n  # @param [Object] msg\n  # @param [Block] callback\n  # @return [Object] sid\n  def request(subject, data=nil, opts={}, &cb)\n    return unless subject\n\n    # In case of using async request then fallback to auto unsubscribe\n    # based request/response and not break compatibility too much since\n    # new request/response style can only be used with fibers.\n    if cb\n      inbox = \"_INBOX.#{@nuid.next}\"\n      s = subscribe(inbox, opts) { |msg, reply|\n        case cb.arity\n        when 0 then cb.call\n        when 1 then cb.call(msg)\n        else cb.call(msg, reply)\n        end\n      }\n      publish(subject, data, inbox)\n      return s\n    end\n\n    # If this is the first request being made, then need to start\n    # the responses mux handler that handles the responses.\n    start_resp_mux_sub! unless @resp_sub_prefix\n\n    # Generate unique token for the reply subject.\n    token = @nuid.next\n    inbox = \"#{@resp_sub_prefix}.#{token}\"\n\n    # Synchronous request/response requires using a Fiber\n    # to be able to await the response.\n    f = Fiber.current\n    @resp_map[token][:fiber] = f\n\n    # If awaiting more than a single response then use array\n    # to include all that could be gathered before the deadline.\n    expected = opts[:max] ||= 1\n    @resp_map[token][:expected] = expected\n    @resp_map[token][:msgs] = [] if expected > 1\n\n    # Announce the request with the inbox using the token.\n    publish(subject, data, inbox)\n\n    # If deadline expires, then discard the token and resume fiber\n    opts[:timeout] ||= 0.5\n    t = EM.add_timer(opts[:timeout]) do\n      if expected > 1\n        f.resume @resp_map[token][:msgs]\n      else\n        f.resume\n      end\n\n      @resp_map.delete(token)\n    end\n\n    # Wait for the response and cancel timeout callback if received.\n    if expected > 1\n      # Wait to receive all replies that can get before deadline.\n      msgs = Fiber.yield\n      EM.cancel_timer(t)\n\n      # Slice and throwaway responses that are not needed.\n      return msgs.slice(0, expected)\n    else\n      msg = Fiber.yield\n      EM.cancel_timer(t)\n      return msg\n    end\n  end\n\n  def start_resp_mux_sub!\n    @resp_sub_prefix = \"_INBOX.#{@nuid.next}\"\n    @resp_map = Hash.new { |h,k| h[k] = { }}\n\n    # Single subscription that will be handling all the requests\n    # using fibers to yield the responses.\n    subscribe(\"#{@resp_sub_prefix}.*\") do |msg, reply, subject|\n      token = subject.split('.').last\n\n      # Discard the response if requestor not interested already.\n      next unless @resp_map.key? token\n\n      # Take fiber that will be passed the response\n      f = @resp_map[token][:fiber]\n      expected = @resp_map[token][:expected]\n\n      if expected == 1\n        f.resume msg\n        @resp_map.delete(token)\n        next\n      end\n\n      if @resp_map[token][:msgs].size < expected\n        @resp_map[token][:msgs] << msg\n\n        msgs = @resp_map[token][:msgs]\n        if msgs.size >= expected\n          f.resume(msgs)\n        else\n          # Wait to gather more messages or timeout.\n          next\n        end\n      end\n\n      @resp_map.delete(token)\n    end\n  end\n\n  # Flushes all messages and subscriptions for the connection.\n  # All messages and subscriptions have been processed by the server\n  # when the optional callback is called.\n  def flush(&blk)\n    queue_server_rt(&blk) if blk\n  end\n\n  # Define a callback to be called when the client connection has been established.\n  # @param [Block] callback\n  def on_connect(&callback)\n    @connect_cb = callback\n  end\n\n  # Define a callback to be called when errors occur on the client connection.\n  # @param [Block] &callback called when an error has been detected.\n  def on_error(&callback)\n    @err_cb, @err_cb_overridden = callback, true\n  end\n\n  # Define a callback to be called when a reconnect attempt is made.\n  # @param [Block] &callback called when a reconnect attempt is made.\n  def on_reconnect(&callback)\n    @reconnect_cb = callback\n  end\n\n  # Define a callback to be called when client is disconnected from server.\n  # @param [Block] &callback called whenever client disconnects from a server.\n  def on_disconnect(&callback)\n    @disconnect_cb = callback\n  end\n\n  # Define a callback to be called when client is disconnected from server.\n  # @param [Block] &callback called when will reach a state when will no longer be connected.\n  def on_close(&callback)\n    @close_cb = callback\n  end\n\n  # Close the connection to the server.\n  def close\n    @closing = true\n    cancel_ping_timer\n    cancel_reconnect_timer\n    close_connection_after_writing if connected?\n    process_disconnect if reconnecting?\n  end\n\n  # Return bytes outstanding waiting to be sent to server.\n  def pending_data_size\n    get_outbound_data_size + @pending_size\n  end\n\n  # Return snapshot of current traffic flow stats in the client.\n  def stats\n    {\n      in_msgs: @msgs_received,\n      out_msgs: @msgs_sent,\n      in_bytes: @bytes_received,\n      out_bytes: @bytes_sent\n    }.freeze\n  end\n\n  def user_err_cb? # :nodoc:\n    err_cb_overridden || NATS.err_cb_overridden\n  end\n\n  def auth_connection?\n    !@uri.user.nil? || @options[:token] || @server_info[:auth_required]\n  end\n\n  def connect_command #:nodoc:\n    cs = {\n      :verbose => @options[:verbose],\n      :pedantic => @options[:pedantic],\n      :lang => ::NATS::LANG,\n      :version => ::NATS::VERSION,\n      :protocol => ::NATS::PROTOCOL_VERSION,\n      :echo => !@options[:no_echo]\n    }\n\n    case\n    when @options[:user_credentials]\n      nonce = @server_info[:nonce]\n      cs[:jwt] = @user_jwt_cb.call\n      cs[:sig] = @signature_cb.call(nonce)\n    when @options[:nkeys_seed]\n      nonce = @server_info[:nonce]\n      cs[:nkey] = @user_nkey_cb.call\n      cs[:sig] = @signature_cb.call(nonce)\n    when @options[:token]\n      cs[:auth_token] = @options[:token]\n    when @uri.password.nil?\n      cs[:auth_token] = @uri.user\n    else\n      cs[:user] = @uri.user\n      cs[:pass] = @uri.password\n    end if auth_connection?\n\n    cs[:name] = @options[:name] if @options[:name]\n    cs[:ssl_required] = @ssl if @ssl\n    cs[:tls_required] = true if @tls\n\n    \"CONNECT #{cs.to_json}#{CR_LF}\"\n  end\n\n  def send_connect_command #:nodoc:\n    send_command(connect_command, true)\n  end\n\n  def queue_server_rt(&cb) #:nodoc:\n    return unless cb\n    (@pongs ||= []) << cb\n    send_command(PING_REQUEST)\n  end\n\n  def on_msg(subject, sid, reply, msg) #:nodoc:\n\n    # Accounting - We should account for inbound even if they are not processed.\n    @msgs_received += 1\n    @bytes_received += msg.bytesize if msg\n\n    return unless sub = @subs[sid]\n\n    # Check for auto_unsubscribe\n    sub[:received] += 1\n    if sub[:max]\n      # Client side support in case server did not receive unsubscribe\n      return unsubscribe(sid) if (sub[:received] > sub[:max])\n      # cleanup here if we have hit the max..\n      @subs.delete(sid) if (sub[:received] == sub[:max])\n    end\n\n    if cb = sub[:callback]\n      case cb.arity\n        when 0 then cb.call\n        when 1 then cb.call(msg)\n        when 2 then cb.call(msg, reply)\n        else cb.call(msg, reply, subject)\n      end\n    end\n\n    # Check for a timeout, and cancel if received >= expected\n    if (sub[:timeout] && sub[:received] >= sub[:expected])\n      EM.cancel_timer(sub[:timeout])\n      sub[:timeout] = nil\n    end\n  end\n\n  def flush_pending #:nodoc:\n    return unless @pending\n    send_data(@pending.join)\n    @pending, @pending_size = nil, 0\n  end\n\n  def receive_data(data) #:nodoc:\n    @buf = @buf ? @buf << data : data\n\n    while (@buf)\n      case @parse_state\n      when AWAITING_INFO_LINE\n        case @buf\n        when INFO\n          @buf = $'\n          process_connect_init($1)\n        else\n          # If we are here we do not have a complete line yet that we understand.\n          return\n        end\n      when AWAITING_CONTROL_LINE\n        case @buf\n        when MSG\n          @buf = $'\n          @sub, @sid, @reply, @needed = $1, $2.to_i, $4, $5.to_i\n          @parse_state = AWAITING_MSG_PAYLOAD\n        when OK # No-op right now\n          @buf = $'\n        when ERR\n          @buf = $'\n          current = server_pool.first\n          current[:error_received] = true\n          if current[:auth_required] && !current[:auth_ok]\n            err_cb.call(NATS::AuthError.new($1))\n          else\n            err_cb.call(NATS::ServerError.new($1))\n          end\n        when PING\n          @pings += 1\n          @buf = $'\n          send_command(PONG_RESPONSE)\n        when PONG\n          @buf = $'\n          cb = @pongs.shift\n          cb.call if cb\n        when INFO\n          @buf = $'\n          process_info($1)\n        when UNKNOWN\n          @buf = $'\n          err_cb.call(NATS::ServerError.new(\"Unknown protocol: #{$1}\"))\n        else\n          # If we are here we do not have a complete line yet that we understand.\n          return\n        end\n        @buf = nil if (@buf && @buf.empty?)\n\n      when AWAITING_MSG_PAYLOAD\n        return unless (@needed && @buf.bytesize >= (@needed + CR_LF_SIZE))\n        on_msg(@sub, @sid, @reply, @buf.slice(0, @needed))\n        @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)\n        @sub = @sid = @reply = @needed = nil\n        @parse_state = AWAITING_CONTROL_LINE\n        @buf = nil if (@buf && @buf.empty?)\n      end\n    end\n  end\n\n  def process_connect_init(info) # :nodoc:\n    # Each JSON parser uses a different key/value pair to use symbol keys\n    # instead of strings when parsing. Passing all three pairs assures each\n    # parser gets what it needs. For the json gem :symbolize_name, for yajl\n    # :symbolize_keys, and for oj :symbol_keys.\n    @server_info = JSON.parse(info, :symbolize_keys => true, :symbolize_names => true, :symbol_keys => true)\n\n    case\n    when (server_using_secure_connection? and client_using_secure_connection?)\n      # Allow parameterizing secure connection via EM#start_tls directly if present.\n      start_tls(@tls || {})\n    when (server_using_secure_connection? and !client_using_secure_connection?)\n      # Call unbind since there is a configuration mismatch between client/server\n      # anyway and communication cannot happen in this state.\n      err_cb.call(NATS::ClientError.new('TLS/SSL required by server'))\n      close_connection_after_writing\n    when (client_using_secure_connection? and !server_using_secure_connection?)\n      err_cb.call(NATS::ClientError.new('TLS/SSL not supported by server'))\n      close_connection_after_writing\n    else\n      # Otherwise, use a regular connection.\n    end\n\n    # Check whether there no echo is supported by the server.\n    if @options[:no_echo]\n      if @server_info[:proto].nil? || @server_info[:proto] < 1\n        err_cb.call(NATS::ServerError.new('No echo option not supported by this server'))\n        close_connection_after_writing\n      end\n    end\n    send_connect_command\n\n    # Only initial INFO command is treated specially for auth reasons,\n    # the rest are processed asynchronously to discover servers.\n    @parse_state = AWAITING_CONTROL_LINE\n    process_info(info)\n    process_connect\n\n    if @server_info[:auth_required]\n      current = server_pool.first\n      current[:auth_required] = true\n\n      # Send pending connect followed by ping/pong to ensure we're authorized.\n      queue_server_rt { current[:auth_ok] = true }\n    end\n    flush_pending\n  end\n\n  def process_info(info_line) #:nodoc:\n    info = JSON.parse(info_line, :symbolize_keys => true, :symbolize_names => true, :symbol_keys => true)\n\n    # Detect any announced server that we might not be aware of...\n    connect_urls = info[:connect_urls]\n    if connect_urls\n      srvs = []\n\n      connect_urls.each do |url|\n        u = URI.parse(\"nats://#{url}\")\n        present = server_pool.detect do |srv|\n          srv[:uri].host == u.host && srv[:uri].port == u.port\n        end\n\n        if not present\n          # Let explicit user and pass options set the credentials.\n          u.user = options[:user] if options[:user]\n          u.password = options[:pass] if options[:pass]\n\n          # Use creds from the current server if not set explicitly.\n          if @uri and !@uri.user.nil? and !@uri.user.empty?\n            u.user ||= @uri.user\n            u.password ||= @uri.password\n          end\n\n          srvs << { :uri => u, :reconnect_attempts => 0, :discovered => true }\n        end\n      end\n      srvs.shuffle! unless @options[:dont_randomize_servers]\n\n      # Include in server pool but keep current one as the first one.\n      server_pool.push(*srvs)\n    end\n\n    info\n  end\n\n  def client_using_secure_connection?\n    @tls || @ssl\n  end\n\n  def server_using_secure_connection?\n    @server_info[:ssl_required] || @server_info[:tls_required]\n  end\n\n  def ssl_verify_peer(cert)\n    incoming = OpenSSL::X509::Certificate.new(cert)\n    store = OpenSSL::X509::Store.new\n    store.set_default_paths\n    store.add_file @options[:tls][:ca_file]\n    result = store.verify(incoming)\n    err_cb.call(NATS::ConnectError.new('TLS Verification failed checking issuer based on CA %s' % @options[:tls][:ca_file])) unless result\n    result\n  rescue NATS::ConnectError\n    false\n  end\n\n  def cancel_ping_timer\n    if @ping_timer\n      EM.cancel_timer(@ping_timer)\n      @ping_timer = nil\n    end\n  end\n\n  def connection_completed #:nodoc:\n    @parse_state = AWAITING_INFO_LINE\n\n    # Delay sending CONNECT or any other command here until we are sure\n    # that we have a valid established secure connection.\n    return if (@ssl or @tls)\n\n    # Mark that we established already TCP connection to the server,\n    # when using TLS we only do so after handshake has been completed.\n    @connected = true\n  end\n\n  def ssl_handshake_completed\n    @connected = true\n  end\n\n  def process_connect #:nodoc:\n    # Reset reconnect attempts since TCP connection has been successful at this point.\n    current = server_pool.first\n    current[:was_connected] = true\n    current[:reconnect_attempts] ||= 0\n    cancel_reconnect_timer if reconnecting?\n\n    # Whip through any pending SUB commands since we replay\n    # all subscriptions already done anyway.\n    @pending.delete_if { |sub| sub[0..2] == SUB_OP } if @pending\n    @subs.each_pair { |k, v| send_command(\"SUB #{v[:subject]} #{v[:queue]} #{k}#{CR_LF}\") }\n\n    unless user_err_cb? or reconnecting?\n      @err_cb = proc { |e| raise e }\n    end\n\n    # We have validated the connection at this point so send CONNECT\n    # and any other pending commands which we need to the server.\n    flush_pending\n\n    if (connect_cb and not @conn_cb_called)\n      # We will round trip the server here to make sure all state from any pending commands\n      # has been processed before calling the connect callback.\n      queue_server_rt do\n        connect_cb.call(self)\n        @conn_cb_called = true\n      end\n    end\n\n    # Notify via reconnect callback that we are again plugged again into the system.\n    if reconnecting?\n      @reconnecting = false\n      @reconnect_cb.call(self) unless @reconnect_cb.nil?\n    end\n\n    # Initialize ping timer and processing\n    @pings_outstanding = 0\n    @pongs_received = 0\n    @ping_timer = EM.add_periodic_timer(@options[:ping_interval]) do\n      send_ping\n    end\n  end\n\n  def send_ping #:nodoc:\n    return if @closing\n    @pings_outstanding += 1\n    if @pings_outstanding > @options[:max_outstanding_pings]\n      close_connection\n      #close\n      return\n    end\n    queue_server_rt { process_pong }\n    flush_pending\n  end\n\n  def process_pong\n    @pongs_received += 1\n    @pings_outstanding -= 1\n  end\n\n  def should_delay_connect?(server)\n    case\n    when server[:was_connected]\n      server[:reconnect_attempts] >= 0\n    when server[:last_reconnect_attempt]\n      (MonotonicTime.now - server[:last_reconnect_attempt]) < @options[:reconnect_time_wait]\n    else\n      false\n    end\n  end\n\n  def schedule_reconnect #:nodoc:\n    @reconnecting = true\n    @connected = false\n    @reconnect_timer = EM.add_timer(@options[:reconnect_time_wait]) { attempt_reconnect }\n  end\n\n  def unbind #:nodoc:\n    # Allow notifying from which server we were disconnected,\n    # but only when we didn't trigger disconnecting ourselves.\n    if @disconnect_cb and connected? and not closing?\n      @disconnect_cb.call(NATS::ConnectError.new(disconnect_error_string))\n    end\n\n    # If we are closing or shouldn't reconnect, go ahead and disconnect.\n    process_disconnect and return if (closing? or should_not_reconnect?)\n    @reconnecting = true if connected?\n    @connected = false\n    @pending = @pongs = nil\n    @buf = nil\n    cancel_ping_timer\n\n    schedule_primary_and_connect\n  end\n\n  def multiple_servers_available?\n    server_pool && server_pool.size > 1\n  end\n\n  def had_error?\n    server_pool.first && server_pool.first[:error_received]\n  end\n\n  def should_not_reconnect?\n    !@options[:reconnect]\n  end\n\n  def cancel_reconnect_timer\n    if @reconnect_timer\n      EM.cancel_timer(@reconnect_timer)\n      @reconnect_timer = nil\n    end\n  end\n\n  def disconnect_error_string\n    return \"Client disconnected from server on #{@uri}\" if @connected\n    return \"Could not connect to server on #{@uri}\"\n  end\n\n  def process_disconnect #:nodoc:\n    # Mute error callback when user has called NATS.close on purpose.\n    if not closing? and @err_cb\n      # Always call error callback for compatibility with previous behavior.\n      err_cb.call(NATS::ConnectError.new(disconnect_error_string))\n    end\n    close_cb.call if @close_cb\n\n    true # Chaining\n  ensure\n    cancel_ping_timer\n    cancel_reconnect_timer\n    if (NATS.client == self)\n      NATS.clear_client\n      EM.stop if ((connected? || reconnecting?) and closing? and not NATS.reactor_was_running?)\n    end\n    @connected = @reconnecting = false\n  end\n\n  def can_reuse_server?(server) #:nodoc:\n    # If we will retry a number of times to reconnect to a server\n    # unless we got an error from it already.\n    reconnecting? && server[:reconnect_attempts] <= @options[:max_reconnect_attempts] && !server[:error_received]\n  end\n\n  def attempt_reconnect #:nodoc:\n    @reconnect_timer = nil\n    current = server_pool.first\n\n    # Snapshot time when trying to reconnect to server\n    # in order to back off for subsequent attempts.\n    current[:last_reconnect_attempt] = MonotonicTime.now\n    current[:reconnect_attempts] ||= 0\n    current[:reconnect_attempts] += 1\n\n    begin\n      EM.reconnect(@uri.host, @uri.port, self)\n    rescue\n      current[:error_received] = true\n      @uri = nil\n      @connected = false\n    end\n  end\n\n  def send_command(command, priority = false) #:nodoc:\n    needs_flush = (connected? && @pending.nil?)\n\n    @pending ||= []\n    @pending << command unless priority\n    @pending.unshift(command) if priority\n    @pending_size += command.bytesize\n\n    EM.next_tick { flush_pending } if needs_flush\n\n    flush_pending if (connected? && @pending_size > MAX_PENDING_SIZE)\n    if (@options[:fast_producer_error] && pending_data_size > FAST_PRODUCER_THRESHOLD)\n      err_cb.call(NATS::ClientError.new(\"Fast Producer: #{pending_data_size} bytes outstanding\"))\n    end\n    true\n  end\n\n  def setup_nkeys_connect\n    begin\n      require 'nkeys'\n      require 'base64'\n    rescue LoadError\n      raise(Error, \"nkeys is not installed\")\n    end\n\n    case\n    when @nkeys_seed\n      @user_nkey_cb = proc {\n        seed = File.read(@nkeys_seed).chomp\n        kp = NKEYS::from_seed(seed)\n\n        # Take a copy since original will be gone with the wipe.\n        pub_key = kp.public_key.dup\n        kp.wipe!\n\n        pub_key\n      }\n\n      @signature_cb = proc { |nonce|\n        seed = File.read(@nkeys_seed).chomp\n        kp = NKEYS::from_seed(seed)\n        raw_signed = kp.sign(nonce)\n        kp.wipe!\n        encoded = Base64.urlsafe_encode64(raw_signed)\n        encoded.gsub('=', '')\n      }\n    when @user_credentials\n      # When the credentials are within a single decorated file.\n      @user_jwt_cb = proc {\n        jwt_start = \"BEGIN NATS USER JWT\".freeze\n        found = false\n        jwt = nil\n        File.readlines(@user_credentials).each do |line|\n          case\n          when found\n            jwt = line.chomp\n            break\n          when line.include?(jwt_start)\n            found = true\n          end\n        end\n        raise(Error, \"No JWT found in #{@user_credentials}\") if not found\n\n        jwt\n      }\n\n      @signature_cb = proc { |nonce|\n        seed_start = \"BEGIN USER NKEY SEED\".freeze\n        found = false\n        seed = nil\n        File.readlines(@user_credentials).each do |line|\n          case\n          when found\n            seed = line.chomp\n            break\n          when line.include?(seed_start)\n            found = true\n          end\n        end\n        raise(Error, \"No nkey user seed found in #{@user_credentials}\") if not found\n\n        kp = NKEYS::from_seed(seed)\n        raw_signed = kp.sign(nonce)\n\n        # seed is a reference so also cleared when doing wipe,\n        # which can be done since Ruby strings are mutable.\n        kp.wipe\n        encoded = Base64.urlsafe_encode64(raw_signed)\n\n        # Remove padding\n        encoded.gsub('=', '')\n      }\n    end\n  end\n\n  # Parse out URIs which can now be an array of server choices\n  # The server pool will contain both explicit and implicit members.\n  def process_uri_options #:nodoc\n    @server_pool = []\n    uri = options[:uris] || options[:servers] || options[:uri]\n    uri = uri.kind_of?(Array) ? uri : [uri]\n    uri.each { |u| server_pool << { :uri => u.is_a?(URI) ? u.dup : URI.parse(u) } }\n    bind_primary\n  end\n\n  # @return [URI] Connected server\n  def connected_server\n    connected? ? @uri : nil\n  end\n\n  # Retrieves the list of servers which have been discovered\n  # via server connect_urls announcements\n  def discovered_servers\n    server_pool.select {|s| s[:discovered] }\n  end\n\n  def bind_primary #:nodoc:\n    first = server_pool.first\n    @uri = first[:uri]\n    @uri.user = options[:user] if options[:user]\n    @uri.password = options[:pass] if options[:pass]\n    first\n  end\n\n  # We have failed on an attempt at the primary (first) server, rotate and try again\n  def schedule_primary_and_connect #:nodoc:\n    # Dump the one we were trying if it wasn't connected\n    current = server_pool.shift\n\n    # In case there was an error from the server we will take it out from rotation\n    # unless we specify infinite reconnects via setting :max_reconnect_attempts to -1\n    if current && (options[:max_reconnect_attempts] < 0 || can_reuse_server?(current))\n      server_pool << current\n    end\n\n    # If we are out of options, go ahead and disconnect then\n    # handle closing connection to NATS.\n    process_disconnect and return if server_pool.empty?\n\n    # bind new one\n    next_server = bind_primary\n\n    # If the next one was connected and we are trying to reconnect\n    # set up timer if we tried once already.\n    if should_delay_connect?(next_server)\n      schedule_reconnect\n    else\n      attempt_reconnect\n      schedule_primary_and_connect if had_error?\n    end\n  end\n\n  def inspect #:nodoc:\n    \"<nats client v#{NATS::VERSION}>\"\n  end\n\n  class MonotonicTime\n    class << self\n      case\n      when defined?(Process::CLOCK_MONOTONIC)\n        def now\n          Process.clock_gettime(Process::CLOCK_MONOTONIC)\n        end\n      when RUBY_ENGINE == 'jruby'\n        def now\n          java.lang.System.nanoTime() / 1_000_000_000.0\n        end\n      else\n        def now\n          # Fallback to regular time behavior\n          ::Time.now.to_f\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/nats/ext/bytesize.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nif RUBY_VERSION <= \"1.8.6\"\n  class String #:nodoc:\n    def bytesize; self.size; end\n  end\nend\n"
  },
  {
    "path": "lib/nats/ext/em.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nbegin\n  require 'eventmachine'\nrescue LoadError\n  require 'rubygems'\n  require 'eventmachine'\nend\n\n# Check for get_outbound_data_size support, fake it out if it doesn't exist, e.g. jruby\nif !EM::Connection.method_defined? :get_outbound_data_size\n  class EM::Connection\n    def get_outbound_data_size; return 0; end\n  end\nend\n"
  },
  {
    "path": "lib/nats/ext/json.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nbegin\n  require 'yajl'\n  require 'yajl/json_gem'\nrescue LoadError\n  begin\n    require 'oj'\n    Oj.mimic_JSON()\n  rescue LoadError\n    require 'rubygems'\n    require 'json'\n  end\nend\n"
  },
  {
    "path": "lib/nats/nuid.rb",
    "content": "# Copyright 2016-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nrequire 'securerandom'\n\nmodule NATS\n  class NUID\n    DIGITS = [*'0'..'9', *'A'..'Z', *'a'..'z']\n    BASE          = 62\n    PREFIX_LENGTH = 12\n    SEQ_LENGTH    = 10\n    TOTAL_LENGTH  = PREFIX_LENGTH + SEQ_LENGTH\n    MAX_SEQ       = BASE**10\n    MIN_INC       = 33\n    MAX_INC       = 333\n    INC = MAX_INC - MIN_INC\n\n    def initialize\n      @prand    = Random.new\n      @seq      = @prand.rand(MAX_SEQ)\n      @inc      = MIN_INC + @prand.rand(INC)\n      @prefix   = ''\n      randomize_prefix!\n    end\n\n    def next\n      @seq += @inc\n      if @seq >= MAX_SEQ\n        randomize_prefix!\n        reset_sequential!\n      end\n      l = @seq\n\n      # Do this inline 10 times to avoid even more extra allocs,\n      # then use string interpolation of everything which works\n      # faster for doing concat.\n      s_10 = DIGITS[l % BASE];\n\n      # Ugly, but parallel assignment is slightly faster here...\n      s_09, s_08, s_07, s_06, s_05, s_04, s_03, s_02, s_01 = \\\n      (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE]),\\\n      (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE]),\\\n      (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE]), (l /= BASE; DIGITS[l % BASE])\n      \"#{@prefix}#{s_01}#{s_02}#{s_03}#{s_04}#{s_05}#{s_06}#{s_07}#{s_08}#{s_09}#{s_10}\"\n    end\n\n    def randomize_prefix!\n      @prefix = \\\n      SecureRandom.random_bytes(PREFIX_LENGTH).each_byte\n        .reduce('') do |prefix, n|\n        prefix << DIGITS[n % BASE]\n      end\n    end\n\n    private\n\n    def reset_sequential!\n      @seq = @prand.rand(MAX_SEQ)\n      @inc = MIN_INC + @prand.rand(INC)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/nats/server/cluster.rb",
    "content": "require 'uri'\n\nmodule NATSD #:nodoc: all\n\n  class Server\n    class << self\n      attr_reader :opt_routes, :route_auth_required, :route_ssl_required, :reconnect_interval\n      attr_accessor :num_routes\n\n      alias route_auth_required? :route_auth_required\n      alias route_ssl_required? :route_ssl_required\n\n      def connected_routes\n        @routes ||= []\n      end\n\n      def add_route(route)\n        connected_routes << route unless route.nil?\n      end\n\n      def remove_route(route)\n        connected_routes.delete(route) unless route.nil?\n      end\n\n      def route_info_string\n        @route_info = {\n          :server_id => Server.id,\n          :host => @options[:cluster_net] || host,\n          :port => @options[:cluster_port],\n          :version => VERSION,\n          :auth_required => route_auth_required?,\n          :ssl_required => false,                 # FIXME!\n          :max_payload => @max_payload\n        }\n        @route_info.to_json\n      end\n\n      def route_key(route_url)\n        r = URI.parse(route_url)\n        \"#{r.host}:#{r.port}\"\n      end\n\n      def route_auth_ok?(user, pass)\n        user == @options[:cluster_user] && pass == @options[:cluster_pass]\n      end\n\n      def solicit_routes #:nodoc:\n        @opt_routes = []\n        NATSD::Server.options[:cluster_routes].each do |r_url|\n          opt_routes << { :route => r_url, :uri => URI.parse(r_url), :key => route_key(r_url) }\n        end\n        try_to_connect_routes\n      end\n\n      def try_to_connect_routes #:nodoc:\n        opt_routes.each do |route|\n          # FIXME, Strip auth\n          debug \"Trying to connect to route: #{route[:route]}\"\n          EM.connect(route[:uri].host, route[:uri].port, NATSD::Route, route)\n        end\n      end\n\n      def broadcast_proto_to_routes(proto)\n        connected_routes.each { |r| r.queue_data(proto) }\n      end\n\n      def rsid_qsub(rsid)\n        cid, sid = parse_rsid(rsid)\n        conn = Server.connections[cid]\n        sub = conn.subscriptions[sid]\n        sub if sub.qgroup\n      rescue\n        nil\n      end\n\n      def parse_rsid(rsid)\n        m = RSID.match(rsid)\n        return [m[1].to_i, m[2]] if m\n      end\n\n      def routed_sid(sub)\n        \"RSID:#{sub.conn.cid}:#{sub.sid}\"\n      end\n\n      def route_sub_proto(sub)\n        return \"SUB #{sub.subject} #{routed_sid(sub)}#{CR_LF}\" if sub.qgroup.nil?\n        return \"SUB #{sub.subject} #{sub.qgroup} #{routed_sid(sub)}#{CR_LF}\"\n      end\n\n      def broadcast_sub_to_routes(sub)\n        broadcast_proto_to_routes(route_sub_proto(sub))\n      end\n\n      def broadcast_unsub_to_routes(sub)\n        opt_max_str = \" #{sub.max_responses}\" unless sub.max_responses.nil?\n        broadcast_proto_to_routes(\"UNSUB #{routed_sid(sub)}#{opt_max_str}#{CR_LF}\")\n      end\n\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/connection.rb",
    "content": "module NATSD #:nodoc: all\n\n  module Connection #:nodoc: all\n\n    attr_accessor :in_msgs, :out_msgs, :in_bytes, :out_bytes\n    attr_reader :cid, :closing, :last_activity, :writev_size, :subscriptions\n    alias :closing? :closing\n\n    def flush_data\n      return if @writev.nil? || closing?\n      send_data(@writev.join)\n      @writev, @writev_size = nil, 0\n    end\n\n    def queue_data(data)\n      EM.next_tick { flush_data } if @writev.nil?\n      (@writev ||= []) << data\n      @writev_size += data.bytesize\n      flush_data if @writev_size > MAX_WRITEV_SIZE\n    end\n\n    def client_info\n      cur_peername = get_peername\n      @client_info ||= (cur_peername.nil? ? 'N/A' : Socket.unpack_sockaddr_in(cur_peername))\n    end\n\n    def info\n      {\n        :cid => cid,\n        :ip => client_info[1],\n        :port => client_info[0],\n        :subscriptions => @subscriptions.size,\n        :pending_size => get_outbound_data_size,\n        :in_msgs => @in_msgs,\n        :out_msgs => @out_msgs,\n        :in_bytes => @in_bytes,\n        :out_bytes => @out_bytes\n      }\n    end\n\n    def max_connections_exceeded?\n      return false unless (Server.num_connections > Server.max_connections)\n      error_close MAX_CONNS_EXCEEDED\n      debug \"Maximum #{Server.max_connections} connections exceeded, c:#{cid} will be closed\"\n      true\n    end\n\n    def post_init\n      @cid = Server.cid\n      @subscriptions = {}\n      @verbose = @pedantic = true # suppressed by most clients, but allows friendly telnet\n      @in_msgs = @out_msgs = @in_bytes = @out_bytes = 0\n      @writev_size = 0\n      @parse_state = AWAITING_CONTROL_LINE\n      send_info\n      debug \"#{type} connection created\", client_info, cid\n      if Server.ssl_required?\n        debug \"Starting TLS/SSL\", client_info, cid\n        flush_data\n        @ssl_pending = EM.add_timer(NATSD::Server.ssl_timeout) { connect_ssl_timeout }\n        start_tls(:verify_peer => true) if Server.ssl_required?\n      end\n      @auth_pending = EM.add_timer(NATSD::Server.auth_timeout) { connect_auth_timeout } if Server.auth_required?\n      @ping_timer = EM.add_periodic_timer(NATSD::Server.ping_interval) { send_ping }\n      @pings_outstanding = 0\n      inc_connections\n      return if max_connections_exceeded?\n    end\n\n    def send_ping\n      return if @closing\n      if @pings_outstanding > NATSD::Server.ping_max\n        error_close UNRESPONSIVE\n        return\n      end\n      queue_data(PING_RESPONSE)\n      flush_data\n      @pings_outstanding += 1\n    end\n\n    def connect_auth_timeout\n      error_close AUTH_REQUIRED\n      debug \"#{type} connection timeout due to lack of auth credentials\", cid\n    end\n\n    def connect_ssl_timeout\n      error_close SSL_REQUIRED\n      debug \"#{type} connection timeout due to lack of TLS/SSL negotiations\", cid\n    end\n\n    def receive_data(data)\n      @buf = @buf ? @buf << data : data\n\n      while (@buf && !@closing)\n        case @parse_state\n        when AWAITING_CONTROL_LINE\n          case @buf\n          when PUB_OP\n            ctrace('PUB OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            @parse_state = AWAITING_MSG_PAYLOAD\n            @msg_sub, @msg_reply, @msg_size = $1, $3, $4.to_i\n            if (@msg_size > NATSD::Server.max_payload)\n              debug_print_msg_too_big(@msg_size)\n              error_close PAYLOAD_TOO_BIG\n            end\n            queue_data(INVALID_SUBJECT) if (@pedantic && !(@msg_sub =~ SUB_NO_WC))\n          when SUB_OP\n            ctrace('SUB OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            sub, qgroup, sid = $1, $3, $4\n            return queue_data(INVALID_SUBJECT) if !($1 =~ SUB)\n            return queue_data(INVALID_SID_TAKEN) if @subscriptions[sid]\n            sub = Subscriber.new(self, sub, sid, qgroup, 0)\n            @subscriptions[sid] = sub\n            Server.subscribe(sub)\n            queue_data(OK) if @verbose\n          when UNSUB_OP\n            ctrace('UNSUB OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            sid, sub = $1, @subscriptions[$1]\n            if sub\n              # If we have set max_responses, we will unsubscribe once we have received\n              # the appropriate amount of responses.\n              sub.max_responses = ($2 && $3) ? $3.to_i : nil\n              delete_subscriber(sub) unless (sub.max_responses && (sub.num_responses < sub.max_responses))\n              queue_data(OK) if @verbose\n            else\n              queue_data(INVALID_SID_NOEXIST) if @pedantic\n            end\n          when PING\n            ctrace('PING OP') if NATSD::Server.trace_flag?\n            @buf = $'\n            queue_data(PONG_RESPONSE)\n            flush_data\n          when PONG\n            ctrace('PONG OP') if NATSD::Server.trace_flag?\n            @buf = $'\n            @pings_outstanding -= 1\n          when CONNECT\n            ctrace('CONNECT OP', strip_op($&)) if NATSD::Server.trace_flag?\n            @buf = $'\n            begin\n              config = JSON.parse($1)\n              process_connect_config(config)\n            rescue => e\n              queue_data(INVALID_CONFIG)\n              log_error\n            end\n          when INFO_REQ\n            ctrace('INFO_REQUEST OP') if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            send_info\n          when UNKNOWN\n            ctrace('Unknown Op', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            queue_data(UNKNOWN_OP)\n          when CTRL_C # ctrl+c or ctrl+d for telnet friendly\n            ctrace('CTRL-C encountered', strip_op($&)) if NATSD::Server.trace_flag?\n            return close_connection\n          when CTRL_D # ctrl+d for telnet friendly\n            ctrace('CTRL-D encountered', strip_op($&)) if NATSD::Server.trace_flag?\n            return close_connection\n          else\n            # If we are here we do not have a complete line yet that we understand.\n            # If too big, cut the connection off.\n            if @buf.bytesize > NATSD::Server.max_control_line\n              debug_print_controlline_too_big(@buf.bytesize)\n              close_connection\n            end\n            return\n          end\n          @buf = nil if (@buf && @buf.empty?)\n\n        when AWAITING_MSG_PAYLOAD\n          return unless (@buf.bytesize >= (@msg_size + CR_LF_SIZE))\n          msg = @buf.slice(0, @msg_size)\n          ctrace('Processing msg', @msg_sub, @msg_reply, msg) if NATSD::Server.trace_flag?\n          queue_data(OK) if @verbose\n          Server.route_to_subscribers(@msg_sub, @msg_reply, msg)\n          @in_msgs += 1\n          @in_bytes += @msg_size\n          @buf = @buf.slice((@msg_size + CR_LF_SIZE), @buf.bytesize)\n          @msg_sub = @msg_size = @reply = nil\n          @parse_state = AWAITING_CONTROL_LINE\n          @buf = nil if (@buf && @buf.empty?)\n        end\n      end\n    end\n\n    def send_info\n      queue_data(\"INFO #{Server.info_string}#{CR_LF}\")\n    end\n\n    # Placeholder\n    def process_info(info)\n    end\n\n    def auth_ok?(user, pass)\n      Server.auth_ok?(user, pass)\n    end\n\n    def process_connect_config(config)\n      @verbose  = config['verbose'] unless config['verbose'].nil?\n      @pedantic = config['pedantic'] unless config['pedantic'].nil?\n\n      return queue_data(OK) unless Server.auth_required?\n\n      EM.cancel_timer(@auth_pending)\n      if auth_ok?(config['user'], config['pass'])\n        queue_data(OK) if @verbose\n        @auth_pending = nil\n      else\n        error_close AUTH_FAILED\n        debug \"Authorization failed for #{type.downcase} connection\", cid\n      end\n    end\n\n    def delete_subscriber(sub)\n      ctrace('DELSUB OP', sub.subject, sub.qgroup, sub.sid) if NATSD::Server.trace_flag?\n      Server.unsubscribe(sub, is_route?)\n      @subscriptions.delete(sub.sid)\n    end\n\n    def error_close(msg)\n      queue_data(msg)\n      flush_data\n      EM.next_tick { close_connection_after_writing }\n      @closing = true\n    end\n\n    def debug_print_controlline_too_big(line_size)\n      sizes = \"#{pretty_size(line_size)} vs #{pretty_size(NATSD::Server.max_control_line)} max\"\n      debug \"Control line size exceeded (#{sizes}), closing connection..\"\n    end\n\n    def debug_print_msg_too_big(msg_size)\n      sizes = \"#{pretty_size(msg_size)} vs #{pretty_size(NATSD::Server.max_payload)} max\"\n      debug \"Message payload size exceeded (#{sizes}), closing connection\"\n    end\n\n    def inc_connections\n      Server.num_connections += 1\n      Server.connections[cid] = self\n    end\n\n    def dec_connections\n      Server.num_connections -= 1\n      Server.connections.delete(cid)\n    end\n\n    def process_unbind\n      dec_connections\n      EM.cancel_timer(@ssl_pending) if @ssl_pending\n      @ssl_pending = nil\n      EM.cancel_timer(@auth_pending) if @auth_pending\n      @auth_pending = nil\n      EM.cancel_timer(@ping_timer) if @ping_timer\n      @ping_timer = nil\n      @subscriptions.each_value { |sub| Server.unsubscribe(sub) }\n      @closing = true\n    end\n\n    def unbind\n      debug \"Client connection closed\", client_info, cid\n      process_unbind\n    end\n\n    def ssl_handshake_completed\n      EM.cancel_timer(@ssl_pending)\n      @ssl_pending = nil\n      cert = get_peer_cert\n      debug \"#{type} Certificate:\", cert ? cert : 'N/A', cid\n    end\n\n    # FIXME! Cert accepted by default\n    def ssl_verify_peer(cert)\n      true\n    end\n\n    def ctrace(*args)\n      trace(args, \"c: #{cid}\")\n    end\n\n    def strip_op(op='')\n      op.dup.sub(CR_LF, EMPTY)\n    end\n\n    def is_route?\n      false\n    end\n\n    def type\n      'Client'\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/connz.rb",
    "content": "module NATSD #:nodoc: all\n\n  class Connz\n    def call(env)\n      c_info = Server.dump_connections\n      qs = env['QUERY_STRING']\n      if (qs =~ /n=(\\d+)/)\n        sort_key = :pending_size\n        n = $1.to_i\n        if (qs =~ /s=(\\S+)/)\n          case $1.downcase\n            when 'in_msgs'; sort_key = :in_msgs\n            when 'msgs_from'; sort_key = :in_msgs\n            when 'out_msgs'; sort_key = :out_msgs\n            when 'msgs_to'; sort_key = :out_msgs\n            when 'in_bytes'; sort_key = :in_bytes\n            when 'bytes_from'; sort_key = :in_bytes\n            when 'out_bytes'; sort_key = :out_bytes\n            when 'bytes_to'; sort_key = :out_bytes\n            when 'subs'; sort_key = :subscriptions\n            when 'subscriptions'; sort_key = :subscriptions\n          end\n        end\n        conns = c_info[:connections]\n        c_info[:connections] = conns.sort { |a,b| b[sort_key] <=> a[sort_key] } [0, n]\n      end\n      connz_json = JSON.pretty_generate(c_info) + \"\\n\"\n      hdrs = RACK_JSON_HDR.dup\n      hdrs['Content-Length'] = connz_json.bytesize.to_s\n      [200, hdrs, connz_json]\n    end\n  end\n\n  class Server\n    class << self\n      def dump_connections\n        conns, total = [], 0\n        ObjectSpace.each_object(NATSD::Connection) do |c|\n          next if c.closing?\n          total += c.info[:pending_size]\n          conns << c.info\n        end\n        {\n          :pending_size => total,\n          :num_connections => conns.size,\n          :connections => conns\n        }\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/const.rb",
    "content": "\nmodule NATSD #:nodoc:\n\n  VERSION  = '0.5.1'\n  APP_NAME = 'nats-server'\n\n  DEFAULT_PORT = 4222\n  DEFAULT_HOST = '0.0.0.0'\n\n  # Parser\n  AWAITING_CONTROL_LINE = 1\n  AWAITING_MSG_PAYLOAD  = 2\n\n  # Ops - See protocol.txt for more info\n  INFO     = /\\AINFO\\s*([^\\r\\n]*)\\r\\n/i\n  PUB_OP   = /\\APUB\\s+([^\\s]+)\\s+(([^\\s]+)[^\\S\\r\\n]+)?(\\d+)\\r\\n/i\n  MSG      = /\\AMSG\\s+([^\\s]+)\\s+([^\\s]+)\\s+(([^\\s]+)[^\\S\\r\\n]+)?(\\d+)\\r\\n/i\n  SUB_OP   = /\\ASUB\\s+([^\\s]+)\\s+(([^\\s]+)[^\\S\\r\\n]+)?([^\\s]+)\\r\\n/i\n  UNSUB_OP = /\\AUNSUB\\s+([^\\s]+)\\s*(\\s+(\\d+))?\\r\\n/i\n  PING     = /\\APING\\s*\\r\\n/i\n  PONG     = /\\APONG\\s*\\r\\n/i\n  INFO_REQ = /\\AINFO_REQ\\s*\\r\\n/i\n\n  CONNECT  = /\\ACONNECT\\s+([^\\r\\n]+)\\r\\n/i\n  UNKNOWN  = /\\A(.*)\\r\\n/\n  CTRL_C   = /\\006/\n  CTRL_D   = /\\004/\n\n  ERR_RESP = /\\A-ERR\\s+('.+')?\\r\\n/i\n  OK_RESP  = /\\A\\+OK\\s*\\r\\n/i #:nodoc:\n\n  # RESPONSES\n  CR_LF = \"\\r\\n\".freeze\n  CR_LF_SIZE = CR_LF.bytesize\n  EMPTY = ''.freeze\n  OK = \"+OK#{CR_LF}\".freeze\n  PING_RESPONSE = \"PING#{CR_LF}\".freeze\n  PONG_RESPONSE = \"PONG#{CR_LF}\".freeze\n  INFO_RESPONSE = \"#{CR_LF}\".freeze\n\n  # ERR responses\n  PAYLOAD_TOO_BIG     = \"-ERR 'Payload size exceeded'#{CR_LF}\".freeze\n  PROTOCOL_OP_TOO_BIG = \"-ERR 'Protocol Operation size exceeded'#{CR_LF}\".freeze\n  INVALID_SUBJECT     = \"-ERR 'Invalid Subject'#{CR_LF}\".freeze\n  INVALID_SID_TAKEN   = \"-ERR 'Invalid Subject Identifier (sid), already taken'#{CR_LF}\".freeze\n  INVALID_SID_NOEXIST = \"-ERR 'Invalid Subject-Identifier (sid), no subscriber registered'#{CR_LF}\".freeze\n  INVALID_CONFIG      = \"-ERR 'Invalid config, valid JSON required for connection configuration'#{CR_LF}\".freeze\n  AUTH_REQUIRED       = \"-ERR 'Authorization is required'#{CR_LF}\".freeze\n  AUTH_FAILED         = \"-ERR 'Authorization failed'#{CR_LF}\".freeze\n  SSL_REQUIRED        = \"-ERR 'TSL/SSL is required'#{CR_LF}\".freeze\n  SSL_FAILED          = \"-ERR 'TLS/SSL failed'#{CR_LF}\".freeze\n  UNKNOWN_OP          = \"-ERR 'Unknown Protocol Operation'#{CR_LF}\".freeze\n  SLOW_CONSUMER       = \"-ERR 'Slow consumer detected, connection dropped'#{CR_LF}\".freeze\n  UNRESPONSIVE        = \"-ERR 'Unresponsive client detected, connection dropped'#{CR_LF}\".freeze\n  MAX_CONNS_EXCEEDED  = \"-ERR 'Maximum client connections exceeded, connection dropped'#{CR_LF}\".freeze\n\n  # Pedantic Mode\n  SUB = /^([^\\.\\*>\\s]+|>$|\\*)(\\.([^\\.\\*>\\s]+|>$|\\*))*$/\n  SUB_NO_WC = /^([^\\.\\*>\\s]+)(\\.([^\\.\\*>\\s]+))*$/\n\n  # Router Subscription Identifiers\n  RSID = /RSID:(\\d+):(\\S+)/\n\n  # Some sane default thresholds\n\n  # 1k should be plenty since payloads sans connect string are separate\n  MAX_CONTROL_LINE_SIZE = 1024\n\n  # Should be using something different if > 1MB payload\n  MAX_PAYLOAD_SIZE = (1024*1024)\n\n  # Maximum outbound size per client\n  MAX_PENDING_SIZE = (10*1024*1024)\n\n  # Maximum pending bucket size\n  MAX_WRITEV_SIZE = (64*1024)\n\n  # Maximum connections default\n  DEFAULT_MAX_CONNECTIONS = (64*1024)\n\n  # TLS/SSL wait time\n  SSL_TIMEOUT = 0.5\n\n  # Authorization wait time\n  AUTH_TIMEOUT = SSL_TIMEOUT + 0.5\n\n  # Ping intervals\n  DEFAULT_PING_INTERVAL = 120\n  DEFAULT_PING_MAX = 2\n\n  # Route Reconnect\n  DEFAULT_ROUTE_RECONNECT_INTERVAL = 1.0\n\n  # HTTP\n  RACK_JSON_HDR = { 'Content-Type' => 'application/json' }\n  RACK_TEXT_HDR = { 'Content-Type' => 'text/plain' }\n\nend\n"
  },
  {
    "path": "lib/nats/server/options.rb",
    "content": "require 'optparse'\nrequire 'yaml'\n\nmodule NATSD\n  class Server\n\n    class << self\n      def parser\n        @parser ||= OptionParser.new do |opts|\n          opts.banner = \"Usage: nats-server [options]\"\n\n          opts.separator \"\"\n          opts.separator \"Server options:\"\n\n          opts.on(\"-a\", \"--addr HOST\", \"Bind to HOST address \" +\n                                       \"(default: #{DEFAULT_HOST})\")           { |host| @options[:addr] = host }\n          opts.on(\"-p\", \"--port PORT\", \"Use PORT (default: #{DEFAULT_PORT})\")  { |port| @options[:port] = port.to_i }\n          opts.on(\"-d\", \"--daemonize\", \"Run daemonized in the background\")     { @options[:daemonize] = true }\n          opts.on(\"-P\", \"--pid FILE\",  \"File to store PID\")                    { |file| @options[:pid_file] = file }\n\n          opts.on(\"-m\", \"--http_port PORT\", \"Use HTTP PORT \")                  { |port| @options[:http_port] = port.to_i }\n\n          opts.on(\"-r\", \"--cluster_port PORT\", \"Use Cluster PORT \")            { |port| @options[:cluster_port] = port.to_i }\n\n          opts.on(\"-c\", \"--config FILE\", \"Configuration File\")                 { |file| @options[:config_file] = file }\n\n          opts.separator \"\"\n          opts.separator \"Logging options:\"\n\n          opts.on(\"-l\", \"--log FILE\", \"File to redirect log output\")           { |file| @options[:log_file] = file }\n          opts.on(\"-T\", \"--logtime\", \"Timestamp log entries (default: false)\") { @options[:log_time] = true }\n          opts.on(\"-S\", \"--syslog IDENT\", \"Enable Syslog output\")              { |ident| @options[:syslog] = ident }\n          opts.on(\"-D\", \"--debug\", \"Enable debugging output\")                  { @options[:debug] = true }\n          opts.on(\"-V\", \"--trace\", \"Trace the raw protocol\")                   { @options[:trace] = true }\n\n          opts.separator \"\"\n          opts.separator \"Authorization options:\"\n\n          opts.on(\"--user user\", \"User required for connections\")              { |user| @options[:user] = user }\n          opts.on(\"--pass password\", \"Password required for connections\")      { |pass| @options[:pass] = pass }\n\n          opts.separator \"\"\n          opts.on(\"--ssl\", \"Enable SSL\")                                       { |ssl| @options[:ssl] = true }\n\n          opts.separator \"\"\n          opts.separator \"Advanced IO options:\"\n\n          opts.on(\"--no_epoll\", \"Disable epoll (Linux)\")                       { @options[:noepoll] = true }\n          opts.on(\"--no_kqueue\", \"Disable kqueue (MacOSX and BSD)\")            { @options[:nokqueue] = true }\n\n          opts.separator \"\"\n          opts.separator \"Common options:\"\n\n          opts.on_tail(\"-h\", \"--help\", \"Show this message\")                    { puts opts; exit }\n          opts.on_tail('-v', '--version', \"Show version\")                      { puts NATSD::Server.version; exit }\n        end\n      end\n\n      def read_config_file\n        return unless config_file = @options[:config_file]\n        config = File.open(config_file) { |f| YAML.load(f) }\n\n        # Command lines args, parsed first, will override these.\n        @options[:port] = config['port'] if @options[:port].nil?\n        @options[:addr] = config['net'] if @options[:addr].nil?\n\n        if auth = config['authorization']\n          @options[:user] = auth['user'] if @options[:user].nil?\n          @options[:pass] = auth['password'] if @options[:pass].nil?\n          @options[:pass] = auth['pass'] if @options[:pass].nil?\n          @options[:token] = auth['token'] if @options[:token].nil?\n          @options[:auth_timeout] = auth['timeout'] if @options[:auth_timeout].nil?\n          # Multiple Users setup\n          @options[:users] = symbolize_users(auth['users']) || []\n        end\n\n        # TLS/SSL\n        @options[:ssl] = config['ssl'] if @options[:ssl].nil?\n\n        @options[:pid_file] = config['pid_file'] if @options[:pid_file].nil?\n        @options[:log_file] = config['log_file'] if @options[:log_file].nil?\n        @options[:log_time] = config['logtime'] if @options[:log_time].nil?\n        @options[:syslog] = config['syslog'] if @options[:syslog].nil?\n        @options[:debug] = config['debug'] if @options[:debug].nil?\n        @options[:trace] = config['trace'] if @options[:trace].nil?\n\n        # these just override if present\n        @options[:max_control_line] = config['max_control_line'] if config['max_control_line']\n        @options[:max_payload] = config['max_payload'] if config['max_payload']\n        @options[:max_pending] = config['max_pending'] if config['max_pending']\n        @options[:max_connections] = config['max_connections'] if config['max_connections']\n\n        # just set\n        @options[:noepoll]  = config['no_epoll'] if config['no_epoll']\n        @options[:nokqueue] = config['no_kqueue'] if config['no_kqueue']\n\n        if http = config['http']\n          if @options[:http_net].nil?\n            @options[:http_net] = http['net'] || @options[:addr]\n          end\n          @options[:http_port] = http['port'] if @options[:http_port].nil?\n          @options[:http_user] = http['user'] if @options[:http_user].nil?\n          @options[:http_password] = http['password'] if @options[:http_password].nil?\n        end\n\n        if ping = config['ping']\n          @options[:ping_interval] = ping['interval'] if @options[:ping_interval].nil?\n          @options[:ping_max] = ping['max_outstanding'] if @options[:ping_max].nil?\n        end\n\n        if cluster = config['cluster']\n          @options[:cluster_port] = cluster['port'] if @options[:cluster_port].nil?\n          if auth = cluster['authorization']\n            @options[:cluster_user] = auth['user'] if @options[:cluster_user].nil?\n            @options[:cluster_pass] = auth['password'] if @options[:cluster_pass].nil?\n            @options[:cluster_pass] = auth['pass'] if @options[:cluster_pass].nil?\n            @options[:cluster_token] = auth['token'] if @options[:cluster_token].nil?\n            @options[:cluster_auth_timeout] = auth['timeout'] if @options[:cluster_auth_timeout].nil?\n            @route_auth_required = true\n          end\n          if routes = cluster['routes']\n            @options[:cluster_routes] = routes if @options[:cluster_routes].nil?\n          end\n        end\n\n      rescue => e\n        log \"Could not read configuration file:  #{e}\"\n        exit 1\n      end\n\n      def setup_logs\n        return unless @options[:log_file]\n        $stdout.reopen(@options[:log_file], 'a')\n        $stdout.sync = true\n        $stderr.reopen($stdout)\n      end\n\n      def open_syslog\n        return unless @options[:syslog]\n        Syslog.open(\"#{@options[:syslog]}\", Syslog::LOG_PID, Syslog::LOG_USER) unless Syslog.opened?\n      end\n\n      def close_syslog\n        Syslog.close if @options[:syslog]\n      end\n\n      def symbolize_users(users)\n        return nil unless users\n        auth_users = []\n        users.each do |u|\n          auth_users << { :user => u['user'], :pass => u['pass'] || u['password'] }\n        end\n        auth_users\n      end\n\n      def finalize_options\n        # Addr/Port\n        @options[:port] ||= DEFAULT_PORT\n        @options[:addr] ||= DEFAULT_HOST\n\n        # Max Connections\n        @options[:max_connections] ||= DEFAULT_MAX_CONNECTIONS\n        @max_connections = @options[:max_connections]\n\n        # Debug and Tracing\n        @debug_flag = @options[:debug]\n        @trace_flag = @options[:trace]\n\n        # Log timestamps\n        @log_time = @options[:log_time]\n\n        debug @options # Block pass?\n        debug \"DEBUG is on\"\n        trace \"TRACE is on\"\n\n        # Syslog\n        @syslog = @options[:syslog]\n\n        # Authorization\n\n        # Multi-user setup for auth\n        if @options[:user]\n          # Multiple Users setup\n          @options[:users] ||= []\n          @options[:users].unshift({:user => @options[:user], :pass => @options[:pass]}) if @options[:user]\n        elsif @options[:users]\n          first = @options[:users].first\n          @options[:user], @options[:pass] = first[:user], first[:pass]\n        end\n\n        @auth_required = (not @options[:user].nil?)\n\n        @ssl_required = @options[:ssl]\n\n        # Pings\n        @options[:ping_interval] ||= DEFAULT_PING_INTERVAL\n        @ping_interval = @options[:ping_interval]\n\n        @options[:ping_max] ||= DEFAULT_PING_MAX\n        @ping_max = @options[:ping_max]\n\n        # Thresholds\n        @options[:max_control_line] ||= MAX_CONTROL_LINE_SIZE\n        @max_control_line = @options[:max_control_line]\n\n        @options[:max_payload] ||= MAX_PAYLOAD_SIZE\n        @max_payload = @options[:max_payload]\n\n        @options[:max_pending] ||= MAX_PENDING_SIZE\n        @max_pending = @options[:max_pending]\n\n        @options[:auth_timeout] ||= AUTH_TIMEOUT\n        @auth_timeout = @options[:auth_timeout]\n\n        @options[:ssl_timeout] ||= SSL_TIMEOUT\n        @ssl_timeout = @options[:ssl_timeout]\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/nats/server/route.rb",
    "content": "module NATSD #:nodoc: all\n\n  # Need to make this a class with EM > 1.0\n  class Route < EventMachine::Connection #:nodoc:\n\n    include Connection\n\n    attr_reader :rid, :remote_rid, :closing, :r_obj, :reconnecting\n    alias :peer_info :client_info\n    alias :reconnecting? :reconnecting\n\n    def initialize(route=nil)\n      @r_obj = route\n    end\n\n    def solicited?\n      r_obj != nil\n    end\n\n    def connection_completed\n      debug \"Route connected\", rid\n      return unless reconnecting?\n\n      # Kill reconnect if we got here from there\n      cancel_reconnect\n      @buf, @closing = nil, false\n      post_init\n    end\n\n    def post_init\n      @rid = Server.rid\n      @subscriptions = {}\n      @in_msgs = @out_msgs = @in_bytes = @out_bytes = 0\n      @writev_size = 0\n      @parse_state = AWAITING_CONTROL_LINE\n\n      # Queue up auth if needed and we solicited the connection\n      debug \"Route connection created\", peer_info, rid\n\n      # queue up auth if needed and we solicited the connection\n      if solicited?\n        debug \"Route sent authorization\", rid\n        send_auth\n      else\n        # FIXME, separate variables for timeout?\n        @auth_pending = EM.add_timer(NATSD::Server.auth_timeout) { connect_auth_timeout } if Server.route_auth_required?\n      end\n\n      send_info\n      @ping_timer = EM.add_periodic_timer(NATSD::Server.ping_interval) { send_ping }\n      @pings_outstanding = 0\n      inc_connections\n      send_local_subs_to_route\n    end\n\n    # TODO: Make sure max_requested is also propogated on reconnect\n    def send_local_subs_to_route\n      ObjectSpace.each_object(NATSD::Connection) do |c|\n        next if c.closing? || c.type != 'Client'\n        c.subscriptions.each_value do |sub|\n          queue_data(NATSD::Server.route_sub_proto(sub))\n        end\n      end\n    end\n\n    def process_connect_route_config(config)\n      @verbose  = config['verbose'] unless config['verbose'].nil?\n      @pedantic = config['pedantic'] unless config['pedantic'].nil?\n\n      return queue_data(OK) unless Server.route_auth_required?\n\n      EM.cancel_timer(@auth_pending)\n      if auth_ok?(config['user'], config['pass'])\n        debug \"Route received proper credentials\", rid\n        queue_data(OK) if @verbose\n        @auth_pending = nil\n      else\n        error_close AUTH_FAILED\n        debug \"Authorization failed for #{type.downcase} connection\", rid\n      end\n    end\n\n    def connect_auth_timeout\n      error_close AUTH_REQUIRED\n      debug \"#{type} connection timeout due to lack of auth credentials\", rid\n    end\n\n    def receive_data(data)\n      @buf = @buf ? @buf << data : data\n      return close_connection if @buf =~ /(\\006|\\004)/ # ctrl+c or ctrl+d for telnet friendly\n\n      while (@buf && !@closing)\n        case @parse_state\n        when AWAITING_CONTROL_LINE\n          case @buf\n          when MSG\n            ctrace('MSG OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            @parse_state = AWAITING_MSG_PAYLOAD\n            @msg_sub, @msg_sid, @msg_reply, @msg_size = $1, $2, $4, $5.to_i\n            if (@msg_size > NATSD::Server.max_payload)\n              debug_print_msg_too_big(@msg_size)\n              error_close PAYLOAD_TOO_BIG\n            end\n            queue_data(INVALID_SUBJECT) if (@pedantic && !(@msg_sub =~ SUB_NO_WC))\n          when SUB_OP\n            ctrace('SUB OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            sub, qgroup, sid = $1, $3, $4\n            return queue_data(INVALID_SUBJECT) if !($1 =~ SUB)\n            return queue_data(INVALID_SID_TAKEN) if @subscriptions[sid]\n            sub = Subscriber.new(self, sub, sid, qgroup, 0)\n            @subscriptions[sid] = sub\n            Server.subscribe(sub, is_route?)\n            queue_data(OK) if @verbose\n          when UNSUB_OP\n            ctrace('UNSUB OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            sid, sub = $1, @subscriptions[$1]\n            if sub\n              # If we have set max_responses, we will unsubscribe once we have received\n              # the appropriate amount of responses.\n              sub.max_responses = ($2 && $3) ? $3.to_i : nil\n              delete_subscriber(sub) unless (sub.max_responses && (sub.num_responses < sub.max_responses))\n              queue_data(OK) if @verbose\n            else\n              queue_data(INVALID_SID_NOEXIST) if @pedantic\n            end\n          when PING\n            ctrace('PING OP') if NATSD::Server.trace_flag?\n            @buf = $'\n            queue_data(PONG_RESPONSE)\n            flush_data\n          when PONG\n            ctrace('PONG OP') if NATSD::Server.trace_flag?\n            @buf = $'\n            @pings_outstanding -= 1\n          when CONNECT\n            ctrace('CONNECT OP', strip_op($&)) if NATSD::Server.trace_flag?\n            @buf = $'\n            begin\n              config = JSON.parse($1)\n              process_connect_route_config(config)\n            rescue => e\n              queue_data(INVALID_CONFIG)\n              log_error\n            end\n          when INFO_REQ\n            ctrace('INFO_REQUEST OP') if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            send_info\n          when INFO\n            ctrace('INFO OP', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            process_info($1)\n          when ERR_RESP\n            ctrace('-ERR', $1) if NATSD::Server.trace_flag?\n            close_connection\n            exit\n          when OK_RESP\n            ctrace('+OK') if NATSD::Server.trace_flag?\n            @buf = $'\n          when UNKNOWN\n            ctrace('Unknown Op', strip_op($&)) if NATSD::Server.trace_flag?\n            return connect_auth_timeout if @auth_pending\n            @buf = $'\n            queue_data(UNKNOWN_OP)\n          else\n            # If we are here we do not have a complete line yet that we understand.\n            # If too big, cut the connection off.\n            if @buf.bytesize > NATSD::Server.max_control_line\n              debug_print_controlline_too_big(@buf.bytesize)\n              close_connection\n            end\n            return\n          end\n          @buf = nil if (@buf && @buf.empty?)\n\n        when AWAITING_MSG_PAYLOAD\n          return unless (@buf.bytesize >= (@msg_size + CR_LF_SIZE))\n          msg = @buf.slice(0, @msg_size)\n\n          ctrace('Processing routed msg', @msg_sub, @msg_reply, msg) if NATSD::Server.trace_flag?\n          queue_data(OK) if @verbose\n\n          # We deliver normal subscriptions like a client publish, which\n          # eliminates the duplicate traversal over the route. However,\n          # qgroups are sent individually per group for only the route\n          # with the intended subscriber, since route interest is L2\n          # semantics, we deliver those direct.\n          if (sub = Server.rsid_qsub(@msg_sid))\n            # Allows nil reply to not have extra space\n            reply = @msg_reply + ' ' if @msg_reply\n            Server.deliver_to_subscriber(sub, @msg_sub, reply, msg)\n          else\n            Server.route_to_subscribers(@msg_sub, @msg_reply, msg, is_route?)\n          end\n\n          @in_msgs += 1\n          @in_bytes += @msg_size\n          @buf = @buf.slice((@msg_size + CR_LF_SIZE), @buf.bytesize)\n          @msg_sub = @msg_size = @reply = nil\n          @parse_state = AWAITING_CONTROL_LINE\n          @buf = nil if (@buf && @buf.empty?)\n        end\n      end\n    end\n\n    def send_auth\n      return unless r_obj[:uri].user\n      cs = { :user => r_obj[:uri].user, :pass => r_obj[:uri].password }\n      queue_data(\"CONNECT #{cs.to_json}#{CR_LF}\")\n    end\n\n    def send_info\n      queue_data(\"INFO #{Server.route_info_string}#{CR_LF}\")\n    end\n\n    def process_info(info_json)\n      info = JSON.parse(info_json)\n      @remote_rid = info['server_id'] unless info['server_id'].nil?\n      super(info_json)\n    end\n\n    def auth_ok?(user, pass)\n      Server.route_auth_ok?(user, pass)\n    end\n\n    def inc_connections\n      Server.num_routes += 1\n      Server.add_route(self)\n    end\n\n    def dec_connections\n      Server.num_routes -= 1\n      Server.remove_route(self)\n    end\n\n    def try_reconnect\n      debug \"Trying to reconnect route\", peer_info, rid\n      EM.reconnect(r_obj[:uri].host, r_obj[:uri].port, self)\n    end\n\n    def cancel_reconnect\n      EM.cancel_timer(@reconnect_timer) if @reconnect_timer\n      @reconnect_timer = nil\n      @reconnecting = false\n    end\n\n    def unbind\n      return if reconnecting?\n      debug \"Route connection closed\", peer_info, rid\n      process_unbind\n      if solicited?\n        @reconnecting = true\n        @reconnect_timer = EM.add_periodic_timer(NATSD::DEFAULT_ROUTE_RECONNECT_INTERVAL) { try_reconnect }\n      end\n    end\n\n    def ctrace(*args)\n      trace(args, \"r: #{rid}\")\n    end\n\n    def is_route?\n      true\n    end\n\n    def type\n      'Route'\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/server.rb",
    "content": "require 'set'\n\nmodule NATSD #:nodoc: all\n\n  # Subscriber\n  Subscriber = Struct.new(:conn, :subject, :sid, :qgroup, :num_responses, :max_responses)\n\n  class Server\n\n    class << self\n      attr_reader :id, :info, :log_time, :auth_required, :ssl_required, :debug_flag, :trace_flag, :syslog, :options\n      attr_reader :max_payload, :max_pending, :max_control_line, :auth_timeout, :ssl_timeout, :ping_interval, :ping_max\n      attr_accessor :varz, :healthz, :connections, :max_connections, :num_connections, :in_msgs, :out_msgs, :in_bytes, :out_bytes\n\n      alias auth_required? :auth_required\n      alias ssl_required?  :ssl_required\n      alias debug_flag?    :debug_flag\n      alias trace_flag?    :trace_flag\n\n      def version; \"nats-server version #{NATSD::VERSION}\" end\n\n      def host; @options[:addr] end\n      def port; @options[:port] end\n      def pid_file; @options[:pid_file] end\n\n      def process_options(argv=[])\n        @options = {}\n\n        # Allow command line to override config file, so do them first.\n        parser.parse!(argv)\n        read_config_file if @options[:config_file]\n        finalize_options\n      rescue OptionParser::InvalidOption => e\n        log_error \"Error parsing options: #{e}\"\n        exit(1)\n      end\n\n      def setup(argv)\n        process_options(argv)\n\n        @id, @cid, @rid = fast_uuid, 1, 1\n        @sublist = Sublist.new\n\n        @connections = {}\n        @num_connections = 0\n        @in_msgs = @out_msgs = 0\n        @in_bytes = @out_bytes = 0\n\n        @num_routes = 0\n\n        @info = {\n          :server_id => Server.id,\n          :host => host,\n          :port => port,\n          :version => VERSION,\n          :auth_required => auth_required?,\n          :ssl_required => ssl_required?,\n          :max_payload => @max_payload\n        }\n\n        # Check for daemon flag\n        if @options[:daemonize]\n          require 'rubygems'\n          require 'daemons'\n          require 'tmpdir'\n          unless @options[:log_file]\n            # These log messages visible to controlling TTY\n            log \"Starting #{NATSD::APP_NAME} version #{NATSD::VERSION} on port #{NATSD::Server.port}\"\n            log \"Starting http monitor on port #{@options[:http_port]}\" if @options[:http_port]\n            log \"Starting routing on port #{@options[:cluster_port]}\" if @options[:cluster_port]\n            log \"Switching to daemon mode\"\n          end\n          opts = {\n            :app_name => APP_NAME,\n            :mode => :exec,\n            :dir_mode => :normal,\n            :dir => Dir.tmpdir\n          }\n          Daemons.daemonize(opts)\n          FileUtils.rm_f(\"#{Dir.tmpdir}/#{APP_NAME}.pid\")\n        end\n\n        setup_logs\n        open_syslog\n\n        # Setup optimized select versions\n        EM.epoll unless @options[:noepoll]\n        EM.kqueue unless @options[:nokqueue]\n\n        # Write pid file if requested.\n        File.open(@options[:pid_file], 'w') { |f| f.puts \"#{Process.pid}\" } if @options[:pid_file]\n      end\n\n      def subscribe(sub, is_route=false)\n        @sublist.insert(sub.subject, sub)\n        broadcast_sub_to_routes(sub) unless is_route\n      end\n\n      def unsubscribe(sub, is_route=false)\n        @sublist.remove(sub.subject, sub)\n        broadcast_unsub_to_routes(sub) unless is_route\n      end\n\n      def deliver_to_subscriber(sub, subject, reply, msg)\n        conn = sub.conn\n\n        # Accounting\n        @out_msgs += 1\n        conn.out_msgs += 1\n        unless msg.nil?\n          mbs = msg.bytesize\n          @out_bytes += mbs\n          conn.out_bytes += mbs\n        end\n\n        conn.queue_data(\"MSG #{subject} #{sub.sid} #{reply}#{msg.bytesize}#{CR_LF}#{msg}#{CR_LF}\")\n\n        # Account for these response and check for auto-unsubscribe (pruning interest graph)\n        sub.num_responses += 1\n        conn.delete_subscriber(sub) if (sub.max_responses && sub.num_responses >= sub.max_responses)\n\n        # Check the outbound queue here and react if need be..\n        if (conn.get_outbound_data_size + conn.writev_size) > NATSD::Server.max_pending\n          conn.error_close SLOW_CONSUMER\n          maxp = pretty_size(NATSD::Server.max_pending)\n          log \"Slow consumer dropped, exceeded #{maxp} pending\", conn.client_info\n        end\n      end\n\n      def route_to_subscribers(subject, reply, msg, is_route=false)\n        qsubs = nil\n\n        # Allows nil reply to not have extra space\n        reply = reply + ' ' if reply\n\n        # Accounting\n        @in_msgs += 1\n        @in_bytes += msg.bytesize unless msg.nil?\n\n        # Routes\n        routes = nil\n\n        @sublist.match(subject).each do |sub|\n          # Skip anyone in the closing state\n          next if sub.conn.closing\n\n          # Skip all routes if sourced from another route (1-hop semantics)\n          next if (is_route && sub.conn.is_route?)\n\n          if sub[:qgroup].nil?\n            if sub.conn.is_route?\n              # Only send messages once over a given route\n              routes ||= Set.new\n              deliver_to_subscriber(sub, subject, reply, msg) unless routes.include?(sub.conn.remote_rid)\n              routes << sub.conn.remote_rid\n            else\n              deliver_to_subscriber(sub, subject, reply, msg)\n            end\n          elsif !is_route\n            if NATSD::Server.trace_flag?\n              trace('Matched queue subscriber', sub[:subject], sub[:qgroup], sub[:sid], sub.conn.client_info)\n            end\n            # Queue this for post processing\n            qsubs ||= Hash.new\n            qsubs[sub[:qgroup]] ||= []\n            qsubs[sub[:qgroup]] << sub\n          end\n        end\n\n        return unless qsubs\n\n        qsubs.each_value do |subs|\n          # Randomly pick a subscriber from the group\n          sub = subs[rand*subs.size]\n          if NATSD::Server.trace_flag?\n            trace('Selected queue subscriber', sub[:subject], sub[:qgroup], sub[:sid], sub.conn.client_info)\n          end\n          deliver_to_subscriber(sub, subject, reply, msg)\n        end\n      end\n\n      def auth_ok?(user, pass)\n        @options[:users].each { |u| return true if (user == u[:user] && pass == u[:pass]) }\n        false\n      end\n\n      def cid\n        @cid += 1\n      end\n\n      def rid\n        @rid += 1\n      end\n\n      def info_string\n        @info.to_json\n      end\n\n      # Monitoring\n      def start_http_server\n        return unless port = @options[:http_port]\n\n        require 'thin'\n\n        log \"Starting http monitor on port #{port}\"\n\n        @healthz = \"ok\\n\"\n\n        @varz = {\n          :start => Time.now,\n          :options => @options,\n          :cores => num_cpu_cores\n        }\n\n        http_server = Thin::Server.new(@options[:http_net], port, :signals => false) do\n          Thin::Logging.silent = true\n          if NATSD::Server.options[:http_user]\n            auth = [NATSD::Server.options[:http_user], NATSD::Server.options[:http_password]]\n            use Rack::Auth::Basic do |username, password|\n              [username, password] == auth\n            end\n          end\n          map '/healthz' do\n            run lambda { |env| [200, RACK_TEXT_HDR, NATSD::Server.healthz] }\n          end\n          map '/varz' do\n            run Varz.new\n          end\n          map '/connz' do\n            run Connz.new\n          end\n        end\n        http_server.start!\n      end\n\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/sublist.rb",
    "content": "#--\n#\n# Sublist implementation for a publish-subscribe system.\n# This container class holds subscriptions and matches\n# candidate subjects to those subscriptions.\n# Certain wildcards are supported for subscriptions.\n# '*' will match any given token at any level.\n# '>' will match all subsequent tokens.\n#--\n# See included test for example usage:\n##\n\nclass Sublist #:nodoc:\n  PWC = '*'.freeze\n  FWC = '>'.freeze\n  CACHE_SIZE = 4096\n\n  attr_reader :count\n\n  SublistNode  = Struct.new(:leaf_nodes, :next_level)\n  SublistLevel = Struct.new(:nodes, :pwc, :fwc)\n\n  EMPTY_LEVEL = SublistLevel.new({})\n\n  def initialize(options = {})\n    @count = 0\n    @results = []\n    @root = SublistLevel.new({})\n    @cache = {}\n  end\n\n  # Ruby is a great language to make selective trade offs of space versus time.\n  # We do that here with a low tech front end cache. The cache holds results\n  # until it is exhausted or if the instance inserts or removes a subscription.\n  # The assumption is that the cache is best suited for high speed matching,\n  # and that once it is cleared out it will naturally fill with the high speed\n  # matches. This can obviously be improved with a smarter LRU structure that\n  # does not need to completely go away when a remove happens..\n  #\n  # front end caching is on by default, but we can turn it off here if needed\n\n  def disable_cache; @cache = nil; end\n  def enable_cache;  @cache ||= {};  end\n  def clear_cache; @cache = {} if @cache; end\n\n  # Random removal\n  def prune_cache\n    return unless @cache\n    keys = @cache.keys\n    @cache.delete(keys[rand(keys.size)])\n  end\n\n  # Insert a subscriber into the sublist for the given subject.\n  def insert(subject, subscriber)\n    # TODO - validate subject as correct.\n    level, tokens = @root, subject.split('.')\n    for token in tokens\n      # This is slightly slower than direct if statements, but looks cleaner.\n      case token\n        when FWC then node = (level.fwc || (level.fwc = SublistNode.new([])))\n        when PWC then node = (level.pwc || (level.pwc = SublistNode.new([])))\n        else node  = ((level.nodes[token]) || (level.nodes[token] = SublistNode.new([])))\n      end\n      level = (node.next_level || (node.next_level = SublistLevel.new({})))\n    end\n    node.leaf_nodes.push(subscriber)\n    @count += 1\n    clear_cache # Clear the cache\n    node.next_level = nil if node.next_level == EMPTY_LEVEL\n  end\n\n  # Remove a given subscriber from the sublist for the given subject.\n  def remove(subject, subscriber)\n    return unless subject && subscriber\n    remove_level(@root, subject.split('.'), subscriber)\n  end\n\n  # Match a subject to all subscribers, return the array of matches.\n  def match(subject)\n    return @cache[subject] if (@cache && @cache[subject])\n    tokens = subject.split('.')\n    @results.clear\n    matchAll(@root, tokens)\n    # FIXME: This is too low tech, will revisit when needed.\n    if @cache\n      prune_cache if @cache.size > CACHE_SIZE\n      @cache[subject] = Array.new(@results).freeze # Avoid tampering of copy\n    end\n    @results\n  end\n\n  private\n\n  def matchAll(level, tokens)\n    node, pwc = nil, nil # Define for scope\n    i, ts = 0, tokens.size\n    while (i < ts) do\n      return if level == nil\n      # Handle a full wildcard here by adding all of the subscribers.\n      @results.concat(level.fwc.leaf_nodes) if level.fwc\n      # Handle an internal partial wildcard by branching recursively\n      lpwc = level.pwc\n      matchAll(lpwc.next_level, tokens[i+1, ts]) if lpwc\n      node, pwc = level.nodes[tokens[i]], lpwc\n      #level = node.next_level if node\n      level = node ? node.next_level : nil\n      i += 1\n    end\n    @results.concat(pwc.leaf_nodes) if pwc\n    @results.concat(node.leaf_nodes) if node\n  end\n\n  def prune_level(level, node, token)\n    # Prune here if needed.\n    return unless level && node\n    return unless node.leaf_nodes.empty? && (!node.next_level || node.next_level == EMPTY_LEVEL)\n    if node == level.fwc\n      level.fwc = nil\n    elsif node == level.pwc\n      level.pwc = nil\n    else\n      level.nodes.delete(token)\n    end\n  end\n\n  def remove_level(level, tokens, subscriber)\n    return unless level\n    token = tokens.shift\n    case token\n      when FWC then node = level.fwc\n      when PWC then node = level.pwc\n      else node = level.nodes[token]\n    end\n    return unless node\n\n    # This could be expensive if a large number of subscribers exist.\n    if tokens.empty?\n      if (node.leaf_nodes && node.leaf_nodes.delete(subscriber))\n        @count -= 1\n        prune_level(level, node, token)\n        clear_cache # Clear the cache\n      end\n    else\n      remove_level(node.next_level, tokens, subscriber)\n      prune_level(level, node, token)\n    end\n  end\n\n  ################################################\n  # Used for tests on pruning subscription nodes.\n  ################################################\n\n  def node_count_level(level, nc)\n    return 0 unless level\n    nc += 1 if level.fwc\n    nc += node_count_level(level.pwc.next_level, nc+1) if level.pwc\n    level.nodes.each_value do |node|\n      nc += node_count_level(node.next_level, nc)\n    end\n    nc += level.nodes.length\n  end\n\n  def node_count\n    node_count_level(@root, 0)\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server/util.rb",
    "content": "require 'pp'\n\ndef fast_uuid #:nodoc:\n  v = [rand(0x0010000),rand(0x0010000),rand(0x0010000),\n       rand(0x0010000),rand(0x0010000),rand(0x1000000)]\n  \"%04x%04x%04x%04x%04x%06x\" % v\nend\n\ndef syslog(args, priority) #:nodoc:\n  Syslog::log(priority, '%s', PP::pp(args.compact, '', 120))\nend\n\ndef log(*args) #:nodoc:\n  return syslog(args, Syslog::LOG_NOTICE) if NATSD::Server.syslog\n  args.unshift(Time.now) if NATSD::Server.log_time\n  PP::pp(args.compact, $stdout, 120)\nend\n\ndef debug(*args) #:nodoc:\n  return unless NATSD::Server.debug_flag?\n  return syslog(args, Syslog::LOG_INFO) if NATSD::Server.syslog\n  log(*args)\nend\n\ndef trace(*args) #:nodoc:\n  return unless NATSD::Server.trace_flag?\n  return syslog(args, Syslog::LOG_DEBUG) if NATSD::Server.syslog\n  log(*args)\nend\n\ndef log_error(e=$!) #:nodoc:\n  debug e, e.backtrace\nend\n\ndef uptime_string(delta)\n  num_seconds = delta.to_i\n  days = num_seconds / (60 * 60 * 24);\n  num_seconds -= days * (60 * 60 * 24);\n  hours = num_seconds / (60 * 60);\n  num_seconds -= hours * (60 * 60);\n  minutes = num_seconds / 60;\n  num_seconds -= minutes * 60;\n  \"#{days}d:#{hours}h:#{minutes}m:#{num_seconds}s\"\nend\n\ndef pretty_size(size, prec=1)\n  return 'NA' unless size\n  return \"#{size}B\" if size < 1024\n  return sprintf(\"%.#{prec}fK\", size/1024.0) if size < (1024*1024)\n  return sprintf(\"%.#{prec}fM\", size/(1024.0*1024.0)) if size < (1024*1024*1024)\n  return sprintf(\"%.#{prec}fG\", size/(1024.0*1024.0*1024.0))\nend\n\ndef num_cpu_cores\n  if RUBY_PLATFORM =~ /linux/\n    return `cat /proc/cpuinfo | grep processor | wc -l`.to_i\n  elsif RUBY_PLATFORM =~ /darwin/\n    `sysctl -n hw.ncpu`.strip.to_i\n  elsif RUBY_PLATFORM =~ /freebsd|netbsd/\n    `sysctl hw.ncpu`.strip.to_i\n  else\n    return 1\n  end\nend\n\ndef shutdown #:nodoc:\n  puts\n  log 'Server exiting..'\n  NATSD::Server.close_syslog\n  EM.stop\n  if NATSD::Server.pid_file\n    FileUtils.rm(NATSD::Server.pid_file) if File.exists? NATSD::Server.pid_file\n  end\n  exit\nend\n\n['TERM','INT'].each { |s| trap(s) { shutdown } }\n\n# FIXME - Should probably be smarter when lots of connections\ndef dump_connection_state\n  log \"Dumping connection state on SIG_USR2\"\n  ObjectSpace.each_object(NATSD::Connection) do |c|\n    log c.info unless c.closing?\n  end\n  log 'Connection Dump Complete'\nend\n\ntrap('USR2') { dump_connection_state }\n"
  },
  {
    "path": "lib/nats/server/varz.rb",
    "content": "module NATSD #:nodoc: all\n\n  class Varz\n    def call(env)\n      varz_json = JSON.pretty_generate(Server.update_varz) + \"\\n\"\n      hdrs = RACK_JSON_HDR.dup\n      hdrs['Content-Length'] = varz_json.bytesize.to_s\n      [200, hdrs, varz_json]\n    end\n  end\n\n  class Server\n    class << self\n\n      def update_varz\n        # Snapshot uptime\n        @varz[:uptime] = uptime_string(Time.now - @varz[:start])\n\n        # Grab current cpu and memory usage.\n        rss, pcpu = `ps -o rss=,pcpu= -p #{Process.pid}`.split\n        @varz[:mem] = rss.to_i\n        @varz[:cpu] = pcpu.to_f\n        @varz[:connections] = num_connections\n        @varz[:in_msgs] = in_msgs\n        @varz[:out_msgs] = out_msgs\n        @varz[:in_bytes] = in_bytes\n        @varz[:out_bytes] = out_bytes\n        @varz[:routes] = num_routes\n        @last_varz_update = Time.now.to_f\n        varz\n      end\n\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/nats/server.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'socket'\nrequire 'fileutils'\nrequire 'pp'\nrequire 'syslog'\n\nep = File.expand_path(File.dirname(__FILE__))\n\nrequire \"#{ep}/ext/em\"\nrequire \"#{ep}/ext/bytesize\"\nrequire \"#{ep}/ext/json\"\nrequire \"#{ep}/server/server\"\nrequire \"#{ep}/server/sublist\"\nrequire \"#{ep}/server/connection\"\nrequire \"#{ep}/server/options\"\nrequire \"#{ep}/server/cluster\"\nrequire \"#{ep}/server/route\"\nrequire \"#{ep}/server/const\"\nrequire \"#{ep}/server/util\"\nrequire \"#{ep}/server/varz\"\nrequire \"#{ep}/server/connz\"\n\n# Do setup\nNATSD::Server.setup(ARGV.dup)\n\n# Event Loop\nEM.run do\n\n  log \"WARNING: nats-server is deprecated and no longer supported. It will be removed in a future release. See https://github.com/nats-io/gnatsd\"\n  log \"Starting #{NATSD::APP_NAME} version #{NATSD::VERSION} on port #{NATSD::Server.port}\"\n  log \"TLS/SSL Support Enabled\" if NATSD::Server.options[:ssl]\n  begin\n    EM.set_descriptor_table_size(32768) # Requires Root privileges\n    EM.start_server(NATSD::Server.host, NATSD::Server.port, NATSD::Connection)\n  rescue => e\n    log \"Could not start server on port #{NATSD::Server.port}\"\n    log_error\n    exit(1)\n  end\n\n  # Check to see if we need to fire up the http monitor port and server\n  if NATSD::Server.options[:http_port]\n    begin\n      NATSD::Server.start_http_server\n    rescue => e\n      log \"Could not start monitoring server on port #{NATSD::Server.options[:http_port]}\"\n      log_error\n      exit(1)\n    end\n  end\n\n  ###################\n  # CLUSTER SETUP\n  ###################\n\n  # Check to see if we need to fire up a routing listen port\n  if NATSD::Server.options[:cluster_port]\n    begin\n      log \"Starting routing on port #{NATSD::Server.options[:cluster_port]}\"\n      EM.start_server(NATSD::Server.host, NATSD::Server.options[:cluster_port], NATSD::Route)\n    rescue => e\n      log \"Could not start routing server on port #{NATSD::Server.options[:cluster_port]}\"\n      log_error\n      exit(1)\n    end\n  end\n\n  # If we have active connections, solicit them now..\n  NATSD::Server.solicit_routes if NATSD::Server.options[:cluster_routes]\n\nend\n"
  },
  {
    "path": "lib/nats/version.rb",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nmodule NATS\n  # NOTE: These are all announced to the server on CONNECT\n  VERSION = \"0.11.2\".freeze\n  LANG    = RUBY_ENGINE\n  PROTOCOL_VERSION = 1\nend\n"
  },
  {
    "path": "nats.gemspec",
    "content": "# Copyright 2010-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nlib = File.expand_path('../lib/', __FILE__)\n$:.unshift lib unless $:.include?(lib)\n\nrequire 'nats/version'\n\nspec = Gem::Specification.new do |s|\n  s.name = 'nats'\n  s.version = NATS::VERSION\n  s.summary = 'NATS is an open-source, high-performance, lightweight cloud messaging system.'\n  s.homepage = 'https://nats.io'\n  s.description = 'NATS is an open-source, high-performance, lightweight cloud messaging system.'\n  s.licenses = ['MIT']\n\n  s.authors = ['Derek Collison']\n  s.email = ['derek.collison@gmail.com']\n  s.add_dependency('eventmachine', '~> 1.2', '>= 1.2')\n\n  s.require_paths = ['lib']\n  s.bindir = 'bin'\n  s.executables = ['nats-pub', 'nats-sub', 'nats-queue', 'nats-request']\n\n  s.files = %w[\n    README.md\n    HISTORY.md\n    nats.gemspec\n    Rakefile\n    bin/nats-server\n    bin/nats-sub\n    bin/nats-pub\n    bin/nats-queue\n    bin/nats-top\n    bin/nats-request\n    lib/nats/client.rb\n    lib/nats/nuid.rb\n    lib/nats/version.rb\n    lib/nats/ext/bytesize.rb\n    lib/nats/ext/em.rb\n    lib/nats/ext/json.rb\n    lib/nats/server.rb\n    lib/nats/server/server.rb\n    lib/nats/server/connection.rb\n    lib/nats/server/cluster.rb\n    lib/nats/server/route.rb\n    lib/nats/server/options.rb\n    lib/nats/server/sublist.rb\n    lib/nats/server/const.rb\n    lib/nats/server/util.rb\n    lib/nats/server/varz.rb\n    lib/nats/server/connz.rb\n  ]\n\nend\n"
  },
  {
    "path": "scripts/install_gnatsd.sh",
    "content": "#!/bin/bash\n\nset -e\n\nexport DEFAULT_NATS_SERVER_VERSION=v2.0.0\n\nexport NATS_SERVER_VERSION=\"${NATS_SERVER_VERSION:=$DEFAULT_NATS_SERVER_VERSION}\"\n\n# check to see if nats-server folder is empty\nif [ ! \"$(ls -A $HOME/nats-server)\" ]; then\n    (\n\tmkdir -p $HOME/nats-server\n\tcd $HOME/nats-server\n\twget https://github.com/nats-io/nats-server/releases/download/$NATS_SERVER_VERSION/nats-server-$NATS_SERVER_VERSION-linux-amd64.zip -O nats-server.zip\n\tunzip nats-server.zip\n\tcp nats-server-$NATS_SERVER_VERSION-linux-amd64/nats-server $HOME/nats-server/nats-server\n    )\nelse\n  echo 'Using cached directory.';\nfi\n"
  },
  {
    "path": "spec/.rspec",
    "content": "-fd\n-c\n"
  },
  {
    "path": "spec/client/attack_spec.rb",
    "content": "\nrequire 'spec_helper'\n\ndescribe 'Client - server attacks' do\n\n  TEST_SERVER = 'nats://127.0.0.1:4222'\n  MSG_SIZE    = 10 * 1024 * 1024\n  BIG_MSG     = 'a' * MSG_SIZE\n  BAD_SUBJECT = 'b' * 1024 * 1024\n\n  before (:each) do\n    @s = NatsServerControl.new(TEST_SERVER, \"/tmp/nats_attack.pid\")\n    @s.start_server(true)\n  end\n\n  after (:each) do\n    @s.kill_server\n  end\n\n  it \"should not let us write large control line buffers\" do\n    errors = []\n    with_em_timeout(3) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      nc = NATS.connect(:servers => [TEST_SERVER], :reconnect => false)\n      nc.flush do\n        nc.publish(BAD_SUBJECT, 'a')\n      end\n    end\n\n    # Just confirm that it is no longer connected\n    expect(NATS.connected?).to be false\n    expect(errors.count >= 1).to eql(true)\n  end\n\n  it \"should not let us write large messages\" do\n    errors = []\n    with_em_timeout(3) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      nc = NATS.connect(:uri => TEST_SERVER, :reconnect => false)\n      nc.flush do\n        nc.publish('foo', BIG_MSG)\n      end\n    end\n    expect(errors.count > 0).to be true\n\n    # NOTE: Race here on whether getting NATS::ServerError or NATS::ConnectError\n    # in case we have been disconnected before reading the error sent by server.\n    case errors.count\n    when 1\n      expect(errors[0]).to be_a NATS::ConnectError\n    when 2\n      expect(errors[0]).to be_a NATS::ServerError\n      expect(errors[1]).to be_a NATS::ConnectError\n    end\n  end\n\n  it \"should complain if we can't kill our server that we started\" do\n    errors = []\n    @s.kill_server\n    with_em_timeout(5) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:uri => TEST_SERVER)\n    end\n    expect(errors.first).to be_a(NATS::ConnectError)\n  end\nend\n"
  },
  {
    "path": "spec/client/auth_spec.rb",
    "content": "\nrequire 'spec_helper'\nrequire 'fileutils'\n\ndescribe 'Client - authorization' do\n  USER = 'derek'\n  PASS = 'mypassword'\n\n  TEST_AUTH_SERVER          = \"nats://#{USER}:#{PASS}@127.0.0.1:9222\"\n  TEST_AUTH_SERVER_NO_CRED  = 'nats://127.0.0.1:9222'\n  TEST_AUTH_SERVER_PID      = '/tmp/nats_authorization.pid'\n  TEST_AUTH_AUTO_SERVER_PID = '/tmp/nats_auto_authorization.pid'\n\n  before (:each) do\n    @s = NatsServerControl.new(TEST_AUTH_SERVER, TEST_AUTH_SERVER_PID)\n    @s.start_server\n  end\n\n  after (:each) do\n    @s.kill_server\n    FileUtils.rm_f TEST_AUTH_SERVER_PID\n  end\n\n  it 'should fail to connect to an authorized server without proper credentials' do\n    errors = []\n    with_em_timeout do |future|\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:uri => TEST_AUTH_SERVER_NO_CRED)\n    end\n    expect(errors.count).to eql(2)\n    expect(errors.first).to be_a NATS::AuthError\n    expect(errors.last).to be_a NATS::ConnectError\n  end\n\n  it 'should take user and password as separate options' do\n    errors = []\n    with_em_timeout(1) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:uri => TEST_AUTH_SERVER_NO_CRED, :user => USER, :pass => PASS)\n    end\n    expect(errors.count).to eql(0)\n  end\n\n  it 'should not continue to try to connect on unauthorized access' do\n    auth_error_callbacks = 0\n    connect_error_callbacks = 0\n    with_em_timeout do\n      # Default error handler raises, so we trap here.\n      NATS.on_error do |e|\n\n        # disconnects\n        connect_error_callbacks += 1 if e.instance_of? NATS::ConnectError\n\n        # authorization\n        auth_error_callbacks +=1 if e.instance_of? NATS::AuthError\n      end\n\n      NATS.connect(:uri => TEST_AUTH_SERVER_NO_CRED)\n    end\n    expect(auth_error_callbacks).to eql(1)\n    expect(connect_error_callbacks).to eql(1)\n  end\n\n  it 'should remove server from the pool on unauthorized access' do\n    error_cb = 0\n    connect_cb = false\n    EM.run do\n      # Default error handler raises, so we trap here.\n      NATS.on_error { error_cb += 1 }\n      connected = false\n      NATS.start(:dont_randomize_servers => true, :servers => [TEST_AUTH_SERVER_NO_CRED, TEST_AUTH_SERVER]) do\n        connect_cb = true\n        EM.stop\n      end\n    end\n    expect(error_cb).to eql(1)\n    expect(connect_cb).to eql(true)\n    expect(NATS.client).to_not be(nil)\n    expect(NATS.client.server_pool.size).to eql(1)\n    NATS.stop # clears err_cb\n  end\n\nend\n"
  },
  {
    "path": "spec/client/autounsub_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - max responses and auto-unsubscribe' do\n\n  before(:each) do\n    @s = NatsServerControl.new(\"nats://127.0.0.1:4222\")\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it \"should only receive N msgs when requested: client support\" do\n    WANT = 10\n    SEND = 20\n    received = 0\n    NATS.start(:servers => [@s.uri]) do\n      NATS.subscribe('foo', :max => WANT) { received += 1 }\n      (0...SEND).each { NATS.publish('foo', 'hello') }\n      NATS.publish('done') { NATS.stop }\n    end\n    expect(received).to eql(WANT)\n  end\n\n  it \"should only receive N msgs when auto-unsubscribed\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.unsubscribe(sid, WANT)\n      (0...SEND).each { NATS.publish('foo', 'hello') }\n      NATS.publish('done') { NATS.stop }\n    end\n    expect(received).to eql(WANT)\n  end\n\n  it \"should not complain when unsubscribing an auto-unsubscribed sid\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo', :max => 1) { received += 1 }\n      (0...SEND).each { NATS.publish('foo', 'hello') }\n      NATS.publish('done') {\n        NATS.unsubscribe(sid)\n        NATS.stop\n      }\n    end\n    expect(received).to eql(1)\n  end\n\n  it \"should allow proper override of auto-unsubscribe max variables to lesser value\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo') {\n        received += 1\n        NATS.unsubscribe(sid, 1)\n      }\n      NATS.unsubscribe(sid, SEND+1)\n      (0...SEND).each { NATS.publish('foo', 'hello') }\n      NATS.publish('done') { NATS.stop }\n    end\n    expect(received).to eql(1)\n  end\n\n  it \"should allow proper override of auto-unsubscribe max variables to higher value\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.unsubscribe(sid, 2)\n      NATS.unsubscribe(sid, WANT)\n      (0...SEND).each { NATS.publish('foo', 'hello') }\n      NATS.publish('done') { NATS.stop }\n    end\n    expect(received).to eql(WANT)\n  end\n\n  it \"should only receive N msgs using request mode with multiple helpers\" do\n    received = 0\n    NATS.start do\n      # Create 5 identical helpers\n      (0...5).each { NATS.subscribe('help') { |msg, reply| NATS.publish(reply, 'I can help!') } }\n      NATS.request('help', nil, :max => 1) { received += 1 }\n      EM.add_timer(0.1) { NATS.stop }\n    end\n    expect(received).to eql(1)\n  end\n\n  it \"should not leak subscriptions on request that auto-unsubscribe properly with :max\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('help') { |msg, reply| NATS.publish(reply, 'I can help!') }\n      (1..100).each do\n        NATS.request('help', 'help request', :max => 1) { received += 1 }\n      end\n      NATS.flush do\n        EM.add_timer(0.1) do\n          NATS.unsubscribe(sid)\n          expect(NATS.client.subscription_count).to eql(0)\n          NATS.stop\n        end\n      end\n    end\n    expect(received).to eql(100)\n  end\n\n  it \"should not complain when unsubscribe called on auto-cleaned up subscription\" do\n    NATS.start do\n      sid = NATS.subscribe('help') { |msg, reply| NATS.publish(reply, 'I can help!') }\n      rsid = NATS.request('help', 'help request', :max => 1) {}\n      NATS.flush do\n        EM.add_timer(0.1) do\n          expect(NATS.client.subscription_count).to eql(1)\n          NATS.unsubscribe(sid)\n          expect(NATS.client.subscription_count).to eql(0)\n          NATS.unsubscribe(rsid)\n          NATS.stop\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/binary_msg_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - test binary message payloads to avoid connection drop' do\n\n  before(:all) do\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n  end\n\n  it \"should not disconnect us if we send binary data\" do\n    got_error = false\n    NATS.on_error { got_error = true; NATS.stop }\n    NATS.start(:reconnect => false) do\n      expect(NATS.connected?).to eql(true)\n      NATS.publish('dont_disconnect_me', \"\\006\")\n      NATS.flush { NATS.stop }\n    end\n    expect(got_error).to eql(false)\n  end\n\nend\n"
  },
  {
    "path": "spec/client/client_cluster_config_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - cluster config' do\n\n  CLUSTER_USER = 'derek'\n  CLUSTER_PASS = 'mypassword'\n\n  CLUSTER_AUTH_PORT = 9292\n  CLUSTER_AUTH_SERVER = \"nats://#{CLUSTER_USER}:#{CLUSTER_PASS}@127.0.0.1:#{CLUSTER_AUTH_PORT}\"\n  CLUSTER_AUTH_SERVER_PID = '/tmp/nats_cluster_authorization.pid'\n\n  before(:all) do\n    @s  = NatsServerControl.new\n    @as = NatsServerControl.new(CLUSTER_AUTH_SERVER, CLUSTER_AUTH_SERVER_PID)\n  end\n\n  before(:each) do\n    [@s, @as].each do |s|\n      s.start_server(true) unless NATS.server_running? s.uri\n    end\n  end\n\n  after(:each) do\n    [@s, @as].each do |s|\n      s.kill_server\n    end\n  end\n\n  it 'should properly process :uri option for multiple servers' do\n    NATS.start(:uri => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'], :dont_randomize_servers => true) do\n      options = NATS.options\n      expect(options).to be_a(Hash)\n      expect(options).to have_key(:uri)\n      expect(options[:uri]).to eql(['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'])\n      NATS.stop\n    end\n  end\n\n  it 'should allow :uris and :servers as aliases' do\n    NATS.start(:uris => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'], :dont_randomize_servers => true) do\n      options = NATS.options\n      expect(options).to be_a(Hash)\n      expect(options).to have_key(:uris)\n      expect(options[:uris]).to eql(['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'])\n      NATS.stop\n    end\n    NATS.start(:servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'], :dont_randomize_servers => true) do\n      options = NATS.options\n      expect(options).to be_a(Hash)\n      expect(options).to have_key(:servers)\n      expect(options[:servers]).to eql(['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223'])\n      NATS.stop\n    end\n  end\n\n  it 'should allow aliases on instance connections' do\n    c1 = c2 = nil\n    NATS.start do\n      c1 = NATS.connect(:uris => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4222'])\n      c2 = NATS.connect(:servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4222'])\n      timeout_nats_on_failure\n    end\n    expect(c1).to_not be(nil)\n    expect(c2).to_not be(nil)\n  end\n\n  it 'should randomize server pool list by default' do\n    servers = ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223',\n               'nats://127.0.0.1:4224', 'nats://127.0.0.1:4225',\n               'nats://127.0.0.1:4226', 'nats://127.0.0.1:4227']\n    NATS.start do\n      NATS.connect(:uri => servers.dup) do |c|\n        sp_servers = []\n        c.server_pool.each { |s| sp_servers << s[:uri].to_s }\n        expect(sp_servers).to_not eql(servers)\n      end\n      timeout_nats_on_failure\n    end\n  end\n\n  it 'should not randomize server pool if options suppress' do\n    servers = ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223',\n               'nats://127.0.0.1:4224', 'nats://127.0.0.1:4225',\n               'nats://127.0.0.1:4226', 'nats://127.0.0.1:4227']\n    NATS.start do\n      NATS.connect(:dont_randomize_servers => true, :uri => servers) do |c|\n        sp_servers = []\n        c.server_pool.each { |s| sp_servers << s[:uri].to_s }\n        expect(sp_servers).to eql(servers)\n      end\n      timeout_nats_on_failure\n    end\n  end\n\n  it 'should connect to first entry if available' do\n    NATS.start(:dont_randomize_servers => true, :uri => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223']) do\n      expect(NATS.client.connected_server).to eql(URI.parse('nats://127.0.0.1:4222'))\n      NATS.stop\n    end\n  end\n\n  it 'should fail to connect if no servers available' do\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n\n      NATS.start(:uri => ['nats://127.0.0.1:4223'])\n    end\n    expect(errors.first).to be_a(NATS::Error)\n  end\n\n  it 'should connect to another server if first is not available' do\n    NATS.start(:dont_randomize_servers => true, :uri => ['nats://127.0.0.1:4224', 'nats://127.0.0.1:4222']) do\n      expect(NATS.client.connected_server).to eql(URI.parse('nats://127.0.0.1:4222'))\n      NATS.stop\n    end\n  end\n\n  it 'should fail if all servers are not available' do\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:uri => ['nats://127.0.0.1:4224', 'nats://127.0.0.1:4223'])\n    end\n    expect(errors.count > 0).to be(true)\n    expect(errors.first).to     be_a(NATS::ConnectError)\n  end\n\n  it 'should fail if server is available but does not have proper auth' do\n    errors = []\n    with_em_timeout(5) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:uri => ['nats://127.0.0.1:4224', \"nats://127.0.0.1:#{CLUSTER_AUTH_PORT}\"], :dont_randomize_servers => true)\n    end\n    expect(errors.count).to eql(2)\n    expect(errors.first).to be_a(NATS::AuthError)\n    expect(errors.last).to  be_a(NATS::ConnectError)\n  end\n\n  it 'should succeed if proper credentials supplied with non-first uri' do\n    with_em_timeout(3) do\n      # FIXME: Flush should not be required to be able to assert connected URI\n      nc = NATS.connect(:dont_randomize_servers => true, :uri => ['nats://127.0.0.1:4224', CLUSTER_AUTH_SERVER])\n      nc.flush do\n        expect(nc.connected_server).to eql(URI.parse(CLUSTER_AUTH_SERVER))\n      end\n    end\n  end\n\n  it 'should allow user/pass overrides' do\n    s_uri = \"nats://127.0.0.1:#{CLUSTER_AUTH_PORT}\"\n\n    errors = []\n    with_em_timeout(5) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect(:servers => [s_uri])\n    end\n    expect(errors.count).to eql(2)\n    expect(errors.first).to be_a(NATS::AuthError)\n    expect(errors.last).to  be_a(NATS::ConnectError)\n\n    errors = []\n    with_em_timeout do\n      NATS.connect(:uri => [s_uri], :user => CLUSTER_USER, :pass => CLUSTER_PASS) do |nc2|\n        nc2.publish(\"hello\", \"world\")\n      end\n    end\n    expect(errors.count).to eql(0)\n  end\n\n  context do\n    before(:all) do\n      @s1_uri = 'nats://derek:foo@127.0.0.1:9290'\n      @s1 = NatsServerControl.new(@s1_uri, '/tmp/nats_cluster_honor_s1.pid')\n      @s1.start_server\n\n      @s2_uri = 'nats://sarah:bar@127.0.0.1:9298'\n      @s2 = NatsServerControl.new(@s2_uri, '/tmp/nats_cluster_honor_s2.pid')\n      @s2.start_server\n    end\n\n    after(:all) do\n      @s1.kill_server\n      @s2.kill_server\n    end\n\n    it 'should honor auth credentials properly for listed servers' do\n      with_em_timeout(5) do\n        # FIXME: Flush should not be required, rather connect should be synchronous...\n        nc = NATS.connect(:dont_randomize_servers => true, :servers => [@s1_uri, @s2_uri])\n        nc.flush do\n          expect(nc.connected_server).to eql(URI.parse(@s1_uri))\n\n          # Disconnect from first server\n          kill_time = Time.now\n          @s1.kill_server\n\n          EM.add_timer(1) do\n            time_diff = Time.now - kill_time\n            expect(time_diff < 2).to eql(true)\n\n            # Confirm that it has connected to the second server\n            expect(nc.connected_server).to eql(URI.parse(@s2_uri))\n\n            # Restart the first server again...\n            @s1.start_server\n\n            # Kill the second server once again to reconnect to first one...\n            kill_time2 = Time.now\n            @s2.kill_server\n\n            EM.add_timer(0.25) do\n              time_diff = Time.now - kill_time2\n              expect(time_diff < 1).to eql(true)\n\n              # Confirm we are reconnecting to the first one again\n              nc.flush do\n                expect(nc.connected_server).to eql(URI.parse(@s1_uri))\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_cluster_reconnect_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - cluster reconnect' do\n\n  before(:all) do\n    auth_options = {\n      'user'     => 'derek',\n      'password' => 'bella',\n      'token'    => 'deadbeef',\n      'timeout'  => 5\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    s3_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s3.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4244,\n      'cluster_port'  => 6224\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts, s3_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: #{auth_options[\"timeout\"]}\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: 5\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2, @s3 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2, @s3].each do |s|\n      s.start_server(true)\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2, @s3].each do |s|\n      s.kill_server\n    end\n  end\n\n  it 'should properly handle exceptions thrown by eventmachine during reconnects' do\n    reconnect_cb = false\n    opts = {\n      :dont_randomize_servers => true,\n      :reconnect_time_wait => 0.25,\n      :servers => [@s1.uri, URI.parse(\"nats://does.not.exist:4222/\"), @s3.uri]\n    }\n    with_em_timeout(5) do\n      nc = NATS.connect(opts)\n      nc.on_reconnect do\n        reconnect_cb = true\n        expect(nc.connected?).to be(true)\n        expect(nc.connected_server).to eql(@s3.uri)\n      end\n\n      EM.add_timer(1) do\n        @s1.kill_server\n      end\n    end\n    expect(reconnect_cb).to eql(true)\n  end\n\n  it 'should call reconnect callback when current connection fails' do\n    reconnect_cb = false\n    opts = {\n      :dont_randomize_servers => true,\n      :reconnect_time_wait => 0.25,\n      :servers => [@s1.uri, @s2.uri, @s3.uri],\n      :max_reconnect_attempts => 5\n    }\n    reconnect_conns = []\n    with_em_timeout(5) do\n      NATS.start(opts) do |c|\n        expect(c.connected_server).to eql(@s1.uri)\n        c.on_reconnect do |conn|\n          reconnect_cb = true\n          reconnect_conns << conn.connected_server\n        end\n        @s1.kill_server\n        EM.add_timer(1) do\n          @s2.kill_server\n        end\n      end\n    end\n\n    expect(reconnect_cb).to eq(true)\n    expect(reconnect_conns.count).to eql(2)\n    expect(reconnect_conns.first).to eql(@s2.uri)\n    expect(reconnect_conns.last).to eql(@s3.uri)\n  end\n\n  it 'should connect to another server if possible before reconnect' do\n    NATS.start(:dont_randomize_servers => true, :servers => [@s1.uri, @s2.uri, @s3.uri]) do |c|\n      timeout_nats_on_failure(15)\n      expect(c.connected_server).to eql(@s1.uri)\n      c.on_reconnect do\n        expect(c.connected_server).to eql(@s2.uri)\n        NATS.stop\n      end\n      @s1.kill_server\n    end\n  end\n\n  it 'should connect to a previous server if multiple servers exit' do\n    NATS.start(:dont_randomize_servers => true, :servers => [@s1.uri, @s2.uri, @s3.uri]) do |c|\n      timeout_nats_on_failure(15)\n      expect(c.connected_server).to eql(@s1.uri)\n      kill_time = Time.now\n\n      expected_uri = nil\n      c.on_reconnect do\n        expect(c.connected_server).to eql(expected_uri)\n        if expected_uri == @s2.uri\n          # Expect to connect back to S1\n          expected_uri = @s1.uri\n          @s1.start_server\n          @s2.kill_server\n        end\n        NATS.stop if c.connected_server == @s1.uri\n      end\n\n      # Expect to connect to S2 after killing S1 and S3.\n      expected_uri = @s2.uri\n\n      @s1.kill_server\n      @s3.kill_server\n    end\n  end\n\n  it 'should use reconnect logic to connect to a previous server if multiple servers exit' do\n    @s2.kill_server # Take this one offline\n    @s3.kill_server # Take this one offline too, since it will be discovered in cluster\n\n    options = {\n      :dont_randomize_servers => true,\n      :servers => [@s1.uri, @s2.uri],\n      :reconnect => true,\n      :max_reconnect_attempts => 2,\n      :reconnect_time_wait => 1\n    }\n    with_em_timeout do\n      NATS.start(options) do |c|\n        expect(c.connected_server).to eql(@s1.uri)\n        c.on_reconnect do\n          expect(c.connected?).to eql(true)\n          expect(c.connected_server).to eql(@s1.uri)\n          NATS.stop\n        end\n        @s1.kill_server\n        @s1.start_server\n      end\n    end\n  end\n\n  context 'when max_reconnect_attempts == -1 (do not remove servers)' do\n    it 'should never remove servers that fail' do\n      options = {\n        :dont_randomize_servers => true,\n        :servers => [@s1.uri, @s2.uri],\n        :reconnect => true,\n        :max_reconnect_attempts => -1,\n        :reconnect_time_wait => 1\n      }\n\n      done = false\n      @s1.kill_server\n      with_em_timeout(2) do\n        NATS.start(options) do |c|\n          expect(c.connected_server).to eql(@s2.uri)\n          expect(c.server_pool.size).to eq(3)\n          EM.add_timer(0.5) do\n            @s3.kill_server # Server goes away from the cluster\n            EM.add_timer(1) do\n              done = true\n              expect(c.server_pool.size).to eq(3)\n            end\n          end\n        end\n      end\n      expect(done).to eql(true)\n    end\n\n    it 'should never remove servers due to client triggered disconnect' do\n\n      options = {\n        :dont_randomize_servers => true,\n        :servers => [@s1.uri, @s2.uri],\n        :reconnect => true,\n        :max_reconnect_attempts => -1,\n        :reconnect_time_wait => 2\n      }\n\n      done = false\n      with_em_timeout(5) do |future|\n        errors = []\n        reconnects = 0\n        NATS.start(options) do |nc|\n          nc.on_error do |e|\n            errors << e\n          end\n\n          nc.on_reconnect do\n            reconnects += 1\n          end\n          expect(nc.connected_server).to eql(@s1.uri)\n          expect(nc.server_pool.size).to eq(3)\n\n          messages = []\n          nc.subscribe(\"hello\") do |msg|\n            messages << msg\n          end\n\n          payload = nc.server_info[:max_payload] + 100\n          nc.publish(\"hello\", 'A' * payload)\n\n          EM.add_timer(1) { @s1.kill_server }\n          EM.add_timer(2.5) do\n            expect(nc.connected_server).to eql(@s2.uri)\n\n            expect(nc.server_pool.size).to eq(3)\n            expect(messages.count).to eql(0)\n\n            EM.add_timer(0.5) do\n              @s3.kill_server\n              expect(errors.count).to eql(1)\n              expect(nc.server_pool.size).to eq(3)\n              done = true\n              nc.close\n              future.resume\n            end\n          end\n        end\n      end\n      expect(done).to eql(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_config_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Client - configuration\" do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it 'should honor setting options' do\n    with_em_timeout do\n      NATS.start(:debug => true, :pedantic => false, :verbose => true, :reconnect => true, :max_reconnect_attempts => 100, :reconnect_time_wait => 5, :uri => 'nats://127.0.0.1:4222') do\n        options = NATS.options\n        expect(options).to be_a Hash\n        expect(options).to have_key :debug\n        expect(options[:debug]).to eql(true)\n        expect(options).to have_key :pedantic\n        expect(options[:pedantic]).to eql(false)\n        expect(options).to have_key :verbose\n        expect(options[:verbose]).to eql(true)\n        expect(options).to have_key :reconnect\n        expect(options[:reconnect]).to eql(true)\n        expect(options).to have_key :max_reconnect_attempts\n        expect(options[:max_reconnect_attempts]).to eql(100)\n        expect(options).to have_key :reconnect_time_wait\n        expect(options[:reconnect_time_wait]).to eql(5)\n        expect(options).to have_key :uri\n        expect(options[:uri].to_s).to eql('nats://127.0.0.1:4222')\n        NATS.stop\n      end\n    end\n  end\n\n  it 'should not complain against bad subjects if pedantic mode disabled' do\n    with_em_timeout do |future|\n      expect do\n        nc = NATS.connect(:pedantic => false, :reconnect => false)\n        nc.flush do\n          nc.publish('foo.>.foo', 'hello') do\n            future.resume(nc)\n          end\n        end\n      end.to_not raise_error\n    end\n  end\n\n  it 'should complain against bad subjects in pedantic mode' do\n    errors = []\n\n    with_em_timeout do |future|\n      nc = nil\n      NATS.on_error do |e|\n        errors << e\n        future.resume(nc)\n      end\n\n      nc = NATS.connect(:pedantic => true, :reconnect => false)\n      nc.flush do\n        nc.publish('foo.>.foo', 'hello')\n      end\n    end\n\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a NATS::ServerError\n  end\n\n  it 'should set verbose mode correctly' do\n    NATS.start(:debug => true, :pedantic => false, :verbose => true) do\n      NATS.publish('foo') { NATS.stop }\n    end\n  end\n\n  it 'should have default ping options' do\n    NATS.start do\n      options = NATS.options\n      expect(options).to be_a Hash\n      expect(options).to have_key :ping_interval\n      expect(options[:ping_interval]).to eql(NATS::DEFAULT_PING_INTERVAL)\n      expect(options).to have_key :max_outstanding_pings\n      expect(options[:max_outstanding_pings]).to eql(NATS::DEFAULT_PING_MAX)\n      NATS.stop\n    end\n  end\n\n  it 'should allow overrides of ping variables' do\n    NATS.start(:ping_interval => 30, :max_outstanding_pings => 4) do\n      options = NATS.options\n      expect(options).to be_a Hash\n      expect(options).to have_key :ping_interval\n      expect(options[:ping_interval]).to eql(30)\n      expect(options).to have_key :max_outstanding_pings\n      expect(options[:max_outstanding_pings]).to eql(4)\n      NATS.stop\n    end\n  end\n\n  it 'should honor environment vars options' do\n    ENV['NATS_VERBOSE'] = 'true'\n    ENV['NATS_PEDANTIC'] = 'true'\n    ENV['NATS_DEBUG'] = 'true'\n    ENV['NATS_RECONNECT'] = 'true'\n    ENV['NATS_FAST_PRODUCER'] = 'true'\n    ENV['NATS_MAX_RECONNECT_ATTEMPTS'] = '100'\n    ENV['NATS_RECONNECT_TIME_WAIT'] = '5'\n    ENV['NATS_URI'] = 'nats://127.0.0.1:4222'\n\n    NATS.start do\n      options = NATS.options\n      expect(options).to be_a Hash\n      expect(options).to have_key :debug\n      expect(options[:debug]).to eql(true)\n      expect(options).to have_key :pedantic\n      expect(options[:pedantic]).to eql(true)\n      expect(options).to have_key :verbose\n      expect(options[:verbose]).to eql(true)\n      expect(options).to have_key :reconnect\n      expect(options[:reconnect]).to eql(true)\n      expect(options).to have_key :fast_producer_error\n      expect(options[:fast_producer_error]).to eql(true)\n      expect(options).to have_key :max_reconnect_attempts\n      expect(options[:max_reconnect_attempts]).to eql(100)\n      expect(options).to have_key :reconnect_time_wait\n      expect(options[:reconnect_time_wait]).to eql(5)\n      expect(options).to have_key :uri\n      expect(options[:uri].to_s).to eql('nats://127.0.0.1:4222')\n      NATS.stop\n    end\n\n    # Restore environment to be without options!\n    ENV.delete 'NATS_VERBOSE'\n    ENV.delete 'NATS_PEDANTIC'\n    ENV.delete 'NATS_DEBUG'\n    ENV.delete 'NATS_RECONNECT'\n    ENV.delete 'NATS_FAST_PRODUCER'\n    ENV.delete 'NATS_MAX_RECONNECT_ATTEMPTS'\n    ENV.delete 'NATS_RECONNECT_TIME_WAIT'\n    ENV.delete 'NATS_URI'\n  end\n\n  it 'should respect the reconnect parameters' do\n    with_em_timeout do\n      expect do\n        NATS.start(:max_reconnect_attempts => 1, :reconnect_time_wait => 1) do\n          NATS.stop\n        end\n      end.to_not raise_error\n    end\n\n    # Stop the server, make sure it can't connect and see that the time to fail make sense\n    start_at = nil\n    closed_at = nil\n    errors = []\n    with_em_timeout(5) do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.on_close do\n        closed_at = Time.now\n      end\n      NATS.start(:max_reconnect_attempts => 1, :reconnect_time_wait => 1) do\n        start_at = Time.now\n        @s.kill_server\n      end\n    end\n    time_diff = closed_at - start_at\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a NATS::ConnectError\n\n    # Check if the reconnect took more than the expected 4 secs...\n    expect(time_diff > 1).to eql(true)\n    expect(time_diff < 4).to eql(true)\n  end\nend\n"
  },
  {
    "path": "spec/client/client_connect_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - connect' do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  context \"No echo support\" do\n    it \"should not receive messages when no echo is enabled\" do\n      errors = []\n      msgs_a = []\n      msgs_b = []\n      with_em_timeout do\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        # Client A will receive all messages from B\n        NATS.connect(uri: @s.uri, no_echo: true) do |nc|\n          nc.subscribe(\"hello\") do |msg|\n            msgs_a << msg\n          end\n\n          EM.add_timer(0.5) do\n            10.times do\n              nc.publish(\"hello\", \"world\")\n            end\n          end\n        end\n\n        # Default is classic behavior of client which is\n        # for echo to be enabled, so will receive messages\n        # from Client A and itself.\n        NATS.connect(uri: @s.uri) do |nc|\n          nc.subscribe(\"hello\") do |msg|\n            msgs_b << msg\n          end\n\n          EM.add_timer(0.5) do\n            10.times do\n              nc.publish(\"hello\", \"world\")\n            end\n          end\n        end\n      end\n\n      expect(errors.count).to eql(0)\n      expect(msgs_a.count).to eql(10)\n      expect(msgs_b.count).to eql(20)\n    end\n\n    it \"should have no echo enabled by default\" do\n      errors = []\n      msgs_a = []\n      msgs_b = []\n      with_em_timeout do\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        # Client A will receive all messages from B\n        NATS.connect(uri: @s.uri) do |nc|\n          nc.subscribe(\"hello\") do |msg|\n            msgs_a << msg\n          end\n\n          EM.add_timer(0.5) do\n            10.times do\n              nc.publish(\"hello\", \"world\")\n            end\n          end\n        end\n\n        # Default is classic behavior of client which is\n        # for echo to be enabled, so will receive messages\n        # from Client A and itself.\n        NATS.connect(uri: @s.uri) do |nc|\n          nc.subscribe(\"hello\") do |msg|\n            msgs_b << msg\n          end\n\n          EM.add_timer(0.5) do\n            10.times do\n              nc.publish(\"hello\", \"world\")\n            end\n          end\n        end\n      end\n\n      expect(errors.count).to eql(0)\n      expect(msgs_a.count).to eql(20)\n      expect(msgs_b.count).to eql(20)\n    end\n\n    it \"should fail if echo is enabled but not supported by server\" do\n      expect do\n        with_em_timeout do\n          OldInfoServer.start {\n            NATS.connect(uri: \"nats://127.0.0.1:9997\", no_echo: true) do |nc|\n            end\n          }\n        end\n      end.to raise_error(NATS::ServerError)\n    end\n\n    it \"should fail if echo is enabled but not supported by server protocol\" do\n      expect do\n        with_em_timeout do\n          OldProtocolInfoServer.start {\n            NATS.connect(uri: \"nats://127.0.0.1:9996\", no_echo: true) do |nc|\n            end\n          }\n        end\n      end.to raise_error(NATS::ServerError)\n    end\n  end\n\n  context \"Simple Connect\" do\n    it \"should connect using host:port\" do\n      with_em_timeout do |future|\n        NATS.connect(\"127.0.0.1:4222\") do |nc|\n          nc.subscribe(\"foo\") do\n            future.resume\n          end\n          nc.publish(\"foo\", \"bar\")\n        end\n      end\n    end\n\n    it \"should connect with just host using default port\" do\n      with_em_timeout do |future|\n        NATS.connect(\"127.0.0.1\") do |nc|\n          nc.subscribe(\"foo\") do\n            future.resume\n          end\n          nc.publish(\"foo\", \"bar\")\n        end\n      end\n    end\n\n    it \"should connect with just host: using default port\" do\n      with_em_timeout do |future|\n        NATS.connect(\"127.0.0.1:\") do |nc|\n          nc.subscribe(\"foo\") do\n            future.resume\n          end\n          nc.publish(\"foo\", \"bar\")\n        end\n      end\n    end\n\n    it \"should fail to connect with empty string\" do\n      expect do\n        with_em_timeout do |future|\n          NATS.connect(\"\")\n        end\n      end.to raise_error(URI::InvalidURIError)\n    end\n\n    it \"should support comma separated list of servers\" do\n      options = {}\n      servers = []\n      with_em_timeout do |future|\n        NATS.connect(\"nats://127.0.0.1:4222,nats://127.0.0.1:4223,nats://127.0.0.1:4224\", dont_randomize_servers: true) do |nc|\n          nc.subscribe(\"foo\") do\n            options = nc.options\n            servers = nc.server_pool\n            future.resume\n          end\n          nc.publish(\"foo\", \"bar\")\n        end\n      end\n      expect(options[:dont_randomize_servers]).to eql(true)\n      expect(servers.count).to eql(3)\n      servers.each do |server|        \n        expect(server[:uri].scheme).to eql('nats')\n        expect(server[:uri].host).to eql('127.0.0.1')\n      end\n      a, b, c = servers\n      expect(a[:uri].port).to eql(4223)\n      expect(b[:uri].port).to eql(4224)\n      expect(c[:uri].port).to eql(4222)\n    end\n\n    it \"should support comma separated list of servers with own user info\" do\n      servers = []\n      with_em_timeout do |future|\n        NATS.connect(\"nats://a:b@127.0.0.1:4222,nats://c:d@127.0.0.1:4223,nats://e:f@127.0.0.1:4224\", dont_randomize_servers: true) do |nc|\n          nc.subscribe(\"foo\") do\n            servers = nc.server_pool\n            future.resume\n          end\n          nc.publish(\"foo\", \"bar\")\n        end\n      end\n      expect(servers.count).to eql(3)\n      servers.each do |server|        \n        expect(server[:uri].scheme).to eql('nats')\n        expect(server[:uri].host).to eql('127.0.0.1')\n      end\n      a, b, c = servers\n      expect(c[:uri].port).to eql(4222)\n      expect(a[:uri].port).to eql(4223)\n      expect(b[:uri].port).to eql(4224)\n\n      expect(c[:uri].user).to eql('a')\n      expect(a[:uri].user).to eql('c')\n      expect(b[:uri].user).to eql('e')\n\n      expect(c[:uri].password).to eql('b')\n      expect(a[:uri].password).to eql('d')\n      expect(b[:uri].password).to eql('f')\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_drain_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - drain' do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it \"should support draining a connection\" do\n    msgs = []\n    errors = []\n    closed = false\n    drained = false\n    after_drain = nil\n    total_msgs_before_drain = nil\n    total_msgs_after_drain = nil\n    total_msgs_sent = nil\n    pending_data_before_draining = nil\n    pending_data_after_draining = nil\n    pending_outbound_data_before_draining = nil\n    pending_outbound_data_after_draining = nil\n\n    with_em_timeout(10) do |future|\n      nc1 = NATS.connect(uri: @s.uri) do |nc|\n        expect(nc.options[:drain_timeout]).to eql(30)\n        nc.on_error do |err|\n          errors << err\n        end\n        nc.on_close do |err|\n          closed = true\n          future.resume\n        end\n\n        nc.subscribe(\"foo\", queue: \"worker\") do |msg, reply|\n          nc.publish(reply, \"ACK:foo\")\n        end\n\n        nc.subscribe(\"bar\") do |msg, reply|\n          nc.publish(reply, \"ACK:bar\")\n        end\n\n        nc.subscribe(\"quux\") do |msg, reply|\n          nc.publish(reply, \"ACK:quux\")\n        end\n\n        EM.add_timer(1) do\n          # Before draining\n          pending_data_before_draining = nc.instance_variable_get('@buf')\n          pending_outbound_data_before_draining = nc.pending_data_size\n\n          total_msgs_before_drain = nc.msgs_received\n          nc.drain do\n            after_drain = nc.draining?\n            drained = true\n\n            total_msgs_sent = nc.msgs_sent\n            total_msgs_after_drain = nc.msgs_received\n            pending_data_after_draining = nc.instance_variable_get('@buf')\n            pending_outbound_data_after_draining = nc.pending_data_size\n          end\n        end\n      end\n\n      # Fast publisher\n      nc2 = NATS.connect(uri: @s.uri) do |nc|\n        inbox = NATS.create_inbox\n        nc.subscribe(inbox) do |msg|\n          msgs << msg\n        end\n\n        timer = EM.add_periodic_timer(0.1) do\n          10000.times do\n            nc.publish(\"foo\", \"hi\", inbox)\n            nc.publish(\"bar\", \"hi\", inbox)\n            nc.publish(\"quux\", \"hi\", inbox)\n          end\n        end\n        EM.add_timer(1) do\n          EM.cancel_timer(timer)\n        end\n      end\n    end\n\n    # Should be the same as the messages received by the first client.\n    expect(msgs.count).to eql(total_msgs_sent)\n    expect(msgs.count).to eql(total_msgs_after_drain)\n    expect(pending_outbound_data_after_draining).to eql(0)\n    expect(pending_data_after_draining).to eql(nil)\n    expect(errors.count).to eql(0)\n    expect(closed).to eql(true)\n    expect(drained).to eql(true)\n    expect(after_drain).to eql(false)\n    expect(total_msgs_before_drain < total_msgs_after_drain).to eql(true)\n    expect(pending_data_after_draining).to eql(nil)\n  end\n\n  it \"should timeout draining if takes too long\" do\n    msgs = []\n    errors = []\n    closed = false\n    drained = false\n    after_drain = nil\n    total_msgs_before_drain = nil\n    total_msgs_after_drain = nil\n    total_msgs_sent = nil\n    pending_data_before_draining = nil\n    pending_data_after_draining = nil\n    pending_outbound_data_before_draining = nil\n    pending_outbound_data_after_draining = nil\n\n    with_em_timeout(10) do |future|\n      # Use a very short timeout for to timeout.\n      nc1 = NATS.connect(uri: @s.uri, drain_timeout: 0.01) do |nc|\n        nc.on_error do |err|\n          errors << err\n        end\n        nc.on_close do |err|\n          closed = true\n          future.resume\n        end\n\n        nc.subscribe(\"foo\", queue: \"worker\") do |msg, reply|\n          nc.publish(reply, \"ACK:foo\")\n        end\n\n        nc.subscribe(\"bar\") do |msg, reply|\n          nc.publish(reply, \"ACK:bar\")\n        end\n\n        nc.subscribe(\"quux\") do |msg, reply|\n          nc.publish(reply, \"ACK:quux\")\n        end\n\n        EM.add_timer(1) do\n          subs = nc.instance_variable_get('@subs')\n          pending_data_before_draining = nc.instance_variable_get('@buf')\n          total_msgs_before_drain = nc.msgs_received\n\n          nc.drain do\n            after_drain = nc.draining?\n            drained = true\n\n            pending_data_after_draining = nc.instance_variable_get('@buf')\n            total_msgs_after_drain = nc.msgs_received\n            total_msgs_sent = nc.msgs_sent\n          end\n        end\n      end\n\n      # Fast publisher\n      nc2 = NATS.connect(uri: @s.uri) do |nc|\n        inbox = NATS.create_inbox\n        nc.subscribe(inbox) do |msg|\n          msgs << msg\n        end\n\n        timer = EM.add_periodic_timer(0.1) do\n          10000.times do\n            nc.publish(\"foo\", \"hi\", inbox)\n            nc.publish(\"bar\", \"hi\", inbox)\n            nc.publish(\"quux\", \"hi\", inbox)\n          end\n        end\n        EM.add_timer(1) do\n          EM.cancel_timer(timer)\n        end\n      end\n    end\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a(NATS::ClientError)\n    expect(errors.first.to_s).to eql(\"Drain Timeout\")\n    expect(closed).to eql(true)\n    expect(drained).to eql(true)\n    expect(after_drain).to eql(false)\n    expect(total_msgs_before_drain < total_msgs_after_drain).to eql(true)\n    expect(pending_data_after_draining).to eql(nil)\n  end\n\n  it \"should disallow subscribe and unsubscribe while draining\" do\n    msgs = []\n    errors = []\n    closed = false\n    drained = false\n    after_drain = nil\n    total_msgs_before_drain = nil\n    total_msgs_after_drain = nil\n    total_msgs_sent = nil\n    pending_data_before_draining = nil\n    pending_data_after_draining = nil\n    pending_outbound_data_before_draining = nil\n    pending_outbound_data_after_draining = nil\n    unsub_result = true\n\n    no_more_subs = nil\n    with_em_timeout(10) do |future|\n      nc1 = NATS.connect(uri: @s.uri) do |nc|\n        expect(nc.options[:drain_timeout]).to eql(30)\n        nc.on_error do |err|\n          errors << err\n        end\n        nc.on_close do |err|\n          closed = true\n          future.resume\n        end\n\n        nc.subscribe(\"foo\", queue: \"worker\") do |msg, reply|\n          nc.publish(reply, \"ACK:foo\")\n        end\n\n        nc.subscribe(\"bar\") do |msg, reply|\n          nc.publish(reply, \"ACK:bar\")\n        end\n\n        nc.subscribe(\"quux\") do |msg, reply|\n          nc.publish(reply, \"ACK:quux\")\n        end\n\n        sub_timer = EM.add_periodic_timer(0.1) do\n          sid = nc.subscribe(\"hello.#{rand(1_000_000)}\") { }\n\n          if sid.nil?\n            no_more_subs = true \n            EM.cancel_timer(sub_timer)\n\n            # Any sid even if invalid should return right away\n            unsub_result = nc.unsubscribe(1)\n          end\n        end\n\n        EM.add_timer(1) do\n          pending_data_before_draining = nc.instance_variable_get('@buf')\n          pending_outbound_data_before_draining = nc.pending_data_size\n\n          total_msgs_before_drain = nc.msgs_received\n          nc.drain do\n            after_drain = nc.draining?\n            drained = true\n\n            total_msgs_sent = nc.msgs_sent\n            total_msgs_after_drain = nc.msgs_received\n            pending_data_after_draining = nc.instance_variable_get('@buf')\n            pending_outbound_data_after_draining = nc.pending_data_size\n          end\n        end\n      end\n\n      # Fast publisher\n      nc2 = NATS.connect(uri: @s.uri) do |nc|\n        inbox = NATS.create_inbox\n        nc.subscribe(inbox) do |msg|\n          msgs << msg\n        end\n\n        timer = EM.add_periodic_timer(0.1) do\n          10000.times do\n            nc.publish(\"foo\", \"hi\", inbox)\n            nc.publish(\"bar\", \"hi\", inbox)\n            nc.publish(\"quux\", \"hi\", inbox)\n          end\n        end\n        EM.add_timer(1) do\n          EM.cancel_timer(timer)\n        end\n      end\n    end\n\n    # Subscribe should have eventually failed\n    expect(no_more_subs).to eql(true)\n    expect(unsub_result).to eql(nil)\n\n    # Should be the same as the messages received by the first client.\n    expect(msgs.count).to eql(total_msgs_sent)\n    expect(msgs.count).to eql(total_msgs_after_drain)\n    expect(pending_outbound_data_after_draining).to eql(0)\n    expect(pending_data_after_draining).to eql(nil)\n    expect(errors.count).to eql(0)\n    expect(closed).to eql(true)\n    expect(drained).to eql(true)\n    expect(after_drain).to eql(false)\n    expect(total_msgs_before_drain < total_msgs_after_drain).to eql(true)\n    expect(pending_data_after_draining).to eql(nil)\n  end\n\n  it \"should disallow publish and flush outbound pending data once subscriptions have been drained\" do\n    msgs = []\n    errors = []\n    closed = false\n    drained = false\n    after_drain = nil\n\n    total_msgs_received_before_drain = nil\n    total_msgs_received_after_drain = nil\n    total_msgs_sent = nil\n\n    pending_data_before_draining = nil\n    pending_data_after_draining = nil\n    pending_outbound_data_before_draining = nil\n    pending_outbound_data_after_draining = nil\n\n    before_publish = nil\n    after_publish = nil\n    extra_pubs = 0\n    with_em_timeout(30) do |future|\n      nc1 = NATS.connect(uri: @s.uri) do |nc|\n        expect(nc.options[:drain_timeout]).to eql(30)\n        nc.on_error do |err|\n          errors << err\n        end\n        nc.on_close do |err|\n          closed = true\n\n          # Give sometime to the other client to receive\n          # all the messages that were published by client\n          # that started to drain.\n          EM.add_timer(5) do\n            future.resume\n          end\n        end\n\n        nc.subscribe(\"foo\", queue: \"worker\") do |msg, reply|\n          10.times { nc.publish(reply, \"ACK:foo\") }\n        end\n\n        nc.subscribe(\"bar\") do |msg, reply|\n          10.times { nc.publish(reply, \"ACK:bar\") }\n        end\n\n        nc.subscribe(\"quux\") do |msg, reply|\n          10.times { nc.publish(reply, \"ACK:quux\") }\n        end\n\n        EM.add_timer(0.5) do\n          pub_timer = EM.add_periodic_timer(0.1) do\n            before_publish = nc.msgs_sent\n            nc.publish(\"hello\", \"world\")\n            after_publish = nc.msgs_sent\n            if before_publish == after_publish\n              EM.cancel_timer(pub_timer)\n            else\n              extra_pubs += 1\n            end\n          end\n        end\n\n        EM.add_timer(1.5) do\n          pending_data_before_draining = nc.instance_variable_get('@buf')\n          pending_outbound_data_before_draining = nc.pending_data_size\n\n          total_msgs_received_before_drain = nc.msgs_received\n          nc.drain do\n            after_drain = nc.draining?\n            drained = true\n            total_msgs_sent = nc.msgs_sent\n            total_msgs_received_after_drain = nc.msgs_received\n            pending_data_after_draining = nc.instance_variable_get('@buf')\n            pending_outbound_data_after_draining = nc.pending_data_size\n          end\n        end\n      end\n\n      # Fast publisher\n      nc2 = NATS.connect(uri: @s.uri) do |nc|\n        inbox = NATS.create_inbox\n        nc.flush do\n          nc.subscribe(inbox) do |msg|\n            msgs << msg\n          end\n        end\n\n        timer = EM.add_periodic_timer(0.2) do\n          10000.times do\n            nc.publish(\"foo\", \"hi\", inbox)\n            nc.publish(\"bar\", \"hi\", inbox)\n            nc.publish(\"quux\", \"hi\", inbox)\n          end\n        end\n        EM.add_timer(1) do\n          EM.cancel_timer(timer)\n        end\n      end\n    end\n\n    # Should be the same as the messages received by the first client.\n    expect(msgs.count).to eql(total_msgs_sent-extra_pubs)\n    expect(msgs.count).to eql(total_msgs_received_after_drain*10)\n    expect(before_publish).to eql(after_publish)\n\n    expect(pending_outbound_data_after_draining).to eql(0) if not pending_outbound_data_after_draining.nil?\n    expect(pending_data_after_draining).to eql(nil)\n    expect(errors.count).to eql(0)\n    expect(closed).to eql(true)\n    expect(drained).to eql(true)\n    expect(after_drain).to eql(false)\n    expect(pending_data_after_draining).to eql(nil)\n  end\n\n  it \"should support draining a connection with NATS.drain\" do\n    msgs = []\n    drained = false\n    after_drain = nil\n    total_msgs_before_drain = nil\n    total_msgs_after_drain = nil\n    total_msgs_sent = nil\n    pending_data_before_draining = nil\n    pending_data_after_draining = nil\n    pending_outbound_data_before_draining = nil\n    pending_outbound_data_after_draining = nil\n\n    with_em_timeout(10) do\n      NATS.start(uri: @s.uri) do |nc|\n        expect(nc.options[:drain_timeout]).to eql(30)\n\n        NATS.subscribe(\"foo\", queue: \"worker\") do |msg, reply|\n          NATS.publish(reply, \"ACK:foo\")\n        end\n\n        NATS.subscribe(\"bar\") do |msg, reply|\n          NATS.publish(reply, \"ACK:bar\")\n        end\n\n        NATS.subscribe(\"quux\") do |msg, reply|\n          NATS.publish(reply, \"ACK:quux\")\n        end\n\n        EM.add_timer(1) do\n          # Before draining\n          pending_data_before_draining = nc.instance_variable_get('@buf')\n          pending_outbound_data_before_draining = nc.pending_data_size\n\n          total_msgs_before_drain = nc.msgs_received\n          NATS.drain do\n            after_drain = nc.draining?\n            drained = true\n\n            total_msgs_sent = nc.msgs_sent\n            total_msgs_after_drain = nc.msgs_received\n            pending_data_after_draining = nc.instance_variable_get('@buf')\n            pending_outbound_data_after_draining = nc.pending_data_size\n          end\n        end\n      end\n\n      # Fast publisher\n      NATS.connect(uri: @s.uri) do |nc|\n        inbox = NATS.create_inbox\n        nc.subscribe(inbox) do |msg|\n          msgs << msg\n        end\n\n        timer = EM.add_periodic_timer(0.1) do\n          10000.times do\n            nc.publish(\"foo\", \"hi\", inbox)\n            nc.publish(\"bar\", \"hi\", inbox)\n            nc.publish(\"quux\", \"hi\", inbox)\n          end\n        end\n        EM.add_timer(1) do\n          EM.cancel_timer(timer)\n        end\n      end\n    end\n\n    # Should be the same as the messages received by the first client.\n    expect(msgs.count).to eql(total_msgs_sent)\n    expect(msgs.count).to eql(total_msgs_after_drain)\n    expect(pending_outbound_data_after_draining).to eql(0)\n    expect(pending_data_after_draining).to eql(nil)\n    expect(drained).to eql(true)\n    expect(after_drain).to eql(false)\n    expect(total_msgs_before_drain < total_msgs_after_drain).to eql(true)\n    expect(pending_data_after_draining).to eql(nil)\n  end\nend\n"
  },
  {
    "path": "spec/client/client_nkeys_connect_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - NATS v2 Auth' do\n\n  context 'with NKEYS and JWT' do\n    before(:each) do\n      config_opts = {\n        'pid_file'      => '/tmp/nats_nkeys_jwt.pid',\n        'host'          => '127.0.0.1',\n        'port'          => 4722,\n      }\n      @s = NatsServerControl.init_with_config_from_string(%Q(\n        authorization { \n          timeout: 2\n        }\n\n        port = #{config_opts['port']}\n        operator = \"./spec/configs/nkeys/op.jwt\"\n\n        # This is for account resolution.\n        resolver = MEMORY\n\n         # This is a map that can preload keys:jwts into a memory resolver.\n         resolver_preload = {\n           # foo\n           AD7SEANS6BCBF6FHIB7SQ3UGJVPW53BXOALP75YXJBBXQL7EAFB6NJNA : \"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiIyUDNHU1BFSk9DNlVZNE5aM05DNzVQVFJIV1pVRFhPV1pLR0NLUDVPNjJYSlZESVEzQ0ZRIiwiaWF0IjoxNTUzODQwNjE1LCJpc3MiOiJPRFdJSUU3SjdOT1M3M1dWQk5WWTdIQ1dYVTRXWFdEQlNDVjRWSUtNNVk0TFhUT1Q1U1FQT0xXTCIsIm5hbWUiOiJmb28iLCJzdWIiOiJBRDdTRUFOUzZCQ0JGNkZISUI3U1EzVUdKVlBXNTNCWE9BTFA3NVlYSkJCWFFMN0VBRkI2TkpOQSIsInR5cGUiOiJhY2NvdW50IiwibmF0cyI6eyJsaW1pdHMiOnsic3VicyI6LTEsImNvbm4iOi0xLCJpbXBvcnRzIjotMSwiZXhwb3J0cyI6LTEsImRhdGEiOi0xLCJwYXlsb2FkIjotMSwid2lsZGNhcmRzIjp0cnVlfX19.COiKg5EFK4Gb2gA7vtKHQK7vjMEUx-RMWYuN-Bg-uVOFs9GLwW7Dxc4TcN-poBGBEkwKnleiA9SjYO3y4-AqBQ\"\n\n           # bar\n           AAXPTP32BD73YW3ACUY6DPXKWBSUW4VEZNE3LD4FUOFDP6KDU43PQVU2 : \"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJPQ1dUQkRQTzVETjRSV0lFNEtJQ1BQWkszUEhHV0dQUVFKNFVET1pQSTVaRzJQUzZKVkpBIiwiaWF0IjoxNTUzODQwNjE5LCJpc3MiOiJPRFdJSUU3SjdOT1M3M1dWQk5WWTdIQ1dYVTRXWFdEQlNDVjRWSUtNNVk0TFhUT1Q1U1FQT0xXTCIsIm5hbWUiOiJiYXIiLCJzdWIiOiJBQVhQVFAzMkJENzNZVzNBQ1VZNkRQWEtXQlNVVzRWRVpORTNMRDRGVU9GRFA2S0RVNDNQUVZVMiIsInR5cGUiOiJhY2NvdW50IiwibmF0cyI6eyJsaW1pdHMiOnsic3VicyI6LTEsImNvbm4iOi0xLCJpbXBvcnRzIjotMSwiZXhwb3J0cyI6LTEsImRhdGEiOi0xLCJwYXlsb2FkIjotMSwid2lsZGNhcmRzIjp0cnVlfX19.KY2fBvYyNCA0dYS7I6_rETGHT4YGkWZSh03XhXxwAvJ8XCfKlVJRY82U-0ERg01SFtPTZ-6BYu-sty1E67ioDA\"\n         }\n      ), config_opts)\n      @s.start_server(true)\n    end\n\n    after(:each) do\n      @s.kill_server\n    end\n\n    it 'should connect to server and publish messages' do\n      with_em_timeout do |f|\n        NATS.start(servers: [\"nats://127.0.0.1:4722\"], user_credentials: \"./spec/configs/nkeys/foo-user.creds\") do\n          NATS.subscribe(\"hello\") do |msg|\n            f.resume\n          end\n          NATS.publish('hello', 'world')\n        end\n      end\n    end\n\n    it 'should fail with auth error if no user credentials present' do\n      expect do \n        NATS.start(servers: [\"nats://127.0.0.1:4722\"]) do\n          NATS.publish('hello', 'world') do\n            EM.stop\n          end\n        end\n      end.to raise_error(NATS::AuthError)\n    end\n  end\n\n  context 'with NKEYS only' do\n    before(:each) do\n      config_opts = {\n        'pid_file'      => '/tmp/nats_nkeys.pid',\n        'host'          => '127.0.0.1',\n        'port'          => 4723,\n      }\n      @s = NatsServerControl.init_with_config_from_string(%Q(\n        authorization { \n          timeout: 2\n        }\n\n        port = #{config_opts['port']}\n        \n        accounts {\n          acme {\n            users [\n              {\n                 nkey = \"UCK5N7N66OBOINFXAYC2ACJQYFSOD4VYNU6APEJTAVFZB2SVHLKGEW7L\",\n                 permissions = {\n                   subscribe = {\n                     allow = [\"hello\", \"_INBOX.>\"]\n                     deny = [\"foo\"]\n                   }\n                   publish = {\n                     allow = [\"hello\", \"_INBOX.>\"]\n                     deny = [\"foo\"]\n                   }\n                 }\n              }\n            ]\n          }\n        }\n      ), config_opts)\n      @s.start_server(true)\n    end\n\n    after(:each) do\n      @s.kill_server\n    end\n\n    it 'should connect to the server and publish messages' do\n      with_em_timeout do |f|\n        NATS.start(servers: [\"nats://127.0.0.1:4723\"], nkeys_seed: \"./spec/configs/nkeys/foo-user.nk\") do\n          NATS.subscribe(\"hello\") do |msg|\n            f.resume\n          end\n          NATS.publish('hello', 'world')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_requests_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - requests' do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it 'should receive responses using single subscription for requests' do\n    msgs = []\n    received = false\n    nats = nil\n    NATS.start do |nc|\n      nats = nc\n      nc.subscribe('need_help') do |msg, reply|\n        nc.publish(reply, \"help-#{msg}\")\n      end\n\n      Fiber.new do\n        msgs << nc.request('need_help', 'yyy')\n        msgs << nc.request('need_help', 'zzz')\n        received = true\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure\n    end\n    expect(received).to eql(true)\n    expect(msgs.first).to eql('help-yyy')\n    expect(msgs.last).to eql('help-zzz')\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should receive a response from a request' do\n    received = false\n    nats = nil\n    NATS.start do |nc|\n      nats = nc\n      nc.subscribe('need_help') do |msg, reply|\n        expect(msg).to eql('yyy')\n        nc.publish(reply, 'help')\n      end\n\n      Fiber.new do\n        response = nc.request('need_help', 'yyy')\n        received = true\n        expect(response).to eql('help')\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure\n    end\n    expect(received).to eql(true)\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should perform similar using class mirror functions' do\n    received = false\n    NATS.start do\n      s = NATS.subscribe('need_help') do |msg, reply|\n        expect(msg).to eql('yyy')\n        NATS.publish(reply, 'help')\n        NATS.unsubscribe(s)\n      end\n\n      Fiber.new do\n        response = NATS.request('need_help', 'yyy')\n        received = true\n        expect(response).to eql('help')\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure\n    end\n    expect(received).to eql(true)\n  end\n\n  it 'should be possible to gather multiple responses before a timeout' do\n    nats = nil\n    responses = []\n    NATS.start do |nc|\n      nats = nc\n      10.times do |n|\n        nc.subscribe('need_help') do |msg, reply|\n          expect(msg).to eql('yyy')\n          nc.publish(reply, \"help-#{n}\")\n        end\n      end\n\n      Fiber.new do\n        responses = nc.request('need_help', 'yyy', max: 5, timeout: 1)\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure(2)\n    end\n    expect(responses.count).to eql(5)\n\n    # NOTE: Behavior change here in NATS v1.2.0, now responses\n    # are not in the same order as the subscriptions.\n    # responses.each_with_index do |response, i|\n    #   expect(response).to eql(\"help-#{i}\")\n    # end\n    expect(responses.count).to eql(5)\n\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should be possible to gather single response from a queue group before a timeout' do\n    nats = nil\n    responses = []\n    NATS.start do |nc|\n      nats = nc\n      10.times do |n|\n        nc.subscribe('need_help', queue: 'worker') do |msg, reply|\n          expect(msg).to eql('yyy')\n          nc.publish(reply, \"help\")\n        end\n      end\n\n      Fiber.new do\n        responses = nc.request('need_help', 'yyy', max: 5, timeout: 1)\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure(2)\n    end\n    expect(responses.count).to eql(1)\n    responses.each_with_index do |response, i|\n      expect(response).to eql(\"help\")\n    end\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should be possible to gather as many responses as possible before the timeout' do\n    nats = nil\n    responses = []\n    NATS.start do |nc|\n      nats = nc\n      nc.subscribe('need_help') do |msg, reply|\n        3.times do |n|\n          nc.publish(reply, \"help-#{n}\")\n        end\n\n        EM.add_timer(1) do\n          3.upto(10).each do |n|\n            nc.publish(reply, \"help-#{n}\")\n          end\n        end\n      end\n\n      Fiber.new do\n        responses = nc.request('need_help', 'yyy', max: 5, timeout: 0.5)\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure(2)\n    end\n\n    # Expected 5 but only 3 made it.\n    expect(responses.count).to eql(3)\n    responses.each_with_index do |response, i|\n      expect(response).to eql(\"help-#{i}\")\n    end\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should return empty array in case waited many responses but got none before timeout' do\n    nats = nil\n    responses = []\n    NATS.start do |nc|\n      nats = nc\n      Fiber.new do\n        responses = nc.request('need_help', 'yyy', max: 5, timeout: 0.5)\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure(2)\n    end\n\n    expect(responses.count).to eql(0)\n    resp_map = nats.instance_variable_get('@resp_map')\n    expect(resp_map.keys.count).to eql(0)\n  end\n\n  it 'should return nil in case waited single response but got none before timeout' do\n    response = nil\n    NATS.start do |nc|\n      Fiber.new do\n        response = nc.request('need_help', 'yyy')\n        NATS.stop\n      end.resume\n\n      timeout_nats_on_failure(2)\n    end\n    expect(response).to eql(nil)\n  end\n\n  it 'should fail if not wrapped in a fiber' do\n    NATS.start do\n      expect do\n        NATS.request('need_help', 'yyy')\n      end.to raise_error(FiberError)\n      NATS.stop\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - specification' do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server(true)\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it \"should complain if it can't connect to server when not running\" do\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n\n      NATS.connect(:uri => 'nats://127.0.0.1:3222')\n    end\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a(NATS::ConnectError)\n  end\n\n  it 'should complain if NATS.start is called without EM running and no block was given', :jruby_excluded do\n    expect(EM.reactor_running?).to eql(false)\n    expect { NATS.start }.to raise_error(NATS::Error)\n    expect(NATS.connected?).to eql(false)\n  end\n\n  it 'should report supplied connection name' do\n    @s.kill_server\n    @s.start_server(true, monitoring: true)\n    NATS.start(uri: 'nats://127.0.0.1:4222', name: 'test-connection') do\n      expect(JSON.parse(Net::HTTP.get(URI('http://localhost:8222/connz')))['connections'][0]['name']).to eq 'test-connection'\n      NATS.stop\n    end\n  end\n\n  it 'should perform basic block start and stop' do\n    NATS.start { NATS.stop }\n  end\n\n  it 'should signal connected state' do\n    NATS.start do\n      expect(NATS.connected?).to eql(true)\n      NATS.stop\n    end\n  end\n\n  it 'should have err_cb cleared after stop' do\n    NATS.start do\n      NATS.on_error { puts 'err' }\n      NATS.stop\n    end\n    expect(NATS.err_cb).to eql(nil)\n  end\n\n  it 'should raise NATS::ServerError on error replies from NATS Server' do\n    skip 'trying to unsubscribe non existant subscription does not send -ERR back in pedantic mode on gnatsd'\n\n    expect do\n      NATS.start(:pedantic => true) do\n        NATS.unsubscribe(10000)\n        NATS.publish('done') { NATS.stop }\n      end\n    end.to raise_error(NATS::ServerError)\n  end\n\n  it 'should do publish without payload and with opt_reply without error' do\n    NATS.start { |nc|\n      nc.publish('foo')\n      nc.publish('foo', 'hello')\n      nc.publish('foo', 'hello', 'reply')\n      NATS.stop\n    }\n  end\n\n  it 'should not complain when publishing to nil' do\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.start do\n        NATS.publish(nil)\n        NATS.publish(nil, 'hello')\n      end\n    end\n    expect(errors.count).to eql(0)\n  end\n\n  it 'should receive a sid when doing a subscribe' do\n    sid = nil\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.connect do |nc|\n        sid = nc.subscribe('foo')\n      end\n    end\n    expect(errors.count).to eql(0)\n    expect(sid).to_not eql(nil)\n  end\n\n  it 'should receive a sid when doing a request' do\n    sid = nil\n    errors = []\n    with_em_timeout do\n      NATS.on_error do |e|\n        errors << e\n      end\n      NATS.start do |nc|\n        sid = nc.request('foo') { }\n      end\n    end\n    expect(sid).to_not eql(nil)\n  end\n\n  it 'should receive a message that it has a subscription to' do\n    received = false\n    received_msg = nil\n    with_em_timeout do\n      NATS.start do |nc|\n        nc.subscribe('foo') do |msg|\n          received = true\n          received_msg = msg\n          NATS.stop\n        end\n        nc.publish('foo', 'xxx')\n      end\n    end\n    expect(received).to eql(true)\n    expect(received_msg).to eql('xxx')\n  end\n\n  it 'should receive a message that it has a wildcard subscription to' do\n    received = false\n    with_em_timeout do\n      NATS.start do |nc|\n        nc.subscribe('*') do |msg|\n          received = true\n          expect(msg).to eql('xxx')\n          NATS.stop\n        end\n        nc.publish('foo', 'xxx')\n      end\n    end\n    expect(received).to eql(true)\n  end\n\n  it 'should not receive a message that it has unsubscribed from' do\n    sid = nil\n    received = 0\n    msgs = []\n    with_em_timeout do\n      NATS.start do |nc|\n        sid = nc.subscribe('*') do |msg|\n          received += 1\n          msgs << msg\n          nc.unsubscribe(sid)\n        end\n        nc.publish('foo', 'xxx')\n      end\n    end\n    expect(received).to eql(1)\n    expect(msgs.first).to eql('xxx')\n  end\n\n  it 'should receive a response from a request' do\n    received = false\n    NATS.start do |nc|\n      nc.subscribe('need_help') do |msg, reply|\n        expect(msg).to eql('yyy')\n        nc.publish(reply, 'help')\n      end\n      nc.request('need_help', 'yyy') do |response|\n        received = true\n        expect(response).to eql('help')\n        NATS.stop\n      end\n      timeout_nats_on_failure\n    end\n    expect(received).to eql(true)\n  end\n\n  it 'should perform similar using class mirror functions' do\n    received = false\n    NATS.start do\n      s = NATS.subscribe('need_help') do |msg, reply|\n        expect(msg).to eql('yyy')\n        NATS.publish(reply, 'help')\n        NATS.unsubscribe(s)\n      end\n      r = NATS.request('need_help', 'yyy') do |response|\n        received = true\n        expect(response).to eql('help')\n        NATS.unsubscribe(r)\n        NATS.stop\n      end\n      timeout_nats_on_failure\n    end\n    expect(received).to eql(true)\n  end\n\n  it 'should return inside closure on publish when server received msg' do\n    received_pub_closure = false\n    NATS.start {\n      NATS.publish('foo') {\n        received_pub_closure = true\n        NATS.stop\n      }\n      timeout_nats_on_failure\n    }\n    expect(received_pub_closure).to eql(true)\n  end\n\n  it 'should return inside closure in ordered fashion when server received msg' do\n    replies = []\n    expected = []\n    received_pub_closure = false\n    NATS.start {\n      (1..100).each { |i|\n        expected << i\n        NATS.publish('foo') { replies << i }\n      }\n      NATS.publish('foo') {\n        received_pub_closure = true\n        NATS.stop\n      }\n      timeout_nats_on_failure\n    }\n    expect(received_pub_closure).to eql(true)\n    expect(replies).to eql(expected)\n  end\n\n  it \"should be able to start and use a new connection inside of start block\" do\n    new_conn = nil\n    received = false\n    NATS.start {\n      NATS.subscribe('foo') { received = true; NATS.stop }\n      new_conn = NATS.connect do\n        new_conn.publish('foo', 'hello')\n      end\n      timeout_nats_on_failure(5)\n    }\n    expect(new_conn).to_not eql(nil)\n    expect(received).to eql(true)\n  end\n\n  it 'should allow proper request/reply across multiple connections' do\n    new_conn = nil\n    received_request = false\n    received_reply = false\n\n    NATS.start {\n      new_conn = NATS.connect\n      new_conn.subscribe('test_conn_rr') do |msg, reply|\n        received_request = true\n        new_conn.publish(reply)\n      end\n      new_conn.on_connect do\n        NATS.request('test_conn_rr') do\n          received_reply = true\n          NATS.stop\n        end\n      end\n      timeout_nats_on_failure\n    }\n    expect(new_conn).to_not eql(nil)\n    expect(received_request).to eql(true)\n    expect(received_reply).to eql(true)\n  end\n\n  it 'should complain if NATS.start called without a block when we would need to start EM' do\n    expect do\n      NATS.start\n      NATS.stop\n    end.to raise_error(NATS::Error)\n  end\n\n  it 'should not complain if NATS.start called without a block when EM is running already', :jruby_excluded do\n    skip 'flapping in newer Rubies'\n\n    EM.run do\n      expect do\n        NATS.start\n        EM.next_tick { NATS.stop { EM.stop } }\n      end.to_not raise_error\n    end\n  end\n\n  it 'should use default url if passed uri is nil' do\n    NATS.start(:uri => nil) {  NATS.stop }\n  end\n\n  it 'should not complain about publish to nil unless in pedantic mode' do\n    NATS.start {\n      NATS.publish(nil, 'Hello!')\n      NATS.stop\n    }\n  end\n\n  it 'should allow proper unsubscribe from within blocks' do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo') { |msg|\n        received += 1\n        expect(sid).to_not eql(true)\n        NATS.unsubscribe(sid)\n      }\n      NATS.publish('foo')\n      NATS.publish('foo') { NATS.stop }\n    end\n    expect(received).to eql(1)\n  end\n\n  it 'should not call error handler for double unsubscribe unless in pedantic mode' do\n    got_error = false\n    NATS.on_error { got_error = true; NATS.stop }\n    NATS.start do\n      s = NATS.subscribe('foo')\n      NATS.unsubscribe(s)\n      NATS.unsubscribe(s)\n      NATS.publish('flush') { NATS.stop }\n    end\n    expect(got_error).to eql(false)\n  end\n\n  it 'should call error handler for double unsubscribe if in pedantic mode' do\n    skip 'double unsubscribe in gnatsd does not send -ERR back'\n\n    got_error = false\n    NATS.on_error { got_error = true; NATS.stop }\n    NATS.start(:pedantic => true) do\n      s = NATS.subscribe('foo')\n      NATS.unsubscribe(s)\n      NATS.unsubscribe(s)\n      NATS.publish('flush') { NATS.stop }\n    end\n    expect(got_error).to eql(true)\n  end\n\n  it 'should monitor inbound and outbound messages and bytes' do\n    msg = 'Hello World!'\n    NATS.start do |c|\n      NATS.subscribe('foo')\n      NATS.publish('foo', msg)\n      NATS.publish('bar', msg)\n      NATS.flush do\n        expect(c.msgs_sent).to eql(2)\n        expect(c.msgs_received).to eql(1)\n        expect(c.bytes_received).to eql(msg.size)\n        expect(c.bytes_sent).to eql(msg.size * 2)\n        NATS.stop\n      end\n    end\n  end\n\n  it \"should allow getting snapshot of inbound and outbound stats\" do\n    stats = nil\n    with_em_timeout do\n      NATS.start do |nats|\n        # Snapshot the stats from either of the callbacks\n        nats.subscribe(\">\") { stats = nats.stats }\n        nats.subscribe(\"foo\") { stats = nats.stats }\n        nats.flush do\n          nats.publish(\"foo\", \"world\")\n        end\n      end\n    end\n    expect(stats[:in_msgs]).to eql(2)\n    expect(stats[:in_bytes]).to eql(10)\n    expect(stats[:out_msgs]).to eql(1)\n    expect(stats[:out_bytes]).to eql(5)\n  end\n\n  it 'should receive a pong from a server after ping_interval' do\n    NATS.start(:ping_interval => 0.75) do\n      expect(NATS.client.pongs_received).to eql(0)\n      EM.add_timer(1) do\n        expect(NATS.client.pongs_received).to eql(1)\n        NATS.stop\n      end\n    end\n  end\n\n  it 'should disconnect from the server when pongs not received' do\n    EM.run do\n      NATS.connect(:ping_interval => 0.1, :max_outstanding_pings => 1, :reconnect => false) do |c|\n        c.on_error { NATS.stop }\n        def c.process_pong\n          # override to not process counters\n        end\n      end\n      EM.add_timer(0.5) do\n        expect(NATS.connected?).to eql(false)\n        EM.stop\n      end\n    end\n  end\n\n  it 'should stop the ping timer when disconnected or closed' do\n    EM.run do\n      $pings_received = 0\n      NATS.connect(:ping_interval => 0.1) do |c|\n        def c.send_ping\n          $pings_received += 1\n          close\n        end\n      end\n      EM.add_timer(0.5) do\n        expect($pings_received).to eql(1)\n        EM.stop\n      end\n    end\n  end\n\n  it 'should allowing setting name for the client on connect' do\n    with_em_timeout do\n      connect_command = \"CONNECT {\\\"verbose\\\":false,\\\"pedantic\\\":false,\\\"lang\\\":\\\"#{NATS::LANG}\\\",\\\"version\\\":\\\"#{NATS::VERSION}\\\",\\\"protocol\\\":#{NATS::PROTOCOL_VERSION},\\\"echo\\\":true,\\\"name\\\":\\\"hello\\\"}\\r\\n\"\n      conn = NATS.connect(:name => \"hello\")\n      expect(conn.connect_command).to eql(connect_command)\n    end\n  end\n\n  it 'should not repeat SUB commands when connecting' do\n    pending_commands = \"CONNECT {\\\"verbose\\\":false,\\\"pedantic\\\":true,\\\"lang\\\":\\\"#{NATS::LANG}\\\",\\\"version\\\":\\\"#{NATS::VERSION}\\\",\\\"protocol\\\":#{NATS::PROTOCOL_VERSION},\\\"echo\\\":true}\\r\\n\"\n    pending_commands += \"PING\\r\\n\"\n    pending_commands += \"SUB hello  2\\r\\nSUB hello  3\\r\\nSUB hello  4\\r\\nSUB hello  5\\r\\nSUB hello  6\\r\\n\"\n\n    msgs = []\n    expect do\n      EM.run do\n        EM.add_timer(1) do\n          expect(msgs.count).to eql(5)\n          EM.stop\n        end\n        conn = NATS.connect(:pedantic => true)\n        expect(conn).to receive(:send_data).once.with(pending_commands).and_call_original\n\n        5.times do\n          conn.subscribe(\"hello\") do |msg|\n            msgs << msg\n          end\n        end\n\n        # Expect INFO followed by PONG response\n        expect(conn).to receive(:receive_data).at_least(:twice).and_call_original\n        expect(conn).to receive(:send_data).once.with(\"PUB hello  5\\r\\nworld\\r\\n\").and_call_original\n        conn.flush do\n          # Once we connected already and received PONG back,\n          # we should be able to publish here.\n          conn.publish(\"hello\", \"world\")\n        end\n      end\n    end.to_not raise_error\n  end\n\n  it 'should accept the same option set twice' do\n    opts = {:uri => 'nats://127.0.0.1:4222'}\n    NATS.start(opts) { NATS.stop }\n    NATS.start(opts) { NATS.stop }\n  end\n\n  describe '#create_inbox' do\n    it 'create the expected format' do\n      expect(NATS.create_inbox).to match(/_INBOX\\.[a-f0-9]{12}/)\n    end\n\n    context 'when Kernel.srand is regularly reset to the same value' do\n      it 'should generate a unique inbox name' do\n        Kernel.srand 5555\n        first_inbox_name = NATS.create_inbox\n\n        Kernel.srand 5555\n        second_inbox_name = NATS.create_inbox\n\n        expect(second_inbox_name).to_not eq(first_inbox_name)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/client_tls_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - TLS spec', :jruby_excluded do\n\n  context 'when server does not support TLS' do\n\n    before(:each) do\n      @non_tls_server = NatsServerControl.new(\"nats://127.0.0.1:4222\")\n      @non_tls_server.start_server\n    end\n\n    after(:each) do\n      @non_tls_server.kill_server\n    end\n\n    it 'should error if client requires TLS' do\n      errors = []\n      closes = 0\n      reconnects = 0\n      disconnects = 0\n\n      options = {\n        :uri => 'nats://127.0.0.1:4222',\n        :reconnect => false,\n        :tls => {\n          :ssl_version => :TLSv1_2,\n          :protocols => [:tlsv1_2],\n          :private_key_file => './spec/configs/certs/key.pem',\n          :cert_chain_file  => './spec/configs/certs/server.pem',\n          :verify_peer      => false\n        }\n      }\n\n      with_em_timeout(5) do |future|\n        nc = nil\n        NATS.on_error      {|e| errors << e }\n        NATS.on_close      { closes += 1 }\n        NATS.on_reconnect  { reconnects += 1 }\n        NATS.on_disconnect { disconnects += 1 }\n\n        nc = NATS.connect(options)\n      end\n\n      expect(errors.count).to eql(2)\n      expect(errors.first).to be_a(NATS::ClientError)\n      expect(errors.first.to_s).to eql(\"TLS/SSL not supported by server\")\n      expect(errors.last).to be_a(NATS::ConnectError)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n\n      # Technically we were never connected to the NATS service\n      # in that server so we don't call disconnect right now.\n      expect(disconnects).to eql(0)\n    end\n  end\n\n  context 'when server requires TLS and no auth needed' do\n\n    before(:each) do\n      @tls_no_auth = NatsServerControl.new(\"nats://127.0.0.1:4444\", '/tmp/test-nats-4444.pid', \"-c ./spec/configs/tls-no-auth.conf\")\n      @tls_no_auth.start_server\n    end\n\n    after(:each) do\n      @tls_no_auth.kill_server\n    end\n\n    it 'should error if client does not set secure connection and dispatch callbacks' do\n      errors = []\n      closes = 0\n      reconnects = 0\n      disconnects = 0\n      reconnects = 0\n\n      with_em_timeout(3) do |future|\n        nc = nil\n\n        NATS.on_close      { closes += 1 }\n        NATS.on_reconnect  { reconnects += 1 }\n        NATS.on_disconnect { disconnects += 1 }\n\n        NATS.on_error do |e|\n          errors << e\n        end\n        nc = NATS.connect(:uri => 'nats://127.0.0.1:4444', :reconnect => false)\n      end\n\n      expect(errors.count > 0).to eq(true)\n      expect(errors.first).to be_a(NATS::ClientError)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n      expect(disconnects).to eql(1)\n    end\n  end\n\n  context 'when server requires TLS and authentication' do\n\n    before(:each) do\n      @tls_auth = NatsServerControl.new(\"nats://127.0.0.1:4443\", '/tmp/test-nats-4443.pid', \"-c ./spec/configs/tls.conf\")\n      @tls_auth.start_server\n    end\n\n    after(:each) do\n      @tls_auth.kill_server\n    end\n\n    it 'should error if client does not set secure connection and dispatch callbacks' do\n      errors = []\n      with_em_timeout(3) do |future|\n        nc = nil\n        NATS.on_error do|e|\n          errors << e\n        end\n        nc = NATS.connect(:uri => 'nats://127.0.0.1:4443', :reconnect => false)\n      end\n\n      # Client disconnected from server\n      expect(errors.count > 0).to eq(true)\n      expect(errors.first).to be_a(NATS::ClientError)\n    end\n\n    it 'should error if client does not set secure connection and stop trying to reconnect eventually' do\n      errors = []\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        nc = nil\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://127.0.0.1:4443'))\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          # NOTE: We cannot close again here in tests since\n          # we would be getting double fiber called errors.\n          # future.resume(nc) if not nc.closing?\n          closes += 1\n          future.resume\n        end\n\n        nc = NATS.connect({\n          :servers => ['nats://127.0.0.1:4443'],\n          :max_reconnect_attempts => 2,\n          :reconnect_time_wait => 1\n        })\n      end\n\n      # FIXME: It will be trying to reconnect for a number of times\n      # and some of the erros that will be getting could be errors\n      # such as Unknown Protocol due to parser failing with secure conn.\n      expect(reconnects).to eql(3)\n      expect(disconnects).to eql(4)\n      expect(closes).to eql(1)\n      expect(errors.count > 0).to eq(true)\n      expect(errors.count < 10).to eq(true)\n\n      # Client disconnected from server\n      expect(errors.first).to be_a(NATS::ClientError)\n      expect(errors.last).to be_a(NATS::ConnectError)\n    end\n\n    it 'should reject secure connection when using deprecated versions' do\n      errors = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        nc = nil\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        nc = NATS.connect({\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],\n          :tls => {\n            :ssl_version => :sslv2\n          }}) do\n          connects += 1\n        end\n        nc.subscribe(\"hello\")\n        nc.flush do\n          nc.close\n        end\n      end\n      expect(errors.count).to eql(1)\n      expect(errors.first).to be_a(NATS::ConnectError)\n      expect(connects).to eql(0)\n      expect(closes).to eql(1)\n      expect(disconnects).to eql(0)\n      expect(reconnects).to eql(0)\n    end\n\n    it 'should connect securely to server and authorize with default TLS and protocols options' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      disconnects = 0\n      reconnects  = 0\n      closes      = 0\n\n      with_em_timeout(10) do\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n        }\n        nc = NATS.connect(\"tls://secret:deadbeef@127.0.0.1:4443\", options) do |nc2|\n          server = nc2.connected_server\n          expect(server.scheme).to eql(\"tls\")\n          expect(server.host).to eql(\"127.0.0.1\")\n          expect(server.port).to eql(4443)\n          expect(server.userinfo).to eql(\"secret:deadbeef\")\n          connects += 1\n\n          info = nc2.server_info\n          expect(info).to_not be(nil)\n          expect(info).to be_a Hash\n          expect(info).to have_key :server_id\n          expect(info).to have_key :version\n          expect(info).to have_key :proto\n          expect(info).to have_key :client_id\n          expect(info).to have_key :max_payload\n          expect(info).to have_key :host\n          expect(info).to have_key :port\n          expect(info).to have_key :auth_required\n          expect(info).to have_key :tls_required\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n            expect(messages.count).to eql(1)\n          end\n        end\n      end\n      expect(errors.count).to eql(0)\n\n      # We are calling close so should not be calling\n      # the disconnect callback here.\n      expect(disconnects).to eql(0)\n      expect(connects).to eql(1)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n    end\n\n    it 'should connect securely to server and authorize with defaults only via setting ssl enabled option' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      disconnects = 0\n      reconnects  = 0\n      closes      = 0\n\n      with_em_timeout(10) do\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :ssl => true\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4443'))\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n          end\n        end\n      end\n      expect(messages.count).to eql(1)\n      expect(errors.count).to eql(0)\n\n      # We are calling close so should not be calling\n      # the disconnect callback here.\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n    end\n\n    it 'should connect securely with default TLS and protocols options' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      reconnects  = 0\n      disconnects = 0\n      closes      = 0\n\n      with_em_timeout(10) do |future|\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            # :ssl_version => :TLSv1_2,\n            # :protocols => [:tlsv1_2],\n            # :private_key_file => './spec/configs/certs/key.pem',\n            # :cert_chain_file  => './spec/configs/certs/server.pem',\n            # :verify_peer => true\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4443'))\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n          end\n        end\n      end\n      expect(errors.count).to eql(0)\n      expect(messages.count).to eql(1)\n      expect(reconnects).to eql(0)\n      expect(closes).to eql(1)\n      expect(disconnects).to eql(0)\n    end\n  end\n\n  context 'when server requires TLS, certificates and authentication' do\n\n    before(:each) do\n      @tls_verify_auth = NatsServerControl.new(\"nats://127.0.0.1:4445\", '/tmp/test-nats-4445.pid', \"-c ./spec/configs/tlsverify.conf\")\n      @tls_verify_auth.start_server\n    end\n\n    after(:each) do\n      @tls_verify_auth.kill_server\n    end\n\n    it 'should error if client does not set secure connection and dispatch callbacks' do\n      errors = []\n      with_em_timeout(10) do |future|\n        nc = nil\n        NATS.on_error do |e|\n          errors << e\n        end\n        nc = NATS.connect(:uri => 'nats://127.0.0.1:4445', :reconnect => false)\n      end\n      expect(errors.count >= 2).to eql(true)\n\n      # Client disconnected from server\n      expect(errors.first).to be_a(NATS::ClientError)\n      expect(errors.last).to  be_a(NATS::ConnectError)\n    end\n\n    it 'should error if client does not set secure connection and stop trying to reconnect eventually' do\n      errors = []\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        nc = nil\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          # NOTE: We cannot close again here in tests since\n          # we would be getting double fiber called errors.\n          # future.resume(nc) if not nc.closing?\n          closes += 1\n        end\n\n        nc = NATS.connect(:uri => 'nats://127.0.0.1:4445', :reconnect_time_wait => 1, :max_reconnect_attempts => 2)\n      end\n      expect(errors.count > 2).to eql(true)\n      expect(errors.count < 10).to eql(true)\n      expect(disconnects).to eql(4)\n      expect(reconnects).to eql(3)\n      expect(closes).to eql(1)\n\n      # Client disconnected from server\n      expect(errors.first).to be_a(NATS::ClientError)\n      expect(errors.last).to  be_a(NATS::ConnectError)\n    end\n\n    it 'should reject secure connection if no certificate is provided' do\n      errors      = []\n      connects    = 0\n      reconnects  = 0\n      disconnects = 0\n      closes      = 0\n\n      with_em_timeout(5) do |future|\n        nc = nil\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        nc = NATS.connect({\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n            :tls => {\n              :ssl_version => :TLSv1_2\n            }\n          }) do\n          connects += 1\n        end\n        nc.subscribe(\"hello\")\n        nc.flush\n      end\n      expect(errors.count).to eql(1)\n      expect(errors.first).to be_a(NATS::ConnectError)\n      expect(connects).to eql(0)\n      expect(closes).to eql(1)\n      expect(disconnects).to eql(0)\n    end\n\n    it 'should connect securely to server and authorize' do\n      errors = []\n      messages = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :verify_peer      => false\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n          end\n        end\n      end\n      expect(errors.count).to eql(0)\n      expect(messages.count).to eql(1)\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(1)\n    end\n\n    it 'should connect securely with default TLS and protocols options' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      reconnects  = 0\n      disconnects = 0\n      closes      = 0\n\n      with_em_timeout(10) do |future|\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n          end\n        end\n      end\n      expect(messages.count).to eql(1)\n      expect(errors.count).to eql(0)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n      expect(disconnects).to eql(0)\n    end\n  end\n\n  context 'when server requires TLS, certificates, authentication and client enables verify peer' do\n\n    before(:each) do\n      @tls_verify_auth = NatsServerControl.new(\"nats://127.0.0.1:4445\", '/tmp/test-nats-4445.pid', \"-c ./spec/configs/tlsverify.conf\")\n      @tls_verify_auth.start_server\n    end\n\n    after(:each) do\n      @tls_verify_auth.kill_server\n    end\n\n    it 'should fail to connect if CA is not given' do\n      errors = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(3) do\n        nc = nil\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :verify_peer      => true\n          }\n        }\n        expect do\n          nc = NATS.connect(options) do |conn|\n            connects += 1\n          end\n        end.to raise_error(NATS::Error)\n      end\n      expect(errors.count).to eql(0)\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(0)\n    end\n\n    it 'should fail to connect if CA is not readable' do\n      errors = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(3) do\n        nc = nil\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :ca_file => './spec/configs/certs/does-not-exists.pem',\n            :verify_peer      => true\n          }\n        }\n        expect do\n          nc = NATS.connect(options) do |conn|\n            connects += 1\n          end\n        end.to raise_error(NATS::Error)\n      end\n\n      # No error here since it fails synchronously\n      expect(errors.count).to eql(0)\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(0)\n    end\n\n    it 'should connect securely to server and authorize' do\n      errors = []\n      messages = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :ca_file => './spec/configs/certs/ca.pem',\n            :verify_peer => true\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n            future.resume(nc)\n          end\n        end\n      end\n      expect(errors.count).to eql(0)\n      expect(messages.count).to eql(1)\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(1)\n    end\n\n    it 'should give up connecting securely to server if cannot verify peer' do\n      errors = []\n      messages = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :ca_file => './spec/configs/certs/bad-ca.pem',\n            :verify_peer => true\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n            future.resume(nc)\n          end\n        end\n      end\n      expect(errors.count).to eql(2)\n      expect(errors.first).to be_a(NATS::ConnectError)\n      expect(errors.first.to_s).to eql('TLS Verification failed checking issuer based on CA ./spec/configs/certs/bad-ca.pem')\n      \n      expect(errors.last).to be_a(NATS::ConnectError)\n      expect(messages.count).to eql(0)\n      expect(disconnects).to eql(0)\n      expect(closes).to eql(1)\n    end\n\n    it 'should connect securely with default TLS and protocols options and assume verify if CA given' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      reconnects  = 0\n      disconnects = 0\n      closes      = 0\n\n      with_em_timeout(10) do |future|\n\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :ca_file => './spec/configs/certs/ca.pem'\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n          end\n        end\n      end\n      expect(messages.count).to eql(1)\n      expect(errors.count).to eql(0)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n      expect(disconnects).to eql(0)\n    end\n\n    it 'should connect securely with verify peer when multiple CAs are present in the CA file' do\n      errors      = []\n      messages    = []\n      connects    = 0\n      reconnects  = 0\n      disconnects = 0\n      closes      = 0\n\n      with_em_timeout(10) do |future|\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n            :ca_file => './spec/configs/certs/multi-ca.pem', # First CA is invalid, second is valid\n            :verify_peer => true\n          }\n        }\n\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))\n\n          connects += 1\n        end\n\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\") do\n            nc.unsubscribe(sid)\n            nc.close\n            future.resume(nc)\n          end\n        end\n      end\n      expect(messages.count).to eql(1)\n      expect(errors.count).to eql(0)\n      expect(closes).to eql(1)\n      expect(reconnects).to eql(0)\n      expect(disconnects).to eql(0)\n    end\n  end\n\n  context 'when servers require TLS' do\n\n    before(:each) do\n      @tls_server = NatsServerControl.new(\"nats://127.0.0.1:4445\", '/tmp/test-nats-4445.pid', \"-c ./spec/configs/tls-no-auth.conf\")\n      @tls_server.start_server\n      @tls_server2 = NatsServerControl.new(\"nats://127.0.0.1:4446\", '/tmp/test-nats-4446.pid', \"-c ./spec/configs/tls-no-auth.conf\")\n      @tls_server2.start_server\n    end\n\n    after(:each) do\n      @tls_server.kill_server if NATS.server_running?(@tls_server.uri)\n      @tls_server2.kill_server if NATS.server_running?(@tls_server2.uri)\n    end\n\n    it 'should connect securely to server and reconnect' do\n      errors = []\n      messages = []\n      connects = 0\n      reconnects = 0\n      disconnects = 0\n      closes = 0\n\n      with_em_timeout(10) do |future|\n        NATS.on_error do |e|\n          errors << e\n        end\n\n        NATS.on_disconnect do |e|\n          disconnects += 1\n        end\n\n        NATS.on_reconnect do\n          reconnects += 1\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        options = {\n          :servers => ['nats://127.0.0.1:4445','nats://127.0.0.1:4446'],\n          :max_reconnect_attempts => 1,\n          :dont_randomize_servers => true,\n          :tls => {\n            :ssl_version => :TLSv1_2,\n            :protocols => [:tlsv1_2],\n            :private_key_file => './spec/configs/certs/key.pem',\n            :cert_chain_file  => './spec/configs/certs/server.pem',\n          }\n        }\n\n        # Confirm connect is ok\n        nc = NATS.connect(options) do |conn|\n          expect(conn.connected_server).to eql(URI.parse('nats://127.0.0.1:4445'))\n\n          connects += 1\n        end\n        sid = nc.subscribe(\"hello\") do |msg|\n          messages << msg\n        end\n        nc.flush do\n          nc.publish(\"hello\", \"world\")\n        end\n\n        # Confirm that the client reconnects\n        EM.add_timer(1) do\n          @tls_server.kill_server\n        end\n\n        # Should be reconnected at this point and subscriptions replayed\n        EM.add_timer(2) do\n          nc.flush do\n            nc.publish(\"hello\", \"again\") do\n              nc.unsubscribe(sid)\n              nc.close\n              future.resume\n            end\n          end\n        end\n      end\n      expect(errors.count).to eql(0)\n      expect(messages.count).to eql(2)\n      expect(disconnects).to eql(1)\n      expect(closes).to eql(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_auth_token_spec.rb",
    "content": "require 'spec_helper'\nrequire 'yaml'\n\ndescribe 'Client - auth token' do\n\n  before(:all) do\n\n    auth_options = {\n      'token'    => 'deadbeef',\n      'timeout'  => 5\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          token: '#{auth_options[\"token\"]}'\n          timeout: #{auth_options[\"timeout\"]}\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: #{auth_options[\"timeout\"]}\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2].each do |s|\n      s.start_server(true)\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2].each do |s|\n      s.kill_server\n    end\n  end\n\n  let(:auth_token) {\n    'deadbeef'\n  }\n\n  it 'should properly connect to different servers using token' do\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri, :token => auth_token)\n      c2 = NATS.connect(:uri => \"nats://#{auth_token}@#{@s1.uri.host}:#{@s1.uri.port}\")\n      c3 = NATS.connect(\"nats://#{auth_token}@#{@s1.uri.host}:#{@s1.uri.port}\")\n      wait_on_connections([c1, c2, c3]) do\n        EM.stop\n      end\n    end\n  end\n\n  it 'should raise auth error when using wrong token' do\n    errors = []\n    with_em_timeout(2) do |future|\n      NATS.on_error do |e|\n        errors << e\n        future.resume\n      end\n      NATS.connect(:uri => @s1.uri, :token => 'wrong')\n    end\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a(NATS::AuthError)\n\n    errors = []\n    with_em_timeout(2) do |future|\n      NATS.on_error do |e|\n        errors << e\n        future.resume\n      end\n      NATS.connect(:uri => \"nats://wrong@#{@s1.uri.host}:#{@s1.uri.port}\")\n    end\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a(NATS::AuthError)\n\n    errors = []\n    with_em_timeout(2) do |future|\n      NATS.on_error do |e|\n        errors << e\n        future.resume\n      end\n      NATS.connect(\"nats://wrong@#{@s1.uri.host}:#{@s1.uri.port}\")\n    end\n    expect(errors.count).to eql(1)\n    expect(errors.first).to be_a(NATS::AuthError)\n  end\n\n  it 'should reuse token for reconnecting' do\n    data = 'Hello World!'\n    to_send = 100\n    received = c1_received = c2_received = 0\n    reconnected = false\n    with_em_timeout(3) do\n      c1 = NATS.connect(:uri => @s1.uri, :token => auth_token)\n      c2 = NATS.connect(:uri => @s2.uri, :token => auth_token)\n      c1.on_reconnect do\n        reconnected = true\n      end\n\n      c1.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        received += 1\n      end\n      c2.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        received += 1\n      end\n\n      wait_on_routes_connected([c1, c2]) do\n        (1..to_send).each { c2.publish('foo', data) }\n      end\n\n      EM.add_timer(0.5) do\n        @s1.kill_server\n        EM.add_timer(1) do\n          (1..to_send).each { c2.publish('foo', data) }\n        end\n      end\n    end\n\n    expect(received).to eql(to_send*2)\n    expect(reconnected).to eql(true)\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_auto_discovery_spec.rb",
    "content": "require 'spec_helper'\nrequire 'yaml'\n\ndescribe 'Client - cluster auto discovery' do\n\n  before(:each) do\n\n    auth_options = {\n      'user'     => 'secret',\n      'password' => 'user',\n      'token'    => 'deadbeef',\n      'timeout'  => 5\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    s3_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s3.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4244,\n      'cluster_port'  => 6224\n    }\n\n    s4_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s4.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4245,\n      'cluster_port'  => 6225\n    }\n\n    s5_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s5.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4246,\n      'cluster_port'  => 6226\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts, s3_config_opts, s4_config_opts, s5_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: 5\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: 5\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2, @s3, @s4, @s5 = nodes\n  end\n\n  after(:each) do\n    [@s1, @s2, @s3, @s4, @s5].each do |s|\n      s.kill_server if NATS.server_running?(s.uri)\n    end\n  end\n\n  it 'should properly discover nodes in cluster upon connect and randomize by default' do\n    # Start servers and form a cluster, client will only be aware of first node\n    # though it will discover the other nodes in the cluster automatically.\n    [@s1, @s2, @s3].each do |node|\n      node.start_server(true)\n    end\n\n    servers_upon_connect = 0\n    c1_msgs = []\n\n    randomize_calls = []\n    allow_any_instance_of(Array).to receive(:'shuffle!') do |srvs|\n      randomize_calls << srvs\n    end\n\n    with_em_timeout do\n      c1 = NATS.connect(:servers => [@s1.uri]) do |nats|\n        servers_upon_connect = nats.server_pool.count\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n        NATS.stop\n      end\n\n      c1.subscribe(\"hello\") do |msg|\n        c1_msgs << msg\n      end\n    end\n    expect(servers_upon_connect).to eql(3)\n    srvs = randomize_calls.last\n    expect(srvs.count).to eql(2)\n    srv_0 = srvs.select{ |srv| srv[:uri] == @s2.uri }.first\n    srv_1 = srvs.select{ |srv| srv[:uri] == @s3.uri }.first\n    expect(srv_0[:uri]).not_to be_nil\n    expect(srv_1[:uri]).not_to be_nil\n  end\n\n  it 'should properly discover nodes in cluster eventually after first connect' do\n    [@s1, @s2].each do |node|\n      node.start_server(true)\n    end\n\n    servers_upon_connect = 0\n    servers_after_connect = 0\n    with_em_timeout(5) do\n      c1 = NATS.connect(:servers => [@s1.uri]) do |nats|\n        servers_upon_connect = nats.server_pool.count\n        expect(nats.connected_server).to eql(@s1.uri)\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        @s3.start_server(true)\n        EM.add_timer(2) do\n          # Should have detected new server asynchronously\n          servers_after_connect = nats.server_pool.count\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          expect(nats.discovered_servers.count).to eql(2)\n        end\n      end\n    end\n    expect(servers_upon_connect).to eql(2)\n    expect(servers_after_connect).to eql(3)\n  end\n\n  it 'should properly discover nodes in cluster eventually' do\n    [@s1, @s2, @s3, @s4, @s5].each do |s|\n      s.start_server(true)\n    end\n    pool_a = nil\n    pool_b = nil\n    pool_c = nil\n    c1_errors = []\n    c2_errors = []\n    c3_errors = []\n\n    with_em_timeout(5) do\n      c1 = NATS.connect(:servers => [@s1.uri], :reconnect => false) do |nats|\n        expect(nats.connected_server).to eql(@s1.uri)\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        EM.add_timer(2) do\n          pool_a = nats.server_pool\n          expect(nats.connected_server).to eql(@s1.uri)\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          c1.close\n        end\n      end\n      c1.on_error do |e|\n        c1_errors << e\n      end\n\n      c2 = NATS.connect(:servers => [@s1.uri], :reconnect => false, :dont_randomize_servers => false) do |nats|\n        expect(nats.connected_server).to eql(@s1.uri)\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        EM.add_timer(2) do\n          pool_b = nats.server_pool\n          expect(nats.connected_server).to eql(@s1.uri)\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          c2.close\n        end\n      end\n      c2.on_error do |e|\n        c2_errors << e\n      end\n\n      c3 = NATS.connect(:servers => [@s1.uri], :reconnect => false) do |nats|\n        expect(nats.connected_server).to eql(@s1.uri)\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        EM.add_timer(2) do\n          pool_c = nats.server_pool\n          expect(nats.connected_server).to eql(@s1.uri)\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          c3.close\n        end\n      end\n      c3.on_error do |e|\n        c3_errors << e\n      end\n    end\n    expect(pool_a.count).to eql(5)\n    expect(pool_b.count).to eql(5)\n    expect(pool_c.count).to eql(5)\n    expect(c1_errors.count).to eql(0)\n    expect(c2_errors.count).to eql(0)\n    expect(c3_errors.count).to eql(0)\n  end\n\n  it 'should properly discover nodes in cluster and reconnect to new one on failure' do\n    [@s1, @s2].each do |node|\n      node.start_server(true)\n    end\n\n    reconnects = []\n    servers_upon_connect = 0\n    servers_after_connect = 0\n    server_pool_state = nil\n    with_em_timeout(10) do\n      NATS.on_reconnect do |nats|\n        reconnects << nats.connected_server\n        server_pool_state = nats.server_pool\n      end\n\n      NATS.connect(:servers => [@s1.uri], :dont_randomize_servers => true) do |nats|\n        servers_upon_connect = nats.server_pool.count\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        @s3.start_server(true)\n        EM.add_timer(2) do\n          # Should have detected new server asynchronously\n          servers_after_connect = nats.server_pool.count\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          @s1.kill_server\n          EM.add_timer(2) do\n            @s1.start_server(true)\n            @s2.kill_server\n          end\n        end\n      end\n    end\n    expect(servers_upon_connect).to eql(2)\n    expect(servers_after_connect).to eql(3)\n    expect(reconnects.count).to eql(2)\n    expect(reconnects.first.port).to eql(@s2.uri.port)\n    expect(reconnects.last.port).to eql(@s3.uri.port)\n    expect(server_pool_state.count).to eql(3)\n    server_pool_state.each do |srv|\n      expect(srv[:was_connected]).to eql(true)\n    end\n  end\n\n  it 'should authenticate to discovered nodes using explicit credentials' do\n    [@s1, @s2].each do |node|\n      node.start_server(true)\n    end\n\n    reconnects = []\n    servers_upon_connect = 0\n    servers_after_connect = 0\n    server_pool_state = nil\n    with_em_timeout(10) do\n      NATS.on_reconnect do |nats|\n        reconnects << nats.connected_server\n        server_pool_state = nats.server_pool\n      end\n\n      NATS.connect({\n          :servers => [\"nats://#{@s1.uri.host}:#{@s1.uri.port}\"],\n          :dont_randomize_servers => true,\n          :user => @s1.uri.user,\n          :pass => @s1.uri.password\n        }) do |nats|\n        servers_upon_connect = nats.server_pool.count\n        expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n\n        @s3.start_server(true)\n        EM.add_timer(2) do\n          # Should have detected new server asynchronously\n          servers_after_connect = nats.server_pool.count\n          expect(nats.server_pool.first[:uri]).to eql(nats.connected_server)\n          @s1.kill_server\n          EM.add_timer(2) do\n            @s1.start_server(true)\n            @s2.kill_server\n          end\n        end\n      end\n    end\n    expect(servers_upon_connect).to eql(2)\n    expect(servers_after_connect).to eql(3)\n    expect(reconnects.count).to eql(2)\n    expect(reconnects.first.port).to eql(@s2.uri.port)\n    expect(reconnects.last.port).to eql(@s3.uri.port)\n    expect(server_pool_state.count).to eql(3)\n    server_pool_state.each do |srv|\n      expect(srv[:was_connected]).to eql(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_lb_spec.rb",
    "content": "require 'spec_helper'\nrequire 'uri'\n\ndescribe 'Client - cluster load balance' do\n\n  before(:each) do\n    auth_options = {\n      'user'     => 'derek',\n      'password' => 'bella',\n      'token'    => 'deadbeef',\n      'timeout'  => 1\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: 0.5\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: 1\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2].each do |s|\n      s.start_server(true)\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2].each do |s|\n      s.kill_server\n    end\n  end\n\n  # Tests that the randomize pool works correctly and that not\n  # all clients are connecting to the same server.\n  it 'should properly load balance between multiple servers with same client config' do\n    clients = []\n    servers = { @s1.uri => 0, @s2.uri => 0 }\n    EM.run do\n      for i in 0...20\n        clients << NATS.connect(:uri => servers.keys)\n      end\n      results = {}\n      wait_on_connections(clients) do\n        clients.each do |c|\n          servers[c.connected_server] += 1\n          port, ip = Socket.unpack_sockaddr_in(c.get_peername)\n          expect(port).to eql(URI(c.connected_server).port)\n        end\n\n        servers.each_value do |v|\n          expect(v > 0).to eql(true)\n        end\n        EM.stop\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_multi_route_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - cluster' do\n\n  before(:all) do\n    auth_options = {\n      'user'     => 'derek',\n      'password' => 'bella',\n      'token'    => 'deadbeef',\n      'timeout'  => 5\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: #{auth_options[\"timeout\"]}\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: #{auth_options[\"timeout\"]}\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2].each do |s|\n      s.start_server(true) unless NATS.server_running? s.uri\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2].each do |s|\n      s.kill_server\n    end\n  end\n\n  it 'should properly route plain messages between different servers' do\n    data = 'Hello World!'\n    received = 0\n    with_em_timeout do\n      c1 = NATS.connect(:uri => @s1.uri) do |nats|\n        nats.subscribe('foo') do |msg|\n          expect(msg).to eql(data)\n          received += 1\n        end\n      end\n      c2 = NATS.connect(:uri => @s2.uri) do |nats|\n        nats.subscribe('foo') do |msg|\n          expect(msg).to eql(data)\n          received += 1\n        end\n      end\n\n      c1.flush do\n        c2.flush do\n          wait_on_routes_connected([c1, c2]) do\n            c2.publish('foo', data)\n            c2.publish('foo', data)\n            flush_routes([c1, c2]) { EM.stop }\n          end\n        end\n      end\n    end\n    expect(received).to eql(4)\n  end\n\n  it 'should properly route messages with staggered startup' do\n\n    @s2.kill_server\n    data = 'Hello World!'\n    received = 0\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri) do\n        c1.subscribe('foo') do |msg|\n          expect(msg).to eql(data)\n          received += 1\n        end\n        c1.flush do\n          @s2.start_server\n          c2 = NATS.connect(:uri => @s2.uri) do\n            flush_routes([c1, c2]) do\n              c2.publish('foo', data)\n              c2.publish('foo', data)\n              flush_routes([c1, c2]) { EM.stop }\n            end\n          end\n        end\n      end\n    end\n    expect(received).to eql(2)\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_retry_connect_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - cluster retry connect' do\n\n  before(:all) do\n    auth_options = {\n      'user'     => 'derek',\n      'password' => 'bella',\n      'token'    => 'deadbeef',\n      'timeout'  => 1\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: 5\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: 5\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2].each do |s|\n      s.start_server(true)\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2].each do |s|\n      s.kill_server\n    end\n  end\n\n  it 'should re-establish asymmetric route connections upon restart' do\n    data = 'Hello World!'\n    received = 0\n    with_em_timeout(5) do |future|\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n\n      c1.subscribe('foo') do |msg|\n        expect(msg).to eql(data)\n        received += 1\n\n        if received == 2\n          future.resume # proper exit\n        elsif received == 1\n          # Here we will kill s1, which does not actively connect to anyone.\n          # Upon restart we will make sure the route was re-established properly.\n\n          @s1.kill_server\n          @s1.start_server\n\n          wait_on_routes_connected([c1, c2]) do\n            # Auto discovery kicks in so connects to next one available\n            expect(c1.connected_server).to eql(@s2.uri)\n            c2.publish('foo', data)\n          end\n        end\n      end\n\n      wait_on_routes_connected([c1, c2]) do\n        c2.publish('foo', data)\n      end\n\n    end\n\n    expect(received).to eql(2)\n  end\n\n  it 'should call the reconnect, disconnect, and close callbacks whenever it detaches from a server' do\n    disconnects = []\n    reconnects = []\n    with_em_timeout(10) do |future|\n      nc = NATS.connect(:servers => [@s1.uri, @s2.uri], :dont_randomize_servers => true, :max_reconnect_attempts => 3) do |nc|\n\n        nc.on_disconnect do |reasons|\n          disconnects << reasons\n        end\n\n        nc.on_reconnect do |nc|\n          reconnects << nc.connected_server\n        end\n\n        nc.on_close do\n          future.resume\n        end\n\n        EM.add_timer(1) do\n          @s1.kill_server\n        end\n\n        EM.add_timer(2) do\n          @s1.start_server\n          @s2.kill_server\n        end\n      end\n\n      nc.flush do\n        nc.subscribe(\"hello\")\n      end\n    end\n    expect(disconnects.count).to eql(3)\n    expect(reconnects.count).to eql(2)\n    expect(disconnects[0].to_s).to eql(%Q(Client disconnected from server on #{@s1.uri}))\n    expect(disconnects[1].to_s).to eql(%Q(Client disconnected from server on #{@s2.uri}))\n    expect(disconnects[2].to_s).to eql(%Q(Client disconnected from server on #{@s1.uri}))\n    expect(reconnects[0]).to eql(@s2.uri)\n    expect(reconnects[1]).to eql(@s1.uri)\n  end\nend\n"
  },
  {
    "path": "spec/client/cluster_spec.rb",
    "content": "require 'spec_helper'\nrequire 'yaml'\n\ndescribe 'Client - cluster' do\n\n  before(:all) do\n\n    auth_options = {\n      'user'     => 'derek',\n      'password' => 'bella',\n      'token'    => 'deadbeef',\n      'timeout'  => 5\n    }\n\n    s1_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s1.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4242,\n      'cluster_port'  => 6222\n    }\n\n    s2_config_opts = {\n      'pid_file'      => '/tmp/nats_cluster_s2.pid',\n      'authorization' => auth_options,\n      'host'          => '127.0.0.1',\n      'port'          => 4243,\n      'cluster_port'  => 6223\n    }\n\n    nodes = []\n    configs = [s1_config_opts, s2_config_opts]\n    configs.each do |config_opts|\n\n      other_nodes_configs = configs.select do |conf|\n        conf['cluster_port'] != config_opts['cluster_port']\n      end\n\n      routes = []\n      other_nodes_configs.each do |conf|\n        routes <<  \"nats-route://foo:bar@127.0.0.1:#{conf['cluster_port']}\"\n      end\n\n      nodes << NatsServerControl.init_with_config_from_string(%Q(\n        host: '#{config_opts['host']}'\n        port:  #{config_opts['port']}\n\n        pid_file: '#{config_opts['pid_file']}'\n\n        authorization {\n          user: '#{auth_options[\"user\"]}'\n          password: '#{auth_options[\"password\"]}'\n          timeout: #{auth_options[\"timeout\"]}\n        }\n\n        cluster {\n          host: '#{config_opts['host']}'\n          port: #{config_opts['cluster_port']}\n\n          authorization {\n            user: foo\n            password: bar\n            timeout: #{auth_options[\"timeout\"]}\n          }\n\n          routes = [\n            #{routes.join(\"\\n            \")}\n          ]\n        }\n      ), config_opts)\n    end\n\n    @s1, @s2 = nodes\n  end\n\n  before(:each) do\n    [@s1, @s2].each do |s|\n      s.start_server(true)\n    end\n  end\n\n  after(:each) do\n    [@s1, @s2].each do |s|\n      s.kill_server\n    end\n  end\n\n  it 'should properly connect to different servers' do\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n      wait_on_connections([c1, c2]) do\n        EM.stop\n      end\n    end\n  end\n\n  it 'should properly route messages for distributed queues on different servers' do\n    data = 'Hello World!'\n    to_send = 100\n    received = c1_received = c2_received = 0\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n      c1.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c1_received += 1\n        received += 1\n      end\n      c2.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c2_received += 1\n        received += 1\n      end\n\n      wait_on_routes_connected([c1, c2]) do\n        (1..to_send).each { c2.publish('foo', data) }\n        flush_routes([c1, c2]) { EM.stop }\n      end\n    end\n\n    expect(received).to eql(to_send)\n    expect(c1_received < to_send).to eql(true)\n    expect(c2_received < to_send).to eql(true)\n    expect(c1_received).to be_within(25).of(to_send/2)\n    expect(c2_received).to be_within(25).of(to_send/2)\n  end\n\n  it 'should properly route messages for distributed queues and normal subscribers on different servers' do\n    data = 'Hello World!'\n    to_send = 100\n    received = c1_received = c2_received = 0\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n      c1.subscribe('foo') do |msg|\n        expect(msg).to eql(data)\n        received += 1\n      end\n      c1.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c1_received += 1\n        received += 1\n      end\n      c2.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c2_received += 1\n        received += 1\n      end\n\n      wait_on_routes_connected([c1, c2]) do\n        (1..to_send).each { c2.publish('foo', data) }\n        flush_routes([c1, c2]) { EM.stop }\n      end\n    end\n\n    expect(received).to eql(to_send*2) # queue subscriber + normal subscriber\n    expect(c1_received < to_send).to eql(true) \n    expect(c2_received < to_send).to eql(true)\n    expect(c1_received).to be_within(20).of(to_send/2)\n    expect(c2_received).to be_within(20).of(to_send/2)\n  end\n\n  it 'should properly route messages for distributed queues with multiple groups on different servers' do\n    data = 'Hello World!'\n    to_send = 100\n    received = c1a_received = c2a_received = 0\n    c1b_received = c2b_received = 0\n\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n\n      c1.subscribe('foo') do |msg|\n        expect(msg).to eql(data)\n        received += 1\n      end\n      c1.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c1a_received += 1\n        received += 1\n      end\n      c1.subscribe('foo', :queue => 'baz') do |msg|\n        expect(msg).to eql(data)\n        c1b_received += 1\n        received += 1\n      end\n\n      c2.subscribe('foo', :queue => 'bar') do |msg|\n        expect(msg).to eql(data)\n        c2a_received += 1\n        received += 1\n      end\n\n      c2.subscribe('foo', :queue => 'baz') do |msg|\n        expect(msg).to eql(data)\n        c2b_received += 1\n        received += 1\n      end\n\n      wait_on_routes_connected([c1, c2]) do\n        (1..to_send).each { c2.publish('foo', data) }\n        (1..to_send).each { c1.publish('foo', data) }\n        flush_routes([c1, c2]) { EM.stop }\n      end\n    end\n\n    expect(received).to eql(to_send*6) # 2 queue subscribers + normal subscriber * 2 pub loops\n    expect(c1a_received).to be_within(25).of(to_send)\n    expect(c2a_received).to be_within(25).of(to_send)\n    expect(c1b_received).to be_within(25).of(to_send)\n    expect(c2b_received).to be_within(25).of(to_send)\n  end\n\n  it 'should properly route messages for distributed queues with reply subjects on different servers' do\n    data = 'Hello World!'\n    to_send = 100\n    received = c1_received = c2_received = 0\n    EM.run do\n      c1 = NATS.connect(:uri => @s1.uri)\n      c2 = NATS.connect(:uri => @s2.uri)\n\n      c1.subscribe('foo', :queue => 'reply_test') do |msg|\n        expect(msg).to eql(data)\n        c1_received += 1\n        received += 1\n      end\n      c2.subscribe('foo', :queue => 'reply_test') do |msg|\n        expect(msg).to eql(data)\n        c2_received += 1\n        received += 1\n      end\n      wait_on_routes_connected([c1, c2]) do\n        (1..to_send).each { c2.publish('foo', data, 'bar') }\n        flush_routes([c1, c2]) { EM.stop }\n      end\n    end\n\n    expect(received).to eql(to_send)\n    expect(c1_received < to_send).to eql(true)\n    expect(c2_received < to_send).to eql(true)\n    expect(c1_received).to be_within(25).of(to_send/2)\n    expect(c2_received).to be_within(25).of(to_send/2)\n  end\nend\n"
  },
  {
    "path": "spec/client/error_on_client_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - error on client' do\n\n  context 'NATS::ServerError' do\n\n    it 'should show the contents of $1 when matched UNKNOWN' do\n      EchoServer.start {\n        expect(EM.reactor_running?).to eql(true)\n        NATS.on_error{ |e|\n          # We use a CONNECT request to match Unknown Protocol\n          expect(e.to_s).to match(/\\AUnknown Protocol: CONNECT.+/i)\n\n          NATS.stop\n          EchoServer.stop\n        }\n\n        # Send CONNECT request to the server.\n        NATS.start(:uri => EchoServer::URI)\n      }\n\n      expect(NATS.connected?).to be_falsey\n      expect(EM.reactor_running?).to be_falsey\n    end\n\n    it 'should disconnect when pings outstanding over limit' do\n      nc = nil\n      errors = []\n      closes = 0\n\n      SilentServer.start {\n        expect(EM.reactor_running?).to eql(true)\n\n        NATS.on_error do |e|\n          errors << e\n          NATS.stop\n          EchoServer.stop\n        end\n\n        NATS.on_close do\n          closes += 1\n        end\n\n        nc = NATS.connect({\n          :servers => [SilentServer::URI],\n          :max_outstanding_pings => 2,\n          :ping_interval => 1,\n          :reconnect => false\n        })\n        expect(nc).to receive(:queue_server_rt).exactly(2).times\n      }\n\n      expect(nc.connected?).to be_falsey\n      expect(EM.reactor_running?).to be_falsey\n      expect(errors.first).to be_a(NATS::ConnectError)\n      expect(closes).to eql(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/fast_producer_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - fast producer' do\n\n  before(:all) do\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n  end\n\n  it 'should report the maximum outbound threshold' do\n    expect(NATS::FAST_PRODUCER_THRESHOLD).to_not eql(nil)\n    expect(NATS::FAST_PRODUCER_THRESHOLD).to eql(10*1024*1024)\n  end\n\n  it 'should report the outstanding bytes pending' do\n\n    data = 'hello world!'\n    proto = \"PUB foo  #{data.size}\\r\\n#{data}\\r\\n\"\n    NATS.start do\n      (1..100).each { NATS.publish('foo', data) }\n      expect(NATS.pending_data_size).to eql(100*proto.size)\n      NATS.stop\n    end\n  end\n\n  it 'should not raise an error when exceeding the threshold by default' do\n    data = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n    EM.run do\n      nc = NATS.connect\n      expect do\n        # Put as many commands in pending as we can and confirm that\n        # hitting the maximum threshold does not yield an error.\n        while (nc.pending_data_size <= NATS::FAST_PRODUCER_THRESHOLD) do\n          nc.publish('foo', data)\n        end\n      end.not_to raise_error\n      nc.close\n      EM.stop\n    end\n  end\n\n  it 'should raise an error when exceeding the threshold if enabled' do\n    data = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n    EM.run do\n      c = NATS.connect(:fast_producer_error => true)\n      expect do\n        while (c.pending_data_size <= NATS::FAST_PRODUCER_THRESHOLD) do\n          c.publish('foo', data)\n        end\n      end.to raise_error(NATS::ClientError)\n      c.close\n      EM.stop\n    end\n  end\n\n  context 'with NATS_FAST_PRODUCER enabled from ENV' do\n    before(:all) do\n      ENV['NATS_FAST_PRODUCER'] = 'true'\n    end\n\n    after(:all) do\n      ENV.delete 'NATS_FAST_PRODUCER'\n    end\n\n    it 'should raise an error when exceeding the threshold' do\n      data = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n      EM.run do\n        c = NATS.connect\n        expect do\n          while (c.pending_data_size <= NATS::FAST_PRODUCER_THRESHOLD) do\n            c.publish('foo', data)\n          end\n        end.to raise_error(NATS::ClientError)\n        c.close\n        EM.stop\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/nuid_spec.rb",
    "content": "# Copyright 2016-2018 The NATS Authors\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nrequire 'spec_helper'\n\ndescribe 'NUID' do\n  it \"should have a fixed length and be unique\" do\n    nuid = NATS::NUID.new\n    entries = []\n    total = 500_000\n    total.times do\n      entry = nuid.next\n      expect(entry.size).to eql(NATS::NUID::TOTAL_LENGTH)\n      entries << entry\n    end\n    entries.uniq!\n    expect(entries.count).to eql(total)\n  end\n\n  it \"should be unique after 1M entries\" do\n    total = 1_000_000\n    entries = []\n    nuid = NATS::NUID.new\n    total.times do\n      entries << nuid.next\n    end\n    entries.uniq!\n    expect(entries.count).to eql(total)\n  end\n\n  it \"should randomize the prefix after sequence is done\" do\n    nuid = NATS::NUID.new\n    seq_a = nuid.instance_variable_get('@seq')\n    inc_a = nuid.instance_variable_get('@inc')\n    a = nuid.next\n\n    seq_b = nuid.instance_variable_get('@seq')\n    inc_b = nuid.instance_variable_get('@inc')\n    expect(seq_a < seq_b).to eql(true)\n    expect(seq_b).to eql(seq_a + inc_a)\n    b = nuid.next\n\n    nuid.instance_variable_set('@seq', NATS::NUID::MAX_SEQ+1)\n    c = nuid.next\n    l = NATS::NUID::PREFIX_LENGTH\n    expect(a[0..l]).to eql(b[0..l])\n    expect(a[0..l]).to_not eql(c[0..l])\n  end\nend\n"
  },
  {
    "path": "spec/client/partial_message_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - partial message behavior' do\n  before do\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after do\n    @s.kill_server\n  end\n\n  it 'should not hold stale message data across a reconnect' do\n    got_message = false\n    expect do\n      with_em_timeout(5) do |future|\n        # First client connects, and will attempt to reconnects\n        c1 = NATS.connect(:uri => @s.uri, :reconnect_time_wait => 0.25)\n        wait_on_connections([c1]) do\n          c1.subscribe('subject') do |msg|\n            got_message = true\n            expect(msg).to eql('complete message')\n            future.resume\n          end\n\n          # Client receives partial message before server terminates.\n          c1.receive_data(\"MSG subject 2 32\\r\\nincomplete\")\n\n          # Server restarts, disconnecting the first client.\n          @s.kill_server\n          @s.start_server\n\n          # One more client connects and publishes a message.\n          NATS.connect(:uri => @s.uri) do |c2|\n            EM.add_timer(0.50) do\n              expect(c1.connected_server).to eql(@s.uri)              \n              expect(c2.connected_server).to eql(@s.uri)\n              c1.flush { c2.publish('subject', 'complete message') }\n            end\n          end\n        end\n      end\n    end.to_not raise_exception\n\n    expect(got_message).to eql(true)\n  end\nend\n"
  },
  {
    "path": "spec/client/queues_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe \"Client - queue group support\" do\n\n  before(:each) do\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after(:each) do\n    @s.kill_server\n  end\n\n  it \"should deliver a message to only one subscriber in a queue group\" do\n    received = 0\n    NATS.start do\n      s1 = NATS.subscribe('foo', :queue => 'g1') { received += 1 }\n      expect(s1).to_not eql(nil)\n      s2 = NATS.subscribe('foo', :queue => 'g1') { received += 1 }\n      expect(s2).to_not eql(nil)\n      NATS.publish('foo', 'hello') { NATS.stop }\n    end\n    expect(received).to eql(1)\n  end\n\n  it \"should allow queue receivers and normal receivers to work together\" do\n    received = 0\n    NATS.start do\n      (0...5).each { NATS.subscribe('foo', :queue => 'g1') { received += 1 } }\n      NATS.subscribe('foo') { received += 1 }\n      NATS.publish('foo', 'hello') { NATS.stop }\n    end\n    expect(received).to eql(2)\n  end\n\n  it \"should spread messages equally across multiple receivers\" do\n    TOTAL = 1000\n    NUM_SUBSCRIBERS = 10\n    AVG = TOTAL / NUM_SUBSCRIBERS\n    ALLOWED_V = TOTAL * 0.05\n    received = Hash.new(0)\n    total = 0\n    NATS.start do\n      (0...NUM_SUBSCRIBERS).each do |i|\n        NATS.subscribe('foo.bar', :queue => 'queue_group_1') do\n          received[i] = received[i] + 1\n          total += 1\n        end\n      end\n      (0...TOTAL).each { NATS.publish('foo.bar', 'ok') }\n      NATS.flush { NATS.stop }\n    end\n    received.each_value do |count|\n      expect((AVG - count).abs < ALLOWED_V).to eql(true)\n    end\n    expect(total).to eql(TOTAL)\n  end\n\n  it \"should deliver a message to only one subscriber in a queue group, regardless of wildcard subjects\" do\n    received = 0\n    NATS.start do\n      NATS.subscribe('foo.bar', :queue => 'g1') { received += 1 }\n      NATS.subscribe('foo.*', :queue => 'g1') { received += 1 }\n      NATS.subscribe('foo.>', :queue => 'g1') { received += 1 }\n      NATS.publish('foo.bar', 'hello') { NATS.stop }\n    end\n    expect(received).to eql(1)\n  end\n\n  it \"should deliver 1 message/group for each publish\" do\n    received_g1 = 0\n    received_g2 = 0\n    NATS.start do\n      NATS.subscribe('foo.bar', :queue => 'g1') { received_g1 += 1 }\n      5.times do\n        NATS.subscribe('foo.bar', :queue => 'g2') { received_g2 += 1 }\n      end\n\n      10.times do\n        NATS.publish('foo.bar', 'hello')\n      end\n      NATS.flush { NATS.stop }\n    end\n    expect(received_g1).to eql(10)\n    expect(received_g2).to eql(10)\n  end\n\n  it \"should re-establish queue groups on reconnect\" do\n    buckets = Hash.new(0)\n\n    num_to_send = 1000\n\n    reconnect_sid = false\n    reconnect_timer = nil\n\n    NATS.start(:reconnect_time_wait => 0.25) do |conn|\n\n      reconnect_sid = NATS.subscribe(\"reconnected_trigger\") do\n        NATS.publish(\"control\", \"reconnected\")\n        NATS.unsubscribe(reconnect_sid)\n        EM.cancel_timer(reconnect_timer)\n      end\n\n      2.times do |ii|\n        NATS.subscribe(\"test_queue\", :queue => \"test_queue\") do\n          buckets[ii] += 1\n          NATS.publish(\"control\", \"ack\")\n        end\n      end\n\n      # Don't hang indefinitely\n      EM.add_timer(30) { EM.stop }\n\n      total_acked = 0\n\n      # Ensure the queue subscribes have been processed\n      NATS.flush do\n        NATS.subscribe(\"control\") do |state|\n          case state\n          when \"start\"\n            @s.kill_server\n            @s.start_server\n            reconnect_timer = EM.add_periodic_timer(0.25) do\n              NATS.publish(\"reconnected_trigger\")\n            end\n          when \"reconnected\"\n            num_to_send.times { NATS.publish(\"test_queue\") }\n          when \"ack\"\n            total_acked +=1\n            NATS.stop if total_acked == num_to_send\n          else\n            puts \"Unexpected message: #{state}\"\n            NATS.stop\n          end\n        end\n      end\n\n      NATS.flush { NATS.publish(\"control\", \"start\") }\n    end\n\n    # Messages are distributed uniformly at random to queue subscribers. This\n    # forms a binomal distribution with a probability of success (receiving a\n    # message) of .5 in the two subscriber case. This verifies that the receive\n    # count of each subscriber is within 3 standard deviations of the mean.\n    # In theory, this means that the test will fail ~ .3% of the time.\n    buckets.values.each do |msg_count|\n      expect(msg_count).to be_within(150).of(500)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/reconnect_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - reconnect specification' do\n\n  before(:all) do\n    R_USER = 'derek'\n    R_PASS = 'mypassword'\n    R_TEST_AUTH_SERVER = \"nats://#{R_USER}:#{R_PASS}@127.0.0.1:9333\"\n    R_TEST_SERVER_PID = '/tmp/nats_reconnect_authorization.pid'\n    E_TEST_SERVER = \"nats://127.0.0.1:9666\"\n    E_TEST_SERVER_PID = '/tmp/nats_reconnect_exception_test.pid'\n\n    @as = NatsServerControl.new(R_TEST_AUTH_SERVER, R_TEST_SERVER_PID)\n    @as.start_server\n    @s = NatsServerControl.new\n    @s.start_server\n    @es = NatsServerControl.new(E_TEST_SERVER, E_TEST_SERVER_PID)\n    @es.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n    @as.kill_server\n    @es.kill_server\n  end\n\n  it 'should properly report connected after connect callback' do\n    NATS.start do\n      expect(NATS.connected?).to eql(true)\n      expect(NATS.reconnecting?).to eql(false)\n      NATS.stop\n    end\n  end\n\n  it 'should do publish without error even if reconnected to an authorized server' do\n    NATS.start(:uri => R_TEST_AUTH_SERVER, :reconnect_time_wait => 0.25) do |c|\n      c.on_reconnect do\n        expect do\n          NATS.publish('reconnect test')\n        end.to_not raise_error\n      end\n      @as.kill_server\n      EM.add_timer(0.25) { @as.start_server }\n      EM.add_timer(1.0) { NATS.stop }\n    end\n  end\n\n  it 'should subscribe if reconnected' do\n    received = false\n    @as.kill_server\n\n    EM.run do\n      c = NATS.connect(:uri => R_TEST_AUTH_SERVER, :reconnect => true, :max_reconnect_attempts => -1, :reconnect_time_wait => 0.25)\n\n      c.subscribe('foo') { |msg|\n        received = true\n      }\n\n      @as.start_server\n\n      EM.add_periodic_timer(0.1) {\n        c.publish('foo', 'xxx')\n      }\n\n      EM.add_timer(1) {\n        NATS.stop\n        EM.stop\n      }\n    end\n\n    expect(received).to eql(true)\n  end\n\n  it 'should not get stuck reconnecting due to uncaught exceptions' do\n    received = false\n    @as.kill_server\n\n    class SomeException < StandardError; end\n\n    expect do\n      EM.run do\n        NATS.connect(:uri => R_TEST_AUTH_SERVER, :reconnect => true, :max_reconnect_attempts => -1, :reconnect_time_wait => 0.25)\n        raise SomeException.new\n      end\n    end.to raise_error(SomeException)\n  end\n\n  it 'should back off trying to reconnect' do\n    @as.start_server\n\n    disconnected_time = nil\n    reconnected_time = nil\n    connected_once = false\n    EM.run do\n      NATS.on_disconnect do\n        # Capture the time of the first disconnect\n        disconnected_time ||= NATS::MonotonicTime.now\n      end\n\n      NATS.on_reconnect do\n        reconnected_time ||= NATS::MonotonicTime.now\n      end\n\n      NATS.connect(:uri => R_TEST_AUTH_SERVER, :reconnect => true, :max_reconnect_attempts => -1, :reconnect_time_wait => 2) do\n        connected_once = true\n      end\n\n      EM.add_timer(0.5) do\n        @as.kill_server\n      end\n\n      EM.add_timer(1) do\n        @as.start_server\n      end\n\n      EM.add_timer(3) do\n        NATS.stop\n        EM.stop\n      end\n    end\n\n    expect(connected_once).to eql(true)\n    expect(disconnected_time).to_not be(nil)\n    expect(reconnected_time).to_not be(nil)\n    expect(reconnected_time - disconnected_time >= 2).to eql(true)\n  end\nend\n"
  },
  {
    "path": "spec/client/server_info_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - server_info support' do\n\n  before(:all) do\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n  end\n\n  it 'should report nil when not connected for server_info' do\n    expect(NATS.connected?).to eql(false)\n    expect(NATS.server_info).to eql(nil)\n  end\n\n  it 'should report the appropriate server_info for connected clients' do\n    NATS.start do\n      info = NATS.server_info\n      expect(info).to_not eql(nil)\n      expect(info).to be_a Hash\n      expect(info).to have_key :server_id\n      expect(info).to have_key :version\n      expect(info).to have_key :proto\n      expect(info).to have_key :client_id\n      expect(info).to have_key :max_payload\n      expect(info).to have_key :host\n      expect(info).to have_key :port\n      NATS.stop\n    end\n  end\n\n  it 'should report server info for individual connections' do\n    NATS.start do\n      info = NATS.client.server_info\n      expect(info).to_not be(nil)\n      expect(info).to be_a Hash\n      expect(info).to have_key :server_id\n      expect(info).to have_key :version\n      expect(info).to have_key :proto\n      expect(info).to have_key :client_id\n      expect(info).to have_key :max_payload\n      expect(info).to have_key :host\n      expect(info).to have_key :port\n      NATS.stop\n    end\n  end\nend\n"
  },
  {
    "path": "spec/client/sub_timeouts_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Client - subscriptions with timeouts' do\n\n  before(:all) do\n    TIMEOUT  = 0.1\n    WAIT     = 0.2\n    @s = NatsServerControl.new\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n  end\n\n  it \"a subscription should not receive a message after a timeout\" do\n    received = 0\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT)\n      EM.add_timer(WAIT) { NATS.publish('foo') { NATS.stop } }\n    end\n    expect(received).to eql(0)\n  end\n\n  it \"a subscription should call the timeout callback if no messages are received\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT) { timeout_recvd = true }\n      EM.add_timer(WAIT) { NATS.stop }\n    end\n    expect(timeout_recvd).to eql(true)\n    expect(received).to eql(0)\n  end\n\n  it \"a subscription should call the timeout callback if no messages are received, connection version\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do |c|\n      sid = c.subscribe('foo') { received += 1 }\n      c.timeout(sid, TIMEOUT) { timeout_recvd = true }\n      EM.add_timer(WAIT) { NATS.stop }\n    end\n    expect(timeout_recvd).to eql(true)\n    expect(received).to eql(0)\n  end\n\n  it \"a subscription should not call the timeout callback if a message is received\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT) { timeout_recvd = true }\n      NATS.publish('foo')\n      NATS.publish('foo')\n      EM.add_timer(WAIT) { NATS.stop }\n    end\n    expect(timeout_recvd).to eql(false)\n    expect(received).to eql(2)\n  end\n\n  it \"a subscription should not call the timeout callback if a correct # messages are received\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT, :expected => 2) { timeout_recvd = true }\n      NATS.publish('foo')\n      NATS.publish('foo')\n      EM.add_timer(WAIT) { NATS.stop }\n    end\n    expect(timeout_recvd).to eql(false)\n    expect(received).to eql(2)\n  end\n\n  it \"a subscription should call the timeout callback if a correct # messages are not received\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT, :expected => 2) { timeout_recvd = true }\n      NATS.publish('foo')\n      EM.add_timer(WAIT) { NATS.publish('foo') { NATS.stop} }\n    end\n    expect(timeout_recvd).to eql(true)\n    expect(received).to eql(1)\n  end\n\n  it \"a subscription should call the timeout callback and message callback if requested\" do\n    received = 0\n    timeout_recvd = false\n    NATS.start do\n      sid = NATS.subscribe('foo') { received += 1 }\n      NATS.timeout(sid, TIMEOUT, :auto_unsubscribe => false) { timeout_recvd = true }\n      EM.add_timer(WAIT) { NATS.publish('foo') { NATS.stop} }\n    end\n    expect(timeout_recvd).to eql(true)\n    expect(received).to eql(1)\n  end\nend\n"
  },
  {
    "path": "spec/configs/certs/bad-ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIGjzCCBHegAwIBAgIJAKT2W9SKY7o4MA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD\nVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR\nBgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv\nY2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy\nMzA2MTdaFw0xOTExMDQyMzA2MTdaMIGLMQswCQYDVQQGEwJVUzELMAkGA1UECBMC\nQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkFwY2VyYSBJbmMx\nEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxvY2FsaG9zdDEcMBoGCSqGSIb3\nDQEJARYNZGVyZWtAbmF0cy5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBAJOyBvFaREbmO/yaw8UD8u5vSk+Qrwdkfa0iHMo11nkcVtynHNKcgRUTkZBC\nxEZILVsuPa+WSUcUc0ej0TmuimrtOjXGn+LD0TrDVz6dd6lBufLXjo1fbUnKUjml\nTBYB2h7StDksrBPFnbEOVKN+qb1No4YxfvbJ6EK3xfnsm3dvamnetJugrmQ2EUlu\nglPNZDIShu9Fcsiq2hjw+dJ2Erl8kx2/PE8nOdcDG9I4wAM71pw9L1dHGmMOnTsq\nopLDVkMNjeIgMPxj5aIhvS8Tcnj16ZNi4h10587vld8fIdz+OgTDFMNi91PgZQmX\n9puXraBGi5UEn0ly57IIY+aFkx74jPWgnVYz8w8G+W2GTFYQEVgHcPTJ4aIPjyRd\nm/cLelV34TMNCoTXmPIKVBkJY01t2awUYN0AcauhmD1L+ihY2lVk330lxQR11ZQ/\nrjSRpG6jzb6diVK5wpNjsRRt5zJgZr6BMp0LYwJESGjt0sF0zZxixvHu8EctVle4\nzX6NHDic7mf4Wvo4rfnUyCGr7Y3OxB2vakq1fDZ1Di9OzpW/k8i/TE+mPRI5GTZt\nlR+c8mBxdV595EKHDxj0gY7PCM3Pe35p3oScWtfbpesTX6a7IL801ZwKKtN+4DOV\nmZhwiefztb/9IFPNXiuQnNh7mf7W2ob7SiGYct8iCLLjT64DAgMBAAGjgfMwgfAw\nHQYDVR0OBBYEFPDMEiYb7Np2STbm8j9qNj1aAvz2MIHABgNVHSMEgbgwgbWAFPDM\nEiYb7Np2STbm8j9qNj1aAvz2oYGRpIGOMIGLMQswCQYDVQQGEwJVUzELMAkGA1UE\nCBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkFwY2VyYSBJ\nbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxvY2FsaG9zdDEcMBoGCSqG\nSIb3DQEJARYNZGVyZWtAbmF0cy5pb4IJAKT2W9SKY7o4MAwGA1UdEwQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAIkoO+svWiudydr4sQNv/XhDvH0GiWMjaI738fAB\nsGUKWXarXM9rsRtoQ78iwEBZmusEv0fmJ9hX275aZdduTJt4AnCBVptnSyMJS6K5\nRZF4ZQ3zqT3QOeWepLqszqRZHf+xNfl9JiXZc3pqNhoh1YXPubCgY+TY1XFSrL+u\nWmbs3n56Cede5+dKwMpT9SfQ7nL1pwKihx16vlBGTjjvJ0RE5Tx+0VRcDgbtIF52\npNlvjg9DL+UqP3S1WR0PcsUss/ygiC1NDegZr+I/04/wEG9Drwk1yPSshWsH90W0\n7TmLDoWf5caAX62jOJtXbsA9JZ16RnIWy2iZYwg4YdE0rEeMbnDzrRucbyBahMX0\nmKc8C+rroW0TRTrqxYDQTE5gmAghCa9EixcwSTgMH/U6zsRbbY62m9WA5fKfu3n0\nz82+c36ijScHLgppTVosq+kkr/YE84ct56RMsg9esEKTxGxje812OSdHp/i2RzqW\nJ59yo7KUn1nX7HsFvBVh9D8147J5BxtPztc0GtCQTXFT73nQapJjAd5J+AC5AB4t\nShE+MRD+XIlPB/aMgtzz9Th8UCktVKoPOpFMC0SvFbbINWL/JO1QGhuZLMTKLjQN\nQBzjrETAOA9PICpI5hcPtTXz172X+I8/tIEFrZfew0Fdt/oAVcnb659zKiR8EuAq\n+Svp\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/configs/certs/ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkQCCQDI2Vsry8+BDDANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExEDAOBgNVBAoMB1N5bmFkaWExEDAOBgNVBAsMB25hdHMu\naW8xEjAQBgNVBAMMCWxvY2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0\ncy5pbzAeFw0xOTEwMTcxMzAzNThaFw0yOTEwMTQxMzAzNThaMHAxCzAJBgNVBAYT\nAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHU3luYWRpYTEQMA4GA1UECwwHbmF0\ncy5pbzESMBAGA1UEAwwJbG9jYWxob3N0MRwwGgYJKoZIhvcNAQkBFg1kZXJla0Bu\nYXRzLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAohX2dXdHIDM5\nyZDWk96b0mwRTHhBIOKtMPTTs/zKmlAgjjDxW7kSg0JimTNds9YbJ33FhcEJKXtV\nKH3Cn0uyZPS1VcTzPr7XP2QI+9SqqLuahkHAhgqoRwK62fTFJgzdZO0f9w9WwzMi\ngGk/v7KkKFa/9xKLCa9DTEJ9FA34HuYoBxXMZvypDm8d+0kxOCdThpzhKeucE4ya\njFlvOP9/l7GyjlczzAD/nt/QhPfSeIx1MF0ICj5qzwPD/jB1ekoL9OShoHvoEyXo\nUO13GMdVmZqwJcS7Vk5XNEZoH0cxSw/SrZGCE9SFjR1t8TAe3QZiZ9E8EAg4IzJQ\njfR2II5LiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBIwib+0xLth/1+URtgQFn8\ndvQNqnJjlqC27U48qiTCTC5vJWbQDqUg9o6gtwZyYEHQ7dMmn68ozDzcGTCxaikV\nn01Bj2ijODK96Jrm/P5aVkP5Cn06FfudluZI2Q/A1cqTsa8V4rj02PpwCcLEaDqX\nyhztlhbKypWrlGuWpVlDBWstyRar98vvRK1XEyBu2NHp2fy49cwJCub4Cmz920fh\noiIwzXIKtfnf1GEjUnsuFPMgCxvhjirYNPWWjqaBldrM/dBJqwTyZf/p6g40vufN\nJJDc65c4tyRwBSBdFn+Q4zD44M0AR/8THAeIfsT42lyl8fMV5A4fe1nAVJDC4Z/H\n-----END CERTIFICATE-----"
  },
  {
    "path": "spec/configs/certs/client-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDQjCCAiqgAwIBAgIJAJCSLX9jr5WzMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHU3luYWRpYTEQMA4GA1UECwwH\nbmF0cy5pbzESMBAGA1UEAwwJbG9jYWxob3N0MRwwGgYJKoZIhvcNAQkBFg1kZXJl\na0BuYXRzLmlvMB4XDTE5MTAxNzEzMjI0MloXDTI5MTAxNDEzMjI0MlowDTELMAkG\nA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsnD6dO3oS\nVoV4yt+c/Ax+XvJPIjNGgThT16clj9fuFhPiZ0mI9pSZ8Kmm2/56F8nj3zFzcThw\nOpYemXtdB+Nj5Oi/mfc9XCf1tzcp2u6CgADUyNMbNg2L04qbjhKhTQzFIvhWO2oa\n++k9CB4Tf1VuLmWTmpBUA20N5kTW98DX2OHHHsKbo26I8XxYCKKfE8xbuREsHSNv\nOq5Hmg9qzuWANAnm4/12Ss9aGLucxcF0SWd3G7oohjGm/BKvSoUbc1v01kL/DBxJ\n5zHyWioezYfLIv9wHEjtuuC+8Lye4NxZ26V0JVizYQT2MyhrByVgD3KTFmyfsK1K\nGPeeKR63YTQXAgMBAAGjQjBAMCkGA1UdEQQiMCCCCWxvY2FsaG9zdIcEfwAAAYEN\nZGVyZWtAbmF0cy5pbzATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUF\nAAOCAQEAfGUnzmpXXigAJxcnVKQy1ago+GGOAGldlDKIcHoofkYibhWWrrojulHF\npRPRVKm2S/P4rRnSsjrPfpf6I2Icd+oVdVxrsWcN5itbul8Xymsjl2gMSJSHknYs\nwTYNjdM4opRioArK69aRa26xXlxRs8YpRErF8Nb5mkxgvtUgtM8t/T/28MBprc7x\n7NuYvohKlOcWbgdBYI+e3CA2XLRG/A+9EmOe8g66vW/uY0eaiWduBJSwXhd+stjg\nelXYnK+EEUpJIK9DeS7r6k6HreNZ2FPM90RxdbMP7Q+i3bJwic4cJG3QOdLl+IqK\ntME8kUPD/63mEDHHMJjgAktgaFX4bQ==\n-----END CERTIFICATE-----"
  },
  {
    "path": "spec/configs/certs/client-key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCsnD6dO3oSVoV4\nyt+c/Ax+XvJPIjNGgThT16clj9fuFhPiZ0mI9pSZ8Kmm2/56F8nj3zFzcThwOpYe\nmXtdB+Nj5Oi/mfc9XCf1tzcp2u6CgADUyNMbNg2L04qbjhKhTQzFIvhWO2oa++k9\nCB4Tf1VuLmWTmpBUA20N5kTW98DX2OHHHsKbo26I8XxYCKKfE8xbuREsHSNvOq5H\nmg9qzuWANAnm4/12Ss9aGLucxcF0SWd3G7oohjGm/BKvSoUbc1v01kL/DBxJ5zHy\nWioezYfLIv9wHEjtuuC+8Lye4NxZ26V0JVizYQT2MyhrByVgD3KTFmyfsK1KGPee\nKR63YTQXAgMBAAECggEBAKc6FHt2NPTxOAxn2C6aDmycBftesfiblnu8EWaVrmgu\noYMV+CsmYZ+mhmZu+mNFCsam5JzoUvp/+BKbNeZSjx2nl0qRmvOqhdhLcbkuLybl\nZmjAS64wNv2Bq+a6xRfaswWGtLuugkS0TCph4+mV0qmVb7mJ5ExQqWXu8kCl9QHn\nuKacp1wVFok9rmEI+byL1+Z01feKrkf/hcF6dk62U7zHNPajViJFTDww7hiHyfUH\n6qsxIe1UWSNKtE61haEHkzqbDIDAy79jX4t3JobLToeVNCbJ7BSPf2IQSPJxELVL\nsidIJhndEjsbDR2CLpIF/EjsiSIaP7jh2zC9fxFpgSkCgYEA1qH0PH1JD5FqRV/p\nn9COYa6EifvSymGo4u/2FHgtX7wNSIQvqAVXenrQs41mz9E65womeqFXT/AZglaM\n1PEjjwcFlDuLvUEYYJNgdXrIC515ZXS6TdvJ0JpQJLx28GzZ7h31tZXfwn68C3/i\nUGEHp+nN1BfBBQnsqvmGFFvHZFUCgYEAzeDlZHHijBlgHU+kGzKm7atJfAGsrv6/\ntw7CIMEsL+z/y7pl3nwDLdZF+mLIvGuKlwIRajEzbYcEuVymCyG2/SmPMQEUf6j+\nC1OmorX9CW8OwHmVCajkIgKn0ICFsF9iFv6aYZmm1kG48AIuYiQ7HOvY/MlilqFs\n1p8sw6ZpQrsCgYEAj7Z9fQs+omfxymYAXnwc+hcKtAGkENL3bIzULryRVSrrkgTA\njDaXbnFR0Qf7MWedkxnezfm+Js5TpkwhnGuiLaC8AZclaCFwGypTShZeYDifEmno\nXT2vkjfhNdfjo/Ser6vr3BxwaSDG9MQ6Wyu9HpeUtFD7c05D4++T8YnKpskCgYEA\npCkcoIAStcWSFy0m3K0B3+dBvAiVyh/FfNDeyEFf24Mt4CPsEIBwBH+j4ugbyeoy\nYwC6JCPBLyeHA8q1d5DVmX4m+Fs1HioBD8UOzRUyA/CzIZSQ21f5OIlHiIDCmQUl\ncNJpBUQAfT2AmpgSphzfqcsBhWeLHjLvVx8rEYLC0fsCgYAiHdPZ3C0f7rWZP93N\ngY4DuldiO4d+KVsWAdBxeNgPznisUI7/ZZ/9NvCxGvA5NynyZr0qlpiKzVvtFJG8\n1ZPUuFFRMAaWn9h5C+CwMPgk65tFC6lw/el0hpmcocSXVdiJEbkV0rnv9iGh0CYX\nHMACGrYlyZdDYM0CH/JAM+K/QQ==\n-----END PRIVATE KEY-----"
  },
  {
    "path": "spec/configs/certs/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDm+0dlzcmiLa+L\nzdVqeVQ8B1/rWnErK+VvvjH7FmVodg5Z5+RXyojpd9ZBrVd6QrLSVMQPfFvBvGGX\n4yI6Ph5KXUefa31vNOOMhp2FGSmaEVhETKGQ0xRh4VfaAerOP5Cunl0TbSyJyjkV\na7aeMtcqTEiFL7Ae2EtiMhTrMrYpBDQ8rzm2i1IyTb9DX5v7DUOmrSynQSlVyXCz\ntRVGNL/kHlItpEku1SHt/AD3ogu8EgqQZFB8xRRw9fubYgh4Q0kx80e4k9QtTKnc\nF3B2NGb/ZcE5Z+mmHIBq8J2zKMijOrdd3m5TbQmzDbETEOjs4L1eoZRLcL/cvYu5\ngmXdr4F7AgMBAAECggEBAK4sr3MiEbjcsHJAvXyzjwRRH1Bu+8VtLW7swe2vvrpd\nw4aiKXrV/BXpSsRtvPgxkXyvdMSkpuBZeFI7cVTwAJFc86RQPt77x9bwr5ltFwTZ\nrXCbRH3b3ZPNhByds3zhS+2Q92itu5cPyanQdn2mor9/lHPyOOGZgobCcynELL6R\nwRElkeDyf5ODuWEd7ADC5IFyZuwb3azNVexIK+0yqnMmv+QzEW3hsycFmFGAeB7v\nMIMjb2BhLrRr6Y5Nh+k58yM5DCf9h/OJhDpeXwLkxyK4BFg+aZffEbUX0wHDMR7f\n/nMv1g6cKvDWiLU8xLzez4t2qNIBNdxw5ZSLyQRRolECgYEA+ySTKrBAqI0Uwn8H\nsUFH95WhWUXryeRyGyQsnWAjZGF1+d67sSY2un2W6gfZrxRgiNLWEFq9AaUs0MuH\n6syF4Xwx/aZgU/gvsGtkgzuKw1bgvekT9pS/+opmHRCZyQAFEHj0IEpzyB6rW1u/\nLdlR3ShEENnmXilFv/uF/uXP5tMCgYEA63LiT0w46aGPA/E+aLRWU10c1eZ7KdhR\nc3En6zfgIxgFs8J38oLdkOR0CF6T53DSuvGR/OprVKdlnUhhDxBgT1oQjK2GlhPx\nJV5uMvarJDJxAwsF+7T4H2QtZ00BtEfpyp790+TlypSG1jo/BnSMmX2uEbV722lY\nhzINLY49obkCgYBEpN2YyG4T4+PtuXznxRkfogVk+kiVeVx68KtFJLbnw//UGT4i\nEHjbBmLOevDT+vTb0QzzkWmh3nzeYRM4aUiatjCPzP79VJPsW54whIDMHZ32KpPr\nTQMgPt3kSdpO5zN7KiRIAzGcXE2n/e7GYGUQ1uWr2XMu/4byD5SzdCscQwJ/Ymii\nLoKtRvk/zWYHr7uwWSeR5dVvpQ3E/XtONAImrIRd3cRqXfJUqTrTRKxDJXkCmyBc\n5FkWg0t0LUkTSDiQCJqcUDA3EINFR1kwthxja72pfpwc5Be/nV9BmuuUysVD8myB\nqw8A/KsXsHKn5QrRuVXOa5hvLEXbuqYw29mX6QKBgDGDzIzpR9uPtBCqzWJmc+IJ\nz4m/1NFlEz0N0QNwZ/TlhyT60ytJNcmW8qkgOSTHG7RDueEIzjQ8LKJYH7kXjfcF\n6AJczUG5PQo9cdJKo9JP3e1037P/58JpLcLe8xxQ4ce03zZpzhsxR2G/tz8DstJs\nb8jpnLyqfGrcV2feUtIZ\n-----END PRIVATE KEY-----"
  },
  {
    "path": "spec/configs/certs/multi-ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDSTCCAjGgAwIBAgIQDbspQLZaH4pjQlTk/trkBzANBgkqhkiG9w0BAQsFADBO\nMQwwCgYDVQQGEwNVU0ExFjAUBgNVBAoTDUNsb3VkIEZvdW5kcnkxJjAkBgNVBAMT\nHWRlZmF1bHQubmF0cy1jYS5ib3NoLWludGVybmFsMB4XDTE4MDcyMDE0NTg1NFoX\nDTE5MDcyMDE0NTg1NFowTjEMMAoGA1UEBhMDVVNBMRYwFAYDVQQKEw1DbG91ZCBG\nb3VuZHJ5MSYwJAYDVQQDEx1kZWZhdWx0Lm5hdHMtY2EuYm9zaC1pbnRlcm5hbDCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCtx6M8UOqd7X2XxVnuap4B\nZK2kHVG3xi7hmJzCsPm2JW29mmF2G62SBlZ+LmvnrOhU9gm0yzYE60jn3T2du7qe\nFUWJhI0VMGFwLJIgDXeroknuH5jrzRJHTZ5bXgDmY7e55gYBgxJ0HOD5kFV9f+iG\n3yt9mHbyEN9EEN/d0AlY7a+9Pid/mGaifWtYLAMhT+13DGS8kVhkWaCEVPFL7rfQ\n3DJ/mvgfKAGAdx9eNvAS+0sGnFkCvxXbhavBUk2krAf+Fo6WtxwxLb/rXvWRt4n7\nzWLbKEXO4y+csBis6DaLZ25jqkV623kxrbLz1rKtA00GVO5f9Y+71v8vBkyweD8C\nAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\nhvcNAQELBQADggEBAJ+3nPKz9VrS57Gxbrzocam4FqNokCxmk+9BaNeIWx5p8Vpb\nVhq6Hh44rljWExEMdgISSQeN3vJrcWUe2u0NbdLCVPtn+D02cVYD9gGHwuRH5Nd6\nDEzKLW5BqFBHxgcwpc97tEfOey+CduNRZbKbrZCYpLud+9NZR36xc0g4zno53HyG\nEBpywVUP0MKchQshl3Wjv50aoqThpaXWnVGd5hOrGWipWt8FUR6gkqYGfJh+sV4L\n5QbsrD5B2AM3BPcyFsUlsfFKPHKR3WMZThjZ3GklRc8niYo1N9Rp6wo4rMpz6Jjc\nQAQiG4TMMMJs8jw8/fU8P4bt0K1TOz7Cmr6xa7c=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDXDCCAkQCCQDI2Vsry8+BDDANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExEDAOBgNVBAoMB1N5bmFkaWExEDAOBgNVBAsMB25hdHMu\naW8xEjAQBgNVBAMMCWxvY2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0\ncy5pbzAeFw0xOTEwMTcxMzAzNThaFw0yOTEwMTQxMzAzNThaMHAxCzAJBgNVBAYT\nAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHU3luYWRpYTEQMA4GA1UECwwHbmF0\ncy5pbzESMBAGA1UEAwwJbG9jYWxob3N0MRwwGgYJKoZIhvcNAQkBFg1kZXJla0Bu\nYXRzLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAohX2dXdHIDM5\nyZDWk96b0mwRTHhBIOKtMPTTs/zKmlAgjjDxW7kSg0JimTNds9YbJ33FhcEJKXtV\nKH3Cn0uyZPS1VcTzPr7XP2QI+9SqqLuahkHAhgqoRwK62fTFJgzdZO0f9w9WwzMi\ngGk/v7KkKFa/9xKLCa9DTEJ9FA34HuYoBxXMZvypDm8d+0kxOCdThpzhKeucE4ya\njFlvOP9/l7GyjlczzAD/nt/QhPfSeIx1MF0ICj5qzwPD/jB1ekoL9OShoHvoEyXo\nUO13GMdVmZqwJcS7Vk5XNEZoH0cxSw/SrZGCE9SFjR1t8TAe3QZiZ9E8EAg4IzJQ\njfR2II5LiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBIwib+0xLth/1+URtgQFn8\ndvQNqnJjlqC27U48qiTCTC5vJWbQDqUg9o6gtwZyYEHQ7dMmn68ozDzcGTCxaikV\nn01Bj2ijODK96Jrm/P5aVkP5Cn06FfudluZI2Q/A1cqTsa8V4rj02PpwCcLEaDqX\nyhztlhbKypWrlGuWpVlDBWstyRar98vvRK1XEyBu2NHp2fy49cwJCub4Cmz920fh\noiIwzXIKtfnf1GEjUnsuFPMgCxvhjirYNPWWjqaBldrM/dBJqwTyZf/p6g40vufN\nJJDc65c4tyRwBSBdFn+Q4zD44M0AR/8THAeIfsT42lyl8fMV5A4fe1nAVJDC4Z/H\n-----END CERTIFICATE-----"
  },
  {
    "path": "spec/configs/certs/server.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDPTCCAiWgAwIBAgIJAJCSLX9jr5W7MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHU3luYWRpYTEQMA4GA1UECwwH\nbmF0cy5pbzESMBAGA1UEAwwJbG9jYWxob3N0MRwwGgYJKoZIhvcNAQkBFg1kZXJl\na0BuYXRzLmlvMB4XDTE5MTAxNzEzNTcyNloXDTI5MTAxNDEzNTcyNlowDTELMAkG\nA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDm+0dlzcmi\nLa+LzdVqeVQ8B1/rWnErK+VvvjH7FmVodg5Z5+RXyojpd9ZBrVd6QrLSVMQPfFvB\nvGGX4yI6Ph5KXUefa31vNOOMhp2FGSmaEVhETKGQ0xRh4VfaAerOP5Cunl0TbSyJ\nyjkVa7aeMtcqTEiFL7Ae2EtiMhTrMrYpBDQ8rzm2i1IyTb9DX5v7DUOmrSynQSlV\nyXCztRVGNL/kHlItpEku1SHt/AD3ogu8EgqQZFB8xRRw9fubYgh4Q0kx80e4k9Qt\nTKncF3B2NGb/ZcE5Z+mmHIBq8J2zKMijOrdd3m5TbQmzDbETEOjs4L1eoZRLcL/c\nvYu5gmXdr4F7AgMBAAGjPTA7MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAd\nBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADggEB\nADQYaEjWlOb9YzUnFGjfDC06dRZjRmK8TW/4GiDHIDk5TyZ1ROtskvyhVyTZJ5Vs\nqXOKJwpps0jK2edtrvZ7xIGw+Y41oPgYYhr5TK2c+oi2UOHG4BXqRbuwz/5cU+nM\nZWOG1OrHBCbrMSeFsn7rzETnd8SZnw6ZE7LI62WstdoCY0lvNfjNv3kY/6hpPm+9\n0bVzurZ28pdJ6YEJYgbPcOvxSzGDXTw9LaKEmqknTsrBKI2qm+myVTbRTimojYTo\nrw/xjHESAue/HkpOwWnFTOiTT+V4hZnDXygiSy+LWKP4zLnYOtsn0lN9OmD0z+aa\ngpoVMSncu2jMIDZX63IkQII=\n-----END CERTIFICATE-----"
  },
  {
    "path": "spec/configs/nkeys/foo-user.creds",
    "content": "-----BEGIN NATS USER JWT-----\neyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXTURGT1dHV1JGWkRGRFVSM0dPUkdESEtUTTdDUlZBVDQ1RkRFMllNRUY1N0VOQ0JBVFFRIiwiaWF0IjoxNTUzODQwOTQ0LCJpc3MiOiJBRDdTRUFOUzZCQ0JGNkZISUI3U1EzVUdKVlBXNTNCWE9BTFA3NVlYSkJCWFFMN0VBRkI2TkpOQSIsIm5hbWUiOiJmb28tdXNlciIsInN1YiI6IlVDSzVON042Nk9CT0lORlhBWUMyQUNKUVlGU09ENFZZTlU2QVBFSlRBVkZaQjJTVkhMS0dFVzdMIiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.Vri09BN561m37GvuSWoGN9L9TSkwQbjC_jIv1BCJcoxZqNc_Pa7WbR12b3SAS4_Ip2D9-2HCwyYib1JUEIO8Bg\n------END NATS USER JWT------\n\n************************* IMPORTANT *************************\nNKEY Seed printed below can be used to sign and prove identity.\nNKEYs are sensitive and should be treated as secrets.\n\n-----BEGIN USER NKEY SEED-----\nSUAMLK2ZNL35WSMW37E7UD4VZ7ELPKW7DHC3BWBSD2GCZ7IUQQXZIORRBU\n------END USER NKEY SEED------\n\n*************************************************************\n"
  },
  {
    "path": "spec/configs/nkeys/foo-user.jwt",
    "content": "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXTURGT1dHV1JGWkRGRFVSM0dPUkdESEtUTTdDUlZBVDQ1RkRFMllNRUY1N0VOQ0JBVFFRIiwiaWF0IjoxNTUzODQwOTQ0LCJpc3MiOiJBRDdTRUFOUzZCQ0JGNkZISUI3U1EzVUdKVlBXNTNCWE9BTFA3NVlYSkJCWFFMN0VBRkI2TkpOQSIsIm5hbWUiOiJmb28tdXNlciIsInN1YiI6IlVDSzVON042Nk9CT0lORlhBWUMyQUNKUVlGU09ENFZZTlU2QVBFSlRBVkZaQjJTVkhMS0dFVzdMIiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.Vri09BN561m37GvuSWoGN9L9TSkwQbjC_jIv1BCJcoxZqNc_Pa7WbR12b3SAS4_Ip2D9-2HCwyYib1JUEIO8Bg"
  },
  {
    "path": "spec/configs/nkeys/foo-user.nk",
    "content": "SUAMLK2ZNL35WSMW37E7UD4VZ7ELPKW7DHC3BWBSD2GCZ7IUQQXZIORRBU"
  },
  {
    "path": "spec/configs/nkeys/op.jwt",
    "content": "-----BEGIN TEST OPERATOR JWT-----\neyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiIyTVhXQ0VaRFo2S0g3NU5ZU1NaUFBTTElET1NEVzdMVVE0RVRUSjJMWEhKQlFGWTRTMkZBIiwiaWF0IjoxNTUzODQwMzM3LCJpc3MiOiJPRFdJSUU3SjdOT1M3M1dWQk5WWTdIQ1dYVTRXWFdEQlNDVjRWSUtNNVk0TFhUT1Q1U1FQT0xXTCIsIm5hbWUiOiJ0ZXN0X29wZXJhdG9yIiwic3ViIjoiT0RXSUlFN0o3Tk9TNzNXVkJOVlk3SENXWFU0V1hXREJTQ1Y0VklLTTVZNExYVE9UNVNRUE9MV0wiLCJ0eXBlIjoib3BlcmF0b3IifQ.V_v6k3aMOpyau83RaqNHW_YmZw8X0ZnJWLOas3YvQYIyXrHF0bL9inBaQw6zXzbN_ViQnNskhB7tM40qguitAg\n------END TEST OPERATOR JWT------\n"
  },
  {
    "path": "spec/configs/tls-no-auth.conf",
    "content": "\n# Simple TLS config file\n\nport: 4444\nnet: \"127.0.0.1\"\n\ntls {\n  cert_file:  \"./spec/configs/certs/server.pem\"\n  key_file:   \"./spec/configs/certs/key.pem\"\n  timeout:    5\n}\n"
  },
  {
    "path": "spec/configs/tls.conf",
    "content": "\n# Simple TLS config file\n\nport: 4443\nnet: \"127.0.0.1\"\n\ntls {\n  cert_file:  \"./spec/configs/certs/server.pem\"\n  key_file:   \"./spec/configs/certs/key.pem\"\n  timeout:    5\n}\n\nauthorization {\n  user:     secret\n  password: deadbeef\n  timeout:  5\n}\n"
  },
  {
    "path": "spec/configs/tlsverify.conf",
    "content": "\n# Simple TLS config file\n\nport: 4445\nnet: \"127.0.0.1\"\n\ntls {\n  cert_file:  \"./spec/configs/certs/server.pem\"\n  key_file:   \"./spec/configs/certs/key.pem\"\n  timeout:    5\n\n  # Optional certificate authority for clients\n  ca_file:   \"./spec/configs/certs/ca.pem\"\n  \n  # Require a client certificate\n  verify:    true\n}\n"
  },
  {
    "path": "spec/server/max_connections_spec.rb",
    "content": "require 'spec_helper'\nrequire 'yaml'\n\ndescribe 'Server - max connections support' do\n\n  before (:all) do\n    MC_SERVER_PID = '/tmp/nats_mc_pid.pid'\n    MC_SERVER = 'nats://localhost:9272'\n    MC_LOG_FILE = '/tmp/nats_mc_test.log'\n    MC_CONFIG = File.dirname(__FILE__) + '/resources/max_connections.yml'\n    MC_FLAGS = \"-c #{MC_CONFIG}\"\n\n    FileUtils.rm_f(MC_LOG_FILE)\n    @s = RubyNatsServerControl.new(MC_SERVER, MC_SERVER_PID, MC_FLAGS)\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n    NATS.server_running?(MC_SERVER).should be_falsey\n    FileUtils.rm_f(MC_LOG_FILE)\n  end\n\n  it 'should not allow connections above the maximum allowed' do\n    config = File.open(MC_CONFIG) { |f| YAML.load(f) }\n    max = config['max_connections']\n\n    err_received = false\n    conns = []\n\n    EM.run do\n      (1..max).each { conns << NATS.connect({:uri => MC_SERVER}) }\n      wait_on_connections(conns) do\n        c = NATS.connect({:uri => MC_SERVER})\n        EM.add_timer(0.25) { NATS.stop }\n        c.on_error do |err|\n          err_received = true\n          err.should be_an_instance_of NATS::ServerError\n          c.close\n          conns.each { |c| c.close }\n          EM.stop\n        end\n      end\n    end\n    err_received.should be_truthy\n  end\n\nend\n"
  },
  {
    "path": "spec/server/monitor_spec.rb",
    "content": "\nrequire 'spec_helper'\nrequire 'fileutils'\n\nrequire 'net/http'\nrequire 'uri'\n\nrequire 'nats/server/server'\nrequire 'nats/server/options'\nrequire 'nats/server/const'\nrequire 'nats/server/util'\n\ndescribe 'Server - monitor' do\n\n  before (:all) do\n    HTTP_SERVER_PID = '/tmp/nats_http.pid'\n    HTTP_SERVER     = 'nats://127.0.0.1:9229'\n    LOG_FILE        = '/tmp/nats_http.log'\n    HTTP_PORT       = 9230\n    HTTP_FLAGS      = \"-m #{HTTP_PORT} -l #{LOG_FILE}\"\n    @rs = RubyNatsServerControl.new(HTTP_SERVER, HTTP_SERVER_PID, HTTP_FLAGS)\n    @rs.start_server\n  end\n\n  after(:all) do\n    @rs.kill_server\n    FileUtils.rm_f(LOG_FILE)\n  end\n\n  it 'should process simple command line arguments for http port' do\n    NATSD::Server.process_options('-m 4222'.split)\n    opts = NATSD::Server.options\n    opts[:http_port].should == 4222\n  end\n\n  it 'should process long command line arguments for http port' do\n    NATSD::Server.process_options('--http 4222'.split)\n    opts = NATSD::Server.options\n    opts[:http_port].should == 4222\n  end\n\n  it 'should properly parse http port from config file' do\n    config_file = File.dirname(__FILE__) + '/resources/monitor.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:http_net].should == '127.0.0.1'\n    opts[:http_port].should == 4222\n    opts[:http_user].should == 'derek'\n    opts[:http_password].should == 'foo'\n  end\n\n  it 'should start monitor http servers when requested' do\n    total_wait, now = 0, Time.now\n    begin\n      s = TCPSocket.open(NATSD::Server.host, HTTP_PORT)\n    rescue Errno::ECONNREFUSED => e\n      total_wait = Time.now - now\n      now = Time.now\n      retry unless total_wait > 5\n    ensure\n      s.close if s\n    end\n  end\n\n  it 'should return \\'ok\\' for /healthz' do\n    host, port = NATSD::Server.host, HTTP_PORT\n    healthz_req = Net::HTTP::Get.new(\"/healthz\")\n    healthz_resp = Net::HTTP.new(host, port).start { |http| http.request(healthz_req) }\n    healthz_resp.body.should =~ /ok/i\n  end\n\n  it 'should return varz with proper members' do\n    host, port = NATSD::Server.host, HTTP_PORT\n    varz_req = Net::HTTP::Get.new(\"/varz\")\n    varz_resp = Net::HTTP.new(host, port).start { |http| http.request(varz_req) }\n    varz_resp.body.should_not be_nil\n    varz = JSON.parse(varz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n    varz.should be_an_instance_of Hash\n    varz.should have_key :start\n    varz.should have_key :options\n    varz.should have_key :mem\n    varz.should have_key :cpu\n    varz.should have_key :cores\n    varz.should have_key :connections\n    varz.should have_key :routes\n    varz.should have_key :in_msgs\n    varz.should have_key :in_bytes\n    varz.should have_key :out_msgs\n    varz.should have_key :out_bytes\n\n    # Check to make sure we pick up cores correctly\n    varz[:cores].should > 0\n  end\n\n  it 'should properly track number of connections' do\n    EM.run do\n      conns = []\n      (1..10).each { conns << NATS.connect(:uri => HTTP_SERVER) }\n      # Wait for them to register and varz to allow updates\n      wait_on_connections(conns) do\n        host, port = NATSD::Server.host, HTTP_PORT\n        varz_req = Net::HTTP::Get.new(\"/varz\")\n        varz_resp = Net::HTTP.new(host, port).start { |http| http.request(varz_req) }\n        varz_resp.body.should_not be_nil\n        varz = JSON.parse(varz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        varz[:connections].should == 10\n\n        conns.each { |c| c.close }\n        EM.stop\n      end\n    end\n  end\n\n  it 'should track inbound and outbound message counts and bytes' do\n    NATS.start(:uri => HTTP_SERVER) do\n      NATS.subscribe('foo')\n      NATS.subscribe('foo')\n      (1..11).each {  NATS.publish('foo', 'hello world') }\n      NATS.flush do\n        host, port = NATSD::Server.host, HTTP_PORT\n        varz_req = Net::HTTP::Get.new(\"/varz\")\n        varz_resp = Net::HTTP.new(host, port).start { |http| http.request(varz_req) }\n        varz_resp.body.should_not be_nil\n        varz = JSON.parse(varz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        varz[:in_msgs].should == 11\n        varz[:out_msgs].should == 22\n        varz[:in_bytes].should == 121\n        varz[:out_bytes].should == 242\n        NATS.stop\n      end\n    end\n  end\n\n  it 'should return connz with proper members' do\n    EM.run do\n      conns = []\n      (1..10).each { conns << NATS.connect(:uri => HTTP_SERVER) }\n      # Wait for them to register and connz to allow updates\n      wait_on_connections(conns) do\n        host, port = NATSD::Server.host, HTTP_PORT\n        connz_req = Net::HTTP::Get.new(\"/connz\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 10\n        connz[:connections].size.should == 10\n        c_info = connz[:connections].first\n        c_info.should have_key :cid\n        c_info.should have_key :ip\n        c_info.should have_key :port\n        c_info.should have_key :subscriptions\n        c_info.should have_key :pending_size\n        c_info.should have_key :in_msgs\n        c_info.should have_key :out_msgs\n        c_info.should have_key :in_bytes\n        c_info.should have_key :out_bytes\n\n        conns.each { |c| c.close }\n        EM.stop\n      end\n    end\n  end\n\n  it 'should return connz with subset of connections if requested' do\n    EM.run do\n      conns = []\n      (1..50).each { conns << NATS.connect(:uri => HTTP_SERVER) }\n      # Wait for them to register and connz to allow updates\n      wait_on_connections(conns) do\n        host, port = NATSD::Server.host, HTTP_PORT\n        connz_req = Net::HTTP::Get.new(\"/connz?n=11\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 50\n        connz[:connections].size.should == 11\n        c_info = connz[:connections].first\n        c_info.should have_key :cid\n        c_info.should have_key :ip\n        c_info.should have_key :port\n        c_info.should have_key :subscriptions\n        c_info.should have_key :pending_size\n        c_info.should have_key :in_msgs\n        c_info.should have_key :out_msgs\n        c_info.should have_key :in_bytes\n        c_info.should have_key :out_bytes\n\n        conns.each { |c| c.close }\n        EM.stop\n      end\n    end\n  end\n\n  it 'should return connz with subset of connections sorted correctly if requested' do\n    EM.run do\n      conns = []\n\n      (1..10).each do\n        conns << NATS.connect(:uri => HTTP_SERVER)\n      end\n      (1..4).each do\n        conns << c = NATS.connect(:uri => HTTP_SERVER)\n        c.subscribe('foo')\n        c.subscribe('foo')\n      end\n\n      conns << c = NATS.connect(:uri => HTTP_SERVER)\n      (1..10).each { c.publish('foo', \"hello world\") }\n\n      # Wait for them to register and connz to allow updates\n      wait_on_connections(conns) do\n        host, port = NATSD::Server.host, HTTP_PORT\n\n        # Test different sorts\n\n        # out_msgs\n        connz_req = Net::HTTP::Get.new(\"/connz?n=4&s=out_msgs\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 4\n        connz[:connections].each do |c_info|\n          c_info[:out_msgs].should == 20\n        end\n\n        # msgs_to\n        connz_req = Net::HTTP::Get.new(\"/connz?n=4&s=msgs_to\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 4\n        connz[:connections].each do |c_info|\n          c_info[:out_msgs].should == 20\n        end\n\n        # out_bytes\n        connz_req = Net::HTTP::Get.new(\"/connz?n=2&s=out_bytes\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 2\n        connz[:connections].each do |c_info|\n          c_info[:out_bytes].should == 220\n        end\n\n        # bytes_to\n        connz_req = Net::HTTP::Get.new(\"/connz?n=2&s=bytes_to\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 2\n        connz[:connections].each do |c_info|\n          c_info[:out_bytes].should == 220\n        end\n\n        # in_msgs\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=in_msgs\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:in_msgs].should == 10\n\n\n        # msgs_from\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=msgs_from\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:in_msgs].should == 10\n\n        # in_bytes\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=in_bytes\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:in_bytes].should == 110\n\n        # bytes_from\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=bytes_from\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:in_bytes].should == 110\n\n        # subscriptions (short form)\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=subs\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:subscriptions].should == 2\n\n        # subscriptions (long form)\n        connz_req = Net::HTTP::Get.new(\"/connz?n=1&s=subscriptions\")\n        connz_resp = Net::HTTP.new(host, port).start { |http| http.request(connz_req) }\n        connz_resp.body.should_not be_nil\n        connz = JSON.parse(connz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n        connz.should have_key :pending_size\n        connz.should have_key :num_connections\n        connz[:num_connections].should == 15\n        connz[:connections].size.should == 1\n        c_info = connz[:connections].first\n        c_info[:subscriptions].should == 2\n\n        conns.each { |c| c.close }\n        EM.stop\n      end\n\n    end\n  end\n\n  it 'should require auth if configured to do so' do\n    config_file = File.dirname(__FILE__) + '/resources/monitor.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    uri = \"nats://#{config['net']}:#{config['port']}\"\n\n    auth_s = RubyNatsServerControl.new(uri, config['pid_file'], \"-c #{config_file}\")\n    auth_s.start_server\n\n    host, port = config['http']['net'], config['http']['port']\n\n    begin\n      sleep(0.5)\n      s = TCPSocket.open(host, port)\n    ensure\n      s.close if s\n    end\n\n    varz_req = Net::HTTP::Get.new(\"/varz\")\n    varz_resp = Net::HTTP.new(host, port).start { |http| http.request(varz_req) }\n    varz_resp.code.should_not == '200'\n    varz_resp.body.should be_empty\n\n    # Do proper auth here\n    varz_req.basic_auth(config['http']['user'], config['http']['password'])\n    varz_resp = Net::HTTP.new(host, port).start { |http| http.request(varz_req) }\n    varz_resp.code.should == '200'\n    varz_resp.body.should_not be_empty\n\n    varz = JSON.parse(varz_resp.body, :symbolize_keys => true, :symbolize_names => true)\n    varz.should be_an_instance_of Hash\n    varz.should have_key :start\n    varz.should have_key :options\n    varz.should have_key :mem\n    varz.should have_key :cpu\n    varz.should have_key :cores\n    varz.should have_key :connections\n    varz.should have_key :in_msgs\n    varz.should have_key :in_bytes\n    varz.should have_key :out_msgs\n    varz.should have_key :out_bytes\n\n    auth_s.kill_server if auth_s\n  end\n\nend\n"
  },
  {
    "path": "spec/server/multi_user_auth_spec.rb",
    "content": "\nrequire 'spec_helper'\nrequire 'fileutils'\nrequire 'nats/server/server'\nrequire \"nats/server/sublist\"\nrequire \"nats/server/options\"\nrequire \"nats/server/const\"\nrequire \"nats/server/util\"\n\ndescribe 'Server - multi-user authorization' do\n\n  before (:all) do\n    config_file = File.dirname(__FILE__) + '/resources/multi_user_auth.yml'\n    @config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    @opts = NATSD::Server.options\n    @log_file = @config['log_file']\n    @host = @config['net']\n    @port = @config['port']\n    @uri = \"nats://#{@host}:#{@port}\"\n    @s = RubyNatsServerControl.new(@uri, @config['pid_file'], \"-c #{config_file}\")\n    @s.start_server\n  end\n\n  after (:all) do\n    @s.kill_server\n    FileUtils.rm_f(@log_file)\n  end\n\n  it 'should have a users option array even with only a single user defined' do\n    config_file = File.dirname(__FILE__) + '/resources/auth.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    user = config['authorization']['user']\n    pass = config['authorization']['pass']\n    opts[:user].should == user\n    opts[:pass].should == pass\n    opts[:users].should_not be_nil\n    opts[:users].should be_an_instance_of Array\n    first = opts[:users].first\n    first[:user].should == user\n    first[:pass].should == pass\n  end\n\n  it 'should have a users array when user passed in on command line' do\n    user = 'derek'\n    pass = 'foo'\n    NATSD::Server.process_options(\"--user #{user} --pass #{pass}\".split)\n    opts = NATSD::Server.options\n    opts[:user].should == user\n    opts[:pass].should == pass\n    opts[:users].should_not be_nil\n    opts[:users].should be_an_instance_of Array\n    first = opts[:users].first\n    first[:user].should == user\n    first[:pass].should == pass\n  end\n\n  it 'should support mixed auth models and report singleton correctly' do\n    config_file = File.dirname(__FILE__) + '/resources/mixed_auth.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    user = config['authorization']['user']\n    pass = config['authorization']['pass']\n    opts[:user].should == user\n    opts[:pass].should == pass\n    opts[:users].should_not be_nil\n    opts[:users].should be_an_instance_of Array\n    first = opts[:users].first\n    first[:user].should == user\n    first[:pass].should == pass\n  end\n\n  it 'should accept pass or password in multi form' do\n    config_file = File.dirname(__FILE__) + '/resources/multi_user_auth_long.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:users].should_not be_nil\n    opts[:users].should be_an_instance_of Array\n    opts[:users].each { |u| u[:pass].should_not be_nil }\n  end\n\n  it 'should report first auth user as main user for backward compatability' do\n    first = @opts[:users].first\n    @opts[:user].should == first[:user]\n    @opts[:pass].should == first[:pass]\n  end\n\n  it 'should not allow unauthorized access with multi auth' do\n    expect do\n      NATS.start(:uri => @uri) { NATS.stop }\n    end.to raise_error NATS::Error\n  end\n\n  it 'should allow multi-users to be configured for auth' do\n    @opts[:users].length.should >= 3\n\n    user1 = @opts[:users][0][:user]\n    pass1 = @opts[:users][0][:pass]\n\n    user2 = @opts[:users][1][:user]\n    pass2 = @opts[:users][1][:pass]\n\n    user3 = @opts[:users][2][:user]\n    pass3 = @opts[:users][2][:pass]\n\n    expect do\n      uri = \"nats://#{user1}:#{pass1}@#{@host}:#{@port}\"\n      NATS.start(:uri => uri) { NATS.stop }\n    end.to_not raise_error\n\n    expect do\n      uri = \"nats://#{user2}:#{pass2}@#{@host}:#{@port}\"\n      NATS.start(:uri => uri) { NATS.stop }\n    end.to_not raise_error\n\n    expect do\n      uri = \"nats://#{user3}:#{pass3}@#{@host}:#{@port}\"\n      NATS.start(:uri => uri) { NATS.stop }\n    end.to_not raise_error\n\n  end\n\nend\n"
  },
  {
    "path": "spec/server/protocol_spec.rb",
    "content": "require 'spec_helper'\nrequire 'nats/server/const'\n\ndescribe 'Server - NATS Protocol' do\n\n  context 'sub' do\n\n    it 'should match simple sub' do\n      str = \"SUB foo 1\\r\\n\"\n      NATSD::SUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == nil\n      $4.should == '1'\n    end\n\n    it 'should not care about case' do\n      str = \"sub foo 1\\r\\n\"\n      NATSD::SUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == nil\n      $4.should == '1'\n    end\n\n    it 'should match a queue group' do\n      str = \"SUB foo bargroup 1\\r\\n\"\n      NATSD::SUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == 'bargroup'\n      $4.should == '1'\n    end\n\n    it 'should not care about extra spaces' do\n      str = \"SUB    foo  bargroup   1\\r\\n\"\n      NATSD::SUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == 'bargroup'\n      $4.should == '1'\n    end\n\n    it 'should care about extra spaces at end' do\n      str = \"SUB    foo  bargroup   1   \\r\\n\"\n      (NATSD::SUB_OP =~ str).should be_falsey\n    end\n\n    it 'should properly match first one when multiple present' do\n      str = \"SUB foo 1\\r\\nSUB bar 2\\r\\nSUB baz 3\\r\\n\"\n      NATSD::SUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == nil\n      $4.should == '1'\n      $'.should == \"SUB bar 2\\r\\nSUB baz 3\\r\\n\"\n    end\n\n    it 'should not tolerate spaces after \\r\\n' do\n      str = \"SUB foo 1\\r\\n   SUB bar 2\\r\\nSUB baz 3\\r\\n\"\n      NATSD::SUB_OP =~ str\n      str = $'\n      NATSD::SUB_OP =~ str\n      $1.should == nil\n      $3.should == nil\n      $4.should == nil\n    end\n\n  end\n\n  context 'unsub' do\n\n    it 'should process simple unsub' do\n      str = \"UNSUB 1\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n    end\n\n    it 'should not care about case' do\n      str = \"unsub 1\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n    end\n\n    it 'should tolerate extra spaces' do\n      str = \"UNSUB   1\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n    end\n\n    it 'should properly match first one when multiple present' do\n      str = \"UNSUB 1\\r\\nUNSUB 2\\r\\nSUB foo 2\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n      $'.should == \"UNSUB 2\\r\\nSUB foo 2\\r\\n\"\n    end\n\n    it 'should properly parse auto-unsubscribe message count' do\n      str = \"UNSUB 1 22\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n      $2.should be\n      $3.to_i.should == 22\n    end\n\n    it 'should properly parse auto-unsubscribe message count with extra spaces' do\n      str = \"UNSUB  1   22\\r\\n\"\n      NATSD::UNSUB_OP =~ str\n      $1.should == '1'\n      $2.should be\n      $3.to_i.should == 22\n    end\n\n  end\n\n  context 'pub' do\n\n    it 'should process simple pub' do\n      str = \"PUB foo 2\\r\\nok\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == nil\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\n\"\n    end\n\n    it 'should not care about case' do\n      str = \"pub foo 2\\r\\nok\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == nil\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\n\"\n    end\n\n    it 'should process reply semantics' do\n      str = \"PUB foo bar 2\\r\\nok\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == 'bar'\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\n\"\n    end\n\n    it 'should not care about extra spaces' do\n      str = \"PUB  foo     bar  2\\r\\nok\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should == 'bar'\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\n\"\n    end\n\n    it 'should care about extra spaces at end' do\n      str = \"PUB  foo     bar  2   \\r\\nok\\r\\n\"\n      (NATSD::PUB_OP =~ str).should be_falsey\n    end\n\n    it 'should properly match first one when multiple present' do\n      str = \"PUB foo bar  2\\r\\nok\\r\\nPUB bar 11\\r\\nHello World\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should ==  'bar'\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\nPUB bar 11\\r\\nHello World\\r\\n\"\n    end\n\n  end\n\n  context 'misc' do\n\n    it 'should process ping requests' do\n      str = \"PING\\r\\n\"\n      (NATSD::PING =~ str).should be_truthy\n    end\n\n    it 'should process ping requests with spaces' do\n      str = \"PING  \\r\\n\"\n      (NATSD::PING =~ str).should be_truthy\n    end\n\n    it 'should process pong responses' do\n      str = \"PONG\\r\\n\"\n      (NATSD::PONG =~ str).should be_truthy\n    end\n\n    it 'should process ping responses with spaces' do\n      str = \"PONG  \\r\\n\"\n      (NATSD::PONG =~ str).should be_truthy\n    end\n\n    it 'should process info requests' do\n      str = \"INFO\\r\\n\"\n      (NATSD::INFO =~ str).should be_truthy\n    end\n\n    it 'should process info requests with spaces' do\n      str = \"INFO  \\r\\n\"\n      (NATSD::INFO =~ str).should be_truthy\n    end\n\n    it 'should process connect requests' do\n      str = \"CONNECT {\\\"user\\\":\\\"derek\\\"}\\r\\n\"\n      NATSD::CONNECT =~ str\n      $1.should == \"{\\\"user\\\":\\\"derek\\\"}\"\n    end\n\n  end\n\n  context 'mixed' do\n\n    it 'should process multiple commands in one buffer properly' do\n      str = \"PUB foo bar  2\\r\\nok\\r\\nSUB bar 22\\r\\nPUB bar 11\\r\\nHello World\\r\\n\"\n      NATSD::PUB_OP =~ str\n      $1.should == 'foo'\n      $3.should ==  'bar'\n      $4.to_i.should == 2\n      $'.should == \"ok\\r\\nSUB bar 22\\r\\nPUB bar 11\\r\\nHello World\\r\\n\"\n      str = $'\n      str = str.slice($4.to_i + NATSD::CR_LF_SIZE, str.bytesize)\n      NATSD::SUB_OP =~ str\n      $1.should == 'bar'\n      $3.should == nil\n      $4.should == '22'\n    end\n\n  end\n\n  context 'client' do\n\n    it 'should process ping and pong responsess' do\n      str = \"PING\\r\\n\"\n      (NATS::PING =~ str).should be_truthy\n      str = \"PING \\r\\n\"\n      (NATS::PING =~ str).should be_truthy\n      str = \"PONG\\r\\n\"\n      (NATS::PONG =~ str).should be_truthy\n      str = \"PONG \\r\\n\"\n      (NATS::PONG =~ str).should be_truthy\n    end\n\n    it 'should process ok responses' do\n      str = \"+OK\\r\\n\"\n      (NATS::OK =~ str).should be_truthy\n      str = \"+OK \\r\\n\"\n      (NATS::OK =~ str).should be_truthy\n    end\n\n    it 'should process err responses' do\n      str = \"-ERR 'string too long'\\r\\n\"\n      (NATS::ERR =~ str).should be_truthy\n      $1.should == \"'string too long'\"\n    end\n\n    it 'should process messages' do\n      str = \"MSG foo 2 11\\r\\nHello World\\r\\n\"\n      NATS::MSG =~ str\n      $1.should == 'foo'\n      $2.should == '2'\n      $4.should == nil\n      $5.to_i.should == 11\n      $'.should == \"Hello World\\r\\n\"\n    end\n\n    it 'should process messages with a reply' do\n      str = \"MSG foo  2 reply_to_me 11\\r\\nHello World\\r\\n\"\n      NATS::MSG =~ str\n      $1.should == 'foo'\n      $2.should == '2'\n      $4.should == 'reply_to_me'\n      $5.to_i.should == 11\n      $'.should == \"Hello World\\r\\n\"\n    end\n\n    it 'should process messages with extra spaces' do\n      str = \"MSG    foo  2     reply_to_me  11\\r\\nHello World\\r\\n\"\n      NATS::MSG =~ str\n      $1.should == 'foo'\n      $2.should == '2'\n      $4.should == 'reply_to_me'\n      $5.to_i.should == 11\n      $'.should == \"Hello World\\r\\n\"\n    end\n\n    it 'should process multiple messages in a single read properly' do\n      str = \"MSG foo 2 11\\r\\nHello World\\r\\nMSG foo  2 reply_to_me 2\\r\\nok\\r\\n\"\n      NATS::MSG =~ str\n      $1.should == 'foo'\n      $2.should == '2'\n      $4.should == nil\n      $5.to_i.should == 11\n      str = $'\n      str = str.slice($5.to_i + NATSD::CR_LF_SIZE, str.bytesize)\n      NATS::MSG =~ str\n      $1.should == 'foo'\n      $2.should == '2'\n      $4.should == 'reply_to_me'\n      $5.to_i.should == 2\n      $'.should == \"ok\\r\\n\"\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/server/resources/auth.yml",
    "content": "\n---\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  pass: foo\n"
  },
  {
    "path": "spec/server/resources/b1_cluster.yml",
    "content": "port: 6242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n\ncluster:\n  port: 6254\n\n  authorization:\n    user: route_user\n    password: cafebabe\n    timeout: 1\n\n  # These are actively connected from this server. Other servers\n  # can connect to us if they supply the correct credentials from\n  # above.\n\n  routes:\n    - nats-route://route_user:cafebabe@127.0.0.1:6256\n    - nats-route://route_user:cafebabe@127.0.0.1:6256\n\npid_file: '/tmp/nats_cluster_b1.pid'\nlog_file: '/tmp/nats_cluster_b1.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   true\ntrace:   true\n"
  },
  {
    "path": "spec/server/resources/b2_cluster.yml",
    "content": "port: 6244\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n\ncluster:\n  port: 6256\n\n  authorization:\n    user: route_user\n    password: cafebabe\n    timeout: 1\n\n  # These are actively connected from this server. Other servers\n  # can connect to us if they supply the correct credentials from\n  # above.\n\n  routes:\n    - nats-route://route_user:cafebabe@127.0.0.1:6254\n\n\npid_file: '/tmp/nats_cluster_b2.pid'\nlog_file: '/tmp/nats_cluster_b2.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   true\ntrace:   true\n\n"
  },
  {
    "path": "spec/server/resources/cluster.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./cluster.yml\n#\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n#\n# NATS can support both full mesh and directed-acyclic\n# graph setups. Its up to the configuration setup to avoid cycles.\n#\n# The port definition allows us to receive incoming connections.\n# Comment out if you want to suppress incoming connections.\n#\n# The server can solicit active connections via the routes definitions below.\n#\n# authorization is similar to client connection definitions.\n\ncluster:\n  port: 4244\n\n  authorization:\n    user: route_user\n    password: cafebabe\n    token: deadbeef\n    timeout: 1\n\n  # These are actively connected from this server. Other servers\n  # can connect to us if they supply the correct credentials from\n  # above.\n\n  routes:\n    - nats-route://foo:bar@127.0.0.1:4220\n    - nats-route://foo:bar@127.0.0.1:4221\n\npid_file: '/tmp/nats_test.pid'\n# log_file: '/tmp/nats_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "spec/server/resources/config.yml",
    "content": "\n---\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\npid_file: '/tmp/nats_test.pid'\nlog_file: '/tmp/nats_test.log'\n\nssl: false\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\nmax_connections: 128\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "spec/server/resources/max_connections.yml",
    "content": "---\n\nport: 9272\nnet: localhost\n\n# Protocol/Limits\nmax_connections: 32\n\n\n"
  },
  {
    "path": "spec/server/resources/mixed_auth.yml",
    "content": "\n---\n\nport: 4242\nnet: localhost\n\nauthorization:\n\n  user: derek\n  pass: foo\n\n  users:\n    - user: sam\n      pass: wrestling\n    - user: meg\n      pass: soccer\n"
  },
  {
    "path": "spec/server/resources/monitor.yml",
    "content": "\n---\n\nport: 4242\nnet: localhost\n\nhttp:\n  net:  127.0.0.1\n  port: 4222\n  user: derek\n  password: foo\n\npid_file: '/tmp/nats_test.pid'\nlog_file: '/tmp/nats_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "spec/server/resources/multi_user_auth.yml",
    "content": "\n---\n\nport: 9226\nnet: localhost\n\nauthorization:\n  users:\n    - user: derek\n      pass: foo\n    - user: sam\n      pass: wrestling\n    - user: meg\n      pass: soccer\n\n  timeout: 1\n\npid_file: '/tmp/nats_multi_user_auth_test.pid'\nlog_file: '/tmp/nats_multi_user_auth_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n\n"
  },
  {
    "path": "spec/server/resources/multi_user_auth_long.yml",
    "content": "\n---\n\nport: 9226\nnet: localhost\n\nauthorization:\n  users:\n    - user: derek\n      pass: foo\n    - user: sam\n      password: wrestling\n    - user: meg\n      password: soccer\n\n  timeout: 1\n\npid_file: '/tmp/nats_multi_user_auth_test.pid'\nlog_file: '/tmp/nats_multi_user_auth_test.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n\n"
  },
  {
    "path": "spec/server/resources/ping.yml",
    "content": "\n---\n\nport: 2421\nnet: localhost\n\npid_file: '/tmp/nats_ping_test.pid'\nlog_file: '/tmp/nats_ping_test.log'\n\n# Ping options (in secs)\nping:\n  interval: 0.1\n  max_outstanding: 2\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n# Protocol/Limits\nmax_control_line: 512\nmax_payload:  512000\nmax_pending: 2000000\n\n# EM/IO\nno_epoll:  false\nno_kqueue: true\n\n\n"
  },
  {
    "path": "spec/server/resources/s1_cluster.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./s[N]_cluster.yml\n#\n\nport: 4242\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n\ncluster:\n  port: 4262\n\n  authorization:\n    user: ruser\n    password: cafebabe\n    token: deadbeef\n    timeout: 1\n\npid_file: '/tmp/nats_cluster_s1.pid'\n#log_file: '/tmp/nats_cluster_s1.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n\n"
  },
  {
    "path": "spec/server/resources/s2_cluster.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./s[N]_cluster.yml\n#\n\nport: 4244\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n\ncluster:\n  port: 4264\n\n  authorization:\n    user: ruser\n    password: cafebabe\n    token: deadbeef\n    timeout: 1\n\n  routes:\n    - nats-route://ruser:cafebabe@localhost:4262 # Connect to S1\n\npid_file: '/tmp/nats_cluster_s2.pid'\n#log_file: '/tmp/nats_cluster_s2.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n\n"
  },
  {
    "path": "spec/server/resources/s3_cluster.yml",
    "content": "\n---\n\n#\n# Sample Server Sonfiguration\n# nats-server -c ./s[N]_cluster.yml\n#\n\nport: 4246\nnet: localhost\n\nauthorization:\n  user: derek\n  password: bella\n  token: deadbeef\n  timeout: 1\n\n# This is the cluster definition for NATS.\n\ncluster:\n  port: 4266\n\n  authorization:\n    user: ruser\n    password: cafebabe\n    token: deadbeef\n    timeout: 1\n\n  routes:\n    - nats-route://ruser:cafebabe@127.0.0.1:4262 # Connect to s1\n\npid_file: '/tmp/nats_cluster_s3.pid'\n#log_file: '/tmp/nats_cluster_s3.log'\n\n# Debug Options\n\nlogtime: true\ndebug:   false\ntrace:   false\n\n\n"
  },
  {
    "path": "spec/server/server_cluster_config_spec.rb",
    "content": "require 'spec_helper'\nrequire 'nats/server/server'\nrequire 'nats/server/sublist'\nrequire 'nats/server/options'\nrequire 'nats/server/const'\nrequire 'nats/server/util'\nrequire 'logger'\n\ndescribe 'Server - cluster configuration' do\n\n  it 'should allow the cluster listen port to be set on command line' do\n    NATSD::Server.process_options('-a localhost -p 5222 -r 8222'.split)\n    opts = NATSD::Server.options\n\n    opts[:addr].should == 'localhost'\n    opts[:port].should == 5222\n    opts[:cluster_port].should == 8222\n  end\n\n  it 'should allow the cluster listen port to be set on command line (long form)' do\n    NATSD::Server.process_options('-a localhost -p 5222 --cluster_port 8222'.split)\n    opts = NATSD::Server.options\n\n    opts[:addr].should == 'localhost'\n    opts[:port].should == 5222\n    opts[:cluster_port].should == 8222\n  end\n\n  it 'should properly parse a config file' do\n    config_file = File.dirname(__FILE__) + '/resources/cluster.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:config_file].should == config_file\n    opts[:port].should == config['port']\n    opts[:addr].should == config['net']\n    opts[:user].should == config['authorization']['user']\n    opts[:pass].should == config['authorization']['password']\n    opts[:token].should == config['authorization']['token']\n    opts[:pid_file].should == config['pid_file']\n    opts[:log_file].should == config['log_file']\n    opts[:log_time].should == config['logtime']\n    opts[:debug].should == config['debug']\n    opts[:trace].should == config['trace']\n    opts[:max_control_line].should == config['max_control_line']\n    opts[:max_payload].should == config['max_payload']\n    opts[:max_pending].should == config['max_pending']\n\n    # cluster specific\n    opts[:cluster_port].should == config['cluster']['port']\n    opts[:cluster_user].should == config['cluster']['authorization']['user']\n    opts[:cluster_pass].should == config['cluster']['authorization']['password']\n    opts[:cluster_token].should == config['cluster']['authorization']['token']\n    opts[:cluster_auth_timeout].should == config['cluster']['authorization']['timeout']\n    opts[:cluster_routes].should == config['cluster']['routes']\n  end\n\nend\n"
  },
  {
    "path": "spec/server/server_config_spec.rb",
    "content": "require 'spec_helper'\nrequire 'nats/server/server'\nrequire \"nats/server/sublist\"\nrequire \"nats/server/options\"\nrequire \"nats/server/const\"\nrequire \"nats/server/util\"\nrequire 'logger'\n\ndescribe \"Server - Configuration\" do\n\n  it 'should return default options with no command line arguments' do\n    NATSD::Server.process_options\n    opts = NATSD::Server.options\n\n    opts.should be_an_instance_of Hash\n    opts.should have_key :port\n    opts.should have_key :addr\n    opts.should have_key :max_control_line\n    opts.should have_key :max_payload\n    opts.should have_key :max_pending\n    opts.should have_key :max_connections\n\n    opts[:port].should == NATSD::DEFAULT_PORT\n    opts[:addr].should == NATSD::DEFAULT_HOST\n  end\n\n  it 'should allow an override with command line arguments' do\n    NATSD::Server.process_options('-a localhost -p 5222 --user derek --pass foo'.split)\n    opts = NATSD::Server.options\n\n    opts[:addr].should == 'localhost'\n    opts[:port].should == 5222\n    opts[:user].should == 'derek'\n    opts[:pass].should == 'foo'\n  end\n\n  it 'should properly parse a config file' do\n    config_file = File.dirname(__FILE__) + '/resources/config.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:config_file].should == config_file\n    opts[:port].should == config['port']\n    opts[:addr].should == config['net']\n    opts[:user].should == config['authorization']['user']\n    opts[:pass].should == config['authorization']['password']\n    opts[:token].should == config['authorization']['token']\n    opts[:ssl].should == config['ssl']\n    opts[:pid_file].should == config['pid_file']\n    opts[:log_file].should == config['log_file']\n    opts[:log_time].should == config['logtime']\n    opts[:debug].should == config['debug']\n    opts[:trace].should == config['trace']\n    opts[:max_control_line].should == config['max_control_line']\n    opts[:max_payload].should == config['max_payload']\n    opts[:max_pending].should == config['max_pending']\n    opts[:max_connections].should == config['max_connections']\n\n  end\n\n  it 'should allow pass and password for authorization config' do\n    config_file = File.dirname(__FILE__) + '/resources/auth.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:user].should == config['authorization']['user']\n    opts[:pass].should == config['authorization']['pass']\n  end\n\n  it 'should allow command line arguments to override config file' do\n    config_file = File.dirname(__FILE__) + '/resources/config.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file} -p 8122 -l /tmp/foo.log\".split)\n    opts = NATSD::Server.options\n\n    opts[:port].should == 8122\n    opts[:log_file].should == '/tmp/foo.log'\n  end\n\n  it 'should properly set logtime under server attributes' do\n    config_file = File.dirname(__FILE__) + '/resources/config.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    NATSD::Server.finalize_options\n    NATSD::Server.log_time.should be_truthy\n  end\n\n  describe \"NATSD::Server.finalize_options\" do\n    before do\n      NATSD::Server.process_options\n    end\n\n    context \"ssl setting is nothing\" do\n      before do\n        NATSD::Server.options[:ssl] = nil\n      end\n\n      it \"shoud properly set @ssl_required to nil\" do\n        NATSD::Server.finalize_options\n        NATSD::Server.ssl_required.should be_falsey\n      end\n    end\n\n    context \"ssl setting is false\" do\n      before do\n        NATSD::Server.options[:ssl] = false\n      end\n\n      it \"should properly set @ssl_required to false\" do\n        NATSD::Server.finalize_options\n        NATSD::Server.ssl_required.should be_falsey\n      end\n    end\n\n    context \"ssl setting is true\" do\n      before do\n        NATSD::Server.options[:ssl] = true\n      end\n\n      it \"should properly set @ssl_required to true\" do\n        NATSD::Server.finalize_options\n        NATSD::Server.ssl_required.should be_truthy\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/server/server_exitcode_spec.rb",
    "content": "require 'spec_helper'\nrequire 'fileutils'\n\ndescribe 'Server - exit codes' do\n\n  before (:all) do\n    config_file = File.dirname(__FILE__) + '/resources/nonexistant.yml'\n    uri = 'nats://localhost:4222'\n    pid_file = '/tmp/test-nats-exit.pid'\n    @s = RubyNatsServerControl.new(uri, pid_file, \"-c #{config_file}\")\n  end\n\n  after(:all) do\n    @s.kill_server\n  end\n\n  it 'should exit with non-zero status code when config file not found' do\n    exit_code = @s.start_server(false)\n    exit_code.should_not == 0\n  end\n\nend\n"
  },
  {
    "path": "spec/server/server_log_spec.rb",
    "content": "require 'spec_helper'\nrequire 'syslog'\nrequire 'nats/server/server'\nrequire 'nats/server/const'\nrequire 'nats/server/options'\nrequire 'nats/server/util'\n\ndescribe 'Server - log and pid files' do\n\n  before(:all) do\n    LOG_SERVER_PID = '/tmp/nats_log_pid.pid'\n    LOG_SERVER = 'nats://localhost:9299'\n    LOG_LOG_FILE = '/tmp/nats_log_test.log'\n    LOG_FLAGS = \"-l #{LOG_LOG_FILE}\"\n    SYSLOG_IDENT = \"nats_syslog_test\"\n    LOG_SYSLOG_FLAGS= \"#{LOG_FLAGS} -S #{SYSLOG_IDENT}\"\n\n    FileUtils.rm_f(LOG_LOG_FILE)\n    @s = RubyNatsServerControl.new(LOG_SERVER, LOG_SERVER_PID, LOG_FLAGS)\n    @s.start_server\n  end\n\n  after(:all) do\n    @s.kill_server\n    NATS.server_running?(LOG_SERVER).should be_falsey\n    FileUtils.rm_f(LOG_LOG_FILE)\n  end\n\n  it 'should create the log file' do\n    File.exists?(LOG_LOG_FILE).should be_truthy\n  end\n\n  it 'should create the pid file' do\n    File.exists?(LOG_SERVER_PID).should be_truthy\n  end\n\n  it 'should not leave a daemonized pid file in current directory' do\n    File.exists?(\"./#{NATSD::APP_NAME}.pid\").should be_falsey\n  end\n\n  it 'should append to the log file after restart' do\n    @s.kill_server\n    @s.start_server\n    File.read(LOG_LOG_FILE).split(\"\\n\").size.should == 4\n  end\n\n  it 'should not output to the log file when enable syslog option' do\n    @s.kill_server\n    FileUtils.rm_f(LOG_LOG_FILE)\n    @s = RubyNatsServerControl.new(LOG_SERVER, LOG_SERVER_PID, LOG_SYSLOG_FLAGS)\n    @s.start_server\n    File.read(LOG_LOG_FILE).split(\"\\n\").size.should == 0\n  end\n\n it 'should use Syslog module methods when enable syslog option' do\n    *log_msg  = 'syslog test'\n    Syslog.should_receive(:open).with(SYSLOG_IDENT, Syslog::LOG_PID, Syslog::LOG_USER)\n    Syslog.should_receive(:log).with(Syslog::LOG_NOTICE, '%s', PP::pp(log_msg, '', 120))\n    Syslog.should_receive(:close)\n    NATSD::Server.process_options(LOG_SYSLOG_FLAGS.split)\n    NATSD::Server.open_syslog\n    log  *log_msg\n    NATSD::Server.close_syslog\n end\n\nend\n"
  },
  {
    "path": "spec/server/server_ping_spec.rb",
    "content": "\nrequire 'spec_helper'\nrequire 'fileutils'\n\nrequire 'nats/server/server'\nrequire 'nats/server/options'\nrequire 'nats/server/const'\nrequire 'nats/server/util'\n\ndescribe 'Server - ping' do\n\n  before (:all) do\n    config_file = File.dirname(__FILE__) + '/resources/ping.yml'\n    config = File.open(config_file) { |f| YAML.load(f) }\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    @opts = NATSD::Server.options\n    @log_file = config['log_file']\n    @host = config['net']\n    @port = config['port']\n    @uri = \"nats://#{@host}:#{@port}\"\n    @rs = RubyNatsServerControl.new(@uri, config['pid_file'], \"-c #{config_file}\")\n    @rs.start_server\n  end\n\n  after(:all) do\n    @rs.kill_server\n    FileUtils.rm_f(@log_file)\n  end\n\n  it 'should set default values for ping if not set' do\n    config_file = File.dirname(__FILE__) + '/resources/config.yml'\n    NATSD::Server.process_options(\"-c #{config_file}\".split)\n    opts = NATSD::Server.options\n    opts[:ping_interval].should == 120\n    opts[:ping_max].should == 2\n  end\n\n  it 'should properly parse ping parameters from config file' do\n    @opts[:ping_interval].should == 0.1\n    @opts[:ping_max].should == 2\n  end\n\n  it 'should ping us periodically' do\n    NATS.start(:uri => @uri) do |connection|\n      time_to_wait = @opts[:ping_interval] * @opts[:ping_max] + 0.2\n      EM.add_timer(time_to_wait) do\n        connection.pings.should >= @opts[:ping_max]\n        NATS.stop\n      end\n    end\n  end\n\n  it 'should disconnect us when we do not respond' do\n    begin\n      s = TCPSocket.open(@host, @port)\n      time_to_wait = @opts[:ping_interval] * (@opts[:ping_max] + 2)\n      sleep(time_to_wait)\n      buf = s.read(1024)\n      buf.should =~ /PING/\n      buf.should =~ /-ERR/\n      buf.should =~ /Unresponsive client detected, connection dropped/\n    ensure\n      s.close if s\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/server/ssl_spec.rb",
    "content": "require 'spec_helper'\nrequire 'fileutils'\n\ndescribe 'Server - SSL' do\n\n  TEST_SERVER_SSL        = \"nats://127.0.0.1:9392\"\n  TEST_SERVER_SSL_PID    = '/tmp/nats_ssl.pid'\n  TEST_SERVER_NO_SSL     = \"nats://127.0.0.1:9394\"\n  TEST_SERVER_NO_SSL_PID = '/tmp/nats_no_ssl.pid'\n\n  before (:all) do\n    @s_ssl = RubyNatsServerControl.new(TEST_SERVER_SSL, TEST_SERVER_SSL_PID, \"--ssl\")\n    @s_ssl.start_server\n\n    @s_no_ssl = RubyNatsServerControl.new(TEST_SERVER_NO_SSL, TEST_SERVER_NO_SSL_PID)\n    @s_no_ssl.start_server\n  end\n\n  after (:all) do\n    @s_ssl.kill_server\n    @s_no_ssl.kill_server\n    FileUtils.rm_f TEST_SERVER_SSL_PID\n    FileUtils.rm_f TEST_SERVER_NO_SSL_PID\n  end\n\n  it 'should fail to connect to an ssl server without TLS/SSL negotiation' do\n    skip 'flapping test'\n\n    errors = []\n    with_em_timeout(3) do |future|\n      nc = nil\n      NATS.on_error do |e|\n        errors << e\n        future.resume(nc)\n      end\n      nc = NATS.connect(:uri => TEST_SERVER_SSL)\n    end\n    expect(errors.count > 0).to eql(true)\n    expect(errors.first).to be_a(NATS::Error)\n  end\n\n  it 'should fail to connect to an no ssl server with TLS/SSL negotiation' do\n    skip 'flapping test'\n\n    errors = []\n    with_em_timeout(3) do |future|\n      nc = nil\n      NATS.on_error do |e|\n        errors << e\n        future.resume(nc)\n      end\n      nc = NATS.connect(:uri => TEST_SERVER_NO_SSL, :ssl => true)\n    end\n    expect(errors.count > 0).to eql(true)\n    expect(errors.first).to be_a(NATS::ClientError)\n  end\n\n  it 'should run TLS/SSL negotiation' do\n    expect do\n      NATS.start(:uri => TEST_SERVER_SSL, :ssl => true) { NATS.stop }\n    end.to_not raise_error\n  end\n\n  it 'should not run TLS/SSL negotiation' do\n    expect do\n      NATS.start(:uri => TEST_SERVER_NO_SSL) { NATS.stop }\n    end.to_not raise_error\n  end\n\nend\n"
  },
  {
    "path": "spec/server/sublist_spec.rb",
    "content": "require 'spec_helper'\nrequire 'nats/server/sublist'\n\ndescribe 'Server - sublist functionality' do\n\n  before do\n    @sublist = Sublist.new\n  end\n\n  it 'should be empty on start' do\n    @sublist.count.should == 0\n    @sublist.match('a').should be_empty\n    @sublist.match('a.b').should be_empty\n  end\n\n  it 'should have N items after N items inserted' do\n    5.times { |n| @sublist.insert(\"a.b.#{n}\", 'foo') }\n    @sublist.count.should == 5\n  end\n\n  it 'should be safe to remove an non-existant subscription' do\n    @sublist.insert('a.b.c', 'foo')\n    @sublist.remove('a.b.x', 'foo')\n  end\n\n  it 'should have 0 items after N items inserted and removed' do\n    5.times { |n| @sublist.insert(\"a.b.#{n}\", 'foo') }\n    @sublist.count.should == 5\n    5.times { |n| @sublist.remove(\"a.b.#{n}\", 'foo') }\n    @sublist.count.should == 0\n  end\n\n  it 'should have N items after N items inserted and others removed' do\n    5.times { |n| @sublist.insert(\"a.b.#{n}\", 'foo') }\n    @sublist.count.should == 5\n    @sublist.remove('a.b.c', 'foo')\n    @sublist.count.should == 5\n  end\n\n  it 'should match and return proper closure for subjects' do\n    @sublist.insert('a', 'foo')\n    m = @sublist.match('a')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n  end\n\n  it 'should not match after the item has been removed' do\n    @sublist.insert('a', 'foo')\n    @sublist.remove('a', 'foo')\n    @sublist.match('a').should be_empty\n  end\n\n  it 'should match simple multiple tokens' do\n    @sublist.insert('a.b.c', 'foo')\n    m = @sublist.match('a.b.c')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n    m = @sublist.match('a.b.z')\n    m.should be_empty\n  end\n\n  it 'should match full wildcards on any proper subject' do\n    @sublist.insert('a.b.>', 'foo')\n    m = @sublist.match('a.b.c')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n    m = @sublist.match('a.b.z')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n    m = @sublist.match('a.b.c.d.e.f')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n  end\n\n  it 'should match positional wildcards on any proper subject' do\n    @sublist.insert('a.*.c', 'foo')\n    m = @sublist.match('a.b.c')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n    m = @sublist.match('a.b.z')\n    m.should be_empty\n    m = @sublist.match('a.z.c')\n    m.should_not be_empty\n    m.size.should == 1\n    m[0].should == 'foo'\n  end\n\n  it 'should properly match multiple wildcards' do\n    @sublist.insert('*.b.*', '2pwc')\n    m = @sublist.match('a.b.c')\n    m.count.should == 1\n    m.should == ['2pwc']\n    @sublist.insert('a.>', 'fwc')\n    m = @sublist.match('a.z.c')\n    m.count.should == 1\n    m.should == ['fwc']\n    m = @sublist.match('a.b.c.d')\n    m.count.should == 1\n    m.should == ['fwc']\n  end\n\n  it 'should properly mix and match simple, fwc, and pwc wildcards' do\n    @sublist.insert('a.b.c', 'simple')\n    @sublist.insert('a.b.>', 'fwc')\n    @sublist.insert('a.*.c', 'pwc-middle')\n    @sublist.insert('*.b.c', 'pwc-first')\n    @sublist.insert('a.b.*', 'pwc-last')\n    m = @sublist.match('a.b.c').sort\n    m.count.should == 5\n    m.should == ['fwc', 'pwc-first', 'pwc-last', 'pwc-middle', 'simple']\n    m = @sublist.match('a.b.c.d.e.f')\n    m.count.should == 1\n    m.should == ['fwc']\n    m = @sublist.match('z.b.c')\n    m.count.should == 1\n    m.should == ['pwc-first']\n    m = @sublist.match('a.z.c')\n    m.count.should == 1\n    m.should == ['pwc-middle']\n    m = @sublist.match('a.b.z').sort\n    m.count.should == 2\n    m.should == ['fwc', 'pwc-last']\n  end\n\n  it 'should have N node_count after N items inserted' do\n    5.times { |n| @sublist.insert(\"a.b.#{n}\", 'foo') }\n    nc = @sublist.send :node_count\n    nc.should == 7\n  end\n\n  it 'should have 0 node_count after N items inserted and removed' do\n    5.times { |n| @sublist.insert(\"#{n}\", 'foo') }\n    5.times { |n| @sublist.remove(\"#{n}\", 'foo') }\n    nc = @sublist.send :node_count\n    nc.should == 0\n  end\n\n  it 'should have 0 node_count after N items with 1 token prefix inserted and removed' do\n    5.times { |n| @sublist.insert(\"INBOX.#{n}\", 'foo') }\n    5.times { |n| @sublist.remove(\"INBOX.#{n}\", 'foo') }\n    nc = @sublist.send :node_count\n    nc.should == 0\n  end\n\n  it 'should have 0 node_count after N items with 3 prefix and wildcards inserted and removed' do\n    5.times { |n| @sublist.insert(\"a.b.*.#{n}\", 'foo') }\n    @sublist.insert('a.b.>', 'foo')\n    5.times { |n| @sublist.remove(\"a.b.*.#{n}\", 'foo') }\n    @sublist.remove('a.b.>', 'foo')\n    nc = @sublist.send :node_count\n    nc.should == 0\n  end\n\n  it 'should have 0 node_count after N items with large middle token range inserted and removed' do\n    5.times { |n| @sublist.insert(\"a.#{n}.c\", 'foo') }\n    5.times { |n| @sublist.remove(\"a.#{n}.c\", 'foo') }\n    nc = @sublist.send :node_count\n    nc.should == 0\n  end\n\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "$:.unshift('./lib')\n\nrequire 'net/http'\nrequire 'nats/client'\nrequire 'tempfile'\n\ndef timeout_nats_on_failure(to=0.25)\n  EM.add_timer(to) do\n    EM.stop\n  end\nend\n\ndef timeout_em_on_failure(to=0.25)\n  EM.add_timer(to) do\n    EM.stop\n  end\nend\n\ndef with_em_timeout(to=1)\n  EM.run do\n    t = EM.add_timer(to) do\n      NATS.stop\n      EM.stop if EM.reactor_running?\n    end\n\n    fib = Fiber.new do |nc|\n      nc.close if nc\n      EM.cancel_timer(t)\n      EM.stop if EM.reactor_running?\n    end\n\n    yield fib\n  end\nend\n\ndef wait_on_connections(conns)\n  return unless conns\n  expected, ready = conns.size, 0\n  conns.each do |c|\n    c.flush do\n      ready += 1\n      yield if ready >= expected\n    end\n  end\nend\n\ndef flush_routes(conns, &blk)\n  wait_on_routes_connected(conns, &blk)\nend\n\ndef wait_on_routes_connected(conns)\n  return unless conns && conns.size > 1\n\n  sub = NATS.create_inbox\n  ready = Array.new(conns.size, false)\n  yield_needed = true\n\n  conns.each_with_index do |c, i|\n    c.subscribe(sub) do |msg|\n      ready[i] = true\n      done, yn = ready.all?, yield_needed\n      yield_needed = false if yn && done\n      yield if yn && done\n    end\n  end\n  conn = conns.first\n  timer = EM.add_periodic_timer(0.1) do\n    conn.publish(sub)\n    timer.cancel if ready.all?\n  end\nend\n\nclass NatsServerControl\n\n  attr_reader :was_running\n  alias :was_running? :was_running\n\n  class << self\n\n    def init_with_config(config_file)\n      config = File.open(config_file) { |f| YAML.load(f) }\n      if auth = config['authorization']\n        uri = \"nats://#{auth['user']}:#{auth['password']}@#{config['net']}:#{config['port']}\"\n      else\n        uri = \"nats://#{config['net']}:#{config['port']}\"\n      end\n      NatsServerControl.new(uri, config['pid_file'], \"-c #{config_file}\")\n    end\n\n    def init_with_config_from_string(config_string, config={})\n      puts config_string if ENV[\"DEBUG_NATS_TEST\"] == \"true\"\n      config_file = Tempfile.new(['nats-cluster-tests', '.conf'])\n      File.open(config_file.path, 'w') do |f|\n        f.puts(config_string)\n      end\n\n      if auth = config['authorization']\n        uri = \"nats://#{auth['user']}:#{auth['password']}@#{config['host']}:#{config['port']}\"\n      else\n        uri = \"nats://#{config['host']}:#{config['port']}\"\n      end\n\n      NatsServerControl.new(uri, config['pid_file'], \"-c #{config_file.path}\", config_file)\n    end\n\n  end\n\n  attr_reader :uri\n\n  def initialize(uri='nats://127.0.0.1:4222', pid_file='/tmp/test-nats.pid', flags=nil, config_file=nil)\n    @uri = uri.is_a?(URI) ? uri : URI.parse(uri)\n    @pid_file = pid_file\n    @flags = flags\n    @config_file = config_file\n  end\n\n  def server_pid\n    @pid ||= File.read(@pid_file).chomp.to_i\n  end\n\n  def server_mem_mb\n    server_status = %x[ps axo pid=,rss= | grep #{server_pid}]\n    parts = server_status.lstrip.split(/\\s+/)\n    rss = (parts[1].to_i)/1024\n  end\n\n  def start_server(wait_for_server=true, monitoring: false)\n    if NATS.server_running? @uri\n      @was_running = true\n      return 0\n    end\n    @pid = nil\n\n    args = \"-p #{@uri.port} -P #{@pid_file}\"\n    args += \" -m 8222\" if monitoring\n    args += \" --user #{@uri.user}\" if @uri.user\n    args += \" --pass #{@uri.password}\" if @uri.password\n    args += \" #{@flags}\" if @flags\n\n    if ENV[\"DEBUG_NATS_TEST\"] == \"true\"\n      system(\"nats-server #{args} -DV &\")\n    else\n      system(\"nats-server #{args} 2> /dev/null &\")\n    end\n    exitstatus = $?.exitstatus\n    NATS.wait_for_server(@uri, 10) if wait_for_server # jruby can be slow on startup...\n    exitstatus\n  end\n\n  def kill_server\n    if File.exists? @pid_file\n      %x[kill -9 #{server_pid} 2> /dev/null]\n      %x[rm #{@pid_file} 2> /dev/null]\n      sleep(0.1)\n      @pid = nil\n    end\n  end\n\nend\n\n# For running tests against the Ruby NATS server :)\nclass RubyNatsServerControl < NatsServerControl\n  def start_server(wait_for_server=true)\n    if NATS.server_running? @uri\n      @was_running = true\n      return 0\n    end\n\n    @pid = nil\n\n    # daemonize really doesn't work on jruby, so should run servers manually to test on jruby\n    args = \"-p #{@uri.port} -P #{@pid_file}\"\n    args += \" --user #{@uri.user}\" if @uri.user\n    args += \" --pass #{@uri.password}\" if @uri.password\n    args += \" #{@flags}\" if @flags\n    args += ' -d'\n\n    %x[bundle exec ruby ./bin/nats-server #{args} 2> /dev/null]\n    exitstatus = $?.exitstatus\n    NATS.wait_for_server(@uri, 10) if wait_for_server #jruby can be slow on startup\n    exitstatus\n  end\nend\n\nmodule EchoServer\n\n  HOST = \"127.0.0.1\".freeze\n  PORT = \"9999\".freeze\n  URI  = \"nats://#{HOST}:#{PORT}\".freeze\n\n  def post_init\n    send_data('INFO {\"server_id\":\"WxE17fQB24XOvaWlhECrhg\",\"max_payload\":1048576,\"client_id\":1}'+\"\\r\\n\")\n  end\n\n  def receive_data(data)\n    send_data(data)\n  end\n\n  class << self\n    def start(&blk)\n      EM.run {\n        EventMachine::start_server(HOST, PORT, self)\n        blk.call\n      }\n    end\n\n    def stop\n      EM.stop_event_loop\n    end\n  end\nend\n\nmodule SilentServer\n\n  HOST = \"127.0.0.1\".freeze\n  PORT = \"9998\".freeze\n  URI  = \"nats://#{HOST}:#{PORT}\".freeze\n\n  def post_init\n    send_data('INFO {\"server_id\":\"WxE17fQB24XOvaWlhECrhg\",\"max_payload\":1048576,\"client_id\":1}'+\"\\r\\n\")\n  end\n\n  # Does not send anything back\n  def receive_data(data)\n  end\n\n  class << self\n    def start(&blk)\n      EM.run {\n        EventMachine::start_server(HOST, PORT, self)\n        blk.call\n      }\n    end\n\n    def stop\n      EM.stop_event_loop\n    end\n  end\nend\n\nmodule OldInfoServer\n\n  HOST = \"127.0.0.1\".freeze\n  PORT = \"9997\".freeze\n  URI  = \"nats://#{HOST}:#{PORT}\".freeze\n\n  def post_init\n    send_data('INFO {\"server_id\":\"WxE17fQB24XOvaWlhECrhg\",\"version\":\"1.3.0\",\"host\":\"0.0.0.0\",\"port\":4222,\"max_payload\":1048576,\"client_id\":1}'+\"\\r\\n\")\n  end\n  \n  def receive_data(data)\n  end\n\n  class << self\n    def start(&blk)\n      EM.run {\n        EventMachine::start_server(HOST, PORT, self)\n        blk.call\n      }\n    end\n\n    def stop\n      EM.stop_event_loop\n    end\n  end\nend\n\nmodule OldProtocolInfoServer\n\n  HOST = \"127.0.0.1\".freeze\n  PORT = \"9996\".freeze\n  URI  = \"nats://#{HOST}:#{PORT}\".freeze\n\n  def post_init\n    send_data('INFO {\"server_id\":\"WxE17fQB24XOvaWlhECrhg\",\"version\":\"1.3.0\",\"host\":\"0.0.0.0\",\"port\":4222,\"max_payload\":1048576,\"client_id\":1,\"proto\": 0}'+\"\\r\\n\")\n  end\n  \n  def receive_data(data)\n  end\n\n  class << self\n    def start(&blk)\n      EM.run {\n        EventMachine::start_server(HOST, PORT, self)\n        blk.call\n      }\n    end\n\n    def stop\n      EM.stop_event_loop\n    end\n  end\nend\n"
  }
]