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

Вроде бы всем хороши компоненты ICS — достаточно удобные в использовании, бесплатные (несмотря на «регистрацию»), поддерживают асинхронный режим работы и т.д. и т.п. Но вот со справочной информацией по работе с компонентами прямо-таки беда. Не то чтобы не было информации вообще — по элементарным операциям типа отправить/получить данные только в Рунете можно найти сотни статей, примеров и т.д., а по чуть более сложным впросам уже все. Приходится скакать с форума на форум в поисках крупиц информации.
Я решил немного упростить себе (и, возможно, всем остальным, кто используют компоненты ICS) жизнь и по мере сил и возможностей рассказывать в блоге все, что касается работы с компонентами ICS, чтобы вся доступная информация была под рукой. Начать решил с компонента HTTPCli, т.к. на данный момент именно он представляет для меня наибольший интерес.

Общая информация о компоненте HTTPCli

HTTPCli — компонент для организации работы с HTTP-протоколом на стороне клиента. Класс THTTPCli описан в модуле OverbyteIcsHttpProt.pas.
Поддерживает выполнение следующих запросов: Get, Post, Put, Head, Close, Abort;
Все запросы выполняются в двух режимах — синхронном и асихронном. Для выполнения асинхроной передачи данных используются методы вида xxxASync.

События компонента HTTPCli

OnStateChange

OnStateChange: TNotifyEvent

срабатывает в момент, когда компонент изменяет свое состояние. Срабатывает в первую очередь до всех остальных событий, а также в момент получения каких-либо данных (заголовков, тела ответа и т.д.).
Текущее состояние компонента содержится в его свойстве State и может принимать следующие значения:

THttpState = (httpReady, httpNotConnected, httpConnected, httpDnsLookup, httpDnsLookupDone,
httpWaitingHeader, httpWaitingBody,  httpBodyReceived,httpWaitingProxyConnect, httpClosing,
httpAborting);

Состояние компонента (State) изменяется в зависимости от выполнения той ил иной команды. При успешном выполнении метода GET состояние может изменятся следующим образом:

httpNotConnected—>httpDnsLookup—>httpDnsLookupDone—>
httpConnected—>httpWaitingHeader—>httpWaitingBody—>
httpBodyReceived—>httpReady

OnSocketError

OnSocketError: TNotifyEvent

Срабатывает, когда происходит какая-либо ошибка сокета. Событие OnSocketError срабатывает как минимум один раз после того как компонент изменяет свое состояние в httpDnsLookupDone вне зависимости от того были ошибки или нет.
Чтобы получить код последней ошибки сокета можно использовать следующее свойство:

  HttpCli1.CtrlSocket.LastError

LastError: integer — код последней ошибки.

OnSessionConnected

OnSessionConnected: TNotifyEvent

Срабатывает сразу после смены состояния объекта на httpConnected. После выполнения события компонент переходит в состояние httpWaitingHeader (ожидание заголовков запроса).

RequestHeaderBegin

RequestHeaderBegin: TNotifyEvent

Срабатывает до того как первая строка заголовков будет добавлена в запрос.

BeforeHeaderSend

BeforeHeaderSend(Sender: TObject; const Method: string; Headers: TStrings)

Срабатывает после RequestHeaderBegin, но до того момента как первый заголовок будет отправлен на сервер. При этом Method содержит строку, которая определяет тип запроса, а Headers — список основных заголовков, таких как, Content-Length, User-Agent и т.д.
Событие удобно использовать, когда необходимо добавить в запрос свои заголовки.

OnCommand

OnCommand(Sender: TObject; var S: string)

Срабатывает на выполнение каждой команды, например, OnCommand многократно срабатывает при добавлении заголовков в запрос. При этом S содержит строку команды, например, строку:

host: www.webdelphi.ru

OnRequestHeaderEnd: TNotifyEvent

Срабатывает после того как все заголовки будут успешно добавлены в запрос.

OnHeaderBegin

OnHeaderBegin: TNotifyEvent

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

OnHeaderData

OnHeaderData: TNotifyEvent

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

HttpCli1.RcvdHeader:TStrings

в котором содержаться все заголовки, полученные от сервера.

OnHeaderEnd

OnHeaderEnd: TNotifyEvent

Срабатывает после получения всех заголовков от сервера, но до того как компонент перейдет в состояние httpWaitingBody (ожидание данных).

OnDocBegin

OnDocBegin: TNotifyEvent

Срабатывает до момента как будет получена первая часть данных документа.

OnDocData

OnDocData(Sender: TObject; Buffer: Pointer;
  Len: Integer)

Срабатывает при получении каждой части данных документа. Pointer содержит указатель на поток в который сохраняются принимаемые данные, Len — размер полученных данных в байтах.

OnDocEnd

OnDocEnd: TNotifyEvent

Срабатывает в момент, когда последние данные документа были получены, после смены компонентом состояния на httpBodyReceived, но до смены состояния на httpReady.

OnSessionClosed

OnSessionClosed: TNotifyEvent

Срабатывает после того как текущее соединение будет завершено.
Событие возникает после OnDocEnd, но до смены состояния события на httpReady.

RequestDone

RequestDone(Sender: TObject; RqType: THttpRequest; ErrCode: Word)

Завершающее событие. Срабатывает самым последним. При этом
RqType содержит тип выполненного запроса и может принимать одно из следующих значений:

THttpRequest = (httpABORT, httpGET, httpPOST, httpPUT, httpHEAD, httpCLOSE);

ErrCode содержит код ошибки, либо 0, если запрос выполнен успешно. Возможные коды ошибок:

httperrNoError = 0;
httperrBusy = 1;
httperrNoData = 2;
httperrAborted = 3;
httperrOverflow = 4;
httperrVersion = 5;
httperrInvalidAuthState = 6;
httperrSslHandShake = 7;

Это, если можно так выразиться, основные события компонента, которые будут срабатывать при получении данных от сервера. Если при выполнении запроса сервер отправляет нам Cookies, то будет срабатывать ещё одно событие:

OnCookie

OnCookie(Sender: TObject; const Data: string; var Accept: Boolean);

Это событие будет срабатывать до момента как строка Cookie будет добавлена к заголовкам сервера, т.е. ДО события OnHeaderData и Data содержит текст Cookie без «set-cookie: «.
В общем случае, при выполнении GET-запроса события будут срабатывать в следующей последовательности:

OnStateChange —>…—> OnStateChange —> OnSocketError —> OnStateChange —> OnSessionConnected —> OnStateChange —> OnRequestHeaderBegin —> OnBeforeHeaderSend —> OnCommand —>…—> OnCommand —> OnRequestHeaderEnd —> OnCommand (добавление пустой строки) —> OnHeaderBegin —> OnCookie —> OnHeaderData —>…—> OnHeaderEnd —> OnStateChange —> OnDocBegin —> OnDocData —>…—> OnDocData —> OnStateChange —> OnDocEnd —> OnSessionClosed —> OnStateChange — OnRequestDone

В случае, если выполняется POST-запрос дополнительно к вышеуказанным событиям добавляются ещё как минимум три события:

OnSendBegin

OnSendBegin: TNotifyEvent

Выполняется до отправки данных на сервер, после того как сформированы заголовки сообщения (после OnCommand с пустой строкой).

OnSendData

OnSendData(Sender: TObject; Buffer: Pointer;
  Len: Integer);

Срабатывает после того как каждая часть сообщения была отправлена на сервер. Параметры (см. OnDocData).

OnSendEnd

OnSendEnd: TNotifyEvent

Срабатывает после того как все данные были отправлены на сервер и ожидается получение первого заголовка ответа.

Когда необходимо использовать события HTTPCli

Вообще, события компонента HTTPCli каждый использует тогда, когда это необходимо в конкретной ситуации и в конкретном случае. Но при работе c HTTPCli иногда могут возникать ситуации, когда без события никак нельзя обойтись.
Чтобы понять важность событий компонента рассмотрим такой пример: отправка заголовков авторизации («Authorization: «) при использовании на сервере OAuth.
Заголовок при таком виде авторизации должен во-первых содержать метод авторизации — «OAuth» и во-вторых, все необходимые параметры, такие как подпись (signature), oauth_nonce и т.д. и т.п.
Сами по себе компоненты ICS достаточно продвинутые и в работе того же HTTPCli можно без проблем использовать разные формы авторизации: Basic, Digest и т.д. При этом заголовок Authorization будет формироваться в процессе работы компонента и отправляться. Как быть с OAuth? Для авторизации нам придётся записывать заголовок вручную.
Однако, если Вы попробуете найти в свойствах компонента, что-то, наподобие Headers:TStrings, то ничего из этого не выйдет. Нет у HTTPCli свойства, отвечающего за хранение отправляемых заголовков. Есть только свойство RcvdHeader в котором содержать принятые заголовки от сервера. И именно в этом случае (добавлении заголовков) нам не обойтись без знания того, что у HTTPCli имеется событие OnBeforeHeaderSend, используя которое можно перед началом отправки «скормить» компоненту любой заголовок.
Например, так:

procedure TFormHTTP.HttpCli1BeforeHeaderSend(Sender: TObject; const Method: string; Headers: TStrings);
begin
  ListBox2.Items.Add('Сработало событие BeforeHeaderSend');
  Headers.Add('Authorization: OAuth ...')
end;

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

5 1 голос
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
12 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Дмитрий
Дмитрий
24/05/2010 15:06

Спасибо.
ICS рулит, я в последнее время прямо-таки помешался на этой либе

Дмитрий
Дмитрий
24/05/2010 15:13

Кстати, выражение «поддерживают асинхронный режим работы » не совсем верное.
Асинхронный режим — основной в ICS, а синхронный только эмулируется.
По-этому правильней писать «поддерживают синхронный режим работы наряду с асинхронным»

Pavel
Pavel
26/05/2010 16:19

А как обстоят дела у этой либы с NTLM Authentication?

Bars
Bars
30/07/2010 07:31

Здравствуйте!

Вот проглю сейчас, пользуя эту либу…
Наткнулся на такую проблему: не могу удерживать ошибки..
Ошибки сокетов пробил. А вот такие как 404 и 403 (forbidden) жить мешают со своими окнами сообщений…

Как от них избавиться, не подскажете? Или только в сорце либы ковыряться?

Bars
Bars
31/07/2010 20:18

Все-таки поковырял сорц и вбил вот такое:

[code]
if (FStatusCode >= 400) and (FStatusCode 401) and (FStatusCode 407) then
begin
DebugLog(loWSockErr, 'Error #' + inttostr(FStatusCode));
Abort;
end;
[/code]

OverbyteIcsHttpProt.pas: строка 3380 (в версии 7.03).
Вдруг, кому-то пригодится..

Юзаю этот логгер, но что-то пока не настроил его, так что об ошибке не оповещает. Зато, не мешает работать теперь эта ошибка)
Робота-индексера для поисковика клепаю, ссылок много, так что мне нужно было что бы только лог велся, и невзирая на ошибки, продолжалось индексирование.

Akella
10/09/2010 18:36

Кто подскажет, как заставить THttpCli «разжимать» gzip контент?
Некоторые сайты отдают сжатый контент. Как быть?

Евгений
Евгений
31/05/2011 13:16

Vlad, благодарю за подробное описание компонента.  Остался пока один вопрос, который непонятно как реализовать. Допустим, программа запустилась, и что-то там в своём отдельном потоке TThread с помощью асинхронного THttpCli скачивает страничку сайта. Но пользователь вдруг сразу закрывает программу, а страничка ещё не скачалась. Как будет вести себя THttpCli? Т.е., в закрытии главной формы программы, пишем что-то типа: TThread.Terminated := True, но в самом деструкторе TThread я же ведь не могу взять и просто написать THttpCli.Free, пока он ещё не закончил свою работу, верно же ведь? Получается, что в теле TThread.Execute нужно писать цикл с проверками: 1. на Terminated; 2. а не закончил ли свою работу… Подробнее »

fantasyDD
fantasyDD
13/06/2011 05:55

ICS THttpCli можно ли установить время жизни запроса.
Если работать через прокси то запрос может жить вечно!
HttpCli.close; по таймеру нежелательно.
Подскажите пожалуйста как решить проблему в самой компоненте?