webhookapp 0.0.5

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4d50683eafe14a8c0c190c1b9f6153a56a28e4b4
4
+ data.tar.gz: 970cedbb05a483f1306edb3abc02061d2e392680
5
+ SHA512:
6
+ metadata.gz: 193c16b0da1f58391034804d20c2d4bd6333bf95443b53bb6944d5266247b19d7015b9d0b78546b818c8d457f83a1cf48df3b8dbe30b62805130b2046752356f
7
+ data.tar.gz: a421fec34ac64b3fbdab24699f2b723ab45c3e4cdeda017e4e58af5afcbf092afbba0b6fca37c32514b652fed3be7d8a6523c75ead3d88b860ca1b7f4c9bfcde
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Steve Smith
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.
@@ -0,0 +1,29 @@
1
+ # Webhookapp
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'webhookapp'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install webhookapp
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/webhookapp/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'webhookapp'
7
+ require 'webhookapp/tunnel/runner'
@@ -0,0 +1 @@
1
+ require 'webhookapp/version'
@@ -0,0 +1,191 @@
1
+ require 'base64'
2
+ require 'socket'
3
+ require 'requester'
4
+
5
+ module Webhookapp
6
+ module Tunnel
7
+ class Client
8
+ def initialize(local_port, hook_id, client_id, username, password)
9
+ raise ArgumentError, 'Unknown local port' unless local_port
10
+ raise ArgumentError, 'Unknown hook' unless hook_id
11
+
12
+ @connections = {}
13
+
14
+ @local_port = local_port
15
+ @hook_id = hook_id
16
+ @client_id = client_id
17
+ @username = username
18
+ @password = password
19
+ @details = nil
20
+ @remote_port = nil
21
+ @control = nil
22
+ end
23
+
24
+ def start
25
+ request_tunnel_details
26
+ if @details
27
+ start_tunnel
28
+ else
29
+ log "Closing due to lack of details"
30
+ end
31
+ end
32
+
33
+ def stop
34
+ @stopping = true
35
+ end
36
+
37
+ private
38
+
39
+ def request_tunnel_details
40
+ log "Client version #{Webhookapp::VERSION}"
41
+ debug "Protocol version: #{PROTOCOL_VERSION}"
42
+
43
+ requester = Requester.new(@hook_id, @client_id, @username, @password)
44
+ details = requester.request_tunnel
45
+ debug "Got details #{details}"
46
+
47
+ if details
48
+ @remote_port = details[:port].to_i
49
+ @host = details[:host]
50
+ @password = details[:password]
51
+ @details = details
52
+ end
53
+ rescue => e
54
+ log "Error: #{e}"
55
+ debug e.backtrace
56
+ nil
57
+ end
58
+
59
+ def start_tunnel
60
+ log "Starting tunnel to local port: #{@local_port}"
61
+ debug "From #{@host}:#{@remote_port} to #{@local_port}"
62
+
63
+ begin
64
+ @control = TCPSocket.open @host, @remote_port
65
+ rescue SocketError, Errno::ECONNREFUSED => e
66
+ log "Cannot connect to tunnel: #{e}"
67
+ return false
68
+ end
69
+
70
+ greet_control
71
+ start_loop
72
+
73
+ log "Remote connection closed" if @control_closed
74
+ log "Stopping tunnel..." if @stopping
75
+ end
76
+
77
+ protected
78
+
79
+ def greet_control
80
+ debug "Sending greeting #{@password}"
81
+ @control.write("password, 0, #{@password}\n")
82
+ end
83
+
84
+ def start_loop
85
+ until @control_closed || @stopping
86
+ sockets = IO.select([@control] + @connections.values, nil, nil, 0.01)
87
+
88
+ sleep(0.1) && next unless sockets
89
+
90
+ debug sockets.inspect
91
+ sockets = sockets[0] + sockets[1]
92
+
93
+ sockets.each do |socket|
94
+ if socket == @control
95
+ debug "Control has data"
96
+ read_control
97
+ else
98
+ debug "#{socket} has data"
99
+ read_local(socket)
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def read_control
106
+ line = @control.gets
107
+ return @control_closed = true if line.nil? || line == ''
108
+
109
+ debug "Control received #{line.inspect}"
110
+ command, connection_id, data = split_control_message(line.gsub(/\n\z/, ''))
111
+
112
+ case command
113
+ when 'open'
114
+ open_local(connection_id)
115
+ when 'accept'
116
+ debug 'Connection accepted'
117
+ when 'close'
118
+ close_local(connection_id)
119
+ when 'request'
120
+ debug "Receive"
121
+ send_local(connection_id, data)
122
+ when 'ping'
123
+ send_pong(data)
124
+ when 'error'
125
+ log "Error: #{data}"
126
+ else
127
+ debug "Unknown control message #{command}: #{line}"
128
+ end
129
+ end
130
+
131
+ def open_local(connection_id)
132
+ debug "Open connection #{connection_id} to #{@local_port}"
133
+ @connections[connection_id] = TCPSocket.open '127.0.0.1', @local_port
134
+ end
135
+
136
+ def close_local(connection_id)
137
+ debug "Close local #{connection_id}"
138
+ if @connections[connection_id]
139
+ debug "Removing connection #{@connections[connection_id]}"
140
+ @connections[connection_id].close
141
+ @connections.delete(connection_id)
142
+ else
143
+ debug "Already closed #{connection_id}: #{@connections.inspect}"
144
+ end
145
+ end
146
+
147
+ def send_local(connection_id, data)
148
+ debug "Sending #{Base64.strict_decode64(data).inspect} to #{connection_id}"
149
+ @connections[connection_id].write(Base64.strict_decode64(data))
150
+ end
151
+
152
+ def read_local(socket)
153
+ socket_id = @connections.find { |_key, value| socket == value }[0]
154
+ debug "Response from #{socket_id.inspect}"
155
+
156
+ # Use recv to take up to 0.5 MB from the socket
157
+ # We cannot use gets as we don't know that this socket is actually \n terminated
158
+ data = socket.recv(1024 * 128)
159
+ # data = socket.recv(10)
160
+ debug "Got #{data.bytesize}B from #{socket_id.inspect}"
161
+
162
+ if data == '' || data.nil?
163
+ debug "Local socket was closed"
164
+ socket.close
165
+ @connections.delete(socket_id)
166
+ @control.write("close, #{socket_id}\n")
167
+ else
168
+ debug "Response #{data.inspect} #{Base64.strict_encode64(data).inspect}"
169
+ @control.write("response, #{socket_id}, #{Base64.strict_encode64(data)}\n")
170
+ end
171
+ end
172
+
173
+ def send_pong(data)
174
+ @control.write("pong, 0, #{data}\n")
175
+ end
176
+
177
+ def split_control_message(message)
178
+ message.split(', ')
179
+ end
180
+
181
+ def log(message)
182
+ puts message
183
+ end
184
+
185
+ def debug(message)
186
+ return unless ENV['DEBUG_WEBHOOKAPP_CLIENT'] == 'true'
187
+ puts message
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,69 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'oauth2'
4
+
5
+ module Webhookapp
6
+ module Tunnel
7
+ class Requester
8
+ DEFAULT_REQUEST_SITE = 'https://www.webhookapp.com'
9
+ DEFAULT_REQUEST_PATH = "#{DEFAULT_REQUEST_SITE}/api/tunnels"
10
+
11
+ class RequestError < StandardError; end
12
+
13
+ attr_accessor :request_path
14
+
15
+ def initialize(hook_id, client_id, username, password)
16
+ raise ArgumentError, "Hook id cannot be nil" if hook_id.nil?
17
+ raise ArgumentError, "Client id cannot be nil" if client_id.nil?
18
+
19
+ set_request_details(hook_id, client_id, username, password)
20
+ end
21
+
22
+ def request_tunnel
23
+ uri = URI(@request_path)
24
+ client = OAuth2::Client.new(@client_id, nil, site: @site, max_redirects: 0, ssl: { verify: @verify_ssl })
25
+ headers = {
26
+ accept: "application/vnd.webhookapp.v#{PROTOCOL_VERSION}+json",
27
+ 'user-agent' => "WebhookApp Tunnel v#{Webhookapp::VERSION}"
28
+ }
29
+ token = client.password.get_token(@username, @password, headers: headers)
30
+ res = token.post(uri, headers: headers)
31
+
32
+ handle_response(res)
33
+ end
34
+
35
+ protected
36
+
37
+ def handle_response(response)
38
+ case response.status
39
+ when 200
40
+ handle_success(response.body)
41
+ else
42
+ raise RequestError, "Cannot get tunnel details from #{@request_path} - #{response.status}: #{response.body}"
43
+ end
44
+ end
45
+
46
+ def handle_success(body)
47
+ details = JSON.load(body)
48
+ {
49
+ host: details['host'],
50
+ port: details['client_port'],
51
+ password: details['password'],
52
+ ttl: details['ttl']
53
+ }
54
+ rescue
55
+ raise RequestError, "Cannot parse details: #{body}"
56
+ end
57
+
58
+ def set_request_details(hook_id, client_id, username, password)
59
+ @hook_id = hook_id
60
+ @client_id = client_id
61
+ @username = username
62
+ @password = password
63
+ @site = ENV['REQUEST_SITE'] || DEFAULT_REQUEST_SITE
64
+ @request_path = (ENV['REQUEST_PATH'] || DEFAULT_REQUEST_PATH) + "?hook_id=#{hook_id}"
65
+ @verify_ssl = ENV['VERIFY_MODE'] != 'none'
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,33 @@
1
+ require 'bundler/setup'
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__)
4
+ require 'client'
5
+
6
+ require 'dotenv'
7
+ Dotenv.load
8
+
9
+ CLIENT_ID = "ace69df24ae5b175054489faafcd2157faf3d771ba1367764f62277d8f608db6"
10
+
11
+ def read_password
12
+ stty_settings = `stty -g`
13
+ print 'Password: '
14
+
15
+ begin
16
+ `stty -echo`
17
+ password = STDIN.gets.chomp
18
+ ensure
19
+ `stty #{stty_settings}`
20
+ end
21
+ password
22
+ end
23
+
24
+ if ARGV[0] && ARGV[1] && ARGV[2]
25
+ password = ARGV[3]
26
+ password = read_password if password.nil?
27
+
28
+ @tunnel = Webhookapp::Tunnel::Client.new(ARGV[0], ARGV[1], CLIENT_ID, ARGV[2], password)
29
+ trap('INT') { @tunnel.stop }
30
+ @tunnel.start
31
+ else
32
+ puts "Usage: tunnel local_port hook_id username [password]"
33
+ end
@@ -0,0 +1,7 @@
1
+ module Webhookapp
2
+ VERSION = "0.0.5"
3
+
4
+ module Tunnel
5
+ PROTOCOL_VERSION = "0.1"
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'webhookapp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "webhookapp"
8
+ spec.version = Webhookapp::VERSION
9
+ spec.authors = ["Steve Smith"]
10
+ spec.email = ["github@scsworld.co.uk"]
11
+ spec.summary = %q{Command line client to support WebhookApp}
12
+ spec.description = %q{Command line client to support WebhookApp}
13
+ spec.homepage = "http://webhookapp.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "bundler", "~> 1.5"
22
+ spec.add_dependency "dotenv", "~> 1.0"
23
+ spec.add_dependency "oauth2", "~> 1.0"
24
+
25
+ spec.add_development_dependency "rake"
26
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webhookapp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Steve Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-02 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.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: oauth2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
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
+ description: Command line client to support WebhookApp
70
+ email:
71
+ - github@scsworld.co.uk
72
+ executables:
73
+ - tunnel
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/tunnel
83
+ - lib/webhookapp.rb
84
+ - lib/webhookapp/tunnel/client.rb
85
+ - lib/webhookapp/tunnel/requester.rb
86
+ - lib/webhookapp/tunnel/runner.rb
87
+ - lib/webhookapp/version.rb
88
+ - webhookapp_client.gemspec
89
+ homepage: http://webhookapp.com
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.2.2
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Command line client to support WebhookApp
113
+ test_files: []