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