webmachine 1.1.0 → 1.2.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/Gemfile +5 -4
- data/README.md +25 -2
- data/examples/debugger.rb +7 -0
- data/examples/logging.rb +41 -0
- data/lib/webmachine/adapters.rb +1 -1
- data/lib/webmachine/adapters/hatetepe.rb +5 -1
- data/lib/webmachine/adapters/lazy_request_body.rb +7 -7
- data/lib/webmachine/adapters/mongrel.rb +27 -12
- data/lib/webmachine/adapters/rack.rb +42 -6
- data/lib/webmachine/adapters/reel.rb +91 -23
- data/lib/webmachine/adapters/webrick.rb +12 -6
- data/lib/webmachine/application.rb +1 -0
- data/lib/webmachine/cookie.rb +1 -1
- data/lib/webmachine/dispatcher.rb +7 -1
- data/lib/webmachine/events.rb +179 -0
- data/lib/webmachine/events/instrumented_event.rb +19 -0
- data/lib/webmachine/response.rb +1 -1
- data/lib/webmachine/streaming/io_encoder.rb +5 -1
- data/lib/webmachine/trace.rb +18 -0
- data/lib/webmachine/trace/fsm.rb +4 -1
- data/lib/webmachine/trace/listener.rb +12 -0
- data/lib/webmachine/version.rb +1 -1
- data/spec/spec_helper.rb +11 -0
- data/spec/support/adapter_lint.rb +125 -0
- data/spec/support/test_resource.rb +73 -0
- data/spec/webmachine/adapters/hatetepe_spec.rb +2 -2
- data/spec/webmachine/adapters/mongrel_spec.rb +6 -51
- data/spec/webmachine/adapters/rack_spec.rb +22 -155
- data/spec/webmachine/adapters/reel_spec.rb +59 -7
- data/spec/webmachine/adapters/webrick_spec.rb +7 -13
- data/spec/webmachine/cookie_spec.rb +1 -1
- data/spec/webmachine/decision/helpers_spec.rb +10 -0
- data/spec/webmachine/events_spec.rb +58 -0
- data/spec/webmachine/request_spec.rb +41 -0
- data/webmachine.gemspec +1 -1
- metadata +63 -24
@@ -9,16 +9,22 @@ module Webmachine
|
|
9
9
|
module Adapters
|
10
10
|
# Connects Webmachine to WEBrick.
|
11
11
|
class WEBrick < Adapter
|
12
|
+
# Used to override default WEBRick options (useful in testing)
|
13
|
+
DEFAULT_OPTIONS = {}
|
12
14
|
|
13
15
|
# Starts the WEBrick adapter
|
14
16
|
def run
|
15
|
-
options = {
|
17
|
+
options = DEFAULT_OPTIONS.merge({
|
16
18
|
:Port => configuration.port,
|
17
19
|
:BindAddress => configuration.ip
|
18
|
-
}.merge(configuration.adapter_options)
|
19
|
-
server =
|
20
|
-
trap("INT"){
|
21
|
-
|
20
|
+
}).merge(configuration.adapter_options)
|
21
|
+
@server = Server.new(dispatcher, options)
|
22
|
+
trap("INT") { shutdown }
|
23
|
+
@server.start
|
24
|
+
end
|
25
|
+
|
26
|
+
def shutdown
|
27
|
+
@server.shutdown if @server
|
22
28
|
end
|
23
29
|
|
24
30
|
# WEBRick::HTTPServer that is run by the WEBrick adapter.
|
@@ -51,7 +57,7 @@ module Webmachine
|
|
51
57
|
when String
|
52
58
|
wres.body << response.body
|
53
59
|
when Enumerable
|
54
|
-
wres.chunked =
|
60
|
+
wres.chunked = response.headers['Transfer-Encoding'] == 'chunked'
|
55
61
|
response.body.each {|part| wres.body << part }
|
56
62
|
else
|
57
63
|
if response.body.respond_to?(:call)
|
data/lib/webmachine/cookie.rb
CHANGED
@@ -40,7 +40,13 @@ module Webmachine
|
|
40
40
|
# @param [Response] response the response object
|
41
41
|
def dispatch(request, response)
|
42
42
|
if resource = find_resource(request, response)
|
43
|
-
Webmachine::
|
43
|
+
Webmachine::Events.instrument('wm.dispatch') do |payload|
|
44
|
+
Webmachine::Decision::FSM.new(resource, request, response).run
|
45
|
+
|
46
|
+
payload[:resource] = resource.class.name
|
47
|
+
payload[:request] = request.dup
|
48
|
+
payload[:code] = response.code
|
49
|
+
end
|
44
50
|
else
|
45
51
|
Webmachine.render_error(404, request, response)
|
46
52
|
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'securerandom' # For AS::Notifications
|
2
|
+
require 'as/notifications'
|
3
|
+
require 'webmachine/events/instrumented_event'
|
4
|
+
|
5
|
+
module Webmachine
|
6
|
+
# {Webmachine::Events} implements the
|
7
|
+
# [ActiveSupport::Notifications](http://rubydoc.info/gems/activesupport/ActiveSupport/Notifications)
|
8
|
+
# instrumentation API. It delegates to the configured backend.
|
9
|
+
# The default backend is
|
10
|
+
# [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications).
|
11
|
+
#
|
12
|
+
# # Published events
|
13
|
+
#
|
14
|
+
# Webmachine publishes some internal events by default. All of them use
|
15
|
+
# the `wm.` prefix.
|
16
|
+
#
|
17
|
+
# ## `wm.dispatch` ({.instrument})
|
18
|
+
#
|
19
|
+
# The payload hash includes the following keys.
|
20
|
+
#
|
21
|
+
# * `:resource` - The resource class name
|
22
|
+
# * `:request` - A copy of the request object
|
23
|
+
# * `:code` - The response code
|
24
|
+
#
|
25
|
+
module Events
|
26
|
+
class << self
|
27
|
+
# The class that {Webmachine::Events} delegates all messages to.
|
28
|
+
# (default [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications))
|
29
|
+
#
|
30
|
+
# It can be changed to an
|
31
|
+
# [ActiveSupport::Notifications](http://rubydoc.info/gems/activesupport/ActiveSupport/Notifications)
|
32
|
+
# compatible backend early in the application startup process.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# require 'webmachine'
|
36
|
+
# require 'active_support/notifications'
|
37
|
+
#
|
38
|
+
# Webmachine::Events.backend = ActiveSupport::Notifications
|
39
|
+
#
|
40
|
+
# Webmachine::Application.new {|app|
|
41
|
+
# # setup application
|
42
|
+
# }.run
|
43
|
+
attr_accessor :backend
|
44
|
+
|
45
|
+
# Publishes the given arguments to all listeners subscribed to the given
|
46
|
+
# event name.
|
47
|
+
# @param name [String] the event name
|
48
|
+
# @example
|
49
|
+
# Webmachine::Events.publish('wm.foo', :hello => 'world')
|
50
|
+
def publish(name, *args)
|
51
|
+
backend.publish(name, *args)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Instrument the given block by measuring the time taken to execute it
|
55
|
+
# and publish it. Notice that events get sent even if an error occurs
|
56
|
+
# in the passed-in block.
|
57
|
+
#
|
58
|
+
# If an exception happens during an instrumentation the payload will
|
59
|
+
# have a key `:exception` with an array of two elements as value:
|
60
|
+
# a string with the name of the exception class, and the exception
|
61
|
+
# message. (when using the default
|
62
|
+
# [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications)
|
63
|
+
# backend)
|
64
|
+
#
|
65
|
+
# @param name [String] the event name
|
66
|
+
# @param payload [Hash] the initial payload
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# Webmachine::Events.instrument('wm.dispatch') do |payload|
|
70
|
+
# execute_some_method
|
71
|
+
#
|
72
|
+
# payload[:custom_payload_value] = 'important'
|
73
|
+
# end
|
74
|
+
def instrument(name, payload = {}, &block)
|
75
|
+
backend.instrument(name, payload, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Subscribes to the given event name.
|
79
|
+
#
|
80
|
+
# @note The documentation of this method describes its behaviour with the
|
81
|
+
# default backed [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications).
|
82
|
+
# It can change if a different backend is used.
|
83
|
+
#
|
84
|
+
# @overload subscribe(name)
|
85
|
+
# Subscribing to an {.instrument} event. The block arguments can be
|
86
|
+
# passed to {Webmachine::Events::InstrumentedEvent}.
|
87
|
+
#
|
88
|
+
# @param name [String, Regexp] the event name to subscribe to
|
89
|
+
# @yieldparam name [String] the event name
|
90
|
+
# @yieldparam start [Time] the event start time
|
91
|
+
# @yieldparam end [Time] the event end time
|
92
|
+
# @yieldparam event_id [String] the event id
|
93
|
+
# @yieldparam payload [Hash] the event payload
|
94
|
+
# @return [Object] the subscriber object (type depends on the backend implementation)
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# # Subscribe to all 'wm.dispatch' events
|
98
|
+
# Webmachine::Events.subscribe('wm.dispatch') {|*args|
|
99
|
+
# event = Webmachine::Events::InstrumentedEvent.new(*args)
|
100
|
+
# }
|
101
|
+
#
|
102
|
+
# # Subscribe to all events that start with 'wm.'
|
103
|
+
# Webmachine::Events.subscribe(/wm\.*/) {|*args| }
|
104
|
+
#
|
105
|
+
# @overload subscribe(name)
|
106
|
+
# Subscribing to a {.publish} event.
|
107
|
+
#
|
108
|
+
# @param name [String, Regexp] the event name to subscribe to
|
109
|
+
# @yieldparam name [String] the event name
|
110
|
+
# @yieldparam *args [Array] the published arguments
|
111
|
+
# @return [Object] the subscriber object (type depends on the backend implementation)
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# Webmachine::Events.subscribe('custom.event') {|name, *args|
|
115
|
+
# args #=> [obj1, obj2, {:num => 1}]
|
116
|
+
# }
|
117
|
+
#
|
118
|
+
# Webmachine::Events.publish('custom.event', obj1, obj2, {:num => 1})
|
119
|
+
#
|
120
|
+
# @overload subscribe(name, listener)
|
121
|
+
# Subscribing with a listener object instead of a block. The listener
|
122
|
+
# object must respond to `#call`.
|
123
|
+
#
|
124
|
+
# @param name [String, Regexp] the event name to subscribe to
|
125
|
+
# @param listener [#call] a listener object
|
126
|
+
# @return [Object] the subscriber object (type depends on the backend implementation)
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# class CustomListener
|
130
|
+
# def call(name, *args)
|
131
|
+
# # ...
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# Webmachine::Events.subscribe('wm.dispatch', CustomListener.new)
|
136
|
+
#
|
137
|
+
def subscribe(name, *args, &block)
|
138
|
+
backend.subscribe(name, *args, &block)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Subscribe to an event temporarily while the block runs.
|
142
|
+
#
|
143
|
+
# @note The documentation of this method describes its behaviour with the
|
144
|
+
# default backed [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications).
|
145
|
+
# It can change if a different backend is used.
|
146
|
+
#
|
147
|
+
# The callback in the following example will be called for all
|
148
|
+
# "sql.active_record" events instrumented during the execution of the
|
149
|
+
# block. The callback is unsubscribed automatically after that.
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# callback = lambda {|name, *args| handle_event(name, *args) }
|
153
|
+
#
|
154
|
+
# Webmachine::Events.subscribed(callback, 'sql.active_record') do
|
155
|
+
# call_active_record
|
156
|
+
# end
|
157
|
+
def subscribed(callback, name, &block)
|
158
|
+
backend.subscribed(callback, name, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Unsubscribes the given subscriber.
|
162
|
+
#
|
163
|
+
# @note The documentation of this method describes its behaviour with the
|
164
|
+
# default backed [AS::Notifications](http://rubydoc.info/gems/as-notifications/AS/Notifications).
|
165
|
+
# It can change if a different backend is used.
|
166
|
+
#
|
167
|
+
# @param subscriber [Object] the subscriber object (type depends on the backend implementation)
|
168
|
+
# @example
|
169
|
+
# subscriber = Webmachine::Events.subscribe('wm.dispatch') {|*args| }
|
170
|
+
#
|
171
|
+
# Webmachine::Events.unsubscribe(subscriber)
|
172
|
+
def unsubscribe(subscriber)
|
173
|
+
backend.unsubscribe(subscriber)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
self.backend = AS::Notifications
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'as/notifications/instrumenter'
|
3
|
+
|
4
|
+
module Webmachine
|
5
|
+
module Events
|
6
|
+
# {Webmachine::Events::InstrumentedEvent} delegates to
|
7
|
+
# [AS::Notifications::Event](http://rubydoc.info/gems/as-notifications/AS/Notifications/Event).
|
8
|
+
#
|
9
|
+
# The class
|
10
|
+
# [AS::Notifications::Event](http://rubydoc.info/gems/as-notifications/AS/Notifications/Event)
|
11
|
+
# is able to take the arguments of an {Webmachine::Events.instrument} event
|
12
|
+
# and provide an object-oriented interface to that data.
|
13
|
+
class InstrumentedEvent < SimpleDelegator
|
14
|
+
def initialize(*args)
|
15
|
+
super(AS::Notifications::Event.new(*args))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/webmachine/response.rb
CHANGED
@@ -45,7 +45,7 @@ module Webmachine
|
|
45
45
|
# @param [String] value the value of the cookie
|
46
46
|
# @param [Hash] attributes for the cookie. See RFC2109.
|
47
47
|
def set_cookie(name, value, attributes = {})
|
48
|
-
cookie = Webmachine::Cookie.new(name, value).to_s
|
48
|
+
cookie = Webmachine::Cookie.new(name, value, attributes).to_s
|
49
49
|
case headers['Set-Cookie']
|
50
50
|
when nil
|
51
51
|
headers['Set-Cookie'] = cookie
|
@@ -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) and chunk != ""
|
17
17
|
yield resource.send(encoder, resource.send(charsetter, chunk))
|
18
18
|
end
|
19
19
|
end
|
@@ -50,6 +50,10 @@ module Webmachine
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def empty?
|
54
|
+
size == 0
|
55
|
+
end
|
56
|
+
|
53
57
|
alias bytesize size
|
54
58
|
|
55
59
|
private
|
data/lib/webmachine/trace.rb
CHANGED
@@ -2,6 +2,7 @@ require 'webmachine/trace/resource_proxy'
|
|
2
2
|
require 'webmachine/trace/fsm'
|
3
3
|
require 'webmachine/trace/pstore_trace_store'
|
4
4
|
require 'webmachine/trace/trace_resource'
|
5
|
+
require 'webmachine/trace/listener'
|
5
6
|
|
6
7
|
module Webmachine
|
7
8
|
# Contains means to enable the Webmachine visual debugger.
|
@@ -13,6 +14,11 @@ module Webmachine
|
|
13
14
|
:pstore => PStoreTraceStore
|
14
15
|
}
|
15
16
|
|
17
|
+
DEFAULT_TRACE_SUBSCRIBER = Webmachine::Events.subscribe(
|
18
|
+
/wm\.trace\..+/,
|
19
|
+
Webmachine::Trace::Listener.new
|
20
|
+
)
|
21
|
+
|
16
22
|
# Determines whether this resource instance should be traced.
|
17
23
|
# @param [Webmachine::Resource] resource a resource instance
|
18
24
|
# @return [true, false] whether to trace the resource
|
@@ -70,5 +76,17 @@ module Webmachine
|
|
70
76
|
end
|
71
77
|
end
|
72
78
|
private :trace_store
|
79
|
+
|
80
|
+
# Sets the trace listener objects.
|
81
|
+
# Defaults to Webmachine::Trace::Listener.new.
|
82
|
+
# @param [Array<Object>] listeners a list of event listeners
|
83
|
+
# @return [Array<Object>] a list of event subscribers
|
84
|
+
def trace_listener=(listeners)
|
85
|
+
Webmachine::Events.unsubscribe(DEFAULT_TRACE_SUBSCRIBER)
|
86
|
+
|
87
|
+
Array(listeners).map do |listener|
|
88
|
+
Webmachine::Events.subscribe(/wm\.trace\..+/, listener)
|
89
|
+
end
|
90
|
+
end
|
73
91
|
end
|
74
92
|
end
|
data/lib/webmachine/trace/fsm.rb
CHANGED
@@ -27,7 +27,10 @@ module Webmachine
|
|
27
27
|
:body => trace_response_body(response.body)
|
28
28
|
}
|
29
29
|
ensure
|
30
|
-
|
30
|
+
Webmachine::Events.publish('wm.trace.record', {
|
31
|
+
:trace_id => resource.object_id.to_s,
|
32
|
+
:trace => response.trace
|
33
|
+
})
|
31
34
|
end
|
32
35
|
|
33
36
|
# Adds a decision to the trace.
|
data/lib/webmachine/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -4,17 +4,28 @@ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'webmachine'
|
6
6
|
require 'rspec'
|
7
|
+
require 'logger'
|
7
8
|
|
8
9
|
RSpec.configure do |config|
|
9
10
|
config.mock_with :rspec
|
10
11
|
config.filter_run :focus => true
|
11
12
|
config.run_all_when_everything_filtered = true
|
12
13
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.formatter = :documentation if ENV['CI']
|
13
15
|
if defined?(::Java)
|
14
16
|
config.seed = Time.now.utc
|
15
17
|
else
|
16
18
|
config.order = :random
|
17
19
|
end
|
20
|
+
|
21
|
+
config.before(:suite) do
|
22
|
+
options = {
|
23
|
+
:Logger => Logger.new("/dev/null"),
|
24
|
+
:AccessLog => []
|
25
|
+
}
|
26
|
+
Webmachine::Adapters::WEBrick::DEFAULT_OPTIONS.merge! options
|
27
|
+
Webmachine::Adapters::Rack::DEFAULT_OPTIONS.merge! options
|
28
|
+
end
|
18
29
|
end
|
19
30
|
|
20
31
|
# For use in specs that need a fully initialized resource
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "support/test_resource"
|
2
|
+
require "net/http"
|
3
|
+
|
4
|
+
shared_examples_for :adapter_lint do
|
5
|
+
attr_accessor :client
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
configuration = Webmachine::Configuration.default
|
9
|
+
dispatcher = Webmachine::Dispatcher.new
|
10
|
+
dispatcher.add_route ["test"], Test::Resource
|
11
|
+
|
12
|
+
@adapter = described_class.new(configuration, dispatcher)
|
13
|
+
@client = Net::HTTP.new(configuration.ip, configuration.port)
|
14
|
+
|
15
|
+
Thread.abort_on_exception = true
|
16
|
+
@server_thread = Thread.new { @adapter.run }
|
17
|
+
|
18
|
+
# Wait until the server is responsive
|
19
|
+
timeout(5) do
|
20
|
+
begin
|
21
|
+
client.start
|
22
|
+
rescue Errno::ECONNREFUSED
|
23
|
+
sleep(0.1)
|
24
|
+
retry
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
after(:all) do
|
30
|
+
@adapter.shutdown
|
31
|
+
@client.finish
|
32
|
+
@server_thread.join
|
33
|
+
end
|
34
|
+
|
35
|
+
it "provides a string-like request body" do
|
36
|
+
request = Net::HTTP::Put.new("/test")
|
37
|
+
request.body = "Hello, World!"
|
38
|
+
request["Content-Type"] = "test/request.stringbody"
|
39
|
+
response = client.request(request)
|
40
|
+
response["Content-Length"].should eq("21")
|
41
|
+
response.body.should eq("String: Hello, World!")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "provides an enumerable request body" do
|
45
|
+
request = Net::HTTP::Put.new("/test")
|
46
|
+
request.body = "Hello, World!"
|
47
|
+
request["Content-Type"] = "test/request.enumbody"
|
48
|
+
response = client.request(request)
|
49
|
+
response["Content-Length"].should eq("19")
|
50
|
+
response.body.should eq("Enum: Hello, World!")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "handles missing pages" do
|
54
|
+
request = Net::HTTP::Get.new("/missing")
|
55
|
+
response = client.request(request)
|
56
|
+
response.code.should eq("404")
|
57
|
+
response["Content-Type"].should eq("text/html")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "handles empty response bodies" do
|
61
|
+
request = Net::HTTP::Post.new("/test")
|
62
|
+
request.body = ""
|
63
|
+
response = client.request(request)
|
64
|
+
response.code.should eq("204")
|
65
|
+
# FIXME: Mongrel/WEBrick fail this test. Is there a bug?
|
66
|
+
#response["Content-Type"].should be_nil
|
67
|
+
response["Content-Length"].should be_nil
|
68
|
+
response.body.should be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it "handles string response bodies" do
|
72
|
+
request = Net::HTTP::Get.new("/test")
|
73
|
+
request["Accept"] = "test/response.stringbody"
|
74
|
+
response = client.request(request)
|
75
|
+
response["Content-Length"].should eq("20")
|
76
|
+
response.body.should eq("String response body")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "handles enumerable response bodies" do
|
80
|
+
request = Net::HTTP::Get.new("/test")
|
81
|
+
request["Accept"] = "test/response.enumbody"
|
82
|
+
response = client.request(request)
|
83
|
+
response["Transfer-Encoding"].should eq("chunked")
|
84
|
+
response.body.should eq("Enumerable response body")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "handles proc response bodies" do
|
88
|
+
request = Net::HTTP::Get.new("/test")
|
89
|
+
request["Accept"] = "test/response.procbody"
|
90
|
+
response = client.request(request)
|
91
|
+
response["Transfer-Encoding"].should eq("chunked")
|
92
|
+
response.body.should eq("Proc response body")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "handles fiber response bodies" do
|
96
|
+
request = Net::HTTP::Get.new("/test")
|
97
|
+
request["Accept"] = "test/response.fiberbody"
|
98
|
+
response = client.request(request)
|
99
|
+
response["Transfer-Encoding"].should eq("chunked")
|
100
|
+
response.body.should eq("Fiber response body")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "handles io response bodies" do
|
104
|
+
request = Net::HTTP::Get.new("/test")
|
105
|
+
request["Accept"] = "test/response.iobody"
|
106
|
+
response = client.request(request)
|
107
|
+
response["Content-Length"].should eq("16")
|
108
|
+
response.body.should eq("IO response body")
|
109
|
+
end
|
110
|
+
|
111
|
+
it "handles request cookies" do
|
112
|
+
request = Net::HTTP::Get.new("/test")
|
113
|
+
request["Accept"] = "test/response.cookies"
|
114
|
+
request["Cookie"] = "echo=echocookie"
|
115
|
+
response = client.request(request)
|
116
|
+
response.body.should eq("echocookie")
|
117
|
+
end
|
118
|
+
|
119
|
+
it "handles response cookies" do
|
120
|
+
request = Net::HTTP::Get.new("/test")
|
121
|
+
request["Accept"] = "test/response.cookies"
|
122
|
+
response = client.request(request)
|
123
|
+
response["Set-Cookie"].should eq("cookie=monster, rodeo=clown")
|
124
|
+
end
|
125
|
+
end
|