em-proxy 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.