уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Итак, пришло время поставить точку в работе с одним из классов библиотеки SynapseTHTTPSend. Большинство постов, касающихся работы с HTTP-протоколом в Delphi связано с использованием этого замечательного класса.

В разное время, в разных ситуациях я освещал в блоге работу с только с одним или несколькими свойствами THTTPSend, избегая при этом рассмотрения работы класса в целом. Но теперь, когда рассмотрены практически все доступные свойства класса, когда приведены необходимые примеры работы, например с теми же proxy или gzip в Synapse, думаю, что не плохо было бы свести всю эту информацию в один большой объединяющий пост. Лишним такая информация явно не будет, тем более, что Вы сможете в любое время сохранить эту статью в pdf и получить небольшой оффлайн-справочник по работе с HTTP в Synapse.

Содержание скрыть
5 Использование различных свойств THTTPSend
5.1 Использование RangeStart и RangeEnd. Как скачать часть файла?

Свойства THTTPSend


Свойство Headers

property Headers: TStringList read FHeaders;

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

  • Expect: 100-continue
  • Content-Length
  • Content-Type
  • Connection
  • Authorization
  • Proxy-Authorization
  • Host

Эти заголовки вставляются автоматически либо определяются с помощью других свойств класса. После того как запрос отправлен и получен ответ свойство Headers содержит заголовки ответа сервера и это необходимо учитывать при работе с объектом, например, в случае, когда необходимо отправить два и более POST, GET-запросов последовательно – первый запрос пройдет корректно, но если не почистить во время headers, то во второй POST-запрос THTTPSend отправит все полученные в первом запросе заголовки и это может вызвать некорректную работу программы. Случается такое довольно редко, но тем не менее всегда следует помнить о такой возможной причине некорректной работы программы.

Свойство Cookies

property Cookies: TStringList read FCookies;

Список пар имя-значение, каждая пара — представляет собой отдельную «печенюшку» (cookie). После каждого успешно выполненного запроса вы можете либо сохранить этот список для дальнейшего использования в программе, либо оставить нетронутым и тогда в следующем запросе все полученные cookies будут переданы на сервер.

Свойство Document

property Document: TMemoryStream read FDocument;

Поток, содержащий данные тела запрос к серверу ДО отправки и тело ответа сервера ПОСЛЕ отправки запроса. При работе с THTTPSend Вы должны самостоятельно следить за содержимым свойства Document.

В зависимости от размера этого потока, при отправке данных на сервер заполняется заголовок Content-Length (см. свойство Headers).

Наиболее распространенная ошибка при работе с THTTPSend (судя по вопросам, касающимся Synapse): отправляется GET-запрос на сервер, например, с целью получения cookies, затем, не очищая тело запроса, на сервер отправляется POST-запрос и сервер возвращает коды ошибок 404, 403 и т.д. Следует помнить, что свойство Document перед каждым новым запросом необходимо проверять на корректность содержащихся в нем данных либо полностью переписывать.

Свойства RangeStart и RangeEnd

  property RangeStart: integer read FRangeStart Write FRangeStart;
  property RangeEnd: integer read FRangeEnd Write FRangeEnd;

Эти свойства используются для указания начала и окончания диапазона закачиваемых данных, например, в случае прерванной загрузки или для скачивания больших объемов данных в нескольких потоках. Если значения свойств равны нулю, то скачивается весь документ.

Свойство MimeType

property MimeType: string read FMimeType Write FMimeType;

Свойство определяет MIME-тип отправляемых на сервер данных. Значение по умолчанию — «text/html«. При отправке данных значение этого свойство автоматически заносится в заголовок Content-Type (см. свойство Headers).

Свойство Protocol

property Protocol: string read FProtocol Write FProtocol;

Определяет версию HTTP-протокола. Допустимые значения: ‘1.1’, ‘1.0’ (по умолчанию) и ‘0.9’. Наиболее часто значение этого свойства может оставаться дефолтным, однако его значение может изменяться вами, например, в случае, когда требуется использовать SOCKS5 proxy.

Свойство KeepAlive

property KeepAlive: Boolean read FKeepAlive Write FKeepAlive;

Значение по умолчанию — True. Указывает, следует ли поддерживать соединение с сервером при обмене данными по протоколу версии 1.1.

Свойство KeepAliveTimeout

property KeepAliveTimeout: integer read FKeepAliveTimeout Write FKeepAliveTimeout;

Задает интервал в секундах в течении которого будет поддерживаться соединение.

Свойство Status100

property Status100: Boolean read FStatus100 Write FStatus100;

Значение по умолчанию — False. Если свойство равно True, то при отправке данных на сервер в заголовки автоматически включается заголовок Expect: 100-continue (см. свойство Headers). Этот заголовок может быть полезен в том случае, когда клиент хочет отправить на сервер большой объем данных и есть подозрение или просто требуется проверить примет ли такой объем данных сервер.

Например, требуется отправить на сервер видео размером 101 Mb. В этом случае клиент вначале отправляет пустой запрос со следующими заголовками:

POST /content/videos HTTP/1.1
Host: media.example.org
Content-Type: video/mp4
Content-Length: 105906176
Expect: 100-continue

В случае, если сервер не может принять файл такого размера/типа и т.д. клиенту возвращается код статуса 417 Expectation Failed. В противном случае (когда сервер может принять файл) клиенту возвращается код статуса 100 и клиент после этого может начинать загрузку контента.

Свойства ProxyHost, ProxyPort, ProxyUser и ProxyPass

property ProxyHost: string read FProxyHost Write FProxyHost;
property ProxyPort: string read FProxyPort Write FProxyPort;
property ProxyUser: string read FProxyUser Write FProxyUser;
property ProxyPass: string read FProxyPass Write FProxyPass;

Эти четыре свойства необходимы для указания параметров proxy-сервера, который будет использоваться для доступа к какому-либо хосту. Обязательным для определения является свойство ProxyHost. Свойство ProxyPort по умолчанию имеет значение 8080, а свойства ProxyUser и ProxyPass определяются только в случае работы с непубличным прокси.

Свойство UserAgent

property UserAgent: string read FUserAgent Write FUserAgent;

С помощью этого свойства можно определить название своего User-Agent’а при доступе к хосту. Значение по умолчанию: Mozilla/4.0 (compatible; Synapse)

Свойство ResultCode

property ResultCode: Integer read FResultCode;

После успешного подключения к хосту возвращает код сатуса. Следует отметить, что «успех подключения» означает не только возвращение кода статуса 200, а любой ответ серера на запрос клиента, в том числе и коды статуса 404, 401, 301 и т.д.

Свойство ResultString

property ResultString: string read FResultString;

После успешного подключения возвращает строку, соответствующую полученному коду сатуса, например, для кода статуса 200 ResultString будет содержать «Or», для 404 — «Not Found» и т.д.

Свойство DownloadSize

property DownloadSize: integer read FDownloadSize;

После успешно выполненного GET-запроса это свойство содержит общее количество полученных данных. Так как это свойство возвращает только общий объем данные его не следует использовать, например, для визуализиции процесса закачки (заполнение ProgressBar’а и т.д.). Для визуализиции процесса закачки следует использовать события сокета, например, событие OnStatus. Также следует отметить, что в величину DownloadSize не входят размер заголовков и cookies.

Свойство UploadSize

property UploadSize: integer read FUploadSize;

После успешно выполненного POST-запроса это свойство содержит общий объем загруженных на сервер данных. Также, как и свойство DownloadSize это свойство не должно использоваться для заполнения ProgressBar’ов и других визуализиций работы THTTPSend.

Свойство AddPortNumberToHost

property AddPortNumberToHost: Boolean read FAddPortNumberToHost write FAddPortNumberToHost;

Флаг, определяющий следует ли включать номер порта в заголовке Host для соединения с хостом. Значение по умолчанию True.

Свойство Sock

property Sock: TTCPBlockSocket read FSock;

Это свойство дает доступ к объекту типа TTCPBlockSocket, который осуществляет все операции по протоколу TCP/IP. Используя свойство Sock можно определять обработчики событий для THTTPSend.

Методы THTTPSend


Метод Clear

procedure Clear;

Очищает поля класса и присваивает им значения по умолчанию.

Метод DecodeStatus

procedure DecodeStatus(const Value: string);

Разбирает строку Value, содержащую статус соединения, заполняя при этом свойства ResultString и ResultCode.

Метод HTTPMethod

function HTTPMethod(const Method, URL: string): Boolean;

Основной метод класса с помощью которого осуществляются все виды запросов (GET, POST, HEAD и т.д.). Method содержит метод запроса в виде строки (регистр не имеет значения), URL — адрес на который отправляется запрос.
Если в URL отсутствует протокол (http:// или https://), то по умолчанию принимается, что запрос идет на http://.
Если соединение выполнено и в результате запроса удалось получить какой-либо код статуса, то метод возвращает True.

Метод Abort

procedure Abort;

Принудительно разрывает соединение. Этот метод удобно использовать в обработчиках событий OnStatus и OnReadFilter (см. описание событий ниже).

События сокета


Перечень событий TTCPBlockSocket

Событие OnStatus

Процедурный тип события:

THookSocketStatus = procedure(Sender: TObject; Reason: THookSocketReason; const Value: String) of object;

Универсальное событие, которое может использоваться в самых различных ситуациях при работе с сокетом, начиная от визуализации закачки данных из Сети и, заканчивая фильтрацией http-трафика.

Параметры:

Событие OnAfterConnect

Процедурный тип события:

THookAfterConnect = procedure(Sender: TObject) of object;

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

Событие OnReadFilter

Процедурный тип события:

THookDataFilter = procedure(Sender: TObject; var Value: AnsiString) of object;

Событие удобно использовать, когда необходимо фильтровать http-трафик «на лету», например, менять содержимое код страницы перед выводом или прерывать закачку документа по достижению какого-либо места в документе и т.д.

Параметры:

  • Value – данные, полученные сокетом, включая заголовки (см. свойство Headers)

Событие OnCreateSocket

Процедурный тип события:

THookCreateSocket = procedure(Sender: TObject) of object;

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

Событие OnMonitor

Процедурный тип события:

THookMonitor = procedure(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer) of object;

Параметры:

  • Writing — true указывает на то, что сокет отправляет данные.
  • Buffer — указатель на область памяти в которой расположены данные.
  • Len — размер отправленных/полученных данных за одну операцию чтении/отправки

Событие OnHeartbeat

Процедурный тип события:

THookHeartbeat = procedure(Sender: TObject) of object;

Событие удобно использовать при выполнении длительных операций без использования потоков. Время срабатывания события можно настроить с помощью свойства HeartbeatRate объекта сокета (см. свойство Sock)

Значения параметра THookSocketReason

Значение Описание Value обработчика события Пример данных в Value
HR_ResolvingBegin Начало определения параметров соединения String Возвращает адрес в формате: host:portнапример, www.webdelphi.ru:80
HR_ResolvingEnd Окончание определения параметров соединения String Возвращает адрес в формате: host:port или IP-адрес:portнапример, www.webdelphi.ru:80 или 217.199.213.171:80
HR_SocketCreate Socket создан методом CreateSocket. String Возвращает семейство сокетов, например,IPv4
HR_SocketClose Socket закрыт методом CloseSoket. String(пустая строка)
HR_Bind Socket связан с IP-адресом по заданному порту String Параметры соединения в формате host:portнапример, www.webdelphi.ru:80
HR_Connect Socket соединен с IP-адресом по заданному порту String Параметры соединения в формате host:portнапример,www.webdelphi.ru:80
HR_CanRead Socket готов принимать данные. Метод CanRead вернул True. String(пустая строка)
HR_CanWrite Socket готов отправить данные. Метод CanWrite вернул True. String(пустая строка)
HR_Listen Socket перешел в режим прослушивания (Только для TCP-сокетов) String(пустая строка)
HR_Accept Socket принял попытку соединения клиента (Только для TCP-сокетов) String(пустая строка)
HR_ReadCount Сообщает сколько байт информации было получено. Integer Количество байт информации, например,1024
HR_WriteCount Сообщает сколько байт информации было отправлено. Integer Количество байт информации, например,1024
HR_Wait Указывает на то, что сокет находится в режиме ожидания Integer Количество миллисекунд ожидания, например,1000
HR_Error Сообщает о том, что во время выполнения операции произошла ошибка String(пустая строка)

Примеры использования событий сокета в THTTPSend

Использование события OnStatus. Как скачать данные с заполнением ProgressBar’a?

Наверное самым часто используемым событием сокета является событие OnStatus, т.к. используя его можно получить практически любые сведения и данные по работе объекта, исключив при этом использование других событий.

Для примера рассмотрим использование события OnStatus при скачивании данных из Сети — сделаем красивое заполнение ProgressBar’a.

Чтобы рассмотреть пример загрузки документа с заполнением ProgressBar’а создадим новое приложение, которое будет скачивать из Сети какой-либо файл.

Вид главного окна приложения представлен на рисунке:

  • urlEdit: TEdit – поле для ввода URL по которому расположен файл
  • btnGet: TButton – кнопка «Скачать» после нажатия которой будет производиться закачка документа
  • pbFile: TProgressBar – ProgressBar для визуализации процесса закачки
  • lbProgress: TLabel – метка для вывода текстовой информации о ходе закачки.

Алгоритм работы программы будет следующим:

  1. Запрашиваем размер файла и сохраняем полученное значение в свойстве ProgressBar.Max
  2. При получении сообщения OnStatus от сокета со значением Reason = HR_ReadCount наращиваем значение ProgressBar.Position на значение Value из параметров события.
  3. Сохраняем полученный файл на диск.
Вначале напишем функцию, которая будет получать размер данных, которые необходимо скачать из Сети. Назовем эту функцию GetSize:
function TfMain.GetSize(URL: string): int64;
var i:integer;
    size:string;
    ch:char;
begin
  Result:=-1;
  with THTTPSend.Create do
  if HTTPMethod('HEAD',URL) then
    begin
      for I := 0 to Headers.Count - 1 do
        begin
          if pos('content-length',lowercase(Headers[i]))>0 then
            begin
              size:='';
              for ch in Headers[i]do
                if ch in ['0'..'9'] then
                   size:=size+ch;
              Result:=StrToInt(size)+Length(Headers.Text);
              break;
            end;
        end;
    end
end;

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

  1. Выполняется HEAD-запрос на заданный URL.
  2. Если запрос выполнен удачно, то проверяется список заголовков ответа сервера и ищется заголовок Content-Length, который содержит размер тела документа.
  3. Считывается значение из Content-Length с использованием цикла for..in..do
  4. К полученному значению прибавляется размер всех полученных заголовков, т.к. кроме самого файла при GET-запросе сервер вернет также и заголовки.
Если Вас не устраивает вид функции GetSize, то можно написать и другой вариант, который будет возвращать тоже самое значение:
uses ... synatil...
...
function TfMain.GetSize2(URL: string): int64;
var size:string;
begin
  Result:=-1;
  with THTTPSend.Create do
  if HTTPMethod('HEAD',URL) then
    begin
      HeadersToList(Headers);//приводим список заголовков к виду Название=Значение
      size:=Headers.Values['Content-Length'];
      Result:=StrToIntDef(size,-1);
      if Result>-1 then
         Result:=Result+Length(Headers.Text)
    end
end;

Здесь мы воспользовались функцией одного из модулей библиотеки Synapse (функция HeadersToList из модуля synatil) для приведения списка заголовков к виду Имя=Значение и затем просто прочитали значение заголовка, используя стандартные свойства TStringList.

Теперь напишем обработчик события OnStatus. Так как все значения параметра THookSocketReason и процедурные типы событий расположены в модуле blcksock, то подключаем этот модуль в uses и пишем:

type
  TfMain = class(TForm)
    Label1: TLabel;
    urlEdit: TEdit;
    btnGet: TButton;
    pbFile: TProgressBar;
    lbProgress: TLabel;
  private
    Download: int64;//счётчик закачанных данных
    procedure SynaProgress(Sender: TObject; Reason: THookSocketReason;//обработчик OnStatus
    const Value: String);
    //функции получения объема данных для закачки
    function GetSize(URL: string): int64;
    function GetSize2(URL: string): int64;
  public
    { Public declarations }
  end;
 
procedure TfMain.SynaProgress(Sender: TObject; Reason: THookSocketReason;
  const Value: String);
begin
  if Reason=HR_ReadCount then
    begin
      Download:=Download+StrToInt(Value);
      if pbFile.Max>0 then
        begin
          pbFile.Position:=Download;
          lbProgress.Caption:=IntToStr(Trunc((Download/pbFile.Max)*100))+'%';
        end
      else
        lbProgress.Caption:=IntToStr(Download)+' b';
      Application.ProcessMessages;
    end
end;

В этом обработчике мы вначале проверяем, когда сокет вернет нам сообщение HR_ReadCount — прочитано X байт данных. Далее, в случае, если имеется общий объем данных для закачки (GetSize вернула положительное значение в Result), то наращиваем ProgressBar и выводим в Label процент скачаных данных. Если же GetSize вернула в Result -1, то ProgressBar остается незаполненным, а в метку выводим объем полученных данных.

Следует отметить, что не всегда сервер возвращает при HEAD-запросе заголовок Content-Length, поэтому в обработчике и присутствует «лишняя» проверка. Например, если Вы попробуете получить Content-Length при скачивании любой страницы блога WebDelphi, то увидите, что GetSize вернет Вам -1 — заголовок не возвращается.

Теперь нам остается только определить обработчик события OnClick кнопки. Выглядит он следующим образом:

procedure TfMain.btnGetClick(Sender: TObject);
var HTTP: THTTPSend;
    MaxSize: int64;
begin
  Download:=0;
  MaxSize:=GetSize2(urlEdit.Text);
  if MaxSize>0 then
    pbFile.Max:=MaxSize
  else
    pbFile.Max:=0;
  HTTP:=THTTPSend.Create;
  HTTP.Sock.OnStatus:=SynaProgress;//определяем обработчик события
  try
    if HTTP.HTTPMethod('GET',urlEdit.Text) then //если файл скачан
       HTTP.Document.SaveToFile(ExtractFilePath(Application.ExeName)+'file.dat');//сохраняем файл
  finally
    HTTP.Free;//освобождаем память
  end;
end;

Проверить работы программы можно, скачав любой файл, например, я попробовал скачать из блога справочник по Ribbon Controls:
А так выглядит программа при скачивании страницы из блога WebDelphi :

Скачать пример можно по ссылке ниже:

Использование события OnReadFilter. Как скачать часть документа?

Ещё одним полезным событием является событие  OnReadFilter. С помощью него можно легко фильтровать HTTP-траффик при работе объекта THTTPSend. К примеру, если Вам достаточно получить со страницы только её заголовок (title), то, в принципе, незачем скачивать весь объем данных — необходимые нам данные находятся в первой части HTML-страницы и, следовательно, без фильтрации полученных данных, мы будем большой объем данных просто выбрасывать. Чтобы сэкономить траффик нам достаточно воспользоваться событием OnReadFilter.

Для примера, напишем программу, которая будет получать со страницы её заголовок.

Вид главного окна программы показан на рисунке:

Для работы с Synapse подключим в uses следующие модули: SynaUtil, HttpSend, blcksock. Для сохранения документа «на лету» объявим в секции private у класса TfMain переменную AllDocument, а также обработчик события OnReadFilter:

type
  TfMain = class(TForm)
    ...
    lbSize: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    AllDocument: string;
    procedure MyFilter(Sender: TObject; var Value: AnsiString);
  public
 
  end;

Обработчик напишем такой:

procedure TfMain.MyFilter(Sender: TObject; var Value: AnsiString);
begin
  AllDocument:=AllDocument+UTF8ToString(Value);
  if (pos('',AllDocument)>0)and (pos('',AllDocument)>0) then
    begin
      lbTitle.Caption:=GetBetween('','',AllDocument);
      lbSize.Caption:=IntToStr(Length(AllDocument))+' байт';
      TBlockSocket(Sender).CloseSocket;
    end;
end;

В приведенном выше обработчике происходит следующее: вначале в переменную AllDocument дописывается полученная от сервера строка. При этом, значение Value переводится из UTF8 к ANSI. На практике Вы можете столкнуться с ситуацией, когда кодировка страницы не UTF-8 и тогда Вам придётся каким-либо образом предварительно узнать кодировку страницы. Для этого вы можете воспользоваться советами из статьи «3 варианта работы с кодировками веб-страниц в Delphi.«.
Далее, если в переменной AllDocument содержатся и открывающий и закрывающий теги title, то мы:

  1. Получаем строку заголовка, используя функцию GetBetween из модуля SynaUtil
  2. Выводим в lbSize объем загруженной части документа
  3. Закрываем соединение для того, чтобы не докачивать оставшуюся часть документа.
Остается дописать обработчик OnClick кнопки. Обработчик будет таким:
procedure TfMain.Button1Click(Sender: TObject);
var HTTP: THTTPSend;
begin
  AllDocument:='';
  HTTP:=THTTPSend.Create;
  try
     HTTP.Sock.OnReadFilter:=MyFilter;
     if HTTP.HTTPMethod('GET',edURL.Text) then
       begin
         lbSize.Caption:=IntToStr(HTTP.DownloadSize);
         ShowMessage('Документ скачан на 100%');
       end;
  finally
    HTTP.Free;
  end;
end;

Здесь следует обратить внимание на то, что если при выполнении GET-запроса метод HTTPMethod возвращает значение True, то это означает, что весь документ был полностью загружен, т.е. цель (экономия трафика) не достигнута.

Проверяем работу программы. Получим заголовок одной из страниц блога WebDelphi.ru:

Cледует помнить, что использование события OnReadFilter может очень сильно тормозить работу программы, в особенности, когда в обработчике события происходит большое количество операций с переменными. Поэтому можно дать следующие рекомендации по использованию данного вида событий сокета:

  1. Старайтесь делать обработчик как можно короче и с минимальным набором операций
  2. Не используйте это событие, если необходимый элемент документа находится в конце — в этом случае, при использовании OnReadFilter, Вы получите минимум экономии трафика, но скорость работы программы значительно снизится.

Исходник рассмотренного примера можно скачать по ссылке ниже:

Также для фильтрации трафика можно использовать событие OnMonitor.

 Использование события OnMonitor. Как узнать время, за которое файл будет загружен из Сети?

Ещё один довольно частый вопрос, возникающий у тех, кто начинает писать свою «качалку» файлов из Интернет типа ReGet – как узнать время, за которое файл будет загружен из Сети? Следует отметить, что на время закачки файла из Сети постоянно могут влиять масса различных факторов, в том числе и стабильность интернет-соединения, загруженность сервера, загруженность канала и т.д. и т.п. Поэтому при работе того же ReGet вы можете наблюдать как счётчик времени «скачет», то увеличивая время до окончания закачки, то наоборот резко снижая. Это происходит по том, что программа в каждый заданный промежуток времени оценивает текущую скорость соединения с хостом и вычисляет время.

Теперь посмотрим, какие возможности для решения задачи предоставляет нам Synapse, а именно THTTPSend.

Для решения задачи можно использовать сразу три события сокета:

  1. OnStatus
  2. OnMonitor
  3. OnHeartBeat

Можно также использовать и OnReadFilter, но это событие лучше использовать по его первоначальному назначению – для фильтрации трафика.

Событие OnHeartBeat вроде бы идеально подходит под задачу, т.к. мы можем явно указать в свойстве HeartBeatRate объекта через какой промежуток времени опрашивать сокет, но это событие, судя по официальному описанию на сайте разработчика, может вызываться с некоторой погрешностью по времени. Причем погрешность может быть как в плюс, так и в минус. Более того, по словам самих разработчиков Synapse вызов этого события может снижать скорость работы программы, а нам этого совсем не нужно.

Так как событие OnStatus мы уже использовали, то в качестве примера воспользуемся событием OnMonitor. Доработаем наше приложение с ProgressBar’ом и добавим в него расчёт времени до окончания загрузки файла.

Скачиваем исходник:

И добавляем на форму следующие компоненты:

Теперь вернемся к нашей задаче. Как оценить время за которое скачается весь файл? Для этого достаточно воспользоваться простой пропорцией:

Здесь V1 – это размер данных, полученный за промежуток времени T1, а X – это время за которое будет скачан объем V2.

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

И здесь нам на помощь снова приходит модуль Synapse  synautil. Cейчас нам для работы потребуются две функции:

function GetTick: LongWord;
function TickDelta(TickOld, TickNew: LongWord): LongWord;

Функция GetTick возвращает текущее значение системного таймера с точностью 1 мс, а TickDelta рассчитывает разницу между двумя значениями таймера.

Используя GetTick мы можем засечь  значение системного таймера относительно которого в дальнейшем мы сможем просчитать значение X из пропорции. Также у нас уже есть функция GetSize, которая вернет нам общий объем данных, который нам необходимо загрузить (V2). Остается последняя переменная — объем V1. Значение V1 мы будем получать из параметров уже имеющейся переменной Download.

Вначале допишем обработчик OnClick кнопки «Скачать»:

procedure TfMain.btnGetClick(Sender: TObject);begintry
    StartTick := GetTick; // засекаем время начала загрузки
   if HTTP.HTTPMethod('GET', urlEdit.Text) then.
end;

Теперь напишем создадим обработчик события OnMonitor:

procedure TfMain.MyMonitor(Sender: TObject; Writing: Boolean;
  const Buffer: TMemory; Len: Integer);
begin
  if not Writing then //были прочитаны данные
     begin
       ElapsedTime := TickDelta(StartTick, GetTick) div 1000;
       lbTime.Caption := IntToStr(Trunc(ElapsedTime * (DocSize - Download) / Download))+' c.';
       lbElapsed.Caption := IntToStr(ElapsedTime)+ 'c.';
     end;
end;

Теперь можете запустить программу и проверить работу, скачав более менее большой файл. Счётчик времени будет работать с погрешностью в 1 секунду.

Исходник программы можно скачать по ссылке ниже:

Работа с HTTPS в Synapse 


Прежде, чем перейдем к рассмотрению примеров работы с HTTPS в Synapse, рассмотрим сам механизм работы библиотеки с защищенными протоколами (в данном случае с https).

Для того, чтобы THTTPSend смог отправлять запросы по https Вам необходимо подключить в uses модуль ssl_openssl.pas. При этом THTTPSend сможет отправлять запросы по https где не требуется предоставления сертификатов, проверка подписей и т.д. Где такая работа возможна?  Например, такая поддержка https может потребоваться при работе с протоколом ClientLogin в Google.

Теперь рассмотрим, что происходит при подключении модуля ssl_openssl в uses модуля вашей программы.

В секции Initialization модуля ssl_openssl происходит инициализация плагина для работы c SSL:

initialization
  if InitSSLInterface then
    SSLImplementation := TSSLOpenSSL;

Здесь следует обратить внимание на метод InitSSLInterface. В этом методе происходит загрузка двух динамических библиотек:

  • ssleay32.dll или libssl32.dll
  • libeay32.dll
Библиотеки должны находится в директории с программой. Скачать dll-ки Вы можете, например, здесь. InitSSLInterface возвращает true в случае, если все необходимые для работы с SSL DLL-библиотеки загружены. В случае успеха SSLImplementation становится уже не просто TCustomSSL, а его потомком — TSSLOpenSSL в котором и реализована вся поддержка SSL включая создание self-signed сертификата на время сеанса.
Если загрузка библиотек невозможна (библиотеки отсутствуют), то ничего не происходит InitSSLInterface остается «заглушкой», но, тем не менее THTTPSend получает возможность отправлять запросы на адреса, начинающиеся с https://.
Следует отметить, что подключение модуля ssl_openssl к проекту никак не влияет на дальнейшую работу THTTSSend с HTTP-протоколом.
Пример авторизации на сайте с использованием HTTPS Вы можете посмотреть в посте «Synapse. Авторизация на сайте. Работа с HTTPS.«. В этом примере не требуется наличие библиотек.

Использование различных свойств THTTPSend


Использование RangeStart и RangeEnd. Как скачать часть файла?

В предыдущем примере, когда мы рассматривали работу с событием OnReadFilter мы скачивали часть HTML-страницы, основываясь на поиске в полученной части документа определенной строки. Этот способ достаточно удобен, когда требуется скачивать именно текстовые документы.

HTTP позволяет запросить не всё содержимое ресурса сразу, а только указанный фрагмент. В таком случае речь идёт о частичных GETвозможность их выполнения необязательна для серверов. Частичные GET в основном используются для докачки файлов и/или быстрого параллельного скачивания в нескольких потоках.

Для выполнения частичных GET-запросов мы можем воспользоваться двумя свойствами THTPSend — RangeStart и RangeEnd. С помощью этих двух свойств мы можем указать серверу какую часть файла нам необходимо получить.

Общие сведения о байтовых диапазонах

Для того, чтобы запросить с сервера определенный фрагмент ресурса необходимо определить в запросе заголовок Range (диапазон). В этом заголовке мы должны указать: название единицы измерения (bytes – байты), начало и окончание диапазона. При задании заголовка возможно указывать несколько диапазонов через запятую или опускать одну из частей диапазона. Диапазон указывается через символ “-”.

Рассмотрим пример задания диапазонов. Пусть, например, ресурс X имеет размер в 6000 байт. Вот как можно задавать байтовые диапазоны:

1. Произвольный фрагмент:

Range: bytes=3000-5900

range_1Скачиваем с 3000 по 5900 байт включительно.

2. Один байт:

Range: bytes=3051-3051

range_2

Скачиваем 3051 байт документа.

3. Фрагмент от начала до определенного байта:

Range: –5000

range_3

Скачиваем с 0 по 5000 байт. Эта запись эквивалентна записи для скачивания произвольного фрагмента, т.е. мы могли бы указать диапазон так:

Range: bytes=0-5000

4. Фрагмент, содержащий количество скачиваемых бай от конца ресурса:

Range: bytes=1000-

range_4

Скачиваем последние 1000 байт, т.е. от 5000 до 5999

5. Несколько фрагментов

Range: bytes=1000-, 100-1000

Скачиваем последние 1000 байт, а также фрагмент с 100 по 1000 байт включительно.

range_5

Как сказано выше – докачка не обязательно должна поддерживаться сервером. Конечно, большинство серверов её поддерживают, но не все и не всегда. Как понять, что сервер поддерживает докачку файлов?

Провести такую проверку можно опытным путем, отправив, например, такой заголовок на сервер:

Range: bytes=1000-

В этом случае ответ сервера может содержать следующие коды статуса:

  • 200 (Ok)– докачка файлов не поддерживается, а тело запроса будет содержать весь документ
  • 206 (Partial Content) – докачка поддерживается, а тело запроса содержит данные из указанного диапазона
  • 416 (Requested Range Not Satisfiable) – ни один из указанных в заголовке Range диапазонов не является корректным.

Пример частичного скачивания файла

Теперь перейдем к реализации примера. Создадим приложение как показано на рисунке:

На форме расположены следующие элементы:

  • edURL: TEdit — поле ввода URL расположения файла
  • edStart, edEnd: TEdit — поля ввода начала и окончания диапазона запрашиваемых данных
  • lbSize: TLabel — метка для вывода количества скачанных байт файла
  • memHeaders: TMemo — Memo для вывода заголовков ответа сервера
  • btnGet:TButton — кнопка для запуска процесса скачивания.
Пишем обработчик события OnClick кнопки:
procedure TfMain.btnGetClick(Sender: TObject);
var HTTP: THTTPSend;
begin
  HTTP:=THTTPSend.Create;
  try
    HTTP.RangeStart:=StrToIntDef(edStart.Text,0);
    HTTP.RangeEnd:=StrToIntDef(edEnd.Text,0);
    if HTTP.HTTPMethod('GET',edURL.Text) then
      begin
        lbSize.Caption:=IntToStr(HTTP.Document.Size);
        memHeaders.Clear;
        memHeaders.Lines.Assign(HTTP.Headers);
      end
    else
      ShowMessage(HTTP.ResultString);
  finally
    HTTP.Free;
  end;
end;

В обработчике создается объект HTTP:THTTPSend которому присваиваются значения свойств RangeStart и RangeEnd. После чего выполняется запрос на заданный в edURL адрес и, в случае успешно выполненного запроса, в Mome выводятся полученные от сервера заголовки.

Теперь запустим программу и попробуем скачать часть какого-либо файла. Результат работы программы показан на рисунке ниже:

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

Скачать рассмотренный пример программы Вы можете по ссылке ниже:

Использование ProxyHost, ProxyPort и Sock. Как работать с прокси в Synapse?

Еще одной замечательной возможностью THTTPSend является поддержка всех видов прокси: HTTP proxy, SOCKS 4 и SOCKS5 proxy.

HTTP proxy сервера сегодня самые популярные прокси. Возможность их использования включена во многие программы: браузеры, download менеджеры, ICQ-клиенты и т.д. Соответственно, в Сети Вы можете обнаружить большое количество сайтов, раздающих списки HTTP proxy. Недостатком публичных HTTP proxy является то, что они очень быстро «умирают». Вопрос о том, как проверить proxy на работоспособность мы оставим в стороне (про это вы можете прочитать в статье «Synapse. Работа с прокси.»), а сосредоточимся на непосредственном использовании свойств класса THTTPSend.

Отличительной особенностью SOCKS прокси является то, что SOCKS позволяет работать с любыми (версия Socks 4 — с TCP, Socks 5 — с TCP и UDP) протоколами. SOCKS proxy передает данные от клиента к серверу, не вникая в содержимое и поэтому может работать с HTTP, FTP, SMTP, POP3, NNTP и т.д.

Итак создадим новое приложение Delphi на главной форме которого расположим следующие компоненты:

На форме расположены следующие элементы:

  • edURL: TEdit — поле для ввода целевого URL
  • cbTypeProxy: TComboBox — список выбора типа прокси. Список содержит следующие строки: HTTP, Socks4, Socks5
  • edProxyHost : TEdit — поле для ввода хоста прокси или его IP-адреса
  • edProxyPort: TEdit — поле для ввода порта прокси
  • btnGet: TButton — кнопка для выполнения GET-запроса
  • memText: TMemo — Memo для вывода полученных от сервера данных.
Специально для демонстрации этого примера я создал небольшой PHP-скрипт результат которого будет возвращать нам переменные окружения сервера по значению которых мы сможем судить как о работоспособности прокси, так и о его типе (прозрачный, анонимный и т.д.)

Напишем такой обработчик OnClick у кнопки:

procedure TForm8.btnGetClick(Sender: TObject);
var HTTP: THTTPSend;
begin
 HTTP:=THTTPSend.Create;
 try
   //настраиваем параметры прокси
   case cbTypeProxy.ItemIndex of
     0:begin
         HTTP.ProxyHost:=edProxyHost.Text;
         HTTP.ProxyPort:=edProxyPort.Text;
       end;
     1:HTTP.Sock.SocksType:=ST_Socks4;
     2:HTTP.Sock.SocksType:=ST_Socks5;
   end;
   if cbTypeProxy.ItemIndex>0 then
     begin
       HTTP.Sock.SocksIP:=edProxyHost.Text;
       HTTP.Sock.SocksPort:=edProxyPort.Text;
     end;
   //отправляем запрос
   if HTTP.HTTPMethod('GET',edURL.Text) then
     begin
       memText.Clear;
       memText.Lines.LoadFromStream(HTTP.Document);
     end
   else
     ShowMessage('Ошибка выполнения запроса');
 finally
   HTTP.Free
 end;
end;

Здесь мы в зависимости от выбранного типа прокси настраиваем либо непосредственно свойства объекта HTTP (ProxyHost, ProxyPort), либо, если используется SOCKS proxy — свойства Sock, а также указываем версию SOCKS proxy. После этого отправляется GET-запрос на заданный URL и результат запроса выводится в Memo.
Проверка работы с HTTP proxy показана на рисунке:
Как видно по ответу, полученному от сервера (см. вывод в Memo) наш прокси прекрасно работает, т.к. переменные окружения сервера возвращают его IP-адрес, более того этот прокси является анонимным, т.к. скрывает наш собственный IP-адрес.

Проверяем работу SOCKS proxy:

При работе с SOCKS proxy Вы можете столкнуться с такой проблемой: proxy 100% рабочий, однако THTTPSend не может выполнить запрос на сервер. В этом случае можно предложить следующие варианты решений:

  1. Проверить версию SOCKS — попробуйте изменить свойство SocksType. Иногда на сайте можно встретить запись о том, что прокси является SOCKS5, но на деле оказывается, что это SOCKS4 (как раз такая ошибка была с прокси на рисунке выше)
  2. Попробуйте присвоить свойству Sock.SocksResolver значение False — в большинстве случаев работы с SOCKS прокси это помогает решить проблему.

Скачать исходник программы можно по ссылке ниже:

 Использование свойств Document и Headers. Как отправить multipart/form-data на сервер?

Достаточно популярным вопросом у тех, кто знакомиться с работой в Сети вообще является вопрос о том как отправить файл на сервер, а иногда и несколько файлов одним POST-запросом. В Synapse, а точнее у THTTPSend подобные операции делаются достаточно просто с использованием всего двух свойств — Document и Headers.

Небольшая справка по вопросу из Вики:

Multipart/form-data — это заголовок поля Content-Type, использующийся для обозначения множественного содержимого. Со стороны клиента чаще всего используются при отправке HTML-формы, файлов, данных, не входящих в набор ASCII, двоичных данных методом POST.

При использовании заголовка нам необходимо обеспечить следующее:

  1. Задать строку, которая будет использоваться в качестве разделителей частей документа
  2. Правильно определить MIME-типы каждой из частей документа
  3. Правильно сформировать заголовок Content-Type
  4. Правильно сформировать тело документа.
Подготовим небольшое приложение, которое будет отправлять на сервер файл и возвращать результат операции. В качестве примера рассмотрим работу с сайтом imagevenue.com.
Главная форма приложения будет содержать следующие элементы:

  • edFile: TEdit — поле для ввода пути к отправляемому на сервер файлу
  • edMime: TEdit — поле для ввода MIME-типа файла
  • edBoundary: TEdit — поле для задания разделителя частей документа. Вообще граница может задаваться и без участия пользователя, но с помощью этого поля нам будет проще разобраться с вопросом.
  • memText: TMemo — Memo для вывода текста страницы, полученной от сервера после выполнения запроса
  • btnFile: TButton — кнопка для запуска диалога открытия графического файла
  • btnBoundary: TButton — кнопка клик по которой будет генерировать случайную строку-разделитель для щапроса
  • btnSend: TButton — кнопка отправки файла на сервер
  • dlgOpen: TOpenPictureDialog — диалог открытия графического файла.
Теперь приступим к реализации нашей программы по пунктам представленным выше. И первый шаг — задать строку, которая будет использоваться в качестве разделителей частей документа.
В качестве строки-разделителя частей документа может выступать последовательность байт, которые не должны встречаться в самом файле.
Обычно браузеры генерируют такие разделители произвольно либо по какому-либо заданному алгоритму. Напишем такую функцию для генерации разделителя:
function TForm8.GetBoundary: string;
begin
  Result:=IntToHex(Random(MaxInt), 8) + '_webdelphi';
end;

И обработчик OnClick кнопки btnBoundary:

procedure TForm8.btnBoundaryClick(Sender: TObject);
begin
  edBoundary.Text:=GetBoundary
end;

При нажатии на кнопку будет генерироваться произвольная строка, которая и будет в дальнейшем использоваться в запросах к серверу. Для большей надежности разделитель содержит также помимо HEX-значения числа и произвольную строку, в данном случае — это строка «_webdelphi«.

Переходим к следующей части наше программы и разберемся как правильно определить MIME-типы каждой из частей документа. MIME-тип части тела запроса можно задать и вручную, однако при большом обили различных типов файлов такой подход к разработке программы будет неправильным, да и сами пользователя совсем не обязаны знать какой тип данных они отправляют и как он определяется. Поэтому логично, что все более менее серьезные библиотеки для работы с Сетью имеют в своем составе специальные методы, которые позволяют определять MIME-тип файла автоматически, например, по его расширению. В данном случае и библиотека Synapse не является исключением из правил. Определить MIME-тип файла по его расширению можно, используя модуль MIMEPart.pas, а точнее метод класса TMimePart — MimeTypeFromExt. Этот метод принимает на входе имя файла, включая его расширение и присваивает одному из свойств класса значение MIME-типа.  Недостатком использования этого способа является малое количество MIME-типов доступных для метода — всего 25 штук.

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

Все известные в Windows MIME-типы Вы можете найти в реестре:

HKEY_CLASSES_ROOT\MIME\Database\Content Type

Можно было бы воспользоваться работой с реестром и каждый раз искать MIME-тип непосредственно в ветке, но этот способ намного более затратный по времени выполнения, чем поиск по массиву констант. В исходнике, который Вы можете скачать ниже Вы можете найти такой массив, содержащий все известные MIME-типы для моей Windows 7. В случае необходимости можете добавить в него свои MIME-типы.

Так как в функции используется достаточно большой массив (на 376 элементов), то метод был вынесен в отдельный модуль MimeFinder.pas в котором определен константый массив следующего вида:

MimeTypesArray: array [1 .. 376, 1 .. 2] of string =
    (('.nml', 'animation/narrative'),
    ('.aac', 'audio/mp4'),
    ('.aif', 'audio/x-aiff'),
    ('.aifc', 'audio/x-aiff'),
    ('.aiff', 'audio/x-aiff'),
    ('.au', 'audio/basic'),
    ('.gsm', 'audio/x-gsm'),
    ('.kar', 'audio/midi'),
    ....)

И сама функция поиска в массиве:

function MimeTypeByExt(const FileName: string):string;
var s: string;
    i:integer;
begin
  s:=lowerCase(ExtractFileExt(FileName));
  for I := 1 to MimeTypeCount do
     begin
       if s=MimeTypesArray[i,1] then
         begin
           Result:=MimeTypesArray[i,2];
           Exit;
         end;
     end;
  Result:='application/octet-stream';
end;

Теперь подключаем модуль к нашему проекту и пишем обработчик OnClick кнопки bthFile:

procedure TForm8.btnFileClick(Sender: TObject);
begin
  if dlgOpen.Execute then
    begin
      edFile.Text:=dlgOpen.FileName;
      edMime.Text:=MimeTypeByExt(edFile.Text)
    end;
end;

Трех ста семидесяти шести различных MIME-типов Вам будет вполне достаточно, чтобы правильно составить запрос на отправку практически любого файла на сервер.

Переходим к следующим шагам: Правильно сформировать заголовок Content-Type и Правильно сформировать тело документа.

Теперь мы напишем обработчик OnClick кнопки btnSend, которая и отправит файл на сервер. Выглядит обработчик следующим образом:

procedure TForm8.btnSendClick(Sender: TObject);
Const
  CRLF = #$0a + #$0d;
var
  HTTP: THTTPSend;
  s: AnsiString;
  FS: TFileStream;
begin
HTTP := THTTPSend.Create;
  try
    FS := TFileStream.Create(edFile.Text, fmOpenRead);
    {Определяем Contetn-Type запроса}
    HTTP.MimeType := 'multipart/form-data; boundary='+edBoundary.Text;
    { Записываем Mime-тип и данные по файлу }
    s := '--'+edBoundary.Text + CRLF +
      'Content-Disposition: form-data; name="userfile[]"; filename="'+ExtractFileName(FS.FileName)+'"'
      + CRLF + 'Content-Type: '+edMime.Text + CRLF + CRLF;
    HTTP.Document.Write(PAnsiChar(s)^, Length(s));
    FS.Position := 0;
    // записываем файл в тело документа
    HTTP.Document.CopyFrom(FS, FS.Size);
    { завершаем тело запроса }
    s := CRLF + '--'+edBoundary.Text+'--' + CRLF;
    HTTP.Document.Write(PAnsiChar(s)^, Length(s)); // завершили тело документа
    // Отправляем запрос
    if HTTP.HTTPMethod('POST', 'http://imagevenue.com/upload.php') then
      memText.Lines.LoadFromStream(HTTP.Document);
  finally
    FS.Free;
    HTTP.Free;
  end;
end;

В этом обработчике мы вначале определяем Content-Type запроса. Для этого мы присваиваем новое значение свойству MimeType. Обратите внимание, что в значении заголовка мы также указываем и сгенерированный нами разделитель частей тела документа.
Далее начинается самое интересное — формирование тела запроса.

Каждая часть тела запрос разделяется нашим разделителем. При этом обратите особое внимание на то как записывается разделитель в теле запроса — открывает новую часть разделитель с двумя предшествующими разделителю символами «-«. Именно так. Далее записывается строка, содержащая заголовок Content-Desposition и на новой строке MIME-тип файла (тот ради которого мы писали функцию).
После этого мы пропускаем 1 строку (см. вставку двух CRLF в строке s). Опять же — запомните это.
И только после этого мы записываем в Document непосредственно данные файла.
После этого на новой строке снова вставляем разделитель при этом указываем двойной «-» как ДО так и ПОСЛЕ строки разделителя. Такая запись означает, что част документа закончена и далее идёт новый фрагмент, с новым MIME-типом, расположением и т.д. (это если отправляется несколько файлов)
И только после этого мы выполняем POST запрос на URL загрузки файла на сервер.

Теперь можно запустить программу и проверить её работу, отправив на сервер какой-нибудь графический файл:

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

Скачать исходник программы, включая модуль для работы с MIME-типами файлов, Вы можете по ссылке ниже:

Использование свойства Cookies. Как анализировать Cookies, полученные от сервера?

Довольно часто при работе с сайтами без использования специальных API можно столкнуться с такой задачей — требуется проанализировать и отобрать определенные cookies или просто отправить все полученные на предыдущем шаги cookies в следующем запросе. Например, отправка cookies может потребоваться при работе с WordPress-блогом, если в нем отключена поддержка XML-RPC.

Посмотрим как можно получат и анализировать cookies в Synapse на примере работы с WordPress. Создадим новое приложение Delphi, расположив на главной форме приложения следующие элементы:

  • edURL : TEdit — URL в результате запроса на который сервер устанавливает куки
  • btnGet: TButton — кнопка для отправки запроса на сервер
  • memCookies: TMemo — Memo для вывода Cookies
Пример программы достаточно прост и одновременно с тем — показателен. Напишем следующий обработчик OnClick кнопки:
procedure TForm8.btnGETClick(Sender: TObject);
var HTTP: THTTPSend;
begin
  HTTP:= THTTPSend.Create;
  memCookies.Lines.Clear;
  try
    if HTTP.HTTPMethod('GET',edURL.Text) then
       memCookies.Lines.Assign(HTTP.Cookies);
  finally
    HTTP.Free
  end;
end;

Не вдаваясь опят же в вопросы анализа сайта, скажу, что Cookies устанавливаются после обращения к URL следующего вида:
http://блог.ru/wp-login.php?redirect_to=http%3A%2F%2Fблог.ru%2Fwp-admin%2F&reauth=1
Посмотрим, что будет содержать Memo после отправки GET-запроса:

Если же Вы проверите заголовки сервера с помощью какого-либо сниффера, то увидите следующую картину:

Как видите сервер устанавливал больше cookies, чем вывелось в Memo. Причина кроется в функции парсинга cookies в Synapse. Эта функция выглядит следующим образом:

procedure THTTPSend.ParseCookies;
var
  n: integer;
  s: string;
  sn, sv: string;
begin
  for n := 0 to FHeaders.Count - 1 do
    if Pos('set-cookie:', lowercase(FHeaders[n])) = 1 then
    begin
      s := SeparateRight(FHeaders[n], ':');
      s := trim(SeparateLeft(s, ';'));
      sn := trim(SeparateLeft(s, '='));
      sv := trim(SeparateRight(s, '='));
      FCookies.Values[sn] := sv;
    end;
end;

То есть, следуя алгоритму функции все одноименные Cookies «склеиваются» в списке и в результате кука у которой есть «двойник» в заголовках получает последнее по списку значение.
Также следует отметить, что в список Cookies попадают только пары имя=значение, а такие атрибуты как пут и домен отсекаются парсером. Если Вам необходим другой способ работы с cookies, то можете написать Helper для THTTPSend, либо переписать уже имеющийся парсер cookies Synapse.

Использование других библиотек совместно с THTTPSend. Как работать с GZip в Synapse?


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

Всё “общение” клиента и сервера на предмет того, что ожидает клиент, какие возможности поддерживаем и т.д. осуществляется с помощью заголовков. Пример использования заголовков мы рассматривали выше, когда изучали работу со свойствами RangeStart и RangeEnd, в принципе, аналогичным образом мы можем сказать серверу, что хотим получить данные сжатые с помощью GZip. Для этого используется следующий заголовок:

Accept-Encoding: gzip,deflate

Что касается версии протокола HTTP, то немного покопавшись в Сети на тему какая из версий протоколов поддерживает GZip сжатие, нашел следующую информацию: nginx не отдает gzip если протокол HTTP/1.0, даже если его об этом просят (в заголовках). Для этого необходимо настроить опцию gzip_http_version, указав минимальную версию протокола 1.0, после чего сервер будет отдавать gzip по протоколу 1.0.

Таким образом, если сервер поддерживает GZip сжатие, то для надежности лучше всего у THTTPSend задавать следующие значения:

HTTPSend.Headers.Add('Accept-Encoding: gzip,deflate');//заголовок
HTTPSend.Protocol:='1.1';//по умолчанию выставляется 1.0

После этого, если сервер может отдавать клиентам сжатые данные, Вы будете их получать. Обращаю на это внимание, т.к. не всегда удается получить то, что нам нужно. И здесь мы плавно переходим ко второму вопросу.

Как узнать, что сервер вернул сжатые данные?

Ровно также как и попросить отдать данные в сжатом виде – через заголовки. Если сервер возвращает сжатые данные, то в заголовках сервера обязательно есть заголовок Content-Encoding. Для нашего случая он будет таким:

Content-Encoding: gzip

Остается только проанализировать заголовки и определить есть ли в них представленный выше заголовок и его значение. С помощью Synapse это можно сделать, например, так:

with THTTPSend.Create do
  begin
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);//привели заголовки к виду Name=Value
      {проверяем заголовок}
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
      begin
        //тут разжимаем данные
      end
      else
        begin
          //так как данные не сжаты - просто считываем строку
          HTMLMemo.Lines.Text :=ReadStrFromStream(Document,Document.Size)
        end;
    end;
  end;
end;

В представленном выше коде используются только возможности библиотеки Synapse, а именно, процедура HeadersToList и функция ReadStrFromStream находятся в модуле SynaUtil.pas о котором я немного рассказывал в статье «Глубины Synapse или дополнительные возможности библиотеки«.

Использование GZip в Synapse?

Для работы с GZip в Delphi в том числе и с Synapse имеется бесплатная библиотека под названием delphi zlib. Также как и Synapse эта библиотека содержит набор классов и методов и никаких визуальных или не визуальных компонентов. Чтобы не таскать каждый раз за собой все модули этой библиотеки при создании новых проектов – просто укажите путь к delphi zlib в настройках Delphi:

Tools – Options – Delphi Options – Library – Library Path

zlib_path

Для распаковки сжатых с помощью GZip данных в модуле ZLibExGZ предусмотрены следующие методы:

для строк:

function  GZDecompressStr(const s: RawByteString): AnsiString; overload;
procedure GZDecompressString(var result: UnicodeString; const s: RawByteString); overload;

И ещё несколько переопределенных методов. На мой взгляд, представленные выше два метода наиболее удобны в использовании для использования их совместно с Synapse.

для потоков:

procedure GZDecompressStream(inStream, outStream: TStream); overload;

И ещё несколько переопределенных методов. Соответсвенно в различных ситуациях нам будет удобно пользоваться либо методами для строк, либо для потоков. Например, использование метода GZDecompressStream может оказаться удобным в таком случае:

var
  DecompStream: TStringStream;
begin
  with THTTPSend.Create do
  begin
    Headers.Add('Accept-Encoding: gzip,deflate');
    Protocol:='1.1';
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
      begin
        DecompStream := TStringStream.Create;
        try
          GZDecompressStream(Document, DecompStream);
          Memo1.Lines.Text := DecompStream.DataString;
        finally
          DecompStream.Free
        end;
      end
      else
        Memo1.Lines.Text :=ReadStrFromStream(Document,Document.Size)
    end;
  end;
end;

Можно переписать этот же код, но используя функцию для распаковки строки:

var
  S: RawByteString;
begin
  with THTTPSend.Create do
  begin
    if CheckBox1.Checked then
      Headers.Add('Accept-Encoding: gzip,deflate');
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);
      S:=ReadStrFromStream(Document,Document.Size);
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
        Memo1.Lines.Text := GZDecompressStr(S)
      else
        Memo1.Lines.Text:=S;
    end;
  end;

Приведенный выше листинги возвращают один и тот же результат — распакованные данные. Подведем небольшой итог по работе с GZip в Synapse:

  1. Для того, чтобы попробовать получить данные сжатые GZip серверу необходимо передать заголовок Accept-Encoding
  2. Для гарантированного получения gzip (если сервер поддерживает такую возможность) необходимо свойству Protocol у объекта THTTPSend всегда указывать значение ‘1.1’. Для проверки, попробуйте получить сжатые данные от Яндекс, не указывая версию протокола 1.1.
  3. Для распаковки данных удобно использовать библиотеку delphi zlib в состав которой входят методы для распаковки строк и потоков с данными.

Наиболее распространенные вопросы по работе с THTTPSend


Как работать с TMemoryStream, TStringist и т.д.?

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

Кроме этого, в библиотеке Synapse присутствует довольно «навороченный» модуль SynaUtils.pas в котором содержится большое количество удобных функций по работе как с потоками, так и списками, строками и т.д. С помощью этого модуля можно значительно сократить время на разработку программ.

Как получить все cookies, полученные от сервера?

Ответ: для хранения cookies, полученных от сервера необходимо воспользоваться свойством THTTPSend:

Cookies: TStringList

Пример получения cookies от сервера можно увидеть выше.

Отправляю повторный запрос на сервер и не получаю ответа. Как быть?

Ответ: Метод HTTPMethod не стирает полученные в предыдущем запросе заголовки (свойство Headers), куки (свойство Cookies) и тело запроса (свойство Document), а использует их для отправки на заданный URL. Для того, чтобы повторный запрос выполнился успешно, необходимо удалить все записи из Headers, Cookies и Document, полученные первым запросом и, при необходимости заполнить их данными, которые необходимо отправить на сервер.

Как получить размер файла, который необходимо скачать из Сети?

Ответ: необходимо отправить HEAD-запрос на URL и, в случае успешно выполненного запроса, получить значение заголовка “Content-Length” из свойства Headers: TStringList, либо для Delphi 2010/Delphi XE воспользоваться хэлпером.

Как указать в заголовках размер отправляемого на сайт файла?

Ответ: никак. При выполнении HTTPMethod заголовок Content-Length заполнится автоматически, на основании данных, записанных в теле запроса (свойство Document: TMemoryStream).

Отправляю POST-запрос на сервер и не получаю ответа, либо возвращается код 4хх. Что я делаю не так?

Ответ: самая распространенная ошибка при отправке POST – неправильно заполненный заголовок Content-Type. По умолчанию этот заголовок имеет значение text/html. Для смены значения заголовка необходимо воспользоваться свойством MimeType: string, например так:

...
var HTTP: THTTPSend;
begin
...
  HTTP:=THTTPSend.Create;
  HTTP.MimeType:='application/x-www-form-urlencoded';
  if HTTP.HTTPMethod('POST','http://webdelphi.ru') then
    ...
end;

Также помните, что в некоторых случаях серверу необходимо пересылать cookies, полученные на предыдущем шаге работы с сервером.

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

Ответ: Для всех видов кодирования/декодирования данных в Synapse используется модуль synacode.pas. Подключите его в uses и получит в распоряжение все необходимые методы, в т.ч. EncodeURL и EncodeURLElement для кодирования URL и его отдельных частей.

Загружаю данные web-странички в Memo а там вместо русских букв кракозябры. Synapse глючит?

Ответ: Synapse не глючит, а возвращает то, что попросили. Для того, чтобы текст в Memo стал читабельным необходимо исправить его кодировку. Подробное описание способов работы с кодировками есть здесь. Кроме того некоторые функции по преобразованию кодировок можно найти и в модуле SynaUtils.pas библиотеки Synapse.

Как сделать визуализацию скачивания/закачки больших файлов?

Ответ: для этого необходимо обработать событие соккета OnStatus. Пример реализации скачивания файла с ProgressBar’ом есть здесь. Про другие события можно прочитать выше в разделе «События сокета».

Как работать с HTTPS в Synapse?

Ответ: Для работы с HTTPS в Synapse необходимо подключить в uses модуль ssl_openssl.pas. При этом в папке с проектом необходимо держать следующие DLL: libeay32.dll, ssleay32.dll. Скачать последние версии библиотек можно здесь. Про работу модуля можно прочитать в разделе «Работа с HTTPS в Synapse«.

Вместо заключения


На этом можно завершить рассказ о том, как работать с THTTPSend. Думаю, что, прочитав эту статью от начала и до конца Вы сможете без проблем использовать этот замечательный класс в своих программах и написать свои парсилки, регалки или просто клиенты для работы с сайтами. Естественно, что в одной, пусть даже и такой достаточно развернутой статье невозможно осветить абсолютно все вопросы, касающиеся работы с Сетью в Delphi. Так, при написании этой статьи я не рассматривал такие важные вопросы как типы запросов и когда их использовать в программах или вопросы, касающиеся анализа содержимого web-форм и работу со снифферами. Цель этой статьи была — максимально подробно рассказать о классе THTTPSend и показать, что используя его, можно написать программу практически любой сложности, а все, что не попало под эту цель, думаю, что имеет смысл рассматривать отдельно и посвятить этому ещё не один пост :).

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

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
77 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Robinzon
26/07/2011 04:41

Полезная статья, спасибо Влад :)
P.S.: Добавил в закладки.

m1sclick
27/07/2011 16:16

Вопрос немного не в ту тему, а как при помощи TPOP3Send проверить количество писем на почте?
 
А по теме статья хороша, больше не нужно по 100 раз пользоваться поиском по блогу если что то забыл.

kolos1010
kolos1010
02/08/2011 01:25

Спасибо за статью. Пишу для своих нужд чекер ссылок, почему-то некоторые страницы не открываются ни через EmbeddedWB ни через HTTPMethod. При использовании Synapse выходит ошибка «Операция прервана». В то же время в обычном браузере эти адреса открываются нормально. Например, вот один урл: http://nashaplaneta.net/asia/malaysia/kuala-lumpur4.php Не могли бы вы подсказать какой-нибудь выход?
 

kolos1010
kolos1010
03/08/2011 00:58

 
Да нет, эту ошибку выдает THTTPSend.
Посмотрел заголовки через bertal.ru — редиректа нет. Таймаут происходит — это ведь когда долго страница загружается? Но ошибка выходит почти сразу.
А используется Ваша же функция:LoadDocument(url), взял отсюда http://www.webdelphi.ru/2010/05/mshtml-v-delphi-analiz-ssylok-na-stranice/, вот и весь исходник.

Трейлер
03/08/2011 23:57

Подскажите пожалуйста как лечить такое:
Методом Post отправляю данные, среди этих данных есть URL который содержит символ &. А он интерпретируется как разделитель. Как отправить & как данные, чтобы он не воспринимался как разделитель?  

m1sclick
16/08/2011 17:16

А THTTPSend.Create чтоли нельзя использовать в событии OnShow формы?
То есть есть приложение с кнопкой, и без. В первом случае HTTPSend создается нажатием кнопки а во втором событием OnShow — так с кнопкой все работает все запросы отправляются, а второй вариант не работает….

kolos1010
kolos1010
20/08/2011 23:56

 
Здравствуйте, как я понял Synapse не работает с кириллическими доменами?

kolos1010
kolos1010
28/08/2011 16:18

С кириллическими доменами не работает, буду пробовать через punycode сначала преобразовывать. Еще одна проблема — пишу чекер ссылок, при загрузке страницы через THTTPSend выскакивает ошибка на определенных сайтах «Эта страница имеет изъян в системе безопасности и может быть опасной. Хотите продолжить?» Не подскажите, как убрать это окно или чтобы оно вообще не появлялось. Используется Ваша же функция:LoadDocument(url), взял отсюда http://www.webdelphi.ru/2010/05/mshtml-v-delphi-analiz-ssylok-na-stranice/ Спасибо.

kolos1010
kolos1010
01/09/2011 12:40

Спасибо, что отвечаете на все вопросы. Сделал как вы указали, к сожалению, не помогло. Я и сам не пойму, каким образом выскакивает это окно. Используется только LoadDocument(url), причем здесь Internet Explorer непонятно. Найти бы способ хотя бы блокировать появление этого окна.

basmach
basmach
07/09/2011 09:47

Спасибо за статью!
Я с удовольствием читаю Ваши статьи. Иногда здорово вручает.
Читал почти все статьи, но ненашел ответа на мучающий мне вопрос:
В Indy есть компонент TidMappedPortTcp.  На дельфи 7 используя его делал простой прозрачный прокси.
На Synapse можно делать такой? В какую сторону надо копать? Я еще не очень разбираюсь на нем.
 

basmach
basmach
08/09/2011 23:24

Спосибо!

Зод
Зод
28/09/2011 00:03

Здрпавствуйте!
Отправляю post запрос методами синапса. Столкнулся с проблемой что не отправляется знак +. Вместо него в сниффере показывается пробел. Не подскажите как это обойти и все таки отправить +?
Пробовал EncodeURLParams, не помогло.

kolos1010
kolos1010
07/10/2011 00:51

Вы оказались правы — synapse ни причем. Окошко «Эта страница имеет изъян в системе безопасности и может быть опасной. Хотите продолжить?» выходит при выполнении последней строчки. Выставлял самый низкий уровень безопасности, обновлял Interner Explorer, все равно выскакивает окно. Как избавиться от этого окошка, помогите пожалуйста. Хотя бы за деньги.
WB_Doc:=CoHTMLDocument.Create as IHTMLDocument2;
V:=VarArrayCreate([0,0],varVariant);
V[0]:=Content.Text;
WB_Doc.write(PSafeArray(TVarData(v).VArray));

rrusz
rrusz
25/10/2011 11:39

а где скачать эту библиотеку? в 7-ке я чет не нашел(

kolos1010
kolos1010
10/11/2011 16:36

 
Никогда не понимал авторов таких сайтов. Vlad вы что избирательно отвечаете? Вы уже несколько моих вопросов проигнорировали. Вам все таки человек с просьбой обращается. Хотя бы напишите: «Иди нах со своими тупыми вопросами» или «Я ни бум-бум в это, разбирайся сам». А то пишешь-пишешь и еще ждешь зря ответа.

yuri.cherepanov
yuri.cherepanov
17/11/2011 16:08

Влад, подскажи пожалуйста, каким образом распараллелить (потоками) ssl сессию, т.е. одним httsend’ ом подключиться по HTTPS к серверу и выполнять параллельно много запросов( в рамках одной функции). Т.е. что в HTTPSEND  определяет ssl сессию (помимо cookies)? Заранее спасибо.

yuri.cherepanov
yuri.cherepanov
18/11/2011 11:07

Влад, подключение не к Google, а к платному сервису, в котором используются и сертификат и пароль. Подключение проходит нормально и один объект работает идеально. Работа заключается в выполнении одной функции, где происходит порядка 15 запросов к этому сервису. Но задача в том, чтобы запускать эту функцию одновременно несколько раз (на сегодня я вижу только поток как решение). Проблема в том, что платный сервис не позволяет выполнять несколько авторизаций одновременно. Если будет 10 копий объекта, то на мой взгляд будут коллизии, так как выполняется все в рамках одного объекта. Мне кажется что нужно параметр сессии передавать новым объектам HTTPSEND. например для… Подробнее »

yuri.cherepanov
yuri.cherepanov
18/11/2011 13:20

все равно спасибо за помощь, Влад. Ковырять модуль… Я уже пробовал менять в HTTPSEND.pas свойства для THTTPSEND доступные только для чтения и делал их еще и записываемыми, чтобы новому объекту присвоить к примеру SSL свойства…не помогло, но может делал как-нибудь не правильно, так что еще раз помучаю то что пытался сделать с потоком и куками и всем остальным.

Дима
Дима
21/12/2011 13:49

Влад, приветствую, подскажи пожалуйста http://www.ararat.cz/synapse/doku.php/download  пойдет -ли под XE2 и если да, там кучи в основном pas-ов, как это добро устанавливается?

tim21701
tim21701
31/12/2011 16:16

Влад, с наступающим! ))
Сделайте хотя бы одну статью по EX2, где описано подключение к удалённому серверу и скачивание базы SQL на комп…
Спасибо.

Andrey
Andrey
30/01/2012 22:09

Очень меня мучает один баг в синапсе, если у сервера в заголовке ответа указан TransferEncoding : chunked;
И сервер отдает пожатые g-zipom данные то синапс читает только первые два байта.
Как его исправить? 

Andrey
Andrey
31/01/2012 02:48

Сча проверил все ок, я наверное не правильно обрабатывал поток, или даже незнаю.
Но условия при которых насколько я помню были ошибки, все вышеуказанное и отсутствие заголовка ContentLength,  http://forum.xakepok.su/index.php вот стр. но тут кон тент лент отображается.

 

Andrey
Andrey
31/01/2012 03:10

Я в htttp протоколе не оч разбираюсь, но отсутствие ContentLength при чанкед енкодинге по логике нормальное явление, т.к чанкед расчитан на то что сервер не знает точно сколько надо будет отдать, и в конце каждого чанка пишет размер.
Вот есче оч заинтересовал вопрос как синапс юзать в C++ не нашол в архиве заголовочных файлов и вообще ничего связаного с C++ но на сайте указано что можно в билдере использовать.

Andrey
Andrey
31/01/2012 03:23

http://synapse.ararat.cz/doku.php/public:howto:cppbuilder
Тут описана  работа с синапсом билдере, точнее как файлы сконвертровать, спасибо вам отVlad за такое обилие статей по синапсу, когда начинал с синапсом работать использовал ваш блог как инструкцию, все досконально описано.
 

Прохожий
Прохожий
01/02/2012 04:44

Респект! Автору донного топика — респект и уважуха! )))
Спасиюо огромное. Всё понятно и доступно.

JokerBaD
JokerBaD
17/02/2012 03:04

Здравствуйте Влад!Может я малёха не по теме)Можно ли работать HTTPSend с  NTLM Authentication?С Basic все просто получилось а вот как реализовать с  NTLM авторизацией незнаю не поможете?работа авторизаций NTLM  http://www.innovation.ch/personal/ronald/ntlm.html 

JokerBaD
JokerBaD
17/02/2012 18:51

СПС Влад за отклик.Там еще и https протокол!Получилось все это реализовать на PHP с помощью Curl.Оказалось не так уж сложно.Сделал свой сайт как прокси)Ну а на делфи нормального кода не нашел и так и не получилось(

Сергей
Сергей
12/03/2012 01:44

Всем привет, кто нить подскажет а как через синапс проверить на существование рабочей ссылки, а то ситуация такая, в файл без расширения пишется текст, в программе по таймеру периодически идет проверка на существование это файла, если он существует то идет считывание, раньше через компоненты инди это делал, там исключение вызывалось если файла по ссылке не было, щас решил перейти на синапс, пробую так:

HttpGetText('http://вашсайт.нет/файл', Memo1.Lines);

так он считывает исходный код сайта, если файла нет, а нужно чтобы выдал к примеру

ShowMessage('файла нет!');

 

Михаил
Михаил
18/03/2012 14:34

Приветствую! У меня довольно специфичная проблема: страница получается не полностью. Явное указывание размера дает ровно такой же результат. Больше 3750 символов нивкакую. Может есть те, кто сталкивался с этим. Прошу помочь в данном вопросе. web:=utf8decode(ReadStrFromStream(Document,Document.Size)); web: string

Михаил
Михаил
19/03/2012 06:37

Нашел ответ )) Собственно проблемы как таковой и не было. Оказывается в режиме дебага содержимое переменной показывалось не полностью, однако весь текст там присутствовал ))

yuri.cherepanov
yuri.cherepanov
23/03/2012 13:43

Vlad, вы тут писали в ответе про chunked что «Я обычно всегда страхуюсь определением события сокета, которое мне «скажет», что контент 100% пришел и можно обработать данные». Vlad, не могли бы подробнее описать пример этой подстраховки. Мне это необходимо для такой же подстраховки. Заранее спасибо.

yuri.cherepanov
yuri.cherepanov
31/03/2012 22:24

Спасибо большое, Влад, я так и предполагал по поводу кода ответа. Но вопрос в том, что ответ может быть и 200, но вот данные все равно не те. Но это уже особенности запроса и сервера. Спасибо еще раз. 
P.S.: и как Indy?)))

yuri.cherepanov
yuri.cherepanov
01/04/2012 00:19

Влад, Indy — это вообще мрак, но к сожалению иногда без него никак. У меня вот проект, где комбинация и synapse и indy на разных приложениях, так с synapse я редко мучаюсь, а indy.. .Вот к примеру не идет IdHTTP через прокси никак и все. и единственный совет от пользователей Indy — только обновить их до супер последней версии. А толку никакого. У меня коллеги свой очень крупный проект перевели с индей на синапс тока вот из-за таких багов. И сейчас очень довольны.

Alex11223
Alex11223
14/04/2012 17:15

Тут ошибка, мне кажется:
Метод Clear
procedure Clear;
Очищает поля класса и присваивает им значения по умолчанию.

На самом он очищает только документ, хедер и миме тип ставит по умолчанию. Собственно это и указано в комментариях\документации: Reset headers and document and Mimetype
и сам код:
FRangeStart := 0;
FRangeEnd := 0;
FDocument.Clear;
FHeaders.Clear;
FMimeType := ‘text/html’;

coma_cat
coma_cat
15/10/2012 21:31

Вначале напишем функцию, которая будет получать размер данных, которые необходимо скачать из Сети. Назовем эту функцию GetSize…

Зачем делать это:

for I := 0 to Headers.Count — 1 do
begin
if pos(‘content-length’,lowercase(Headers[i]))>0 then
begin
size:=»;
for ch in Headers[i]do
if ch in [‘0’..’9′] then
size:=size+ch;
Result:=StrToInt(size)+Length(Headers.Text);
break;
end;
end;

…или это:

HeadersToList(Headers);//приводим список заголовков к виду Название=Значение
size:=Headers.Values[‘Content-Length’];

…когда можно сделать это:
http.Headers.NameValueSeparator := ‘:’;
size := http.Headers.Values[‘Content-Length’];))

Ndker
Ndker
22/11/2012 14:47

Хорошая статья, только в пункте «Использование свойств Document и Headers. Как отправить multipart/form-data на сервер?» в примере ошибка CRLF = #$0a + #$0d; Местами перепутаны, должно быть CRLF = #$0d + #$0a;

Sergei
Sergei
23/11/2012 15:29

Статья отличная, но у меня возникла такая проблемка, может где что недопонял, как шифруется и передается сам знак амперсанд(&) через пост запрос синапса, а то переменной присваивается текст и если там будет типа &a=5 то он разделяет текст и полная строка не передается.

Sergei
Sergei
23/11/2012 17:34

Я в курсе что им можно подчеркнуть первый символ, но суть вопроса именно в передаче через post synapse, к примеру такой код:
var HTTP: THTTPSend;
data:TStringstream;
stroka:string;
begin
try
HTTP := THTTPSend.Create;
data:=TStringStream.Create(»);
stroka:=’тратата http://site.ru/viewtopic.php?a=6&b=3‘;
Data.WriteString(‘name=’);
Data.WriteString(‘&txt=’stroka);
Data.Position:=0;
HTTP.Document.LoadFromStream(Data);
HTTP.MimeType := ‘application/x-www-form-urlencoded’;
if HTTP.HTTPMethod(‘POST’,’http://site.ru/send.php’) then showmessage(‘отправлено’);
finally
FreeAndNil(http);
FreeAndNil(data);
end;
end;
И вот в результате такой передачи получается передать только ‘тратата http://site.ru/viewtopic.php?a=6‘ а ‘&b=3’ он заносит как еще дополнительная переменная, а нужно чтобы он отправлял всю строка не имея значения какие символы там используются вот.

Sergei
Sergei
26/11/2012 03:45

Нашел решение проблемы, вот в этой строке над было просто подправить Data.WriteString(‘&txt=’+EncodeURLElement(stroka)); ранее обращался к этой функции но не получалось потому что я кодировал весь передаваемый параметр, а над было просто значение во как)

trackback

[…] Библиотека Synapse. Работа с модулем HTTPSend.pas. […]

Сергей
Сергей
30/05/2013 01:22

Подскажите как в программе которая в цикле читает письма с мыла и скачивает архив правильно проверить дисконнект?
Делать пинг каждый проход к примеру на гугл, или по таймеру секунд в 10 прерывать этот цикл?

Артем
Артем
04/12/2013 03:48

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

Подскажите пожалуйста, как отследить ответ. Пытаюсь работать с яндекс АПИ, а именно OAuth. Для авторизации я отправляю следующую строку методом GET

https://oauth.yandex.ru/authorize?response_type=token&ampclient_id=223145
Сервер при этом перебрасывает меня на другую (пустую) страницу. Значения в строке адреса меняются. Подскажите пожалуйста, как извлечь эти данные (получить ответ от сервера и вывести в мемо).

Сергей
Сергей
04/12/2013 20:03

сначала посмотри что отсылает на сервер браузер с помощью хттп аналирера и продублируй

Артем
Артем
04/12/2013 22:57
Ответить на  Сергей

Спасибо, я почти сразу разобрался. Анализатор HTTP установил.

Артем
Артем
07/12/2013 06:12

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

Подскажите пожалуйста как решить проблему, третий день мучаюсь.
Нужно серверу через idHTTP отправить get запрос. в одном из параметров должен присутствовать русский текст
http://site.ru/strminu?prev=привет
но заместо слова ПРИВЕТ, отправляются знаки вопроса -> http://site.ru/strminu?prev=???????

пытался и TEncoder,UTF8Encode,SetCodePage — кучу способов перепробовал, не помогает. может как то их нужно комбинировать или что-то другое применить. подскажите пожалуйста

Артем
Артем
07/12/2013 17:29
Ответить на  Vlad

Превратил. Сервер не хочет это воспринимать. Смотрю HTTP запросы аналогичной программы, она как раз передает русскую строку в таком виде http://site.ru/strminu?prev=привет в данный момент я передаю в таком http://site.ru/strminu?prev=%D0%E3%5

Артем
Артем
07/12/2013 21:52
Ответить на  Vlad

HTTP/1.1 400 Bad request

Я хочу отправить яндексу каптчу, но связи с тем, что русский текст отображается ???? или через %DA&5D, то поялвяется такая ошибка.

Пример ссылки, которую я отправляю яндексу

http://yandex.ru/checkcaptcha?rep=?????&key=20O9jBAE3GNWIHRD884HZ6kdb89j9qEa_0/1386427592/1333273f783c31d329f44cb084f95f50_5d98d885f6bc32382275f52e63fd24f3&retpath=http://yandex.ua/yandsearch?site=entropiya-blog.ru&text=&lr=143&redircnt=1386204099.1&ncrnd=4362_9880d495480da89eebca5be4eb68d1ba

rep-ответ на каптчу (вводится в edit.text)
key и retpath — поля hidden (на странице отображения каптчи, я сделал парсинг их значений и отправляю яндексу, вместе с ответом на каптчу.

если не брать во внимания функции парсинга, то вот он

strUri:String;
strUri:=’http://yandex.ru/checkcaptcha?rep=’+Edit2.Text+’&key=’+key+’&retpath=’+retpath;
IdHTTP1.Get(strUri);

trackback

[…] может показаться меньшим, чем ранее опубликованная большая статья на тему о работе THTTPSend. Также я не рассмотрел здесь и работу с HTTPS. Скажу лишь, […]

Donni
Donni
15/03/2014 13:51

А если использовать if httpsend.httpmethod(‘GET’,’ya.ru’) then то какие ошибки будут считаться за false(404,503 и тд)?

trackback

[…] просто, подключив в uses всего один модуль — blcksock. У объекта THTTPSend имеются следующие […]

trackback

[…] Synapse. Для работы нам понадобиться всего один модуль Synapse — httpsend.pas. Создадим новое приложение и подключим этот модуль в […]