-
-
Notifications
You must be signed in to change notification settings - Fork 485
/
http_transport.rb
150 lines (122 loc) · 4.2 KB
/
http_transport.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true
require 'faraday'
require 'zlib'
module Sentry
class HTTPTransport < Transport
GZIP_ENCODING = "gzip"
GZIP_THRESHOLD = 1024 * 30
CONTENT_TYPE = 'application/x-sentry-envelope'
DEFAULT_DELAY = 60
RETRY_AFTER_HEADER = "retry-after"
RATE_LIMIT_HEADER = "x-sentry-rate-limits"
attr_reader :conn, :adapter
def initialize(*args)
super
@adapter = @transport_configuration.http_adapter || Faraday.default_adapter
@conn = set_conn
@endpoint = @dsn.envelope_endpoint
end
def send_data(data)
encoding = ""
if should_compress?(data)
data = Zlib.gzip(data)
encoding = GZIP_ENCODING
end
response = conn.post @endpoint do |req|
req.headers['Content-Type'] = CONTENT_TYPE
req.headers['Content-Encoding'] = encoding
req.headers['X-Sentry-Auth'] = generate_auth_header
req.body = data
end
if has_rate_limited_header?(response.headers)
handle_rate_limited_response(response.headers)
end
rescue Faraday::Error => e
error_info = e.message
if e.response
if e.response[:status] == 429
handle_rate_limited_response(e.response[:headers])
else
error_info += "\nbody: #{e.response[:body]}"
error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
end
end
raise Sentry::ExternalError, error_info
end
private
def has_rate_limited_header?(headers)
headers[RETRY_AFTER_HEADER] || headers[RATE_LIMIT_HEADER]
end
def handle_rate_limited_response(headers)
rate_limits =
if rate_limits = headers[RATE_LIMIT_HEADER]
parse_rate_limit_header(rate_limits)
elsif retry_after = headers[RETRY_AFTER_HEADER]
# although Sentry doesn't send a date string back
# based on HTTP specification, this could be a date string (instead of an integer)
retry_after = retry_after.to_i
retry_after = DEFAULT_DELAY if retry_after == 0
{ nil => Time.now + retry_after }
else
{ nil => Time.now + DEFAULT_DELAY }
end
rate_limits.each do |category, limit|
if current_limit = @rate_limits[category]
if current_limit < limit
@rate_limits[category] = limit
end
else
@rate_limits[category] = limit
end
end
end
def parse_rate_limit_header(rate_limit_header)
time = Time.now
result = {}
limits = rate_limit_header.split(",")
limits.each do |limit|
next if limit.nil? || limit.empty?
begin
retry_after, categories = limit.strip.split(":").first(2)
retry_after = time + retry_after.to_i
categories = categories.split(";")
if categories.empty?
result[nil] = retry_after
else
categories.each do |category|
result[category] = retry_after
end
end
rescue StandardError
end
end
result
end
def should_compress?(data)
@transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
end
def set_conn
server = @dsn.server
log_debug("Sentry HTTP Transport connecting to #{server}")
Faraday.new(server, :ssl => ssl_configuration, :proxy => @transport_configuration.proxy) do |builder|
@transport_configuration.faraday_builder&.call(builder)
builder.response :raise_error
builder.options.merge! faraday_opts
builder.headers[:user_agent] = "sentry-ruby/#{Sentry::VERSION}"
builder.adapter(*adapter)
end
end
# TODO: deprecate and replace where possible w/Faraday Builder
def faraday_opts
[:timeout, :open_timeout].each_with_object({}) do |opt, memo|
memo[opt] = @transport_configuration.public_send(opt) if @transport_configuration.public_send(opt)
end
end
def ssl_configuration
{
verify: @transport_configuration.ssl_verification,
ca_file: @transport_configuration.ssl_ca_file
}.merge(@transport_configuration.ssl || {})
end
end
end