Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TaskFactory.FromAsync with BeginGetRequestStream/EndGetRequestStream hangs

I have a method like this, that hangs when declaring responseObject using Task.Factory.FromAsync()

private async Task<string> post(string url, string postdata)
        {
            var request = WebRequest.Create(new Uri(url)) as HttpWebRequest;
            request.Method = "POST";

            // this works
            Task<Stream> requestStream = Task<Stream>.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, request);
            var sw = new StreamWriter(requestStream.Result);
            byte[] data = Encoding.UTF8.GetBytes(postdata);
            await requestStream.Result.WriteAsync(data, 0, data.Length);

            // this hangs
            Task<WebResponse> responseObject = Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, request); // Hangs here
            // Doesn't get to here
            var responseStream = responseObject.Result.GetResponseStream();
            var sr = new StreamReader(responseStream);
            string received = await sr.ReadToEndAsync();

            return received;
}

It works fine if I manually run the Begin/End methods, like this:

        request.BeginGetRequestStream(async ar =>
        {
            var requestStream = request.EndGetRequestStream(ar);
            using (var sw = new StreamWriter(requestStream))
            {
                byte[] data = Encoding.UTF8.GetBytes(postdata);
                await requestStream.WriteAsync(data, 0, data.Length);
            }

            request.BeginGetResponse(async a =>
            {
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(a);
                var responseStream = response.GetResponseStream();
                using (var sr = new StreamReader(responseStream))
                {
                    string received = await sr.ReadToEndAsync();
                }

            }, null);
        }, null);

However, in that case, ReadToEndAsync() is run on a separate thread, and I want the method to return the result, which isn't possible since the method returns before the BeginGetResponse callback is finished.

At this point I think I am doing this horribly wrong and going about this completely the wrong way, so any help would be much appreciated.

like image 733
carbin Avatar asked Dec 20 '22 11:12

carbin


1 Answers

First of all: Don't use ".Result" in async methods. This will block the thread that is running your method. Use "await" instead, so the thread jumps back to your method when the result is acquired.

The problem with your code is that you open a request stream, but you never close it. So when should it expect to finish your request and send it? It'll always expect more input to come, until the request runs in a timeout. Also you have not set a content-type and content-length.

private async Task<string> post(string url, string postdata)
    {
        var request = WebRequest.Create(new Uri(url)) as HttpWebRequest;
        request.Method = "POST";

        byte[] data = Encoding.UTF8.GetBytes(postdata);
        request.ContentLength = data.Length;

        using (var requestStream = await Task<Stream>.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, request))
        {
            await requestStream.WriteAsync(data, 0, data.Length);
        }


        WebResponse responseObject = await Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, request);
        var responseStream = responseObject.GetResponseStream();
        var sr = new StreamReader(responseStream);
        string received = await sr.ReadToEndAsync();

        return received;
}
like image 171
user1908061 Avatar answered Jan 27 '23 02:01

user1908061