├── .gitignore ├── Gemfile ├── Gemfile.lock ├── History.txt ├── LICENSE.txt ├── README.md ├── Rakefile ├── circle.yml ├── lib ├── websocket-client-simple.rb └── websocket-client-simple │ ├── client.rb │ └── version.rb ├── sample ├── client.rb ├── echo_server.rb └── webbrowser │ ├── index.html │ └── main.js ├── test ├── echo_server.rb ├── test_connect_block.rb ├── test_helper.rb └── test_websocket_client_simple.rb └── websocket-client-simple.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | InstalledFiles 7 | _yardoc 8 | coverage 9 | doc/ 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in websocket-client-simple.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | websocket-client-simple (0.3.0) 5 | event_emitter 6 | websocket 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | event_emitter (0.2.5) 12 | eventmachine (1.0.9.1) 13 | minitest (5.8.4) 14 | rake (10.5.0) 15 | websocket (1.2.2) 16 | websocket-eventmachine-base (1.2.0) 17 | eventmachine (~> 1.0) 18 | websocket (~> 1.0) 19 | websocket-native (~> 1.0) 20 | websocket-eventmachine-server (1.0.1) 21 | websocket-eventmachine-base (~> 1.0) 22 | websocket-native (1.0.0) 23 | 24 | PLATFORMS 25 | ruby 26 | 27 | DEPENDENCIES 28 | bundler (~> 1.3) 29 | eventmachine 30 | minitest 31 | rake 32 | websocket-client-simple! 33 | websocket-eventmachine-server 34 | 35 | BUNDLED WITH 36 | 1.11.2 37 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 0.3.0 2016-02-20 2 | 3 | * "connect" method runs a given block before connecting WebSocket #12 4 | * thank you for suggestion @codekitchen 5 | 6 | === 0.2.5 2016-02-18 7 | 8 | * bugfixed sending when broken pipe #15 9 | * add :verify_mode option for SSL Context #14 10 | * thank you for contributing @michaelvilensky 11 | 12 | === 0.2.4 2015-11-12 13 | 14 | * support handshake headers #11 15 | * thank you for contributing @mathieugagne 16 | 17 | === 0.2.3 2015-10-26 18 | 19 | * kill thread at end of method 20 | * thank you for contributing @hansy 21 | 22 | === 0.2.2 2014-11-18 23 | 24 | * add :ssl_version option to specify version of SSL/TLS 25 | * thank you for contributing @tonybyrne 26 | 27 | === 0.2.1 2014-10-19 28 | 29 | * bugfix socket reading 30 | 31 | === 0.2.0 2014-06-07 32 | 33 | * SSL support with wss:// and https:// scheme 34 | * thank you for contributing @mallowlabs 35 | 36 | === 0.1.0 2014-05-08 37 | 38 | * add accessor Client#handshake #5 39 | * bugfix socket reading 40 | 41 | === 0.0.9 2014-04-03 42 | 43 | * emit "error" in receive thread 44 | * rescue only Errno::EPIPE, not all Errors 45 | 46 | === 0.0.8 2014-01-29 47 | 48 | * bugfix Client#close #4 49 | 50 | === 0.0.7 2014-01-29 51 | 52 | * send CLOSE frame in Client#close #4 53 | 54 | === 0.0.6 2014-01-17 55 | 56 | * add function Client#open? 57 | 58 | === 0.0.5 2013-03-23 59 | 60 | * kill read thread on close 61 | 62 | === 0.0.4 2013-03-23 63 | 64 | * fix sample and README 65 | 66 | === 0.0.3 2013-03-23 67 | 68 | * :type => :text option in send 69 | 70 | === 0.0.2 2013-03-22 71 | 72 | * remove unnecessary sleep 73 | 74 | === 0.0.1 2013-03-22 75 | 76 | * release 77 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2014 Sho Hashimoto 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | websocket-client-simple 2 | ======================= 3 | 4 | :warning: Important notice :warning: 5 | ------------------------------------ 6 | 7 | The development of this repository has moved to [ruby-jp/websocket-client-simple](https://github.com/ruby-jp/websocket-client-simple). 8 | 9 | ------------- 10 | 11 | Simple WebSocket Client for Ruby 12 | 13 | - https://github.com/shokai/websocket-client-simple 14 | - https://rubygems.org/gems/websocket-client-simple 15 | 16 | [](https://circleci.com/gh/shokai/websocket-client-simple) 17 | 18 | Installation 19 | ------------ 20 | 21 | gem install websocket-client-simple 22 | 23 | 24 | Usage 25 | ----- 26 | ```ruby 27 | require 'rubygems' 28 | require 'websocket-client-simple' 29 | 30 | ws = WebSocket::Client::Simple.connect 'ws://example.com:8888' 31 | 32 | ws.on :message do |msg| 33 | puts msg.data 34 | end 35 | 36 | ws.on :open do 37 | ws.send 'hello!!!' 38 | end 39 | 40 | ws.on :close do |e| 41 | p e 42 | exit 1 43 | end 44 | 45 | ws.on :error do |e| 46 | p e 47 | end 48 | 49 | loop do 50 | ws.send STDIN.gets.strip 51 | end 52 | ``` 53 | 54 | `connect` runs a given block before connecting websocket 55 | 56 | ```ruby 57 | WebSocket::Client::Simple.connect 'ws://example.com:8888' do |ws| 58 | ws.on :open do 59 | puts "connect!" 60 | end 61 | 62 | ws.on :message do |msg| 63 | puts msg.data 64 | end 65 | end 66 | ``` 67 | 68 | 69 | Sample 70 | ------ 71 | [websocket chat](https://github.com/shokai/websocket-client-simple/tree/master/sample) 72 | 73 | 74 | Test 75 | ---- 76 | 77 | % gem install bundler 78 | % bundle install 79 | % export WS_PORT=8888 80 | % rake test 81 | 82 | 83 | Contributing 84 | ------------ 85 | 1. Fork it 86 | 2. Create your feature branch (`git checkout -b my-new-feature`) 87 | 3. Commit your changes (`git commit -am 'Add some feature'`) 88 | 4. Push to the branch (`git push origin my-new-feature`) 89 | 5. Create new Pull Request 90 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new do |t| 5 | t.pattern = "test/test_*.rb" 6 | end 7 | 8 | task :default => :test 9 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | ruby: 3 | version: "2.2.2" 4 | -------------------------------------------------------------------------------- /lib/websocket-client-simple.rb: -------------------------------------------------------------------------------- 1 | require 'event_emitter' 2 | require 'websocket' 3 | require 'socket' 4 | require 'openssl' 5 | require 'uri' 6 | 7 | require 'websocket-client-simple/version' 8 | require 'websocket-client-simple/client' 9 | 10 | module WebSocket 11 | module Client 12 | module Simple 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/websocket-client-simple/client.rb: -------------------------------------------------------------------------------- 1 | module WebSocket 2 | module Client 3 | module Simple 4 | 5 | def self.connect(url, options={}) 6 | client = ::WebSocket::Client::Simple::Client.new 7 | yield client if block_given? 8 | client.connect url, options 9 | return client 10 | end 11 | 12 | class Client 13 | include EventEmitter 14 | attr_reader :url, :handshake 15 | 16 | def connect(url, options={}) 17 | return if @socket 18 | @url = url 19 | uri = URI.parse url 20 | @socket = TCPSocket.new(uri.host, 21 | uri.port || (uri.scheme == 'wss' ? 443 : 80)) 22 | if ['https', 'wss'].include? uri.scheme 23 | ctx = OpenSSL::SSL::SSLContext.new 24 | ctx.ssl_version = options[:ssl_version] || 'SSLv23' 25 | ctx.verify_mode = options[:verify_mode] || OpenSSL::SSL::VERIFY_NONE #use VERIFY_PEER for verification 26 | cert_store = OpenSSL::X509::Store.new 27 | cert_store.set_default_paths 28 | ctx.cert_store = cert_store 29 | @socket = ::OpenSSL::SSL::SSLSocket.new(@socket, ctx) 30 | @socket.connect 31 | end 32 | @handshake = ::WebSocket::Handshake::Client.new :url => url, :headers => options[:headers] 33 | @handshaked = false 34 | @pipe_broken = false 35 | frame = ::WebSocket::Frame::Incoming::Client.new 36 | @closed = false 37 | once :__close do |err| 38 | close 39 | emit :close, err 40 | end 41 | 42 | @thread = Thread.new do 43 | while !@closed do 44 | begin 45 | unless recv_data = @socket.getc 46 | sleep 1 47 | next 48 | end 49 | unless @handshaked 50 | @handshake << recv_data 51 | if @handshake.finished? 52 | @handshaked = true 53 | emit :open 54 | end 55 | else 56 | frame << recv_data 57 | while msg = frame.next 58 | emit :message, msg 59 | end 60 | end 61 | rescue => e 62 | emit :error, e 63 | end 64 | end 65 | end 66 | 67 | @socket.write @handshake.to_s 68 | end 69 | 70 | def send(data, opt={:type => :text}) 71 | return if !@handshaked or @closed 72 | type = opt[:type] 73 | frame = ::WebSocket::Frame::Outgoing::Client.new(:data => data, :type => type, :version => @handshake.version) 74 | begin 75 | @socket.write frame.to_s 76 | rescue Errno::EPIPE => e 77 | @pipe_broken = true 78 | emit :__close, e 79 | end 80 | end 81 | 82 | def close 83 | return if @closed 84 | if !@pipe_broken 85 | send nil, :type => :close 86 | end 87 | @closed = true 88 | @socket.close if @socket 89 | @socket = nil 90 | emit :__close 91 | Thread.kill @thread if @thread 92 | end 93 | 94 | def open? 95 | @handshake.finished? and !@closed 96 | end 97 | 98 | end 99 | 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/websocket-client-simple/version.rb: -------------------------------------------------------------------------------- 1 | module WebSocket 2 | module Client 3 | module Simple 4 | VERSION = "0.3.0" 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sample/client.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift File.expand_path '../lib', File.dirname(__FILE__) 3 | require 'rubygems' 4 | require 'websocket-client-simple' 5 | 6 | puts "websocket-client-simple v#{WebSocket::Client::Simple::VERSION}" 7 | 8 | url = ARGV.shift || 'ws://localhost:8080' 9 | 10 | ws = WebSocket::Client::Simple.connect url 11 | 12 | ws.on :message do |msg| 13 | puts ">> #{msg.data}" 14 | end 15 | 16 | ws.on :open do 17 | puts "-- websocket open (#{ws.url})" 18 | end 19 | 20 | ws.on :close do |e| 21 | puts "-- websocket close (#{e.inspect})" 22 | exit 1 23 | end 24 | 25 | ws.on :error do |e| 26 | puts "-- error (#{e.inspect})" 27 | end 28 | 29 | loop do 30 | ws.send STDIN.gets.strip 31 | end 32 | -------------------------------------------------------------------------------- /sample/echo_server.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'eventmachine' 3 | require 'websocket-eventmachine-server' 4 | 5 | PORT = (ARGV.shift || 8080).to_i 6 | 7 | EM::run do 8 | @channel = EM::Channel.new 9 | 10 | puts "start websocket server - port:#{PORT}" 11 | 12 | WebSocket::EventMachine::Server.start(:host => "0.0.0.0", :port => PORT) do |ws| 13 | ws.onopen do 14 | sid = @channel.subscribe do |mes| 15 | ws.send mes 16 | end 17 | puts "<#{sid}> connect" 18 | 19 | @channel.push "hello new client <#{sid}>" 20 | 21 | ws.onmessage do |msg| 22 | puts "<#{sid}> #{msg}" 23 | @channel.push "<#{sid}> #{msg}" 24 | end 25 | 26 | ws.onclose do 27 | puts "<#{sid}> disconnected" 28 | @channel.unsubscribe sid 29 | @channel.push "<#{sid}> disconnected" 30 | end 31 | end 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /sample/webbrowser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |