baidubce-sdk 0.9.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.
- 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
|
+
|