nayutaya-webhook-dispatcher 0.0.1 → 0.0.2
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.
- data/Rakefile +10 -3
- data/example.rb +6 -36
- data/lib/webhook-dispatcher/acl/allow_entry.rb +12 -0
- data/lib/webhook-dispatcher/acl/deny_entry.rb +12 -0
- data/lib/webhook-dispatcher/acl/entry_base.rb +94 -0
- data/lib/webhook-dispatcher/acl.rb +54 -44
- data/lib/webhook-dispatcher/core.rb +107 -0
- data/lib/webhook-dispatcher/request/base.rb +9 -0
- data/lib/webhook-dispatcher/request/get.rb +4 -0
- data/lib/webhook-dispatcher/request/head.rb +12 -0
- data/lib/webhook-dispatcher/request/post.rb +17 -0
- data/lib/webhook-dispatcher/response.rb +2 -3
- data/lib/webhook-dispatcher/version.rb +1 -1
- data/test/acl/allow_entry_test.rb +17 -0
- data/test/acl/deny_entry_test.rb +13 -0
- data/test/acl/entry_base_test.rb +248 -0
- data/test/acl_test.rb +131 -34
- data/test/core_test.rb +336 -0
- data/test/request/base_test.rb +45 -0
- data/test/request/get_test.rb +30 -0
- data/test/request/head_test.rb +30 -0
- data/test/request/post_test.rb +45 -0
- data/test/response_test.rb +12 -15
- data/test/test_helper.rb +16 -0
- data/webhook-dispatcher.gemspec +20 -12
- data/webhook-dispatcher.gemspec.erb +1 -3
- metadata +21 -11
- data/test/request_base_test.rb +0 -20
- data/test/request_get_test.rb +0 -14
data/Rakefile
CHANGED
@@ -5,10 +5,11 @@ task :default => [:test]
|
|
5
5
|
|
6
6
|
Rake::TestTask.new do |test|
|
7
7
|
test.libs << "test"
|
8
|
-
test.test_files = Dir.glob("test
|
8
|
+
test.test_files = Dir.glob("test/**/*_test.rb")
|
9
9
|
test.verbose = true
|
10
10
|
end
|
11
11
|
|
12
|
+
desc "Generate gemspec file from template"
|
12
13
|
task :gemspec do
|
13
14
|
require "erb"
|
14
15
|
require "lib/webhook-dispatcher/version"
|
@@ -19,8 +20,14 @@ task :gemspec do
|
|
19
20
|
version = WebHookDispatcher::VERSION
|
20
21
|
date = Time.now.strftime("%Y-%m-%d")
|
21
22
|
|
22
|
-
files
|
23
|
-
|
23
|
+
files = Dir.glob("**/*").
|
24
|
+
select { |path| File.file?(path) }.
|
25
|
+
reject { |path| /^nbproject\// =~ path }.
|
26
|
+
sort
|
27
|
+
|
28
|
+
test_files = Dir.glob("test/**.rb").
|
29
|
+
select { |path| File.file?(path) }.
|
30
|
+
sort
|
24
31
|
|
25
32
|
File.open("webhook-dispatcher.gemspec", "wb") { |file|
|
26
33
|
file.write(erb.result(binding))
|
data/example.rb
CHANGED
@@ -1,47 +1,17 @@
|
|
1
1
|
|
2
2
|
# このスクリプトはイメージであり、動作しません
|
3
3
|
|
4
|
-
|
5
|
-
WebHookPublisher.read_timeout = 5 # sec
|
6
|
-
WebHookPublisher.user_agent = "hoge"
|
7
|
-
WebHookPublisher.acl = x
|
8
|
-
WebHookPublisher.acl_with {
|
9
|
-
}
|
4
|
+
require "webhook-dispatcher"
|
10
5
|
|
11
|
-
|
12
|
-
|
13
|
-
wp.read_timeout = 5 # sec
|
14
|
-
wp.user_agent = "hoge"
|
15
|
-
wp.acl = WebHookPublisher::Acl.with {
|
16
|
-
allow :all
|
17
|
-
allow "*", :all
|
18
|
-
allow "*", 1024..3000
|
19
|
-
allow "*", 0..1024
|
20
|
-
allow "*", [1,2,3,4]
|
21
|
-
deny IPAdd.new("0.0.0.0/0")
|
22
|
-
allow "127.0.0.0/8"
|
23
|
-
allow "localhost"
|
24
|
-
}
|
6
|
+
p dispatcher = WebHookDispatcher.new
|
7
|
+
p request = WebHookDispatcher::Request::Get.new(URI.parse("http://www.google.co.jp"))
|
25
8
|
|
26
|
-
|
27
|
-
acl.add_deny(...)
|
28
|
-
acl.add_allow(...)
|
29
|
-
acl.allow?(ipaddr)
|
30
|
-
acl.deny?(ipaddr)
|
31
|
-
acl.with { ... }
|
32
|
-
|
33
|
-
|
34
|
-
request_obj = WebHookPublisher::Request.new(:get, URI.new(..))
|
35
|
-
request_obj.http_method = :get
|
36
|
-
request_obj.uri = uri
|
9
|
+
p response = dispatcher.request(request)
|
37
10
|
|
11
|
+
=begin
|
38
12
|
res = wp.request(request_obj)
|
39
13
|
res = wp.get(url)
|
40
14
|
res = wp.head(url)
|
41
15
|
res = wp.post(url, data)
|
42
16
|
#=> WebHookPublisher::Response
|
43
|
-
|
44
|
-
res.status #=> :timeout
|
45
|
-
res.status_code #=> 200
|
46
|
-
res.status_message #=> OK
|
47
|
-
res.inner_exception #=> #<RuntimeError>
|
17
|
+
=end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
|
2
|
+
require "ipaddr"
|
3
|
+
|
4
|
+
class WebHookDispatcher
|
5
|
+
class Acl
|
6
|
+
class EntryBase
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class WebHookDispatcher::Acl::EntryBase
|
12
|
+
def initialize(options = nil)
|
13
|
+
case options
|
14
|
+
when nil, :all
|
15
|
+
@addr = nil
|
16
|
+
@name = nil
|
17
|
+
@port = nil
|
18
|
+
when Hash
|
19
|
+
options = options.dup
|
20
|
+
@addr = normalize_addr(options.delete(:addr))
|
21
|
+
@name = normalize_name(options.delete(:name))
|
22
|
+
@port = normalize_port(options.delete(:port))
|
23
|
+
raise(ArgumentError) unless options.empty?
|
24
|
+
else raise(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :addr, :name, :port
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
return false unless other.instance_of?(self.class)
|
32
|
+
return (self.to_a == other.to_a)
|
33
|
+
end
|
34
|
+
|
35
|
+
def match?(addr, name, port)
|
36
|
+
return match_addr?(addr) && match_name?(name) && match_port?(port)
|
37
|
+
end
|
38
|
+
|
39
|
+
def value
|
40
|
+
raise(NotImplementedError)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_a
|
44
|
+
return [@addr, @name, @port]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def normalize_addr(addr)
|
50
|
+
case addr
|
51
|
+
when nil then return nil
|
52
|
+
when :all then return nil
|
53
|
+
when String then return IPAddr.new(addr)
|
54
|
+
when IPAddr then return addr
|
55
|
+
else raise(ArgumentError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def normalize_name(name)
|
60
|
+
case name
|
61
|
+
when nil then return nil
|
62
|
+
when :all then return nil
|
63
|
+
when String then return name.downcase
|
64
|
+
when Regexp then return name
|
65
|
+
else raise(ArgumentError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def normalize_port(port)
|
70
|
+
case port
|
71
|
+
when nil then return nil
|
72
|
+
when :all then return nil
|
73
|
+
when Integer then return [port]
|
74
|
+
when Array then return port.sort
|
75
|
+
when Range then return port
|
76
|
+
else raise(ArgumentError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def match_addr?(addr)
|
81
|
+
return true if self.addr.nil?
|
82
|
+
return (!addr.nil? && self.addr.include?(addr))
|
83
|
+
end
|
84
|
+
|
85
|
+
def match_name?(name)
|
86
|
+
return true if self.name.nil?
|
87
|
+
return (!name.nil? && (self.name === name.downcase))
|
88
|
+
end
|
89
|
+
|
90
|
+
def match_port?(port)
|
91
|
+
return true if self.port.nil?
|
92
|
+
return (!port.nil? && self.port.include?(port))
|
93
|
+
end
|
94
|
+
end
|
@@ -1,26 +1,62 @@
|
|
1
1
|
|
2
2
|
require "ipaddr"
|
3
|
+
require "webhook-dispatcher/acl/allow_entry"
|
4
|
+
require "webhook-dispatcher/acl/deny_entry"
|
3
5
|
|
4
6
|
class WebHookDispatcher::Acl
|
5
7
|
def initialize
|
6
|
-
@
|
8
|
+
@entries = []
|
7
9
|
end
|
8
10
|
|
9
11
|
def self.with(&block)
|
10
12
|
return self.new.with(&block)
|
11
13
|
end
|
12
14
|
|
15
|
+
def self.allow_all
|
16
|
+
return self.with { allow :all }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.deny_all
|
20
|
+
return self.with { deny :all }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.ipaddr?(value)
|
24
|
+
return true if value.instance_of?(IPAddr)
|
25
|
+
begin
|
26
|
+
IPAddr.new(value)
|
27
|
+
return true
|
28
|
+
rescue ArgumentError
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.create_matching_targets(addr_or_name, port)
|
34
|
+
if self.ipaddr?(addr_or_name)
|
35
|
+
addr = addr_or_name
|
36
|
+
return [[IPAddr.new(addr), nil, port]]
|
37
|
+
else
|
38
|
+
name = addr_or_name
|
39
|
+
_name, _aliases, _type, *addresses = TCPSocket.gethostbyname(name)
|
40
|
+
return addresses.map { |addr| [IPAddr.new(addr), name, port] }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ==(other)
|
45
|
+
return false unless other.instance_of?(self.class)
|
46
|
+
return (@entries == other.instance_eval { @entries })
|
47
|
+
end
|
48
|
+
|
13
49
|
def size
|
14
|
-
return @
|
50
|
+
return @entries.size
|
15
51
|
end
|
16
52
|
|
17
|
-
def add_allow(
|
18
|
-
@
|
53
|
+
def add_allow(options)
|
54
|
+
@entries << AllowEntry.new(options)
|
19
55
|
return self
|
20
56
|
end
|
21
57
|
|
22
|
-
def add_deny(
|
23
|
-
@
|
58
|
+
def add_deny(options)
|
59
|
+
@entries << DenyEntry.new(options)
|
24
60
|
return self
|
25
61
|
end
|
26
62
|
|
@@ -31,8 +67,8 @@ class WebHookDispatcher::Acl
|
|
31
67
|
|
32
68
|
this = self
|
33
69
|
(class << obj; self; end).class_eval {
|
34
|
-
define_method(:allow) { |
|
35
|
-
define_method(:deny) { |
|
70
|
+
define_method(:allow) { |options| this.add_allow(options) }
|
71
|
+
define_method(:deny) { |options| this.add_deny(options) }
|
36
72
|
private :allow, :deny
|
37
73
|
}
|
38
74
|
|
@@ -41,44 +77,18 @@ class WebHookDispatcher::Acl
|
|
41
77
|
return self
|
42
78
|
end
|
43
79
|
|
44
|
-
def allow?(
|
45
|
-
|
46
|
-
result = record.value if record.include?(ipaddr)
|
47
|
-
result
|
48
|
-
}
|
49
|
-
end
|
50
|
-
|
51
|
-
def deny?(ipaddr)
|
52
|
-
return !self.allow?(ipaddr)
|
53
|
-
end
|
54
|
-
|
55
|
-
class RecordBase
|
56
|
-
def initialize(ipaddr)
|
57
|
-
@ipaddr =
|
58
|
-
case ipaddr
|
59
|
-
when :all then IPAddr.new("0.0.0.0/0")
|
60
|
-
when String then IPAddr.new(ipaddr)
|
61
|
-
when IPAddr then ipaddr
|
62
|
-
else raise(ArgumentError, "invalid IP address")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
attr_reader :ipaddr
|
67
|
-
|
68
|
-
def include?(ipaddr)
|
69
|
-
return @ipaddr.include?(ipaddr)
|
70
|
-
end
|
71
|
-
end
|
80
|
+
def allow?(addr_or_name, port = nil)
|
81
|
+
targets = self.class.create_matching_targets(addr_or_name, port)
|
72
82
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
83
|
+
return targets.all? { |taddr, tname, tport|
|
84
|
+
@entries.inject(true) { |result, entry|
|
85
|
+
result = entry.value if entry.match?(taddr, tname, tport)
|
86
|
+
result
|
87
|
+
}
|
88
|
+
}
|
77
89
|
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
return false
|
82
|
-
end
|
91
|
+
def deny?(addr_or_name, port = nil)
|
92
|
+
return !self.allow?(addr_or_name, port)
|
83
93
|
end
|
84
94
|
end
|
@@ -1,3 +1,110 @@
|
|
1
1
|
|
2
|
+
require "net/http"
|
3
|
+
require "webhook-dispatcher/version"
|
4
|
+
require "webhook-dispatcher/acl"
|
5
|
+
require "webhook-dispatcher/request/get"
|
6
|
+
require "webhook-dispatcher/request/head"
|
7
|
+
require "webhook-dispatcher/request/post"
|
8
|
+
require "webhook-dispatcher/response"
|
9
|
+
|
2
10
|
class WebHookDispatcher
|
11
|
+
def initialize(options = {})
|
12
|
+
options = options.dup
|
13
|
+
@open_timeout = options.delete(:open_timeout) || self.class.open_timeout || self.class.default_open_timeout
|
14
|
+
@read_timeout = options.delete(:read_timeout) || self.class.read_timeout || self.class.default_read_timeout
|
15
|
+
@user_agent = options.delete(:user_agent) || self.class.user_agent || self.class.default_user_agent
|
16
|
+
@acl = options.delete(:acl) || self.class.acl || self.class.default_acl
|
17
|
+
raise(ArgumentError, "invalid parameter") unless options.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
@open_timeout = nil
|
22
|
+
@read_timeout = nil
|
23
|
+
@user_agent = nil
|
24
|
+
@acl = nil
|
25
|
+
|
26
|
+
attr_accessor :open_timeout, :read_timeout, :user_agent, :acl
|
27
|
+
|
28
|
+
define_method(:default_open_timeout) { 10 }
|
29
|
+
define_method(:default_read_timeout) { 10 }
|
30
|
+
define_method(:default_user_agent) { "webhook-dispatcher #{self::VERSION}" }
|
31
|
+
define_method(:default_acl) { Acl.new }
|
32
|
+
|
33
|
+
def acl_with(&block)
|
34
|
+
self.acl = Acl.with(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :open_timeout, :read_timeout, :user_agent, :acl
|
39
|
+
|
40
|
+
def acl_with(&block)
|
41
|
+
self.acl = Acl.with(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def request(request)
|
45
|
+
http_conn = request.create_http_connector
|
46
|
+
http_req = request.create_http_request
|
47
|
+
setup_http_connector(http_conn)
|
48
|
+
setup_http_request(http_req)
|
49
|
+
|
50
|
+
begin
|
51
|
+
if @acl.deny?(http_conn.address, http_conn.port)
|
52
|
+
return Response.new(
|
53
|
+
:status => :denied,
|
54
|
+
:message => "denied.")
|
55
|
+
end
|
56
|
+
|
57
|
+
http_res = http_conn.start { http_conn.request(http_req) }
|
58
|
+
|
59
|
+
return Response.new(
|
60
|
+
:status => (http_res.kind_of?(Net::HTTPSuccess) ? :success : :failure),
|
61
|
+
:http_code => http_res.code.to_i,
|
62
|
+
:message => http_res.message)
|
63
|
+
rescue TimeoutError => e
|
64
|
+
return Response.new(
|
65
|
+
:status => :timeout,
|
66
|
+
:message => "timeout.",
|
67
|
+
:exception => e)
|
68
|
+
rescue Errno::ECONNREFUSED => e
|
69
|
+
return Response.new(
|
70
|
+
:status => :refused,
|
71
|
+
:message => "connection refused.",
|
72
|
+
:exception => e)
|
73
|
+
rescue Errno::ECONNRESET => e
|
74
|
+
return Response.new(
|
75
|
+
:status => :reset,
|
76
|
+
:message => "connection reset by peer.",
|
77
|
+
:exception => e)
|
78
|
+
rescue => e
|
79
|
+
return Response.new(
|
80
|
+
:status => :error,
|
81
|
+
:message => "#{e.class}: #{e.message}",
|
82
|
+
:exception => e)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def get(uri)
|
87
|
+
return self.request(Request::Get.new(uri))
|
88
|
+
end
|
89
|
+
|
90
|
+
def head(uri)
|
91
|
+
return self.request(Request::Head.new(uri))
|
92
|
+
end
|
93
|
+
|
94
|
+
def post(uri, body = nil)
|
95
|
+
return self.request(Request::Post.new(uri, body))
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def setup_http_connector(http_conn)
|
101
|
+
http_conn.open_timeout = self.open_timeout
|
102
|
+
http_conn.read_timeout = self.read_timeout
|
103
|
+
return http_conn
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup_http_request(http_request)
|
107
|
+
http_request["User-Agent"] = self.user_agent
|
108
|
+
http_request
|
109
|
+
end
|
3
110
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
require "uri"
|
3
|
+
require "webhook-dispatcher/core"
|
3
4
|
|
4
5
|
class WebHookDispatcher
|
5
6
|
module Request
|
@@ -12,4 +13,12 @@ class WebHookDispatcher::Request::Base
|
|
12
13
|
end
|
13
14
|
|
14
15
|
attr_accessor :uri
|
16
|
+
|
17
|
+
def create_http_connector
|
18
|
+
return Net::HTTP.new(self.uri.host, self.uri.port)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_http_request
|
22
|
+
raise(NotImplementedError)
|
23
|
+
end
|
15
24
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require "webhook-dispatcher/request/base"
|
3
|
+
|
4
|
+
class WebHookDispatcher::Request::Post < WebHookDispatcher::Request::Base
|
5
|
+
def initialize(uri, body = nil)
|
6
|
+
super(uri)
|
7
|
+
@body = body
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :body
|
11
|
+
|
12
|
+
def create_http_request
|
13
|
+
req = Net::HTTP::Post.new(self.uri.request_uri)
|
14
|
+
req.body = self.body
|
15
|
+
return req
|
16
|
+
end
|
17
|
+
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
class WebHookDispatcher::Response
|
3
3
|
def initialize(options = {})
|
4
4
|
options = options.dup
|
5
|
-
@success = (options.delete(:success) == true)
|
6
5
|
@status = options.delete(:status) || :unknown
|
7
6
|
@http_code = options.delete(:http_code) || nil
|
8
7
|
@message = options.delete(:message) || nil
|
@@ -10,9 +9,9 @@ class WebHookDispatcher::Response
|
|
10
9
|
raise(ArgumentError, "invalid parameter") unless options.empty?
|
11
10
|
end
|
12
11
|
|
13
|
-
attr_reader :
|
12
|
+
attr_reader :status, :http_code, :message, :exception
|
14
13
|
|
15
14
|
def success?
|
16
|
-
return self.success
|
15
|
+
return (self.status == :success)
|
17
16
|
end
|
18
17
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
3
|
+
require "webhook-dispatcher/acl/allow_entry"
|
4
|
+
|
5
|
+
class AllowEntryTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@klass = WebHookDispatcher::Acl::AllowEntry
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# インスタンスメソッド
|
12
|
+
#
|
13
|
+
|
14
|
+
def test_value
|
15
|
+
assert_equal(true, @klass.new.value)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
3
|
+
require "webhook-dispatcher/acl/deny_entry"
|
4
|
+
|
5
|
+
class DenyEntryTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@klass = WebHookDispatcher::Acl::DenyEntry
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_value
|
11
|
+
assert_equal(false, @klass.new.value)
|
12
|
+
end
|
13
|
+
end
|