123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import 'dart:convert';
- import 'package:http/http.dart';
- import 'package:sentry/sentry.dart';
- /// A transport is in charge of sending the event to the Sentry server.
- class TunneledTransport implements Transport {
- final Uri _tunnel;
- final SentryOptions _options;
- final Dsn? _dsn;
- _CredentialBuilder? _credentialBuilder;
- final Map<String, String> _headers;
- factory TunneledTransport(Uri tunnel, SentryOptions options) {
- return TunneledTransport._(tunnel, options);
- }
- TunneledTransport._(this._tunnel, this._options)
- : _dsn = _options.dsn != null ? Dsn.parse(_options.dsn!) : null,
- _headers = _buildHeaders(
- _options.platformChecker.isWeb,
- _options.sdk.identifier,
- ) {
- _credentialBuilder = _CredentialBuilder(
- _dsn,
- _options.sdk.identifier,
- _options.clock,
- );
- }
- @override
- Future<SentryId?> send(SentryEnvelope envelope) async {
- final streamedRequest = await _createStreamedRequest(envelope);
- final response = await _options.httpClient
- .send(streamedRequest)
- .then(Response.fromStream);
- if (response.statusCode != 200) {
- // body guard to not log the error as it has performance impact to allocate
- // the body String.
- if (_options.debug) {
- _options.logger(
- SentryLevel.error,
- 'API returned an error, statusCode = ${response.statusCode}, '
- 'body = ${response.body}',
- );
- }
- return const SentryId.empty();
- } else {
- _options.logger(
- SentryLevel.debug,
- 'Envelope ${envelope.header.eventId ?? "--"} was sent successfully.',
- );
- }
- final eventId = json.decode(response.body)['id'];
- if (eventId == null) {
- return null;
- }
- return SentryId.fromId(eventId);
- }
- Future<StreamedRequest> _createStreamedRequest(
- SentryEnvelope envelope,
- ) async {
- final streamedRequest = StreamedRequest('POST', _tunnel);
- envelope
- .envelopeStream(_options)
- .listen(streamedRequest.sink.add)
- .onDone(streamedRequest.sink.close);
- streamedRequest.headers.addAll(_credentialBuilder!.configure(_headers));
- return streamedRequest;
- }
- }
- class _CredentialBuilder {
- final String _authHeader;
- final ClockProvider _clock;
- int get timestamp => _clock().millisecondsSinceEpoch;
- _CredentialBuilder._(String authHeader, ClockProvider clock)
- : _authHeader = authHeader,
- _clock = clock;
- factory _CredentialBuilder(
- Dsn? dsn,
- String sdkIdentifier,
- ClockProvider clock,
- ) {
- final authHeader = _buildAuthHeader(
- publicKey: dsn?.publicKey,
- secretKey: dsn?.secretKey,
- sdkIdentifier: sdkIdentifier,
- );
- return _CredentialBuilder._(authHeader, clock);
- }
- static String _buildAuthHeader({
- String? publicKey,
- String? secretKey,
- String? sdkIdentifier,
- }) {
- var header = 'Sentry sentry_version=7, sentry_client=$sdkIdentifier, '
- 'sentry_key=$publicKey';
- if (secretKey != null) {
- header += ', sentry_secret=$secretKey';
- }
- return header;
- }
- Map<String, String> configure(Map<String, String> headers) {
- return headers
- ..addAll(
- <String, String>{
- 'X-Sentry-Auth': '$_authHeader, sentry_timestamp=$timestamp'
- },
- );
- }
- }
- Map<String, String> _buildHeaders(bool isWeb, String sdkIdentifier) {
- final headers = {'Content-Type': 'application/x-sentry-envelope'};
- // NOTE(lejard_h) overriding user agent on VM and Flutter not sure why
- // for web it use browser user agent
- if (!isWeb) {
- headers['User-Agent'] = sdkIdentifier;
- }
- return headers;
- }
|