0

I have a function which checks if proxies are working or not, I decided to use Async Tasks to achieve this goal because it took way too much time to do it one by one, so I implemented this code:

var asyncTasks = new Task[toCheck.Count];
for (int i = 0; i < toCheck.Count; i++)
{
      Console.SetCursorPosition(0, Console.CursorTop);
      Console.Write("Checking... {0} proxies left {1} proxies working", toCheckCount, working.Count);
      asyncTasks[i] = check(toCheck[i]);
      toCheckCount -= 1;
}
Task.WaitAll(asyncTasks);
public static async Task check(string proxy)
{
      ServicePointManager.Expect100Continue = true;
      ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault;
      var proxyObj = new WebProxy(proxy);
      proxyObj.Address = new Uri($"socks4://{proxy}");
      var handler = new HttpClientHandler
      {
          Proxy = proxyObj
      };
      var httpClient = new HttpClient(handler);
      try
      { 
          httpResponseMessage response = await httpClient.GetAsync("https://www.google.com/");
          working.Add(proxy);
      }
      catch (HttpRequestException) {  }
      httpClient.Dispose();
}

This code is working in Visual Studio without problems, it works both in debug and release mode, however when I try to execute the .exe in the net6.0 folder the program runs through the whole code in a second and throws various exceptions ranging from request aborted to SSL connection couldn't be established to other common HTTP-related issues which stem from bad proxies, nothing unusual since this is exactly how the code checks if proxies work or not.

The problem is that the errors just don't stop and in the end 0 working proxies are returned. These results are obviously wrong since the same exact code yields the working proxies correctly in Visual Studio, so at this point I really have no clue what is going on here. I already searched for people that had similar issues but the results only said to await the code which I obviously already do, besides that switching the line

httpResponseMessage response = await httpClient.GetAsync("https://www.google.com/");

to:

httpResponseMessage response = httpClient.GetAsync("https://www.google.com/").Result;

worked, however, this is not a viable fix since it renders the whole purpose of this task useless.

//Edit To clarify I consider the program to be working, if it puts the proxies which sucessfully connected to google without exceptions in the List of working proxies. If an exception is encountered during the request the used proxy is ignored. this is exactly what happens when ran through VS, however if I run it via the .exe file directly it just instantly throws HttpExceptions on every single proxy, leading to the program showing 0 working proxies. This is wrong since you can test a proxy list in VS and see that x proxies are working and then check the same list through the .exe file and see that seemingly none are working, this result is false since it even marks good proxies as not working.

xliquid
  • 15
  • 5
  • 1
    Add a `Console.ReadLine()` at the beginning of your code, execute from the .exe, attach the VS debugger to the process, press the `Enter` key on the console, debug your code and see what's going on. – Camilo Terevinto Feb 23 '22 at 15:49
  • 2
    If `working.Add` is referring to `List.Add`, then that would not be threadsafe access. – Stephen Cleary Feb 23 '22 at 15:56
  • @StephenCleary That wouldn't cause the HTTP requests to fail though – Camilo Terevinto Feb 23 '22 at 15:58
  • @CamiloTerevinto thanks I am gonna try this now – xliquid Feb 23 '22 at 15:58
  • @StephenCleary I know I will make it threadsafe later, however I was just trying to have a quick and dirty prototype application for now anyways and like Camilo already said, that doesn't cause the issue so it really isn't my concern right now. – xliquid Feb 23 '22 at 16:02
  • @CamiloTerevinto with the VS Debugger attached it actually works, however I only get the same errors I get when just printing the Exceptions, nothing else. – xliquid Feb 23 '22 at 16:39
  • As a side note, the `HttpClient` class is intended to be instantiated [once](https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client#create-and-initialize-httpclient), and reused throughout the life of an application. – Theodor Zoulias Feb 23 '22 at 16:43
  • 1
    I think you will need to go into more detail about the difference you're seeing between running in Visual Studio vs running the exe directly. What are you observing and what do you define as "working" and "not working"? – Gabriel Luci Feb 23 '22 at 16:45
  • @TheodorZoulias I know, however not really an option since I need to pass diffrent proxies every time the task is called and that doesn't work with just one HttpClient, however after Gabriel Luci pointed it out I added a Dispose call to atleast reduce the work load slightly. – xliquid Feb 23 '22 at 16:47
  • How many are the proxies that you are checking? (approximately) – Theodor Zoulias Feb 23 '22 at 16:51
  • I would suggest to switch from your current `Task.WaitAll` approach to the [`Parallel.ForEachAsync`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreachasync) API ([example](https://stackoverflow.com/questions/15136542/parallel-foreach-with-asynchronous-lambda/68901782#68901782)), and use it with a small `MaxDegreeOfParallelism` like 2, and see what happens. It might be that your current approach is parallelizing the work too much. – Theodor Zoulias Feb 23 '22 at 16:55
  • Could you include in the question the exception that is thrown when a good proxy is checked via the .exe file directly? We need the exception type, the exception message, and the line of code that caused the exception. Having the exception's stack trace might also be helpful. – Theodor Zoulias Feb 23 '22 at 17:06
  • @TheodorZoulias I tried your implementation but sadly I still got the same erorr and sorry if I didn't do a great job at explaining the checking process (english is not my first language) but if the proxy is good there is no exception thrown, the problem is that when ran through the .exe file, simply every proxy is labeled as bad which just isn't true. – xliquid Feb 23 '22 at 17:13
  • As far as I understand a good proxy is labeled as bad by not being added in the `working` list. This can only happen if an exception is thrown. Currently you are swallowing the exceptions, because [you think that they are not really useful](https://stackoverflow.com/questions/71239964/httpclient-async-request-only-works-in-visual-studio?noredirect=1#comment125925382_71240285). We need to see these exceptions, so that we can at least agree with you that they are not useful. – Theodor Zoulias Feb 23 '22 at 17:36
  • You understand the checking process and the problem correctly, but the exceptions which are thrown are already outlined in the question they are simply request aborted, SL connection couldn't be established, Host refused the connection, etc. these exceptions are the same as the ones the code is meant to encounter while checking and they all get thrown at the same line, that line being the line that makes the request, and since it works in VS, I know that that line can't be the issue, and since the exceptions only get thrown on that line I know that they aren't really useful. – xliquid Feb 23 '22 at 17:47
  • Let's focus on the good proxies that are labeled as bad, and let's ignore the genuinely bad proxies. This will simplify things. Please create a list with proxies that you know that they are good, and provide us with an much info as possible about the behavior of your program when checking these -known to be good- proxies. Without including detailed exception information, your question is not answerable IMHO. – Theodor Zoulias Feb 23 '22 at 17:54
  • 1
    @TheodorZoulias for some reason when I recheck the few good proxies that I am left with none of them are marked as working even in VS, honestly this issue seems to be far more than just that it only works in VS, sadly I only discoverd this now, however I still think that nonetheless it should work like in VS when executed with the file. Maybe something is wrong with the codebase, maybe with the project itself, I don't know anymore tbh. I will probably lay this question on ice for a few days, recode everything and tell you guys how I fixed it, I am sorry if I wasted the time of anyone here. – xliquid Feb 23 '22 at 18:52

1 Answers1

0

I can't say for sure what you're experiencing that, but there are a few things in your code that are fishy and fixing them might end up fixing this issue.

First,

Task.WaitAll(asyncTasks);

Use await instead:

await Task.WhenAll(asyncTasks);

If this is a console app, you can change your Main method to be async Task, which is allowed since C# 7.1. Since it seems you've established that the problem seems to have something to do with it being asynchronous, this may resolve the issue. Waiting synchronously on asynchronous code is a common cause of problems.

Next,

catch (HttpRequestException) {  }

At least during testing, when you know some kind of failure is happening, log this somewhere, even if you just output the exception message to the console, in case some weird kind of exception is happening that you aren't expecting.

Also, both HttpResponseMessage and HttpClient are IDisposable. Usually you wouldn't dispose HttpClient, but reuse it, but I see why you are. In a loop like this where you're repeatedly allocating objects like this, it's wise to clean up after yourself since garbage collection won't be able to run until the loop is done. So either call .Dispose() on them when you're done, or put them in a using block.

Gabriel Luci
  • 33,807
  • 4
  • 44
  • 75
  • Thanks for the answer, I already had the first fix implemented but that didn't work either resulting in me using Task.WaitAll(asyncTasks); which didn't work either, also I didn't log the erorr since the exceptions weren't really useful anyways, I already listed them in the question and I didn't experience any other ones, even when broadening the catch Clause by catching just a plain Exception, I will try changing the main method to a async Task and see how that goes. – xliquid Feb 23 '22 at 16:10
  • sadly both fixes didn't really work my main is now a async task and I am using await Task.WhenAll(asyncTasks); instead of Task.WaitAll(asyncTasks); however the program still doesn't work when I execute the .exe file – xliquid Feb 23 '22 at 16:17