HttpWebRequest истекает, если Thread.CurrentThread.CurrentCulture изменен - PullRequest
1 голос
/ 06 декабря 2011

Если вы измените Thread.CurrentThread.CurrentCulture на что-то отличное от установленного (культура ОС), вызов HttpWebRequest.GetResponse () всегда будет прерываться.

Просто для пояснения, вот содержимое моего служебного класса, который выполняет грязную работу:

private static void ReplicateCookies(HttpWebRequest request)
{
    if (HttpContext.Current == null)
        return;
    if (request.CookieContainer == null)
        request.CookieContainer = new CookieContainer();
    foreach (var cookie in from object key in HttpContext.Current.Request.Cookies.Keys select HttpContext.Current.Request.Cookies[key.ToString()])
        request.CookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, string.IsNullOrEmpty(cookie.Domain) 
            ? HttpContext.Current.Request.Url.Host
            : cookie.Domain));
}

private HttpWebRequest CreateRequest(string url, string method = null)
{
    var httpRequest = WebRequest.Create(url) as HttpWebRequest;
    ReplicateCookies(httpRequest);
    httpRequest.KeepAlive = false;
    httpRequest.Method = method ?? "GET";
    httpRequest.Timeout = 20000;
    httpRequest.Proxy = null;
    httpRequest.ServicePoint.ConnectionLeaseTimeout = 20000;
    httpRequest.ServicePoint.MaxIdleTime = 10000;
    return httpRequest;
}

public string ReadWebPage(string url)
{
    var oldCulture = Thread.CurrentThread.CurrentCulture.Name;
    Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
    string result;
    var httpRequest = CreateRequest(url);
    httpRequest.Headers.Add("Accept-Language", oldCulture);
    try
    {
        using (var httpResponse = httpRequest.GetResponse())
        {
            using (var stream = httpResponse.GetResponseStream())
            {
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    result = reader.ReadToEnd();
                    reader.Close();
                }
                stream.Flush();
                stream.Close();
            }
            httpResponse.Close();
        }
    }
    finally
    {
        httpRequest.Abort();
    }
    Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo(oldCulture);
    return result;
}

Если вам интересно:

  • в ReplicateCookies, если вы вызываете CookieCOntainer.Добавить со значением null в качестве домена он завершится с ошибкой во время выполнения
  • код уже содержит исправление, устраняющее проблему CurrentCulture. Продолжайте, выньте весь код, который изменяет культуру, установите что-то в вашей культуре потока (например, «fr-FR») и добавьте код.

Я убедился, что:

  • прокси не вмешивается в запрос
  • никакие настройки брандмауэра не запрещают подобные запросы

Возможно, стоит упомянуть, что это использовалось внутри веб-приложения MVC3 для внутренней генерации шаблонных сообщений, где шаблон сам является веб-страницей. Приложение устанавливает свою культуру следующим образом (Global.asax.cs):

protected void Application_AcquireRequestState()
{
    if (HttpContext.Current == null)
        return;
    if (HttpContext.Current.Session == null)
        return;
    if (HttpContext.Current.Session["Culture"] != null)
        CultureHelper.SetCulture(HttpContext.Current.Session["Culture"].ToString());
}

И вспомогательный класс:

public static class CultureHelper
{
    public static void SetCulture(string culture)
    {
        if (string.IsNullOrEmpty(culture))
            return;
        var cultureInfo = new CultureInfo(culture);
        if (culture.ToLower().Equals("fr-fr") || culture.ToLower().Equals("fr"))
            cultureInfo.NumberFormat.NumberGroupSeparator = cultureInfo.NumberFormat.CurrencyGroupSeparator = ".";
        Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = cultureInfo;
    }
}

Французский язык по умолчанию использует пробел в качестве разделителя тысяч по некоторым причинам - таким образом, оператор if.

Метод ReplicateCookies копирует cookie сеанса и cookie аутентификации во внутренний запрос, так что сеанс / аутентификация сохраняется в результирующем внутреннем запросе.

Сайт явно имеет средства для изменения текущей культуры (изменения значения сеанса). До исправления в методе ReadWebPage все работало нормально, пока не была изменена культура. После этого каждый HttpWebRequest.GetResponse будет время ожидания, пока культура не будет изменена обратно - в моем случае - en-GB.

Есть идеи, почему это происходит? Я мог бы сделать что-то совершенно неправильно где-то. Как я уже сказал, он работает в своем текущем состоянии. Это просто не очень красиво.

1 Ответ

2 голосов
/ 06 декабря 2011

Поскольку все попытки быстро удалить этот пост до того, как кто-либо его увидит, не увенчались успехом, вот решение. Как обычно, это было то, о чем вы никогда не думаете - сессия. Пока я рисую "Разбей свою голову здесь!" черная точка на листе бумаги, вот исправленный код:

private static void ReplicateCookies(HttpWebRequest request)
{
    if (HttpContext.Current == null)
        return;
    if (request.CookieContainer == null)
        request.CookieContainer = new CookieContainer();
    foreach (var cookieKey in HttpContext.Current.Request.Cookies.Keys)
    {
        if (!cookieKey.ToString().Equals("ASP.NET_SessionId"))
        {
            var cookie = HttpContext.Current.Request.Cookies[cookieKey.ToString()];
            request.CookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, string.IsNullOrEmpty(cookie.Domain)
                ? HttpContext.Current.Request.Url.Host
                : cookie.Domain));
        }

    }
}

private HttpWebRequest CreateRequest(string url, string method = null)
{
    var httpRequest = WebRequest.Create(url) as HttpWebRequest;
    ReplicateCookies(httpRequest);
    httpRequest.KeepAlive = false;
    httpRequest.Method = method ?? "GET";
    httpRequest.Timeout = 20000;
    httpRequest.Proxy = null;
    httpRequest.ServicePoint.ConnectionLeaseTimeout = 20000;
    httpRequest.ServicePoint.MaxIdleTime = 10000;
    httpRequest.Headers.Add("Accept-Language", Thread.CurrentThread.CurrentCulture.Name);
    return httpRequest;
}

public string ReadWebPage(string url)
{
    string result;
    var httpRequest = CreateRequest(url);
    try
    {
        using (var httpResponse = httpRequest.GetResponse())
        {
            using (var stream = httpResponse.GetResponseStream())
            {
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    result = reader.ReadToEnd();
                    reader.Close();
                }
                stream.Flush();
                stream.Close();
            }
            httpResponse.Close();
        }
    }
    finally
    {
        httpRequest.Abort();
    }
    return result;
}

Объяснение: проблема была в методе ReplicateCookies. Среди всего прочего это было переписывание куки сессии ASP. И поскольку он делал вызов тому же процессу, то позднее зависание (и в конечном итоге истекло время ожидания), потому что сеанс из исходного запроса был заблокирован - и входящий запрос пытался использовать тот же сеанс.

Теперь можно повесить эту большую черную точку магнита на стене ...

...