middle_squid 1.0

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.md +227 -0
  7. data/Rakefile +7 -0
  8. data/bin/middle_squid +7 -0
  9. data/lib/middle_squid/actions.rb +77 -0
  10. data/lib/middle_squid/adapter.rb +54 -0
  11. data/lib/middle_squid/adapters/squid.rb +57 -0
  12. data/lib/middle_squid/backends/keyboard.rb +31 -0
  13. data/lib/middle_squid/backends/thin.rb +14 -0
  14. data/lib/middle_squid/blacklist.rb +67 -0
  15. data/lib/middle_squid/builder.rb +159 -0
  16. data/lib/middle_squid/cli.rb +119 -0
  17. data/lib/middle_squid/core_ext/hash.rb +29 -0
  18. data/lib/middle_squid/database.rb +47 -0
  19. data/lib/middle_squid/exceptions.rb +4 -0
  20. data/lib/middle_squid/helpers.rb +74 -0
  21. data/lib/middle_squid/indexer.rb +194 -0
  22. data/lib/middle_squid/runner.rb +37 -0
  23. data/lib/middle_squid/server.rb +84 -0
  24. data/lib/middle_squid/uri.rb +31 -0
  25. data/lib/middle_squid/version.rb +3 -0
  26. data/lib/middle_squid.rb +46 -0
  27. data/middle_squid.gemspec +37 -0
  28. data/middle_squid_wrapper.sh +4 -0
  29. data/test/helper.rb +26 -0
  30. data/test/resources/backslash/cat/list +1 -0
  31. data/test/resources/black/ads/domains +2 -0
  32. data/test/resources/black/ads/urls +1 -0
  33. data/test/resources/black/tracker/domains +2 -0
  34. data/test/resources/black/tracker/urls +2 -0
  35. data/test/resources/copy_of_duplicates/cat/copy_of_list +2 -0
  36. data/test/resources/copy_of_duplicates/cat/list +2 -0
  37. data/test/resources/copy_of_duplicates/copy_of_cat/copy_of_list +2 -0
  38. data/test/resources/copy_of_duplicates/copy_of_cat/list +2 -0
  39. data/test/resources/duplicates/cat/copy_of_list +2 -0
  40. data/test/resources/duplicates/cat/list +2 -0
  41. data/test/resources/duplicates/copy_of_cat/copy_of_list +2 -0
  42. data/test/resources/duplicates/copy_of_cat/list +2 -0
  43. data/test/resources/empty/cat/emptylist +0 -0
  44. data/test/resources/empty_path/cat/list +1 -0
  45. data/test/resources/expressions/cat/list +3 -0
  46. data/test/resources/gray/isp/domains +2 -0
  47. data/test/resources/gray/isp/urls +1 -0
  48. data/test/resources/gray/news/domains +2 -0
  49. data/test/resources/hello.rb +2 -0
  50. data/test/resources/invalid_byte/cat/list +1 -0
  51. data/test/resources/mixed/cat/list +2 -0
  52. data/test/resources/subdirectory/cat/ignore/.gitkeep +0 -0
  53. data/test/resources/trailing_space/cat/list +2 -0
  54. data/test/test_actions.rb +76 -0
  55. data/test/test_adapter.rb +61 -0
  56. data/test/test_blacklist.rb +189 -0
  57. data/test/test_builder.rb +89 -0
  58. data/test/test_cli.rb +105 -0
  59. data/test/test_database.rb +20 -0
  60. data/test/test_hash.rb +28 -0
  61. data/test/test_helper.rb +76 -0
  62. data/test/test_indexer.rb +457 -0
  63. data/test/test_keyboard.rb +79 -0
  64. data/test/test_runner.rb +56 -0
  65. data/test/test_server.rb +86 -0
  66. data/test/test_squid.rb +110 -0
  67. data/test/test_thin.rb +7 -0
  68. data/test/test_uri.rb +69 -0
  69. metadata +363 -0
@@ -0,0 +1,84 @@
1
+ module MiddleSquid
2
+ # Manages the internal HTTP server.
3
+ class Server
4
+ DEFAULT_HOST = '127.0.0.1'.freeze
5
+ DEFAULT_PORT = 0
6
+
7
+ TOKEN_TIMEOUT = 10
8
+
9
+ Thin::Logging.logger = Logger.new STDERR
10
+ Thin::Logging.level = Logger::WARN
11
+
12
+ # @return [String]
13
+ attr_reader :host
14
+
15
+ # @return [Fixnum]
16
+ attr_reader :port
17
+
18
+ def initialize
19
+ @tokens = {}
20
+ @thin = Thin::Server.new DEFAULT_HOST, DEFAULT_PORT, method(:handler),
21
+ :backend => Backends::Thin,
22
+ :signals => false
23
+ end
24
+
25
+ def start
26
+ @thin.start
27
+
28
+ sockname = EM.get_sockname @thin.backend.signature
29
+ @port, @host = Socket.unpack_sockaddr_in sockname
30
+ end
31
+
32
+ def stop
33
+ @thin.stop
34
+ @port = @host = nil
35
+ end
36
+
37
+ # Creates a temporary token.
38
+ #
39
+ # @param block [#call] called when the token is requested
40
+ # @return [String] random token
41
+ def token_for(block)
42
+ token = SecureRandom.uuid
43
+ @tokens[token] = block
44
+
45
+ EM.add_timer(TOKEN_TIMEOUT) {
46
+ @tokens.delete token
47
+ }
48
+
49
+ token
50
+ end
51
+
52
+ private
53
+ def handler(env)
54
+ callback = @tokens[env['PATH_INFO'][1..-1]]
55
+
56
+ return [
57
+ 404,
58
+ {'Content-Type' => 'text/plain'},
59
+ ['[MiddleSquid] Invalid Token']
60
+ ] unless callback
61
+
62
+ request = Rack::Request.new env
63
+ response = Thin::AsyncResponse.new env
64
+
65
+ Fiber.new {
66
+ retval = callback.call request, response
67
+
68
+ if retval.is_a?(Array) && retval.size == 3
69
+ status, headers, body = retval
70
+
71
+ headers.sanitize_headers!
72
+
73
+ response.status = status
74
+ response.headers.merge! headers
75
+ response.write body
76
+ end
77
+
78
+ response.done
79
+ }.resume
80
+
81
+ response.finish
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ module MiddleSquid
2
+ # Provides distinguishable host and path for the blacklists.
3
+ #
4
+ # @see http://rubydoc.info/gems/addressable/Addressable/URI
5
+ class URI < Addressable::URI
6
+ DOT = '.'.freeze
7
+ SLASH = '/'.freeze
8
+
9
+ # @return [String]
10
+ def cleanhost
11
+ clean = normalized_host.force_encoding Encoding::UTF_8
12
+ clean.sub! /\Awww\./, ''
13
+ clean.sub! /\.+\z/, ''
14
+ clean.insert 0, DOT
15
+ clean
16
+ end
17
+
18
+ # @return [String]
19
+ def cleanpath
20
+ dirty = normalized_path.force_encoding Encoding::UTF_8
21
+ p = Pathname.new(dirty).cleanpath
22
+
23
+ file = p.basename('.*').to_s.downcase
24
+ p = p.dirname if %w[index default].include? file
25
+
26
+ clean = p.to_s[1..-1]
27
+ clean << SLASH unless clean.empty?
28
+ clean
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module MiddleSquid
2
+ VERSION = '1.0'
3
+ end
@@ -0,0 +1,46 @@
1
+ require 'middle_squid/version'
2
+
3
+ require 'fiber'
4
+ require 'pathname'
5
+ require 'securerandom'
6
+
7
+ require 'addressable/uri'
8
+ require 'eventmachine'
9
+ require 'em-http-request'
10
+ require 'sqlite3'
11
+ require 'thin'
12
+ require 'thin/async'
13
+ require 'thor'
14
+
15
+ # Copyright (C) 2014 by Christian Fillion
16
+ #
17
+ # @see Builder Configuration syntax (DSL)
18
+ # @see Actions List of predefined actions
19
+ # @see Helpers List of predefined helpers
20
+ # @see Adapters Available adapters
21
+ module MiddleSquid
22
+ require 'middle_squid/actions'
23
+ require 'middle_squid/database'
24
+ require 'middle_squid/helpers'
25
+
26
+ module Adapters
27
+ require 'middle_squid/adapter'
28
+ require 'middle_squid/adapters/squid'
29
+ end
30
+
31
+ module Backends
32
+ require 'middle_squid/backends/keyboard'
33
+ require 'middle_squid/backends/thin'
34
+ end
35
+
36
+ require 'middle_squid/blacklist'
37
+ require 'middle_squid/builder'
38
+ require 'middle_squid/cli'
39
+ require 'middle_squid/exceptions'
40
+ require 'middle_squid/indexer'
41
+ require 'middle_squid/runner'
42
+ require 'middle_squid/server'
43
+ require 'middle_squid/uri'
44
+
45
+ require 'middle_squid/core_ext/hash'
46
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path '../lib', __FILE__
3
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
4
+
5
+ require 'middle_squid/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'middle_squid'
9
+ spec.version = MiddleSquid::VERSION
10
+ spec.authors = ['Christian Fillion']
11
+ spec.email = ['middle_squid@cfillion.tk']
12
+ spec.summary = 'A redirector, url mangler and webpage interceptor for the Squid HTTP proxy'
13
+ spec.homepage = 'https://github.com/cfillion/middle_squid'
14
+ spec.license = 'GPL-3.0+'
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_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'coveralls', '~> 0.7'
23
+ spec.add_development_dependency 'minitest', '~> 5.4'
24
+ spec.add_development_dependency 'rack-test', '~> 0.6'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'simplecov', '~> 0.9'
27
+ spec.add_development_dependency 'thin-async-test', '~> 1.0'
28
+ spec.add_development_dependency 'webmock', '~> 1.18'
29
+
30
+ spec.add_runtime_dependency 'addressable', '~> 2.3'
31
+ spec.add_runtime_dependency 'em-http-request', '~> 1.1'
32
+ spec.add_runtime_dependency 'eventmachine', '~> 1.0'
33
+ spec.add_runtime_dependency 'sqlite3', '~> 1.3'
34
+ spec.add_runtime_dependency 'thin', '~> 1.6'
35
+ spec.add_runtime_dependency 'thin_async', '~> 0.1'
36
+ spec.add_runtime_dependency 'thor', '~> 0.19'
37
+ end
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+
3
+ GEM_HOME=$(ruby -e 'puts Gem.user_dir')
4
+ exec $GEM_HOME/bin/middle_squid $*
data/test/helper.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ Coveralls::Output.silent = true
5
+
6
+ SimpleCov.formatters = [
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+
11
+ SimpleCov.start {
12
+ project_name 'MiddleSquid'
13
+ add_filter '/test/'
14
+
15
+ add_group 'Adapters', '/adapter'
16
+ add_group 'Backends', '/backend'
17
+ }
18
+
19
+ require 'minitest/autorun'
20
+ require 'rack/test'
21
+ require 'thin/async/test'
22
+ require 'webmock/minitest'
23
+
24
+ require 'middle_squid'
25
+
26
+ MiddleSquid::Database.setup ':memory:'
@@ -0,0 +1 @@
1
+ google.com\path\to\file
@@ -0,0 +1,2 @@
1
+ ads.google.com
2
+ doubleclick.net
@@ -0,0 +1 @@
1
+ google.com/adsense
@@ -0,0 +1,2 @@
1
+ xiti.com
2
+ google-analytics.com
@@ -0,0 +1,2 @@
1
+ feedproxy.google.com/~r/
2
+ cloudfront-labs.amazonaws.com/x.png
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
@@ -0,0 +1,2 @@
1
+ host.com
2
+ host.com/path
File without changes
@@ -0,0 +1 @@
1
+ host.com/
@@ -0,0 +1,3 @@
1
+ (/ads/|/ad/|/banner/|/sponsor/|/event.ng/|/Advertisement/|adverts/)
2
+ [0-9]
3
+ ^[0-9]+
@@ -0,0 +1,2 @@
1
+ 000webhost.com
2
+ comcast.com
@@ -0,0 +1 @@
1
+ www.telus.com/content/internet/
@@ -0,0 +1,2 @@
1
+ reddit.com
2
+ news.ycombinator.com
@@ -0,0 +1,2 @@
1
+ puts "hello #{self}"
2
+ run proc {}
@@ -0,0 +1 @@
1
+ host.com/path_with_�_invalid_byte
@@ -0,0 +1,2 @@
1
+ url.com/path
2
+ domain.com
File without changes
@@ -0,0 +1,2 @@
1
+ before.com/path
2
+ after.com/path
@@ -0,0 +1,76 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class TestActions < MiniTest::Test
4
+ FakeClass = Class.new { include MiddleSquid::Actions }
5
+
6
+ def setup
7
+ @obj = FakeClass.new
8
+ end
9
+
10
+ def test_accept
11
+ action = catch :action do
12
+ @obj.accept
13
+ end
14
+
15
+ assert_equal [:accept, {}], action
16
+ end
17
+
18
+ def test_redirect_301
19
+ action = catch :action do
20
+ @obj.redirect_to 'url'
21
+ end
22
+
23
+ assert_equal [:redirect, {
24
+ :status => 301,
25
+ :url => 'url'
26
+ }], action
27
+ end
28
+
29
+ def test_redirect_custom_status
30
+ action = catch :action do
31
+ @obj.redirect_to 'new url', status: 418
32
+ end
33
+
34
+ assert_equal [:redirect, {
35
+ :status => 418,
36
+ :url => 'new url'
37
+ }], action
38
+ end
39
+
40
+ def test_replace
41
+ action = catch :action do
42
+ @obj.replace_by 'http://duckduckgo.com/'
43
+ end
44
+
45
+ assert_equal [:replace, {
46
+ :url => 'http://duckduckgo.com/'
47
+ }], action
48
+ end
49
+
50
+ def test_intercept
51
+ mock = MiniTest::Mock.new
52
+ mock.expect :token_for, 'qwfpgjluy', [Proc]
53
+ mock.expect :host, '127.0.0.1'
54
+ mock.expect :port, 8901
55
+ @obj.define_singleton_method(:server) { mock }
56
+
57
+ action = catch :action do
58
+ EM.run {
59
+ @obj.intercept {}
60
+ EM.next_tick { EM.stop }
61
+ }
62
+ end
63
+
64
+ assert_equal [:replace, {
65
+ :url => 'http://127.0.0.1:8901/qwfpgjluy'
66
+ }], action
67
+
68
+ mock.verify
69
+ end
70
+
71
+ def test_intercept_requires_a_block
72
+ assert_raises ArgumentError do
73
+ @obj.intercept
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class TestAdapter < MiniTest::Test
4
+ def setup
5
+ @obj = MiddleSquid::Adapter.new
6
+ end
7
+
8
+ def test_handler
9
+ bag = []
10
+
11
+ @obj.handler = proc {|*args| bag << args }
12
+
13
+ @obj.define_singleton_method(:output) { |*args| bag << args }
14
+
15
+ @obj.handle 'http://test.com', ['hello', 'world']
16
+
17
+ uri, extras = bag.shift
18
+ assert_instance_of MiddleSquid::URI, uri
19
+ assert_equal 'http://test.com', uri.to_s
20
+ assert_equal ['hello', 'world'], extras
21
+
22
+ assert_equal [:accept, {}], bag.shift # default action
23
+
24
+ assert_empty bag
25
+ end
26
+
27
+ def test_default_output
28
+ assert_raises NotImplementedError do
29
+ @obj.output :accept, {}
30
+ end
31
+ end
32
+
33
+ def test_overridden_output
34
+ bag = []
35
+
36
+ @obj.handler = proc { throw :action, [:type, :options] }
37
+
38
+ @obj.define_singleton_method(:output) { |*args| bag << args }
39
+
40
+ @obj.handle 'http://test.com', []
41
+
42
+ assert_equal [:type, :options], bag.shift
43
+ assert_empty bag
44
+ end
45
+
46
+ def test_empty_url
47
+ error = assert_raises MiddleSquid::InvalidURIError do
48
+ @obj.handle nil, []
49
+ end
50
+
51
+ assert_equal "invalid URL received: ''", error.message
52
+ end
53
+
54
+ def test_invalid_url
55
+ error = assert_raises MiddleSquid::InvalidURIError do
56
+ @obj.handle 'hello world', []
57
+ end
58
+
59
+ assert_equal "invalid URL received: 'hello world'", error.message
60
+ end
61
+ end
@@ -0,0 +1,189 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class TestBlackList < MiniTest::Test
4
+ include MiddleSquid::Database
5
+
6
+ DOMAINS = %w{
7
+ .cfillion.tk
8
+ .duckduckgo.com
9
+ .sub.stackoverflow.com
10
+ .analytics.google.com
11
+ .anidb.net
12
+ }
13
+
14
+ URLS = [
15
+ %w[.google.com analytics/],
16
+ %w[.youtube.com watch/],
17
+ %w[.github.com user/],
18
+ %w[.host.net file.html/],
19
+ %w[.host.net index.html/],
20
+ ]
21
+
22
+ def setup
23
+ db.transaction
24
+
25
+ db.execute 'DELETE FROM domains'
26
+ db.execute 'DELETE FROM urls'
27
+
28
+ DOMAINS.each_with_index {|domain, index|
29
+ db.execute 'INSERT INTO domains (category, host) VALUES (?, ?)',
30
+ [index % 2 == 0 ? 'even' : 'odd', domain]
31
+ }
32
+
33
+ URLS.each_with_index {|url, index|
34
+ db.execute 'INSERT INTO urls (category, host, path) VALUES (?, ?, ?)',
35
+ [index % 2 == 0 ? 'even' : 'odd', *url]
36
+ }
37
+
38
+ db.commit
39
+ end
40
+
41
+ def test_category
42
+ bl = MiddleSquid::BlackList.new 'cat_name'
43
+ assert_equal 'cat_name', bl.category
44
+ end
45
+
46
+ def test_aliases_default
47
+ bl = MiddleSquid::BlackList.new 'cat_name'
48
+ assert_equal [], bl.aliases
49
+ end
50
+
51
+ def test_aliases_custom
52
+ bl = MiddleSquid::BlackList.new 'cat_name', aliases: ['name_cat']
53
+ assert_equal ['name_cat'], bl.aliases
54
+ end
55
+
56
+ def test_unmatch
57
+ uri = MiddleSquid::URI.parse('http://anidb.net/a9002')
58
+ odd = MiddleSquid::BlackList.new 'odd'
59
+
60
+ refute odd.include_domain? uri
61
+ refute odd.include_url? uri
62
+ refute odd.include? uri
63
+ end
64
+
65
+ def test_domain
66
+ uri = MiddleSquid::URI.parse('http://cfillion.tk/love-of-babble/')
67
+ even = MiddleSquid::BlackList.new 'even'
68
+
69
+ assert even.include_domain? uri
70
+ refute even.include_url? uri
71
+ assert even.include? uri
72
+ end
73
+
74
+ def test_domain_trailing_dots
75
+ uri = MiddleSquid::URI.parse('http://cfillion.tk...')
76
+ even = MiddleSquid::BlackList.new 'even'
77
+
78
+ assert even.include_domain? uri
79
+ assert even.include? uri
80
+ end
81
+
82
+ def test_domain_partial_match
83
+ uri1 = MiddleSquid::URI.parse('http://www.anidb.net/')
84
+ uri2 = MiddleSquid::URI.parse('http://abcde.anidb.net/')
85
+ uri3 = MiddleSquid::URI.parse('http://bus.sub.stackoverflow.com/')
86
+ even = MiddleSquid::BlackList.new 'even'
87
+
88
+ assert even.include_domain?(uri1), 'www'
89
+ assert even.include? uri1
90
+
91
+ assert even.include_domain?(uri2), 'any subdomain'
92
+ assert even.include? uri2
93
+
94
+ assert even.include_domain?(uri3), 'sub-sub-domain'
95
+ assert even.include? uri3
96
+ end
97
+
98
+ def test_domain_right_match
99
+ uri = MiddleSquid::URI.parse('http://google.com')
100
+ odd = MiddleSquid::BlackList.new 'odd'
101
+
102
+ refute odd.include_domain? uri
103
+ refute odd.include? uri
104
+ end
105
+
106
+ def test_domain_left_match
107
+ uri = MiddleSquid::URI.parse('http://duckduckgo/')
108
+ odd = MiddleSquid::BlackList.new 'odd'
109
+
110
+ refute odd.include_domain? uri
111
+ refute odd.include? uri
112
+ end
113
+
114
+ def test_domain_wrong_subdomain
115
+ uri = MiddleSquid::URI.parse('http://fakeanalytics.google.com/')
116
+ odd = MiddleSquid::BlackList.new 'odd'
117
+
118
+ refute odd.include_domain? uri
119
+ refute odd.include? uri
120
+ end
121
+
122
+ def test_url
123
+ uri = MiddleSquid::URI.parse('http://google.com/analytics')
124
+ even = MiddleSquid::BlackList.new 'even'
125
+
126
+ assert even.include_url? uri
127
+ refute even.include_domain? uri
128
+ assert even.include? uri
129
+ end
130
+
131
+ def test_url_wrong_extension
132
+ uri = MiddleSquid::URI.parse('http://host.net/file.php')
133
+ odd = MiddleSquid::BlackList.new 'odd'
134
+
135
+ refute odd.include_url? uri
136
+ refute odd.include? uri
137
+ end
138
+
139
+ def test_url_partial_path
140
+ uri = MiddleSquid::URI.parse('http://github.com/us')
141
+ odd = MiddleSquid::BlackList.new 'odd'
142
+
143
+ refute odd.include_url? uri
144
+ refute odd.include? uri
145
+ end
146
+
147
+ def test_url_left_match
148
+ uri = MiddleSquid::URI.parse('http://host.net/file.html_ohno')
149
+ odd = MiddleSquid::BlackList.new 'odd'
150
+
151
+ refute odd.include_url? uri
152
+ refute odd.include? uri
153
+ end
154
+
155
+ def test_url_longer_path
156
+ uri = MiddleSquid::URI.parse('http://github.com/user/repository')
157
+ even = MiddleSquid::BlackList.new 'even'
158
+
159
+ assert even.include_url? uri
160
+ assert even.include? uri
161
+ end
162
+
163
+ def test_url_query_string
164
+ uri = MiddleSquid::URI.parse('http://github.com/user?tab=repositories')
165
+ even = MiddleSquid::BlackList.new 'even'
166
+
167
+ assert even.include_url? uri
168
+ assert even.include? uri
169
+ end
170
+
171
+ def test_url_cheap_tricks
172
+ uri = MiddleSquid::URI.parse('http://google.com//maps/../analytics/./')
173
+ even = MiddleSquid::BlackList.new 'even'
174
+
175
+ assert even.include_url? uri
176
+ assert even.include? uri
177
+ end
178
+
179
+ def test_group_demo
180
+ uri = MiddleSquid::URI.parse('http://youtube.com/watch?v=test')
181
+
182
+ even = MiddleSquid::BlackList.new 'even'
183
+ odd = MiddleSquid::BlackList.new 'odd'
184
+
185
+ group = [even, odd]
186
+
187
+ assert group.any? {|bl| bl.include? uri }
188
+ end
189
+ end