hoss-agent 1.0.9
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/.github/ISSUE_TEMPLATE/Bug_report.md +40 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +60 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Dockerfile +43 -0
- data/Gemfile +105 -0
- data/LICENSE +201 -0
- data/hoss-agent.gemspec +42 -0
- data/lib/hoss-agent.rb +210 -0
- data/lib/hoss.rb +21 -0
- data/lib/hoss/agent.rb +235 -0
- data/lib/hoss/central_config.rb +184 -0
- data/lib/hoss/central_config/cache_control.rb +51 -0
- data/lib/hoss/child_durations.rb +64 -0
- data/lib/hoss/config.rb +315 -0
- data/lib/hoss/config/bytes.rb +42 -0
- data/lib/hoss/config/duration.rb +40 -0
- data/lib/hoss/config/options.rb +154 -0
- data/lib/hoss/config/regexp_list.rb +30 -0
- data/lib/hoss/config/wildcard_pattern_list.rb +54 -0
- data/lib/hoss/context.rb +64 -0
- data/lib/hoss/context/request.rb +28 -0
- data/lib/hoss/context/request/socket.rb +36 -0
- data/lib/hoss/context/request/url.rb +59 -0
- data/lib/hoss/context/response.rb +47 -0
- data/lib/hoss/context/user.rb +59 -0
- data/lib/hoss/context_builder.rb +112 -0
- data/lib/hoss/deprecations.rb +39 -0
- data/lib/hoss/error.rb +49 -0
- data/lib/hoss/error/exception.rb +70 -0
- data/lib/hoss/error/log.rb +41 -0
- data/lib/hoss/error_builder.rb +90 -0
- data/lib/hoss/event.rb +131 -0
- data/lib/hoss/instrumenter.rb +107 -0
- data/lib/hoss/internal_error.rb +23 -0
- data/lib/hoss/logging.rb +70 -0
- data/lib/hoss/metadata.rb +36 -0
- data/lib/hoss/metadata/process_info.rb +35 -0
- data/lib/hoss/metadata/service_info.rb +76 -0
- data/lib/hoss/metadata/system_info.rb +47 -0
- data/lib/hoss/metadata/system_info/container_info.rb +136 -0
- data/lib/hoss/naively_hashable.rb +38 -0
- data/lib/hoss/rails.rb +68 -0
- data/lib/hoss/railtie.rb +42 -0
- data/lib/hoss/report.rb +9 -0
- data/lib/hoss/sinatra.rb +53 -0
- data/lib/hoss/spies.rb +104 -0
- data/lib/hoss/spies/faraday.rb +106 -0
- data/lib/hoss/spies/http.rb +86 -0
- data/lib/hoss/spies/net_http.rb +101 -0
- data/lib/hoss/stacktrace.rb +33 -0
- data/lib/hoss/stacktrace/frame.rb +66 -0
- data/lib/hoss/stacktrace_builder.rb +124 -0
- data/lib/hoss/transport/base.rb +191 -0
- data/lib/hoss/transport/connection.rb +55 -0
- data/lib/hoss/transport/connection/http.rb +139 -0
- data/lib/hoss/transport/connection/proxy_pipe.rb +94 -0
- data/lib/hoss/transport/filters.rb +60 -0
- data/lib/hoss/transport/filters/hash_sanitizer.rb +77 -0
- data/lib/hoss/transport/filters/secrets_filter.rb +48 -0
- data/lib/hoss/transport/headers.rb +74 -0
- data/lib/hoss/transport/serializers.rb +113 -0
- data/lib/hoss/transport/serializers/context_serializer.rb +112 -0
- data/lib/hoss/transport/serializers/error_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/event_serializer.rb +73 -0
- data/lib/hoss/transport/serializers/metadata_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/report_serializer.rb +33 -0
- data/lib/hoss/transport/user_agent.rb +48 -0
- data/lib/hoss/transport/worker.rb +330 -0
- data/lib/hoss/util.rb +54 -0
- data/lib/hoss/util/inflector.rb +110 -0
- data/lib/hoss/util/lru_cache.rb +65 -0
- data/lib/hoss/util/throttle.rb +52 -0
- data/lib/hoss/version.rb +22 -0
- metadata +147 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/transport/connection/proxy_pipe'
|
21
|
+
|
22
|
+
module Hoss
|
23
|
+
module Transport
|
24
|
+
class Connection
|
25
|
+
# @api private
|
26
|
+
class Http
|
27
|
+
include Logging
|
28
|
+
|
29
|
+
def initialize(config, headers: nil)
|
30
|
+
@config = config
|
31
|
+
@headers = headers || Headers.new(config)
|
32
|
+
@client = build_client
|
33
|
+
@closed = Concurrent::AtomicBoolean.new(true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def open(url)
|
37
|
+
@closed.make_false
|
38
|
+
@rd, @wr = ProxyPipe.pipe(compress: @config.http_compression?)
|
39
|
+
@request = open_request_in_thread(url)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.open(config, url)
|
43
|
+
new(config).tap do |http|
|
44
|
+
http.open(url)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def post(url, body: nil, headers: nil)
|
49
|
+
request(:post, url, body: body, headers: headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get(url, headers: nil)
|
53
|
+
request(:get, url, headers: headers)
|
54
|
+
end
|
55
|
+
|
56
|
+
def request(method, url, body: nil, headers: nil)
|
57
|
+
@client.send(
|
58
|
+
method,
|
59
|
+
url,
|
60
|
+
body: body,
|
61
|
+
headers: (headers ? @headers.merge(headers) : @headers).to_h,
|
62
|
+
ssl_context: @config.ssl_context
|
63
|
+
).flush
|
64
|
+
end
|
65
|
+
|
66
|
+
def write(str)
|
67
|
+
@wr.write(str)
|
68
|
+
@wr.bytes_sent
|
69
|
+
end
|
70
|
+
|
71
|
+
def close(reason)
|
72
|
+
return if closed?
|
73
|
+
|
74
|
+
debug '%s: Closing request with reason %s', thread_str, reason
|
75
|
+
@closed.make_true
|
76
|
+
|
77
|
+
@wr&.close(reason)
|
78
|
+
return if @request.nil? || @request&.join(5)
|
79
|
+
|
80
|
+
error(
|
81
|
+
'%s: APM Server not responding in time, terminating request',
|
82
|
+
thread_str
|
83
|
+
)
|
84
|
+
@request.kill
|
85
|
+
end
|
86
|
+
|
87
|
+
def closed?
|
88
|
+
@closed.true?
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect
|
92
|
+
format(
|
93
|
+
'%s closed: %s>',
|
94
|
+
super.split.first,
|
95
|
+
closed?
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def thread_str
|
102
|
+
format('[THREAD:%s]', Thread.current.object_id)
|
103
|
+
end
|
104
|
+
|
105
|
+
def open_request_in_thread(url)
|
106
|
+
debug '%s: Opening new request', thread_str
|
107
|
+
Thread.new do
|
108
|
+
begin
|
109
|
+
resp = post(url, body: @rd, headers: @headers.chunked.to_h)
|
110
|
+
|
111
|
+
if resp&.status == 202
|
112
|
+
debug 'APM Server responded with status 202'
|
113
|
+
elsif resp
|
114
|
+
error "APM Server responded with an error:\n%p", resp.body.to_s
|
115
|
+
end
|
116
|
+
rescue Exception => e
|
117
|
+
error(
|
118
|
+
"Couldn't establish connection to APM Server:\n%p", e.inspect
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_client
|
125
|
+
client = HTTP.headers(@headers)
|
126
|
+
return client unless @config.proxy_address && @config.proxy_port
|
127
|
+
|
128
|
+
client.via(
|
129
|
+
@config.proxy_address,
|
130
|
+
@config.proxy_port,
|
131
|
+
@config.proxy_username,
|
132
|
+
@config.proxy_password,
|
133
|
+
@config.proxy_headers
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module Hoss
|
21
|
+
module Transport
|
22
|
+
class Connection
|
23
|
+
# @api private
|
24
|
+
class ProxyPipe
|
25
|
+
def initialize(enc = nil, compress: true)
|
26
|
+
rd, wr = IO.pipe(enc)
|
27
|
+
|
28
|
+
@read = rd
|
29
|
+
@write = Write.new(wr, compress: compress)
|
30
|
+
|
31
|
+
# Http.rb<4 calls rewind on the request bodies, but IO::Pipe raises
|
32
|
+
# ~mikker
|
33
|
+
return if HTTP::VERSION.to_i >= 4
|
34
|
+
def rd.rewind; end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :read, :write
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
class Write
|
41
|
+
include Logging
|
42
|
+
|
43
|
+
def initialize(io, compress: true)
|
44
|
+
@io = io
|
45
|
+
@compress = compress
|
46
|
+
@bytes_sent = Concurrent::AtomicFixnum.new(0)
|
47
|
+
@config = Hoss.agent&.config # this is silly, fix Logging
|
48
|
+
|
49
|
+
return unless compress
|
50
|
+
enable_compression!
|
51
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@io))
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.finalize(io)
|
55
|
+
proc { io.close }
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :io
|
59
|
+
|
60
|
+
def enable_compression!
|
61
|
+
io.binmode
|
62
|
+
@io = Zlib::GzipWriter.new(io)
|
63
|
+
end
|
64
|
+
|
65
|
+
def close(reason = nil)
|
66
|
+
debug("Closing writer with reason #{reason}")
|
67
|
+
io.close
|
68
|
+
end
|
69
|
+
|
70
|
+
def closed?
|
71
|
+
io.closed?
|
72
|
+
end
|
73
|
+
|
74
|
+
def write(str)
|
75
|
+
io.puts(str).tap do
|
76
|
+
@bytes_sent.update do |curr|
|
77
|
+
@compress ? io.tell : curr + str.bytesize
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def bytes_sent
|
83
|
+
@bytes_sent.value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.pipe(**args)
|
88
|
+
pipe = new(**args)
|
89
|
+
[pipe.read, pipe.write]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/transport/filters/secrets_filter'
|
21
|
+
|
22
|
+
module Hoss
|
23
|
+
module Transport
|
24
|
+
# @api private
|
25
|
+
module Filters
|
26
|
+
SKIP = :skip
|
27
|
+
|
28
|
+
def self.new(config)
|
29
|
+
Container.new(config)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
class Container
|
34
|
+
def initialize(config)
|
35
|
+
@filters = { secrets: SecretsFilter.new(config) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def add(key, filter)
|
39
|
+
@filters = @filters.merge(key => filter)
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove(key)
|
43
|
+
@filters.delete(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply!(payload)
|
47
|
+
@filters.reduce(payload) do |result, (_key, filter)|
|
48
|
+
result = filter.call(result)
|
49
|
+
break SKIP if result.nil?
|
50
|
+
result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def length
|
55
|
+
@filters.length
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module Hoss
|
21
|
+
module Transport
|
22
|
+
module Filters
|
23
|
+
class HashSanitizer
|
24
|
+
FILTERED = '[FILTERED]'
|
25
|
+
|
26
|
+
KEY_FILTERS = [
|
27
|
+
/passw(or)?d/i,
|
28
|
+
/auth/i,
|
29
|
+
/^pw$/,
|
30
|
+
/secret/i,
|
31
|
+
/token/i,
|
32
|
+
/api[-._]?key/i,
|
33
|
+
/session[-._]?id/i,
|
34
|
+
/(set[-_])?cookie/i
|
35
|
+
].freeze
|
36
|
+
|
37
|
+
VALUE_FILTERS = [
|
38
|
+
# (probably) credit card number
|
39
|
+
/^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
|
40
|
+
].freeze
|
41
|
+
|
42
|
+
attr_accessor :key_filters
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
@key_filters = KEY_FILTERS
|
46
|
+
end
|
47
|
+
|
48
|
+
def strip_from!(obj, key_filters = KEY_FILTERS)
|
49
|
+
return unless obj&.is_a?(Hash)
|
50
|
+
|
51
|
+
obj.each do |k, v|
|
52
|
+
if filter_key?(k)
|
53
|
+
next obj[k] = FILTERED
|
54
|
+
end
|
55
|
+
|
56
|
+
case v
|
57
|
+
when Hash
|
58
|
+
strip_from!(v)
|
59
|
+
when String
|
60
|
+
if filter_value?(v)
|
61
|
+
obj[k] = FILTERED
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def filter_key?(key)
|
68
|
+
@key_filters.any? { |regex| regex.match(key) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def filter_value?(value)
|
72
|
+
VALUE_FILTERS.any? { |regex| regex.match(value) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/transport/filters/hash_sanitizer'
|
21
|
+
|
22
|
+
module Hoss
|
23
|
+
module Transport
|
24
|
+
module Filters
|
25
|
+
# @api private
|
26
|
+
class SecretsFilter
|
27
|
+
def initialize(config)
|
28
|
+
@config = config
|
29
|
+
@sanitizer = HashSanitizer.new
|
30
|
+
@sanitizer.key_filters += config.custom_key_filters +
|
31
|
+
config.sanitize_field_names
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(payload)
|
35
|
+
@sanitizer.strip_from! payload.dig(:transaction, :context, :request, :headers)
|
36
|
+
@sanitizer.strip_from! payload.dig(:transaction, :context, :request, :env)
|
37
|
+
@sanitizer.strip_from! payload.dig(:transaction, :context, :request, :cookies)
|
38
|
+
@sanitizer.strip_from! payload.dig(:transaction, :context, :response, :headers)
|
39
|
+
@sanitizer.strip_from! payload.dig(:error, :context, :request, :headers)
|
40
|
+
@sanitizer.strip_from! payload.dig(:error, :context, :response, :headers)
|
41
|
+
@sanitizer.strip_from! payload.dig(:transaction, :context, :request, :body)
|
42
|
+
|
43
|
+
payload
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module Hoss
|
21
|
+
module Transport
|
22
|
+
# @api private
|
23
|
+
class Headers
|
24
|
+
HEADERS = {
|
25
|
+
'Content-Type' => 'application/x-ndjson',
|
26
|
+
'Transfer-Encoding' => 'chunked'
|
27
|
+
}.freeze
|
28
|
+
GZIP_HEADERS = HEADERS.merge(
|
29
|
+
'Content-Encoding' => 'gzip'
|
30
|
+
).freeze
|
31
|
+
|
32
|
+
def initialize(config, initial: {})
|
33
|
+
@config = config
|
34
|
+
@hash = build!(initial)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_accessor :hash
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
@hash[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
def []=(key, value)
|
44
|
+
@hash[key] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def merge(other)
|
48
|
+
self.class.new(@config, initial: @hash.merge(other))
|
49
|
+
end
|
50
|
+
|
51
|
+
def merge!(other)
|
52
|
+
@hash.merge!(other)
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_h
|
57
|
+
@hash
|
58
|
+
end
|
59
|
+
|
60
|
+
def chunked
|
61
|
+
merge(
|
62
|
+
@config.http_compression? ? GZIP_HEADERS : HEADERS
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def build!(headers)
|
69
|
+
headers[:'User-Agent'] = UserAgent.new(@config).to_s
|
70
|
+
headers
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|