Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions IPinfo.Tests/IPApiTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void TestGetDetails()
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponse actual = client.IPApi.GetDetails(ip);

var expectations = new List<Tuple<object, object>>()
Expand Down Expand Up @@ -43,7 +43,7 @@ public void TestGetDetails()
Assert.False(actual.Privacy.Vpn);
Assert.False(actual.Privacy.Tor);
Assert.False(actual.Privacy.Relay);
Assert.True(actual.Privacy.Hosting);
Assert.True(actual.Privacy.Hosting);
}

[Fact]
Expand All @@ -53,11 +53,11 @@ public void TestBogonIPV4()
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponse actual = client.IPApi.GetDetails(ip);

Assert.Equal("127.0.0.1", actual.IP);
Assert.True(actual.Bogon);
Assert.True(actual.Bogon);
}

[Fact]
Expand All @@ -67,11 +67,11 @@ public void TestBogonIPV6()
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponse actual = client.IPApi.GetDetails(ip);

Assert.Equal("2001:0:c000:200::0:255:1", actual.IP);
Assert.True(actual.Bogon);
Assert.True(actual.Bogon);
}

[Fact]
Expand All @@ -81,11 +81,11 @@ public void TestNonBogonIPV4()
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponse actual = client.IPApi.GetDetails(ip);

Assert.Equal("1.1.1.1", actual.IP);
Assert.False(actual.Bogon);
Assert.False(actual.Bogon);
}

[Fact]
Expand All @@ -95,11 +95,43 @@ public void TestNonBogonIPV6()
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponse actual = client.IPApi.GetDetails(ip);

Assert.Equal("2a03:2880:f10a:83:face:b00c:0:25de", actual.IP);
Assert.False(actual.Bogon);
Assert.False(actual.Bogon);
}

[Fact]
public void TestGetResproxy()
{
string ip = "175.107.211.204";
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseResproxy actual = client.IPApi.GetResproxy(ip);

Assert.Equal("175.107.211.204", actual.IP);
Assert.NotNull(actual.LastSeen);
Assert.NotNull(actual.PercentDaysSeen);
Assert.NotNull(actual.Service);
}

[Fact]
public void TestGetResproxyNotFound()
{
string ip = "8.8.8.8";
IPinfoClient client = new IPinfoClient.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseResproxy actual = client.IPApi.GetResproxy(ip);

Assert.Null(actual.IP);
Assert.Null(actual.LastSeen);
Assert.Null(actual.PercentDaysSeen);
Assert.Null(actual.Service);
}
}
}
88 changes: 88 additions & 0 deletions src/IPinfo/Apis/IPApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,93 @@ public Models.IPResponse GetDetailsV6(string ipv6Address)
return responseModel;
}

/// <summary>
/// Retrieves residential proxy details for an IP address.
/// </summary>
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
public Models.IPResponseResproxy GetResproxy(
IPAddress ipAddress)
{
string ipString = ipAddress?.ToString();
return this.GetResproxy(ipString);
}

/// <summary>
/// Retrieves residential proxy details for an IP address.
/// </summary>
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
public Models.IPResponseResproxy GetResproxy(
string ipAddress)
{
Task<Models.IPResponseResproxy> t = this.GetResproxyAsync(ipAddress);
ApiHelper.RunTaskSynchronously(t);
return t.Result;
}

/// <summary>
/// Retrieves residential proxy details for an IP address.
/// </summary>
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
public Task<Models.IPResponseResproxy> GetResproxyAsync(
IPAddress ipAddress,
CancellationToken cancellationToken = default)
{
string ipString = ipAddress?.ToString();
return this.GetResproxyAsync(ipString, cancellationToken);
}

/// <summary>
/// Retrieves residential proxy details for an IP address.
/// </summary>
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
public async Task<Models.IPResponseResproxy> GetResproxyAsync(
string ipAddress,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(ipAddress))
{
return null;
}

string cacheKey = "resproxy:" + ipAddress;

// first check the data in the cache if cache is available
IPResponseResproxy resproxyResponse = (IPResponseResproxy)GetFromCache(cacheKey);
if (resproxyResponse != null)
{
return resproxyResponse;
}

// prepare query string for API call.
StringBuilder queryBuilder = new StringBuilder(this.BaseUrl);
queryBuilder.Append("resproxy/{ip_address}");
// process optional template parameters.
ApiHelper.AppendUrlWithTemplateParameters(queryBuilder, new Dictionary<string, object>()
{
{ "ip_address", ipAddress },
});

// prepare the API call request to fetch the response.
HttpRequest httpRequest = this.CreateGetRequest(queryBuilder.ToString());

// invoke request and get response.
HttpStringResponse response = await this.GetClientInstance().ExecuteAsStringAsync(httpRequest, cancellationToken).ConfigureAwait(false);
HttpContext context = new HttpContext(httpRequest, response);

// handle errors defined at the API level.
this.ValidateResponse(context);

var responseModel = System.Text.Json.JsonSerializer.Deserialize<IPResponseResproxy>(response.Body);

SetInCache(cacheKey, responseModel);
return responseModel;
}

}
}
39 changes: 39 additions & 0 deletions src/IPinfo/Models/IPResponseResproxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;

namespace IPinfo.Models
{
/// <summary>
/// Residential proxy detection response.
/// </summary>
public class IPResponseResproxy
{
/// <summary>
/// The IP address.
/// </summary>
[JsonPropertyName("ip")]
public string IP { get; set; }

/// <summary>
/// The last time this IP was seen as a residential proxy.
/// </summary>
[JsonPropertyName("last_seen")]
public string LastSeen { get; set; }

/// <summary>
/// The percentage of days seen as a residential proxy.
/// </summary>
[JsonPropertyName("percent_days_seen")]
public double? PercentDaysSeen { get; set; }

/// <summary>
/// The residential proxy service name.
/// </summary>
[JsonPropertyName("service")]
public string Service { get; set; }

// immutable type
[JsonConstructor]
public IPResponseResproxy(string ip, string lastSeen, double? percentDaysSeen, string service) =>
(IP, LastSeen, PercentDaysSeen, Service) = (ip, lastSeen, percentDaysSeen, service);
}
}
Loading