Как реализовать собственный прокси-сервер?

В конечном счете, я хочу блокировать загрузки в элементе управления .NET WebBrowser , фактически ограничивая его отображением HTML, изображений, сценариев и т.п., но никогда, никогда не отображать диалоговое окно «Сохранить как» для пользователя.

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

Я нашел класс HttpListener, но есть две проблемы с ним:

  1. Слушателю требуется предопределенный набор префиксов, которые его запускают. Однако я бы предпочел не жестко кодировать имена хостов или номера портов в своем приложении и оставить его универсальным.
  2. Мне пришлось бы реализовать код, который выполняет фактический запрос самостоятельно - нет ли чего-то, что делает это для меня, где я могу просто подключиться к строке, изучить содержимое ответа и изменить его по мере необходимости (например, фильтр запроса в J2EE делает на стороне сервера)?

Обновлять

Хорошо, я думаю, мне нужно прояснить это: мое приложение .NET (богатый клиент) используется в нескольких проектах, которые также имеют веб-приложения. Приложение .NET включает повторно используемую универсальную форму с элементом управления WebBrowser. Другие разработчики используют эту форму для интеграции доступа к своим веб-приложениям в приложение .NET.

Я хочу заблокировать загрузку, поэтому я хочу, чтобы моя форма WebBrowser перехватывала весь трафик и гарантировала, что он не приведет к диалоговому окну «Сохранить как». Таким образом...

  • Я не знаю, какие имена хостов будут
  • Веб-браузер указывает на настоящие URL-адреса, пользователь щелкает обычные ссылки, запускает JavaScript... все это обслуживается веб-приложением.
  • Если бы веб-браузер указывал на локальный хост (как было предложено), мне пришлось бы анализировать ответы и переписывать все ссылки, чтобы они снова указывали на локальный хост, сохраняя исходный URL-адрес. Я не хочу проходить через эти хлопоты.

person Jens Bannmann    schedule 03.02.2009    source источник


Ответы (2)


HttpListener должно быть хорошо, но это просто оболочка для http.sys, и эта библиотека доступна только в Windows XP и выше.

Префиксы HTTP

Вам нужен только один префикс http://127.0.0.1:8080/, потому что он предназначен только для вашего локального веб-контроля. В качестве альтернативы поддерживаются подстановочные знаки, такие как http://*:8080/, но нет причин использовать их в вашем случае.

Йенс Баннманн написал:

Приложения, к которым осуществляется доступ, находятся не на localhost, они могут быть где угодно. Вот почему я не хочу ничего жестко кодировать.

что вы имеете в виду под приложениями? вы имеете в виду сайты? это нечто совершенно другое, ваш специальный прокси-сервер будет прослушивать HttpListenerRequests на http://127.0.0.1:8080/, и поэтому ваш веб-контроль должен использовать прокси-сервер http://127.0.0.1:8080/. На данный момент все еще находится на локальной машине.

Преобразование между HttpListenerRequest/Response и HttpWebRequest/Response

Преобразуйте каждый входящий HttpListenerRequest в HttpWebRequest, запросите ответ, и вы получите объект HttpWebResponse, проанализируйте его, разрешен ли ответ для вашего элемента управления WebBrowser, и если да, запишите его в объект HttpListnererResponse, в противном случае напишите что-то еще (статус ошибки).

Вероятно, это самый простой способ создать собственный прокси-сервер на .NET.

Йенс Баннманн написал:

Правильно, это преобразование было тем, чего я хотел избежать. Или я могу сделать это всего несколькими строками кода? Глядя на API, он выглядит более сложным.

На самом деле это довольно просто, потому что протокол http тривиален. В основном он состоит из трех частей.

  • Строка запроса (содержит URL, метод http и версию http)
  • Заголовки (на самом деле это то, что делает API таким огромным и важным, но на самом деле все эти свойства и методы являются просто тонким слоем поверх необработанных заголовков http. Все, что вам нужно сделать, это скопировать все заголовки напрямую в общий путь от HttpListenerRequest к HttpWebRequest. Оба класса имеют общее свойство Headers для необработанного доступа)
  • Тело сообщения (просто скопируйте его содержимое, если оно есть)

Полное преобразование будет выглядеть примерно так:

HttpListenerRequest listenerRequest;

WebRequest webRequest = WebRequest.Create(listenerRequest.Url);
webRequest.Method = listenerRequest.HttpMethod;
webRequest.Headers.Add(listenerRequest.Headers);
byte[] body = new byte[listenerRequest.InputStream.Length];
listenerRequest.InputStream.Read(body, 0, body.Length);
webRequest.GetRequestStream().Write(body, 0, body.Length);

WebResponse webResponse = webRequest.GetResponse();

Если вам нужна дополнительная помощь по протоколу http, обратитесь к этой статье в Википедии.

Вы также можете проверить этот проект с открытым исходным кодом, чтобы узнать, как это сделать. Это не зависит от класса HttpListener, но это законченное решение для прокси-сервера, и его легко модифицировать под ваши нужды.

person Community    schedule 03.02.2009
comment
1. Приложения, к которым осуществляется доступ, находятся не на localhost, они могут быть где угодно. Вот почему я не хочу ничего жестко кодировать. 2. Верно, этого преобразования я хотел избежать. Или я могу сделать это всего несколькими строками кода? Глядя на API, он выглядит более сложным. - person Jens Bannmann; 03.02.2009
comment
что вы имеете в виду под приложениями? вы имеете в виду сайты? Ага, сайты. Когда форма отображается, веб-браузер указывает на URL-адрес, скажем, foo/app. Затем пользователь работает с приложением и переходит по ссылкам, например. foo/app/x.do. Как здесь работает локальный хост? - person Jens Bannmann; 03.02.2009
comment
ваш веб-браузер никогда не связывается с этими веб-сайтами напрямую, он всегда делегирует запрос вашему специальному прокси-серверу, работающему на локальном хосте. Я думаю, вам придется начать писать код, чтобы полностью понять, как все это сочетается друг с другом. - person lubos hasko; 03.02.2009
comment
Привет, используя опубликованный код преобразования, я всегда получаю исключение: заголовок «Хост» не может быть изменен напрямую. Имя параметра: имя - person Tute; 06.02.2009
comment
Не все заголовки можно установить с помощью Headers.Add(), у них есть свои свойства. - person Vladimir Dyuzhev; 25.03.2009
comment
Возможно, стоит упомянуть, что InputStream.Length — это long, а byte.Length — это муравей int. На практике это, вероятно, не будет иметь значения в 99% случаев, но если пользователь делает что-то безумное, например, отправляет файл >2 ГБ, написанный пример может дать сбой. - person jmbpiano; 11.01.2017

Возможно прокси с открытым исходным кодом?

http://www.mentalis.org/soft/projects/proxy/

person Greg Ogle    schedule 03.02.2009