socket_duplex 1.0.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
+ 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: []