pac 1.0.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.
- data/LICENSE +19 -0
- data/README.md +93 -0
- data/bin/parsepac +10 -0
- data/lib/pac.rb +40 -0
- data/lib/pac/file.rb +21 -0
- data/lib/pac/functions.rb +132 -0
- data/lib/pac/runtimes.rb +45 -0
- data/lib/pac/runtimes/johnson.rb +86 -0
- data/lib/pac/runtimes/mustang.rb +60 -0
- data/lib/pac/runtimes/rubyracer.rb +87 -0
- data/lib/pac/runtimes/rubyrhino.rb +77 -0
- data/lib/pac/test_helper.rb +4 -0
- data/lib/pac/version.rb +3 -0
- data/test/pac/functions_test.rb +13 -0
- data/test/pac_test.rb +39 -0
- metadata +93 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2011 by Samuel Kadolph
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# pac
|
2
|
+
|
3
|
+
pac is a gem to parse [proxy auto-config](http://en.wikipedia.org/wiki/Proxy_auto-config) files.
|
4
|
+
pac uses a JavaScript runtime to evaulate a proxy auto-config file the same way a browser does to determine what proxy (if
|
5
|
+
any at all) should a program use to connect to a server. You must install on of the supported JavaScript runtimes:
|
6
|
+
therubyracer, therubyrhino, johnson or mustang.
|
7
|
+
|
8
|
+
Big thanks to [sstephenson](https://github.com/sstephenson)'s [execjs](https://github.com/sstephenson/execjs) for the
|
9
|
+
runtime wrapper code.
|
10
|
+
|
11
|
+
## Installing
|
12
|
+
|
13
|
+
### Recommended
|
14
|
+
|
15
|
+
```
|
16
|
+
gem install pac
|
17
|
+
```
|
18
|
+
|
19
|
+
### Edge
|
20
|
+
|
21
|
+
```
|
22
|
+
git clone https://github.com/samuelkadolph/ruby-pac
|
23
|
+
cd ruby-pac && rake install
|
24
|
+
```
|
25
|
+
|
26
|
+
## Requirements
|
27
|
+
|
28
|
+
After installing the `pac` gem you must install a JavaScript runtime. Compatible runtimes include:
|
29
|
+
|
30
|
+
* [therubyracer](https://rubygems.org/gems/therubyracer) Google V8 embedded within Ruby
|
31
|
+
* [therubyrhino](https://rubygems.org/gems/therubyrhino/) Mozilla Rhino embedded within JRuby
|
32
|
+
* [johnson](https://rubygems.org/gems/johnson/) Mozilla SpiderMonkey embedded within Ruby 1.8
|
33
|
+
* [mustang](https://rubygems.org/gems/mustang/) Mustang V8 embedded within Ruby
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Command Line
|
38
|
+
|
39
|
+
```
|
40
|
+
parsepac https://github.com/downloads/samuelkadolph/ruby-pac/sample.pac https://github.com # => "PROXY proxy:8080"
|
41
|
+
parsepac https://github.com/downloads/samuelkadolph/ruby-pac/sample.pac http://ruby-lang.com # => "PROXY proxy:8080; DIRECT"
|
42
|
+
parsepac https://github.com/downloads/samuelkadolph/ruby-pac/sample.pac http://samuel.kadolph.com # => "DIRECT"
|
43
|
+
```
|
44
|
+
|
45
|
+
### Ruby
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require "rubygems"
|
49
|
+
require "pac"
|
50
|
+
|
51
|
+
pac = PAC.load("https://github.com/downloads/samuelkadolph/ruby-pac/sample.pac")
|
52
|
+
pac.find("https://github.com") # => "PROXY proxy:8080"
|
53
|
+
pac.find("http://ruby-lang.com") # => "PROXY proxy:8080; DIRECT"
|
54
|
+
pac.find("http://samuel.kadolph.com") # => "DIRECT"
|
55
|
+
|
56
|
+
pac = PAC.read("sample.pac")
|
57
|
+
|
58
|
+
pac = PAC.source <<-JS
|
59
|
+
function FindProxyForURL(url, host) {
|
60
|
+
return "DIRECT";
|
61
|
+
}
|
62
|
+
JS
|
63
|
+
pac.find("http://localhost") # => "DIRECT"
|
64
|
+
```
|
65
|
+
|
66
|
+
## Available JavaScript Functions
|
67
|
+
|
68
|
+
* isPlainHostName(host)
|
69
|
+
* dnsDomainIs(host, domain)
|
70
|
+
* localHostOrDomainIs(host, hostdom)
|
71
|
+
* isResolvable(host)
|
72
|
+
* isInNet(host, pattern, mask)
|
73
|
+
* dnsResolve(host)
|
74
|
+
* myIpAddress()
|
75
|
+
* dnsDomainLevels(host)
|
76
|
+
* shExpMatch(str, shexp)
|
77
|
+
* weekdayRange(wd1, wd2, gmt)
|
78
|
+
* dateRange(*args)
|
79
|
+
* timeRange(*args)
|
80
|
+
|
81
|
+
## Developers
|
82
|
+
|
83
|
+
### Contributing
|
84
|
+
|
85
|
+
If you want to contribute: fork, branch & pull request.
|
86
|
+
|
87
|
+
### Running Tests
|
88
|
+
|
89
|
+
```
|
90
|
+
bundle install
|
91
|
+
rake test
|
92
|
+
rake test:rubyracer
|
93
|
+
```
|
data/bin/parsepac
ADDED
data/lib/pac.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
module PAC
|
4
|
+
require "pac/version"
|
5
|
+
require "pac/file"
|
6
|
+
require "pac/runtimes"
|
7
|
+
|
8
|
+
class Error < StandardError
|
9
|
+
end
|
10
|
+
class RuntimeError < Error
|
11
|
+
end
|
12
|
+
class ProgramError < Error
|
13
|
+
end
|
14
|
+
class RuntimeUnavailable < RuntimeError
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_reader :runtime
|
19
|
+
|
20
|
+
def runtime=(runtime)
|
21
|
+
raise RuntimeUnavailable, "#{runtime.name} is unavailable on this system" unless runtime.available?
|
22
|
+
@runtime = runtime
|
23
|
+
end
|
24
|
+
|
25
|
+
def load(url, options = {})
|
26
|
+
require "open-uri"
|
27
|
+
File.new(open(url, { :proxy => false }.merge(options)).read)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read(file)
|
31
|
+
File.new(::File.read(file))
|
32
|
+
end
|
33
|
+
|
34
|
+
def source(source)
|
35
|
+
File.new(source)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
self.runtime = Runtimes.autodetect
|
40
|
+
end
|
data/lib/pac/file.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
module PAC
|
4
|
+
require "pac/functions"
|
5
|
+
|
6
|
+
class File
|
7
|
+
attr_reader :source, :context
|
8
|
+
|
9
|
+
def initialize(source)
|
10
|
+
@source = source.dup.freeze
|
11
|
+
@context = PAC.runtime.compile(@source)
|
12
|
+
@context.include Functions
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(url)
|
16
|
+
uri = URI.parse(url)
|
17
|
+
raise ArgumentError, "url is missing host" unless uri.host
|
18
|
+
context.call("FindProxyForURL", url, uri.host)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "ipaddr"
|
2
|
+
require "resolv"
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
module PAC
|
6
|
+
module Functions
|
7
|
+
DAYS = { "MON" => 1, "TUE" => 2, "WED" => 3, "THU" => 4, "FRI" => 5, "SAT" => 6, "SUN" => 7 }
|
8
|
+
MONTHS = { "JAN" => 1, "FEB" => 2, "MAR" => 3, "APR" => 4, "MAY" => 5, "JUN" => 6,
|
9
|
+
"JUL" => 7, "AUG" => 8, "SEP" => 9, "OCT" => 10, "NOV" => 11, "DEC" => 12 }
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def isPlainHostName(host)
|
13
|
+
not host.include? "."
|
14
|
+
end
|
15
|
+
|
16
|
+
def dnsDomainIs(host, domain)
|
17
|
+
host.end_with? domain
|
18
|
+
end
|
19
|
+
|
20
|
+
def localHostOrDomainIs(host, hostdom)
|
21
|
+
host == hostdom or hostdom.include? host
|
22
|
+
end
|
23
|
+
|
24
|
+
def isResolvable(host)
|
25
|
+
!!resolve_host(host)
|
26
|
+
end
|
27
|
+
|
28
|
+
def isInNet(host, pattern, mask)
|
29
|
+
IPAddr.new(pattern).mask(mask).include? resolve_host(host)
|
30
|
+
end
|
31
|
+
|
32
|
+
def dnsResolve(host)
|
33
|
+
resolve_host(host)
|
34
|
+
end
|
35
|
+
|
36
|
+
def myIpAddress()
|
37
|
+
resolve_host(Socket.gethostname)
|
38
|
+
end
|
39
|
+
|
40
|
+
def dnsDomainLevels(host)
|
41
|
+
host.scan(".").size
|
42
|
+
end
|
43
|
+
|
44
|
+
def shExpMatch(str, shexp)
|
45
|
+
::File.fnmatch(shexp, str)
|
46
|
+
end
|
47
|
+
|
48
|
+
def weekdayRange(wd1, wd2 = nil, gmt = nil)
|
49
|
+
time = Time.now
|
50
|
+
time = time.utc if gmt == "GMT"
|
51
|
+
|
52
|
+
(DAYS[wd1]..DAYS[wd2 || wd1]).include? time.wday
|
53
|
+
end
|
54
|
+
|
55
|
+
def dateRange(*args)
|
56
|
+
time = Time.now
|
57
|
+
time = time.utc if args.last == "GMT" and args.pop
|
58
|
+
|
59
|
+
case args.size
|
60
|
+
when 1
|
61
|
+
check_date_part(time, args[0])
|
62
|
+
when 2
|
63
|
+
check_date_part(time, args[0]..args[1])
|
64
|
+
when 4
|
65
|
+
check_date_part(time, args[0]..args[2]) and
|
66
|
+
check_date_part(time, args[1]..args[3])
|
67
|
+
when 6
|
68
|
+
check_date_part(time, args[0]..args[3]) and
|
69
|
+
check_date_part(time, args[1]..args[4]) and
|
70
|
+
check_date_part(time, args[2]..args[5])
|
71
|
+
else
|
72
|
+
raise ArgumentError, "wrong number of arguments"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def timeRange(*args)
|
77
|
+
time = Time.now
|
78
|
+
time = time.utc if args.last == "GMT" and args.pop
|
79
|
+
|
80
|
+
case args.size
|
81
|
+
when 1
|
82
|
+
time.hour == args[0]
|
83
|
+
when 2
|
84
|
+
(args[0]..args[1]).include? time.hour
|
85
|
+
when 4
|
86
|
+
(args[0]..args[2]).include? time.hour and
|
87
|
+
(args[1]..args[3]).include? time.min
|
88
|
+
when 6
|
89
|
+
(args[0]..args[3]).include? time.hour and
|
90
|
+
(args[1]..args[4]).include? time.min and
|
91
|
+
(args[2]..args[5]).include? time.sec
|
92
|
+
else
|
93
|
+
raise ArgumentError, "wrong number of arguments"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def check_date_part(time, part, operation = :==)
|
99
|
+
case part
|
100
|
+
when String
|
101
|
+
time.month.send(operation, MONTHS[part])
|
102
|
+
when Integer
|
103
|
+
if part < 100
|
104
|
+
time.day.send(operation, part)
|
105
|
+
else
|
106
|
+
time.year.send(operation, part)
|
107
|
+
end
|
108
|
+
when Range
|
109
|
+
check_date_part(time, part.begin, :>=) and check_date_part(time, part.end, :<=)
|
110
|
+
else
|
111
|
+
raise ArgumentError, "wrong type"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def resolve_host(host)
|
116
|
+
Resolv.each_address(host) do |address|
|
117
|
+
begin
|
118
|
+
return address if IPAddr.new(address).ipv4?
|
119
|
+
rescue ArgumentError
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# We couldn't find an IPv4 address for the host
|
124
|
+
nil
|
125
|
+
rescue Resolv::ResolvError, NoMethodError
|
126
|
+
# Have to rescue NoMethodError because jruby has a bug with non existant hostnames
|
127
|
+
# See http://jira.codehaus.org/browse/JRUBY-6054
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/pac/runtimes.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module PAC
|
2
|
+
module Runtimes
|
3
|
+
require "pac/runtimes/rubyracer"
|
4
|
+
require "pac/runtimes/rubyrhino"
|
5
|
+
require "pac/runtimes/johnson"
|
6
|
+
require "pac/runtimes/mustang"
|
7
|
+
|
8
|
+
RubyRacer = RubyRacerRuntime.new
|
9
|
+
RubyRhino = RubyRhinoRuntime.new
|
10
|
+
Johnson = JohnsonRuntime.new
|
11
|
+
Mustang = MustangRuntime.new
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def autodetect
|
15
|
+
from_environment || best_available || raise(RuntimeUnavailable, "Could not find a JavaScript runtime. See https://github.com/samuelkadolph/ruby-pac for a list of runtimes.")
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_environment
|
19
|
+
if name = ENV["JS_RUNTIME"]
|
20
|
+
if runtime = const_get(name)
|
21
|
+
if runtime.available?
|
22
|
+
runtime if runtime.available?
|
23
|
+
else
|
24
|
+
raise RuntimeUnavailable, "#{runtime.name} runtime is not available on this system"
|
25
|
+
end
|
26
|
+
elsif !name.empty?
|
27
|
+
raise RuntimeUnavailable, "#{name} runtime is not defined"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def best_available
|
33
|
+
runtimes.find(&:available?)
|
34
|
+
end
|
35
|
+
|
36
|
+
def runtimes
|
37
|
+
@runtimes ||= [RubyRacer, RubyRhino, Johnson, Mustang]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.runtimes
|
43
|
+
Runtimes.runtimes
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module PAC
|
2
|
+
module Runtimes
|
3
|
+
class JohnsonRuntime
|
4
|
+
class Context
|
5
|
+
def initialize(source = "")
|
6
|
+
@runtime = ::Johnson::Runtime.new
|
7
|
+
@runtime.evaluate(source)
|
8
|
+
end
|
9
|
+
|
10
|
+
def include(mod)
|
11
|
+
(mod.methods - Module.methods).each do |name|
|
12
|
+
@runtime[name] = mod.method(name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(properties, *args)
|
17
|
+
unbox @runtime.evaluate(properties).call(*args)
|
18
|
+
rescue Johnson::Error => e
|
19
|
+
if syntax_error?(e)
|
20
|
+
raise RuntimeError, e.message
|
21
|
+
else
|
22
|
+
raise ProgramError, e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def unbox(value)
|
27
|
+
case
|
28
|
+
when function?(value)
|
29
|
+
nil
|
30
|
+
when string?(value)
|
31
|
+
value.respond_to?(:force_encoding) ? value.force_encoding("UTF-8") : value
|
32
|
+
when array?(value)
|
33
|
+
value.map { |v| unbox(v) }
|
34
|
+
when object?(value)
|
35
|
+
value.inject({}) do |vs, (k, v)|
|
36
|
+
vs[k] = unbox(v) unless function?(v)
|
37
|
+
vs
|
38
|
+
end
|
39
|
+
else
|
40
|
+
value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def syntax_error?(error)
|
46
|
+
error.message =~ /^syntax error at /
|
47
|
+
end
|
48
|
+
|
49
|
+
def function?(value)
|
50
|
+
value.respond_to?(:function?) && value.function?
|
51
|
+
end
|
52
|
+
|
53
|
+
def string?(value)
|
54
|
+
value.is_a?(String)
|
55
|
+
end
|
56
|
+
|
57
|
+
def array?(value)
|
58
|
+
array_test.call(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def object?(value)
|
62
|
+
value.respond_to?(:inject)
|
63
|
+
end
|
64
|
+
|
65
|
+
def array_test
|
66
|
+
@array_test ||= @runtime.evaluate("(function(a) { return a instanceof [].constructor})")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def name
|
71
|
+
"Johnson (SpiderMonkey)"
|
72
|
+
end
|
73
|
+
|
74
|
+
def compile(source)
|
75
|
+
Context.new(source)
|
76
|
+
end
|
77
|
+
|
78
|
+
def available?
|
79
|
+
require "johnson"
|
80
|
+
true
|
81
|
+
rescue LoadError
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module PAC
|
2
|
+
module Runtimes
|
3
|
+
class MustangRuntime
|
4
|
+
class Context
|
5
|
+
def initialize(source = "")
|
6
|
+
source = source.encode("UTF-8") if source.respond_to?(:encode)
|
7
|
+
|
8
|
+
@v8_context = ::Mustang::Context.new
|
9
|
+
@v8_context.eval(source)
|
10
|
+
end
|
11
|
+
|
12
|
+
def include(mod)
|
13
|
+
(mod.methods - Module.methods).each do |name|
|
14
|
+
@v8_context[name] = mod.method(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(properties, *args)
|
19
|
+
unbox @v8_context.eval(properties).call(*args)
|
20
|
+
rescue NoMethodError => e
|
21
|
+
raise ProgramError, e.message
|
22
|
+
end
|
23
|
+
|
24
|
+
def unbox(value)
|
25
|
+
case value
|
26
|
+
when ::Mustang::V8::Array
|
27
|
+
value.map { |v| unbox(v) }
|
28
|
+
when ::Mustang::V8::Boolean
|
29
|
+
value.to_bool
|
30
|
+
when ::Mustang::V8::NullClass, ::Mustang::V8::UndefinedClass
|
31
|
+
nil
|
32
|
+
when ::Mustang::V8::Function
|
33
|
+
nil
|
34
|
+
when ::Mustang::V8::SyntaxError
|
35
|
+
raise RuntimeError, value.message
|
36
|
+
when ::Mustang::V8::Error
|
37
|
+
raise ProgramError, value.message
|
38
|
+
else
|
39
|
+
value.respond_to?(:delegate) ? value.delegate : value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def name
|
45
|
+
"Mustang (V8)"
|
46
|
+
end
|
47
|
+
|
48
|
+
def compile(source)
|
49
|
+
Context.new(source)
|
50
|
+
end
|
51
|
+
|
52
|
+
def available?
|
53
|
+
require "mustang"
|
54
|
+
true
|
55
|
+
rescue LoadError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module PAC
|
2
|
+
module Runtimes
|
3
|
+
class RubyRacerRuntime
|
4
|
+
class Context
|
5
|
+
def initialize(source = "")
|
6
|
+
source = source.encode("UTF-8") if source.respond_to?(:encode)
|
7
|
+
|
8
|
+
lock do
|
9
|
+
@v8_context = V8::Context.new
|
10
|
+
@v8_context.eval(source)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def include(mod)
|
15
|
+
(mod.methods - Module.methods).each do |name|
|
16
|
+
@v8_context[name] = mod.method(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(properties, *args)
|
21
|
+
lock do
|
22
|
+
begin
|
23
|
+
unbox @v8_context.eval(properties).call(*args)
|
24
|
+
rescue ::V8::JSError => e
|
25
|
+
if e.value["name"] == "SyntaxError"
|
26
|
+
raise RuntimeError, e.message
|
27
|
+
else
|
28
|
+
raise ProgramError, e.message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def unbox(value)
|
35
|
+
case value
|
36
|
+
when ::V8::Function
|
37
|
+
nil
|
38
|
+
when ::V8::Array
|
39
|
+
value.map { |v| unbox(v) }
|
40
|
+
when ::V8::Object
|
41
|
+
value.inject({}) do |vs, (k, v)|
|
42
|
+
vs[k] = unbox(v) unless v.is_a?(::V8::Function)
|
43
|
+
vs
|
44
|
+
end
|
45
|
+
when String
|
46
|
+
value.respond_to?(:force_encoding) ? value.force_encoding("UTF-8") : value
|
47
|
+
else
|
48
|
+
value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def lock
|
54
|
+
result, exception = nil, nil
|
55
|
+
V8::C::Locker() do
|
56
|
+
begin
|
57
|
+
result = yield
|
58
|
+
rescue Exception => e
|
59
|
+
exception = e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if exception
|
64
|
+
raise exception
|
65
|
+
else
|
66
|
+
result
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def name
|
72
|
+
"therubyracer (V8)"
|
73
|
+
end
|
74
|
+
|
75
|
+
def compile(source)
|
76
|
+
Context.new(source)
|
77
|
+
end
|
78
|
+
|
79
|
+
def available?
|
80
|
+
require "v8"
|
81
|
+
true
|
82
|
+
rescue LoadError
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module PAC
|
2
|
+
module Runtimes
|
3
|
+
class RubyRhinoRuntime
|
4
|
+
class Context
|
5
|
+
def initialize(source = "")
|
6
|
+
source = source.encode("UTF-8") if source.respond_to?(:encode)
|
7
|
+
|
8
|
+
@rhino_context = Rhino::Context.new
|
9
|
+
fix_memory_limit! @rhino_context
|
10
|
+
@rhino_context.eval(source)
|
11
|
+
end
|
12
|
+
|
13
|
+
def include(mod)
|
14
|
+
(mod.methods - Module.methods).each do |name|
|
15
|
+
@rhino_context[name] = mod.method(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(properties, *args)
|
20
|
+
unbox @rhino_context.eval(properties).call(*args)
|
21
|
+
rescue ::Rhino::JavascriptError => e
|
22
|
+
if e.message == "syntax error"
|
23
|
+
raise RuntimeError, e.message
|
24
|
+
else
|
25
|
+
raise ProgramError, e.message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def unbox(value)
|
30
|
+
case value = ::Rhino::To.ruby(value)
|
31
|
+
when ::Rhino::NativeFunction
|
32
|
+
nil
|
33
|
+
when ::Rhino::NativeObject
|
34
|
+
value.inject({}) do |vs, (k, v)|
|
35
|
+
case v
|
36
|
+
when ::Rhino::NativeFunction, ::Rhino::J::Function
|
37
|
+
nil
|
38
|
+
else
|
39
|
+
vs[k] = unbox(v)
|
40
|
+
end
|
41
|
+
vs
|
42
|
+
end
|
43
|
+
when Array
|
44
|
+
value.map { |v| unbox(v) }
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
# Disables bytecode compiling which limits you to 64K scripts
|
52
|
+
def fix_memory_limit!(context)
|
53
|
+
if context.respond_to?(:optimization_level=)
|
54
|
+
context.optimization_level = -1
|
55
|
+
else
|
56
|
+
context.instance_eval { @native.setOptimizationLevel(-1) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def name
|
62
|
+
"therubyrhino (Rhino)"
|
63
|
+
end
|
64
|
+
|
65
|
+
def compile(source)
|
66
|
+
Context.new(source)
|
67
|
+
end
|
68
|
+
|
69
|
+
def available?
|
70
|
+
require "rhino"
|
71
|
+
true
|
72
|
+
rescue LoadError
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/pac/version.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "pac/test_helper"
|
2
|
+
|
3
|
+
describe PAC::Functions do
|
4
|
+
describe "isResolvable()" do
|
5
|
+
it "should return true for localhost" do
|
6
|
+
PAC::Functions.isResolvable("localhost").must_equal true
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return false for awidhaowuhuiuhiuug" do
|
10
|
+
PAC::Functions.isResolvable("awidhaowuhuiuhiuug").must_equal false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/test/pac_test.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "pac/test_helper"
|
2
|
+
|
3
|
+
describe PAC do
|
4
|
+
describe "read" do
|
5
|
+
before do
|
6
|
+
@path = File.expand_path("../files/sample.pac", __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should load a file from a path" do
|
10
|
+
pac = PAC.read(@path)
|
11
|
+
pac.wont_be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return DIRECT for a url" do
|
15
|
+
pac = PAC.read(@path)
|
16
|
+
pac.find("http://localhost").must_equal "DIRECT"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "source" do
|
21
|
+
before do
|
22
|
+
@source = <<-JS
|
23
|
+
function FindProxyForURL(url, host) {
|
24
|
+
return "DIRECT";
|
25
|
+
}
|
26
|
+
JS
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should load source" do
|
30
|
+
pac = PAC.source(@source)
|
31
|
+
pac.wont_be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return DIRECT for a url" do
|
35
|
+
pac = PAC.source(@source)
|
36
|
+
pac.find("http://localhost").must_equal "DIRECT"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Samuel Kadolph
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70297018438300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.9.2
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70297018438300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: minitest
|
27
|
+
requirement: &70297018437400 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.3.1
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70297018437400
|
36
|
+
description: ! 'pac uses a JavaScript runtime to evaulate a proxy auto-config file
|
37
|
+
the same way a browser does to determine what proxy (if
|
38
|
+
|
39
|
+
any at all) should a program use to connect to a server. You must install on of
|
40
|
+
the supported JavaScript runtimes:
|
41
|
+
|
42
|
+
therubyracer, therubyrhino, johnson or mustang.
|
43
|
+
|
44
|
+
'
|
45
|
+
email:
|
46
|
+
- samuel@kadolph.com
|
47
|
+
executables:
|
48
|
+
- parsepac
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- bin/parsepac
|
53
|
+
- lib/pac/file.rb
|
54
|
+
- lib/pac/functions.rb
|
55
|
+
- lib/pac/runtimes/johnson.rb
|
56
|
+
- lib/pac/runtimes/mustang.rb
|
57
|
+
- lib/pac/runtimes/rubyracer.rb
|
58
|
+
- lib/pac/runtimes/rubyrhino.rb
|
59
|
+
- lib/pac/runtimes.rb
|
60
|
+
- lib/pac/test_helper.rb
|
61
|
+
- lib/pac/version.rb
|
62
|
+
- lib/pac.rb
|
63
|
+
- LICENSE
|
64
|
+
- README.md
|
65
|
+
- test/pac/functions_test.rb
|
66
|
+
- test/pac_test.rb
|
67
|
+
homepage: https://github.com/samuelkadolph/ruby-pac
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 1.8.7
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.10
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: pac is a gem to parse proxy auto-config files.
|
91
|
+
test_files:
|
92
|
+
- test/pac/functions_test.rb
|
93
|
+
- test/pac_test.rb
|