Подписка

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

Наши проекты

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 15 февраля 2010 в 18:54.
Категории: Delphi в Web.


Сегодня решил дописать свой THTTPSender, который использует в работе WinInet.  Первое, что необходимо было сделать - это научиться правильно читать код ответа сервера для того, чтобы проолжить работу над Google Data API. При этом важно было не только "поймать" код 200 или 400, но и определять момены, когда сервер выдает коды 301 или 302.  Обычно при работе с WinInet этот момент (определение кодов перенаправления) опускается, т.к. чаще всего удобно, чтобы перенаправление происходило автоматически. Сегодня разберемся как отлавливать все коды стауса и чиать заголовки сервера.Для того, чтобы получить какую либо служебную информацию от сервера мы можем воспользоваться методом HttpQueryInfo. Рассмотрим входные параметры функции:

  • hRequest - Handle, который мы получаем при выполнении HttpOpenRequest или InternetOpenUrl.
  • dwInfoLevel - комбинация флагов, каждый из которых указывает, какая информация нам необходима
  • lpvBuffer - указатель на буфер в котоый будет происходить запись данных.
  • lpdwBufferLength - размер буфера
    lpdwIndex - индекс заголовка

Сама функция HttpQueryInfo работает следующим образом: при выполнении происходит попытка чтения в буфер запрошенной информации. Если размер буфера оказывается мал, то вызывается исключение ERROR_INSUFFICIENT_BUFFER и переменная lpdwBufferLength будет содержать необходимую размерность буфера для приёма всех данных. В случае, если мы запрашиваем информацию, которую сервер вернуть не в состоянии, вызывается исключение ERROR_HTTP_HEADER_NOT_FOUND.

Немного поэкспериментировав с HttpQueryInfo обнаружил следующее: по документации MSDN для успешного выполнения функции необходимо указывать хэндл, который мы берем из HttpOpenRequest или InternetOpenUrl. Если использовать InternetOpenUrl, то  так и есть - функция отрабатывает как надо и возвращает результат. Если я использую HttpOpenRequest, то функция отказывается возвращать данные (при этом GetLastError ничего не выдает), но срабатывает после выполнения метода HttpSendRequest. Не думаю, что это глюк, но учитывать этот момент стоит.

Теперь, что касается исключение ERROR_HTTP_HEADER_NOT_FOUND. На это исключение можно довольно легко налететь. Например, мы запрашиваем информацию о последнем изменении страницы. Для этого мы устанавливаем флаг HTTP_QUERY_LAST_MODIFIED и пробуем выполнить метод. Если в заголовках сервера нет заголовка Last-Modified, то функция вернет False и в результате выполнения GetLastError мы получим исключение ERROR_HTTP_HEADER_NOT_FOUND. Поэтому следует очень осторожно пользоваться методом, когда есть вероятность того, что в заголовках нет необходимой информации.

Чтобы получать всю информацию от сервера, которую он выдает, я пользуюсь флагом HTTP_QUERY_RAW_HEADERS_CRLF, при этом возвращается строка, содержащая все заголовки сервера и каждый заголовок разделен символами CR/LF (#10#13), что удобно, например, для использования результата в списках TStringList.

Теперь перейдем непосредственно к программированию. Итак, пишем функцию для получения информации сервера. Назовем её, например GetQueryInfo. В процессе работы нам может потребоваться:

  1. Получние како-либо конкретной информации, например, код ответа сервера.
  2. Получение всей информации - всех заголовков.

Соответственно, в первом случае, будет излишним взводить HTTP_QUERY_RAW_HEADERS_CRLF (код статуса занимает 6-8 байт, а заголовки могу насчитывать килобайты информации). Следовательно в качестве входных парамеров функции будут выступать:

  1. Хэндл (hRequest)
  2. Флаг (dwInfoLevel) тип параметра - integer.

Теперь по самому алгоритму. Будем делать так:

  1. Задаем буферу минимальный размер, которого будет достаточно, чтобы прочитать код статуса (сама функция возвращает минимальное значение 8 байт)
  2. Пробуем выполнить функцию HttpQueryInfo
  3. Если возвращается ERROR_INSUFFICIENT_BUFFER,
    тоизменяем размер буфера и повторно вызываем HttpQueryInfo.
  4. Если возвращается код ошибки отличный от 122 (ERROR_INSUFFICIENT_BUFFER), то завершаем работу без повторного вызова HttpQueryInfo.

Все вышесказанное в Delphi выглядит следующим образом:

function THTTPSender.GetQueryInfo(hRequest: Pointer; Flag: integer): string;
var code: String;
    size,index:Cardinal;
begin
  SetLength(code,8);//достаточная длина для чтения статус-кода
  size:=Length(code);
  index:=0;
  if HttpQueryInfo(hRequest, Flag ,PChar(code),size,index)then
    Result:=Code
  else
    if GetLastError=ERROR_INSUFFICIENT_BUFFER then //увеличиваем буффер
      begin
        SetLength(code,size);
        size:=Length(code);
        if HttpQueryInfo(hRequest,Flag,PChar(code),size,index) then
          Result:=code;
      end
  else
    begin
      FErrorCode:=GetLastError;
      Result:='';
    end;
end;

Теперь используем эту функцию для чтения и сохранения заголовков сервера.  Для получения всех заголовков сервера необходимо сделать следующий вызов:

GetQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF)

На выходе - строка. Для того, чтобы не вызывать метод несколько раз для получения информации, достаточно разобрать полученные заголовки и гарантированно мы здесь можем получить:

  • данные по протоколу
  • код статуса сервера
  • ответ сервера (текст)
  • если код статуса 301 или 302 - получить адрес перенаправления (заголовок Location).

Онако здесь следует учесть один момент. Заключается он в следующем:

чтобы отлавливать коды 301 и 302 при вызове метода HttpOpenRequest обязательно надо взводить флаг INTERNET_FLAG_NO_AUTO_REDIRECT, ктороый останавливает автоперенаправление на адрес.

После взведения флага можно спокойно читать все статус-коды. Например с использованием следующей процедуры:

procedure THTTPSender.ParseHeaders(HeasersStr: string);
var i:integer;
    s: string;
begin
  if not Assigned(FHeaders)then FHeaders:=TStringList.Create;
  FHeaders.Clear;
  FHeaders.Text:=HeasersStr;
  FHeaders.Delete(FHeaders.Count-1); //последний элемент содержит всегда CRLF
 
  if FHeaders.Count>0 then
    begin
//читаем данные о протоколе, статус-коде и ответе сервера
      S:=FHeaders[0];
      FProtocol:=copy(s,1,pos(' ',s)-1);
      Delete(s,1,Length(FProtocol)+1);
      FProtocol:=copy(FProtocol,1,pos('/',FProtocol)-1);
      FResponseCode:=StrToInt(copy(s,1,pos(' ',s)-1));
      Delete(s,1,Length(IntToStr(FResponseCode))+1);
      FResponseText:=Trim(s);
//если было перенаправление, то читаем адрес
     if (ResponseCode=HTTP_STATUS_MOVED)or(ResponseCode=HTTP_STATUS_REDIRECT) then
       for I := 0 to FHeaders.Count - 1 do
         begin
           if pos('location:',lowercase(FHeaders[i]))>0 then
             begin
               FLocation:=LowerCase(FProtocol)+'//'+FDomain+'/'+Trim(copy(FHeaders[i],10,length(FHeaders[i])-9));
               break;
             end;
         end;
  end;
end;

Таким образом, вместо того, чтобы вызывать HttpQueryInfo
несколько раз для получения необходимой информации, мы вызываем метод всего 1-2 раза и читаем сразу все. Можно дописать приведенную выше процедуру и получать, например, данные по установленным кукам.

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

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

WP_Cloudy
  • Павел пишет:

    Спасибо автору. Интересная статья, попробую в свободное время

  • Андрей пишет:

    А как в WinInet куки чистить? на родном сайте http://msdn.microsoft.com/en-us/library/aa385473(v=vs.85).aspx нашёл функцию «InternetClearAllPerSiteCookieDecisions» но подключить её не получилось. Я уже изучался, праграмма есть для регистрации, но регатся могу только один раз. Куки остаются и я как бы всегда авторизированный(((( Спасает только переоткрытие программы.

Ваш ответ

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

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