Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 23 мая 2010 в 21:29.
Категории: Delphi в Web.


Вроде бы всем хороши компоненты 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;

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

--------------------------
Представьте себе ситуацию: Вам надо позвонить клиенту или заказчику, а труба не фурычит. Что делать? Срочный ремонт сотовых телефонов будет единственным Вашим спасением. Ремонтируют всё от Fly до IPhone.
--------------------------
Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Комментарии (12)

WP_Cloudy
  • Дмитрий пишет:

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

  • Дмитрий пишет:

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

  • Vlad пишет:

    Дмитрий, спасибо за уточнение. Да, либа неплохая, сейчас перевожу библиотеку OAuth с Indy на ICS — тестовое приложение похудело примерно на 450 кило + немного по-шустрее работать стала.

  • Pavel пишет:

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

  • Vlad пишет:

    Вроде бы поддерживает, но самому пока использовать не приходилось

  • Bars пишет:

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

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

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

  • Vlad пишет:

    Ну в Synapse эта проблема решается намного проще. А с ICS почему бы получение веб-страницы не завернуть в try..except..end и не обрабатывать HttpCli1.CtrlSocket.LastError? Не пробовали так делать?

  • Bars пишет:

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


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

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

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

  • Akella пишет:

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

  • Евгений пишет:

    Vlad, благодарю за подробное описание компонента. 
    Остался пока один вопрос, который непонятно как реализовать.

    Допустим, программа запустилась, и что-то там в своём отдельном потоке TThread с помощью асинхронного THttpCli скачивает страничку сайта. Но пользователь вдруг сразу закрывает программу, а страничка ещё не скачалась. Как будет вести себя THttpCli?

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

  • fantasyDD пишет:

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

  • Vlad пишет:

    Про ICS могу ошибаться, но посмотрите на конструктор компонента — там где-то должно задаваться врем отклика. Наример в Synapse у объекта Sock задается время 60000 — 1 минута, меняем на 1000 и сокет ждет ответ 1 секунду.

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник

   


Линии технической поддержки, установка снпч.