xamarin-test-cloud 2.0.0.pre1 → 2.0.0.pre2
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 +2 -1
- data/README.md +4 -4
- data/lib/xamarin-test-cloud/cli.rb +18 -19
- data/lib/xamarin-test-cloud/http/payload.rb +268 -0
- data/lib/xamarin-test-cloud/http/request.rb +46 -0
- data/lib/xamarin-test-cloud/http/retriable_client.rb +166 -0
- data/lib/xamarin-test-cloud/version.rb +1 -1
- metadata +33 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5fb53a5aad5e3006ce408c5bcdc838fc9710b27
|
4
|
+
data.tar.gz: 08f838fe24bacc453b7b6b3479a3a23e467c376e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7715a00158c011e57f6671c25590577113bece273948659f9f01037147ab95a04d65546c7ee81515ad556a05a044412c65eca920598dc9cb6021aa35e87f3ed2
|
7
|
+
data.tar.gz: ff1dcb9dae040573210204b0dad1cc1a5343368b5148bceb477e9a1c9ea797ea836301715b9220d1c5327932ec91a15808524bb9195c62f9f681dcce429a9328
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -10,9 +10,9 @@
|
|
10
10
|
|
11
11
|
```
|
12
12
|
$ bundle update
|
13
|
-
$ rake test # All tests.
|
14
|
-
$ rake unit # Unit tests.
|
15
|
-
$ rake integration # Integration tests.
|
16
|
-
$ rake spec # rspec tests
|
13
|
+
$ bundle exec rake test # All tests.
|
14
|
+
$ bundle exec rake unit # Unit tests.
|
15
|
+
$ bundle exec rake integration # Integration tests.
|
16
|
+
$ bundle exec rake spec # rspec tests
|
17
17
|
```
|
18
18
|
|
@@ -4,7 +4,6 @@ require 'erb'
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'zip'
|
6
6
|
require 'digest'
|
7
|
-
require 'rest_client'
|
8
7
|
require 'json'
|
9
8
|
require 'rbconfig'
|
10
9
|
require 'tmpdir'
|
@@ -12,6 +11,8 @@ require 'fileutils'
|
|
12
11
|
require 'retriable'
|
13
12
|
require 'xamarin-test-cloud/version'
|
14
13
|
require 'xamarin-test-cloud/retriable_options'
|
14
|
+
require 'xamarin-test-cloud/http/request'
|
15
|
+
require 'xamarin-test-cloud/http/retriable_client'
|
15
16
|
require 'securerandom'
|
16
17
|
require 'open3'
|
17
18
|
|
@@ -391,7 +392,6 @@ module XamarinTestCloud
|
|
391
392
|
if debug?
|
392
393
|
log "Packaging gems in: #{tmpdir}"
|
393
394
|
end
|
394
|
-
start_at = Time.now
|
395
395
|
|
396
396
|
server = verify_app_and_extract_test_server
|
397
397
|
|
@@ -471,14 +471,13 @@ module XamarinTestCloud
|
|
471
471
|
puts "Will upload to file path: #{self.endpoint_path}"
|
472
472
|
end
|
473
473
|
|
474
|
-
|
475
|
-
response = http_post(endpoint_path, upload_data) do |response, request, result, &block|
|
474
|
+
response = http_post(endpoint_path, upload_data) do |response, request|
|
476
475
|
if debug?
|
477
476
|
puts "Request url: #{request.url}"
|
478
477
|
puts "Response code: #{response.code}"
|
479
478
|
puts "Response body: #{response.body}"
|
480
479
|
end
|
481
|
-
case response.
|
480
|
+
case response.status_code
|
482
481
|
when 200..202
|
483
482
|
response
|
484
483
|
when 400
|
@@ -496,7 +495,7 @@ module XamarinTestCloud
|
|
496
495
|
end
|
497
496
|
end
|
498
497
|
|
499
|
-
return :status => response.
|
498
|
+
return :status => response.status_code, :body => JSON.parse(response.body)
|
500
499
|
|
501
500
|
end
|
502
501
|
|
@@ -562,7 +561,6 @@ module XamarinTestCloud
|
|
562
561
|
|
563
562
|
response = http_post('check_hash', out)
|
564
563
|
|
565
|
-
|
566
564
|
cache_status = JSON.parse(response)
|
567
565
|
|
568
566
|
log_header('Gathering files based on negotiation')
|
@@ -701,26 +699,27 @@ module XamarinTestCloud
|
|
701
699
|
def http_post(address, args = {}, &block)
|
702
700
|
args['uploader_version'] = XamarinTestCloud::VERSION
|
703
701
|
args['session_id'] = session_id
|
704
|
-
|
702
|
+
|
703
|
+
request = XamarinTestCloud::HTTP::Request.new(address, args)
|
704
|
+
|
705
|
+
exec_options = {:header => { 'Content-Type' => 'application/x-www-form-urlencoded' }}
|
705
706
|
if ENV['XTC_USERNAME'] && ENV['XTC_PASSWORD']
|
706
707
|
exec_options[:user] = ENV['XTC_USERNAME']
|
707
708
|
exec_options[:password] = ENV['XTC_PASSWORD']
|
708
709
|
end
|
709
710
|
|
711
|
+
client = XamarinTestCloud::HTTP::RetriableClient.new("#{host}/")
|
712
|
+
|
710
713
|
if block_given?
|
711
|
-
exec_options =
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
response
|
716
|
-
block.call(response, request, result, &other_block)
|
717
|
-
end
|
714
|
+
exec_options[:header] = {'Content-Type' => 'multipart/form-data'}
|
715
|
+
exec_options[:timeout] = 90000000
|
716
|
+
response = client.post(request, exec_options)
|
717
|
+
block.call(response, request)
|
718
|
+
response
|
718
719
|
else
|
719
|
-
|
720
|
-
response
|
720
|
+
response = client.post(request, exec_options)
|
721
|
+
response.body
|
721
722
|
end
|
722
|
-
|
723
|
-
response.body
|
724
723
|
end
|
725
724
|
|
726
725
|
def is_windows?
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# Adapted from RestClient payload.rb and used under the following license:
|
2
|
+
#
|
3
|
+
# The MIT License (MIT)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2008-2014 Rest Client Authors
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
15
|
+
# copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
require 'tempfile'
|
26
|
+
require 'stringio'
|
27
|
+
require 'mime/types'
|
28
|
+
|
29
|
+
module XamarinTestCloud
|
30
|
+
module HTTP
|
31
|
+
|
32
|
+
# Copied from rest-client
|
33
|
+
module Payload
|
34
|
+
extend self
|
35
|
+
|
36
|
+
|
37
|
+
def generate(params)
|
38
|
+
if params.is_a?(String)
|
39
|
+
Base.new(params)
|
40
|
+
elsif params.is_a?(Hash)
|
41
|
+
if params.delete(:multipart) == true || has_file?(params)
|
42
|
+
Multipart.new(params)
|
43
|
+
else
|
44
|
+
UrlEncoded.new(params)
|
45
|
+
end
|
46
|
+
elsif params.respond_to?(:read)
|
47
|
+
Streamed.new(params)
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def has_file?(params)
|
54
|
+
params.any? do |_, v|
|
55
|
+
case v
|
56
|
+
when Hash
|
57
|
+
has_file?(v)
|
58
|
+
when Array
|
59
|
+
has_file_array?(v)
|
60
|
+
else
|
61
|
+
v.respond_to?(:path) && v.respond_to?(:read)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_file_array?(params)
|
67
|
+
params.any? do |v|
|
68
|
+
case v
|
69
|
+
when Hash
|
70
|
+
has_file?(v)
|
71
|
+
when Array
|
72
|
+
has_file_array?(v)
|
73
|
+
else
|
74
|
+
v.respond_to?(:path) && v.respond_to?(:read)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Base
|
80
|
+
def initialize(params)
|
81
|
+
build_stream(params)
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_stream(params)
|
85
|
+
@stream = StringIO.new(params)
|
86
|
+
@stream.seek(0)
|
87
|
+
end
|
88
|
+
|
89
|
+
def read(bytes=nil)
|
90
|
+
@stream.read(bytes)
|
91
|
+
end
|
92
|
+
|
93
|
+
alias :to_s :read
|
94
|
+
|
95
|
+
# Flatten parameters by converting hashes of hashes to flat hashes
|
96
|
+
# {keys1 => {keys2 => value}} will be transformed into [keys1[key2], value]
|
97
|
+
def flatten_params(params, parent_key = nil)
|
98
|
+
result = []
|
99
|
+
params.each do |key, value|
|
100
|
+
calculated_key = parent_key ? "#{parent_key}[#{handle_key(key)}]" : handle_key(key)
|
101
|
+
if value.is_a? Hash
|
102
|
+
result += flatten_params(value, calculated_key)
|
103
|
+
elsif value.is_a? Array
|
104
|
+
result += flatten_params_array(value, calculated_key)
|
105
|
+
else
|
106
|
+
result << [calculated_key, value]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
def flatten_params_array(value, calculated_key)
|
113
|
+
result = []
|
114
|
+
value.each do |elem|
|
115
|
+
if elem.is_a? Hash
|
116
|
+
result += flatten_params(elem, calculated_key)
|
117
|
+
elsif elem.is_a? Array
|
118
|
+
result += flatten_params_array(elem, calculated_key)
|
119
|
+
else
|
120
|
+
result << ["#{calculated_key}[]", elem]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
def headers
|
127
|
+
{'Content-Length' => size.to_s}
|
128
|
+
end
|
129
|
+
|
130
|
+
def size
|
131
|
+
@stream.size
|
132
|
+
end
|
133
|
+
|
134
|
+
alias :length :size
|
135
|
+
|
136
|
+
def close
|
137
|
+
@stream.close unless @stream.closed?
|
138
|
+
end
|
139
|
+
|
140
|
+
def inspect
|
141
|
+
result = to_s.inspect
|
142
|
+
@stream.seek(0)
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
def short_inspect
|
147
|
+
(size > 500 ? "#{size} byte(s) length" : inspect)
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
class Streamed < Base
|
153
|
+
def build_stream(params = nil)
|
154
|
+
@stream = params
|
155
|
+
end
|
156
|
+
|
157
|
+
def size
|
158
|
+
if @stream.respond_to?(:size)
|
159
|
+
@stream.size
|
160
|
+
elsif @stream.is_a?(IO)
|
161
|
+
@stream.stat.size
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
alias :length :size
|
166
|
+
end
|
167
|
+
|
168
|
+
class UrlEncoded < Base
|
169
|
+
def build_stream(params = nil)
|
170
|
+
@stream = StringIO.new(flatten_params(params).collect do |entry|
|
171
|
+
"#{entry[0]}=#{handle_key(entry[1])}"
|
172
|
+
end.join("&"))
|
173
|
+
@stream.seek(0)
|
174
|
+
end
|
175
|
+
|
176
|
+
# for UrlEncoded escape the keys
|
177
|
+
def handle_key key
|
178
|
+
Parser.escape(key.to_s, Escape)
|
179
|
+
end
|
180
|
+
|
181
|
+
def headers
|
182
|
+
super.merge({'Content-Type' => 'application/x-www-form-urlencoded'})
|
183
|
+
end
|
184
|
+
|
185
|
+
Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
186
|
+
Escape = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
187
|
+
end
|
188
|
+
|
189
|
+
class Multipart < Base
|
190
|
+
EOL = "\r\n"
|
191
|
+
|
192
|
+
def build_stream(params)
|
193
|
+
b = "--#{boundary}"
|
194
|
+
|
195
|
+
@stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
|
196
|
+
@stream.binmode
|
197
|
+
@stream.write(b + EOL)
|
198
|
+
|
199
|
+
if params.is_a? Hash
|
200
|
+
x = flatten_params(params)
|
201
|
+
else
|
202
|
+
x = params
|
203
|
+
end
|
204
|
+
|
205
|
+
last_index = x.length - 1
|
206
|
+
x.each_with_index do |a, index|
|
207
|
+
k, v = * a
|
208
|
+
if v.respond_to?(:read) && v.respond_to?(:path)
|
209
|
+
create_file_field(@stream, k, v)
|
210
|
+
else
|
211
|
+
create_regular_field(@stream, k, v)
|
212
|
+
end
|
213
|
+
@stream.write(EOL + b)
|
214
|
+
@stream.write(EOL) unless last_index == index
|
215
|
+
end
|
216
|
+
@stream.write('--')
|
217
|
+
@stream.write(EOL)
|
218
|
+
@stream.seek(0)
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_regular_field(s, k, v)
|
222
|
+
s.write("Content-Disposition: form-data; name=\"#{k}\"")
|
223
|
+
s.write(EOL)
|
224
|
+
s.write(EOL)
|
225
|
+
s.write(v)
|
226
|
+
end
|
227
|
+
|
228
|
+
def create_file_field(s, k, v)
|
229
|
+
begin
|
230
|
+
s.write("Content-Disposition: form-data;")
|
231
|
+
s.write(" name=\"#{k}\";") unless (k.nil? || k=='')
|
232
|
+
s.write(" filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
|
233
|
+
s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
|
234
|
+
s.write(EOL)
|
235
|
+
while (data = v.read(8124))
|
236
|
+
s.write(data)
|
237
|
+
end
|
238
|
+
ensure
|
239
|
+
v.close if v.respond_to?(:close)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def mime_for(path)
|
244
|
+
mime = MIME::Types.type_for path
|
245
|
+
mime.empty? ? 'text/plain' : mime[0].content_type
|
246
|
+
end
|
247
|
+
|
248
|
+
def boundary
|
249
|
+
@boundary ||= rand(1_000_000).to_s
|
250
|
+
end
|
251
|
+
|
252
|
+
# for Multipart do not escape the keys
|
253
|
+
def handle_key key
|
254
|
+
key
|
255
|
+
end
|
256
|
+
|
257
|
+
def headers
|
258
|
+
super.merge({'Content-Type' => %Q{multipart/form-data; boundary=#{boundary}}})
|
259
|
+
end
|
260
|
+
|
261
|
+
def close
|
262
|
+
@stream.close!
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module XamarinTestCloud
|
2
|
+
module HTTP
|
3
|
+
|
4
|
+
class RequestError < RuntimeError; ; end
|
5
|
+
|
6
|
+
# A representation of an HTTP request that can be passed passed to the HTTP
|
7
|
+
# client as an argument for `get` or `post`.
|
8
|
+
# @!visibility private
|
9
|
+
class Request
|
10
|
+
attr_reader :route, :params
|
11
|
+
|
12
|
+
def initialize(route, params={})
|
13
|
+
@route = route
|
14
|
+
@params = params
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a new Request from `route` and `parameters`.
|
18
|
+
#
|
19
|
+
# @param [String] route The http route for the new request.
|
20
|
+
# @param [Array, Hash] parameters An Array or Hash of parameters.
|
21
|
+
# @return [Request] A new Request for `route` with `parameters`.
|
22
|
+
# @raise [RequestError] Raises an error if the parameters cannot be
|
23
|
+
# converted to JSON
|
24
|
+
def self.request(route, parameters)
|
25
|
+
Request.new(route, Request.data(parameters))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Converts `parameters` to JSON.
|
31
|
+
#
|
32
|
+
# @param [Array, Hash] parameters An Array or Hash of parameters.
|
33
|
+
# @return [String] A JSON formatted string that represents the parameters.
|
34
|
+
# @raise [RequestError] Raises an error if the parameters cannot be
|
35
|
+
# converted to JSON
|
36
|
+
def self.data(parameters)
|
37
|
+
begin
|
38
|
+
JSON.generate(parameters)
|
39
|
+
rescue *[TypeError, JSON::GeneratorError] => e
|
40
|
+
raise RequestError, "#{e}: could not generate JSON from '#{parameters}'"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
require 'xamarin-test-cloud/http/payload'
|
3
|
+
|
4
|
+
module XamarinTestCloud
|
5
|
+
module HTTP
|
6
|
+
|
7
|
+
class Error < RuntimeError; ; end
|
8
|
+
|
9
|
+
# An HTTP client that retries its connection on errors and can time out.
|
10
|
+
# @!visibility private
|
11
|
+
class RetriableClient
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
RETRY_ON =
|
16
|
+
[
|
17
|
+
# Raised for indicating a connection timeout error
|
18
|
+
HTTPClient::ConnectTimeoutError,
|
19
|
+
|
20
|
+
# Raised for indicating a request sending timeout error
|
21
|
+
HTTPClient::SendTimeoutError,
|
22
|
+
|
23
|
+
# Raised for indicating a response receiving timeout error.
|
24
|
+
HTTPClient::ReceiveTimeoutError,
|
25
|
+
|
26
|
+
Errno::ECONNREFUSED,
|
27
|
+
|
28
|
+
# The server sent a partial response
|
29
|
+
# Errno::ECONNRESET,
|
30
|
+
#
|
31
|
+
# Client sent TCP reset (RST) before server has accepted the
|
32
|
+
# connection requested by client.
|
33
|
+
Errno::ECONNABORTED,
|
34
|
+
]
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
HEADER =
|
38
|
+
{
|
39
|
+
'Content-Type' => 'application/json;charset=utf-8'
|
40
|
+
}
|
41
|
+
|
42
|
+
# Creates a new retriable client.
|
43
|
+
#
|
44
|
+
# This initializer takes multiple options. If the option is not
|
45
|
+
# documented, it should be considered _private_. You use undocumented
|
46
|
+
# options at your own risk.
|
47
|
+
#
|
48
|
+
# @param [String] endpoint The server to make the HTTP request
|
49
|
+
# on.
|
50
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
51
|
+
# @option options [Number] :retries (5) How often to retry.
|
52
|
+
# @option options [Number] :timeout (5) How long to wait for a response
|
53
|
+
# before timing out.
|
54
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
55
|
+
# retries.
|
56
|
+
def initialize(endpoint, options = {})
|
57
|
+
@client = options[:client] || ::HTTPClient.new
|
58
|
+
@endpoint = endpoint
|
59
|
+
@retries = options.fetch(:retries, 3)
|
60
|
+
@timeout = options.fetch(:timeout, 15)
|
61
|
+
@connect_timeout = options.fetch(:connect_timeout, 15)
|
62
|
+
@interval = options.fetch(:interval, 0.5)
|
63
|
+
@on_error = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!visibility private
|
67
|
+
def on_error(type, &block)
|
68
|
+
@on_error[type] = block
|
69
|
+
end
|
70
|
+
|
71
|
+
# Make an HTTP get request.
|
72
|
+
#
|
73
|
+
# This method takes multiple options. If the option is not documented,
|
74
|
+
# it should be considered _private_. You use undocumented options at
|
75
|
+
# your own risk.
|
76
|
+
#
|
77
|
+
# @param [Calabash::HTTP::Request] request The request.
|
78
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
79
|
+
# @option options [Number] :retries (5) How often to retry.
|
80
|
+
# @option options [Number] :timeout (5) How long to wait for a response
|
81
|
+
# before timing out.
|
82
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
83
|
+
# retries.
|
84
|
+
def get(request, options={})
|
85
|
+
request(request, :get, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Make an HTTP post request.
|
89
|
+
#
|
90
|
+
# This method takes multiple options. If the option is not documented,
|
91
|
+
# it should be considered _private_. You use undocumented options at
|
92
|
+
# your own risk.
|
93
|
+
#
|
94
|
+
# @param [Calabash::HTTP::Request] request The request.
|
95
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
96
|
+
# @option options [Number] :retries (3) How many times to retry.
|
97
|
+
# @option options [Number] :timeout (15) How long to wait for a response
|
98
|
+
# before timing out.
|
99
|
+
# @option options [Number] :connect_timeout (15) How long to wait for a
|
100
|
+
# connection.
|
101
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
102
|
+
# retries.
|
103
|
+
def post(request, options={})
|
104
|
+
request(request, :post, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def request(request, request_method, options={})
|
110
|
+
retries = options.fetch(:retries, @retries)
|
111
|
+
timeout = options.fetch(:timeout, @timeout)
|
112
|
+
connect_timeout = options.fetch(:timeout, @connect_timeout)
|
113
|
+
interval = options.fetch(:interval, @interval)
|
114
|
+
header = options.fetch(:header, HEADER)
|
115
|
+
|
116
|
+
start_time = Time.now
|
117
|
+
last_error = nil
|
118
|
+
|
119
|
+
client = @client.dup
|
120
|
+
client.receive_timeout = timeout
|
121
|
+
client.connect_timeout = connect_timeout
|
122
|
+
|
123
|
+
if options.fetch(:user, nil) && options.fetch(:password, nil)
|
124
|
+
client.set_auth(options.fetch(:user), options.fetch(:password))
|
125
|
+
end
|
126
|
+
|
127
|
+
retries.times do |i|
|
128
|
+
first_try = i == 0
|
129
|
+
|
130
|
+
# Subtract the aggregate time we've spent thus far to make sure we're
|
131
|
+
# not exceeding the request timeout across retries.
|
132
|
+
time_diff = start_time + timeout - Time.now
|
133
|
+
|
134
|
+
|
135
|
+
if time_diff <= 0
|
136
|
+
raise HTTP::Error, 'Timeout exceeded'
|
137
|
+
end
|
138
|
+
|
139
|
+
client.receive_timeout = [time_diff, client.receive_timeout].min
|
140
|
+
|
141
|
+
payload = Payload.generate(request.params)
|
142
|
+
|
143
|
+
header.merge!(payload.headers) if payload
|
144
|
+
|
145
|
+
begin
|
146
|
+
return client.send(request_method, @endpoint + request.route,
|
147
|
+
payload.to_s, header)
|
148
|
+
rescue *RETRY_ON => e
|
149
|
+
|
150
|
+
if first_try
|
151
|
+
if @on_error[e.class]
|
152
|
+
@on_error[e.class].call(@endpoint)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
last_error = e
|
157
|
+
sleep interval
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
raise HTTP::Error, last_error
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xamarin-test-cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.pre2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Krukow
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-01-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -79,20 +79,6 @@ dependencies:
|
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '1.1'
|
82
|
-
- !ruby/object:Gem::Dependency
|
83
|
-
name: rest-client
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '1.6'
|
89
|
-
type: :runtime
|
90
|
-
prerelease: false
|
91
|
-
version_requirements: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '1.6'
|
96
82
|
- !ruby/object:Gem::Dependency
|
97
83
|
name: retriable
|
98
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,6 +99,34 @@ dependencies:
|
|
113
99
|
- - "<"
|
114
100
|
- !ruby/object:Gem::Version
|
115
101
|
version: '2.1'
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: httpclient
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.6'
|
109
|
+
type: :runtime
|
110
|
+
prerelease: false
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '2.6'
|
116
|
+
- !ruby/object:Gem::Dependency
|
117
|
+
name: mime-types
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '2.99'
|
123
|
+
type: :runtime
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '2.99'
|
116
130
|
- !ruby/object:Gem::Dependency
|
117
131
|
name: rake
|
118
132
|
requirement: !ruby/object:Gem::Requirement
|
@@ -259,6 +273,9 @@ files:
|
|
259
273
|
- README.md
|
260
274
|
- bin/test-cloud
|
261
275
|
- lib/xamarin-test-cloud/cli.rb
|
276
|
+
- lib/xamarin-test-cloud/http/payload.rb
|
277
|
+
- lib/xamarin-test-cloud/http/request.rb
|
278
|
+
- lib/xamarin-test-cloud/http/retriable_client.rb
|
262
279
|
- lib/xamarin-test-cloud/retriable_options.rb
|
263
280
|
- lib/xamarin-test-cloud/version.rb
|
264
281
|
homepage: http://xamarin.com/test-cloud
|