webmachine 1.6.0 → 2.0.0.beta
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +2 -5
- data/documentation/adapters.md +2 -11
- data/examples/debugger.rb +5 -3
- data/examples/logging.rb +2 -2
- data/examples/webrick.rb +2 -2
- data/lib/webmachine/adapter.rb +0 -3
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -1
- data/lib/webmachine/adapters/rack.rb +38 -34
- data/lib/webmachine/adapters/rack_mapped.rb +1 -2
- data/lib/webmachine/adapters/webrick.rb +11 -12
- data/lib/webmachine/adapters.rb +0 -2
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +1 -1
- data/lib/webmachine/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +12 -12
- data/lib/webmachine/cookie.rb +20 -18
- data/lib/webmachine/decision/conneg.rb +24 -24
- data/lib/webmachine/decision/falsey.rb +0 -1
- data/lib/webmachine/decision/flow.rb +19 -20
- data/lib/webmachine/decision/fsm.rb +4 -4
- data/lib/webmachine/decision/helpers.rb +21 -21
- data/lib/webmachine/dispatcher/route.rb +28 -28
- data/lib/webmachine/dispatcher.rb +3 -2
- data/lib/webmachine/errors.rb +7 -8
- data/lib/webmachine/etags.rb +2 -1
- data/lib/webmachine/header_negotiation.rb +5 -6
- data/lib/webmachine/headers.rb +5 -5
- data/lib/webmachine/media_type.rb +5 -5
- data/lib/webmachine/quoted_string.rb +3 -3
- data/lib/webmachine/request.rb +7 -10
- data/lib/webmachine/rescueable_exception.rb +3 -3
- data/lib/webmachine/resource/authentication.rb +3 -4
- data/lib/webmachine/resource/callbacks.rb +3 -3
- data/lib/webmachine/resource/encodings.rb +3 -9
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/response.rb +7 -9
- data/lib/webmachine/spec/adapter_lint.rb +67 -69
- data/lib/webmachine/spec/test_resource.rb +22 -22
- data/lib/webmachine/streaming/encoder.rb +3 -2
- data/lib/webmachine/streaming/io_encoder.rb +4 -3
- data/lib/webmachine/trace/fsm.rb +25 -18
- data/lib/webmachine/trace/resource_proxy.rb +10 -9
- data/lib/webmachine/trace/static/http-headers-status-v3.png +0 -0
- data/lib/webmachine/trace/trace_resource.rb +22 -24
- data/lib/webmachine/trace.rb +7 -6
- data/lib/webmachine/translation.rb +3 -3
- data/lib/webmachine/version.rb +1 -1
- metadata +52 -86
- data/.gitignore +0 -31
- data/Gemfile +0 -46
- data/Guardfile +0 -11
- data/RELEASING.md +0 -21
- data/Rakefile +0 -44
- data/lib/webmachine/adapters/httpkit.rb +0 -74
- data/lib/webmachine/adapters/reel.rb +0 -113
- data/memory_test.rb +0 -37
- data/spec/spec_helper.rb +0 -56
- data/spec/webmachine/adapter_spec.rb +0 -39
- data/spec/webmachine/adapters/httpkit_spec.rb +0 -10
- data/spec/webmachine/adapters/rack_mapped_spec.rb +0 -71
- data/spec/webmachine/adapters/rack_spec.rb +0 -62
- data/spec/webmachine/adapters/reel_spec.rb +0 -76
- data/spec/webmachine/adapters/webrick_spec.rb +0 -12
- data/spec/webmachine/application_spec.rb +0 -74
- data/spec/webmachine/chunked_body_spec.rb +0 -30
- data/spec/webmachine/configuration_spec.rb +0 -27
- data/spec/webmachine/cookie_spec.rb +0 -99
- data/spec/webmachine/decision/conneg_spec.rb +0 -166
- data/spec/webmachine/decision/falsey_spec.rb +0 -8
- data/spec/webmachine/decision/flow_spec.rb +0 -1148
- data/spec/webmachine/decision/fsm_spec.rb +0 -163
- data/spec/webmachine/decision/helpers_spec.rb +0 -216
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +0 -22
- data/spec/webmachine/dispatcher/route_spec.rb +0 -248
- data/spec/webmachine/dispatcher_spec.rb +0 -104
- data/spec/webmachine/errors_spec.rb +0 -13
- data/spec/webmachine/etags_spec.rb +0 -75
- data/spec/webmachine/events_spec.rb +0 -58
- data/spec/webmachine/headers_spec.rb +0 -99
- data/spec/webmachine/media_type_spec.rb +0 -85
- data/spec/webmachine/request_spec.rb +0 -273
- data/spec/webmachine/rescueable_exception_spec.rb +0 -15
- data/spec/webmachine/resource/authentication_spec.rb +0 -68
- data/spec/webmachine/response_spec.rb +0 -51
- data/spec/webmachine/trace/fsm_spec.rb +0 -37
- data/spec/webmachine/trace/resource_proxy_spec.rb +0 -34
- data/spec/webmachine/trace/trace_store_spec.rb +0 -29
- data/spec/webmachine/trace_spec.rb +0 -17
- data/webmachine.gemspec +0 -25
@@ -23,11 +23,11 @@ module Webmachine
|
|
23
23
|
obj
|
24
24
|
when MEDIA_TYPE_REGEX
|
25
25
|
type, raw_params = $1, $2
|
26
|
-
params =
|
26
|
+
params = raw_params.scan(PARAMS_REGEX).map { |m| [m[0], m[2].to_s] }.to_h
|
27
27
|
new(type, params)
|
28
28
|
else
|
29
29
|
unless Array === obj && String === obj[0] && Hash === obj[1]
|
30
|
-
raise ArgumentError, t('invalid_media_type', :
|
30
|
+
raise ArgumentError, t('invalid_media_type', type: obj.inspect)
|
31
31
|
end
|
32
32
|
type = parse(obj[0])
|
33
33
|
type.params.merge!(obj[1])
|
@@ -43,7 +43,7 @@ module Webmachine
|
|
43
43
|
|
44
44
|
# @param [String] type the main media type, e.g. application/json
|
45
45
|
# @param [Hash] params the media type parameters
|
46
|
-
def initialize(type, params={})
|
46
|
+
def initialize(type, params = {})
|
47
47
|
@type, @params = type, params
|
48
48
|
end
|
49
49
|
|
@@ -88,13 +88,13 @@ module Webmachine
|
|
88
88
|
# @param [Hash] params the requested params
|
89
89
|
# @return [true,false] whether it is an acceptable match
|
90
90
|
def params_match?(other)
|
91
|
-
other.all? {|k,v| params[k] == v }
|
91
|
+
other.all? { |k, v| params[k] == v }
|
92
92
|
end
|
93
93
|
|
94
94
|
# Reconstitutes the type into a String
|
95
95
|
# @return [String] the type as a String
|
96
96
|
def to_s
|
97
|
-
[type, *params.map {|k,v| "#{k}=#{v}" }].join(
|
97
|
+
[type, *params.map { |k, v| "#{k}=#{v}" }].join(';')
|
98
98
|
end
|
99
99
|
|
100
100
|
# @return [String] The major type, e.g. "application", "text", "image"
|
@@ -19,10 +19,10 @@ module Webmachine
|
|
19
19
|
|
20
20
|
# Ensures that quotes exist around a quoted-string
|
21
21
|
def quote(str)
|
22
|
-
if str
|
22
|
+
if QS_ANCHORED.match?(str)
|
23
23
|
str
|
24
24
|
else
|
25
|
-
%
|
25
|
+
%("#{escape_quotes str}")
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -33,7 +33,7 @@ module Webmachine
|
|
33
33
|
|
34
34
|
# Unescapes quotes within a quoted string
|
35
35
|
def unescape_quotes(str)
|
36
|
-
str.
|
36
|
+
str.delete('\\')
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
data/lib/webmachine/request.rb
CHANGED
@@ -21,7 +21,7 @@ module Webmachine
|
|
21
21
|
# @param [Headers] headers the HTTP request headers
|
22
22
|
# @param [String,#to_s,#each,nil] body the entity included in the
|
23
23
|
# request, if present
|
24
|
-
def initialize(method, uri, headers, body, routing_tokens=nil, base_uri=nil)
|
24
|
+
def initialize(method, uri, headers, body, routing_tokens = nil, base_uri = nil)
|
25
25
|
@method, @headers, @body = method, headers, body
|
26
26
|
@uri = build_uri(uri, headers)
|
27
27
|
@routing_tokens = routing_tokens || @uri.path.match(ROUTING_PATH_MATCH)[1].split(SLASH)
|
@@ -37,12 +37,12 @@ module Webmachine
|
|
37
37
|
# lowercased-underscored version of the header name, e.g.
|
38
38
|
# `if_unmodified_since`.
|
39
39
|
def method_missing(m, *args, &block)
|
40
|
-
if m
|
40
|
+
if HTTP_HEADERS_MATCH.match?(m)
|
41
41
|
# Access headers more easily as underscored methods.
|
42
42
|
header_name = m.to_s.tr(UNDERSCORE, DASH)
|
43
43
|
if (header_value = @headers[header_name])
|
44
44
|
# Make future lookups faster.
|
45
|
-
self.class.class_eval <<-RUBY, __FILE__, __LINE__
|
45
|
+
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
46
46
|
def #{m}
|
47
47
|
@headers["#{header_name}"]
|
48
48
|
end
|
@@ -66,8 +66,8 @@ module Webmachine
|
|
66
66
|
def query
|
67
67
|
unless @query
|
68
68
|
@query = {}
|
69
|
-
(uri.query || '').split(
|
70
|
-
key, value = kv.split(
|
69
|
+
(uri.query || '').split('&').each do |kv|
|
70
|
+
key, value = kv.split('=')
|
71
71
|
if key && value
|
72
72
|
key, value = CGI.unescape(key), CGI.unescape(value)
|
73
73
|
@query[key] = value
|
@@ -82,9 +82,7 @@ module Webmachine
|
|
82
82
|
# @return [Hash]
|
83
83
|
# {} if no Cookies header set
|
84
84
|
def cookies
|
85
|
-
|
86
|
-
@cookies = Webmachine::Cookie.parse(headers['Cookie'])
|
87
|
-
end
|
85
|
+
@cookies ||= Webmachine::Cookie.parse(headers['Cookie'])
|
88
86
|
@cookies
|
89
87
|
end
|
90
88
|
|
@@ -93,7 +91,7 @@ module Webmachine
|
|
93
91
|
# @return [Boolean]
|
94
92
|
# true if this request was made via HTTPS
|
95
93
|
def https?
|
96
|
-
uri.scheme ==
|
94
|
+
uri.scheme == 'https'
|
97
95
|
end
|
98
96
|
|
99
97
|
# Is this a GET request?
|
@@ -191,6 +189,5 @@ module Webmachine
|
|
191
189
|
|
192
190
|
parse_host(uri, headers.fetch(HOST))
|
193
191
|
end
|
194
|
-
|
195
192
|
end # class Request
|
196
193
|
end # module Webmachine
|
@@ -2,7 +2,7 @@ module Webmachine::RescuableException
|
|
2
2
|
require_relative 'errors'
|
3
3
|
require 'set'
|
4
4
|
|
5
|
-
UNRESCUABLE_DEFAULTS =
|
5
|
+
UNRESCUABLE_DEFAULTS = [
|
6
6
|
Webmachine::MalformedRequest,
|
7
7
|
NoMemoryError, SystemExit, SignalException
|
8
8
|
].freeze
|
@@ -45,7 +45,7 @@ module Webmachine::RescuableException
|
|
45
45
|
# @param (see #remove)
|
46
46
|
#
|
47
47
|
def self.add(*exceptions)
|
48
|
-
exceptions.each{|e| UNRESCUABLE.delete(e)}
|
48
|
+
exceptions.each { |e| UNRESCUABLE.delete(e) }
|
49
49
|
end
|
50
50
|
|
51
51
|
#
|
@@ -57,6 +57,6 @@ module Webmachine::RescuableException
|
|
57
57
|
# A subclass of Exception.
|
58
58
|
#
|
59
59
|
def self.remove(*exceptions)
|
60
|
-
exceptions.each{|e| UNRESCUABLE.add(e)}
|
60
|
+
exceptions.each { |e| UNRESCUABLE.add(e) }
|
61
61
|
end
|
62
62
|
end
|
@@ -23,14 +23,13 @@ module Webmachine
|
|
23
23
|
# @yieldparam [String] user the passed username
|
24
24
|
# @yieldparam [String] password the passed password
|
25
25
|
# @yieldreturn [true,false] whether the username/password is correct
|
26
|
-
def basic_auth(header, realm=
|
27
|
-
if header =~ BASIC_HEADER && (
|
26
|
+
def basic_auth(header, realm = 'Webmachine')
|
27
|
+
if header =~ BASIC_HEADER && yield(*$1.unpack1('m*').split(/:/, 2))
|
28
28
|
true
|
29
29
|
else
|
30
|
-
%
|
30
|
+
%(Basic realm="#{realm}")
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
34
33
|
end # module Authentication
|
35
34
|
end # class Resource
|
36
35
|
end # module Webmachine
|
@@ -265,7 +265,7 @@ module Webmachine
|
|
265
265
|
# @api callback
|
266
266
|
# @see Encodings
|
267
267
|
def encodings_provided
|
268
|
-
{IDENTITY => :encode_identity
|
268
|
+
{IDENTITY => :encode_identity}
|
269
269
|
end
|
270
270
|
|
271
271
|
# If this method is implemented, it should return a list of
|
@@ -360,7 +360,8 @@ module Webmachine
|
|
360
360
|
# constructed and sent. The return value is ignored, so any effect
|
361
361
|
# of this method must be by modifying the response.
|
362
362
|
# @api callback
|
363
|
-
def finish_request
|
363
|
+
def finish_request
|
364
|
+
end
|
364
365
|
|
365
366
|
#
|
366
367
|
# This method is called when an error is raised within a subclass of
|
@@ -389,7 +390,6 @@ module Webmachine
|
|
389
390
|
def validate_content_checksum
|
390
391
|
nil
|
391
392
|
end
|
392
|
-
|
393
393
|
end # module Callbacks
|
394
394
|
end # class Resource
|
395
395
|
end # module Webmachine
|
@@ -15,23 +15,17 @@ module Webmachine
|
|
15
15
|
# The 'deflate' encoding, which uses libz's DEFLATE compression.
|
16
16
|
def encode_deflate(data)
|
17
17
|
# The deflate options were borrowed from Rack and Mongrel1.
|
18
|
-
Zlib::Deflate.deflate(data,
|
19
|
-
# drop the zlib header which causes both Safari and IE to choke
|
20
|
-
-Zlib::MAX_WBITS,
|
21
|
-
Zlib::DEF_MEM_LEVEL,
|
22
|
-
Zlib::DEFAULT_STRATEGY
|
23
|
-
])
|
18
|
+
Zlib::Deflate.deflate(data, Zlib::DEFAULT_COMPRESSION, -Zlib::MAX_WBITS, Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY)
|
24
19
|
end
|
25
20
|
|
26
21
|
# The 'gzip' encoding, which uses GNU Zip (via libz).
|
27
22
|
# @note Because of the header/checksum requirements, gzip cannot
|
28
23
|
# be used on streamed responses.
|
29
24
|
def encode_gzip(data)
|
30
|
-
|
31
|
-
Zlib::GzipWriter.wrap(StringIO.new(out)){|gz| gz << data }
|
25
|
+
''.tap do |out|
|
26
|
+
Zlib::GzipWriter.wrap(StringIO.new(out)) { |gz| gz << data }
|
32
27
|
end
|
33
28
|
end
|
34
|
-
|
35
29
|
end # module Encodings
|
36
30
|
end # class Resource
|
37
31
|
end # module Webmachine
|
data/lib/webmachine/resource.rb
CHANGED
@@ -59,11 +59,11 @@ module Webmachine
|
|
59
59
|
end
|
60
60
|
|
61
61
|
private
|
62
|
+
|
62
63
|
# When no specific charsets are provided, this acts as an identity
|
63
64
|
# on the response body. Probably deserves some refactoring.
|
64
65
|
def charset_nop(x)
|
65
66
|
x
|
66
67
|
end
|
67
|
-
|
68
68
|
end # class Resource
|
69
69
|
end # module Webmachine
|
data/lib/webmachine/response.rb
CHANGED
@@ -35,7 +35,7 @@ module Webmachine
|
|
35
35
|
# of the target resource, or manually set the Location header
|
36
36
|
# using {#headers}.
|
37
37
|
# @param [String, URI] location the target of the redirection
|
38
|
-
def do_redirect(location=nil)
|
38
|
+
def do_redirect(location = nil)
|
39
39
|
headers['Location'] = location.to_s if location
|
40
40
|
self.redirect = true
|
41
41
|
end
|
@@ -54,8 +54,8 @@ module Webmachine
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
alias_method :is_redirect?, :redirect
|
58
|
+
alias_method :redirect_to, :do_redirect
|
59
59
|
|
60
60
|
# A {Hash} that can flatten array values into single values with a separator
|
61
61
|
class HeaderHash < ::Hash
|
@@ -63,17 +63,15 @@ module Webmachine
|
|
63
63
|
# @param [String] The separator used to join Array values
|
64
64
|
# @return [HeaderHash] A new {HeaderHash} with Array values flattened
|
65
65
|
def flattened(separator = ',')
|
66
|
-
|
66
|
+
collect { |k, v|
|
67
67
|
case v
|
68
68
|
when Array
|
69
|
-
[k,v.join(separator)]
|
69
|
+
[k, v.join(separator)]
|
70
70
|
else
|
71
|
-
[k,v]
|
71
|
+
[k, v]
|
72
72
|
end
|
73
|
-
}
|
74
|
-
|
73
|
+
}.to_h
|
75
74
|
end
|
76
75
|
end
|
77
|
-
|
78
76
|
end # class Response
|
79
77
|
end # module Webmachine
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'webmachine/spec/test_resource'
|
2
|
+
require 'net/http'
|
3
3
|
|
4
|
-
ADDRESS =
|
4
|
+
ADDRESS = '127.0.0.1'
|
5
5
|
|
6
6
|
shared_examples_for :adapter_lint do
|
7
7
|
attr_reader :client
|
@@ -12,7 +12,7 @@ shared_examples_for :adapter_lint do
|
|
12
12
|
temp_server = TCPServer.new(ADDRESS, 0)
|
13
13
|
port = temp_server.addr[1]
|
14
14
|
temp_server.close # only frees Ruby resource, socket is in TIME_WAIT at OS level
|
15
|
-
|
15
|
+
# so we can't have our adapter use it too quickly
|
16
16
|
|
17
17
|
sleep(0.1) # 'Wait' for temp_server to *really* close, not just TIME_WAIT
|
18
18
|
port
|
@@ -20,10 +20,10 @@ shared_examples_for :adapter_lint do
|
|
20
20
|
|
21
21
|
def create_test_application(port)
|
22
22
|
Webmachine::Application.new.tap do |application|
|
23
|
-
application.dispatcher.add_route [
|
23
|
+
application.dispatcher.add_route ['test'], Test::Resource
|
24
24
|
|
25
25
|
application.configure do |c|
|
26
|
-
c.ip
|
26
|
+
c.ip = ADDRESS
|
27
27
|
c.port = port
|
28
28
|
end
|
29
29
|
end
|
@@ -37,12 +37,10 @@ shared_examples_for :adapter_lint do
|
|
37
37
|
|
38
38
|
def wait_until_server_responds_to(client)
|
39
39
|
Timeout.timeout(5, TestApplicationNotResponsive) do
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
retry
|
45
|
-
end
|
40
|
+
client.start
|
41
|
+
rescue Errno::ECONNREFUSED
|
42
|
+
sleep(0.01)
|
43
|
+
retry
|
46
44
|
end
|
47
45
|
end
|
48
46
|
|
@@ -62,9 +60,9 @@ shared_examples_for :adapter_lint do
|
|
62
60
|
@server_thread.kill
|
63
61
|
end
|
64
62
|
|
65
|
-
it
|
66
|
-
request = Net::HTTP::Get.new(
|
67
|
-
request[
|
63
|
+
it 'provides the request URI' do
|
64
|
+
request = Net::HTTP::Get.new('/test')
|
65
|
+
request['Accept'] = 'test/response.request_uri'
|
68
66
|
response = client.request(request)
|
69
67
|
expect(response.body).to eq("http://#{ADDRESS}:#{@port}/test")
|
70
68
|
end
|
@@ -80,92 +78,92 @@ shared_examples_for :adapter_lint do
|
|
80
78
|
# end
|
81
79
|
# end
|
82
80
|
|
83
|
-
it
|
84
|
-
request = Net::HTTP::Put.new(
|
85
|
-
request.body =
|
86
|
-
request[
|
81
|
+
it 'provides a string-like request body' do
|
82
|
+
request = Net::HTTP::Put.new('/test')
|
83
|
+
request.body = 'Hello, World!'
|
84
|
+
request['Content-Type'] = 'test/request.stringbody'
|
87
85
|
response = client.request(request)
|
88
|
-
expect(response[
|
89
|
-
expect(response.body).to eq(
|
86
|
+
expect(response['Content-Length']).to eq('21')
|
87
|
+
expect(response.body).to eq('String: Hello, World!')
|
90
88
|
end
|
91
89
|
|
92
|
-
it
|
93
|
-
request = Net::HTTP::Put.new(
|
94
|
-
request.body =
|
95
|
-
request[
|
90
|
+
it 'provides an enumerable request body' do
|
91
|
+
request = Net::HTTP::Put.new('/test')
|
92
|
+
request.body = 'Hello, World!'
|
93
|
+
request['Content-Type'] = 'test/request.enumbody'
|
96
94
|
response = client.request(request)
|
97
|
-
expect(response[
|
98
|
-
expect(response.body).to eq(
|
95
|
+
expect(response['Content-Length']).to eq('19')
|
96
|
+
expect(response.body).to eq('Enum: Hello, World!')
|
99
97
|
end
|
100
98
|
|
101
|
-
it
|
102
|
-
request = Net::HTTP::Get.new(
|
99
|
+
it 'handles missing pages' do
|
100
|
+
request = Net::HTTP::Get.new('/missing')
|
103
101
|
response = client.request(request)
|
104
|
-
expect(response.code).to eq(
|
105
|
-
expect(response[
|
102
|
+
expect(response.code).to eq('404')
|
103
|
+
expect(response['Content-Type']).to eq('text/html')
|
106
104
|
end
|
107
105
|
|
108
|
-
it
|
109
|
-
request = Net::HTTP::Post.new(
|
110
|
-
request.body =
|
106
|
+
it 'handles empty response bodies' do
|
107
|
+
request = Net::HTTP::Post.new('/test')
|
108
|
+
request.body = ''
|
111
109
|
response = client.request(request)
|
112
|
-
expect(response.code).to eq(
|
113
|
-
expect([
|
110
|
+
expect(response.code).to eq('204')
|
111
|
+
expect(['0', nil]).to include(response['Content-Length'])
|
114
112
|
expect(response.body).to be_nil
|
115
113
|
end
|
116
114
|
|
117
|
-
it
|
118
|
-
request = Net::HTTP::Get.new(
|
119
|
-
request[
|
115
|
+
it 'handles string response bodies' do
|
116
|
+
request = Net::HTTP::Get.new('/test')
|
117
|
+
request['Accept'] = 'test/response.stringbody'
|
120
118
|
response = client.request(request)
|
121
|
-
expect(response[
|
122
|
-
expect(response.body).to eq(
|
119
|
+
expect(response['Content-Length']).to eq('20')
|
120
|
+
expect(response.body).to eq('String response body')
|
123
121
|
end
|
124
122
|
|
125
|
-
it
|
126
|
-
request = Net::HTTP::Get.new(
|
127
|
-
request[
|
123
|
+
it 'handles enumerable response bodies' do
|
124
|
+
request = Net::HTTP::Get.new('/test')
|
125
|
+
request['Accept'] = 'test/response.enumbody'
|
128
126
|
response = client.request(request)
|
129
|
-
expect(response[
|
130
|
-
expect(response.body).to eq(
|
127
|
+
expect(response['Transfer-Encoding']).to eq('chunked')
|
128
|
+
expect(response.body).to eq('Enumerable response body')
|
131
129
|
end
|
132
130
|
|
133
|
-
it
|
134
|
-
request = Net::HTTP::Get.new(
|
135
|
-
request[
|
131
|
+
it 'handles proc response bodies' do
|
132
|
+
request = Net::HTTP::Get.new('/test')
|
133
|
+
request['Accept'] = 'test/response.procbody'
|
136
134
|
response = client.request(request)
|
137
|
-
expect(response[
|
138
|
-
expect(response.body).to eq(
|
135
|
+
expect(response['Transfer-Encoding']).to eq('chunked')
|
136
|
+
expect(response.body).to eq('Proc response body')
|
139
137
|
end
|
140
138
|
|
141
|
-
it
|
142
|
-
request = Net::HTTP::Get.new(
|
143
|
-
request[
|
139
|
+
it 'handles fiber response bodies' do
|
140
|
+
request = Net::HTTP::Get.new('/test')
|
141
|
+
request['Accept'] = 'test/response.fiberbody'
|
144
142
|
response = client.request(request)
|
145
|
-
expect(response[
|
146
|
-
expect(response.body).to eq(
|
143
|
+
expect(response['Transfer-Encoding']).to eq('chunked')
|
144
|
+
expect(response.body).to eq('Fiber response body')
|
147
145
|
end
|
148
146
|
|
149
|
-
it
|
150
|
-
request = Net::HTTP::Get.new(
|
151
|
-
request[
|
147
|
+
it 'handles io response bodies' do
|
148
|
+
request = Net::HTTP::Get.new('/test')
|
149
|
+
request['Accept'] = 'test/response.iobody'
|
152
150
|
response = client.request(request)
|
153
|
-
expect(response[
|
151
|
+
expect(response['Content-Length']).to eq('17')
|
154
152
|
expect(response.body).to eq("IO response body\n")
|
155
153
|
end
|
156
154
|
|
157
|
-
it
|
158
|
-
request = Net::HTTP::Get.new(
|
159
|
-
request[
|
160
|
-
request[
|
155
|
+
it 'handles request cookies' do
|
156
|
+
request = Net::HTTP::Get.new('/test')
|
157
|
+
request['Accept'] = 'test/response.cookies'
|
158
|
+
request['Cookie'] = 'echo=echocookie'
|
161
159
|
response = client.request(request)
|
162
|
-
expect(response.body).to eq(
|
160
|
+
expect(response.body).to eq('echocookie')
|
163
161
|
end
|
164
162
|
|
165
|
-
it
|
166
|
-
request = Net::HTTP::Get.new(
|
167
|
-
request[
|
163
|
+
it 'handles response cookies' do
|
164
|
+
request = Net::HTTP::Get.new('/test')
|
165
|
+
request['Accept'] = 'test/response.cookies'
|
168
166
|
response = client.request(request)
|
169
|
-
expect(response[
|
167
|
+
expect(response['Set-Cookie']).to eq('cookie=monster, rodeo=clown')
|
170
168
|
end
|
171
169
|
end
|
@@ -1,35 +1,35 @@
|
|
1
1
|
module Test
|
2
2
|
class Resource < Webmachine::Resource
|
3
3
|
def allowed_methods
|
4
|
-
[
|
4
|
+
['GET', 'PUT', 'POST']
|
5
5
|
end
|
6
6
|
|
7
7
|
def content_types_accepted
|
8
8
|
[
|
9
|
-
[
|
10
|
-
[
|
9
|
+
['test/request.stringbody', :from_string],
|
10
|
+
['test/request.enumbody', :from_enum]
|
11
11
|
]
|
12
12
|
end
|
13
13
|
|
14
14
|
def content_types_provided
|
15
15
|
[
|
16
|
-
[
|
17
|
-
[
|
18
|
-
[
|
19
|
-
[
|
20
|
-
[
|
21
|
-
[
|
22
|
-
[
|
23
|
-
[
|
16
|
+
['test/response.stringbody', :to_string],
|
17
|
+
['test/response.enumbody', :to_enum],
|
18
|
+
['test/response.procbody', :to_proc],
|
19
|
+
['test/response.fiberbody', :to_fiber],
|
20
|
+
['test/response.iobody', :to_io_body],
|
21
|
+
['test/response.cookies', :to_cookies],
|
22
|
+
['test/response.request_uri', :to_request_uri],
|
23
|
+
['test/response.rack_env', :to_rack_env]
|
24
24
|
]
|
25
25
|
end
|
26
26
|
|
27
27
|
def from_string
|
28
|
-
response.body = "String: #{request.body
|
28
|
+
response.body = "String: #{request.body}"
|
29
29
|
end
|
30
30
|
|
31
31
|
def from_enum
|
32
|
-
response.body =
|
32
|
+
response.body = 'Enum: '
|
33
33
|
request.body.each do |part|
|
34
34
|
response.body += part
|
35
35
|
end
|
@@ -41,22 +41,22 @@ module Test
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def to_string
|
44
|
-
|
44
|
+
'String response body'
|
45
45
|
end
|
46
46
|
|
47
47
|
def to_enum
|
48
|
-
[
|
48
|
+
['Enumerable ', 'response ', 'body']
|
49
49
|
end
|
50
50
|
|
51
51
|
def to_proc
|
52
|
-
|
52
|
+
proc { 'Proc response body' }
|
53
53
|
end
|
54
54
|
|
55
55
|
def to_fiber
|
56
56
|
Fiber.new do
|
57
|
-
Fiber.yield
|
58
|
-
Fiber.yield
|
59
|
-
|
57
|
+
Fiber.yield 'Fiber '
|
58
|
+
Fiber.yield 'response '
|
59
|
+
'body'
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -65,12 +65,12 @@ module Test
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def to_cookies
|
68
|
-
response.set_cookie(
|
69
|
-
response.set_cookie(
|
68
|
+
response.set_cookie('cookie', 'monster')
|
69
|
+
response.set_cookie('rodeo', 'clown')
|
70
70
|
# FIXME: Mongrel/WEBrick fail if this method returns nil
|
71
71
|
# Might be a net/http issue. Is this a bug?
|
72
72
|
# @see Flow#o18, Helpers#encode_body_if_set
|
73
|
-
request.cookies[
|
73
|
+
request.cookies['echo'] || ''
|
74
74
|
end
|
75
75
|
|
76
76
|
def to_request_uri
|
@@ -12,12 +12,13 @@ module Webmachine
|
|
12
12
|
end
|
13
13
|
|
14
14
|
protected
|
15
|
+
|
15
16
|
# @return [true, false] whether the stream will be modified by
|
16
17
|
# the encoder and/or charsetter. Only returns true if using the
|
17
18
|
# built-in "encode_identity" and "charset_nop" methods.
|
18
19
|
def is_unencoded?
|
19
|
-
encoder.to_s ==
|
20
|
-
charsetter.to_s ==
|
20
|
+
encoder.to_s == 'encode_identity' &&
|
21
|
+
charsetter.to_s == 'charset_nop'
|
21
22
|
end
|
22
23
|
end # class Encoder
|
23
24
|
end # module Streaming
|
@@ -13,7 +13,7 @@ module Webmachine
|
|
13
13
|
# @yield [chunk]
|
14
14
|
# @yieldparam [String] chunk a chunk of the response, encoded
|
15
15
|
def each
|
16
|
-
while chunk = body.read(CHUNK_SIZE)
|
16
|
+
while (chunk = body.read(CHUNK_SIZE)) && (chunk != '')
|
17
17
|
yield resource.send(encoder, resource.send(charsetter, chunk))
|
18
18
|
end
|
19
19
|
end
|
@@ -26,7 +26,7 @@ module Webmachine
|
|
26
26
|
if can_copy_stream?
|
27
27
|
IO.copy_stream(body, outstream)
|
28
28
|
else
|
29
|
-
each {|chunk| outstream << chunk }
|
29
|
+
each { |chunk| outstream << chunk }
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -60,9 +60,10 @@ module Webmachine
|
|
60
60
|
size == 0
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
alias_method :bytesize, :size
|
64
64
|
|
65
65
|
private
|
66
|
+
|
66
67
|
def can_copy_stream?
|
67
68
|
IO.respond_to?(:copy_stream) && is_unencoded? && !is_string_io?
|
68
69
|
end
|