Подтвердить что ты не робот

Запросить веб-страницу в С#, обманывая Host

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

Я пробовал следующее:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Headers.Set("Host", "www.mywebsite.com");
WebResponse response = request.GetResponse();

Очевидно, что это не работает, поскольку я не могу обновить заголовок, и я не знаю, действительно ли это действительно так.

4b9b3361

Ответ 1

Мне удалось найти более длинный маршрут, используя сокеты. Я нашел ответ на странице MSDN для IPEndPoint:

string getString = "GET /path/mypage.htm HTTP/1.1\r\nHost: www.mysite.mobi\r\nConnection: Close\r\n\r\n";
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
Socket socket = null;
String strPage = null;
try
{
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.23.1.93"), 80);
    socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(ip);
}
catch (SocketException ex)
{
    Console.WriteLine("Source:" + ex.Source);
    Console.WriteLine("Message:" + ex.Message);
}
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);

while (bytes > 0)
{
    bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
    strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();

Ответ 2

Хотя это очень поздний ответ, возможно, кто-то может получить от него выгоду

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://192.168.1.1"));
request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, request.Headers, new object[] {"Host","www.mysite.com"});

Отражение - ваш друг:)

Ответ 3

У меня была проблема с URL-адресами, которые я использовал, имел несколько разных IP-адресов, я хотел называть каждый адрес отдельно, используя одно и то же имя DNS в хосте - в решении используется прокси:

string retVal = "";
            // Can't change the 'Host' header property because .NET protects it
            // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // request.Headers.Set(HttpRequestHeader.Host, DEPLOYER_HOST);
            // so we must use a workaround
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Proxy = new WebProxy(ip);
            using (WebResponse response = request.GetResponse())
            {
                using (TextReader reader = new StreamReader(response.GetResponseStream()))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                        retVal += line;
                }
            }
            return retVal;

Заголовок узла устанавливается с помощью "url" автоматически .NET, а "ip" содержит фактический адрес веб-сервера, с которым вы хотите связаться (вы также можете использовать имя DNS)

Ответ 4

Я знаю, что это старо, но я столкнулся с этой же самой точной проблемой, и я нашел лучшее решение для этого, используя сокеты или отражение...

То, что я сделал, это создать новый класс, который выполняет функции WebHeaderCollection и обходит проверку того, что вы в нее вставляете:

public class MyHeaderCollection:WebHeaderCollection
{
    public new void Set(string name, string value)
    {
        AddWithoutValidate(name, value);
    }
    //or
    public new string this[string name]
    {
        get { return base[name]; }
        set { AddWithoutValidate(name, value); }
    }
}

и вот как вы его используете:

    var http = WebRequest.Create("http://example.com/");
    var headers = new MyHeaderCollection();
    http.Headers = headers;

    //Now you can add/override anything you like without validation:
    headers.Set("Host", http.RequestUri.Host);
    //or
    headers["Host"] = http.RequestUri.Host;

Надеюсь, это поможет любому, кто ищет это!

Ответ 5

Я знаю, что это старый вопрос, но в эти дни вы можете это сделать.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Host = "www.mywebstite.com";
WebResponse response = request.GetResponse();

Ответ 6

Necromancing.

Для тех, кто еще на .NET 2.0
На самом деле это довольно легко, если вы знаете, как это сделать.

Проблема в том, что вы не можете установить заголовок хоста, потому что среда не позволит вам изменить значение во время выполнения. (.net framework 4.0+ позволит вам переопределить хост в httpwebrequest).

Следующая попытка будет включать заголовок с отражением - как показано в верхнем верхнем ответе здесь - обойти его, что позволит вам изменить значение заголовка. Но во время выполнения он будет переписать это значение с помощью хост-части URL, что означает, что отражение ничего не принесет вам, поэтому я не понять, почему люди держат вверх голосование.

Если имя dns не существует, что, откровенно говоря, является единственным случаем, в котором вы хотите сделать это в первую очередь, вы не можете его установить, поскольку .NET не может его решить, и вы не может переопределить распознаватель .NET DNS.

Но что вы можете сделать, это настройка веб-прокси с тем же IP-адресом, что и целевой сервер.

Итак, если ваш IP-адрес сервера 28.14.88.71:

public class myweb : System.Net.WebClient
{
    protected override System.Net.WebRequest GetWebRequest(System.Uri address)
    {
        System.Net.WebRequest request = (System.Net.WebRequest)base.GetWebRequest(address);
        //string host = "redmine.nonexistantdomain.com";

        //request.Headers.GetType().InvokeMember("ChangeInternal",
        //    System.Reflection.BindingFlags.NonPublic |
        //    System.Reflection.BindingFlags.Instance |
        //    System.Reflection.BindingFlags.InvokeMethod, null,
        //    request.Headers, new object[] { "Host", host }
        //);

        //server IP and port
        request.Proxy = new System.Net.WebProxy("http://28.14.88.71:80");

        // .NET 4.0 only
        System.Net.HttpWebRequest foo = (System.Net.HttpWebRequest)request;
        //foo.Host = host;

        // The below reflection-based operation is not necessary, 
        // if the server speaks HTTP 1.1 correctly
        // and the firewall doesn't interfere
        // https://yoursunny.com/t/2009/HttpWebRequest-IP/
        System.Reflection.FieldInfo horribleProxyServicePoint = (typeof(System.Net.ServicePoint))
            .GetField("m_ProxyServicePoint", System.Reflection.BindingFlags.NonPublic |
            System.Reflection.BindingFlags.Instance);

        horribleProxyServicePoint.SetValue(foo.ServicePoint, false);
        return foo; // or return request; if you don't neet this
    }


    }

и вуаля, теперь

myweb wc = new myweb();
string str = wc.DownloadString("http://redmine.netexistantdomain.com");

и вы вернете правильную страницу, если 28.14.88.71 - это веб-сервер с виртуальным хостингом на основе имени (на основе http-host-header).

Теперь у вас есть правильный ответ на исходный вопрос, как для WebRequest, так и для WebClient. Я думаю, что использование пользовательских сокетов для этого было бы неправильным подходом, особенно при использовании SSL, и когда фактическое решение будет таким простым...

Ответ 8

Заголовок "Host" защищен и не может быть изменен программно. Я полагаю, что обойти это можно, и вы можете попытаться привязать его к собственному свойству "InnerCollection" объекта WebRequest и вызвать метод "Установить" на нем "Добавить", чтобы изменить заголовок Host. Я не пробовал это, но, быстро взглянув на исходный код в Reflector, я думаю, что это легко выполнить. Но да, привязка к частным свойствам объектов инфраструктуры - не лучший способ добиться чего-то.:) Используйте, только если вы ДОЛЖНЫ.

edit: Или как другой парень упоминает связанный вопрос, просто откройте сокет и сделайте быстрый "GET" вручную. Не должно быть проблем, если вам не нужно возиться с другими вещами, такими как куки или любые другие тонкости, предоставляемые HttpWebRequest.