Конференция "Сети" » IdHTTPProxyServer и размер ресурса
 
  • kernel © (01.07.08 20:06) [0]
    Доброго времени суток, уважаемые.

    Бьюсь вторые сутки над "выдачей" информации о файле перед скачиванием через "испеченный" мною на основе  IdHTTPProxyServer прокси сервере.
    Т.е. при скачивании файла через свой прокси, например, с помощью IE, должно сначала появиться окно, в котором мне будет хотя бы известен размер файла, после чего, в случае моего согласия, файл скачивается. Так вот дело в том, что мне не известен размер файла, пока сам этот файл не скачается полностью (например, в IE окошко сохранения файла появляется после полного скачивания этого файла), хотя без прокси все нормально. В самом прокси я передаю в заголовке ответа Content-Length, после чего само тело файла.
    Кто-нибудь знает, как устранить эту проблемку?
    ЗЫ: извиняюсь, если что-то коряво написал - у меня уже ночь, голова не соображает :)
  • umbra © (01.07.08 23:44) [1]

    > Т.е. при скачивании файла через свой прокси, например, с
    > помощью IE, должно сначала появиться окно, в котором мне
    > будет хотя бы известен размер файла, после чего, в случае
    > моего согласия, файл скачивается.


    Тут дело не в прокси-сервере, а в клиенте. Т.е. при получении ответа с кодом 200 (и с указанием размера в заголовках), клиент должен показывать табличку :)
    Прокси-сервер тут ни причем. Хотя табличку может показывать и он, но тогда это не будет иметь отношения к клиенту, собственно скачивающему этот файл. Да и не дело это для сервера - таблички показывать :)
  • Поросенок Винни-Пух © (01.07.08 23:51) [2]
    (например, в IE окошко сохранения файла появляется после полного скачивания этого файла),

    ну это ты сам так написал свой прокси.
  • umbra © (02.07.08 12:35) [3]
    в принципе есть запрос HEAD, предназначенный для получения информации о ресурсе. Прокси может слать сначала его, показывать табличку :) (хотя и не его это дело), а потом уже, в зависимости от реакции пользователя, слать или не слать GET
  • Поросенок Винни-Пух © (02.07.08 12:58) [4]
    да у него в дизайне кода проблема.
    получив ссылку от браузера он пока его на прокси не выкачает, к передаче обратно в браузер не переходит.
  • Поросенок Винни-Пух © (02.07.08 13:00) [5]
    этож какие таймауты надо разрешать в браузере чтобы через такой замечательный прокси качать что-нибуть большее чем testgirl.jpg в 100 кб
  • kernel © (02.07.08 13:19) [6]

    > Поросенок Винни-Пух ©   (02.07.08 12:58) [4]
    >
    > да у него в дизайне кода проблема.
    > получив ссылку от браузера он пока его на прокси не выкачает,
    >  к передаче обратно в браузер не переходит.


    Вообще IdHTTPProxyServer читает требуемые данные в Stream с запрашиваемого сервера, затем отсылает это браузеру. Но заголовок ответа (включая Content-Length) отсылается серверу до сбрасывания данных в/из stream:

    {Например, при обработке GET:    }
    var
     LClient: TIdTCPClient;
     LDocument: string;
     LHeaders: TIdHeaderList;
     LRemoteHeaders: TIdHeaderList;
     LURI: TIdURI;
     LPageSize: Integer;
    begin
     ASender.PerformReply := false;
     LHeaders := TIdHeaderList.Create; try
       ASender.Context.Connection.IOHandler.Capture(LHeaders, '');
       LClient := TIdTCPClient.Create(nil); try
         LURI := TIdURI.Create(ASender.Params.Strings[0]); try
           LClient.Port := Sys.StrToInt(LURI.Port, 80);
           LClient.Host := LURI.Host;
           //We have to remove the host and port from the request
           LDocument := LURI.Path + LURI.Document + LURI.Params;
         finally Sys.FreeAndNil(LURI); end;
         LClient.Connect; try
           LClient.IOHandler.WriteLn('GET ' + LDocument + ' HTTP/1.0'); {Do not Localize}
           LClient.IOHandler.Write(LHeaders);
           LClient.IOHandler.WriteLn('');
           LRemoteHeaders := TIdHeaderList.Create; try
             LClient.IOHandler.Capture(LRemoteHeaders, '');
    // вот заголовок отправляется:   ==============================
             ASender.Context.Connection.IOHandler.Write(LRemoteHeaders);
             ASender.Context.Connection.IOHandler.WriteLn('');
    // ======================================================
             LPageSize := Sys.StrToInt(LRemoteHeaders.Values['Content-Length'], -1) ; {Do not Localize}

    // вот ресурс отправляется:   ================================
             TransferData(LClient, ASender.Context.Connection, LDocument, LPageSize, LRemoteHeaders);
    // ======================================================
           finally Sys.FreeAndNil(LRemoteHeaders); end;
         finally LClient.Disconnect; end;
       finally Sys.FreeAndNil(LClient); end;
     finally Sys.FreeAndNil(LHeaders); end;




    > umbra ©   (02.07.08 12:35) [3]
    >
    > в принципе есть запрос HEAD, предназначенный для получения
    > информации о ресурсе. Прокси может слать сначала его, показывать
    > табличку :) (хотя и не его это дело), а потом уже, в зависимости
    > от реакции пользователя, слать или не слать GET

    Почему то браузер не просит информацию о ресурсе HEAD`ом. Я даже сам настоятельно ему отправлял эту информацию, не спрашивая у него разрешения - не помогло :)
  • umbra © (02.07.08 13:38) [7]

    > Почему то браузер не просит информацию о ресурсе HEAD`ом

    а чего он должен ее просить? не для этого его делали :)
    Надо писат плагин/надстройку. Непонятно только, причем здесь прокси.
  • kernel © (02.07.08 13:57) [8]
    Бррр... И все же, когда мы скачиваем файл без прокси, нам известно какого он размера (см. окошко в браузере). А известно браузеру размер файла из-за полученного Content-Length. Прокси тоже сначала отправляет заголовок с Content-Length, но почему-то браузер не реагирует :/ Все это работает и без HEAD.
  • umbra © (02.07.08 15:18) [9]
    значит прокси кривой. Как у вас происходит отдача браузеру? вы видно и правда  сначала все на прокси закачиваете, хотя хттп прокси должен создавать туннель, покоторому все идет без задержек.
  • Поросенок Винни-Пух © (02.07.08 15:42) [10]
    И все же, когда мы скачиваем файл без прокси, нам известно какого он размера

    И что же здесь странного?
    То что контент ленс не только при подаче head отдается?
  • Поросенок Винни-Пух © (02.07.08 15:43) [11]
    Вообще IdHTTPProxyServer читает требуемые данные в Stream с запрашиваемого сервера, затем отсылает это браузеру

    ну как я и говорил.
    пока твой прокси не всосет четырехгиговый dvd рип целиком, браузер ни байта от него не получит.
  • kernel © (02.07.08 15:45) [12]

    > значит прокси кривой. Как у вас происходит отдача браузеру?
    >  вы видно и правда  сначала все на прокси закачиваете, хотя
    > хттп прокси должен создавать туннель, покоторому все идет
    > без задержек.

    Само тело ресурса закачивается на прокси, после чего отдается клиенту:

    ...
    var
     LStream: TIdStream;
    begin
     //TODO: Have an event to let the user perform stream creation
     LStream := TIdMemoryStream.Create; try
       ASrc.IOHandler.ReadStream(LStream, ASize, ASize = -1);
       LStream.Position := 0;
       DoHTTPDocument(ADocument, LStream, AHeaders); // TyT Hu4ero oco6eHHoro
       // Need to recreate IdStream, DoHTTPDocument passes it as a var and user can change the
       // stream that is returned
       ADest.IOHandler.Write(LStream);
     finally Sys.FreeAndNil(LStream); end;
    ...



    Собственно, все это - стандартный код IdHTTPProxyServer (Indy 10), исходники которого есть в самом Delphi (2006 и т.д.).

    Сам заголовок отдается раньше тела запрашиваемого ресурса (см. пост kernel ©   (02.07.08 13:19) [6]).
  • kernel © (02.07.08 15:47) [13]
    > Поросенок Винни-Пух ©   (02.07.08 15:43) [11]
    > ну как я и говорил.пока твой прокси не всосет четырехгиговый
    > dvd рип целиком, браузер ни байта от него не получит.

    Как же тогда браузер узнает размер файла, даже без прокси?
  • Поросенок Винни-Пух © (02.07.08 15:50) [14]
    Как же тогда браузер узнает размер файла, даже без прокси?

    из заголовка ответа конечно же.
  • Поросенок Винни-Пух © (02.07.08 15:54) [15]
    ты наверное думаешь, что ие действует так:
    посылает гет или пост, читает заголовок, останавливает закачку, показывает окно с кнопкой отмена и пока не нажмешь ок не начинает качать сам контент.

    только браузер делает не так да и кнопки "ок" там нет.
    предположу, что получив заголовок и начав качать само тело файла, он через некоторое время асинхронно показывает окошко, в котором можно прекратить закачку.
  • Поросенок Винни-Пух © (02.07.08 15:56) [16]
    а твой волшебный прокси отдает только загловок, потом забивает на браузер и занимается высасыванием файла.
  • umbra © (02.07.08 16:22) [17]

    > а твой волшебный прокси отдает только загловок, потом забивает
    > на браузер и занимается высасыванием файла

    да, TIdHTTPProxyServer сделан криво. Пока он не закачает весь ресурс (причем по умолчанию - в память :)), он ничего не отдает. А поскольку сокеты блокирующие, то и события никакие произойти не могут, пока весь ресурс не закачается. Его надо рихтовать и рихтовать, чтобы до ума довести :)
  • Поросенок Винни-Пух © (02.07.08 16:27) [18]
    там и события особо никакие не нужны.
    достаточно иметь два вложенных цикла чтения с сервера и отдачи браузеру.
    ну и читать/отдавать конечно блочно, а не в стрим.
  • Anatoly Podgoretsky © (02.07.08 16:50) [19]

    > Как же тогда браузер узнает размер файла, даже без прокси?

    Вопрос не правильный. А вот правильный

    Как же тогда браузер узнает размер файла, без моего прокси?
  • kernel © (03.07.08 19:46) [20]

    > Поросенок Винни-Пух ©   (02.07.08 16:27) [18]
    > ...
    > достаточно иметь два вложенных цикла чтения с сервера и
    > отдачи браузеру.


    А почему именно два цикла?

    В общем, сделал такое "по-блочное" чтение:

    var
     LStream: TIdStream;
     StreamBuf: TIdBytes;
     DocSize: Integer;
    begin
     LStream := TIdMemoryStream.Create; try
     ReqBlock := not RequestAllow(reqUrl);
     DocSize:=0;

     while (ASrc.IOHandler.Connected) and
      (ADest.IOHandler.Connected) do
      begin
      if ASrc.IOHandler.Readable then
       begin
       ASrc.IOHandler.ReadBytes(StreamBuf, -1, false);
       ADest.IOHandler.WriteDirect(StreamBuf);
       DocSize:=DocSize + SizeOf(StreamBuf);
       end;
      end;

    //  ПОЧЕМУ-ТО ДАЛЬШЕ (начиная с этой строки)
    //  НИЧЕГО НЕ ВЫПОЛНЯЕТСЯ

    end;



    Это единственное, что у меня почти заработало.
    Почему-то такой код работает через раз :(
    Складывается впечатление, что происходит где-то в цикле подвисание. Иногда страницы вообще не отдаются :(
  • kernel © (04.07.08 10:45) [21]
    Кто-нибудь может подсказать, как по-блочно читать? :)
  • umbra © (04.07.08 11:13) [22]

    > Кто-нибудь может подсказать, как по-блочно читать?

    поскольку сокеты блокирующие, то поблочно читать можно только многократными запросами диапазона байтов ресурса. Для файлов это нормально, для страничек - нет.
    Лучший, по-моему, выход - создать общий ресурс (типа временный файл) + два потока на клиента у прокси. Один читает ответ на запрос и пишет в файл, другой - читает записанное и шлет его клиенту. Но тут надо уже синхронизировать эти потоки так, чтобы тот, который отдает клиенту, не читал все правильно.
  • Поросенок Винни-Пух © (04.07.08 11:16) [23]
    поскольку сокеты блокирующие, то поблочно читать можно только многократными запросами диапазона байтов ресурса. Для файлов это нормально, для страничек - нет.

    не страницу поблочно тянуть, а из тсп соединения читать блоками
  • umbra © (04.07.08 11:32) [24]

    > а из тсп соединения читать блоками

    так оно ж потоковое. тогда всегда есть опасность, что сервер, с которого тянут может обидеться и отключиться :)

    Еще вариант - скачать инди поновее, потому как тот, для которого есть автоматический инсталлятор имеет версию 10.1.5, а текущая - 10.2.3. И в ней проксисервер сильно переделан и, возможно, доведен до ума. Недостаток в том, что устанавливать придется вручную.
    Скачать можно отсюда - ftp://indy.fulgan.com/ZIP/
  • kernel © (04.07.08 12:26) [25]

    > umbra ©   (04.07.08 11:32) [24]

    Спасибо за информацию, сейчас посмотрю новый инди :)
  • kernel © (06.07.08 17:31) [26]
    Собственно, проблема решилась установкой нового Инди, в котором можно задавать режим полнофайловой или потоковой передачи данных. Огромное спасибо umbra, ну и всем, кто откликнулся :)
  • имя (09.11.08 15:32) [27]
    Удалено модератором
 
Конференция "Сети" » IdHTTPProxyServer и размер ресурса
Есть новые Нет новых   [134433   +21][b:0][p:0.002]