-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Use HttpWebRequest instead of HttpClient on .NET Framework (#1853)
- Loading branch information
1 parent
7da3e59
commit 8d6cf0f
Showing
37 changed files
with
1,792 additions
and
481 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/Agent/NewRelic/Agent/Core/DataTransport/Client/Constants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// Constants shared between implementations of IHttpClient | ||
/// </summary> | ||
public static class Constants | ||
{ | ||
public const string EmptyResponseBody = "{}"; | ||
public const int CompressMinimumByteLength = 20; | ||
public const int ProtocolVersion = 17; | ||
public const string LicenseKeyParameterName = "license_key"; | ||
|
||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
src/Agent/NewRelic/Agent/Core/DataTransport/Client/DataTransportAuditLogger.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using NewRelic.Agent.Core.Logging; | ||
using NewRelic.Core; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// Simple wrapper for audit logging, shared by implementations of IHttpClient | ||
/// </summary> | ||
public static class DataTransportAuditLogger | ||
{ | ||
/// <summary> | ||
/// This represents the direction or flow of data. Used for audit logs. | ||
/// </summary> | ||
public enum AuditLogDirection | ||
{ | ||
Sent = 1, | ||
Received = 2 | ||
} | ||
|
||
/// <summary> | ||
/// This represents the origin or source of data. Used for audit logs. | ||
/// </summary> | ||
public enum AuditLogSource | ||
{ | ||
Collector = 1, | ||
Beacon = 2, | ||
InstrumentedApp = 3 | ||
} | ||
|
||
public const string AuditLogFormat = "Data {0} from the {1} : {2}"; | ||
private const string LicenseKeyParameterName = "license_key"; | ||
|
||
public static void Log(AuditLogDirection direction, AuditLogSource source, string uri) | ||
{ | ||
if (AuditLog.IsAuditLogEnabled) | ||
{ | ||
var message = string.Format(AuditLogFormat, direction, source, | ||
Strings.ObfuscateLicenseKeyInAuditLog(uri, LicenseKeyParameterName)); | ||
AuditLog.Log(message); | ||
} | ||
} | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/Agent/NewRelic/Agent/Core/DataTransport/Client/HttpClientBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using System; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using NewRelic.Agent.Core.DataTransport.Client.Interfaces; | ||
using NewRelic.Core.Logging; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// Abstract base shared by implementations of IHttpClient | ||
/// </summary> | ||
public abstract class HttpClientBase : IHttpClient | ||
{ | ||
protected bool _diagnoseConnectionError = true; | ||
protected static IWebProxy _proxy = null; | ||
|
||
protected HttpClientBase(IWebProxy proxy) | ||
{ | ||
_proxy = proxy; | ||
} | ||
|
||
public abstract Task<IHttpResponse> SendAsync(IHttpRequest request); | ||
|
||
public virtual void Dispose() | ||
{ | ||
#if !NETFRAMEWORK | ||
if (_lazyHttpClient.IsValueCreated) | ||
_lazyHttpClient.Value.Dispose(); | ||
#endif | ||
} | ||
|
||
|
||
protected void DiagnoseConnectionError(string host) | ||
{ | ||
_diagnoseConnectionError = false; | ||
try | ||
{ | ||
if (!IPAddress.TryParse(host, out _)) | ||
{ | ||
Dns.GetHostEntry(host); | ||
} | ||
} | ||
catch (Exception) | ||
{ | ||
Log.ErrorFormat("Unable to resolve host name \"{0}\"", host); | ||
} | ||
|
||
TestConnection(); | ||
} | ||
|
||
protected void TestConnection() | ||
{ | ||
const string testAddress = "http://www.google.com"; | ||
try | ||
{ | ||
#if NETFRAMEWORK | ||
using (var wc = new WebClient()) | ||
{ | ||
wc.Proxy = _proxy; | ||
|
||
wc.DownloadString(testAddress); | ||
} | ||
#else | ||
_lazyHttpClient.Value.GetAsync(testAddress).GetAwaiter().GetResult(); | ||
#endif | ||
Log.InfoFormat("Connection test to \"{0}\" succeeded", testAddress); | ||
} | ||
catch (Exception) | ||
{ | ||
var message = $"Connection test to \"{testAddress}\" failed."; | ||
if (_proxy != null) | ||
{ | ||
message += $" Check your proxy settings ({_proxy.GetProxy(new Uri(testAddress))})"; | ||
} | ||
|
||
Log.Error(message); | ||
} | ||
} | ||
|
||
#if !NETFRAMEWORK | ||
// use a single HttpClient for all TestConnection() invocations | ||
private readonly Lazy<HttpClient> _lazyHttpClient = new Lazy<HttpClient>(() => new HttpClient(new HttpClientHandler() { Proxy = _proxy })); | ||
#endif | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/Agent/NewRelic/Agent/Core/DataTransport/Client/HttpClientWrapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#if !NETFRAMEWORK | ||
using System; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using NewRelic.Agent.Configuration; | ||
using NewRelic.Agent.Core.Config; | ||
using NewRelic.Agent.Core.DataTransport.Client.Interfaces; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
public class HttpClientWrapper : IHttpClientWrapper | ||
{ | ||
private readonly HttpClient _httpClient; | ||
private readonly int _timeoutMilliseconds; | ||
|
||
public HttpClientWrapper(HttpClient client, int timeoutMilliseconds) | ||
{ | ||
_httpClient = client; | ||
_timeoutMilliseconds = timeoutMilliseconds; | ||
} | ||
|
||
|
||
public void Dispose() | ||
{ | ||
_httpClient.Dispose(); | ||
} | ||
|
||
public async Task<IHttpResponseMessageWrapper> SendAsync(HttpRequestMessage message) | ||
{ | ||
var cts = new CancellationTokenSource(_timeoutMilliseconds); | ||
return new HttpResponseMessageWrapper(await _httpClient.SendAsync(message, cts.Token)); | ||
} | ||
|
||
public TimeSpan Timeout | ||
{ | ||
get | ||
{ | ||
return _httpClient.Timeout; | ||
} | ||
set | ||
{ | ||
_httpClient.Timeout = value; | ||
} | ||
} | ||
} | ||
} | ||
#endif |
27 changes: 27 additions & 0 deletions
27
src/Agent/NewRelic/Agent/Core/DataTransport/Client/HttpContentHeadersWrapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#if !NETFRAMEWORK | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http.Headers; | ||
using NewRelic.Agent.Core.DataTransport.Client.Interfaces; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// HttpContentHeaders wrapper to enable mocking in unit tests | ||
/// </summary> | ||
public class HttpContentHeadersWrapper : IHttpContentHeadersWrapper | ||
{ | ||
private readonly HttpContentHeaders _headers; | ||
|
||
public HttpContentHeadersWrapper(HttpContentHeaders headers) | ||
{ | ||
_headers = headers; | ||
} | ||
|
||
public ICollection<string> ContentEncoding => _headers.ContentEncoding.ToList(); | ||
} | ||
} | ||
#endif |
32 changes: 32 additions & 0 deletions
32
src/Agent/NewRelic/Agent/Core/DataTransport/Client/HttpContentWrapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#if !NETFRAMEWORK | ||
using System.IO; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using NewRelic.Agent.Core.DataTransport.Client.Interfaces; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// HttpContent wrapper to enable mocking in unit tests | ||
/// </summary> | ||
public class HttpContentWrapper : IHttpContentWrapper | ||
{ | ||
private readonly HttpContent _httpContent; | ||
|
||
public HttpContentWrapper(HttpContent httpContent) | ||
{ | ||
_httpContent = httpContent; | ||
} | ||
|
||
public Task<Stream> ReadAsStreamAsync() | ||
{ | ||
return _httpContent.ReadAsStreamAsync(); | ||
} | ||
|
||
public IHttpContentHeadersWrapper Headers => new HttpContentHeadersWrapper(_httpContent.Headers); | ||
} | ||
} | ||
#endif |
54 changes: 54 additions & 0 deletions
54
src/Agent/NewRelic/Agent/Core/DataTransport/Client/HttpRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2020 New Relic, Inc. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using NewRelic.Agent.Configuration; | ||
using NewRelic.Agent.Core.DataTransport.Client.Interfaces; | ||
|
||
namespace NewRelic.Agent.Core.DataTransport.Client | ||
{ | ||
/// <summary> | ||
/// Abstraction of a client request | ||
/// </summary> | ||
public class HttpRequest : IHttpRequest | ||
{ | ||
private readonly IConfiguration _configuration; | ||
private Uri _uri; | ||
|
||
public HttpRequest(IConfiguration configuration) | ||
{ | ||
_configuration = configuration; | ||
Content = new NRHttpContent(_configuration); | ||
} | ||
|
||
public IConnectionInfo ConnectionInfo { get; set; } | ||
public string Endpoint { get; set; } | ||
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(); | ||
public Uri Uri => _uri ??= GetUri(Endpoint, ConnectionInfo); // cache the Uri | ||
|
||
public IHttpContent Content { get; } | ||
public Guid RequestGuid { get; set; } | ||
|
||
private Uri GetUri(string method, IConnectionInfo connectionInfo) | ||
{ | ||
var uri = new StringBuilder("/agent_listener/invoke_raw_method?method=") | ||
.Append(method) | ||
.Append($"&{Constants.LicenseKeyParameterName}=") | ||
.Append(_configuration.AgentLicenseKey) | ||
.Append("&marshal_format=json") | ||
.Append("&protocol_version=") | ||
.Append(Constants.ProtocolVersion); | ||
|
||
if (_configuration.AgentRunId != null) | ||
{ | ||
uri.Append("&run_id=").Append(_configuration.AgentRunId); | ||
} | ||
|
||
var uriBuilder = new UriBuilder(connectionInfo.HttpProtocol, connectionInfo.Host, connectionInfo.Port, uri.ToString()); | ||
|
||
return new Uri(uriBuilder.Uri.ToString().Replace("%3F", "?")); | ||
} | ||
} | ||
} |
Oops, something went wrong.