Skip to content

Commit 6d50c2d

Browse files
authored
Merge pull request #40 from ipinfo/silvano/eng-649-add-resproxy-support-in-ipinfocsharp-library
Add Residential Proxy Detection API support
2 parents 8df4b76 + cc89def commit 6d50c2d

File tree

3 files changed

+169
-10
lines changed

3 files changed

+169
-10
lines changed

IPinfo.Tests/IPApiTest.cs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public void TestGetDetails()
1515
IPinfoClient client = new IPinfoClient.Builder()
1616
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
1717
.Build();
18-
18+
1919
IPResponse actual = client.IPApi.GetDetails(ip);
2020

2121
var expectations = new List<Tuple<object, object>>()
@@ -43,7 +43,7 @@ public void TestGetDetails()
4343
Assert.False(actual.Privacy.Vpn);
4444
Assert.False(actual.Privacy.Tor);
4545
Assert.False(actual.Privacy.Relay);
46-
Assert.True(actual.Privacy.Hosting);
46+
Assert.True(actual.Privacy.Hosting);
4747
}
4848

4949
[Fact]
@@ -53,11 +53,11 @@ public void TestBogonIPV4()
5353
IPinfoClient client = new IPinfoClient.Builder()
5454
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
5555
.Build();
56-
56+
5757
IPResponse actual = client.IPApi.GetDetails(ip);
5858

5959
Assert.Equal("127.0.0.1", actual.IP);
60-
Assert.True(actual.Bogon);
60+
Assert.True(actual.Bogon);
6161
}
6262

6363
[Fact]
@@ -67,11 +67,11 @@ public void TestBogonIPV6()
6767
IPinfoClient client = new IPinfoClient.Builder()
6868
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
6969
.Build();
70-
70+
7171
IPResponse actual = client.IPApi.GetDetails(ip);
7272

7373
Assert.Equal("2001:0:c000:200::0:255:1", actual.IP);
74-
Assert.True(actual.Bogon);
74+
Assert.True(actual.Bogon);
7575
}
7676

7777
[Fact]
@@ -81,11 +81,11 @@ public void TestNonBogonIPV4()
8181
IPinfoClient client = new IPinfoClient.Builder()
8282
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
8383
.Build();
84-
84+
8585
IPResponse actual = client.IPApi.GetDetails(ip);
8686

8787
Assert.Equal("1.1.1.1", actual.IP);
88-
Assert.False(actual.Bogon);
88+
Assert.False(actual.Bogon);
8989
}
9090

9191
[Fact]
@@ -95,11 +95,43 @@ public void TestNonBogonIPV6()
9595
IPinfoClient client = new IPinfoClient.Builder()
9696
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
9797
.Build();
98-
98+
9999
IPResponse actual = client.IPApi.GetDetails(ip);
100100

101101
Assert.Equal("2a03:2880:f10a:83:face:b00c:0:25de", actual.IP);
102-
Assert.False(actual.Bogon);
102+
Assert.False(actual.Bogon);
103+
}
104+
105+
[Fact]
106+
public void TestGetResproxy()
107+
{
108+
string ip = "175.107.211.204";
109+
IPinfoClient client = new IPinfoClient.Builder()
110+
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
111+
.Build();
112+
113+
IPResponseResproxy actual = client.IPApi.GetResproxy(ip);
114+
115+
Assert.Equal("175.107.211.204", actual.IP);
116+
Assert.NotNull(actual.LastSeen);
117+
Assert.NotNull(actual.PercentDaysSeen);
118+
Assert.NotNull(actual.Service);
119+
}
120+
121+
[Fact]
122+
public void TestGetResproxyNotFound()
123+
{
124+
string ip = "8.8.8.8";
125+
IPinfoClient client = new IPinfoClient.Builder()
126+
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
127+
.Build();
128+
129+
IPResponseResproxy actual = client.IPApi.GetResproxy(ip);
130+
131+
Assert.Null(actual.IP);
132+
Assert.Null(actual.LastSeen);
133+
Assert.Null(actual.PercentDaysSeen);
134+
Assert.Null(actual.Service);
103135
}
104136
}
105137
}

src/IPinfo/Apis/IPApi.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,93 @@ public Models.IPResponse GetDetailsV6(string ipv6Address)
223223
return responseModel;
224224
}
225225

226+
/// <summary>
227+
/// Retrieves residential proxy details for an IP address.
228+
/// </summary>
229+
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
230+
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
231+
public Models.IPResponseResproxy GetResproxy(
232+
IPAddress ipAddress)
233+
{
234+
string ipString = ipAddress?.ToString();
235+
return this.GetResproxy(ipString);
236+
}
237+
238+
/// <summary>
239+
/// Retrieves residential proxy details for an IP address.
240+
/// </summary>
241+
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
242+
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
243+
public Models.IPResponseResproxy GetResproxy(
244+
string ipAddress)
245+
{
246+
Task<Models.IPResponseResproxy> t = this.GetResproxyAsync(ipAddress);
247+
ApiHelper.RunTaskSynchronously(t);
248+
return t.Result;
249+
}
250+
251+
/// <summary>
252+
/// Retrieves residential proxy details for an IP address.
253+
/// </summary>
254+
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
255+
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
256+
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
257+
public Task<Models.IPResponseResproxy> GetResproxyAsync(
258+
IPAddress ipAddress,
259+
CancellationToken cancellationToken = default)
260+
{
261+
string ipString = ipAddress?.ToString();
262+
return this.GetResproxyAsync(ipString, cancellationToken);
263+
}
264+
265+
/// <summary>
266+
/// Retrieves residential proxy details for an IP address.
267+
/// </summary>
268+
/// <param name="ipAddress">The IP address to check for residential proxy information.</param>
269+
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
270+
/// <returns>Returns the Models.IPResponseResproxy response from the API call.</returns>
271+
public async Task<Models.IPResponseResproxy> GetResproxyAsync(
272+
string ipAddress,
273+
CancellationToken cancellationToken = default)
274+
{
275+
if (string.IsNullOrEmpty(ipAddress))
276+
{
277+
return null;
278+
}
279+
280+
string cacheKey = "resproxy:" + ipAddress;
281+
282+
// first check the data in the cache if cache is available
283+
IPResponseResproxy resproxyResponse = (IPResponseResproxy)GetFromCache(cacheKey);
284+
if (resproxyResponse != null)
285+
{
286+
return resproxyResponse;
287+
}
288+
289+
// prepare query string for API call.
290+
StringBuilder queryBuilder = new StringBuilder(this.BaseUrl);
291+
queryBuilder.Append("resproxy/{ip_address}");
292+
// process optional template parameters.
293+
ApiHelper.AppendUrlWithTemplateParameters(queryBuilder, new Dictionary<string, object>()
294+
{
295+
{ "ip_address", ipAddress },
296+
});
297+
298+
// prepare the API call request to fetch the response.
299+
HttpRequest httpRequest = this.CreateGetRequest(queryBuilder.ToString());
300+
301+
// invoke request and get response.
302+
HttpStringResponse response = await this.GetClientInstance().ExecuteAsStringAsync(httpRequest, cancellationToken).ConfigureAwait(false);
303+
HttpContext context = new HttpContext(httpRequest, response);
304+
305+
// handle errors defined at the API level.
306+
this.ValidateResponse(context);
307+
308+
var responseModel = System.Text.Json.JsonSerializer.Deserialize<IPResponseResproxy>(response.Body);
309+
310+
SetInCache(cacheKey, responseModel);
311+
return responseModel;
312+
}
313+
226314
}
227315
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace IPinfo.Models
4+
{
5+
/// <summary>
6+
/// Residential proxy detection response.
7+
/// </summary>
8+
public class IPResponseResproxy
9+
{
10+
/// <summary>
11+
/// The IP address.
12+
/// </summary>
13+
[JsonPropertyName("ip")]
14+
public string IP { get; set; }
15+
16+
/// <summary>
17+
/// The last time this IP was seen as a residential proxy.
18+
/// </summary>
19+
[JsonPropertyName("last_seen")]
20+
public string LastSeen { get; set; }
21+
22+
/// <summary>
23+
/// The percentage of days seen as a residential proxy.
24+
/// </summary>
25+
[JsonPropertyName("percent_days_seen")]
26+
public double? PercentDaysSeen { get; set; }
27+
28+
/// <summary>
29+
/// The residential proxy service name.
30+
/// </summary>
31+
[JsonPropertyName("service")]
32+
public string Service { get; set; }
33+
34+
// immutable type
35+
[JsonConstructor]
36+
public IPResponseResproxy(string ip, string lastSeen, double? percentDaysSeen, string service) =>
37+
(IP, LastSeen, PercentDaysSeen, Service) = (ip, lastSeen, percentDaysSeen, service);
38+
}
39+
}

0 commit comments

Comments
 (0)