em-proxy 0.1.5 → 0.1.6

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.
@@ -7,23 +7,26 @@ PATH
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
- addressable (2.2.2)
11
- ansi (1.2.2)
12
- diff-lcs (1.1.2)
13
- em-http-request (0.3.0)
14
- addressable (>= 2.0.0)
15
- escape_utils
16
- eventmachine (>= 0.12.9)
17
- escape_utils (0.1.9)
18
- eventmachine (0.12.11)
19
- rspec (2.4.0)
20
- rspec-core (~> 2.4.0)
21
- rspec-expectations (~> 2.4.0)
22
- rspec-mocks (~> 2.4.0)
23
- rspec-core (2.4.0)
24
- rspec-expectations (2.4.0)
10
+ addressable (2.2.6)
11
+ ansi (1.4.0)
12
+ diff-lcs (1.1.3)
13
+ em-http-request (1.0.0)
14
+ addressable (>= 2.2.3)
15
+ em-socksify
16
+ eventmachine (>= 1.0.0.beta.3)
17
+ http_parser.rb (>= 0.5.2)
18
+ em-socksify (0.1.0)
19
+ eventmachine
20
+ eventmachine (1.0.0.beta.4)
21
+ http_parser.rb (0.5.3)
22
+ rspec (2.7.0)
23
+ rspec-core (~> 2.7.0)
24
+ rspec-expectations (~> 2.7.0)
25
+ rspec-mocks (~> 2.7.0)
26
+ rspec-core (2.7.1)
27
+ rspec-expectations (2.7.0)
25
28
  diff-lcs (~> 1.1.2)
26
- rspec-mocks (2.4.0)
29
+ rspec-mocks (2.7.0)
27
30
 
28
31
  PLATFORMS
29
32
  ruby
@@ -32,5 +35,4 @@ DEPENDENCIES
32
35
  ansi
33
36
  em-http-request
34
37
  em-proxy!
35
- eventmachine
36
38
  rspec
@@ -0,0 +1,67 @@
1
+ # EM-Proxy
2
+
3
+ EventMachine Proxy DSL for writing high-performance transparent / intercepting proxies in Ruby.
4
+
5
+ - EngineYard tutorial: [Load testing your environment using em-proxy](http://docs.engineyard.com/em-proxy.html)
6
+ - [Slides from RailsConf 2009](http://bit.ly/D7oWB)
7
+ - [GoGaRuCo notes & Slides](http://www.igvita.com/2009/04/20/ruby-proxies-for-scale-and-monitoring/)
8
+
9
+ ## Getting started
10
+
11
+ $> gem install em-proxy
12
+ $> em-proxy
13
+ Usage: em-proxy [options]
14
+ -l, --listen [PORT] Port to listen on
15
+ -d, --duplex [host:port, ...] List of backends to duplex data to
16
+ -r, --relay [hostname:port] Relay endpoint: hostname:port
17
+ -v, --verbose Run in debug mode
18
+
19
+ $> em-proxy -l 8080 -r localhost:8081 -d localhost:8082,localhost:8083 -v
20
+
21
+ The above will start em-proxy on port 8080, relay and respond with data from port 8081, and also (optional) duplicate all traffic to ports 8082 and 8083 (and discard their responses).
22
+
23
+
24
+ ## Simple port forwarding proxy
25
+
26
+ ```ruby
27
+ Proxy.start(:host => "0.0.0.0", :port => 80, :debug => true) do |conn|
28
+ conn.server :srv, :host => "127.0.0.1", :port => 81
29
+
30
+ # modify / process request stream
31
+ conn.on_data do |data|
32
+ p [:on_data, data]
33
+ data
34
+ end
35
+
36
+ # modify / process response stream
37
+ conn.on_response do |backend, resp|
38
+ p [:on_response, backend, resp]
39
+ resp
40
+ end
41
+
42
+ # termination logic
43
+ conn.on_finish do |backend, name|
44
+ p [:on_finish, name]
45
+
46
+ # terminate connection (in duplex mode, you can terminate when prod is done)
47
+ unbind if backend == :srv
48
+ end
49
+ end
50
+ ```
51
+
52
+ For more examples see the /examples directory.
53
+
54
+ - SMTP Spam Filtering
55
+ - Duplicating traffic
56
+ - Selective forwarding
57
+ - Beanstalkd interceptor
58
+ - etc.
59
+
60
+ A schema-free MySQL proof of concept, via an EM-Proxy server:
61
+
62
+ - http://www.igvita.com/2010/03/01/schema-free-mysql-vs-nosql/
63
+ - Code in examples/schemaless-mysql
64
+
65
+ ## License
66
+
67
+ The MIT License - Copyright (c) 2010 Ilya Grigorik
data/Rakefile CHANGED
@@ -1,4 +1,10 @@
1
1
  require 'bundler'
2
-
3
- Bundler.setup
4
2
  Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc "Run all RSpec tests"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
10
+ task :test => [:spec]
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "em-proxy"
6
- s.version = "0.1.5"
6
+ s.version = "0.1.6"
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Ilya Grigorik"]
9
9
  s.email = ["ilya@igvita.com"]
@@ -22,4 +22,4 @@ Gem::Specification.new do |s|
22
22
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
23
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
24
  s.require_paths = ["lib"]
25
- end
25
+ end
@@ -1,4 +1,6 @@
1
- require 'lib/em-proxy'
1
+ $:<< '../lib' << 'lib'
2
+
3
+ require 'em-proxy'
2
4
  require 'ansi/code'
3
5
  require 'uri'
4
6
 
@@ -0,0 +1,47 @@
1
+ require 'em-proxy'
2
+ require 'http/parser' # gem install http_parser.rb
3
+ require 'uuid' # gem install uuid
4
+
5
+ # > ruby em-proxy-http.rb
6
+ # > curl --proxy localhost:9889 www.google.com
7
+
8
+ host = "0.0.0.0"
9
+ port = 9889
10
+ puts "listening on #{host}:#{port}..."
11
+
12
+ Proxy.start(:host => host, :port => port) do |conn|
13
+
14
+ @p = Http::Parser.new
15
+ @p.on_headers_complete = proc do |h|
16
+ session = UUID.generate
17
+ puts "New session: #{session} (#{h.inspect})"
18
+
19
+ host, port = h['Host'].split(':')
20
+ conn.server session, :host => host, :port => (port || 80)
21
+ conn.relay_to_servers @buffer
22
+
23
+ @buffer.clear
24
+ end
25
+
26
+ @buffer = ''
27
+
28
+ conn.on_connect do |data,b|
29
+ puts [:on_connect, data, b].inspect
30
+ end
31
+
32
+ conn.on_data do |data|
33
+ @buffer << data
34
+ @p << data
35
+
36
+ data
37
+ end
38
+
39
+ conn.on_response do |backend, resp|
40
+ puts [:on_response, backend, resp].inspect
41
+ resp
42
+ end
43
+
44
+ conn.on_finish do |backend, name|
45
+ puts [:on_finish, name].inspect
46
+ end
47
+ end
@@ -1,12 +1,11 @@
1
1
  module EventMachine
2
2
  module ProxyServer
3
3
  class Backend < EventMachine::Connection
4
- attr_accessor :plexer, :data, :name, :debug
4
+ attr_accessor :plexer, :name, :debug
5
5
 
6
6
  def initialize(debug = false)
7
7
  @debug = debug
8
8
  @connected = EM::DefaultDeferrable.new
9
- @data = []
10
9
  end
11
10
 
12
11
  def connection_completed
@@ -17,7 +16,6 @@ module EventMachine
17
16
 
18
17
  def receive_data(data)
19
18
  debug [@name, data]
20
- @data.push data
21
19
  @plexer.relay_from_backend(@name, data)
22
20
  end
23
21
 
@@ -1,114 +1,114 @@
1
- module EventMachine
2
- module ProxyServer
3
- class Connection < EventMachine::Connection
4
- attr_accessor :debug
5
-
6
- ##### Proxy Methods
7
- def on_data(&blk); @on_data = blk; end
8
- def on_response(&blk); @on_response = blk; end
9
- def on_finish(&blk); @on_finish = blk; end
10
- def on_connect(&blk); @on_connect = blk; end
11
-
12
- ##### EventMachine
13
- def initialize(options)
14
- @debug = options[:debug] || false
15
- @servers = {}
16
- end
17
-
18
- def receive_data(data)
19
- debug [:connection, data]
20
- processed = @on_data.call(data) if @on_data
21
-
22
- return if processed == :async or processed.nil?
23
- relay_to_servers(processed)
24
- end
25
-
26
- def relay_to_servers(processed)
27
- if processed.is_a? Array
28
- data, servers = *processed
29
-
30
- # guard for "unbound" servers
31
- servers = servers.collect {|s| @servers[s]}.compact
32
- else
33
- data = processed
34
- servers ||= @servers.values.compact
35
- end
36
-
37
- servers.each do |s|
38
- s.send_data data unless data.nil?
39
- end
40
- end
41
-
42
- #
43
- # initialize connections to backend servers
44
- #
45
- def server(name, opts)
46
- srv = EventMachine::connect(opts[:host], opts[:port], EventMachine::ProxyServer::Backend, @debug) do |c|
47
- c.name = name
48
- c.plexer = self
49
- c.proxy_incoming_to(self, 10240) if opts[:relay_server]
50
- end
51
- self.proxy_incoming_to(srv, 10240) if opts[:relay_client]
52
-
53
- @servers[name] = srv
54
- end
55
-
56
- #
57
- # [ip, port] of the connected client
58
- #
59
- def peer
60
- peername = get_peername
61
- @peer ||= peername ? Socket.unpack_sockaddr_in(peername).reverse : nil
62
- end
63
-
64
- #
65
- # relay data from backend server to client
66
- #
67
- def relay_from_backend(name, data)
68
- debug [:relay_from_backend, name, data]
69
-
70
- data = @on_response.call(name, data) if @on_response
71
- send_data data unless data.nil?
72
- end
73
-
74
- def connected(name)
75
- debug [:connected]
76
- @on_connect.call(name) if @on_connect
77
- end
78
-
79
- def unbind
80
- debug [:unbind, :connection]
81
-
82
- # terminate any unfinished connections
83
- @servers.values.compact.each do |s|
84
- s.close_connection
85
- end
86
- end
87
-
88
- def unbind_backend(name)
89
- debug [:unbind_backend, name]
90
- @servers[name] = nil
91
-
92
- # if all connections are terminated downstream, then notify client
93
- close_connection_after_writing if @servers.values.compact.size.zero?
94
-
95
- if @on_finish
96
- @on_finish.call(name)
97
-
98
- # not sure if this is required
99
- # @on_finish.call(:done) if @servers.values.compact.size.zero?
100
- end
101
- end
102
-
103
- private
104
-
105
- def debug(*data)
106
- if @debug
107
- require 'pp'
108
- pp data
109
- puts
110
- end
111
- end
112
- end
113
- end
114
- end
1
+ module EventMachine
2
+ module ProxyServer
3
+ class Connection < EventMachine::Connection
4
+ attr_accessor :debug
5
+
6
+ ##### Proxy Methods
7
+ def on_data(&blk); @on_data = blk; end
8
+ def on_response(&blk); @on_response = blk; end
9
+ def on_finish(&blk); @on_finish = blk; end
10
+ def on_connect(&blk); @on_connect = blk; end
11
+
12
+ ##### EventMachine
13
+ def initialize(options)
14
+ @debug = options[:debug] || false
15
+ @servers = {}
16
+ end
17
+
18
+ def receive_data(data)
19
+ debug [:connection, data]
20
+ processed = @on_data.call(data) if @on_data
21
+
22
+ return if processed == :async or processed.nil?
23
+ relay_to_servers(processed)
24
+ end
25
+
26
+ def relay_to_servers(processed)
27
+ if processed.is_a? Array
28
+ data, servers = *processed
29
+
30
+ # guard for "unbound" servers
31
+ servers = servers.collect {|s| @servers[s]}.compact
32
+ else
33
+ data = processed
34
+ servers ||= @servers.values.compact
35
+ end
36
+
37
+ servers.each do |s|
38
+ s.send_data data unless data.nil?
39
+ end
40
+ end
41
+
42
+ #
43
+ # initialize connections to backend servers
44
+ #
45
+ def server(name, opts)
46
+ srv = EventMachine::connect(opts[:host], opts[:port], EventMachine::ProxyServer::Backend, @debug) do |c|
47
+ c.name = name
48
+ c.plexer = self
49
+ c.proxy_incoming_to(self, 10240) if opts[:relay_server]
50
+ end
51
+ self.proxy_incoming_to(srv, 10240) if opts[:relay_client]
52
+
53
+ @servers[name] = srv
54
+ end
55
+
56
+ #
57
+ # [ip, port] of the connected client
58
+ #
59
+ def peer
60
+ peername = get_peername
61
+ @peer ||= peername ? Socket.unpack_sockaddr_in(peername).reverse : nil
62
+ end
63
+
64
+ #
65
+ # relay data from backend server to client
66
+ #
67
+ def relay_from_backend(name, data)
68
+ debug [:relay_from_backend, name, data]
69
+
70
+ data = @on_response.call(name, data) if @on_response
71
+ send_data data unless data.nil?
72
+ end
73
+
74
+ def connected(name)
75
+ debug [:connected]
76
+ @on_connect.call(name) if @on_connect
77
+ end
78
+
79
+ def unbind
80
+ debug [:unbind, :connection]
81
+
82
+ # terminate any unfinished connections
83
+ @servers.values.compact.each do |s|
84
+ s.close_connection
85
+ end
86
+ end
87
+
88
+ def unbind_backend(name)
89
+ debug [:unbind_backend, name]
90
+ @servers[name] = nil
91
+ close = :close
92
+
93
+ if @on_finish
94
+ close = @on_finish.call(name)
95
+ end
96
+
97
+ # if all connections are terminated downstream, then notify client
98
+ if @servers.values.compact.size.zero? and close != :keep
99
+ close_connection_after_writing
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def debug(*data)
106
+ if @debug
107
+ require 'pp'
108
+ pp data
109
+ puts
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,5 +1,5 @@
1
- require 'spec/helper'
2
- require 'examples/balancing'
1
+ require 'helper'
2
+ require File.join(File.dirname(__FILE__), '../', 'examples/balancing')
3
3
 
4
4
  describe BalancingProxy do
5
5
 
@@ -1,6 +1,5 @@
1
- require 'rubygems'
2
- require 'rspec'
3
- require 'pp'
1
+ require 'bundler/setup'
4
2
  require 'em-http'
3
+ require 'pp'
5
4
 
6
- require 'lib/em-proxy'
5
+ require 'em-proxy'
@@ -1,4 +1,4 @@
1
- require 'spec/helper'
1
+ require 'helper'
2
2
 
3
3
  describe Proxy do
4
4
 
@@ -68,7 +68,7 @@ describe Proxy do
68
68
  end
69
69
 
70
70
  Proxy.start(:host => "0.0.0.0", :port => 8080) do |conn|
71
- conn.server :goog, :host => "google.com", :port => 80
71
+ conn.server :bing, :host => "google.com", :port => 80
72
72
  conn.server :yhoo, :host => "yahoo.com", :port => 80
73
73
  conn.on_data { |data| data }
74
74
 
metadata CHANGED
@@ -1,89 +1,73 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: em-proxy
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 5
9
- version: 0.1.5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ilya Grigorik
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-01-16 00:00:00 -05:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2011-12-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: eventmachine
22
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2156629200 !ruby/object:Gem::Requirement
23
17
  none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
30
22
  type: :runtime
31
23
  prerelease: false
32
- version_requirements: *id001
33
- - !ruby/object:Gem::Dependency
24
+ version_requirements: *2156629200
25
+ - !ruby/object:Gem::Dependency
34
26
  name: rspec
35
- requirement: &id002 !ruby/object:Gem::Requirement
27
+ requirement: &2156628760 !ruby/object:Gem::Requirement
36
28
  none: false
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- segments:
41
- - 0
42
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
43
33
  type: :development
44
34
  prerelease: false
45
- version_requirements: *id002
46
- - !ruby/object:Gem::Dependency
35
+ version_requirements: *2156628760
36
+ - !ruby/object:Gem::Dependency
47
37
  name: em-http-request
48
- requirement: &id003 !ruby/object:Gem::Requirement
38
+ requirement: &2156628320 !ruby/object:Gem::Requirement
49
39
  none: false
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- segments:
54
- - 0
55
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
56
44
  type: :development
57
45
  prerelease: false
58
- version_requirements: *id003
59
- - !ruby/object:Gem::Dependency
46
+ version_requirements: *2156628320
47
+ - !ruby/object:Gem::Dependency
60
48
  name: ansi
61
- requirement: &id004 !ruby/object:Gem::Requirement
49
+ requirement: &2156627720 !ruby/object:Gem::Requirement
62
50
  none: false
63
- requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- segments:
67
- - 0
68
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
69
55
  type: :development
70
56
  prerelease: false
71
- version_requirements: *id004
57
+ version_requirements: *2156627720
72
58
  description: EventMachine Proxy DSL
73
- email:
59
+ email:
74
60
  - ilya@igvita.com
75
- executables:
61
+ executables:
76
62
  - em-proxy
77
63
  extensions: []
78
-
79
64
  extra_rdoc_files: []
80
-
81
- files:
65
+ files:
82
66
  - .gitignore
83
67
  - .rspec
84
68
  - Gemfile
85
69
  - Gemfile.lock
86
- - README.rdoc
70
+ - README.md
87
71
  - Rakefile
88
72
  - bin/em-proxy
89
73
  - em-proxy.gemspec
@@ -92,6 +76,7 @@ files:
92
76
  - examples/balancing.rb
93
77
  - examples/beanstalkd_interceptor.rb
94
78
  - examples/duplex.rb
79
+ - examples/http_proxy.rb
95
80
  - examples/line_interceptor.rb
96
81
  - examples/port_forward.rb
97
82
  - examples/relay_port_forward.rb
@@ -107,41 +92,32 @@ files:
107
92
  - spec/balancing_spec.rb
108
93
  - spec/helper.rb
109
94
  - spec/proxy_spec.rb
110
- has_rdoc: true
111
95
  homepage: http://github.com/igrigorik/em-proxy
112
96
  licenses: []
113
-
114
97
  post_install_message:
115
98
  rdoc_options: []
116
-
117
- require_paths:
99
+ require_paths:
118
100
  - lib
119
- required_ruby_version: !ruby/object:Gem::Requirement
101
+ required_ruby_version: !ruby/object:Gem::Requirement
120
102
  none: false
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- hash: -2051780992673318158
125
- segments:
126
- - 0
127
- version: "0"
128
- required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
108
  none: false
130
- requirements:
131
- - - ">="
132
- - !ruby/object:Gem::Version
133
- hash: -2051780992673318158
134
- segments:
135
- - 0
136
- version: "0"
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
137
113
  requirements: []
138
-
139
114
  rubyforge_project: em-proxy
140
- rubygems_version: 1.3.7
115
+ rubygems_version: 1.8.10
141
116
  signing_key:
142
117
  specification_version: 3
143
118
  summary: EventMachine Proxy DSL
144
- test_files:
119
+ test_files:
145
120
  - spec/balancing_spec.rb
146
121
  - spec/helper.rb
147
122
  - spec/proxy_spec.rb
123
+ has_rdoc:
@@ -1,82 +0,0 @@
1
- = EM-Proxy
2
-
3
- EventMachine Proxy DSL for writing high-performance transparent / intercepting proxies in Ruby.
4
-
5
- - Slides from RailsConf 2009: http://bit.ly/D7oWB
6
- - GoGaRuCo notes & Slides: http://www.igvita.com/2009/04/20/ruby-proxies-for-scale-and-monitoring/
7
-
8
- == Getting started
9
-
10
- $> gem install em-proxy
11
- $> em-proxy
12
- Usage: em-proxy [options]
13
- -l, --listen [PORT] Port to listen on
14
- -d, --duplex [host:port, ...] List of backends to duplex data to
15
- -r, --relay [hostname:port] Relay endpoint: hostname:port
16
- -v, --verbose Run in debug mode
17
-
18
- $> em-proxy -l 8080 -r localhost:8081 -d localhost:8082,localhost:8083 -v
19
-
20
- The above will start em-proxy on port 8080, relay and respond with data from port 8081, and also (optional) duplicate all traffic to ports 8082 and 8083 (and discard their responses).
21
-
22
- == Simple port forwarding proxy
23
-
24
- Proxy.start(:host => "0.0.0.0", :port => 80, :debug => true) do |conn|
25
- conn.server :srv, :host => "127.0.0.1", :port => 81
26
-
27
- # modify / process request stream
28
- conn.on_data do |data|
29
- p [:on_data, data]
30
- data
31
- end
32
-
33
- # modify / process response stream
34
- conn.on_response do |backend, resp|
35
- p [:on_response, backend, resp]
36
- resp
37
- end
38
-
39
- # termination logic
40
- conn.on_finish do |backend, name|
41
- p [:on_finish, name]
42
-
43
- # terminate connection (in duplex mode, you can terminate when prod is done)
44
- unbind if backend == :srv
45
- end
46
- end
47
-
48
- For more examples see the /examples directory.
49
- - SMTP Spam Filtering
50
- - Duplicating traffic
51
- - Selective forwarding
52
- - Beanstalkd interceptor
53
- - etc.
54
-
55
- A schema-free MySQL proof of concept, via an EM-Proxy server:
56
- - http://www.igvita.com/2010/03/01/schema-free-mysql-vs-nosql/
57
- - Code in examples/schemaless-mysql
58
-
59
- == License
60
-
61
- (The MIT License)
62
-
63
- Copyright (c) 2010 Ilya Grigorik
64
-
65
- Permission is hereby granted, free of charge, to any person obtaining
66
- a copy of this software and associated documentation files (the
67
- 'Software'), to deal in the Software without restriction, including
68
- without limitation the rights to use, copy, modify, merge, publish,
69
- distribute, sublicense, and/or sell copies of the Software, and to
70
- permit persons to whom the Software is furnished to do so, subject to
71
- the following conditions:
72
-
73
- The above copyright notice and this permission notice shall be
74
- included in all copies or substantial portions of the Software.
75
-
76
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
77
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
80
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
81
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
82
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.