11using System . Net ;
2+ using System . Net . Http ;
23using System . Text ;
34
45namespace Geocoding . MapQuest ;
@@ -137,8 +138,9 @@ where l.Quality < Quality.COUNTRY
137138 /// <returns>The deserialized MapQuest response.</returns>
138139 public async Task < MapQuestResponse > Execute ( BaseRequest f , CancellationToken cancellationToken = default ( CancellationToken ) )
139140 {
140- HttpWebRequest request = await Send ( f , cancellationToken ) . ConfigureAwait ( false ) ;
141- MapQuestResponse r = await Parse ( request , cancellationToken ) . ConfigureAwait ( false ) ;
141+ using var client = BuildClient ( ) ;
142+ using var request = CreateRequest ( f ) ;
143+ MapQuestResponse r = await Parse ( client , request , cancellationToken ) . ConfigureAwait ( false ) ;
142144 if ( r is not null && ! r . Results . IsNullOrEmpty ( ) )
143145 {
144146 foreach ( MapQuestResult o in r . Results )
@@ -161,100 +163,95 @@ where l.Quality < Quality.COUNTRY
161163 return r ! ;
162164 }
163165
164- private async Task < HttpWebRequest > Send ( BaseRequest f , CancellationToken cancellationToken )
166+ /// <summary>
167+ /// Builds the HTTP client used for MapQuest requests.
168+ /// </summary>
169+ /// <returns>The configured HTTP client.</returns>
170+ protected virtual HttpClient BuildClient ( )
171+ {
172+ if ( Proxy is null )
173+ return new HttpClient ( ) ;
174+
175+ return new HttpClient ( new HttpClientHandler { Proxy = Proxy } ) ;
176+ }
177+
178+ private HttpRequestMessage CreateRequest ( BaseRequest f )
165179 {
166180 if ( f is null )
167181 throw new ArgumentNullException ( nameof ( f ) ) ;
168182
169- HttpWebRequest request ;
170- bool hasBody = false ;
183+ Uri requestUri ;
171184 switch ( f . RequestVerb )
172185 {
173186 case "GET" :
174187 case "DELETE" :
175188 case "HEAD" :
176189 {
177190 var u = $ "{ f . RequestUri } json={ WebUtility . UrlEncode ( f . RequestBody ) } &";
178- request = WebRequest . CreateHttp ( u ) ;
191+ requestUri = new Uri ( u , UriKind . Absolute ) ;
179192 }
180193 break ;
181194 case "POST" :
182195 case "PUT" :
183196 default :
184197 {
185- request = WebRequest . CreateHttp ( f . RequestUri ) ;
186- hasBody = ! String . IsNullOrWhiteSpace ( f . RequestBody ) ;
198+ requestUri = f . RequestUri ;
187199 }
188200 break ;
189201 }
190- request . Method = f . RequestVerb ;
191- request . ContentType = "application/" + f . InputFormat + "; charset=utf-8" ;
192-
193- if ( Proxy is not null )
194- request . Proxy = Proxy ;
195202
196- if ( hasBody )
203+ var request = new HttpRequestMessage ( new HttpMethod ( f . RequestVerb ) , requestUri ) ;
204+ if ( ! String . IsNullOrWhiteSpace ( f . RequestBody )
205+ && ! String . Equals ( f . RequestVerb , "GET" , StringComparison . OrdinalIgnoreCase )
206+ && ! String . Equals ( f . RequestVerb , "DELETE" , StringComparison . OrdinalIgnoreCase )
207+ && ! String . Equals ( f . RequestVerb , "HEAD" , StringComparison . OrdinalIgnoreCase ) )
197208 {
198- byte [ ] buffer = Encoding . UTF8 . GetBytes ( f . RequestBody ) ;
199- //request.Headers.ContentLength = buffer.Length;
200- using ( cancellationToken . Register ( request . Abort , false ) )
201- using ( Stream rs = await request . GetRequestStreamAsync ( ) . ConfigureAwait ( false ) )
202- {
203- cancellationToken . ThrowIfCancellationRequested ( ) ;
204- await rs . WriteAsync ( buffer , 0 , buffer . Length , cancellationToken ) . ConfigureAwait ( false ) ;
205- await rs . FlushAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
206- }
209+ request . Content = new StringContent ( f . RequestBody , Encoding . UTF8 , "application/" + f . InputFormat ) ;
207210 }
211+
208212 return request ;
209213 }
210214
211- private async Task < MapQuestResponse > Parse ( HttpWebRequest request , CancellationToken cancellationToken )
215+ private async Task < MapQuestResponse > Parse ( HttpClient client , HttpRequestMessage request , CancellationToken cancellationToken )
212216 {
213- if ( request is null )
214- throw new ArgumentNullException ( nameof ( request ) ) ;
215-
216217 string requestInfo = $ "[{ request . Method } ] { request . RequestUri } ";
217218 try
218219 {
219- string json ;
220- using ( HttpWebResponse response = ( HttpWebResponse ) await request . GetResponseAsync ( ) . ConfigureAwait ( false ) )
221- {
222- cancellationToken . ThrowIfCancellationRequested ( ) ;
223- if ( ( int ) response . StatusCode >= 300 ) //error
224- throw new Exception ( ( int ) response . StatusCode + " " + response . StatusDescription ) ;
220+ using var response = await client . SendAsync ( request , cancellationToken ) . ConfigureAwait ( false ) ;
221+ cancellationToken . ThrowIfCancellationRequested ( ) ;
222+
223+ var json = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
224+
225+ if ( ! response . IsSuccessStatusCode )
226+ throw new Exception ( $ "{ ( int ) response . StatusCode } { requestInfo } | { response . ReasonPhrase } { BuildResponsePreview ( json ) } ") ;
225227
226- using ( var sr = new StreamReader ( response . GetResponseStream ( ) ! ) )
227- json = await sr . ReadToEndAsync ( ) . ConfigureAwait ( false ) ;
228- }
229228 if ( String . IsNullOrWhiteSpace ( json ) )
230229 throw new Exception ( "Remote system response with blank: " + requestInfo ) ;
231230
232231 MapQuestResponse ? o = json . FromJSON < MapQuestResponse > ( ) ;
233232 if ( o is null )
234- throw new Exception ( "Unable to deserialize remote response: " + requestInfo + " => " + json ) ;
233+ throw new Exception ( "Unable to deserialize remote response: " + requestInfo ) ;
235234
236235 return o ;
237236 }
238- catch ( WebException wex ) //convert to simple exception & close the response stream
237+ catch ( HttpRequestException ex )
239238 {
240- if ( wex . Response is not HttpWebResponse response )
241- throw new Exception ( $ "{ requestInfo } | { wex . Status } | { wex . Message } ", wex ) ;
242-
243- using ( response )
244- {
245- var sb = new StringBuilder ( requestInfo ) ;
246- sb . Append ( " | " ) ;
247- sb . Append ( response . StatusDescription ) ;
248- sb . Append ( " | " ) ;
249- using ( var sr = new StreamReader ( response . GetResponseStream ( ) ! ) )
250- {
251- sb . Append ( await sr . ReadToEndAsync ( ) . ConfigureAwait ( false ) ) ;
252- }
253- throw new Exception ( ( int ) response . StatusCode + " " + sb . ToString ( ) ) ;
254- }
239+ throw new Exception ( $ "{ requestInfo } | { ex . Message } ", ex ) ;
255240 }
256241 }
257242
243+ private static string BuildResponsePreview ( string ? body )
244+ {
245+ if ( String . IsNullOrWhiteSpace ( body ) )
246+ return String . Empty ;
247+
248+ var preview = body ! . Trim ( ) ;
249+ if ( preview . Length > 256 )
250+ preview = preview . Substring ( 0 , 256 ) + "..." ;
251+
252+ return " | Response preview: " + preview ;
253+ }
254+
258255 /// <inheritdoc />
259256 public async Task < IEnumerable < ResultItem > > GeocodeAsync ( IEnumerable < string > addresses , CancellationToken cancellationToken = default ( CancellationToken ) )
260257 {
0 commit comments