Class: Sentry::HTTPTransport

Inherits:
Transport show all
Defined in:
lib/sentry/transport/http_transport.rb

Direct Known Subclasses

SpotlightTransport

Constant Summary collapse

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"
USER_AGENT =
"sentry-ruby/#{Sentry::VERSION}"
HTTP_ERRORS =
[
  Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
  Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
  Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
].freeze

Constants inherited from Transport

Transport::CLIENT_REPORT_INTERVAL, Transport::CLIENT_REPORT_REASONS, Transport::PROTOCOL_VERSION

Instance Attribute Summary

Attributes inherited from Transport

#discarded_events, #last_client_report_sent, #logger, #rate_limits

Instance Method Summary collapse

Methods inherited from Transport

#any_rate_limited?, #envelope_from_event, #flush, #is_rate_limited?, #record_lost_event, #send_envelope, #send_event, #serialize_envelope

Constructor Details

#initialize(*args) ⇒ HTTPTransport

Returns a new instance of HTTPTransport.

[View source]

26
27
28
29
# File 'lib/sentry/transport/http_transport.rb', line 26

def initialize(*args)
  super
  log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn
end

Instance Method Details

#connObject

[View source]

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/sentry/transport/http_transport.rb', line 89

def conn
  server = URI(@dsn.server)

  # connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
  # Net::HTTP will automatically read the env vars.
  # See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
  connection =
    if proxy = normalize_proxy(@transport_configuration.proxy)
      ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
    else
      ::Net::HTTP.new(server.hostname, server.port)
    end

  connection.use_ssl = server.scheme == "https"
  connection.read_timeout = @transport_configuration.timeout
  connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
  connection.open_timeout = @transport_configuration.open_timeout

  ssl_configuration.each do |key, value|
    connection.send("#{key}=", value)
  end

  connection
end

#endpointObject

[View source]

71
72
73
# File 'lib/sentry/transport/http_transport.rb', line 71

def endpoint
  @dsn.envelope_endpoint
end

#generate_auth_headerObject

[View source]

75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/sentry/transport/http_transport.rb', line 75

def generate_auth_header
  return nil unless @dsn

  now = Sentry.utc_now.to_i
  fields = {
    "sentry_version" => PROTOCOL_VERSION,
    "sentry_client" => USER_AGENT,
    "sentry_timestamp" => now,
    "sentry_key" => @dsn.public_key
  }
  fields["sentry_secret"] = @dsn.secret_key if @dsn.secret_key
  "Sentry " + fields.map { |key, value| "#{key}=#{value}" }.join(", ")
end

#send_data(data) ⇒ Object

[View source]

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
# File 'lib/sentry/transport/http_transport.rb', line 31

def send_data(data)
  encoding = ""

  if should_compress?(data)
    data = Zlib.gzip(data)
    encoding = GZIP_ENCODING
  end

  headers = {
    "Content-Type" => CONTENT_TYPE,
    "Content-Encoding" => encoding,
    "User-Agent" => USER_AGENT
  }

  auth_header = generate_auth_header
  headers["X-Sentry-Auth"] = auth_header if auth_header

  response = conn.start do |http|
    request = ::Net::HTTP::Post.new(endpoint, headers)
    request.body = data
    http.request(request)
  end

  if response.code.match?(/\A2\d{2}/)
    handle_rate_limited_response(response) if has_rate_limited_header?(response)
  elsif response.code == "429"
    log_debug("the server responded with status 429")
    handle_rate_limited_response(response)
  else
    error_info = "the server responded with status #{response.code}"
    error_info += "\nbody: #{response.body}"
    error_info += " Error in headers is: #{response['x-sentry-error']}" if response["x-sentry-error"]

    raise Sentry::ExternalError, error_info
  end
rescue SocketError, *HTTP_ERRORS => e
  on_error if respond_to?(:on_error)
  raise Sentry::ExternalError.new(e&.message)
end