websocket_client_lite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 57be41a22406e00807c3747e93f1621de3775fbc94d2c7e5f527e55a518a8677
4
+ data.tar.gz: 32f3e7db065169c8f3bf5a9e93856f12737599298941b9109a0540a1a4b3a636
5
+ SHA512:
6
+ metadata.gz: a22713d01e0191bd2531b9c1e9b2807f7447ef1da7e49f6004f13f573fa6e8db3c1dae219f22ea50593537b64fe96c98ad2260d1a23eec98c479d5042f13fdb1
7
+ data.tar.gz: 71b753100eed4e7d3b05429bf6433f8e201ca3b3acf81d94b556693c88d9b53cef51c306b643073da746d6a49fdbfd68a7272304147ca6d5d67100a4fe000dcb
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Layout/LineLength:
5
+ Max: 150
6
+
7
+ Metrics/AbcSize:
8
+ Max: 30
9
+
10
+ Metrics/ClassLength:
11
+ Max: 150
12
+
13
+ Metrics/CyclomaticComplexity:
14
+ Max: 10
15
+
16
+ Metrics/MethodLength:
17
+ Max: 30
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+
22
+ Style/StringLiterals:
23
+ Enabled: true
24
+ EnforcedStyle: double_quotes
25
+
26
+ Style/StringLiteralsInInterpolation:
27
+ Enabled: true
28
+ EnforcedStyle: double_quotes
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-03-27
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in websocket_client_lite.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "rspec", "~> 3.0"
10
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,60 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ websocket_client_lite (0.1.0)
5
+ websocket
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ diff-lcs (1.5.0)
12
+ json (2.6.3)
13
+ parallel (1.22.1)
14
+ parser (3.2.1.1)
15
+ ast (~> 2.4.1)
16
+ rainbow (3.1.1)
17
+ rake (13.0.6)
18
+ regexp_parser (2.7.0)
19
+ rexml (3.2.5)
20
+ rspec (3.12.0)
21
+ rspec-core (~> 3.12.0)
22
+ rspec-expectations (~> 3.12.0)
23
+ rspec-mocks (~> 3.12.0)
24
+ rspec-core (3.12.1)
25
+ rspec-support (~> 3.12.0)
26
+ rspec-expectations (3.12.2)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.12.0)
29
+ rspec-mocks (3.12.4)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.12.0)
32
+ rspec-support (3.12.0)
33
+ rubocop (1.48.1)
34
+ json (~> 2.3)
35
+ parallel (~> 1.10)
36
+ parser (>= 3.2.0.0)
37
+ rainbow (>= 2.2.2, < 4.0)
38
+ regexp_parser (>= 1.8, < 3.0)
39
+ rexml (>= 3.2.5, < 4.0)
40
+ rubocop-ast (>= 1.26.0, < 2.0)
41
+ ruby-progressbar (~> 1.7)
42
+ unicode-display_width (>= 2.4.0, < 3.0)
43
+ rubocop-ast (1.28.0)
44
+ parser (>= 3.2.1.0)
45
+ ruby-progressbar (1.13.0)
46
+ unicode-display_width (2.4.2)
47
+ websocket (1.2.9)
48
+
49
+ PLATFORMS
50
+ arm64-darwin-22
51
+ x86_64-darwin-22
52
+
53
+ DEPENDENCIES
54
+ rake (~> 13.0)
55
+ rspec (~> 3.0)
56
+ rubocop (~> 1.21)
57
+ websocket_client_lite!
58
+
59
+ BUNDLED WITH
60
+ 2.4.9
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Ueda Satoshi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # WebsocketClientLite
2
+
3
+ Easy to use WebSocket client for Ruby.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```sh
10
+ $ bundle add websocket_client_lite
11
+ ```
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ ```sh
16
+ $ gem install websocket_client_lite
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'websocket_client_lite'
23
+
24
+ url = 'wss://echo.websocket.org/'
25
+ logger = Logger.new($stdout)
26
+ client = WebsocketClientLite.new(url, logger: logger)
27
+ return unless client.handshake
28
+
29
+ client.send_text('hello')
30
+ client.each_payload do |payload|
31
+ puts payload
32
+ client.close
33
+ end
34
+ ```
35
+
36
+ ## Development
37
+
38
+ After checking out the repo, run `bin/setup` to install dependencies.
39
+ Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
40
+
41
+ To install this gem onto your local machine, run `bundle exec rake install`.
42
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
+
44
+ ## Contributing
45
+
46
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gunyoki/websocket_client_lite.
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class WebsocketClientLite
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "openssl"
5
+ require "socket"
6
+ require "websocket"
7
+ require_relative "websocket_client_lite/version"
8
+
9
+ class WebsocketClientLite
10
+ attr_accessor :url, :logger, :incoming_queue, :handshake_object, :socket, :state
11
+
12
+ def initialize(url, logger: nil)
13
+ @url = url
14
+ @logger = logger || Logger.new(File::NULL)
15
+ @incoming_queue = Queue.new
16
+ @state = nil
17
+ end
18
+
19
+ def handshake
20
+ @state = :connecting
21
+ @handshake_object = WebSocket::Handshake::Client.new(url: @url)
22
+ @socket = make_socket(@handshake_object.host, @handshake_object.port, @handshake_object.secure)
23
+ @socket.write(@handshake_object.to_s)
24
+
25
+ until @handshake_object.finished?
26
+ byte = @socket.read(1)
27
+ @handshake_object << byte
28
+ end
29
+
30
+ @handshake_object.valid?
31
+ @state = :open
32
+ true
33
+ rescue StandardError => e
34
+ @logger.error("Handshake failed: #{e.inspect}")
35
+ @socket&.close
36
+ @socket = nil
37
+ @state = nil
38
+ false
39
+ end
40
+
41
+ def each_payload(&block)
42
+ thread = nil
43
+ return unless @state == :open
44
+
45
+ thread = start_incoming_thread
46
+
47
+ while %i[open closing_by_client].include?(@state) do
48
+ frame = @incoming_queue.pop
49
+ case frame.type
50
+ when :ping
51
+ send_pong(frame.data)
52
+ when :pong
53
+ # no-op
54
+ when :text, :binary
55
+ block.yield(frame.data)
56
+ when :close
57
+ if @state == :open
58
+ @state = :closing_by_server
59
+ send_close(frame.data)
60
+ end
61
+ @state = :closed
62
+ else
63
+ raise "Unknown frame. type: #{frame.type}, data: #{frame.data}"
64
+ end
65
+ end
66
+ ensure
67
+ thread&.join(1)
68
+ @socket&.close
69
+ @socket = nil
70
+ @state = nil
71
+ end
72
+
73
+ def close(code = nil, data = nil)
74
+ if @state == :open
75
+ @state = :closing_by_client
76
+ send_frame(:close, data, code: code)
77
+ end
78
+ end
79
+
80
+ def send_ping(code, data)
81
+ send_frame(:ping, data, code: code)
82
+ end
83
+
84
+ def send_pong(data)
85
+ send_frame(:pong, data)
86
+ end
87
+
88
+ def send_close(code)
89
+ send_frame(:close, nil, code: code)
90
+ end
91
+
92
+ def send_text(data)
93
+ send_frame(:text, data)
94
+ end
95
+
96
+ def send_binary(data)
97
+ send_frame(:binary, data)
98
+ end
99
+
100
+ private
101
+
102
+ def make_socket(host, port, secure)
103
+ socket = Socket.tcp(host, port)
104
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
105
+ if secure
106
+ socket = OpenSSL::SSL::SSLSocket.new(socket)
107
+ socket.connect
108
+ socket.post_connection_check(host)
109
+ socket.sync_close = true
110
+ end
111
+ socket
112
+ end
113
+
114
+ def start_incoming_thread
115
+ Thread.start(@socket, @incoming_queue, @logger) do |socket, incoming_queue, logger|
116
+ frame_processor = WebSocket::Frame::Incoming::Client.new
117
+ until socket.closed?
118
+ binary = socket.readpartial(4096)
119
+ frame_processor << binary
120
+ while (frame = frame_processor.next)
121
+ logger.debug("[ws incoming] type: #{frame.type}, code: #{frame.code}, data: #{frame.data}")
122
+ incoming_queue << frame
123
+ end
124
+ end
125
+ rescue StandardError => e
126
+ logger.error("[ws incoming] #{e.inspect}")
127
+ end
128
+ end
129
+
130
+ def send_frame(type, data, code: nil)
131
+ logger.debug("[ws outgoing] type: #{type}, code: #{code}, data: #{data}")
132
+ frame = WebSocket::Frame::Outgoing::Client.new(data: data, type: type, code: code, version: @handshake_object.version)
133
+ @socket.write(frame.to_s)
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: websocket_client_lite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ueda Satoshi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: It works as a WebSocket client by simply implementing a block that handles
28
+ messages.
29
+ email:
30
+ - gunyoki@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - CHANGELOG.md
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - lib/websocket_client_lite.rb
44
+ - lib/websocket_client_lite/version.rb
45
+ homepage: https://rubygems.org/gems/websocket_client_lite
46
+ licenses:
47
+ - MIT
48
+ metadata:
49
+ homepage_uri: https://rubygems.org/gems/websocket_client_lite
50
+ source_code_uri: https://github.com/gunyoki/websocket_client_lite
51
+ changelog_uri: https://github.com/gunyoki/websocket_client_lite/CHANGELOG.md
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.6.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.4.6
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Easy to use WebSocket client
71
+ test_files: []