1- using System ;
2- using System . IO ;
1+ using System . IO ;
32using System . Net ;
43using System . Net . Http ;
54using System . Net . Http . Headers ;
65using System . Text ;
7- using System . Text . Json ;
86using System . Threading . Tasks ;
97
108namespace AbuseIPDB
119{
12- public static class API
10+ internal static class API
1311 {
14- public const int MaxRetries = 3 ;
15- public const int RetryDelay = 1000 * 3 ;
16- public const int ExtraDelay = 1000 ;
17- public const int PreviewMaxLength = 500 ;
18-
1912 public static async Task < HttpResponseMessage > Request
2013 (
2114 this HttpClient cl ,
2215 HttpMethod method ,
23- string url ,
16+ string path ,
2417 object obj ,
25- HttpStatusCode target = HttpStatusCode . OK ,
2618 bool absoluteUrl = false )
27- => await Request ( cl , method , url , new StringContent ( JsonSerializer . Serialize ( obj ) , Encoding . UTF8 , "application/json" ) , target , absoluteUrl : absoluteUrl ) ;
19+ => await Request ( cl , method , path , await obj . Serialize ( ) , absoluteUrl : absoluteUrl ) ;
2820
2921 public static async Task < HttpResponseMessage > Request
3022 (
3123 this HttpClient cl ,
3224 HttpMethod method ,
33- string url ,
25+ string path ,
3426 Stream stream ,
3527 string fieldName ,
3628 string fileName ,
37- HttpStatusCode target = HttpStatusCode . OK ,
3829 bool absoluteUrl = false )
39- => await Request ( cl , method , url , new StreamContent ( stream ) , target , fieldName , fileName , absoluteUrl ) ;
30+ => await Request ( cl , method , path , new StreamContent ( stream ) , fieldName , fileName , absoluteUrl ) ;
4031
4132 public static async Task < HttpResponseMessage > Request
4233 (
4334 this HttpClient cl ,
4435 HttpMethod method ,
45- string url ,
36+ string path ,
4637 HttpContent content = null ,
47- HttpStatusCode target = HttpStatusCode . OK ,
4838 string fieldName = null ,
4939 string fileName = null ,
5040 bool absoluteUrl = false )
5141 {
52- int retries = 0 ;
53-
54- HttpResponseMessage res = null ;
42+ using HttpRequestMessage req = new ( method , absoluteUrl ? path : string . Concat ( Constants . BaseUri , path ) ) ;
5543
56- while ( res is null || ! target . HasFlag ( res . StatusCode ) && retries < MaxRetries )
44+ if ( ! string . IsNullOrEmpty ( fieldName ) && ! string . IsNullOrEmpty ( fileName ) )
5745 {
58- HttpRequestMessage req = new ( method , absoluteUrl ? url : string . Concat ( AbuseIPDBClient . BaseUri , url ) ) ;
59-
60- if ( content is not StreamContent ) req . Content = content ;
61- else req . Content = new MultipartFormDataContent ( )
46+ req . Content = new MultipartFormDataContent ( )
6247 {
6348 { content , fieldName , fileName }
6449 } ;
65-
66- res = await cl . SendAsync ( req ) ;
67-
68- MediaTypeHeaderValue contentType = res . Content . Headers . ContentType ;
69- if ( ! absoluteUrl && contentType . MediaType != "application/json" )
70- {
71- bool includePreview = contentType . MediaType . StartsWith ( "text/" ) ;
72- string preview = null ;
73-
74- if ( includePreview )
75- {
76- string data = await res . Content . ReadAsStringAsync ( ) ;
77- preview = $ "\n Preview: { data [ ..Math . Min ( data . Length , PreviewMaxLength ) ] } ";
78- }
79-
80- throw new AbuseIPDBException ( $ "Expected response to be JSON, but received '{ contentType . MediaType } '{ preview } ") ;
81- }
82-
83- if ( ! target . HasFlag ( res . StatusCode ) && ( int ) res . StatusCode >= 500 ) await Task . Delay ( RetryDelay ) ;
84- else break ;
85-
86- retries ++ ;
8750 }
51+ else req . Content = content ;
8852
89- if ( ! target . HasFlag ( res . StatusCode ) )
90- {
91- AbuseIPDBError [ ] errors = ( await res . Deseralize < AbuseIPDBErrorContainer > ( ) ) ? . Errors ;
92- if ( errors is null ) throw new AbuseIPDBException ( $ "Failed to request { method } { url } , expected one of the following status codes: { string . Join ( ", " , res . StatusCode . GetFlags ( ) ) } but received { res . StatusCode } ") ;
53+ HttpResponseMessage res = await cl . SendAsync ( req ) ;
9354
94- string suffix = errors . Length == 1 ? "" : "s" ;
55+ MediaTypeHeaderValue contentType = res . Content . Headers . ContentType ;
56+ if ( ! absoluteUrl && contentType . MediaType != "application/json" )
57+ {
58+ bool includePreview = contentType . MediaType . StartsWith ( "text/" ) ;
59+ string preview = includePreview ? $ "\n Preview: { await res . GetPreview ( ) } " : null ;
9560
96- StringBuilder sb = new ( ) ;
97- sb . AppendLine ( $ "Failed to request { method } { url } , received { errors . Length } API error{ suffix } .") ;
98- for ( int i = 0 ; i < errors . Length ; i ++ )
99- {
100- AbuseIPDBError error = errors [ i ] ;
101- error . Index = i ;
102- sb . Append ( $ "[#{ i + 1 } ] (Status Code: { error . Status } ) { error . Detail } ") ;
61+ throw new AbuseIPDBException ( $ "Expected response to be JSON, but received '{ contentType . MediaType } '{ preview } ") ;
62+ }
10363
104- if ( error . Source is null || error . Source . Parameter is null ) { sb . AppendLine ( ) ; continue ; }
105- sb . AppendLine ( $ ", source parameter: { error . Source . Parameter } ") ;
106- }
64+ if ( res . IsSuccessStatusCode ) return res ;
10765
108- AbuseIPDBException ex = new ( sb . ToString ( ) , errors ) ;
66+ AbuseIPDBError [ ] errors = ( ( await res . Deseralize < AbuseIPDBErrorContainer > ( ) ) ? . Errors )
67+ ?? throw new AbuseIPDBException ( $ "Failed to request { method } { path } , received status code { res . StatusCode } ") ;
10968
110- if ( res . StatusCode == HttpStatusCode . TooManyRequests )
111- {
112- RetryConditionHeaderValue retry = res . Headers . RetryAfter ;
113- if ( retry is not null ) ex . RetryAfter = ( int ) retry . Delta . Value . TotalMilliseconds ;
114- }
69+ string suffix = errors . Length == 1 ? "" : "s" ;
70+ StringBuilder sb = new ( ) ;
71+
72+ sb . AppendLine ( $ "Failed to request { method } { path } , received { errors . Length } API error{ suffix } .") ;
73+ for ( int i = 0 ; i < errors . Length ; i ++ )
74+ {
75+ AbuseIPDBError error = errors [ i ] ;
76+ error . Index = i ;
77+ sb . Append ( $ "[#{ i + 1 } ] (status code: { error . Status } ) { error . Detail } ") ;
11578
116- throw ex ;
79+ if ( error . Source is null || error . Source . Parameter is null ) { sb . AppendLine ( ) ; continue ; }
80+ sb . AppendLine ( $ ", source parameter: { error . Source . Parameter } ") ;
11781 }
11882
119- return res ;
120- }
121-
122- public static async Task < T > Deseralize < T > ( this HttpResponseMessage res , JsonSerializerOptions options = null )
123- {
124- Stream stream = await res . Content . ReadAsStreamAsync ( ) ;
125- if ( stream . Length == 0 ) throw new AbuseIPDBException ( "Response content is empty, can't parse as JSON." ) ;
83+ AbuseIPDBException ex = new ( sb . ToString ( ) , errors ) ;
12684
127- try
85+ if ( res . StatusCode == HttpStatusCode . TooManyRequests )
12886 {
129- return await JsonSerializer . DeserializeAsync < T > ( stream , options ) ;
87+ RetryConditionHeaderValue retry = res . Headers . RetryAfter ;
88+ if ( retry is not null ) ex . RetryAfter = ( int ) retry . Delta . Value . TotalMilliseconds ;
13089 }
131- catch ( Exception ex )
132- {
133- using StreamReader sr = new ( stream ) ;
134- string text = await sr . ReadToEndAsync ( ) ;
13590
136- throw new AbuseIPDBException ( $ "Exception while parsing JSON: { ex . GetType ( ) . Name } => { ex . Message } \n Preview: { text [ ..Math . Min ( text . Length , PreviewMaxLength ) ] } ") ;
137- }
91+ throw ex ;
13892 }
13993 }
14094}
0 commit comments