I still working on learning Xamarin.iOs and randomly (sometime on the simulator but more often on the device), I've a timeout exception calling HttpWebRequest.GetRequestStream() in an async method. However, I create a new HttpWebRequest each time the method is called and I think I properly close and dispose everything. Here's a snippet of the code :
private async static Task<T> internalCallService<T>(ILogger logger,string url, string body, string httpVerb, string contentType)
{
T result = default(T);
WebRequest wr=null;
try
{
logger.Log("Calling {0} started", url);
wr = HttpWebRequest.Create(url);
wr.ContentType = contentType;
wr.Method = httpVerb;
wr.Timeout = DEFAULT_TIMEOUT;
byte[] streamContent = System.Text.Encoding.UTF8.GetBytes(body);
using (Stream dataStream = wr.GetRequestStream())
{
dataStream.Write(streamContent, 0, streamContent.Length);
dataStream.Flush();
dataStream.Close();
var sw = new Stopwatch();
sw.Start();
using (var response = (await wr.GetResponseAsync().ConfigureAwait(false)))
{
if (response != null)
{
sw.Stop();
logger.Log("Time to call {0} : {1} ms", url, sw.ElapsedMilliseconds);
try
{
var webResponse = response as HttpWebResponse;
if (webResponse != null && webResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = webResponse.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
var responseText = reader.ReadToEnd();
result = JsonConvert.DeserializeObject<T>(responseText);
reader.Close();
}
responseStream.Close();
}
}
}
finally
{
if (response != null)
{
response.Close();
}
}
}
}
}
}
catch (Exception e)
{
logger.Log(e.GetBaseException().Message);
throw;
}
finally
{
if (wr != null)
{
wr.Abort();
wr = null;
}
}
return result;
}
Any idea what's going on ?
Thanks.
Related
I'm trying to post a piece of data but it seems to be getting caught and hangs (but not throwing an error).
internal IAsyncResult RequestGet<TPostType>(Action<string> callback, string path, TPostType value)
{
var http = (HttpWebRequest)WebRequest.Create(Connection.GetUri(path).Uri);
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
var parsedContent = JsonConvert.SerializeObject(value);
var encoding = new ASCIIEncoding();
var bytes = encoding.GetBytes(parsedContent);
return http.BeginGetRequestStream(ar =>
{
try
{
var stream = http.EndGetRequestStream(ar);
stream.Write(bytes, 0, bytes.Length);
stream.Close();
http.BeginGetResponse(body =>
{
// its around here that it hangs.
var response = http.EndGetResponse(ar);
var s = response.GetResponseStream();
ReadStream(callback, s);
}, null);
}
catch (WebException webex)
{
WebResponse errResp = webex.Response;
using (Stream respStream = errResp.GetResponseStream())
{
Console.WriteLine(new StreamReader(respStream).ReadToEnd());
}
}
}, null);
}
There were a few times when it didn't hang but told me that the request body was missing, however I didn't make any progress beyond that. Could someone please inform me as to what I'm doing wrong?
The ReadStream method works for GET requests, but for context:
private static IAsyncResult ReadStream(Action<string> callback, Stream stream)
{
var buffer = new byte[5000];
var asyncResult = stream.BeginRead(buffer, 0, 5000, ar2 =>
{
try
{
callback(Encoding.UTF8.GetString(buffer, 0, 5000));
}
catch (Exception ex)
{
// do better error handling
Console.WriteLine("exception reading stream");
Console.WriteLine(ex);
}
}, null);
return asyncResult;
}
For more context on how its being used:
public IAsyncResult BeginGetChainPart(Action<BlockData.BlockList> callback, int height)
{
var asyncResult = new HttpConnector(Connection).RequestGet(body =>
{
callback(JsonConvert.DeserializeObject<BlockData.BlockList>(body));
}, "/local/chain/blocks-after", new BlockData.BlockHeight { Height = height });
return asyncResult;
}
found the solution, posting in case anyone comes across the same issue:
internal IAsyncResult RequestGet<TPostType>(Action<string> callback, string path, TPostType value)
{
var http = (HttpWebRequest)WebRequest.Create(Connection.GetUri(path).Uri);
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
var parsedContent = JsonConvert.SerializeObject(value);
var encoding = new ASCIIEncoding();
var bytes = encoding.GetBytes(parsedContent);
return http.BeginGetRequestStream(ar => {
try
{
var stream = http.EndGetRequestStream(ar);
stream.Write(bytes, 0, bytes.Length);
stream.Close();
var response = http.GetResponse();
var responseStream = response.GetResponseStream();
ReadStream(callback, responseStream);
}
catch (WebException webex)
{
WebResponse errResp = webex.Response;
using (Stream respStream = errResp.GetResponseStream())
{
StreamReader reader = new StreamReader(respStream);
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
}, null);
}
I have a service that builds an HttpWebRequest and sets its body with a JSON string to be sent to a system's API. At the point of calling for the request stream, I am ocassionaly getting a WebException timeout error. Why would it error at this point? Does it test connecting to the other system before continuing?
try
{
//Get default request data
if (requestObj == null)
requestObj = new ApiRequest();
//Convert request info to json+bytes
string url = _baseUrl + path;
string requestStr = JsonConvert.SerializeObject(requestObj, _jsonSettings);
requestObjJson = requestStr;
byte[] data = Encoding.UTF8.GetBytes(requestStr);
//Post to SystemX
webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.ContentType = "application/json;charset=UTF-8";
webRequest.Headers.Add("access_token", _accessToken);
webRequest.Method = "POST";
webRequest.ContentLength = data.Length;
webRequestJson = JsonConvert.SerializeObject(webRequest, Formatting.Indented);
using (Stream dataStream = webRequest.GetRequestStream())
{
dataStream.Write(data, 0, data.Length);
dataStream.Close();
}
webRequestJson = JsonConvert.SerializeObject(webRequest, Formatting.Indented);
LogMessage("SystemXApiAdapter._request() Request", "Information only. Not an error.", webRequestJson + ";" + requestObjJson, connectionString, customerName, licenseInfoID);
//Get response
string responseString = null;
HttpStatusCode status;
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
if (response != null)
{
using (Stream stream = response.GetResponseStream())
using (StreamReader sr = new StreamReader(stream))
{
responseString = sr.ReadToEnd();
status = response.StatusCode;
}
response.Close();
}
else
{
status = HttpStatusCode.NoContent;
}
}
T responseObj = null;
LogMessage("SystemXApiAdapter._request() Response", "Information only. Not an error.", responseString, connectionString, customerName, licenseInfoID);
if (typeof(T) == typeof(EmptyApiResponse))
return null;
if (string.IsNullOrEmpty(responseString))
throw new Exception("Empty response for non-empty request");
responseObj = JsonConvert.DeserializeObject<T>(responseString);
if (callback != null)
callback(responseObj);
//Get next page of data if needed
if (handlePaging && status == HttpStatusCode.PartialContent)
{
//Move to next page and recursively make more requests until we've got everything
requestObj.page_num++;
T moreData = _request<T>(path, connectionString, customerName, licenseInfoID, true, requestObj, callback);
if (moreData != null)
responseObj = (T)(responseObj).AppendResponseData(moreData);
}
return responseObj;
}
catch (Exception ex)
{
SystemXApiException SystemXEx = new SystemXApiException(requestObj, ex, webRequestJson);
if (!bIsRetry && SystemXEx.StatusCode.HasValue && retryOnStatuses.Contains(SystemXEx.StatusCode.Value))
{
return _request<T>(path, connectionString, customerName, licenseInfoID, handlePaging, requestObj, callback, true);
}
else
{
throw SystemXEx;
}
if (ex is WebException)
{
WebException webEx = (WebException)ex;
HttpStatusCode? StatusCode = null;
string responseBody = null;
//Read Status Code
if (webEx.Status == WebExceptionStatus.ProtocolError)
{
var response = webEx.Response as HttpWebResponse;
if (response != null)
{
StatusCode = response.StatusCode;
}
}
//Retry once on applicable status code
if (!bIsRetry && StatusCode.HasValue && retryOnStatuses.Contains(StatusCode.Value))
return _request<T>(path, connectionString, customerName, licenseInfoID, handlePaging, requestObj, callback, true);
//Read response Body
if (webEx.Response != null)
{
using (Stream responseStream = webEx.Response.GetResponseStream())
using (StreamReader sr = new StreamReader(responseStream, Encoding.Default))
{
responseBody = sr.ReadToEnd();
}
}
else
{
responseBody = "Empty response body.";
}
string message = null;
//Format new Exception
if (StatusCode.HasValue)
message = string.Format("{0} {1}: {2}: {3}: {4}", (int)StatusCode, StatusCode.ToString(), responseBody, requestObjJson, webRequestJson);
else
message = string.Format("{0} {1}: {2}: {3}: {4}", "No status code", "No status code", responseBody, requestObjJson, webRequestJson);
throw new Exception(message, ex);
}
else
throw;
}
finally
{
if (webRequest != null)
{
webRequest.Abort();
}
}
One option is to increase the time limit.
https://msdn.microsoft.com/pt-br/library/system.net.httpwebrequest.timeout(v=vs.110).aspx
webRequest.Timeout=2000; (2 seconds)
Another is to repeat the execution after a timeout, waiting x seconds.
Any connection can go through problems, your code need to be prepared for this.
I hope it helped in someway.
This question has been asked a million times, and yet none of the responses work for me. The one I was most excited about was Http Post for Windows Phone 8 but because it requires delegates, it's not right for my code... the Postdata function is called from repositories, it would be nice to get a response straight from this function!
How do I add post parameters to this code? I've been trying to get it to work for a good 10 hours now.
// Repository code
string url = "/bla/bla/" + blaId + "/";
Dictionary<string, string> postParams = new Dictionary<string, string>();
postParams.Add("value", message);
string response = await BlaDataContext.PostData(url, postParams);
// ...
public static async Task<string> PostData(string url, Dictionary<String, String> postParams)
{
HttpWebRequest request = WebRequest.CreateHttp(APIURL + url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
postParams.Add("oauth_token", Contract.AccessToken); // where do I add this to the request??
try
{
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
Debug.WriteLine(response.ContentType);
Stream responseStream = response.GetResponseStream();
string data;
using (var reader = new StreamReader(responseStream))
{
data = reader.ReadToEnd();
}
responseStream.Close();
return data;
}
catch (Exception e)
{
// whatever
}
}
HttpWebRequest request = WebRequest.CreateHttp("" + url);
//we could move the content-type into a function argument too.
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
postParams.Add("oauth_token", ""); // where do I add this to the request??
try
{
//this is how you do it
using(var stream = await request.GetRequestStreamAsync())
{
byte[] jsonAsBytes = Encoding.UTF8.GetBytes(string.Join("&", postParams.Select(pp => pp.Key + "=" + pp.Value)));
await stream.WriteAsync(jsonAsBytes, 0, jsonAsBytes.Length);
}
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
Debug.WriteLine(response.ContentType);
System.IO.Stream responseStream = response.GetResponseStream();
string data;
using (var reader = new System.IO.StreamReader(responseStream))
{
data = reader.ReadToEnd();
}
responseStream.Close();
return data;
}
Async Await HttpWebRequest Extensions:
public static class HttpExtensions
{
public static Task<Stream> GetRequestStreamAsync(this HttpWebRequest request)
{
var tcs = new TaskCompletionSource<Stream>();
try
{
request.BeginGetRequestStream(iar =>
{
try
{
var response = request.EndGetRequestStream(iar);
tcs.SetResult(response);
}
catch (Exception exc)
{
tcs.SetException(exc);
}
}, null);
}
catch (Exception exc)
{
tcs.SetException(exc);
}
return tcs.Task;
}
public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<HttpWebResponse>();
request.BeginGetResponse(asyncResponse =>
{
try
{
HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
HttpWebResponse someResponse =
(HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
taskComplete.TrySetResult(someResponse);
}
catch (WebException webExc)
{
HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
taskComplete.TrySetResult(failedResponse);
}
}, request);
return taskComplete.Task;
}
}
With the extensions, I think it's a little cleaner.
You need to write the parameters to the request body.
You could use an extension method like this one:
public static void AddFormData(this HttpWebRequest request, IDictionary<string, string> data)
{
using (var memStream = new MemoryStream())
using (var writer = new StreamWriter(memStream))
{
bool first = true;
foreach (var d in data)
{
if (!first)
writer.Append("&");
writer.Write(Uri.EscapeDataString(d.Key));
writer.Write("=");
writer.Write(Uri.EscapeDataString(d.Value));
first = false;
}
writer.Flush();
request.ContentLength = memStream.Length;
memStream.Position = 0;
using (var reqStream = request.GetRequestStream())
{
memStream.CopyTo(reqStream);
}
}
}
Call it like this:
request.AddFormData(postParams);
I have this code that returns null if a bad result is encountered and in the case of an exception:
private JArray GetRESTData(string uri)
{
try
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<JArray>(s);
}
MessageBox.Show(string.Format("Status code == {0}", webResponse.StatusCode));
return null;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
...but it smells a bit rancid. Is there a way I can refactor this to not call "return null" twice?
I would recommend you don't handle the exception within this function, assume it behaves correctly.
private JArray GetRESTData(string uri)
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<JArray>(s);
}
I removed the StatusCode check because the HttpWebResponse class will already throw an exception if it's not a valid status code.
The responsibility of this method should be to get the rest data, not to deal with user interaction (the MessageBox).
To refactor this further, I would make another method to make the web-request, and another to parse the response.
private JArray GetRESTData(string uri)
{
var json = ReadFromUri(uri);
return JsonConvert.DeserializeObject<JArray>(json);
}
private string ReadFromUri(string uri)
{
using (var webRequest = (HttpWebRequest)WebRequest.Create(uri))
using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
using (var reader = new StreamReader(webResponse.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
To use this method:
try
{
var myArray = GetRESTData("http://someservice.com/bananabread");
}
catch (WebException exception)
{
MessageBox.Show("Some exception happened: {0}", exception);
}
Just move the return out of the try/catch block
try
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<JArray>(s);
}
MessageBox.Show(string.Format("Status code == {0}", webResponse.StatusCode));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return null;
I like prefer a single return point, if possible:
private JArray GetRESTData(string uri)
{
JArray ret = null; // single return value, declared outside of try/catch
try
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
ret = JsonConvert.DeserializeObject<JArray>(s);
}
MessageBox.Show(string.Format("Status code == {0}", webResponse.StatusCode));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return ret;
}
Note: in this simple example, this is probably enough. In more complex functions, you might want to set ret to null within the exception handler if it has been constructed but in an invalid state. If you're using RAII however, this shouldn't really be an issue.
I get warning on responseStream in following function:
private static string GetResponseString(WebResponse response)
{
using (var responseStream = response.GetResponseStream())
{
if (responseStream != null)
{
using (var responseReader = new StreamReader(responseStream))
{
var strResponse = responseReader.ReadToEnd();
return strResponse;
}
}
}
return string.Empty;
}
I call this function from places like like this one:
var request = (HttpWebRequest)WebRequest.Create(Uri);
request.Headers.Add("Authorization", "GoogleLogin auth=" + this.SecurityToken);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.Timeout = 5000;
// build the post string
var postString = new StringBuilder();
postString.AppendFormat("registration_id={0}", recipientId);
postString.AppendFormat("&data.payload={0}", message);
postString.AppendFormat("&collapse_key={0}", collapseKey);
// write the post-string as a byte array
var requestData = Encoding.ASCII.GetBytes(postString.ToString());
request.ContentLength = requestData.Length;
var requestStream = request.GetRequestStream();
requestStream.Write(requestData, 0, requestData.Length);
requestStream.Close();
// Do the actual request and read the response stream
try
{
var response = request.GetResponse();
var responseString = GetResponseString(response);
response.Close();
return responseString.Contains("id=")
? SendStatus.Ok
: GetSendStatusFromResponse(responseString);
}
catch (WebException ex)
{
var webResponse = (HttpWebResponse)ex.Response;
if (webResponse != null)
{
if (webResponse.StatusCode.Equals(HttpStatusCode.Unauthorized))
{
return SendStatus.Unauthorized;
}
if (webResponse.StatusCode.Equals(HttpStatusCode.ServiceUnavailable))
{
return SendStatus.ServiceUnavailable;
}
}
this.LoggerService.Log(null, ex);
return SendStatus.GeneralException;
}
StreamReader takes ownership of the stream passed to it in the constructor call in the sense that it will call Dispose on it when the StreamReader itself is closed - hence it will already be disposed when the outer Using statement attempts to dispose of it.