http_vanilli 0.0.2
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/lib/angry_web/matcher.rb +105 -0
- data/lib/http_vanilli/abstract_responder.rb +13 -0
- data/lib/http_vanilli/basic_mapper.rb +60 -0
- data/lib/http_vanilli/basic_mapping.rb +22 -0
- data/lib/http_vanilli/net_http/override.rb +88 -0
- data/lib/http_vanilli/net_http/request.rb +33 -0
- data/lib/http_vanilli/net_http/response.rb +37 -0
- data/lib/http_vanilli/net_http/stubs.rb +25 -0
- data/lib/http_vanilli/net_http/util.rb +36 -0
- data/lib/http_vanilli/other_mapper.rb +54 -0
- data/lib/http_vanilli/request.rb +12 -0
- data/lib/http_vanilli/responders/block.rb +19 -0
- data/lib/http_vanilli/responders/rack.rb +61 -0
- data/lib/http_vanilli/responders.rb +6 -0
- data/lib/http_vanilli/response.rb +46 -0
- data/lib/http_vanilli/test_adapters/rspec.rb +9 -0
- data/lib/http_vanilli/util.rb +32 -0
- data/lib/http_vanilli/version.rb +3 -0
- data/lib/http_vanilli.rb +62 -0
- metadata +94 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'rack'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class Object
|
6
|
+
def tapp(tag=nil)
|
7
|
+
print "#{tag}=" if tag
|
8
|
+
pp self
|
9
|
+
self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AngryWeb
|
14
|
+
class ValueMatcher
|
15
|
+
attr_reader :mismatch_reason
|
16
|
+
def initialize(value)
|
17
|
+
@mismatch_reason = "<no reason>"
|
18
|
+
@value = value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class HashSubsetMatcher < ValueMatcher
|
23
|
+
def match?(params)
|
24
|
+
@value.keys.all? do |k|
|
25
|
+
match = @value[k]
|
26
|
+
param = params[k.to_s]
|
27
|
+
|
28
|
+
case match
|
29
|
+
when Regexp
|
30
|
+
!! param[match]
|
31
|
+
else
|
32
|
+
match == param
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class REMatcher < ValueMatcher
|
39
|
+
def match?(other)
|
40
|
+
other.to_s.match(@value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class EqlMatcher < ValueMatcher
|
45
|
+
def match?(other)
|
46
|
+
@value == other
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Matcher
|
51
|
+
attr_reader :mismatches
|
52
|
+
|
53
|
+
def initialize(&blk)
|
54
|
+
@parts = {}
|
55
|
+
yield self
|
56
|
+
end
|
57
|
+
|
58
|
+
def match?(uri)
|
59
|
+
uri = Addressable::URI.heuristic_parse(uri)
|
60
|
+
uri_hash = uri.to_hash
|
61
|
+
|
62
|
+
@mismatches = []
|
63
|
+
|
64
|
+
@parts.each {|k,v|
|
65
|
+
unless v.match?(__transform_value_from_uri(uri_hash,k))
|
66
|
+
@mismatches << v.mismatch_reason
|
67
|
+
end
|
68
|
+
}
|
69
|
+
|
70
|
+
@mismatches.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def hash_subset(spec)
|
74
|
+
HashSubsetMatcher.new(spec)
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing(meth,*args,&block)
|
78
|
+
if args.size == 1
|
79
|
+
@parts[meth] = __convert_value(args.first)
|
80
|
+
else
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def __convert_value(value)
|
86
|
+
case value
|
87
|
+
when ValueMatcher
|
88
|
+
value
|
89
|
+
when Regexp
|
90
|
+
REMatcher.new(value)
|
91
|
+
else
|
92
|
+
EqlMatcher.new(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def __transform_value_from_uri(uri,key)
|
97
|
+
case key
|
98
|
+
when :params
|
99
|
+
Rack::Utils.parse_query(uri[:query])
|
100
|
+
else
|
101
|
+
uri[key]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module AbstractResponder
|
3
|
+
include HttpVanilli::NetHttp::YieldResponse
|
4
|
+
|
5
|
+
def response_for_request(request)
|
6
|
+
code,headers,body = rack_response(request)
|
7
|
+
|
8
|
+
response = HttpVanilli::NetHttp::Response.new(code,headers,body)
|
9
|
+
|
10
|
+
nh_yield_response(response,request)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module HttpVanilli
|
4
|
+
class BasicMapper
|
5
|
+
attr_accessor :responder
|
6
|
+
|
7
|
+
def initialize(extra_responders = {})
|
8
|
+
responder_classes.update(extra_responders)
|
9
|
+
end
|
10
|
+
|
11
|
+
def responder_classes
|
12
|
+
@responder_classes ||= {
|
13
|
+
:block => HttpVanilli::Responders::Block,
|
14
|
+
:rack => HttpVanilli::Responders::Rack
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def responders; @responders ||= [] end
|
19
|
+
|
20
|
+
def add_block_responder(*args,&block)
|
21
|
+
responders << responder_classes[:block].new(*args,&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_rack_responder(*args,&block)
|
25
|
+
responders << responder_classes[:rack].new(*args,&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_responder(kind,*args,&block)
|
29
|
+
responders << responder_classes[kind].new(*args,&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
## Mapping API
|
33
|
+
|
34
|
+
# Take the info from the innards of Net::HTTP and build a request.
|
35
|
+
def build_request(kind,http,request,&block)
|
36
|
+
HttpVanilli::Request.build(kind,http,request,&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Should we map the request?
|
40
|
+
def map_request?(request)
|
41
|
+
!! find_responder(request)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Map the request
|
45
|
+
def map_request(request)
|
46
|
+
find_responder(request).response_for_request(request)
|
47
|
+
end
|
48
|
+
|
49
|
+
# The request wasn't matched and normal net connection was disallowed.
|
50
|
+
def unmapped_request(request)
|
51
|
+
raise "unmatched_request #{request}"
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def find_responder(request)
|
57
|
+
responders.find {|responder| responder.match?(request)}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module Unused
|
4
|
+
class BasicMapping
|
5
|
+
include HttpVanilli::NetHttp::YieldResponse
|
6
|
+
|
7
|
+
def initialize(method,url,&block)
|
8
|
+
@method = method
|
9
|
+
@url = Addressable::URI.heuristic_parse(url)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(request)
|
14
|
+
(request.host == @url.host)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rack_response
|
18
|
+
@block[request]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'http_vanilli/net_http/stubs'
|
2
|
+
require 'http_vanilli/net_http/util'
|
3
|
+
require 'http_vanilli/net_http/request'
|
4
|
+
require 'http_vanilli/net_http/response'
|
5
|
+
require 'net/http'
|
6
|
+
require 'net/https'
|
7
|
+
require 'stringio'
|
8
|
+
|
9
|
+
module HttpVanilli
|
10
|
+
module NetHttp
|
11
|
+
def self.override!
|
12
|
+
record_loaded_net_http_replacement_libs
|
13
|
+
puts_warning_for_net_http_around_advice_libs_if_needed
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Net #:nodoc: all
|
19
|
+
class BufferedIO
|
20
|
+
def initialize_with_http_vanilli(io, debug_output = nil)
|
21
|
+
@read_timeout = 60
|
22
|
+
@rbuf = ''
|
23
|
+
@debug_output = debug_output
|
24
|
+
|
25
|
+
@io = case io
|
26
|
+
when Socket, OpenSSL::SSL::SSLSocket, IO
|
27
|
+
io
|
28
|
+
when String
|
29
|
+
if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
|
30
|
+
File.open(io, "r")
|
31
|
+
else
|
32
|
+
StringIO.new(io)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
raise "Unable to create local socket" unless @io
|
36
|
+
end
|
37
|
+
alias_method :initialize_without_http_vanilli, :initialize
|
38
|
+
alias_method :initialize, :initialize_with_http_vanilli
|
39
|
+
end
|
40
|
+
|
41
|
+
class HTTP
|
42
|
+
class << self
|
43
|
+
def socket_type_with_http_vanilli
|
44
|
+
HttpVanilli::NetHttp::StubSocket
|
45
|
+
end
|
46
|
+
alias_method :socket_type_without_http_vanilli, :socket_type
|
47
|
+
alias_method :socket_type, :socket_type_with_http_vanilli
|
48
|
+
end
|
49
|
+
|
50
|
+
def request_with_http_vanilli(request, body = nil, &block)
|
51
|
+
mapper = HttpVanilli.request_mapper
|
52
|
+
|
53
|
+
request.set_body_internal body
|
54
|
+
|
55
|
+
# Wrap Net::HTTPRequest & associated info in a HttpVanilli::Request
|
56
|
+
vanilli_request = mapper.build_request(:net_http, self, request, &block)
|
57
|
+
|
58
|
+
# The mapper can map the request. Do it.
|
59
|
+
if mapper.map_request?(vanilli_request)
|
60
|
+
@socket = Net::HTTP.socket_type.new
|
61
|
+
mapper.map_request(vanilli_request)
|
62
|
+
|
63
|
+
# otherwise, either allow the request to happen as normal,
|
64
|
+
elsif HttpVanilli.allow_net_connect?
|
65
|
+
connect_without_http_vanilli
|
66
|
+
request_without_http_vanilli(request, body, &block)
|
67
|
+
|
68
|
+
# or note the request as unmapped.
|
69
|
+
else
|
70
|
+
mapper.unmapped_request(vanilli_request)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
alias_method :request_without_http_vanilli, :request
|
74
|
+
alias_method :request, :request_with_http_vanilli
|
75
|
+
|
76
|
+
|
77
|
+
def connect_with_http_vanilli
|
78
|
+
unless @@alredy_checked_for_net_http_replacement_libs ||= false
|
79
|
+
HttpVanilli::NetHttp.puts_warning_for_net_http_replacement_libs_if_needed
|
80
|
+
@@alredy_checked_for_net_http_replacement_libs = true
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
alias_method :connect_without_http_vanilli, :connect
|
85
|
+
alias_method :connect, :connect_with_http_vanilli
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'http_vanilli/request'
|
2
|
+
|
3
|
+
module HttpVanilli
|
4
|
+
module NetHttp
|
5
|
+
class Request < HttpVanilli::Request
|
6
|
+
attr_reader :block, :uri, :method, :original_request
|
7
|
+
|
8
|
+
def initialize(http,request,&block)
|
9
|
+
@http = http
|
10
|
+
@original_request = request
|
11
|
+
@block = block
|
12
|
+
|
13
|
+
protocol = http.use_ssl? ? "https" : "http"
|
14
|
+
|
15
|
+
path = request.path
|
16
|
+
path = Addressable::URI.parse(request.path).request_uri if request.path =~ /^https?:/
|
17
|
+
|
18
|
+
@uri = Addressable::URI.parse( "#{protocol}://#{http.address}:#{http.port}#{path}" )
|
19
|
+
@method = request.method.downcase.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def host; @uri.host end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{@method} #{@uri.to_s}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def body
|
29
|
+
@original_request.body
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module NetHttp
|
3
|
+
module YieldResponse
|
4
|
+
def nh_yield_response(response,request)
|
5
|
+
nh_rsp = response.to_net_http
|
6
|
+
request.block[nh_rsp] if request.block
|
7
|
+
nh_rsp
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Response < HttpVanilli::Response
|
12
|
+
attr_accessor :headers, :body
|
13
|
+
attr_reader :code, :message
|
14
|
+
|
15
|
+
def initialize(code,headers,body,message=StatusMessage[code.to_i])
|
16
|
+
@code,@body,@message = code.to_i,body,message
|
17
|
+
@headers = headers || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def body
|
21
|
+
@body.join("")
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_net_http
|
25
|
+
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, message)
|
26
|
+
|
27
|
+
response.instance_variable_set(:@body, body) if body
|
28
|
+
headers.each { |name, value| response[name] = value }
|
29
|
+
|
30
|
+
response.instance_variable_set(:@read, true)
|
31
|
+
response.extend HttpVanilli::NetHttp::ResponseMixin
|
32
|
+
|
33
|
+
response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module NetHttp
|
3
|
+
class StubSocket #:nodoc:
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def closed?
|
9
|
+
@closed ||= true
|
10
|
+
end
|
11
|
+
|
12
|
+
def readuntil(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module ResponseMixin #:nodoc:
|
18
|
+
def read_body(*args, &block)
|
19
|
+
yield @body if block_given?
|
20
|
+
@body
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module NetHttp
|
3
|
+
def self.puts_warning_for_net_http_around_advice_libs_if_needed
|
4
|
+
libs = {"Samuel" => defined?(Samuel)}
|
5
|
+
warnings = libs.select { |_, loaded| loaded }.map do |name, _|
|
6
|
+
<<-TEXT.gsub(/ {10}/, '')
|
7
|
+
\e[1mWarning: HttpVanilli was loaded after #{name}\e[0m
|
8
|
+
* #{name}'s code is being ignored when a request is handled by HttpVanilli::NetHttp,
|
9
|
+
because both libraries work by patching Net::HTTP.
|
10
|
+
* To fix this, just reorder your requires so that HttpVanilli is before #{name}.
|
11
|
+
TEXT
|
12
|
+
end
|
13
|
+
$stderr.puts "\n" + warnings.join("\n") + "\n" if warnings.any?
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.record_loaded_net_http_replacement_libs
|
17
|
+
libs = {"RightHttpConnection" => defined?(RightHttpConnection)}
|
18
|
+
@loaded_net_http_replacement_libs = libs.map { |name, loaded| name if loaded }.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.puts_warning_for_net_http_replacement_libs_if_needed
|
22
|
+
libs = {"RightHttpConnection" => defined?(RightHttpConnection)}
|
23
|
+
warnings = libs.select { |_, loaded| loaded }.
|
24
|
+
reject { |name, _| @loaded_net_http_replacement_libs.include?(name) }.
|
25
|
+
map do |name, _|
|
26
|
+
<<-TEXT.gsub(/ {10}/, '')
|
27
|
+
\e[1mWarning: #{name} was loaded after HttpVanilli::NetHttp\e[0m
|
28
|
+
* HttpVanilli's code is being ignored, because #{name} replaces parts of
|
29
|
+
Net::HTTP without deferring to other libraries. This will break Net::HTTP requests.
|
30
|
+
* To fix this, just reorder your requires so that #{name} is before HttpVanilli.
|
31
|
+
TEXT
|
32
|
+
end
|
33
|
+
$stderr.puts "\n" + warnings.join("\n") + "\n" if warnings.any?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module HttpVanilli
|
5
|
+
class OtherMapper
|
6
|
+
def customise_request(&block)
|
7
|
+
@customise_request = block
|
8
|
+
end
|
9
|
+
|
10
|
+
# Take the info from the innards of Net::HTTP and build a request.
|
11
|
+
def build_request(kind,http,request,body,&block)
|
12
|
+
req = OpenStruct.new
|
13
|
+
req.block = block
|
14
|
+
|
15
|
+
pp http
|
16
|
+
pp request.to_hash
|
17
|
+
|
18
|
+
protocol = http.use_ssl? ? "https" : "http"
|
19
|
+
|
20
|
+
path = request.path
|
21
|
+
path = Addressable::URI.parse(request.path).request_uri if request.path =~ /^http/
|
22
|
+
|
23
|
+
req.uri = Addressable::URI.parse( "#{protocol}://#{http.address}:#{http.port}#{path}" )
|
24
|
+
req.method = request.method.downcase.to_sym
|
25
|
+
|
26
|
+
if @customise_request
|
27
|
+
@customise_request[req]
|
28
|
+
end
|
29
|
+
|
30
|
+
req
|
31
|
+
end
|
32
|
+
|
33
|
+
# Should we map the request?
|
34
|
+
def map_request?(request)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
# Map the request
|
39
|
+
def map_request(request)
|
40
|
+
response = HttpVanilli::NetHttp::Response.new(200,'cool')
|
41
|
+
response.body = "boddy"
|
42
|
+
|
43
|
+
nhrsp = response.to_net_http
|
44
|
+
request.block[nhrsp] if request.block
|
45
|
+
|
46
|
+
nhrsp
|
47
|
+
end
|
48
|
+
|
49
|
+
# The request wasn't matched and normal net connection was disallowed.
|
50
|
+
def unmapped_request(request)
|
51
|
+
raise "unmatched_request :("
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module Responders
|
3
|
+
class Block
|
4
|
+
include HttpVanilli::AbstractResponder
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def match?(request)
|
11
|
+
!! @block[request]
|
12
|
+
end
|
13
|
+
|
14
|
+
def rack_response(request)
|
15
|
+
@block[request]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
module Responders
|
3
|
+
class Rack
|
4
|
+
include HttpVanilli::AbstractResponder
|
5
|
+
|
6
|
+
def initialize(app_class,*args,&block)
|
7
|
+
@app_class = app_class
|
8
|
+
@args = args
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
BadStatus = 999
|
13
|
+
def match?(request)
|
14
|
+
rsp = rack_response(request)
|
15
|
+
Array === rsp && rsp.first != BadStatus
|
16
|
+
end
|
17
|
+
|
18
|
+
def rack_response(request)
|
19
|
+
uri = request.uri
|
20
|
+
|
21
|
+
env = {
|
22
|
+
'REQUEST_METHOD' => request.original_request.method,
|
23
|
+
|
24
|
+
# this'll to for now
|
25
|
+
'SCRIPT_NAME' => "",
|
26
|
+
'PATH_INFO' => uri.path,
|
27
|
+
|
28
|
+
'QUERY_STRING' => uri.query,
|
29
|
+
'SERVER_NAME' => uri.host,
|
30
|
+
'SERVER_PORT' => uri.port
|
31
|
+
}
|
32
|
+
|
33
|
+
request.original_request.each_header {|k,v|
|
34
|
+
env["HTTP_#{k.upcase}"] = v
|
35
|
+
}
|
36
|
+
|
37
|
+
rack_input = StringIO.new((request.body || '').to_s)
|
38
|
+
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
|
39
|
+
|
40
|
+
env.update({"rack.version" => [1,1],
|
41
|
+
"rack.input" => rack_input,
|
42
|
+
"rack.errors" => $stderr,
|
43
|
+
|
44
|
+
"rack.multithread" => true,
|
45
|
+
"rack.multiprocess" => false,
|
46
|
+
"rack.run_once" => false,
|
47
|
+
|
48
|
+
"rack.url_scheme" => request.uri.scheme
|
49
|
+
})
|
50
|
+
|
51
|
+
app.call(env)
|
52
|
+
end
|
53
|
+
|
54
|
+
def app
|
55
|
+
inner_app = lambda {|l| [BadStatus,{'Content-Type' => 'text/plain'},['']]}
|
56
|
+
@app_class.new(inner_app,*@args,&@block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module HttpVanilli
|
2
|
+
class Response
|
3
|
+
StatusMessage = {
|
4
|
+
100, 'Continue',
|
5
|
+
101, 'Switching Protocols',
|
6
|
+
200, 'OK',
|
7
|
+
201, 'Created',
|
8
|
+
202, 'Accepted',
|
9
|
+
203, 'Non-Authoritative Information',
|
10
|
+
204, 'No Content',
|
11
|
+
205, 'Reset Content',
|
12
|
+
206, 'Partial Content',
|
13
|
+
300, 'Multiple Choices',
|
14
|
+
301, 'Moved Permanently',
|
15
|
+
302, 'Found',
|
16
|
+
303, 'See Other',
|
17
|
+
304, 'Not Modified',
|
18
|
+
305, 'Use Proxy',
|
19
|
+
307, 'Temporary Redirect',
|
20
|
+
400, 'Bad Request',
|
21
|
+
401, 'Unauthorized',
|
22
|
+
402, 'Payment Required',
|
23
|
+
403, 'Forbidden',
|
24
|
+
404, 'Not Found',
|
25
|
+
405, 'Method Not Allowed',
|
26
|
+
406, 'Not Acceptable',
|
27
|
+
407, 'Proxy Authentication Required',
|
28
|
+
408, 'Request Timeout',
|
29
|
+
409, 'Conflict',
|
30
|
+
410, 'Gone',
|
31
|
+
411, 'Length Required',
|
32
|
+
412, 'Precondition Failed',
|
33
|
+
413, 'Request Entity Too Large',
|
34
|
+
414, 'Request-URI Too Large',
|
35
|
+
415, 'Unsupported Media Type',
|
36
|
+
416, 'Request Range Not Satisfiable',
|
37
|
+
417, 'Expectation Failed',
|
38
|
+
500, 'Internal Server Error',
|
39
|
+
501, 'Not Implemented',
|
40
|
+
502, 'Bad Gateway',
|
41
|
+
503, 'Service Unavailable',
|
42
|
+
504, 'Gateway Timeout',
|
43
|
+
505, 'HTTP Version Not Supported'
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module HttpVanilli
|
4
|
+
class Util
|
5
|
+
def self.decode_userinfo_from_header(header)
|
6
|
+
header.sub(/^Basic /, "").unpack("m").first
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.encode_unsafe_chars_in_userinfo(userinfo)
|
10
|
+
unsafe_in_userinfo = /[^#{URI::REGEXP::PATTERN::UNRESERVED};&=+$,]|^(#{URI::REGEXP::PATTERN::ESCAPED})/
|
11
|
+
userinfo.split(":").map { |part| uri_escape(part, unsafe_in_userinfo) }.join(":")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.strip_default_port_from_uri(uri)
|
15
|
+
case uri
|
16
|
+
when %r{^http://} then uri.sub(%r{:80(/|$)}, '\1')
|
17
|
+
when %r{^https://} then uri.sub(%r{:443(/|$)}, '\1')
|
18
|
+
else uri
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Wrapper for URI escaping that switches between URI::Parser#escape and
|
23
|
+
# URI.escape for 1.9-compatibility
|
24
|
+
def self.uri_escape(*args)
|
25
|
+
if URI.const_defined?(:Parser)
|
26
|
+
URI::Parser.new.escape(*args)
|
27
|
+
else
|
28
|
+
URI.escape(*args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/http_vanilli.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
def tapp(tag=nil)
|
6
|
+
print "#{tag}=" if tag
|
7
|
+
pp self
|
8
|
+
self
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module HttpVanilli
|
13
|
+
autoload :Util , 'http_vanilli/util'
|
14
|
+
|
15
|
+
autoload :BasicMapper , 'http_vanilli/basic_mapper'
|
16
|
+
|
17
|
+
autoload :AbstractResponder, 'http_vanilli/abstract_responder'
|
18
|
+
autoload :Responders , 'http_vanilli/responders'
|
19
|
+
|
20
|
+
autoload :Request , 'http_vanilli/request'
|
21
|
+
autoload :Response, 'http_vanilli/response'
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def here
|
25
|
+
@here ||= Pathname(__FILE__).dirname
|
26
|
+
end
|
27
|
+
|
28
|
+
def override_net_http!
|
29
|
+
require here+'http_vanilli/net_http/override'
|
30
|
+
HttpVanilli::NetHttp.override!
|
31
|
+
end
|
32
|
+
|
33
|
+
def allow_net_connect!
|
34
|
+
@allow_net_connect = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def disallow_net_connect!
|
38
|
+
@allow_net_connect = false
|
39
|
+
end
|
40
|
+
|
41
|
+
def allow_net_connect?
|
42
|
+
!(FalseClass === @allow_net_connect)
|
43
|
+
end
|
44
|
+
|
45
|
+
def use_basic_mapper!(extra_responder_classes={})
|
46
|
+
self.request_mapper = BasicMapper.new(extra_responder_classes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def request_mapper=(request_mapper)
|
50
|
+
@request_mapper = request_mapper
|
51
|
+
end
|
52
|
+
|
53
|
+
def request_mapper
|
54
|
+
unless @request_mapper
|
55
|
+
raise "HttpVanilli requires a request mapper.\n" +
|
56
|
+
"Use the basic mapper with HttpVanilli.basic_mapper! ,\n" +
|
57
|
+
"or plug one in with HttpVanilli.request_mapper=Yourmapper.new"
|
58
|
+
end
|
59
|
+
@request_mapper
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http_vanilli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Lachie Cox
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-01 00:00:00 +10:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: exemplor
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
description: HttpVanilli gives you lip-synced http connections with pluggable band members for testing.
|
33
|
+
email:
|
34
|
+
- lachiec@gmail.com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- lib/angry_web/matcher.rb
|
43
|
+
- lib/http_vanilli/abstract_responder.rb
|
44
|
+
- lib/http_vanilli/basic_mapper.rb
|
45
|
+
- lib/http_vanilli/basic_mapping.rb
|
46
|
+
- lib/http_vanilli/net_http/override.rb
|
47
|
+
- lib/http_vanilli/net_http/request.rb
|
48
|
+
- lib/http_vanilli/net_http/response.rb
|
49
|
+
- lib/http_vanilli/net_http/stubs.rb
|
50
|
+
- lib/http_vanilli/net_http/util.rb
|
51
|
+
- lib/http_vanilli/other_mapper.rb
|
52
|
+
- lib/http_vanilli/request.rb
|
53
|
+
- lib/http_vanilli/responders/block.rb
|
54
|
+
- lib/http_vanilli/responders/rack.rb
|
55
|
+
- lib/http_vanilli/responders.rb
|
56
|
+
- lib/http_vanilli/response.rb
|
57
|
+
- lib/http_vanilli/test_adapters/rspec.rb
|
58
|
+
- lib/http_vanilli/util.rb
|
59
|
+
- lib/http_vanilli/version.rb
|
60
|
+
- lib/http_vanilli.rb
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://github.com/lachie/http_vanilli
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 1
|
83
|
+
- 3
|
84
|
+
- 6
|
85
|
+
version: 1.3.6
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: http_vanilli
|
89
|
+
rubygems_version: 1.3.6
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Flexible web connection mocking lib.
|
93
|
+
test_files: []
|
94
|
+
|