PDA

Просмотр полной версии : gzip decompression, browser (delphi)


ErrorNeo
21.03.2010, 20:39
вопрос:

есть html страница... скажем zalil.ru (для примера)
ответ от сервера при её запросе выглядит так:
HTTP/1.1 200 OK
Server: nginx/0.3.17
Date: Sun, 21 Mar 2010 16:22:39 GMT
Content-Type: text/html; charset=windows-1251
Transfer-Encoding: chunked
Connection: keep-alive

e90
<html>

<head>
<title>Хранение файла, бесплатно закачать и скачать</title>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" media="all" href="/style/default.css" />
</head>
<body>


<table id="container">

<tr>
<td id="content">
<div style="position:absolute; top:20px; right:20px; text-align:right;">

<form name="search" action="/page/search" method="get">
<input name="name" type="text" value="Поиск" style="width:200px;" onfocus="this.value=''" onblur="if(this.value=='')this.value='Поиск'">
</form>
<br>

Обладатели Flash Player могут <a href="/page/test">протестировать</a> новый аплоадер.<br><br>
В некоторых подсетях плохая скорость соединения.<br> Ведутся переговоры с хостером, надеемся разрешить эту проблему.
</div>

<div id="center">


<form action="/upload/" name="sendform" enctype="multipart/form-data" method="post" id="sendform" onSubmit="sending()">

<input type="file" name="file" size="53" onFocus="choice()" onclick="choice()">
<input type="submit" name="submit" value="&nbsp;&nbsp;Send&nbsp;&nbsp;" id="submit">
</form>

<p>Сервис позволяет разместить файл размером не более 50 Мб.<br>
Файл будет удален через 1 месяц после последнего скачивания.</p>


</div>


<script language="JavaScript">

var sendform;
var flag;

setTimeout(function() {
sendform=document.getElementById("sendform");
flag=1;
sendform.submit.disabled=true;
sendform.submit.value=' Send ';

}, 200);


function choice()
{
if(flag==1)
sendform.submit.disabled=false;
}

function sending()
{
sendform.submit.disabled=true;
sendform.submit.value="Sending";
flag=0;
}
</script>



</td>
</tr>
<tr>
<td id="copyright">


<!-- # ECTO*COUNTER-->
<script language="javascript">
document.write('<img s'+'rc="http://support.ecto.ru/counter.php'+
'?i=2&r='+escape(document.referrer)+
'" style="border:0;"/>');
</script>
<noscript>
<img src="http://support.ecto.ru/counter.php?i=2" border="0"/>
</noscript>
<!-- # ECTO*COUNTER-->


<span>&copy;2010 <a href="http://fhn.ru">fhn*createam</a></span>
<a href="/page/">О&nbsp;проекте</a>
<a href="mailto:ad@zalil.ru">Реклама</a>

</td>
</tr>
</table>


<div style="position:absolute;bottom:6;right:10;">
<!--Rating@Mail.ru COUNTEr--><script language="JavaScript" type="text/javascript"><!--
d=document;var a='';a+=';r='+escape(d.referrer)
js=10//--></script><script language="JavaScript1.1" type="text/javascript"><!--
a+=';j='+navigator.javaEnabled()
js=11//--></script><script language="JavaScript1.2" type="text/javascript"><!--
s=screen;a+=';s='+s.width+'*'+s.height
a+=';d='+(s.colorDepth?s.colorDepth:s.pixelDepth)
js=12//--></script><script language="JavaScript1.3" type="text/javascript"><!--
js=13//--></script><script language="JavaScript" type="text/javascript"><!--
d.write('<a href="http://top.mail.ru/jump?from=956201"'+
' target=_top><img src="http://d7.c9.be.a0.top.list.ru/counter'+
'?id=956201;t=84;js='+js+a+';rand='+Math.random()+
'" alt="Рейтинг@Mail.ru"'+' border=0 height=18 width=88/><\/a>')
if(11<js)d.write('<'+'!-- ')//--></script><noscript><a
target=_top href="http://top.mail.ru/jump?from=956201"><img
src="http://d7.c9.be.a0.top.list.ru/counter?js=na;id=956201;t=84"
border=0 height=18 width=88
alt="Рейтинг@Mail.ru"/></a></noscript><script language="JavaScript" type="text/javascript"><!--
if(11<js)d.write('--'+'>')//--></script><!--/COUNTER-->


</div>








<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2283330-1";
urchinTracker();
</script>






</body>

</html>
0


есть та же самая страница, запрошенная с изпользованием gzip - выглядит она примерно так
HTTP/1.1 200 OK
Server: nginx/0.3.17
Date: Sun, 21 Mar 2010 16:06:59 GMT
Content-Type: text/html; charset=windows-1251
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip

a
<тут 10 символов>
6da
<тут 24 строки с разными символами>
0
Собствено вопрос:
1. Откуда считать начало gzip тела (которое длинной 6da) - с начала строки, следующей за строкой с размером и до строки с нулем без учета символов переноса строки перед нулем?
2. как конвертнуть текст gzip обратно в нормальный?
ф-цией (из либы ZLibEx)
function DecompressString(const aString: string): string;
var
lStr: TStringStream;
lDS: TZDecompressionStream;
begin
lStr := TStringStream.Create(aString);
try
lDS := TZDecompressionStream.Create(lStr);
try
SetLength(Result, lDS.Size);
lDS.Read(Pointer(Result)^, lDS.Size);
finally
lDS.Free;
end;
finally
lStr.Free;
end;
end;увы не удается - пишет "data error", при том что на вход ф-ции я подаю следующее:
reply:=copy(reply,pos('6da',reply)+3,1754); //1745 это 6da
где reply - изначально полученный мной от сервера ответ.

Прошу показать как с этим справиться конкретно на примере страницы zalil.ru, т.к. она маленькая + неизменяющаяся.
Но можно и на любой другой

ps.
вожусь с этой *ней уже часов 6.
за реальную помощь в решении вопроса преобразования gzip в обычный текст при использовании сокетов (никаких компонет, никакого инди - мне 1000-1500 потоков надо - инди убьет себя) с меня +20

Chrome~
21.03.2010, 22:14
Пробуй передавать на вход своей функции не переменную типа String, а какую-нибудь переменную типа TMemoryStream, так как String не сможет сохранить в себе все символы, которые передает тебе сервер.

ErrorNeo
21.03.2010, 22:21
так... окей.
Начало блока gzip - пара байт: 0x1f,0x8b (так подсказал RFC 1952)
длинна блока... наверное 6da (или любая другая хекс-цифра, которая идет перед шифроблоком и вероятно обозначает его длинну)

тем не менее, как я не варьирую длинну блока (исходя из того, что первый символ блока - 0x1f) - все равно получаю "data error" :-(

Chrome~ - хз..
ф-ция, что я указал (найденная в гугле) вроде как работает именно с переменной string. хотя фиг его знает что тут как, если быть до конца точным

__mad
22.03.2010, 00:09
http://en.wikipedia.org/wiki/Chunked_transfer_encoding

то есть


a
<тут A символов>
6da
<тут 6da символов>
0


объединяешь в


<тут A символов><тут 6da символов>


и передаешь в zlib

ErrorNeo
22.03.2010, 00:48
...cpc конечно, но все равно выдает ту же ошибку.
Дело в том, что основные "тесты" я провожу, конечно же, не на паге zalil.ru.
Та пага, где я тестюсь идет не chunked а единым блоком...(
и увы преобразовать её (как и любую другую, не-chunked) из gzip в обычный текст обозначенный выше ф-цией не получается (почему именно - фиг его знает. "data error")

было бы супер, если бы гашелся кто-то, ко когда-нибудь реализовывал комбинацию delphi+windows_sockets+gzip

slesh
22.03.2010, 00:57
Мож баг в распаковщике? Ты попробуй полученный код распаковать в php
и если удастся, нормально, значит баг в модуле

noneim
22.03.2010, 16:23
Распаковка gzip (взято отсюда - http://www.rsdn.ru/forum/winapi/2171857.flat.aspx):



BOOL WINAPI DecompressFile(HANDLE hArchFile, HANDLE hPlainFile)
{
HRESULT rc;
IEncodingFilterFactory* pEflt=NULL;
IDataFilter* pDF=NULL;
BOOL bOK=TRUE;
rc = CoCreateInstance( CLSID_StdEncodingFilterFac, NULL, CLSCTX_INPROC_SERVER, IID_IEncodingFilterFactory, (LPVOID *) &pEflt);
if( FAILED(rc) )
return FALSE;
pEflt->GetDefaultFilter(_T("gzip"), _T("text"), &pDF);
if (pDF)
{
BYTE in_buff[32768];
BYTE out_buff[32768];
DWORD rdd=0;
DWORD wrt=0;
LONG proc=0;
LONG outpt=0;
__int64 filesize=0;
__int64 processed=0;
GetFileSizeEx(hArchFile, (PLARGE_INTEGER)&filesize);
while (processed<filesize)
{
SetFilePointerEx(hArchFile, *((PLARGE_INTEGER)&processed), NULL, FILE_BEGIN);
ReadFile(hArchFile, in_buff, sizeof(in_buff), &rdd, NULL);
rc=pDF->DoDecode(0, sizeof(in_buff), in_buff, sizeof(out_buff), out_buff, rdd, &proc, &outpt, 0);
if (FAILED(rc))
{
bOK=FALSE;
break;
}
WriteFile(hPlainFile, out_buff, outpt, &wrt, NULL);
processed+=proc;
}
do
{
rc=pDF->DoDecode(0, sizeof(in_buff), in_buff, sizeof(out_buff), out_buff, 0, &proc, &outpt, 0);
if (FAILED(rc))
{
bOK=FALSE;
break;
}
WriteFile(hPlainFile, out_buff, outpt, &wrt, 0);
}while(outpt>0);
pDF->Release();
pEflt->Release();
return bOK;
}
pEflt->Release();
return FALSE;
}



все работает, проверено
может учитывал лишние \r\n символы?

ErrorNeo
29.03.2010, 08:09
все работает, проверено
может учитывал лишние \r\n символы?
я тоже нашел ф-ции для Си, и даже не для файлов, а для строк. Но все же, наверное, в названии темы не зря написано слово "Delphi" :)
Знаешь ли, не всегда просто перевести код (который не понимаешь) с одного языка, который плохо понимаешь на другой, который, пусть даже, и знаешь более менее неплохо.

s0l_ir0n
29.03.2010, 11:02
delphi zlib 1.2.3.2009
# zlib version 1.2.3 for delphi 5, 6, 7, 8, 2005, 2006, 2007, 2009
# now supports simple gzip files
# includes zlib source code and c++ builder 6 project files (c++ builder 6 was used to compile c source into object files)

http://www.base2ti.com/zlib.htm

maestro-ant
30.03.2010, 00:49
можно гору обойти:
в заголовок-запроса напиши так чтоб сервер не паковал данные

Chrome~
30.03.2010, 01:08
можно гору обойти:
в заголовок-запроса напиши так чтоб сервер не паковал данные
Нет, ну ты в реале уникальный человек!!! Ему нужно именно расшифровать запакованные данные, а не получить чистый текст от сервака.

ErrorNeo
31.03.2010, 09:49
delphi zlib 1.2.3.2009
# zlib version 1.2.3 for delphi 5, 6, 7, 8, 2005, 2006, 2007, 2009
# now supports simple gzip files
# includes zlib source code and c++ builder 6 project files (c++ builder 6 was used to compile c source into object files)

http://www.base2ti.com/zlib.htm
я это нашел и сам. только приспособить это к пакованым http запросам чета не получается...=\
может конечно мало *лся... там есть какие-то ф-ции для работы с "потоковыми данными" итд. Хотя.. все же хотелось бы по возможности увидеть готовый пример.

maestro-ant - хром уже ответил. "Не паковать данные"? - я и так их(в данный момент) не пакую... потому что не умею распаковывать:( Но хотелось бы все-таки научиться, чтобы уменьшить затраты траффика => увеличить количество обрабатываемых http страниц в минуту.

M_script_
31.03.2010, 10:24
ErrorNeo, юзай либкурл и будет тебе счастье ;)

maestro-ant
02.04.2010, 14:32
Chrome~
попрошу без оскорблений.

ErrorNeo
Мой совет вполне дельный. Разве тебе критично-нужно чтоб трафик был упакованный? Если нет, то в заголовке запроса, который отсылаешь серверу нужно выкинуть строчку Accept-encoding gzip. И все! упаковка данных происходить не будет.
Я так делал когда писал "подмену выдачи". gzip-тупо не нужен мне был. Она нужна для экономии трафика.

M_script_
02.04.2010, 15:03
Она нужна для экономии трафика.
Ему это как раз и нужно )

Chrome~
02.04.2010, 16:10
Chrome~
попрошу без оскорблений.
Я вас не оскорблял.

Тоже интересуюсь данным вопросом, раньше искал ответ, но так и не нашел.

ErrorNeo
05.04.2010, 14:49
да, знаю, что либкурл поможет(((
но в данном случае доделаю всё и без коспрессии, а дальше...
если еще 1 приложение придется писать такое - а полюбому придется - буду уже юзать.... либкурл :'(
(прогонять полтора террабафта траффика вообще без компрессии это все излишне же сурово, даже для меня)

maestro-ant - да, мне критична именно экономия траффика. Без упаковки я запросы и так умею слать - да и любой дурак умеет. Не в обиду тебе.) А вот распавовывать пакованные методами дельфи - увы - далеко не каждый. Если быть точнее - то судя по этой теме вообще никто это не делал у нас при использовании вин-сок, да и... вообще хоть каким-либо методом на дельфи.

обломно.

GhostOnline
05.04.2010, 20:35
Пробовал TIdCompressorZLib прикрутить? Что-то мне кажется что не пробовал

Кстати реальный пример того что библиотеки типа инди не хуже винсок. Гзип уменьшает трафф на порядка 70%, вот и посчитайте сколько страниц можно получить в инди и в винсок за промежуток времени

Jingo Bo
05.04.2010, 21:48
ErrorNeo если еспользуется Indy 9(Delphi 7), то багов в ней куча и gzip там попросту не работает. Для этого нужно скачать заплатки этой версии(там вполть до Classes) :)

Вот архив :
http://slil.ru/28913262

Распаковывешь эту папку в папку сос воей прогой и все *.pas подключаем к проекту через Project Manager, перезапускаем Delphi и используем как пытались, могу поспорить в этом беда.

Jingo Bo
05.04.2010, 21:51
Блин, я туплю, но один фик посмотреть те сорцы можно и вытащить для себя то что нужно:)

M_script_
05.04.2010, 23:01
Кстати реальный пример того что библиотеки типа инди не хуже винсок.
А ты запусти 1000 потоков в проге, написанной на инди и сразу поймешь, чем инди хуже ;)

GhostOnline
06.04.2010, 00:10
А нахрена 1000 потоков если и 300 упирается в лимит траффа?

Инди дохрена процессорного времени отбирает?

M_script_
06.04.2010, 02:07
А нахрена 1000 потоков если и 300 упирается в лимит траффа?
Это от канала зависит и от размера страниц. Может и 1к потоков не весь канал забивать, примеры есть.

Инди дохрена процессорного времени отбирает?
Слишком много ошибок начинает появляться при большом количестве потоков.

Jingo Bo
06.04.2010, 02:29
Слишком много ошибок начинает появляться при большом количестве потоков.
Нет, проверял, даже при большом количестве (+ 400) разницы нет. Проблема с инди в том что много логических ошибок, у меня на 9 основные исправлены, пока не замечал. А так использовать можно:)

И кстати если при большом количестве у вас начинает в инди сыпаться мног ошибок - скорее всего плохая синхронизация потоков. Есть в инди одно узкое конечно место где все потоки сходятся, но там всё идеально сделано.(Файл IdComponent.pas, создание класса) Там речь идёт о критической секции и прикол в том что на XP(ещё где хз) при огромном количестве потоков эти критические секции(по сути системные объекты) глючат(пропускают потоки, когда главный поток блокирует), не объяснимо но факт. Может там трабла, я хз.

Jingo Bo
06.04.2010, 02:30
Это от канала зависит и от размера страниц. Может и 1к потоков не весь канал забивать, примеры есть.
Не помню какое на XP хом эдишн стоит ограничение на использование активных TCP соединений, но не высокое.

M_script_
06.04.2010, 04:00
И кстати если при большом количестве у вас начинает в инди сыпаться мног ошибок - скорее всего плохая синхронизация потоков.
С синхронизацией все нормально. В инди проблемы в основном при использовании прокси.

Не помню какое на XP хом эдишн стоит ограничение на использование активных TCP соединений, но не высокое.
Я win2003 использую обычно )

[stranger]
07.04.2010, 00:07
как плавно съехали с gzip.. :)
по сабжу: что-то GZDecompressStr не хочет работать с результатом GZCompressStr.. Data Error! В чем прикол?

nick1000
17.04.2010, 20:48
Вот нашел в нете функцию позволяющую распаковывать gzip без заголовка(10 байт) и окончания (8 байт=CRC32+длинна исходного текста). На залил ру ответ без окончания.

а вообще ошибка возникала из-за того что в TZDecompressionStream используется InflateInit_ в котором по-умолчанию стоит windowBits=15, а нужно windowBits=-15 , что и указывается в InflateInit2_
(хотя сам адлер пишет что этот параметр должен быть в диапазоне 8..15).

Вот код:

function InflateInit2(var stream: TZStreamRec; windowBits: Integer): Integer;
begin
result := InflateInit2_(stream,windowBits,ZLIB_VERSION,SizeO f(TZStreamRec));
end;


function ZDecompressCheck(code: Integer): Integer;
begin
Result := code;

if code < 0 then
begin
raise EZDecompressionError.Create(_z_errmsg[2 - code]);
end;
end;

procedure GZDecompress(const inBuffer: Pointer; inSize: Integer;
out outBuffer: Pointer; out outSize: Integer; outEstimate: Integer = 0);
var
zstream: TZStreamRec;
delta : Integer;
begin
FillChar(zstream,SizeOf(TZStreamRec),0);

delta := (inSize + 255) and not 255;

if outEstimate = 0 then outSize := delta
else outSize := outEstimate;

GetMem(outBuffer,outSize);

try
zstream.next_in := inBuffer;
zstream.avail_in := inSize;
zstream.next_out := outBuffer;
zstream.avail_out := outSize;

ZDecompressCheck(InflateInit2(zstream, -15));

try
while ZDecompressCheck(inflate(zstream,Z_NO_FLUSH)) <> Z_STREAM_END do
begin
Inc(outSize,delta);
ReallocMem(outBuffer,outSize);

zstream.next_out := PAnsiChar(Integer(outBuffer) + zstream.total_out);
zstream.avail_out := delta;
end;
finally
ZDecompressCheck(inflateEnd(zstream));
end;

ReallocMem(outBuffer,zstream.total_out);
outSize := zstream.total_out;
except
FreeMem(outBuffer);
raise;
end;
end;


function GZDecompressStr(const s: AnsiString): AnsiString;
var
buffer: Pointer;
size : Integer;
begin
GZDecompress(PChar(s),Length(s),buffer,size);

SetLength(result,size);
Move(buffer^,result[1],size);

FreeMem(buffer);
end;


procedure TForm1.Button6Click(Sender: TObject);
var ss,s,reply:string;
begin

Здесь получаем ответ через winsocks
........
reply:=copy(reply,pos('6da',reply),1757);
//или по-другому. Главное чтобы в reply остался '6da' .

s := Copy(reply, 11, Length(reply)-10);
ss:=GZDecompressStr(s);
Memo1.Text:=ss;
end;