tap-http 0.2.1 → 0.3.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/History +12 -0
- data/README +9 -70
- data/controllers/capture_controller.rb +55 -0
- data/lib/tap/http/get.rb +19 -0
- data/lib/tap/http/request.rb +263 -0
- data/lib/tap/http/submit.rb +31 -0
- data/lib/tap/test/http_test.rb +36 -121
- data/views/capture_controller/index.erb +30 -0
- metadata +17 -7
- data/cgi/echo.rb +0 -24
- data/cgi/http_to_yaml.rb +0 -108
- data/cgi/parse_http.rb +0 -129
- data/lib/tap/http/dispatch.rb +0 -411
- data/lib/tap/test/http_test/requests.rb +0 -194
data/lib/tap/http/dispatch.rb
DELETED
@@ -1,411 +0,0 @@
|
|
1
|
-
require 'tap/http/utils'
|
2
|
-
require 'net/http'
|
3
|
-
require 'thread'
|
4
|
-
|
5
|
-
module Tap
|
6
|
-
module Http
|
7
|
-
|
8
|
-
# :startdoc::manifest submits an http request
|
9
|
-
#
|
10
|
-
# Dispatch is a base class for submitting HTTP requests from a request
|
11
|
-
# hash. Multiple requests may be submitted on individual threads, up
|
12
|
-
# to a configurable limit.
|
13
|
-
#
|
14
|
-
# Request hashes are like the following:
|
15
|
-
#
|
16
|
-
# request_method: GET
|
17
|
-
# url: http://tap.rubyforge.org/
|
18
|
-
# headers: {}
|
19
|
-
# params: {}
|
20
|
-
# version: 1.1
|
21
|
-
#
|
22
|
-
# Missing fields are added from the task configuration. Note that since
|
23
|
-
# Dispatch takes hash inputs, it is often convenient to save requests in
|
24
|
-
# a .yml file and sequence dispatch with load:
|
25
|
-
#
|
26
|
-
# [requests.yml]
|
27
|
-
# - url: http://tap.rubyforge.org/
|
28
|
-
# - url: http://tap.rubyforge.org/about.html
|
29
|
-
#
|
30
|
-
# % rap load requests.yml --:i dispatch --+ dump
|
31
|
-
#
|
32
|
-
# :startdoc::manifest-end
|
33
|
-
# === Dispatch Methods
|
34
|
-
#
|
35
|
-
# Dispatch itself provides methods for constructing and submitting get and
|
36
|
-
# post HTTP requests from a request hash.
|
37
|
-
#
|
38
|
-
# res = Tap::Http::Dispatch.submit(
|
39
|
-
# :url => "http://tap.rubyforge.org",
|
40
|
-
# :version => '1.1',
|
41
|
-
# :request_method => 'GET',
|
42
|
-
# :headers => {},
|
43
|
-
# :params => {}
|
44
|
-
# )
|
45
|
-
# res.inspect # => "#<Net::HTTPOK 200 OK readbody=true>"
|
46
|
-
# res.body =~ /Tap/ # => true
|
47
|
-
#
|
48
|
-
# Headers and parameters take the form:
|
49
|
-
#
|
50
|
-
# {
|
51
|
-
# 'single' => 'value',
|
52
|
-
# 'multiple' => ['value one', 'value two']
|
53
|
-
# }
|
54
|
-
#
|
55
|
-
# To capture request hashes from web forms using Firefox, see the README.
|
56
|
-
class Dispatch < Tap::Task
|
57
|
-
class << self
|
58
|
-
def intern(*args, &block)
|
59
|
-
instance = new(*args)
|
60
|
-
instance.extend Support::Intern(:process_response)
|
61
|
-
instance.process_response_block = block
|
62
|
-
instance
|
63
|
-
end
|
64
|
-
|
65
|
-
# Constructs and submits an http request to the url using the request hash.
|
66
|
-
# Request hashes are like this:
|
67
|
-
#
|
68
|
-
# {
|
69
|
-
# :url => "http://tap.rubyforge.org",
|
70
|
-
# :version => '1.1',
|
71
|
-
# :request_method => 'GET',
|
72
|
-
# :headers => {},
|
73
|
-
# :params => {}
|
74
|
-
# }
|
75
|
-
#
|
76
|
-
# If left unspecified, the default configuration values will be used (but
|
77
|
-
# note that since the default url is nil, a url MUST be specified).
|
78
|
-
# Headers and parameters can use array values to specifiy multiple values
|
79
|
-
# for the same key.
|
80
|
-
#
|
81
|
-
# Submit only support get and post request methods; see construct_get and
|
82
|
-
# construct_post for more details. A block may be given to receive the
|
83
|
-
# Net::HTTP and request just prior to submission.
|
84
|
-
#
|
85
|
-
# Returns the Net::HTTP response.
|
86
|
-
#
|
87
|
-
def submit(request_hash)
|
88
|
-
url_or_uri = request_hash[:url] || configurations[:url].default
|
89
|
-
headers = request_hash[:headers] || configurations[:headers].default
|
90
|
-
params = request_hash[:params] || configurations[:params].default
|
91
|
-
request_method = request_hash[:request_method] || configurations[:request_method].default
|
92
|
-
version = request_hash[:version] || configurations[:version].default
|
93
|
-
|
94
|
-
raise ArgumentError, "no url specified" unless url_or_uri
|
95
|
-
uri = url_or_uri.kind_of?(URI) ? url_or_uri : URI.parse(url_or_uri)
|
96
|
-
uri.path = "/" if uri.path.empty?
|
97
|
-
|
98
|
-
# construct the request based on the method
|
99
|
-
request = case request_method.to_s
|
100
|
-
when /^get$/i then construct_get(uri, headers, params)
|
101
|
-
when /^post$/i then construct_post(uri, headers, params)
|
102
|
-
else
|
103
|
-
raise ArgumentError, "unsupported request method: #{request_method}"
|
104
|
-
end
|
105
|
-
|
106
|
-
# set the http version
|
107
|
-
version_method = "version_#{version.to_s.gsub(".", "_")}".to_sym
|
108
|
-
if ::Net::HTTP.respond_to?(version_method)
|
109
|
-
::Net::HTTP.send(version_method)
|
110
|
-
else
|
111
|
-
raise ArgumentError, "unsupported HTTP version: #{version}"
|
112
|
-
end
|
113
|
-
|
114
|
-
# submit the request
|
115
|
-
res = ::Net::HTTP.new(uri.host, uri.port).start do |http|
|
116
|
-
yield(http, request) if block_given?
|
117
|
-
http.request(request)
|
118
|
-
end
|
119
|
-
|
120
|
-
# fetch redirections
|
121
|
-
redirection_limit = request_hash[:redirection_limit]
|
122
|
-
redirection_limit ? fetch_redirection(res, redirection_limit) : res
|
123
|
-
end
|
124
|
-
|
125
|
-
# Constructs a Net::HTTP::Post query, setting headers and parameters.
|
126
|
-
#
|
127
|
-
# ==== Supported Content Types:
|
128
|
-
#
|
129
|
-
# - application/x-www-form-urlencoded (the default)
|
130
|
-
# - multipart/form-data
|
131
|
-
#
|
132
|
-
# The multipart/form-data content type may specify a boundary. If no
|
133
|
-
# boundary is specified, a randomly generated boundary will be used
|
134
|
-
# to delimit the parameters.
|
135
|
-
#
|
136
|
-
# post = construct_post(
|
137
|
-
# URI.parse('http://some.url/'),
|
138
|
-
# {:content_type => 'multipart/form-data; boundary=1234'},
|
139
|
-
# {:key => 'value'})
|
140
|
-
#
|
141
|
-
# post.body
|
142
|
-
# # => %Q{--1234\r
|
143
|
-
# # Content-Disposition: form-data; name="key"\r
|
144
|
-
# # \r
|
145
|
-
# # value\r
|
146
|
-
# # --1234--\r
|
147
|
-
# # }
|
148
|
-
#
|
149
|
-
# (Note the carriage returns are required in multipart content)
|
150
|
-
#
|
151
|
-
# The content-length header is determined automatically from the
|
152
|
-
# formatted request body; manually specified content-length headers
|
153
|
-
# will be overridden.
|
154
|
-
#
|
155
|
-
def construct_post(uri, headers, params)
|
156
|
-
req = ::Net::HTTP::Post.new( URI.encode("#{uri.path}#{format_query(uri)}") )
|
157
|
-
headers = headerize_keys(headers)
|
158
|
-
content_type = headers['Content-Type']
|
159
|
-
|
160
|
-
case content_type
|
161
|
-
when nil, /^application\/x-www-form-urlencoded$/i
|
162
|
-
req.body = format_www_form_urlencoded(params)
|
163
|
-
headers['Content-Type'] ||= "application/x-www-form-urlencoded"
|
164
|
-
headers['Content-Length'] = req.body.length
|
165
|
-
|
166
|
-
when /^multipart\/form-data(;\s*boundary=(.*))?$/i
|
167
|
-
# extract the boundary if it exists
|
168
|
-
boundary = $2 || rand.to_s[2..20]
|
169
|
-
|
170
|
-
req.body = format_multipart_form_data(params, boundary)
|
171
|
-
headers['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
|
172
|
-
headers['Content-Length'] = req.body.length
|
173
|
-
|
174
|
-
else
|
175
|
-
raise ArgumentError, "unsupported Content-Type for POST: #{content_type}"
|
176
|
-
end
|
177
|
-
|
178
|
-
headers.each_pair { |key, value| req[key] = value }
|
179
|
-
req
|
180
|
-
end
|
181
|
-
|
182
|
-
# Constructs a Net::HTTP::Get query. All parameters in uri and params are
|
183
|
-
# encoded and added to the request URI.
|
184
|
-
#
|
185
|
-
# get = construct_get(URI.parse('http://some.url/path'), {}, {:key => 'value'})
|
186
|
-
# get.path # => "/path?key=value"
|
187
|
-
#
|
188
|
-
def construct_get(uri, headers, params)
|
189
|
-
req = ::Net::HTTP::Get.new( URI.encode("#{uri.path}#{format_query(uri, params)}") )
|
190
|
-
headerize_keys(headers).each_pair { |key, value| req[key] = value }
|
191
|
-
req
|
192
|
-
end
|
193
|
-
|
194
|
-
# Checks the type of the response; if it is a redirection, fetches the
|
195
|
-
# redirection. Otherwise return the response.
|
196
|
-
#
|
197
|
-
# Notes:
|
198
|
-
# - Fetch will recurse up to the input redirection limit (default 10)
|
199
|
-
# - Responses that are not Net::HTTPRedirection or Net::HTTPSuccess
|
200
|
-
# raise an error.
|
201
|
-
def fetch_redirection(res, limit=10)
|
202
|
-
raise 'exceeded the redirection limit' if limit < 1
|
203
|
-
|
204
|
-
case res
|
205
|
-
when ::Net::HTTPRedirection
|
206
|
-
redirect = ::Net::HTTP.get_response( URI.parse(res['location']) )
|
207
|
-
fetch_redirection(redirect, limit - 1)
|
208
|
-
when ::Net::HTTPSuccess
|
209
|
-
res
|
210
|
-
else
|
211
|
-
raise StandardError, res.error!
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
# Constructs a URI query string from the uri and the input parameters.
|
216
|
-
# Multiple values for a parameter may be specified using an array.
|
217
|
-
# The query is not encoded, so you may need to URI.encode it later.
|
218
|
-
#
|
219
|
-
# format_query(URI.parse('http://some.url/path'), {:key => 'value'})
|
220
|
-
# # => "?key=value"
|
221
|
-
#
|
222
|
-
# format_query(URI.parse('http://some.url/path?one=1'), {:two => '2'})
|
223
|
-
# # => "?one=1&two=2"
|
224
|
-
#
|
225
|
-
def format_query(uri, params={})
|
226
|
-
query = []
|
227
|
-
query << uri.query if uri.query
|
228
|
-
params.each_pair do |key, values|
|
229
|
-
values = [values] unless values.kind_of?(Array)
|
230
|
-
values.each { |value| query << "#{key}=#{value}" }
|
231
|
-
end
|
232
|
-
"#{query.empty? ? '' : '?'}#{query.join('&')}"
|
233
|
-
end
|
234
|
-
|
235
|
-
# Formats params as 'application/x-www-form-urlencoded' for use as the
|
236
|
-
# body of a post request. Multiple values for a parameter may be
|
237
|
-
# specified using an array. The result is obviously URI encoded.
|
238
|
-
#
|
239
|
-
# format_www_form_urlencoded(:key => 'value with spaces')
|
240
|
-
# # => "key=value%20with%20spaces"
|
241
|
-
#
|
242
|
-
def format_www_form_urlencoded(params={})
|
243
|
-
query = []
|
244
|
-
params.each_pair do |key, values|
|
245
|
-
values = [values] unless values.kind_of?(Array)
|
246
|
-
values.each { |value| query << "#{key}=#{value}" }
|
247
|
-
end
|
248
|
-
URI.encode( query.join('&') )
|
249
|
-
end
|
250
|
-
|
251
|
-
# Formats params as 'multipart/form-data' using the specified boundary,
|
252
|
-
# for use as the body of a post request. Multiple values for a parameter
|
253
|
-
# may be specified using an array. All newlines include a carriage
|
254
|
-
# return for proper formatting.
|
255
|
-
#
|
256
|
-
# format_multipart_form_data(:key => 'value')
|
257
|
-
# # => %Q{--1234567890\r
|
258
|
-
# # Content-Disposition: form-data; name="key"\r
|
259
|
-
# # \r
|
260
|
-
# # value\r
|
261
|
-
# # --1234567890--\r
|
262
|
-
# # }
|
263
|
-
#
|
264
|
-
# To specify a file, use a hash of file-related headers.
|
265
|
-
#
|
266
|
-
# format_multipart_form_data(:key => {
|
267
|
-
# 'Content-Type' => 'text/plain',
|
268
|
-
# 'Filename' => "path/to/file.txt"}
|
269
|
-
# )
|
270
|
-
# # => %Q{--1234567890\r
|
271
|
-
# # Content-Disposition: form-data; name="key"; filename="path/to/file.txt"\r
|
272
|
-
# # Content-Type: text/plain\r
|
273
|
-
# # \r
|
274
|
-
# # \r
|
275
|
-
# # --1234567890--\r
|
276
|
-
# # }
|
277
|
-
#
|
278
|
-
def format_multipart_form_data(params, boundary="1234567890")
|
279
|
-
body = []
|
280
|
-
params.each_pair do |key, values|
|
281
|
-
values = [values] unless values.kind_of?(Array)
|
282
|
-
|
283
|
-
values.each do |value|
|
284
|
-
body << case value
|
285
|
-
when Hash
|
286
|
-
hash = headerize_keys(value)
|
287
|
-
filename = hash.delete('Filename') || ""
|
288
|
-
content = File.exists?(filename) ? File.read(filename) : ""
|
289
|
-
|
290
|
-
header = "Content-Disposition: form-data; name=\"#{key.to_s}\"; filename=\"#{filename}\"\r\n"
|
291
|
-
hash.each_pair { |key, value| header << "#{key}: #{value}\r\n" }
|
292
|
-
"#{header}\r\n#{content}\r\n"
|
293
|
-
else
|
294
|
-
%Q{Content-Disposition: form-data; name="#{key.to_s}"\r\n\r\n#{value.to_s}\r\n}
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
body.collect {|p| "--#{boundary}\r\n#{p}" }.join('') + "--#{boundary}--\r\n"
|
300
|
-
end
|
301
|
-
|
302
|
-
protected
|
303
|
-
|
304
|
-
# Helper to headerize the keys of a hash to headers.
|
305
|
-
# See Utils#headerize.
|
306
|
-
def headerize_keys(hash) # :nodoc:
|
307
|
-
result = {}
|
308
|
-
hash.each_pair do |key, value|
|
309
|
-
result[Utils.headerize(key)] = value
|
310
|
-
end
|
311
|
-
result
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
config :url, nil # the target url
|
316
|
-
config :headers, {}, &c.hash # a hash of request headers
|
317
|
-
config :params, {}, &c.hash # a hash of query parameters
|
318
|
-
config :request_method, 'GET' # the request method (get or post)
|
319
|
-
config :version, 1.1 # the HTTP version
|
320
|
-
config :redirection_limit, nil, &c.integer_or_nil # the redirection limit for the request
|
321
|
-
|
322
|
-
config :max_threads, 10, &c.integer # the maximum number of request threads
|
323
|
-
|
324
|
-
# Prepares the request_hash by symbolizing keys and adding missing
|
325
|
-
# parameters using the current configuration values.
|
326
|
-
def prepare(request_hash)
|
327
|
-
request_hash.inject(
|
328
|
-
:url => url,
|
329
|
-
:headers => headers,
|
330
|
-
:params => params,
|
331
|
-
:request_method => request_method,
|
332
|
-
:version => version,
|
333
|
-
:redirection_limit => redirection_limit
|
334
|
-
) do |options, (key, value)|
|
335
|
-
options[(key.to_sym rescue key) || key] = value
|
336
|
-
options
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def process(*requests)
|
341
|
-
# build a queue of all the requests to be handled
|
342
|
-
queue = Queue.new
|
343
|
-
requests.each_with_index do |request, index|
|
344
|
-
queue.enq [prepare(request), index]
|
345
|
-
index += 1
|
346
|
-
end
|
347
|
-
|
348
|
-
# submit and retrieve all requests before processing
|
349
|
-
# responses. this assures responses are processed
|
350
|
-
# in order, in case it matters.
|
351
|
-
lock = Mutex.new
|
352
|
-
responses = []
|
353
|
-
request_threads = Array.new(max_threads) do
|
354
|
-
Thread.new do
|
355
|
-
begin
|
356
|
-
while !queue.empty?
|
357
|
-
request, index = queue.deq(true)
|
358
|
-
log(request[:request_method], request[:url])
|
359
|
-
|
360
|
-
res = Dispatch.submit(request)
|
361
|
-
lock.synchronize { responses[index] = res }
|
362
|
-
end
|
363
|
-
rescue(ThreadError)
|
364
|
-
# Catch errors due to the queue being empty.
|
365
|
-
# (this should not occur as the queue is checked)
|
366
|
-
raise $! unless $!.message == 'queue empty'
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
request_threads.each {|thread| thread.join }
|
371
|
-
|
372
|
-
# process responses and collect results
|
373
|
-
errors = []
|
374
|
-
responses = responses.collect do |res|
|
375
|
-
begin
|
376
|
-
process_response(res)
|
377
|
-
rescue(ResponseError)
|
378
|
-
errors << [$!, responses.index(res)]
|
379
|
-
nil
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
unless errors.empty?
|
384
|
-
handle_response_errors(responses, errors)
|
385
|
-
end
|
386
|
-
|
387
|
-
responses
|
388
|
-
end
|
389
|
-
|
390
|
-
# Hook for processing a response. By default process_response
|
391
|
-
# simply logs the response message and returns the response.
|
392
|
-
def process_response(res)
|
393
|
-
log(nil, res.message)
|
394
|
-
res
|
395
|
-
end
|
396
|
-
|
397
|
-
# A hook for handling a batch of response errors, perhaps
|
398
|
-
# doing something meaningful with the successful responses.
|
399
|
-
# By default, concatenates the error messages and raises
|
400
|
-
# a new ResponseError.
|
401
|
-
def handle_response_errors(responses, errors)
|
402
|
-
errors.collect! {|error, n| "request #{n}: #{error.message}"}
|
403
|
-
errors.unshift("Error processing responses:")
|
404
|
-
raise ResponseError, errors.join("\n")
|
405
|
-
end
|
406
|
-
|
407
|
-
class ResponseError < StandardError
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
@@ -1,194 +0,0 @@
|
|
1
|
-
module Tap
|
2
|
-
module Test
|
3
|
-
module HttpTest
|
4
|
-
|
5
|
-
# A collection of sample requests used in testing.
|
6
|
-
module Requests
|
7
|
-
GET_REQUESTS = {}
|
8
|
-
POST_REQUESTS = {}
|
9
|
-
|
10
|
-
def self.add(type, name, request, expected)
|
11
|
-
collection = case type
|
12
|
-
when :get then GET_REQUESTS
|
13
|
-
when :post then POST_REQUESTS
|
14
|
-
end
|
15
|
-
|
16
|
-
collection["#{type}_#{name}"] = [request.lstrip.gsub(/\n/, "\r\n"), expected]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
#
|
24
|
-
# get requests
|
25
|
-
#
|
26
|
-
|
27
|
-
Tap::Test::HttpTest::Requests.add :get, :basic, %q{
|
28
|
-
GET /path HTTP/1.1
|
29
|
-
Host: www.example.com
|
30
|
-
Keep-Alive: 300
|
31
|
-
Connection: keep-alive
|
32
|
-
}, {
|
33
|
-
:url => "http://www.example.com/path",
|
34
|
-
:version => '1.1',
|
35
|
-
:request_method => 'GET',
|
36
|
-
:headers => {
|
37
|
-
"Host" => "www.example.com",
|
38
|
-
"Keep-Alive" => "300",
|
39
|
-
"Connection" => 'keep-alive'},
|
40
|
-
:params => {}
|
41
|
-
}
|
42
|
-
|
43
|
-
Tap::Test::HttpTest::Requests.add :get, :header_less, %q{
|
44
|
-
GET /path HTTP/1.1
|
45
|
-
}, {
|
46
|
-
:url => "/path",
|
47
|
-
:version => '1.1',
|
48
|
-
:request_method => 'GET',
|
49
|
-
:headers => {},
|
50
|
-
:params => {}
|
51
|
-
}
|
52
|
-
|
53
|
-
Tap::Test::HttpTest::Requests.add :get, :version_less, %q{
|
54
|
-
GET /path
|
55
|
-
}, {
|
56
|
-
:url => "/path",
|
57
|
-
:version => '0.9',
|
58
|
-
:request_method => 'GET',
|
59
|
-
:headers => {},
|
60
|
-
:params => {}
|
61
|
-
}
|
62
|
-
|
63
|
-
Tap::Test::HttpTest::Requests.add :get, :with_query, %q{
|
64
|
-
GET /path?one=value%20one&two=value%20two HTTP/1.1
|
65
|
-
}, {
|
66
|
-
:url => "/path",
|
67
|
-
:version => '1.1',
|
68
|
-
:request_method => 'GET',
|
69
|
-
:headers => {},
|
70
|
-
:params => {
|
71
|
-
'one' => 'value one',
|
72
|
-
'two' => 'value two'}
|
73
|
-
}
|
74
|
-
|
75
|
-
#
|
76
|
-
# post requests
|
77
|
-
#
|
78
|
-
|
79
|
-
Tap::Test::HttpTest::Requests.add :post, :with_multipart_form_data, %q{
|
80
|
-
POST /path HTTP/1.1
|
81
|
-
Host: www.example.com
|
82
|
-
Content-Type: multipart/form-data; boundary=1234567890
|
83
|
-
Content-Length: 158
|
84
|
-
|
85
|
-
--1234567890
|
86
|
-
Content-Disposition: form-data; name="one"
|
87
|
-
|
88
|
-
value one
|
89
|
-
--1234567890
|
90
|
-
Content-Disposition: form-data; name="two"
|
91
|
-
|
92
|
-
value two
|
93
|
-
--1234567890--
|
94
|
-
}, {
|
95
|
-
:url => "http://www.example.com/path",
|
96
|
-
:version => '1.1',
|
97
|
-
:request_method => 'POST',
|
98
|
-
:headers => {
|
99
|
-
"Host" => 'www.example.com',
|
100
|
-
"Content-Type" => "multipart/form-data; boundary=1234567890",
|
101
|
-
"Content-Length" => "158"},
|
102
|
-
:params => {
|
103
|
-
'one' => 'value one',
|
104
|
-
'two' => 'value two'}
|
105
|
-
}
|
106
|
-
|
107
|
-
Tap::Test::HttpTest::Requests.add :post, :with_multipart_data_and_multiple_values, %q{
|
108
|
-
POST /path HTTP/1.1
|
109
|
-
Host: www.example.com
|
110
|
-
Content-Type: multipart/form-data; boundary=1234567890
|
111
|
-
Content-Length: 158
|
112
|
-
|
113
|
-
--1234567890
|
114
|
-
Content-Disposition: form-data; name="key"
|
115
|
-
|
116
|
-
value one
|
117
|
-
--1234567890
|
118
|
-
Content-Disposition: form-data; name="key"
|
119
|
-
|
120
|
-
value two
|
121
|
-
--1234567890--
|
122
|
-
}, {
|
123
|
-
:url => "http://www.example.com/path",
|
124
|
-
:version => '1.1',
|
125
|
-
:request_method => 'POST',
|
126
|
-
:headers => {
|
127
|
-
"Host" => 'www.example.com',
|
128
|
-
"Content-Type" => "multipart/form-data; boundary=1234567890",
|
129
|
-
"Content-Length" => "158"},
|
130
|
-
:params => {
|
131
|
-
'key' => ["value one", "value two"]}
|
132
|
-
}
|
133
|
-
|
134
|
-
Tap::Test::HttpTest::Requests.add :post, :with_file_data, %q{
|
135
|
-
POST /path HTTP/1.1
|
136
|
-
Host: www.example.com
|
137
|
-
Content-Type: multipart/form-data; boundary=1234567890
|
138
|
-
Content-Length: 148
|
139
|
-
|
140
|
-
--1234567890
|
141
|
-
Content-Disposition: form-data; name="key"; filename="file.txt"
|
142
|
-
Content-Type: application/octet-stream
|
143
|
-
|
144
|
-
value one
|
145
|
-
--1234567890--
|
146
|
-
}, {
|
147
|
-
:url => "http://www.example.com/path",
|
148
|
-
:version => '1.1',
|
149
|
-
:request_method => 'POST',
|
150
|
-
:headers => {
|
151
|
-
"Host" => 'www.example.com',
|
152
|
-
"Content-Type" => "multipart/form-data; boundary=1234567890",
|
153
|
-
"Content-Length" => "148"},
|
154
|
-
:params => {
|
155
|
-
'key' => {'Filename' => 'file.txt', 'Content-Type' => 'application/octet-stream'}}
|
156
|
-
}
|
157
|
-
|
158
|
-
Tap::Test::HttpTest::Requests.add :post, :with_mixed_multi_value_file_data, %q{
|
159
|
-
POST /path HTTP/1.1
|
160
|
-
Host: www.example.com
|
161
|
-
Content-Type: multipart/form-data; boundary=1234567890
|
162
|
-
Content-Length: 329
|
163
|
-
|
164
|
-
--1234567890
|
165
|
-
Content-Disposition: form-data; name="key"
|
166
|
-
|
167
|
-
one
|
168
|
-
--1234567890
|
169
|
-
Content-Disposition: form-data; name="key"; filename="one.txt"
|
170
|
-
Content-Type: application/octet-stream
|
171
|
-
|
172
|
-
value one
|
173
|
-
--1234567890
|
174
|
-
Content-Disposition: form-data; name="key"; filename="two.txt"
|
175
|
-
Content-Type: text/plain
|
176
|
-
|
177
|
-
value two
|
178
|
-
--1234567890--
|
179
|
-
}, {
|
180
|
-
:url => "http://www.example.com/path",
|
181
|
-
:version => '1.1',
|
182
|
-
:request_method => 'POST',
|
183
|
-
:headers => {
|
184
|
-
"Host" => 'www.example.com',
|
185
|
-
"Content-Type" => "multipart/form-data; boundary=1234567890",
|
186
|
-
"Content-Length" => "329"},
|
187
|
-
:params => {
|
188
|
-
'key' => [
|
189
|
-
"one",
|
190
|
-
{'Filename' => 'one.txt', 'Content-Type' => 'application/octet-stream'},
|
191
|
-
{'Filename' => 'two.txt', 'Content-Type' => 'text/plain'}]}
|
192
|
-
}
|
193
|
-
|
194
|
-
|