socket_duplex 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf86bc3bcc2e7134ebbb05833da75421b521d2f3
4
+ data.tar.gz: 716448a796d76653a9c27e376871428c041d7f17
5
+ SHA512:
6
+ metadata.gz: b5f3fc1fda60758d11e2200e31fc737c40be0e02940cf5a9940e8bfe341183a79fb39189f0bb65f4fef2ed0b3fc74e4107a0a9e62885301e3a2be2f2c964a425
7
+ data.tar.gz: 6cf09a8a93af86ca0d38d153213c5ed1c5f8abb4b9ff7efcade7f26316b50b309c5155af58a0b6311f0c32057b79f6e5c3dd16abc58d88d7bc5652252efde873
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in get_test.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # ruby-agent
2
+ Secure Ruby on Rails with SECful.
3
+
4
+ Installation
5
+ ------------
6
+ 1. Add the following to your Gemfile:
7
+ gem 'socket_duplex', :git => 'git@github.com:SECful/ruby-agent.git'
8
+
9
+ 2. Add the following to your config.ru:
10
+ require 'socket_duplex'
11
+ use Rack::SocketDuplex, 'wss://localhost:7000', 'token'
12
+
13
+ For a non SSL websocket use:
14
+ use Rack::SocketDuplex, 'ws://localhost:7000', 'token', OpenSSL::SSL::VERIFY_NONE
15
+
@@ -0,0 +1,138 @@
1
+ require 'json'
2
+ require 'socket'
3
+ require 'securerandom'
4
+
5
+ require_relative 'websocket-client-simple'
6
+
7
+ module Rack
8
+ class SocketDuplex
9
+ MAX_QUEUE_SIZE = 50
10
+ NUM_OF_THREADS = 5
11
+
12
+ def initialize(app, socket_path, secful_key, verify_mode=OpenSSL::SSL::VERIFY_PEER)
13
+ @app, @socket_path, @verify_mode = app, socket_path, verify_mode
14
+ begin
15
+ @secful_key = secful_key
16
+ @agent_identifier = SecureRandom.hex
17
+ @machine_ip = Socket.ip_address_list.detect(&:ipv4_private?).try(:ip_address)
18
+ @queue = SizedQueue.new(MAX_QUEUE_SIZE)
19
+ @threads_to_sockets = {}
20
+ Thread.new { activate_workers() }
21
+ rescue Exception
22
+ end
23
+ end
24
+
25
+ def call(env)
26
+ begin
27
+ dup._call(env)
28
+ rescue Exception
29
+ @app.call(env)
30
+ end
31
+ end
32
+
33
+ protected
34
+
35
+ def _call(env)
36
+ status, headers, body = @app.call(env)
37
+ if @queue.length < @queue.max
38
+ @queue << env
39
+ end
40
+ return [status, headers, body]
41
+ end
42
+
43
+ def activate_workers
44
+ NUM_OF_THREADS.times do
45
+ Thread.new {worker()}
46
+ end
47
+ end
48
+
49
+ def worker
50
+ loop do
51
+ env = @queue.pop
52
+ if env
53
+ connect_to_ws(Thread.current)
54
+ handle_request(env)
55
+ end rescue nil
56
+ end
57
+ end
58
+
59
+ def connect_to_ws(thr)
60
+ begin
61
+ ws = @threads_to_sockets[thr]
62
+ if !ws
63
+ headers = { 'Agent-Type' => 'Ruby',
64
+ 'Agent-Version' => '1.0',
65
+ 'Agent-Identifier' => @agent_identifier,
66
+ 'Authorization' => 'Bearer ' + @secful_key }
67
+ ws = WebSocket::Client::Simple.connect @socket_path, verify_mode: @verify_mode, headers: headers
68
+ sleep(3)
69
+ end
70
+ if !ws.open?
71
+ sleep(60)
72
+ end
73
+ if !ws.open?
74
+ ws.close()
75
+ ws = nil
76
+ end
77
+ rescue nil
78
+ end
79
+ @threads_to_sockets[thr] = ws
80
+ end
81
+
82
+ def handle_request(env)
83
+ request_hash = {}
84
+ if env['rack.url_scheme'] == 'http'
85
+ write_env(request_hash, env)
86
+ ws = @threads_to_sockets[Thread.current]
87
+ begin
88
+ ws.send request_hash.to_json
89
+ rescue Exception
90
+ if ws
91
+ ws.close()
92
+ end rescue nil
93
+ @threads_to_sockets[Thread.current] = nil
94
+ end
95
+ end rescue nil
96
+ end
97
+
98
+ def write_env(request_hash, env)
99
+ write_request_line request_hash, env
100
+ write_headers request_hash, env
101
+ write_post_body request_hash, env
102
+ end
103
+
104
+ def write_request_line(request_hash, env)
105
+ path_with_query_string = env['PATH_INFO']
106
+ path_with_query_string << "?#{env['QUERY_STRING']}" if env['QUERY_STRING'].length > 0
107
+ request_hash[:request] = {path: path_with_query_string,
108
+ version: env['HTTP_VERSION'],
109
+ method: env['REQUEST_METHOD']}
110
+ request_hash[:userSrcIp] = env['REMOTE_ADDR']
111
+ request_hash[:companyLocalIps] = [@machine_ip || env['SERVER_NAME']]
112
+ end
113
+
114
+ def write_headers(request_hash, env)
115
+ headers = env.select { |k, v| k =~ /^HTTP_/ }
116
+ headers = Hash[headers.map { |k, v| [k[5..-1].gsub('_', '-'), v] }]
117
+ headers.delete('VERSION')
118
+ if env['CONTENT_TYPE']
119
+ headers[:'CONTENT-TYPE'] = env['CONTENT_TYPE']
120
+ end
121
+ if env['CONTENT_LENGTH']
122
+ headers[:'CONTENT-LENGTH'] = env['CONTENT_LENGTH']
123
+ end
124
+
125
+ request_hash[:request][:headers] = headers_arr = []
126
+ headers.each do |k,v|
127
+ headers_arr << {key: k, value: v}
128
+ end
129
+ end
130
+
131
+ def write_post_body(request_hash, env)
132
+ if env['CONTENT_LENGTH'] && (content_length = env['CONTENT_LENGTH'].to_i) > 0
133
+ request_hash[:request][:payload] = env['rack.input'].read
134
+ env['rack.input'].rewind
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,91 @@
1
+ module WebSocket
2
+ module Client
3
+ module Simple
4
+
5
+ def self.connect(url, options={})
6
+ ::WebSocket::Client::Simple::Client.new(url, options)
7
+ end
8
+
9
+ class Client
10
+ include EventEmitter
11
+ attr_reader :url, :handshake
12
+
13
+ def initialize(url, options={})
14
+ @url = url
15
+ uri = URI.parse url
16
+ @socket = TCPSocket.new(uri.host,
17
+ uri.port || (uri.scheme == 'wss' ? 443 : 80))
18
+ if ['https', 'wss'].include? uri.scheme
19
+ ctx = OpenSSL::SSL::SSLContext.new
20
+ ctx.ssl_version = options[:ssl_version] || 'SSLv23'
21
+ ctx.verify_mode = options[:verify_mode] || OpenSSL::SSL::VERIFY_NONE #use VERIFY_PEER for verification
22
+ cert_store = OpenSSL::X509::Store.new
23
+ cert_store.set_default_paths
24
+ ctx.cert_store = cert_store
25
+ @socket = ::OpenSSL::SSL::SSLSocket.new(@socket, ctx)
26
+ @socket.connect
27
+ end
28
+ @handshake = ::WebSocket::Handshake::Client.new :url => url, :headers => options[:headers]
29
+ @handshaked = false
30
+ frame = ::WebSocket::Frame::Incoming::Client.new
31
+ @closed = false
32
+ once :__close do |err|
33
+ close
34
+ emit :close, err
35
+ end
36
+
37
+ @thread = Thread.new do
38
+ while !@closed do
39
+ begin
40
+ unless recv_data = @socket.getc
41
+ sleep 1
42
+ next
43
+ end
44
+ unless @handshaked
45
+ @handshake << recv_data
46
+ if @handshake.finished?
47
+ @handshaked = true
48
+ emit :open
49
+ end
50
+ else
51
+ frame << recv_data
52
+ while msg = frame.next
53
+ emit :message, msg
54
+ end
55
+ end
56
+ rescue => e
57
+ emit :error, e
58
+ end
59
+ end
60
+ end
61
+
62
+ @socket.write @handshake.to_s
63
+ end
64
+
65
+ def send(data, opt={:type => :text})
66
+ return if !@handshaked or @closed
67
+ type = opt[:type]
68
+ frame = ::WebSocket::Frame::Outgoing::Client.new(:data => data, :type => type, :version => @handshake.version)
69
+ #begin
70
+ @socket.write frame.to_s
71
+ #rescue Errno::EPIPE => e
72
+ # emit :__close, e
73
+ #end
74
+ end
75
+
76
+ def close
77
+ @closed = true
78
+ @socket.close if @socket
79
+ @socket = nil
80
+ Thread.kill @thread if @thread
81
+ end
82
+
83
+ def open?
84
+ @handshake.finished? and !@closed
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,7 @@
1
+ module WebSocket
2
+ module Client
3
+ module Simple
4
+ VERSION = "0.2.4"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
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
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "socket_duplex"
6
+ spec.version = '1.0.0'
7
+ spec.authors = ["Secful"]
8
+ spec.description = %q{Rack middleware that duplexes HTTP traffic}
9
+ spec.summary = spec.description
10
+
11
+ spec.files = `git ls-files`.split($/).reject{|f| f == "Gemfile.lock" }
12
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+ spec.require_paths = ["lib"]
15
+
16
+ spec.add_development_dependency "bundler", "~> 1.3"
17
+ spec.add_development_dependency "rake"
18
+ spec.add_development_dependency "minitest"
19
+ spec.add_development_dependency "websocket-eventmachine-server"
20
+ spec.add_development_dependency "eventmachine"
21
+
22
+ spec.add_runtime_dependency "websocket"
23
+ spec.add_runtime_dependency "event_emitter"
24
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: socket_duplex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Secful
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: websocket-eventmachine-server
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: eventmachine
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: websocket
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: event_emitter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Rack middleware that duplexes HTTP traffic
112
+ email:
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - Gemfile
118
+ - README.md
119
+ - lib/socket_duplex.rb
120
+ - lib/websocket-client-simple.rb
121
+ - lib/websocket-client-simple/client.rb
122
+ - lib/websocket-client-simple/version.rb
123
+ - socket_duplex.gemspec
124
+ homepage:
125
+ licenses: []
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.4.5.1
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Rack middleware that duplexes HTTP traffic
147
+ test_files: []