baidubce-sdk 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +177 -0
- data/README.md +1266 -0
- data/Rakefile +6 -0
- data/baidubce-sdk.gemspec +31 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/baidubce/auth/bce_credentials.rb +31 -0
- data/lib/baidubce/auth/bce_v1_signer.rb +76 -0
- data/lib/baidubce/bce_base_client.rb +52 -0
- data/lib/baidubce/bce_client_configuration.rb +47 -0
- data/lib/baidubce/bce_constants.rb +20 -0
- data/lib/baidubce/exception.rb +34 -0
- data/lib/baidubce/http/base_http_client.rb +259 -0
- data/lib/baidubce/http/http_constants.rb +102 -0
- data/lib/baidubce/retry_policy.rb +87 -0
- data/lib/baidubce/services/bos/bos_client.rb +461 -0
- data/lib/baidubce/services/bos/bos_constants.rb +25 -0
- data/lib/baidubce/services/sts/sts_client.rb +38 -0
- data/lib/baidubce/utils/log.rb +51 -0
- data/lib/baidubce/utils/utils.rb +124 -0
- data/lib/baidubce/version.rb +7 -0
- data/samples/baidubce/bos_sample.rb +376 -0
- data/samples/baidubce/sts_sample.rb +82 -0
- metadata +174 -0
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'baidubce/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "baidubce-sdk"
|
8
|
+
spec.version = Baidubce::VERSION
|
9
|
+
spec.authors = ["xiaoyong"]
|
10
|
+
spec.email = ["xiaoyong@baidu.com"]
|
11
|
+
|
12
|
+
spec.summary = 'BaiduBce BOS Ruby SDK'
|
13
|
+
spec.description = 'The official Ruby sdk used to accessing BaiduBce Object Storage Service'
|
14
|
+
spec.homepage = "https://github.com/baidubce/bce-sdk-ruby"
|
15
|
+
spec.license = "Apache-2.0"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "lib/baidubce"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "rest-client", "~> 2.0", ">= 2.0.2"
|
28
|
+
spec.add_development_dependency "logger", "~> 1.2", ">= 1.2.8"
|
29
|
+
spec.add_development_dependency "mimemagic", "~> 0.3", ">= 0.3.2"
|
30
|
+
spec.required_ruby_version = ">= 2.0.0"
|
31
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "baidubce/bce"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
=begin
|
14
|
+
Provides access to the BCE credentials used for accessing BCE services: BCE access key ID and
|
15
|
+
secret access key.
|
16
|
+
These credentials are used to securely sign requests to BCE services.
|
17
|
+
=end
|
18
|
+
|
19
|
+
module Baidubce
|
20
|
+
module Auth
|
21
|
+
class BceCredentials
|
22
|
+
attr_accessor :access_key_id, :secret_access_key, :security_token
|
23
|
+
|
24
|
+
def initialize(access_key_id, secret_access_key, security_token="")
|
25
|
+
@access_key_id = access_key_id
|
26
|
+
@secret_access_key = secret_access_key
|
27
|
+
@security_token = security_token
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
# This module provides authentication functions for bce services.
|
14
|
+
|
15
|
+
require 'time'
|
16
|
+
require 'openssl'
|
17
|
+
|
18
|
+
require_relative '../utils/utils'
|
19
|
+
|
20
|
+
module Baidubce
|
21
|
+
module Auth
|
22
|
+
class BceV1Signer
|
23
|
+
|
24
|
+
def get_canonical_headers(headers, headers_to_sign = nil)
|
25
|
+
default = false
|
26
|
+
if headers_to_sign.to_a.empty?
|
27
|
+
default = true
|
28
|
+
headers_to_sign = ["host", "content-md5", "content-length", "content-type"]
|
29
|
+
end
|
30
|
+
|
31
|
+
ret_arr = []
|
32
|
+
headers_arr = []
|
33
|
+
headers.each do |key, value|
|
34
|
+
next if value.to_s.strip.empty?
|
35
|
+
if headers_to_sign.include?(key.downcase) ||
|
36
|
+
(default && key.downcase.to_s.start_with?(Http::BCE_PREFIX))
|
37
|
+
str = ERB::Util.url_encode(key.downcase) + ":" + ERB::Util.url_encode(value.to_s.strip)
|
38
|
+
ret_arr << str
|
39
|
+
headers_arr << key.downcase
|
40
|
+
end
|
41
|
+
end
|
42
|
+
ret_arr.sort!
|
43
|
+
headers_arr.sort!
|
44
|
+
return ret_arr.join("\n"), headers_arr
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_canonical_uri_path(path)
|
48
|
+
return '/' if path.to_s.empty?
|
49
|
+
encoded_path = Utils.url_encode_except_slash(path)
|
50
|
+
return path[0] == '/' ? encoded_path : '/' + encoded_path
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create the authorization.
|
54
|
+
def sign(credentials, http_method, path, headers, params,
|
55
|
+
timestamp=nil, expiration_in_seconds=1800, headers_to_sign=nil)
|
56
|
+
|
57
|
+
timestamp = Time.now.to_i if timestamp.nil?
|
58
|
+
sign_key_info = sprintf('bce-auth-v1/%s/%s/%d',
|
59
|
+
credentials.access_key_id,
|
60
|
+
Time.at(timestamp).utc.iso8601,
|
61
|
+
expiration_in_seconds)
|
62
|
+
sign_key = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
63
|
+
credentials.secret_access_key, sign_key_info)
|
64
|
+
canonical_uri = get_canonical_uri_path(path)
|
65
|
+
canonical_querystring = Utils.get_canonical_querystring(params, true)
|
66
|
+
canonical_headers, headers_to_sign = get_canonical_headers(headers, headers_to_sign)
|
67
|
+
canonical_request = [http_method, canonical_uri, canonical_querystring, canonical_headers].join("\n")
|
68
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
69
|
+
sign_key, canonical_request)
|
70
|
+
|
71
|
+
headers_str = headers_to_sign.join(';') unless headers_to_sign.nil?
|
72
|
+
sign_key_info + '/' + headers_str + '/' + signature
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright 2017 Baidu, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
# This module provide base class for BCE service clients.
|
14
|
+
|
15
|
+
require_relative 'auth/bce_v1_signer'
|
16
|
+
require_relative 'auth/bce_credentials'
|
17
|
+
require_relative 'http/base_http_client'
|
18
|
+
require_relative 'bce_client_configuration'
|
19
|
+
|
20
|
+
module Baidubce
|
21
|
+
|
22
|
+
class BceBaseClient
|
23
|
+
|
24
|
+
include Http
|
25
|
+
include Auth
|
26
|
+
|
27
|
+
def initialize(config, service_id="", region_supported=true)
|
28
|
+
@config = config
|
29
|
+
@service_id = service_id
|
30
|
+
@region_supported = region_supported
|
31
|
+
@config.endpoint = compute_endpoint if @config.endpoint.to_s.empty?
|
32
|
+
@http_client = BaseHttpClient.new()
|
33
|
+
@signer = BceV1Signer.new()
|
34
|
+
end
|
35
|
+
|
36
|
+
def compute_endpoint
|
37
|
+
if @region_supported
|
38
|
+
return sprintf('%s://%s.%s.%s',
|
39
|
+
@config.protocol,
|
40
|
+
@service_id,
|
41
|
+
@config.region,
|
42
|
+
DEFAULT_SERVICE_DOMAIN)
|
43
|
+
else
|
44
|
+
return sprintf('%s://%s.%s',
|
45
|
+
@config.protocol,
|
46
|
+
@service_id,
|
47
|
+
DEFAULT_SERVICE_DOMAIN)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright 2017 Baidu, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
# This module defines a common configuration class for BCE.
|
14
|
+
|
15
|
+
require_relative 'retry_policy'
|
16
|
+
|
17
|
+
module Baidubce
|
18
|
+
|
19
|
+
DEFAULT_PROTOCOL = "http"
|
20
|
+
DEFAULT_REGION = "bj"
|
21
|
+
DEFAULT_OPEN_TIMEOUT_IN_MILLIS = 60 * 1000
|
22
|
+
DEFAULT_READ_TIMEOUT_IN_MILLIS = 10 * 60 * 1000
|
23
|
+
DEFAULT_SEND_BUF_SIZE = 1024 * 1024
|
24
|
+
DEFAULT_RECV_BUF_SIZE = 10 * 1024 * 1024
|
25
|
+
|
26
|
+
class BceClientConfiguration
|
27
|
+
attr_accessor :credentials, :endpoint, :protocol, :region, :open_timeout_in_millis,
|
28
|
+
:read_timeout_in_millis, :send_buf_size, :recv_buf_size, :retry_policy
|
29
|
+
|
30
|
+
def initialize(credentials,
|
31
|
+
endpoint,
|
32
|
+
options={})
|
33
|
+
|
34
|
+
@credentials = credentials
|
35
|
+
@endpoint = endpoint
|
36
|
+
@protocol = options['protocol'] || DEFAULT_PROTOCOL
|
37
|
+
@region = options['region'] || DEFAULT_REGION
|
38
|
+
@open_timeout_in_millis = options['open_timeout_in_millis'] ||
|
39
|
+
DEFAULT_OPEN_TIMEOUT_IN_MILLIS
|
40
|
+
@read_timeout_in_millis = options['read_timeout_in_millis'] || DEFAULT_READ_TIMEOUT_IN_MILLIS
|
41
|
+
@send_buf_size = options['send_buf_size'] || DEFAULT_SEND_BUF_SIZE
|
42
|
+
@recv_buf_size = options['recv_buf_size'] || DEFAULT_RECV_BUF_SIZE
|
43
|
+
@retry_policy = options['retry_policy'] || BackOffRetryPolicy.new
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright 2017 Baidu, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
# This module provide constants for BCE sdk.
|
14
|
+
|
15
|
+
module Baidubce
|
16
|
+
|
17
|
+
DEFAULT_SERVICE_DOMAIN = 'baidubce.com'
|
18
|
+
DEFAULT_ENCODING = 'UTF-8'
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class BceClientException < RuntimeError
|
2
|
+
attr_accessor :message
|
3
|
+
|
4
|
+
def initialize(message)
|
5
|
+
@message = message
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class BceServerException < RuntimeError
|
11
|
+
attr_accessor :status_code
|
12
|
+
attr_accessor :message
|
13
|
+
|
14
|
+
def initialize(status_code, message)
|
15
|
+
@status_code = status_code
|
16
|
+
@message = message
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class BceHttpException < RuntimeError
|
22
|
+
attr_accessor :http_code
|
23
|
+
attr_accessor :http_headers
|
24
|
+
attr_accessor :http_body
|
25
|
+
attr_accessor :message
|
26
|
+
|
27
|
+
def initialize(http_code, http_headers, http_body, message)
|
28
|
+
@http_code = http_code
|
29
|
+
@http_headers = http_headers
|
30
|
+
@http_body = http_body
|
31
|
+
@message = message
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# Copyright 2017 Baidu, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
4
|
+
# except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the
|
9
|
+
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
10
|
+
# either express or implied. See the License for the specific language governing permissions
|
11
|
+
# and limitations under the License.
|
12
|
+
|
13
|
+
# This module provide http request function for bce services.
|
14
|
+
|
15
|
+
require 'rest-client'
|
16
|
+
require 'time'
|
17
|
+
require 'objspace'
|
18
|
+
require 'fiber'
|
19
|
+
|
20
|
+
require_relative '../version'
|
21
|
+
require_relative '../exception'
|
22
|
+
require_relative '../retry_policy'
|
23
|
+
require_relative '../utils/utils'
|
24
|
+
require_relative '../utils/log'
|
25
|
+
require_relative '../bce_constants'
|
26
|
+
require_relative 'http_constants'
|
27
|
+
|
28
|
+
module Baidubce
|
29
|
+
module Http
|
30
|
+
|
31
|
+
class BaseHttpClient
|
32
|
+
|
33
|
+
include Log
|
34
|
+
# Send request to BCE services.
|
35
|
+
def send_request(config, signer, http_method, path, params, headers, body, save_path=nil, &block)
|
36
|
+
headers[USER_AGENT] = sprintf(
|
37
|
+
'bce-sdk-ruby/%s/%s/%s',
|
38
|
+
VERSION,
|
39
|
+
RUBY_VERSION,
|
40
|
+
RUBY_PLATFORM
|
41
|
+
)
|
42
|
+
|
43
|
+
should_get_new_date = headers.has_key?(BCE_DATE) ? false : true
|
44
|
+
|
45
|
+
url, headers[HOST] = Utils.parse_url_host(config)
|
46
|
+
url += Utils.url_encode_except_slash(path)
|
47
|
+
query_str = Utils.get_canonical_querystring(params, false)
|
48
|
+
url += "?#{query_str}" unless query_str.to_s.empty?
|
49
|
+
|
50
|
+
logger.info("url: #{url}, params: #{params}")
|
51
|
+
set_content_length_header(headers, body, &block)
|
52
|
+
headers[STS_SECURITY_TOKEN] = config.credentials.security_token unless config.credentials.security_token.to_s.empty?
|
53
|
+
|
54
|
+
retries_attempted = 0
|
55
|
+
while true
|
56
|
+
headers[BCE_DATE] = Time.now.utc.iso8601 if should_get_new_date
|
57
|
+
headers[AUTHORIZATION] = signer.sign(config.credentials, http_method,
|
58
|
+
path, headers, params)
|
59
|
+
|
60
|
+
logger.debug("Request headers: #{headers}")
|
61
|
+
args = { method: http_method,
|
62
|
+
url: url,
|
63
|
+
headers: headers,
|
64
|
+
payload: body,
|
65
|
+
open_timeout: config.open_timeout_in_millis / 1000.0,
|
66
|
+
read_timeout: config.read_timeout_in_millis / 1000.0
|
67
|
+
}
|
68
|
+
args[:payload] = BufWriter.new(&block) if block_given?
|
69
|
+
|
70
|
+
begin
|
71
|
+
if save_path
|
72
|
+
logger.debug("Response save file path: #{save_path}")
|
73
|
+
resp_headers = {}
|
74
|
+
File.open(save_path, 'w+') { |f|
|
75
|
+
block = proc { |response|
|
76
|
+
response.read_body { |chunk| f << chunk }
|
77
|
+
resp_headers = response.to_hash
|
78
|
+
resp_headers.each { |k, v| resp_headers[k]=v[0] }
|
79
|
+
raise BceHttpException.new(response.code.to_i,
|
80
|
+
resp_headers, '', 'get_object_to_file exception') if response.code.to_i > 300
|
81
|
+
}
|
82
|
+
block_arg = { block_response: block }
|
83
|
+
args.merge! block_arg
|
84
|
+
RestClient::Request.new(args).execute
|
85
|
+
return '', resp_headers
|
86
|
+
}
|
87
|
+
else
|
88
|
+
resp = RestClient::Request.new(args).execute
|
89
|
+
logger.debug("Response code: #{resp.code}")
|
90
|
+
logger.debug("Response headers: #{resp.headers.to_s}")
|
91
|
+
return resp.body, resp.headers
|
92
|
+
end
|
93
|
+
rescue BceHttpException, RestClient::ExceptionWithResponse => err
|
94
|
+
logger.debug("ExceptionWithResponse: #{err.http_code}, #{err.http_body}, #{err.http_headers}, #{err.message}")
|
95
|
+
if config.retry_policy.should_retry(err.http_code, retries_attempted)
|
96
|
+
delay_in_millis = config.retry_policy.get_delay_before_next_retry_in_millis(retries_attempted)
|
97
|
+
sleep(delay_in_millis / 1000.0)
|
98
|
+
else
|
99
|
+
request_id = err.http_headers[:x_bce_request_id]
|
100
|
+
if err.is_a?(BceHttpException)
|
101
|
+
err.http_body = File.read(save_path)
|
102
|
+
request_id = err.http_headers['x-bce-request-id']
|
103
|
+
end
|
104
|
+
msg = err.http_body
|
105
|
+
if err.http_body.empty?
|
106
|
+
msg = "{\"code\":\"#{err.http_code}\",\"message\":\"#{err.message}\",\"requestId\":\"#{request_id}\"}"
|
107
|
+
end
|
108
|
+
raise BceServerException.new(err.http_code, msg)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
retries_attempted += 1
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
def set_content_length_header(headers, body, &block)
|
118
|
+
unless block_given?
|
119
|
+
if body.to_s.empty?
|
120
|
+
headers[CONTENT_LENGTH] = 0
|
121
|
+
elsif body.instance_of?(String)
|
122
|
+
body = body.encode('UTF-8') if body.encoding.name != 'UTF-8'
|
123
|
+
headers[CONTENT_LENGTH] = body.bytesize
|
124
|
+
elsif body.instance_of?(File)
|
125
|
+
headers[CONTENT_LENGTH] = body.size()
|
126
|
+
else
|
127
|
+
headers[CONTENT_LENGTH] = ObjectSpace.memsize_of(body)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class BufWriter
|
133
|
+
|
134
|
+
def initialize()
|
135
|
+
@buffer = ""
|
136
|
+
@producer = Fiber.new { yield self if block_given? }
|
137
|
+
@producer.resume
|
138
|
+
end
|
139
|
+
|
140
|
+
def read(bytes = nil, outbuf = nil)
|
141
|
+
ret = ""
|
142
|
+
while true
|
143
|
+
if bytes
|
144
|
+
fail if bytes < 0
|
145
|
+
piece = @buffer.slice!(0, bytes)
|
146
|
+
if piece
|
147
|
+
ret << piece
|
148
|
+
bytes -= piece.size
|
149
|
+
break if bytes == 0
|
150
|
+
end
|
151
|
+
else
|
152
|
+
ret << @buffer
|
153
|
+
@buffer.clear
|
154
|
+
end
|
155
|
+
|
156
|
+
if @producer.alive?
|
157
|
+
@producer.resume
|
158
|
+
else
|
159
|
+
break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
if outbuf
|
164
|
+
outbuf.clear
|
165
|
+
outbuf << ret
|
166
|
+
end
|
167
|
+
|
168
|
+
return nil if ret.empty? && !bytes.nil? && bytes > 0
|
169
|
+
ret
|
170
|
+
end
|
171
|
+
|
172
|
+
def write(chunk)
|
173
|
+
@buffer << chunk.to_s
|
174
|
+
Fiber.yield
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
alias << write
|
179
|
+
|
180
|
+
def closed?
|
181
|
+
false
|
182
|
+
end
|
183
|
+
|
184
|
+
def close
|
185
|
+
end
|
186
|
+
|
187
|
+
def inspect
|
188
|
+
"@buffer: " + @buffer[0, 32].inspect + "...#{@buffer.size} bytes"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module RestClient
|
196
|
+
module Payload
|
197
|
+
class Base
|
198
|
+
def headers
|
199
|
+
({'content-length' => size.to_s} if size) || {}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
module Net
|
206
|
+
class HTTP
|
207
|
+
def transport_request(req)
|
208
|
+
count = 0
|
209
|
+
begin
|
210
|
+
begin_transport req
|
211
|
+
res = catch(:response) {
|
212
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
213
|
+
begin
|
214
|
+
res = HTTPResponse.read_new(@socket)
|
215
|
+
res.decode_content = req.decode_content
|
216
|
+
end while res.kind_of?(HTTPContinue)
|
217
|
+
|
218
|
+
res.uri = req.uri
|
219
|
+
|
220
|
+
res
|
221
|
+
}
|
222
|
+
res.reading_body(@socket, req.response_body_permitted?) {
|
223
|
+
yield res if block_given?
|
224
|
+
}
|
225
|
+
rescue Net::OpenTimeout
|
226
|
+
raise
|
227
|
+
rescue Net::ReadTimeout, IOError, EOFError,
|
228
|
+
Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE,
|
229
|
+
# avoid a dependency on OpenSSL
|
230
|
+
defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
|
231
|
+
Timeout::Error => exception
|
232
|
+
if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
|
233
|
+
count += 1
|
234
|
+
@socket.close if @socket and not @socket.closed?
|
235
|
+
D "Conn close because of error #{exception}, and retry"
|
236
|
+
if req.body_stream
|
237
|
+
if req.body_stream.respond_to?(:rewind)
|
238
|
+
req.body_stream.rewind
|
239
|
+
else
|
240
|
+
raise
|
241
|
+
end
|
242
|
+
end
|
243
|
+
retry
|
244
|
+
end
|
245
|
+
D "Conn close because of error #{exception}"
|
246
|
+
@socket.close if @socket and not @socket.closed?
|
247
|
+
raise
|
248
|
+
end
|
249
|
+
|
250
|
+
end_transport req, res
|
251
|
+
res
|
252
|
+
rescue => exception
|
253
|
+
D "Conn close because of error #{exception}"
|
254
|
+
@socket.close if @socket and not @socket.closed?
|
255
|
+
raise exception
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|