Порядок инициализации
От: Аноним  
Дата: 10.09.19 16:45
Оценка:
Почему приведенный код работает, если порядок инициализации:
static HttpClient httpClient = new System.Net.Http.HttpClient(new Http2CustomHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, Proxy = p });        
static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };

И не работает, если порядок инициализации:
static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };
static HttpClient httpClient = new System.Net.Http.HttpClient(new Http2CustomHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, Proxy = p });

Код:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public class Http2CustomHandler : WinHttpHandler
        {
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
            {
                request.Version = new Version("2.0");
                return base.SendAsync(request, cancellationToken);
            }
        }
        //так не работает
        //static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };
        static HttpClient httpClient = new System.Net.Http.HttpClient(new Http2CustomHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, Proxy = p });
        //так работает
        static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };

        public static string GetHtmlFromUrl(string Url)
        {
            var resTask = httpClient.GetAsync(new Uri(Url));
            var response = resTask.Result;
            var strTask = response.Content.ReadAsStringAsync();
            var strResponse = strTask.Result;
            return strResponse;
        }
        private async void Button1_Click(object sender, EventArgs e)
        {
            string strGetAsync = "https://bittrex.com/api/v1.1/public/getmarketsummaries";
            HttpResponseMessage response = await httpClient.GetAsync(strGetAsync).ConfigureAwait(false);
            HttpStatusCode responseStatus = response.StatusCode;
        }
    }
}
Re: Порядок инициализации
От: KRT Украина  
Дата: 11.09.19 12:10
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>Почему приведенный код работает, если порядок инициализации:

А>
А>static HttpClient httpClient = new System.Net.Http.HttpClient(new Http2CustomHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, Proxy = p });        
А>static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };
А>

А>И не работает, если порядок инициализации:
А>
А>static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("мой логин прокси", "мой пароль прокси") };
А>static HttpClient httpClient = new System.Net.Http.HttpClient(new Http2CustomHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, Proxy = p });
А>


В первом случае во время инициализации httpClient Proxy равна null и тогда запросы идут напрямую. Что именно не работает во втором случае? Может что-то с прокси сервером?
Re[2]: Порядок инициализации
От: Passerby  
Дата: 11.09.19 14:56
Оценка:
Все заработало. В конце сообщения корректировка кода.
Здравствуйте, KRT, Вы писали:
KRT>В первом случае во время инициализации httpClient Proxy равна null и тогда запросы идут напрямую. Что именно не работает во втором случае? Может что-то с прокси сервером?
Возникает ошибка во время запуска: System.InvalidOperationException: "When using a non-null Proxy, the WindowsProxyUsePolicy property must be set to WindowsProxyUsePolicy.UseCustomProxy."
Эта ошибка на строке кода:
return base.SendAsync(request, cancellationToken);

в методе
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)

Прокси через http 1 работает
Написал в организацию прокси вопрос, почему не работает http/2 через прокси, код взял отсюда: https://blog.dotnetframework.org/2019/04/05/support-http2-in-your-c-client-code/
Их ответ:
"проверили curl

curl --http2 --proxy http://93.189.149.200:41965 --proxy-user мой логи:мой пароль -I https://bittrex.com/api/v1.1/public/getmarketsummaries
HTTP/1.0 200 Connection established

HTTP/2 200
date: Tue, 10 Sep 2019 08:43:30 GMT
content-type: application/json; charset=utf-8
set-cookie: __cfduid=d8cfdff69619c414dfa6ed3a0cde432341568105009; expires=Wed, 09-Sep-20 08:43:29 GMT; path=/; domain=.bittrex.com; HttpOnly
cache-control: public, max-age=0
expires: Tue, 10 Sep 2019 08:43:30 GMT
last-modified: Tue, 10 Sep 2019 08:43:29 GMT
vary: *
strict-transport-security: max-age=15768000
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
cf-cache-status: MISS
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 51401d585883c43d-LED


ответ есть, может я что-то не понимаю ?"


В групповых политиках или в реестре прокси не выставлял, т.к. хотелось бы, чтобы некоторые запросы могли идти без прокси. Но похоже надо выставлять в групповых политиках или в реестре.
По http 1 прокси работает без изменения реестра:
static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("логин", "пароль") };
static HttpClientHandler handler = new HttpClientHandler { Proxy = p };
protected static HttpClient client = new HttpClient(handler);

Установил свойство WindowsProxyUsePolicy=WindowsProxyUsePolicy.UseCustomProxy все заработало.

Теперь как бы проверить, что запрос идет через прокси, а не напрямую.
Отредактировано 11.09.2019 17:29 Passerby . Предыдущая версия . Еще …
Отредактировано 11.09.2019 15:30 Passerby . Предыдущая версия .
Отредактировано 11.09.2019 15:17 Passerby . Предыдущая версия .
Отредактировано 11.09.2019 15:16 Passerby . Предыдущая версия .
Отредактировано 11.09.2019 15:15 Passerby . Предыдущая версия .
Отредактировано 11.09.2019 15:13 Passerby . Предыдущая версия .
Отредактировано 11.09.2019 14:57 Passerby . Предыдущая версия .
Re[3]: Порядок инициализации
От: Somescout  
Дата: 11.09.19 18:16
Оценка: 2 (1)
Здравствуйте, Passerby, Вы писали:

static WebProxy p = new WebProxy("93.189.149.200", 41965) { Credentials = new NetworkCredential("логин", "пароль") };
static HttpClientHandler handler = new HttpClientHandler { Proxy = p };
protected static HttpClient client = new HttpClient(handler);

P>Установил свойство WindowsProxyUsePolicy=WindowsProxyUsePolicy.UseCustomProxy все заработало.

А зачем вообще эти поля сделаны статичными?

P>Теперь как бы проверить, что запрос идет через прокси, а не напрямую.


Если хочется 100% убедиться в этом — ставьте Wireshark, в фильтр вписывайте ip.dst_host==93.189.149.200 (так, кажется), если пакет появится — то прокси работает.
ARI ARI ARI... Arrivederci!
Re[4]: Порядок инициализации
От: Passerby  
Дата: 11.09.19 23:19
Оценка:
Здравствуйте, Somescout, Вы писали:
S>А зачем вообще эти поля сделаны статичными?
А что в этом плохого?

Программа была запущена до добавления WindowsProxyUsePolicy=WindowsProxyUsePolicy.UseCustomProxy в варианте из первого сообщения, когда не возникает ошибки. Посмотрел ошибки, оказалось, что за сутки работы была одна ошибка с HttpStatusCode равным 502
https://docs.microsoft.com/ru-ru/dotnet/api/system.net.httpstatuscode?view=netcore-3.0
"BadGateway 502
Эквивалент HTTP-состояния 502. Значение BadGateway указывает, что промежуточный прокси-сервер получил неправильный ответ от другого прокси или исходного сервера."

Т.е. судя по ошибке прокси все же работает, хотя не должен бы был, т.к.
KRT>В первом случае во время инициализации httpClient Proxy равна null и тогда запросы идут напрямую.
Конечно, можно предположить, что провайдер пускает поток через прокси, но вряд ли. Совсем не понятно.
Отредактировано 11.09.2019 23:20 Passerby . Предыдущая версия .
Re[5]: Порядок инициализации
От: Somescout  
Дата: 12.09.19 21:05
Оценка:
Здравствуйте, Passerby, Вы писали:

S>>А зачем вообще эти поля сделаны статичными?

P>А что в этом плохого?

Я не претендую на профессиональное знание NetFramework, но вроде всегда и везде статичные поля использовались для хранения разделяемого состояния. У вас все эти поля используются для одиночного запроса и на первый (и второй) взгляд делать их статичными нет никакого смысла.
ARI ARI ARI... Arrivederci!
Re[6]: Порядок инициализации
От: Passerby  
Дата: 13.09.19 00:14
Оценка:
Здравствуйте, Somescout, Вы писали:
S>Я не претендую на профессиональное знание NetFramework, но вроде всегда и везде статичные поля использовались для хранения разделяемого состояния. У вас все эти поля используются для одиночного запроса и на первый (и второй) взгляд делать их статичными нет никакого смысла.
Мне казалось, что делать в классе что-то не статик требуется для того, чтобы разные объекты этого класса могли использовать разные значения. А если этого не требуется, то предпочтений выбора статик или не статик не знаю. Интересно, почему не статик в таком случае является предпочтительным?
Что касается http/2. Сделал запрос https://2ip.ru/ Как и писал KRT в первом случае запрос идет не через прокси. А если правильная инициализация (когда была ошибка) и указать WindowsProxyUsePolicy=WindowsProxyUsePolicy.UseCustomProxy то 2ip.ru возвращает адрес прокси.
Теперь бы разобраться, что лучше указывать: WindowsProxyUsePolicy=WindowsProxyUsePolicy.UseCustomProxy или UseWinHttpProxy или UseWinInetProxy
Отредактировано 13.09.2019 1:09 Passerby . Предыдущая версия . Еще …
Отредактировано 13.09.2019 1:07 Passerby . Предыдущая версия .
Re[7]: Порядок инициализации
От: okon  
Дата: 15.09.19 02:46
Оценка: -1
Здравствуйте, Passerby, Вы писали:

P>Мне казалось, что делать в классе что-то не статик требуется для того, чтобы разные объекты этого класса могли использовать разные значения.


С т.з. читаемости кода статик в данном случае это плохой вариант.
Т.е. гляда на эту строку нет понимания что это используется только в одном месте, возникает предположение что это объекты используются в разных статичных методах.
А это в свою очередь намекает на небезопасность в многопоточной реализации.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re[8]: Порядок инициализации
От: Passerby  
Дата: 15.09.19 17:25
Оценка:
Здравствуйте, okon, Вы писали:
O>Т.е. гляда на эту строку нет понимания что это используется только в одном месте, возникает предположение что это объекты используются в разных статичных методах.
Так все же симметрично: не статик вызывает предположение, что эти свойства разные в разных классах и используются в разных не статик методах.

O>А это в свою очередь намекает на небезопасность в многопоточной реализации.

Про намек поподробней.
Re[9]: Порядок инициализации
От: okon  
Дата: 16.09.19 09:23
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Здравствуйте, okon, Вы писали:

O>>Т.е. гляда на эту строку нет понимания что это используется только в одном месте, возникает предположение что это объекты используются в разных статичных методах.
P>Так все же симметрично: не статик вызывает предположение, что эти свойства разные в разных классах и используются в разных не статик методах.

Имеется ввиду если их вообще убрать из полей и использовать как локальные переменные в методе — тогда будет очевидно что они используются только в этом методе из других мест к ним доступа нет.

O>>А это в свою очередь намекает на небезопасность в многопоточной реализации.

P>Про намек поподробней.

Ну если у тебя инстанс какого-то объекта может быть вызван из разных потоков, например HttpClient, то есть большая вероятность что они начнут мешать друг другу и использовать какое-либо поле/состояние объекта одновременно, будут писать/читать, тем самым получится непредсказуемое поведение.
Поэтому безопаснее создавать инстанс каждый раз, в твоем случае локально в процедуре, тем самым гарантируя что если процедуру вызовут из двух разных потоков то у каждого HttpClient будет свое состояние.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re[10]: Порядок инициализации
От: Passerby  
Дата: 16.09.19 20:02
Оценка: 10 (1)
Здравствуйте, okon, Вы писали:
O>Имеется ввиду если их вообще убрать из полей и использовать как локальные переменные в методе — тогда будет очевидно что они используются только в этом методе из других мест к ним доступа нет.

O>Ну если у тебя инстанс какого-то объекта может быть вызван из разных потоков, например HttpClient, то есть большая вероятность что они начнут мешать друг другу и использовать какое-либо поле/состояние объекта одновременно, будут писать/читать, тем самым получится непредсказуемое поведение.

O>Поэтому безопаснее создавать инстанс каждый раз, в твоем случае локально в процедуре, тем самым гарантируя что если процедуру вызовут из двух разных потоков то у каждого HttpClient будет свое состояние.
Объект класса HttpClient большой и создавать его в каждом новом методе накладно. Кроме того есть рекомендация делать один такой объект на все вызовы. Ничего не может помешать вызывать его из разных потоков (что я и делаю).
Отредактировано 16.09.2019 20:04 Passerby . Предыдущая версия .
Re[11]: Порядок инициализации
От: okon  
Дата: 16.09.19 22:18
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Здравствуйте, okon, Вы писали:

O>>Имеется ввиду если их вообще убрать из полей и использовать как локальные переменные в методе — тогда будет очевидно что они используются только в этом методе из других мест к ним доступа нет.

O>>Ну если у тебя инстанс какого-то объекта может быть вызван из разных потоков, например HttpClient, то есть большая вероятность что они начнут мешать друг другу и использовать какое-либо поле/состояние объекта одновременно, будут писать/читать, тем самым получится непредсказуемое поведение.

O>>Поэтому безопаснее создавать инстанс каждый раз, в твоем случае локально в процедуре, тем самым гарантируя что если процедуру вызовут из двух разных потоков то у каждого HttpClient будет свое состояние.
P>Объект класса HttpClient большой и создавать его в каждом новом методе накладно. Кроме того есть рекомендация делать один такой объект на все вызовы. Ничего не может помешать вызывать его из разных потоков (что я и делаю).

Мы сейчас про читабельность , если используемый класс так спроектирован что мешает выполнить функциональные требования, то да придется пожертвовать читабельностью, функционал важнее.
Насчет помешать вызывать из разных потоков — у тебя поле доступно для записи, и потенциально этот инстанс может быть изменен, это и читабельность и дизайн ( лучше запрещать запись если предполагается что объект создается один раз ).

Насчет специфики HttpClient, в документации действительно пишут что лучше создавать один раз, при этом также указывают методы которые ThreadSafe,
  HttpClient
[q]
he following methods are thread safe:

CancelPendingRequests
DeleteAsync
GetAsync
GetByteArrayAsync
GetStreamAsync
GetStringAsync
PostAsync
PutAsync
SendAsync

но у HttpClient намного больше методов и есть шанс нарваться не на thread safe.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re[12]: Порядок инициализации
От: Passerby  
Дата: 16.09.19 23:40
Оценка: +1 :)
Здравствуйте, okon, Вы писали:

O>Мы сейчас про читабельность

а начиналось со статик
Re[13]: Порядок инициализации
От: okon  
Дата: 17.09.19 01:57
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Здравствуйте, okon, Вы писали:


O>>Мы сейчас про читабельность

P>а начиналось со статик

и начиналось с читабельности

С т.з. читаемости кода статик в данном случае это плохой вариант.


Проблемы с многопоточностью и возможность перезаписи статического иннстанса мне кажется более важными для обсуждения
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Отредактировано 17.09.2019 1:59 okon . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.