WebClient UploadStringAsync hangs

M

Malcolm Hall

UploadStringAsync is supposed to be asyncronous but I can make it hang
by passing it an invalid url. It looks to me like it does a
synchronous DNS request first that makes it hang. I don't want my app
to hang under any circumstances when making this web client request,
especially not long DNS requests, does anyone know a work around? Btw
if testing this you need to change the url to a different duff one
every time.

string server = "http://www.google.duffurl";
string param = "test=5";
WebClient wc = new WebClient();
wc.Headers["Content-Type"] = "application/x-www-form-urlencoded";
wc.Encoding = Encoding.UTF8;
wc.UploadStringCompleted += new UploadStringCompletedEventHandler
(wc_UploadStringCompleted);
Uri u = new Uri(server);
wc.UploadStringAsync(u, "POST", param); // hangs if can't find server
Debug.Writeline("upload sent");
 
P

Peter Duniho

Malcolm said:
UploadStringAsync is supposed to be asyncronous but I can make it hang
by passing it an invalid url. It looks to me like it does a
synchronous DNS request first that makes it hang. I don't want my app
to hang under any circumstances when making this web client request,
especially not long DNS requests, does anyone know a work around?

Sorry. Not repro here (Windows 7, .NET 3.5).

For what it's worth, the detailed remarks in the MSDN documentation only
promise that the sending of the string is done asynchronously. They
don't rule out the possibility that the method may attempt to resolve
the server address prior to starting the asynchronous operation.

I took a quick look at the implementation in Reflector, but didn't see
any obvious attempts to resolve the URI before the UploadStringAsync()
method returns. That doesn't mean it doesn't, but if it does it's
well-buried under a bunch of initialization code.

I would have tried stepping through the actual .NET code, but the
symbol/source server has been acting up again and for some reason my
..NET source-level debugging is broken again. :(

It might help if you would be more specific about your problem. What
version of .NET are you using? What version of Windows? When you write
"hang", what do you really mean? Does your program literally never
return from that method? Or are you (inaccurately) using the word
"hang" simply to mean the method call takes longer than you expect?
Btw
if testing this you need to change the url to a different duff one
every time.

I just modified your code example to incorporate
"DateTime.Now.Ticks.ToString()" in the URI, to guarantee a new URI each
time I execute the test.

Pete
 
M

Malcolm Hall

I too didn't see anything obvious in Reflector. I added current time
debug output before and after the line:

21/11/2009 13:19:55
21/11/2009 13:20:07

As you can see its taking 12 seconds to complete the call to
UploadStringAsync. This blocks the UI thread and causes (Not
responding) to appear in the Form's title. This is more or less the
same time it takes DNS to error if I type ping and a bad domain into
CMD so it does appear to be a DNS problem.

I'm running Windows 7 and .NET 3.5. I'm going to try some of the async
methods its using internally to see if they have the same problem. I
think BeginGetRequestStream was one of them... Or I'll try source
debugging because not tried that before.
 
P

Peter Duniho

Malcolm said:
I too didn't see anything obvious in Reflector. I added current time
debug output before and after the line:

21/11/2009 13:19:55
21/11/2009 13:20:07

As you can see its taking 12 seconds to complete the call to
UploadStringAsync.

Well, like I said, I was unable to reproduce the problem. In fact, I
did the same kind of timing modification to the code example when I
original tested it, and while the call to UploadStringAsync() took
almost all of the total execution time, it was generally less than 100ms
to return.
This blocks the UI thread and causes (Not
responding) to appear in the Form's title. This is more or less the
same time it takes DNS to error if I type ping and a bad domain into
CMD so it does appear to be a DNS problem.

Well, given that it's controlled by whether the domain is valid or not,
I figured that was obvious. :) Why my system is so quick to reject the
URL, I don't know.

Maybe I've got an Internet provider with faster DNS servers, or lack of
redirection (in fact, my ISP defaults to DNS redirection so they can
serve up fake page results to sell ads, but I'm able to turn that off
and have), or their DNS servers don't have a fallback (i.e. query
additional servers), or some combination of the above.

In the end, however, I think the fundamental answer is that the
UploadStringAsync() method is probably working as designed.
Specifically, it never promised that the DNS lookup itself would be
asynchronous, and to the extent that the delay cause by a DNS lookup is
not under your control, you may always suffer some delay in the code
using that method.
I'm running Windows 7 and .NET 3.5. I'm going to try some of the async
methods its using internally to see if they have the same problem. I
think BeginGetRequestStream was one of them... Or I'll try source
debugging because not tried that before.

Good luck. I'd be curious to know if you get it working. Like I said,
on my computer it's broken again. Last time, that was because the
Microsoft servers weren't working right, and I wasted so much time
trying to debug the problem before, I really don't have any interest
messing around with it again. But if you can confirm the servers are
working properly, that might suggest it'd be worthwhile for me to reset
things and try again. :)

Pete
 
G

Goran Sliskovic

....
Well, like I said, I was unable to reproduce the problem. In fact, I did
the same kind of timing modification to the code example when I original
tested it, and while the call to UploadStringAsync() took almost all of
the total execution time, it was generally less than 100ms to return.
....

You have to test probably with non-existant DNS server, not non-existant
domain. DNS servers are very fast in getting the IP from name. That means
that they are also fast in finding the domains that do not exist. ;)

Try to set a DNS server to a non-existant IP address in your network. Don't
use IP of online computer.

Regards,
Goran
 
M

Malcolm Hall

The hang happens at BeginGetRequestStream. It must be my ISP cause I
had some other people try the ping test and theirs fails almost
instantly like you. I thought the whole point of the async methods is
to make it easier to have a responsive UI and not have the complexity
of creating threads but that all goes out the window beacuse of this
case. So I guess I have 2 choices:

1. Do my own DNS resolve in a thread and then invoke the UI thread and
replace the domain name with the IP in the async request if it is
valid.
2. Resort to using the Background Worker or Async Operation classes
and use the async web req methods in sync (you know the pattern when
you do waitone on the async handle so you can still abort)...

Actually has anyone seen a good async Http Post class with events?
That's all I really need.
 
M

Malcolm Hall

Durgaprasad Gorti @ http://social.msdn.microsoft.com/fo.../thread/2cb74a7e-6e8f-4d05-b86a-2401df5d2ed3/

"You are absolutely correct. If you call the BeginGetResponse(), today
we block until the
name resolution happens. This is result of an unfortunate series of
issues we had to
workthrough in the initial release and we were locked into backward
compatibility for the
2.0 release.

We are considering this to be one of the important issues to fix in a
future release.
Sorry for the inconvinience caused. Let me know if there is anything
else I can help you with"

Sept 2006!!! Guess it wasn't important enough then :-(
 
P

Peter Duniho

Goran said:
....
Well, like I said, I was unable to reproduce the problem. In fact, I
did the same kind of timing modification to the code example when I
original tested it, and while the call to UploadStringAsync() took
almost all of the total execution time, it was generally less than
100ms to return.
....

You have to test probably with non-existant DNS server, not non-existant
domain. [...]

No. I have to test using the same scenario that the OP is using. The
point is to try to achieve results comparable to his, not necessarily to
find _another way_ to force a delay in DNS resolution.

Pete
 
P

Peter Duniho

Malcolm said:
Durgaprasad Gorti @ http://social.msdn.microsoft.com/fo.../thread/2cb74a7e-6e8f-4d05-b86a-2401df5d2ed3/

"You are absolutely correct. If you call the BeginGetResponse(), today
we block until the
name resolution happens. This is result of an unfortunate series of
issues we had to
workthrough in the initial release and we were locked into backward
compatibility for the
2.0 release.

We are considering this to be one of the important issues to fix in a
future release.
Sorry for the inconvinience caused. Let me know if there is anything
else I can help you with"

Sept 2006!!! Guess it wasn't important enough then :-(

Have you tried it in the .NET 4.0 beta? Maybe it has been fixed, finally.

There is, of course, the issue that just because Durgaprasad Gorti
considered it an important issue in 2006, that doesn't mean he even
still works in that group, never mind still considers it important.

A much better way to state your case and provide for a way to track
progress of the bug resolution is to submit a report on the Connect web
site. Then you can see the official response, as well as have others
provide feedback regarding importance and workarounds.

As far as workarounds go, you have lots of options, not just the two you
mentioned. But yes, wrapping a synchronous call in an asynchronous
operation is a tried-and-true way of dealing with this sort of thing.
If you're dealing with a GUI and really need the completion to be
signaled on the main GUI thread, BackgroundWorker is the way to go.
Otherwise, a simple asynchronous delegate invocation might be fine.

Pete
 
G

Goran Sliskovic

Peter Duniho said:
You have to test probably with non-existant DNS server, not non-existant
domain. [...]

No. I have to test using the same scenario that the OP is using. The
point is to try to achieve results comparable to his, not necessarily to
find _another way_ to force a delay in DNS resolution.

Pete

We have first to make sure that delay in DNS resolution causes the problem.
You are not going to reproduce that if you DNS server is properly
configured.

Goran
 
G

Goran Sliskovic

Malcolm Hall said:
The hang happens at BeginGetRequestStream. It must be my ISP cause I
had some other people try the ping test and theirs fails almost
instantly like you. I thought the whole point of the async methods is
to make it easier to have a responsive UI and not have the complexity
of creating threads but that all goes out the window beacuse of this
case. So I guess I have 2 choices:

1. Do my own DNS resolve in a thread and then invoke the UI thread and
replace the domain name with the IP in the async request if it is
valid.
2. Resort to using the Background Worker or Async Operation classes
and use the async web req methods in sync (you know the pattern when
you do waitone on the async handle so you can still abort)...

Actually has anyone seen a good async Http Post class with events?
That's all I really need.

You can verify that the problem is in DNS by using network protocol analyzer
such as wireshark (free).
Most likely it is the problem with DNS. Try to resolve name in command
prompt using nslookup and see what happens. I guess it will block for some
time on your system, it doesn't on my.
Your ideas will probably work.
It looks like a bug in the .NET framework to me. I expect async method to
queue the request and return immediatly. Though you can always stretch the
meaning of the work "immediately".

Goran
 
P

Peter Duniho

Goran said:
Peter Duniho said:
You have to test probably with non-existant DNS server, not
non-existant domain. [...]

No. I have to test using the same scenario that the OP is using. The
point is to try to achieve results comparable to his, not necessarily
to find _another way_ to force a delay in DNS resolution.

Pete

We have first to make sure that delay in DNS resolution causes the
problem.

YOU may want to make sure of that. Please feel free to if you like.
But I have no need or desire to, and telling me that _I_ need to do that
is simply incorrect.

The point of my test is to try to reproduce the problem the OP is
having. Not to prove or disprove the plausibility of some theory.

If you make an assumption about the cause of the problem, and then
design your test to only succeed when your assumption is true, you
haven't demonstrated anything useful except that your theory _could_ be
correct. It doesn't say anything about whether it _is_ correct.
You are not going to reproduce that if you DNS server is
properly configured.

That all depends on what the actual problem is. My goal was simply to
determine whether or not, on my computer, the exact code that the OP
posted (or something very similar to it) causes the exact problem that
the OP is asking about.

I've done exactly that. Nothing more, nothing less.

Pete
 
G

Goran Sliskovic

Peter Duniho said:
....

I've done exactly that. Nothing more, nothing less.

Pete

Whatever... However from what you did we know exactly what we knew before,
that we know nothing. This is a newsgroup where we try to help somebody.
It's not about theory, it's about practice.

Regards,
Goran
 
M

Malcolm Hall

I have found an easy work around until MS fix this:

This is an excerpt from: http://www.informit.com/blogs/blog.aspx?uk=Is-this-really-asynchronous

"There is a way around the problem: queue a background thread to issue
the asynchronous request. Yes, I know it sounds crazy, but it works.
And it's incredibly easy to do with anonymous delegates:

ThreadPool.QueueUserWorkItem(delegate(object state)
{
cli.DownloadStringAsync((Uri)state);
}, GetNextUrlFromQueue());

That spawns a thread, which then issues the asynchronous Web request.
The time waiting for DNS lookup is spent in the background thread
rather than on the main processing thread. It looks pretty goofy, and
unless it's commented well somebody six months from now will wonder
what I was smoking when I wrote it."
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top