Skip to content

Commit 50d29c5

Browse files
Merge pull request #175 from bugsnag/PLAT-13887/httpclient
Replace default delivery with HttpClient implementation
2 parents f50f6ed + 744b37c commit 50d29c5

File tree

20 files changed

+290
-443
lines changed

20 files changed

+290
-443
lines changed

src/Bugsnag/Bugsnag.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
<Reference Include="System.Runtime.Serialization" />
1212
<Reference Include="System.Xml" />
1313
<Reference Include="System.Xml.Linq" />
14+
<Reference Include="System.Net.Http" />
1415
</ItemGroup>
1516
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
1617
<PackageReference Include="System.Collections.Specialized" Version="4.3.0" />
1718
<PackageReference Include="System.Diagnostics.TraceSource" Version="4.3.0" />
1819
<PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" />
19-
<PackageReference Include="System.Net.Requests" Version="4.3.0" />
2020
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
2121
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
2222
</ItemGroup>

src/Bugsnag/Client.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ public Client(string apiKey) : this(new Configuration(apiKey))
4747
/// Constructs a client with the default storage and delivery classes.
4848
/// </summary>
4949
/// <param name="configuration"></param>
50-
public Client(IConfiguration configuration) : this(configuration, ThreadQueueDelivery.Instance, new Breadcrumbs(configuration), new SessionTracker(configuration))
50+
public Client(IConfiguration configuration) : this(configuration, DefaultDelivery.Instance, new Breadcrumbs(configuration), new SessionTracker(configuration))
5151
{
52-
52+
DefaultDelivery.Instance.Configure(configuration);
5353
}
5454

5555
/// <summary>

src/Bugsnag/DefaultDelivery.cs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using Bugsnag.Payload;
2+
using System;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Net;
7+
using System.Net.Http;
8+
using System.Net.Http.Headers;
9+
using System.Text;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
13+
namespace Bugsnag
14+
{
15+
public class DefaultDelivery : IDelivery
16+
{
17+
private static DefaultDelivery instance = null;
18+
private static readonly object instanceLock = new object();
19+
20+
private HttpClient _httpClient;
21+
private readonly CancellationTokenSource _cancellationTokenSource;
22+
private readonly Task _processingTask;
23+
private readonly BlockingCollection<IPayload> _queue;
24+
25+
private DefaultDelivery()
26+
{
27+
_httpClient = new HttpClient();
28+
_queue = new BlockingCollection<IPayload>(new ConcurrentQueue<IPayload>());
29+
_cancellationTokenSource = new CancellationTokenSource();
30+
_processingTask = Task.Run(() => ProcessQueueAsync(_cancellationTokenSource.Token));
31+
}
32+
33+
public static DefaultDelivery Instance
34+
{
35+
get
36+
{
37+
lock (instanceLock)
38+
{
39+
if (instance == null)
40+
{
41+
instance = new DefaultDelivery();
42+
}
43+
44+
return instance;
45+
}
46+
}
47+
}
48+
49+
public void Configure(IConfiguration configuration)
50+
{
51+
if (configuration.Proxy != null)
52+
{
53+
_httpClient.Dispose();
54+
_httpClient = new HttpClient(new HttpClientHandler { Proxy = configuration.Proxy });
55+
}
56+
}
57+
58+
public void Send(IPayload payload)
59+
{
60+
_queue.Add(payload);
61+
}
62+
63+
internal void Stop()
64+
{
65+
Task.WaitAll(new[] { _processingTask }, TimeSpan.FromSeconds(5));
66+
_cancellationTokenSource.Cancel();
67+
_httpClient.Dispose();
68+
_cancellationTokenSource.Dispose();
69+
_queue.Dispose();
70+
}
71+
72+
private async Task ProcessQueueAsync(CancellationToken cancellationToken)
73+
{
74+
while (!cancellationToken.IsCancellationRequested)
75+
{
76+
IPayload payload = null;
77+
78+
try
79+
{
80+
// Take will block until an item is available or cancellation is requested
81+
payload = _queue.Take(cancellationToken);
82+
}
83+
catch (OperationCanceledException)
84+
{
85+
// Exit gracefully when cancellation is requested
86+
break;
87+
}
88+
89+
if (payload != null)
90+
{
91+
await SendPayloadAsync(payload);
92+
}
93+
}
94+
}
95+
96+
private async Task SendPayloadAsync(IPayload payload)
97+
{
98+
try
99+
{
100+
byte[] serializedPayload = payload.Serialize();
101+
if (serializedPayload == null)
102+
{
103+
return;
104+
}
105+
106+
using (var request = new HttpRequestMessage(HttpMethod.Post, payload.Endpoint))
107+
{
108+
request.Content = new ByteArrayContent(serializedPayload);
109+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
110+
111+
// Add headers from payload
112+
if (payload.Headers != null)
113+
{
114+
foreach (var header in payload.Headers)
115+
{
116+
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
117+
}
118+
}
119+
120+
// Add the Bugsnag-Sent-At header
121+
request.Headers.Add("Bugsnag-Sent-At", DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture));
122+
123+
// Send the request and log any failures
124+
var response = await _httpClient.SendAsync(request);
125+
126+
if (!response.IsSuccessStatusCode)
127+
{
128+
Trace.WriteLine($"Failed to send payload to Bugsnag - received status code {response.StatusCode}");
129+
}
130+
}
131+
}
132+
catch (System.Exception ex)
133+
{
134+
Trace.WriteLine($"Error sending payload to Bugsnag: {ex}");
135+
}
136+
}
137+
}
138+
}

src/Bugsnag/IDelivery.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ public interface IPayload
1717
{
1818
Uri Endpoint { get; }
1919

20-
IWebProxy Proxy { get; }
21-
2220
KeyValuePair<string, string>[] Headers { get; }
2321

2422
byte[] Serialize();

src/Bugsnag/Payload/BatchedSessions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ public BatchedSessions(IConfiguration configuration, NotifierInfo notifier, App
1919
{
2020
_configuration = configuration;
2121
Endpoint = configuration.SessionEndpoint;
22-
Proxy = configuration.Proxy;
22+
2323
_headers = new KeyValuePair<string, string>[] {
2424
new KeyValuePair<string, string>(Payload.Headers.ApiKeyHeader, configuration.ApiKey),
2525
new KeyValuePair<string, string>(Payload.Headers.PayloadVersionHeader, "1.0")
2626
};
27+
2728
this.AddToPayload("notifier", notifier);
2829
this.AddToPayload("device", device);
2930
this.AddToPayload("app", app);
@@ -32,8 +33,6 @@ public BatchedSessions(IConfiguration configuration, NotifierInfo notifier, App
3233

3334
public Uri Endpoint { get; set; }
3435

35-
public IWebProxy Proxy { get; set; }
36-
3736
public KeyValuePair<string, string>[] Headers => _headers;
3837

3938
public byte[] Serialize()

src/Bugsnag/Payload/Report.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public Report(IConfiguration configuration, System.Exception exception, HandledS
3939
{
4040
_ignored = false;
4141
Endpoint = configuration.Endpoint;
42-
Proxy = configuration.Proxy;
42+
4343
_headers = new KeyValuePair<string, string>[] {
4444
new KeyValuePair<string, string>(Payload.Headers.ApiKeyHeader, configuration.ApiKey),
4545
new KeyValuePair<string, string>(Payload.Headers.PayloadVersionHeader, _payloadVersion),
@@ -87,8 +87,6 @@ public void Ignore()
8787
/// </summary>
8888
public Uri Endpoint { get; set; }
8989

90-
public IWebProxy Proxy { get; set; }
91-
9290
public KeyValuePair<string, string>[] Headers { get { return _headers; } }
9391

9492
public byte[] Serialize()

src/Bugsnag/SessionsStore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private void SendSessions(object state)
3939
foreach (var item in sessionData)
4040
{
4141
var payload = new BatchedSessions(item.Key, item.Value);
42-
ThreadQueueDelivery.Instance.Send(payload);
42+
DefaultDelivery.Instance.Send(payload);
4343
}
4444
}
4545

src/Bugsnag/ThreadQueueDelivery.cs

Lines changed: 0 additions & 173 deletions
This file was deleted.

0 commit comments

Comments
 (0)