nayutaya-webhook-dispatcher 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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/*_test.rb")
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 = Dir.glob("**/*").select { |s| File.file?(s) }
23
- test_files = Dir.glob("test/**").select { |s| File.file?(s) }
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
- WebHookPublisher.open_timeout = 5 # sec
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
- wp = WebHookPublisher.new
12
- wp.open_timeout = 5 # sec
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
- acl.clear
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
- res.success? #=> true/false
44
- res.status #=> :timeout
45
- res.status_code #=> 200
46
- res.status_message #=> OK
47
- res.inner_exception #=> #<RuntimeError>
17
+ =end
@@ -0,0 +1,12 @@
1
+
2
+ require "webhook-dispatcher/acl/entry_base"
3
+
4
+ class WebHookDispatcher
5
+ class Acl
6
+ class AllowEntry < EntryBase
7
+ def value
8
+ return true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require "webhook-dispatcher/acl/entry_base"
3
+
4
+ class WebHookDispatcher
5
+ class Acl
6
+ class DenyEntry < EntryBase
7
+ def value
8
+ return false
9
+ end
10
+ end
11
+ end
12
+ 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
- @records = []
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 @records.size
50
+ return @entries.size
15
51
  end
16
52
 
17
- def add_allow(ipaddr)
18
- @records << AllowRecord.new(ipaddr)
53
+ def add_allow(options)
54
+ @entries << AllowEntry.new(options)
19
55
  return self
20
56
  end
21
57
 
22
- def add_deny(ipaddr)
23
- @records << DenyRecord.new(ipaddr)
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) { |ipaddr| this.add_allow(ipaddr) }
35
- define_method(:deny) { |ipaddr| this.add_deny(ipaddr) }
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?(ipaddr)
45
- return @records.inject(true) { |result, record|
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
- class AllowRecord < RecordBase
74
- def value
75
- return true
76
- end
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
- class DenyRecord < RecordBase
80
- def value
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
@@ -5,4 +5,8 @@ class WebHookDispatcher::Request::Get < WebHookDispatcher::Request::Base
5
5
  def initialize(uri)
6
6
  super(uri)
7
7
  end
8
+
9
+ def create_http_request
10
+ return Net::HTTP::Get.new(self.uri.request_uri)
11
+ end
8
12
  end
@@ -0,0 +1,12 @@
1
+
2
+ require "webhook-dispatcher/request/base"
3
+
4
+ class WebHookDispatcher::Request::Head < WebHookDispatcher::Request::Base
5
+ def initialize(uri)
6
+ super(uri)
7
+ end
8
+
9
+ def create_http_request
10
+ return Net::HTTP::Head.new(self.uri.request_uri)
11
+ end
12
+ 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 :success, :status, :http_code, :message, :exception
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
@@ -1,4 +1,4 @@
1
1
 
2
2
  class WebHookDispatcher
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  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