Skip to content

Commit

Permalink
Add proxy support (#2192)
Browse files Browse the repository at this point in the history
  • Loading branch information
denrase authored Aug 6, 2024
1 parent 6e9c5a2 commit 33527b4
Show file tree
Hide file tree
Showing 16 changed files with 699 additions and 9 deletions.
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@
appRunner: () => runApp(MyApp()),
);
```
- Add proxy support ([#2192](https://github.com/getsentry/sentry-dart/pull/2192))
- Configure a `SentryProxy` object and set it on `SentryFlutter.init`
```dart
import 'package:flutter/widgets.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = 'https://example@sentry.io/add-your-dsn-here';
options.proxy = SentryProxy(
type: SenryProxyType.http,
host: 'localhost',
port: 8080,
);
},
// Init your App.
appRunner: () => runApp(MyApp()),
);
}
```

### Improvements

Expand Down Expand Up @@ -72,7 +93,7 @@ SentryFlutter.init((options) =>
- This allows viewing the correct dart formatted raw stacktrace in the Sentry UI
- Support `ignoredExceptionsForType` ([#2150](https://github.com/getsentry/sentry-dart/pull/2150))
- Filter out exception types by calling `SentryOptions.addExceptionFilterForType(Type exceptionType)`

### Fixes

- Disable sff & frame delay detection on web, linux and windows ([#2182](https://github.com/getsentry/sentry-dart/pull/2182))
Expand Down
2 changes: 2 additions & 0 deletions dart/lib/sentry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ export 'src/sentry_span_operations.dart';
export 'src/utils.dart';
// spotlight debugging
export 'src/spotlight.dart';
// proxy
export 'src/protocol/sentry_proxy.dart';
16 changes: 16 additions & 0 deletions dart/lib/src/http_client/client_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:meta/meta.dart';
import 'package:http/http.dart';

import '../sentry_options.dart';

@internal
ClientProvider getClientProvider() {
return ClientProvider();
}

@internal
class ClientProvider {
Client getClient(SentryOptions options) {
return Client();
}
}
67 changes: 67 additions & 0 deletions dart/lib/src/http_client/io_client_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'dart:io';

import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:meta/meta.dart';

import '../protocol.dart';
import '../protocol/sentry_proxy.dart';
import '../sentry_options.dart';
import 'client_provider.dart';

@internal
ClientProvider getClientProvider() {
return IoClientProvider(
() {
return HttpClient();
},
(user, pass) {
return HttpClientBasicCredentials(user, pass);
},
);
}

@internal
class IoClientProvider implements ClientProvider {
final HttpClient Function() _httpClient;
final HttpClientCredentials Function(String, String) _httpClientCredentials;

IoClientProvider(this._httpClient, this._httpClientCredentials);

@override
Client getClient(SentryOptions options) {
final proxy = options.proxy;
if (proxy == null) {
return Client();
}
final pac = proxy.toPacString();
if (proxy.type == SentryProxyType.socks) {
options.logger(
SentryLevel.warning,
"Setting proxy '$pac' is not supported.",
);
return Client();
}
options.logger(
SentryLevel.info,
"Setting proxy '$pac'",
);
final httpClient = _httpClient();
httpClient.findProxy = (url) => pac;

final host = proxy.host;
final port = proxy.port;
final user = proxy.user;
final pass = proxy.pass;

if (host != null && port != null && user != null && pass != null) {
httpClient.addProxyCredentials(
host,
port,
'',
_httpClientCredentials(user, pass),
);
}
return IOClient(httpClient);
}
}
62 changes: 62 additions & 0 deletions dart/lib/src/protocol/sentry_proxy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
class SentryProxy {
final SentryProxyType type;
final String? host;
final int? port;
final String? user;
final String? pass;

SentryProxy({required this.type, this.host, this.port, this.user, this.pass});

String toPacString() {
String type = 'DIRECT';
switch (this.type) {
case SentryProxyType.direct:
return 'DIRECT';
case SentryProxyType.http:
type = 'PROXY';
break;
case SentryProxyType.socks:
type = 'SOCKS';
break;
}
if (host != null && port != null) {
return '$type $host:$port';
} else if (host != null) {
return '$type $host';
} else {
return 'DIRECT';
}
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
return {
if (host != null) 'host': host,
if (port != null) 'port': port,
'type': type.toString().split('.').last.toUpperCase(),
if (user != null) 'user': user,
if (pass != null) 'pass': pass,
};
}

SentryProxy copyWith({
String? host,
int? port,
SentryProxyType? type,
String? user,
String? pass,
}) =>
SentryProxy(
host: host ?? this.host,
port: port ?? this.port,
type: type ?? this.type,
user: user ?? this.user,
pass: pass ?? this.pass,
);
}

enum SentryProxyType {
direct,
http,
socks;
}
13 changes: 13 additions & 0 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,19 @@ class SentryOptions {
/// ```
Spotlight spotlight = Spotlight(enabled: false);

/// Configure a proxy to use for SDK API calls.
///
/// On io platforms without native SDKs (dart, linux, windows), this will use
/// an 'IOClient' with inner 'HTTPClient' for http communication.
/// A http proxy will be set in returned for 'HttpClient.findProxy' in the
/// form 'PROXY <your_host>:<your_port>'.
/// When setting 'user' and 'pass', the 'HttpClient.addProxyCredentials'
/// method will be called with empty 'realm'.
///
/// On Android & iOS, the proxy settings are handled by the native SDK.
/// iOS only supports http proxies, while macOS also supports socks.
SentryProxy? proxy;

SentryOptions({this.dsn, PlatformChecker? checker}) {
if (checker != null) {
platformChecker = checker;
Expand Down
5 changes: 3 additions & 2 deletions dart/lib/src/transport/http_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import '../sentry_options.dart';
import '../sentry_envelope.dart';
import 'transport.dart';
import 'rate_limiter.dart';
import '../http_client/client_provider.dart'
if (dart.library.io) '../http_client/io_client_provider.dart';

/// A transport is in charge of sending the event to the Sentry server.
class HttpTransport implements Transport {
Expand All @@ -22,9 +24,8 @@ class HttpTransport implements Transport {

factory HttpTransport(SentryOptions options, RateLimiter rateLimiter) {
if (options.httpClient is NoOpClient) {
options.httpClient = Client();
options.httpClient = getClientProvider().getClient(options);
}

return HttpTransport._(options, rateLimiter);
}

Expand Down
4 changes: 3 additions & 1 deletion dart/lib/src/transport/spotlight_http_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'http_transport_request_handler.dart';

import '../../sentry.dart';
import '../noop_client.dart';
import '../http_client/client_provider.dart'
if (dart.library.io) '../http_client/io_client_provider.dart';

/// Spotlight HTTP transport decorator that sends Sentry envelopes to both Sentry and Spotlight.
class SpotlightHttpTransport extends Transport {
Expand All @@ -13,7 +15,7 @@ class SpotlightHttpTransport extends Transport {

factory SpotlightHttpTransport(SentryOptions options, Transport transport) {
if (options.httpClient is NoOpClient) {
options.httpClient = Client();
options.httpClient = getClientProvider().getClient(options);
}
return SpotlightHttpTransport._(options, transport);
}
Expand Down
Loading

0 comments on commit 33527b4

Please sign in to comment.