From 8304031ce7798b243d203bf55ffac8f1b3f75297 Mon Sep 17 00:00:00 2001 From: "gu.martinm@gmail.com" Date: Tue, 3 Jun 2014 10:04:06 +0200 Subject: [PATCH] HttpClientsExamples: working with HttpClient class --- .../HttpClientsExamples/HttpClientExample.cs | 228 ++++++++++++++++++--- .../HttpClientsExamples.MonoDevelop.csproj | 4 +- .../HttpClientsExamples/Program.cs | 8 +- 3 files changed, 205 insertions(+), 35 deletions(-) diff --git a/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientExample.cs b/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientExample.cs index 4ec49bc..faf6a69 100644 --- a/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientExample.cs +++ b/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientExample.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using System.Net; using System.IO; +using System.Text; namespace HttpClientsExamples { @@ -21,6 +22,21 @@ namespace HttpClientsExamples } using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds (5) }) { + /** + * WHEN USING HttpCompletionOption.ResponseContentRead!!! + * The Mono implementation is using response.Content.LoadIntoBufferAsync. So, it seems as if it is going + * to read the full data and it will be stored in a buffer. So, when using HttpCompletionOption.ResponseContentRead + * (it is the default option for HttpClient) it does not matter if you want to use a stream instead of the full remote + * answer because you have already received the full data :/ + * + * SO, I GUESS IF YOU WANT TO SAVE MEMORY YOU SHOULD USE HttpCompletionOption.ResponseHeadersRead and + * content.ReadAsStreamAsync or client.GetStreamAsync (whatever with stream and HttpCompletionOption.ResponseHeadersRead option) + * + * For example, in this implementation I am using the content.ReadAsStreamAsync method, this stream comes + * (I guess) from the buffer which already stored the full data. So even if you think you are + * saving memory because you are using a stream you are not. The stream does not come from the TCP connection but + * from the buffer (I guess) + */ Task task = client.GetAsync (uri, HttpCompletionOption.ResponseContentRead, CancellationToken.None); try { @@ -42,46 +58,198 @@ namespace HttpClientsExamples using(HttpResponseMessage httpWebResponse = task.Result) { // May httpWebResponse be null? API says nothing (as usual...) API sucks. - if (httpWebResponse.StatusCode == HttpStatusCode.OK) + // See GetStringAsync Mono implementation of HttpClient.cs, when using HttpCompletionOption.ResponseContentRead + // option I do not think httpWebResponse will be null. + httpWebResponse.EnsureSuccessStatusCode (); + + using(HttpContent content = httpWebResponse.Content) { - using(HttpContent content = httpWebResponse.Content) + // May content be null? API says nothing (as usual...) API sucks. + // See GetStringAsync Mono implementation of HttpClient.cs, when using HttpCompletionOption.ResponseContentRead + // option I do not think content will be null. + Task taskStream = content.ReadAsStreamAsync (); + try { + Task.WaitAll (taskStream); + } catch (AggregateException ae) { + ae.Handle (e => { + if (e is OperationCanceledException) { + Console.WriteLine ("Cancelling a Task, catching OperationCanceledException: {0}", e); + return true; + } else { + Console.WriteLine ("Cancelling a Task, dunno what are you: {0}", e); + return true; + } + }); + } + + if (taskStream.Status == TaskStatus.RanToCompletion) { - // May content be null? API says nothing (as usual...) API sucks. - Task taskStream = content.ReadAsStreamAsync (); try { - Task.WaitAll (taskStream); - } catch (AggregateException ae) { - ae.Handle (e => { - if (e is OperationCanceledException) { - Console.WriteLine ("Cancelling a Task, catching OperationCanceledException: {0}", e); - return true; - } else { - Console.WriteLine ("Cancelling a Task, dunno what are you: {0}", e); - return true; - } - }); - } - - if (taskStream.Status == TaskStatus.RanToCompletion) - { - try { - using(Stream replyStream = taskStream.Result) - // May replyStream be null? API says nothing (as usual...) API sucks. - using (StreamReader replyStreamReader = new StreamReader (replyStream)) - { - string s = replyStreamReader.ReadToEnd (); - Console.WriteLine (s); - } - } - catch(IOException e) { - Console.WriteLine ("HttpClient, IOException: ", e); + using(Stream replyStream = taskStream.Result) + // May replyStream be null? API says nothing (as usual...) API sucks. + // When using HttpCompletionOption.ResponseContentRead option I do not think replyStream will be null. + using (StreamReader replyStreamReader = new StreamReader (replyStream)) + { + string s = replyStreamReader.ReadToEnd (); + Console.WriteLine (s); } } + catch(IOException e) { + Console.WriteLine ("HttpClient, IOException: ", e); + } } } } } } + + + Console.WriteLine("HttpClient with async statement"); + // No AggregateException when using async (you could retrieve AggregateException if you use Task.Exception but if you are + // using async is because you want to forget about Task.Wait, Task.Result, etc, etc ,etc) + // We receive only what would be the first exception in the AggregateException!!! + // See: http://msmvps.com/blogs/jon_skeet/archive/2011/06/22/eduasync-part-11-more-sophisticated-but-lossy-exception-handling.aspx + // http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx + uri = null; + while (uri == null || uri.Length == 0) + { + Console.WriteLine("Specify the URI of the resource to retrieve."); + Console.WriteLine("Write URI: "); + uri = Console.ReadLine(); + } + Task taskHttpClient = this.DoGetAsync (uri); + try { + Task.WaitAll (taskHttpClient); + } catch (AggregateException ae) { + ae.Handle (e => { + if (e is OperationCanceledException) { + Console.WriteLine ("Cancelling a Task, catching OperationCanceledException: {0}", e); + return true; + } else { + Console.WriteLine ("Cancelling a Task, dunno what are you: {0}", e); + return true; + } + }); + } + if (taskHttpClient.Status == TaskStatus.RanToCompletion) + { + string result = taskHttpClient.Result; + Console.WriteLine (result); + } + + + Console.WriteLine("HttpClient GetStringAsync with async statement"); + // No AggregateException when using async (you could retrieve AggregateException if you use Task.Exception but if you are + // using async is because you want to forget about Task.Wait, Task.Result, etc, etc ,etc) + // We receive only what would be the first exception in the AggregateException!!! + // See: http://msmvps.com/blogs/jon_skeet/archive/2011/06/22/eduasync-part-11-more-sophisticated-but-lossy-exception-handling.aspx + // http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx + uri = null; + while (uri == null || uri.Length == 0) + { + Console.WriteLine("Specify the URI of the resource to retrieve."); + Console.WriteLine("Write URI: "); + uri = Console.ReadLine(); + } + taskHttpClient = this.DoGetStringAsync (uri); + try { + Task.WaitAll (taskHttpClient); + } catch (AggregateException ae) { + ae.Handle (e => { + if (e is OperationCanceledException) { + Console.WriteLine ("Cancelling a Task, catching OperationCanceledException: {0}", e); + return true; + } else { + Console.WriteLine ("Cancelling a Task, dunno what are you: {0}", e); + return true; + } + }); + } + if (taskHttpClient.Status == TaskStatus.RanToCompletion) + { + string result = taskHttpClient.Result; + Console.WriteLine (result); + } + + + // TODO: you must write another example with HttpCompletionOption.ResponseHeadersRead and I want + // to use in WebClientExamples and HttpWebRequestExample the same logic as in httpWebResponse.EnsureSuccessStatusCode + } + + private async Task DoGetAsync(string uri) + { + using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds (5) }) + { + /** + * WHEN USING HttpCompletionOption.ResponseContentRead!!! + * The Mono implementation is using response.Content.LoadIntoBufferAsync. So, it seems as if it is going + * to read the full data and it will be stored in a buffer. So, when using HttpCompletionOption.ResponseContentRead + * (it is the default option for HttpClient) it does not matter if you want to use a stream instead of the full remote + * answer because you have already received the full data :/ + * + * SO, I GUESS IF YOU WANT TO SAVE MEMORY YOU SHOULD USE HttpCompletionOption.ResponseHeadersRead and + * content.ReadAsStreamAsync or client.GetStreamAsync (whatever with stream and HttpCompletionOption.ResponseHeadersRead option) + * + * For example, in this implementation I am using in my ReadResponseAsync the content.ReadAsStreamAsync method, + * this stream comes (I guess) from the buffer which already stored the full data. So even if you think you are + * saving memory because you are using a stream you are not. The stream does not come from the TCP connection but + * from the buffer (I guess) + */ + using (HttpResponseMessage httpWebResponse = + await client.GetAsync (uri, HttpCompletionOption.ResponseContentRead, CancellationToken.None)) + { + // May httpWebResponse be null? API says nothing (as usual...) API sucks. + // See GetStringAsync Mono implementation of HttpClient.cs, when using HttpCompletionOption.ResponseContentRead + // option I do not think httpWebResponse will be null. + httpWebResponse.EnsureSuccessStatusCode (); + + using (HttpContent content = httpWebResponse.Content) + { + // May content be null? API says nothing (as usual...) API sucks. + // See GetStringAsync Mono implementation of HttpClient.cs, when using HttpCompletionOption.ResponseContentRead + // option I do not think content will be null. + return await ReadResponseAsync(content); + } + } + } + } + + async private Task ReadResponseAsync(HttpContent content) + { + /** + * Taken from HttpContent.cs ReadAsStringAsync() Mono implementation. + */ + Encoding encoding; + if (content.Headers != null && content.Headers.ContentType != null && content.Headers.ContentType.CharSet != null) { + encoding = Encoding.GetEncoding (content.Headers.ContentType.CharSet); + } else { + encoding = Encoding.UTF8; + } + + using (Stream replyStream = await content.ReadAsStreamAsync ()) + // May replyStream be null? API says nothing (as usual...) API sucks. + // When using HttpCompletionOption.ResponseContentRead option I do not think replyStream will be null. + using (StreamReader replyStreamReader = new StreamReader (replyStream, encoding)) + { + return await replyStreamReader.ReadToEndAsync(); + } + } + + + private async Task DoGetStringAsync(string uri) + { + using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds (5) }) + { + /** + * This is the best method (in my case). It makes many things for me (see its Mono implementation): + * 1. It checks for the HTTP return status code (it is using httpWebResponse.EnsureSuccessStatusCode) + * 2. It is using the remote received encoding to encode data. + * + * So, I can rely on this method when I do not need to save memory by means of using stream and + * HttpCompletionOption.ResponseHeadersRead option. :) + */ + return await client.GetStringAsync (uri); + } } } } diff --git a/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientsExamples.MonoDevelop.csproj b/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientsExamples.MonoDevelop.csproj index c16adcf..5fa10cb 100644 --- a/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientsExamples.MonoDevelop.csproj +++ b/Allgemeines/HttpClientsExamples/HttpClientsExamples/HttpClientsExamples.MonoDevelop.csproj @@ -33,7 +33,9 @@ - + + ..\..\..\..\..\..\..\usr\mymono\lib\mono\4.5\System.Net.Http.dll + diff --git a/Allgemeines/HttpClientsExamples/HttpClientsExamples/Program.cs b/Allgemeines/HttpClientsExamples/HttpClientsExamples/Program.cs index be9d1aa..4b2afed 100644 --- a/Allgemeines/HttpClientsExamples/HttpClientsExamples/Program.cs +++ b/Allgemeines/HttpClientsExamples/HttpClientsExamples/Program.cs @@ -6,10 +6,10 @@ namespace HttpClientsExamples { public static void Main(string[] args) { - WebClientExample webclientExample = new WebClientExample(); - webclientExample.Test(); - HttpWebRequestExample httpWebRequestExample = new HttpWebRequestExample(); - httpWebRequestExample.Test(); + //WebClientExample webclientExample = new WebClientExample(); + //webclientExample.Test(); + //HttpWebRequestExample httpWebRequestExample = new HttpWebRequestExample(); + //httpWebRequestExample.Test(); HttpClientExample httpClientExample = new HttpClientExample(); httpClientExample.Test(); } -- 2.1.4