![]() |
|
Модераторы: Snowy, Poseidon, MetalFan |
![]()
|
|
| kanaris |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
Задача такова. Хочу написать программу, которая посредством TClientSocket и TServerSocket будет пропускать через себя запрос и ответ таким образом:
1) программа висит на каком-нибудь порту (например 888) в режиме сервера (принимает запросы); 2) пользователь в браузере заходит на адрес http://localhost:888/ (т.е. браузер посылает запрос в программу); 3) программа получила запрос от браузера, и отправила его на сайт (например htmlbook.ru), поменяв в запросе строчку "Host: localhost:888" на "Host: htmlbook.ru"; 4) дождавшись ответа от htmlbook.ru, программа в точности передает его в браузер. Т.е. получается, что пишешь в браузере http://localhost:888 и тебе открывается сайт htmlbook.ru. Т.е. выполняется "ретрансляция" запроса. Должно поддерживаться несколько соединений (т.к. браузер открывает сразу несколько запросов с сервером). Как это реализовать? (прокси не предлагайте, это не совсем то) |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
||||
|
||||
| kanaris |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
Попробовал реализовать через TIdMappedPortTCP. Здесь вообще все просто, выполняется тоже самое. Но как подменить адрес в запросе?
|
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
Вообще - URL это отдельная история - бывают абсолютные, относительные, с "нестандартным" синтаксисом... Сделал маппер, удовлетворяющий твоим требованиям (код во вложении), но: 1. При вводе в строку адреса "http://localhost:port" только ПЕРВЫЙ запрос (и ответ, соответственно) идет через маппер, все остальное браузер сам начинает получать с этого URL (тестировалось на примере yandex.ru и rambler.ru). Исключение составляют их короткие адреса - ya.ru и r0.ru, и то - далеко не все соединения браузера идут через маппер. 2. При вводе в строку адреса "http://localhost:port" браузер отправляет ОТНОСИТЕЛЬНЫЙ запрос, который выглядит как "GET / HTTP/1.1.........". Оно и понятно - чего ему возиться с полными адресами, когда localhost - он под боком? Ну и ... в общем, сам посмотришь. P.S. Код - под D7, в D2009 работать не будет (гарантия 99%). Присоединённый файл ( Кол-во скачиваний: 16 )
HTTPMapper.zip 4,40 Kb |
|||
|
||||
| kanaris |
|
||||||||||||||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
kami, большое спасибо за программу (все работает, у меня дельфи 7). Но есть одно но: загружается только главная страница, а остальные не грузятся, пишется Not found.
Попытался проснифать трафик между программой и удаленным сервером, понял в чем проблема: Запрос главной страницы:
- здесь, судя по первой строке, веб-сервер понял, о каком хосте идет речь, поэтому строку "Host: localhost:99" проигнорировал. Запрос других страниц:
здесь в адресе хост не указан, а в параметре Host стоит значение localhost:99. Сервер не понял, с какого хоста загружать, поэтому грузил по умолчанию, где естественно такого файла нет, на что он выдал ответ:
Дело в том, что на одном сервере (91.203.4.180) может находиться несколько хостов. Поэтому, чтобы сервер понял, о каком именно хосте идет речь, нужно указывать это в параметре Host. Правильно было бы отсылать такие заголовки: Запрос главной страницы:
Запрос других страниц:
Насколько я понял, все дело в функции TRemoteConnection.TranslateBuffer. В комментариях написано:
Дальше после этого количества байт не идет новый заголовок, а просто заканчивается транзакция (а остальные страницы загружаются через другие соединения). Размер тела в байтах указывается для того, чтобы на стороне клиента было известно, сколько весит страница (файл), чтобы судить, сколько % скачалось.
На самом деле все гораздо проще, не нужно ничего парсить, нужно просто поменять строку Host: localhost:99 на Host: (хост сервера) (в заголовках, до #13#10#13#10) и тогда все будет работать. А после тела больше не идет никаких заголовков. Напишите пожалуйста код, как заменить эту строку? p.s. Извините за глупые вопросы, последний раз я кодил на дельфи в 2006-м году, я уже ничего не помню как что делается. Для меня в дельфи будет сложно даже такие простые вещи, как замена подстроки в строке (я сам работаю в php, там намного все проще). Это сообщение отредактировал(а) kanaris - 7.12.2009, 14:12 |
||||||||||||||
|
|||||||||||||||
| Демо |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1278 Регистрация: 3.11.2005 Репутация: 3 Всего: 50 |
kanaris,
Тебя же kami предупредил уже: Соответственно и получаешь эффекты. Решение - прокси-сервер. -------------------- |
|||
|
||||
| kanaris |
|
||||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
я увидел
решение уже почти готово (thx to kami), осталось только заменить одну строчку. Я бы сам поискал заменил, но я не понял, откуда берутся данные, как они меняются, и куда они отправляются. Я только понял, что данные передаются в функцию TRemoteConnection.TranslateBuffer параметром var Buffer: string. Не понял, откуда взялись переменные FCurrentBodyLength, FCurrentHeaderLength, еще не понял что делается с переменной Result. |
||||
|
|||||
| kami |
|
||||||||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
Достаточно подробный анализ.
Понятно. Потому я и сказал - работал только с конкретными случаями, когда знал, что на что нужно подменять.
Совершенно верно. Именно в нее приходит полный заголовок запроса, и после изменения отправляется серверу. Вот это как раз не проблема. Только яндексу это до одного места Итак, необходимые изменения 1. Слегка переделываем конструктор TRemoteConnection:
Не забудь переправить объявление и в секции interface, и внести дополнительное поле FOldPort в его private секцию. Не внесешь - Delphi будет ругаться, так что "пропустить" не получится 2. Изменения в
3. Изменения в серверную часть:
|
||||||||||
|
|||||||||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
С "локальной" стороны при активации программы создается серверный сокет (модуль uMapper). Когда браузер инициирует соединение с нашим серверным сокетом в процедуре OnConnect уже мы создаем "внешнее" соединение с указанным хостом перенаправления. В этой же процедуре OnConnect внешнему соединению сообщаются координаты локального (для того, чтобы данные, принимаемые из внешнего соединения можно было бы передавать ему), а сокет локального соединения соответственно знает координаты внешнего (Содержится в TCustomWinSocket.Data). Сперва в передающий буфер мы накапливаем заголовок (то, что во время тестирования заголовок приходит за одно чтение еще ничего не значит - читать про разбиение и склейку TCP пакетов). Когда TranslateBuffer определяет, что заголовок (из браузера через серверный сокет в TremoteConnection) поступил полностью, то: 1. подменяется Host: бла-бла-бла на нужное значение 2. определяется, не собирается ли передаваться кроме заголовка еще и данные (Content-length). Размер текущего заголовка и размер его данных записывается в соответствующие переменные. Это делается потому, что заголовок и его данные могут не передаться за один прием внешнему серверу (не зря методы Read и Write сокетов - это функции). Объяснил уже в п.1 Если ты про результат TranslateBuffer, то в нем определяется, сколько байт из буфера на передачу можно отправить во внешнее соединение и соответственно - удалить их из буфера. Удаление из буфера на передачу производится непосредственно при отправке, по фактическому количеству переданного. Забыл добавить - переменные FCurrentBodyLength, FCurrentHeaderLength определяют, сколько данных из текущего заголовка и сопровождаемых им данных осталось передать. Вторая, и более важная задача - определить момент начала нового заголовка (когда и FCurrentBodyLength и FCurrentHeaderLength станут =0) Это сообщение отредактировал(а) kami - 7.12.2009, 21:00 |
|||
|
||||
| kanaris |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
Теперь работает. Но, с методом GET все нормально, а вот с POST - проблемы. При отправке POST-запроса, выполнение программы обрывается, выскакивает след. ошибка:
Например, хост в настройках указать example.com, зайти по ссылке http://localhost:99/relax/tests/ и нажать в тесте "Результаты". Что это может быть? Upd: В пост-запросе, помимо заголовков, есть еще и тело (т.е. оно не пустое в отличии от метода GET). Может быть с этим связано. Это сообщение отредактировал(а) kanaris - 16.4.2015, 21:35 |
|||
|
||||
| kami |
|
||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
Вообще-то тело обрабатывается в той же функции TranslateBuffer. Да, я немного напортачил там. Но что мешает пройтись отладчиком, тем более что проблема обнаруживается сразу же?
|
||||
|
|||||
| kanaris |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 70 Регистрация: 16.2.2008 Репутация: нет Всего: -1 |
||||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
При трассировке Post-запроса выясняется, что неправильно определялись положения начала и конца Content-Length. Из-за этого в FCurrentBodyLength пытались записать StrToInt(пустая_строка), что само собой вызывало исключение. В приведенном в предыдущем посте коде это исправлено (правильно вычисляются индексы начала и конца символов, содержащих "длину тела". Добавлено через 5 минут и 51 секунду На всякий случай - полный вариант со всеми исправлениями. Присоединённый файл ( Кол-во скачиваний: 19 )
HTTPMapper.zip 4,30 Kb |
|||
|
||||
![]()
|
| Правила форума "Delphi: Сети" | |
|
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делится вскрытыми компонентами
Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Delphi: Сети | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |