Synapse. Сокеты


Чтобы посмотреть этот PDF файл с форматированием и разметкой, скачайте его и откройте на своем компьютере.
Библиотека Synapse

Авторы: Баженов В., Балабаев М.

© 2013 5elphi в Internet. Публикация на других ресурсах сети Интернет без
разрешения авторов запрещена


Оглавление

Глава

1.

Работа

с

HTTP

в

Synapse

................................
................................
................................
................

1

Общие

сведения

о

протоколе

................................
................................
................................
................

1

Стартовая

строк
а

................................
................................
................................
................................
..

3

Методы

HTTP

................................
................................
................................
................................
.......

4

Коды

состояния
................................
................................
................................
................................
....

4

Заголовки

................................
................................
................................
................................
.............

6

Тело

сообщени
я

................................
................................
................................
................................
...

7

Анализ

сообщений

клиента

и

сервера

................................
................................
................................
..

7

Модуль

HTTPSend
.
pas

................................
................................
................................
...........................

10

Класс

THTTPSend

................................
................................
................................
................................

10

Вспомогательные

методы

................................
................................
................................
................

13

Примеры

работы

с

HTTP

в

Synapse

................................
................................
................................
......

14

Первый

пример:

скачиваем

большие

файлы

с

заполнением

ProgressBar
’а

................................

14

Второй

пример:

пишем

свой

аналог

свойства

HandleRedirects

для

THTTPSend

..........................

18

Третий

пример:

фильтруем

считываемые

данные.

................................
................................
.......

22

Четвертый

п
ример:

частичное

скачивание

файла

с

сервера.

Частичный

GET
.

............................

26

Пятый пример: Использование
GZip

в
Synapse
.

................................
................................
..............

35



Глава

1.

Работа

с

HTTP

в

Synapse

Общие

сведения

о

протоколе

Протокол

передачи

гипертекста

HTTP

(
H
yper

T
ext

T
ransfer

P
rotocol)



это

базирующийся

на

TCP/IP

протокол

передачи

гипертекста,

обеспечивающий

доступ

к

документам

на

web
-
узлах.


Осн
овная

задача

протокола

состоит

в

установлении

связи

с

web
-
сервером

и

обеспечении

доставки

HTML
-
страниц

web
-
браузеру

клиента
.

Протокол

HTTP:



определяет

взаимодействие

партнеров

на

прикладном

уровне;



предназначен

для

передачи

сообщений,

являющихся

блоками

ги
пертекста;



используется

в

службе

глобального

соединения.

Транспортным

протоколом

для

HTTP

является

протокол

TCP,

причем

сервер

HTTP

(сервер

Web)

находится

в

состоянии

ожидания

соединения

со

стороны

клиента

(
стандартно

по

порту

80

TCP
)
,

а

клиент

HTTP

(брауз
ер

Web)

является

инициатором

соединения.


Взаимодействие

между

клиентом

и

сервером

Web

осуществляется

путем

обмена

сообщениями.


Сообщения

HTTP

делятся

на

запросы

клиента

серверу

и

ответы

сервера

клиенту
.

Каждое

HTTP
-
сообщение

состоит

из

трёх

частей,

кото
рые

передаются

в

указанном

порядке:



Стартовая

строка

(англ.

Starting

line
)



определяет

тип

сообщения;



Заголовки

(англ.

Headers
)



характеризуют

тело

сообщения,

параметры

передачи

и

прочие

сведения;



Тело

сообщения

(англ.

Message

Body
)



непосредственно

дан
ные

сообщения.

Обязательно

должно

отделяться

от

заголовков

пустой

строкой.

Заголовки

и

тело

сообщения

могут

отсутствовать,

но

стартовая

строка

является

обязательным

элементом
,

так

как

указывает

на

тип

запроса/ответа.

Исключением

является

версия

0.9

протоко
ла



настоящее

время

наиболее

Рис. 1.
Взаимодействие клиента и сервера по протоколу HTTP


HTTP
по
TCP

HTTP
по
TCP

Клиент

Сервер

часто

используется

версия

1.1)
,

у

которой

сообщение

запроса

содержит

только

стартовую

строку,

а

сообщения

ответа

только

тело

сообщения.

В

самом

общем

случае

з
апросы

и

ответы

выглядят

следующим

образом:

стартовая

строка

заголо
вок

1

заголовок

2

...

заголовок

N

CR

LF

(пустая

строка)

тело

сообщения.

Стартовая

строка

Стартовые

строки

различаются

для

запроса

и

ответа.


Строка

запроса

выглядит

так:

Метод

URI

HTTP/Версия

Здесь:

Метод



название

запроса,

одно

слово

заглавными

буквами.

В

версии

HTTP

0.9

использовался

только

метод


методы

для

версии

1.1

могут

быть

как

,

так

и

POST
,

PUT

и

т.д
.

О

методах

в

HTTP

мы

поговорим

ниже.

URI

определяет

путь

к

запрашиваемому

документу.

Версия



пара

разделённых

точкой

арабских

цифр.

Например:

1.0.

Например,

чтобы

запросить

главную

страницу

какого
-
либо

сайта

клиент

может

передать

такую

стартовую

строку:


/
index
.
html

HTTP
/1.
1

Стартовая

строка

ответа

сервера

имеет

следующий

формат:

HTTP/Версия

КодСостояния

Пояснение

Здесь:

Версия



пара

раздел
ённых

точкой

арабских

цифр

как

в

запросе.

КодСостояния

(англ.

Status

Code)



три

арабские

цифры.

По

коду

статуса

определяется

дальнейшее

содержимое

сообщения

и

поведение

клиента.

Б
олее

подробно

о

кодах

состояния

будет

рассказано

ниже
.

Пояснение

(англ.

Reason

Phrase)



текстовое

короткое

пояснение

к

коду

ответа

для

пользователя.

Никак

не

влияет

на

сообщение

и

является

необязательным.

Например,

на

предыдущий

наш

запрос

клиентом

данной

страницы

сервер

ответил

строкой:

HTTP/1.0

20
0

OK

Методы

HTTP

Метод

HTTP

(англ.

HTTP




последовательность

из

любых

символов,

кроме

управляющих

и

разделителей,

указывающая

на

основную

операцию

над

ресурсом.

Обычно

метод

представляет

собой

короткое

английское

слово,

записанное

заглавными

буквам
и.

Н
азвание

метода

чувствительно

к

регистру.

Каждый

сервер

обязан

поддерживать

как

минимум

методы


и

HEAD
.

Если

сервер

не

распознал

указанный

клиентом

метод,

то

он

должен

вернуть

статус

501

(Not

Implemented)
.

Если

серверу

метод

известен,

но

он

непримени
м

к

конкретному

ресурсу,

то

возвращается

сообщение

с

кодом

405


Not

Allowed)
.

В

обоих

случаях

серверу

следует

включить

в

сообщение

ответа

заголовок

Allow

со

списком

поддерживаемых

методов.

Кроме

методов


и

HEAD,

часто

применяется

метод

POST.

Опис
ание

различных

методов

HTTP

приведено

в

Приложении

X

Рассмотрим

методы

HTTP

более

подробно.

Коды

состояния

К
од

состояния

является

частью

первой

строки

ответа

сервера.

Он

представляет

собой

целое

число

из

трех

арабских

цифр.


Первая

цифра

указывает

на

клас
с

состояния.

За

кодом

ответа

обычно

следует

отделённая

пробелом

поясняющая

фраза

на

английском

языке,

которая

разъясняет

человеку

причину

именно

такого

ответа.


Примеры
:

201

Created

403

Access

allowed

only

for

registered

users

507

Insufficient

Storage


Кли
ент

узнаёт

по

коду

ответа

о

результатах

его

запроса

и

определяет,

какие

действия

ему

предпринимать

дальше.

Набор

кодов

состояния

является

стандартом,

и

они

описаны

в

соответствующих

документах

RFC.

Клиент

может

не

знать

все

коды

состояния,

но

он

обязан

отр
еагировать

в

соответствии

с

классом

кода.

В

настоящее

время

выделено

пять

классов

кодов

состояния.

1xx

Informational


(Информационный)

В

этот

класс

выделены

коды,

информирующие

о

процессе

передачи.

В

HTTP/1.0

сообщения

с

такими

кодами

должны

игнорироваться
.

В

HTTP/1.1

клиент

должен

быть

готов

принять

этот

класс

сообщений

как

обычный

ответ,

но

ничего

отправлять

серверу

не

нужно.

Сами

сообщения

от

сервера

содержат

только

стартовую

строку

ответа

и,

если

требуется,

несколько

специфичных

для

ответа

полей

заголов
ка.

Прокси
-
серверы

подобные

сообщения

должны

отправлять

дальше

от

сервера

к

клиенту.

2xx

Success

(Успех)

Сообщения

данного

класса

информируют

о

случаях

успешного

принятия

и

обработки

запроса

клиента.

В

зависимости

от

статуса

сервер

может

ещё

передать

загол
овки

и

тело

сообщения.

3xx

Redirection

(Перенаправление)

Коды

класса

3xx

сообщают

клиенту

что

для

успешного

выполнения

операции

необходимо

сделать

другой

запрос

(как

правило

по

другому

URI).

Из

данного

класса

пять

кодов

301,

302,

303,

305

и

307

относятся

н
епосредственно

к

перенаправлениям.

Адрес,

по

которому

клиенту

следует

произвести

запрос,

сервер

указывает

в

заголовке

Location
.

При

этом

допускается

использование

фрагментов

в

целевом

URI.

4xx

Client

Error

(Ошибка

клиента)

Класс

кодов

4xx

предназначен

для

указания

ошибок

со

стороны

клиента.

При

использовании

всех

методов,

кроме

HEAD
,

сервер

должен

вернуть

в

теле

сообщения

гипертекстовое

пояснение

для

пользователя.

5xx

Server

Error

(Ошибка

сервера)

Коды

5xx

выделены

под

случаи

неудачного

выполнения

операции

по

вине

сервера.

Для

всех

ситуаций,

кроме

использования

метода

HEAD
,

сервер

должен

включать

в

тело

сообщения

объяснение,

которое

клиент

отобразит

пользователю.

Список

различных

кодов

состояния

представлен

в

Приложении

X
.

Заголовки

Заголовки HTTP

(англ.
HT
TP Headers
)


это строки в HTTP
-
сообщении,
содержащие разделённую двоеточием пару
параметр
-
значение
. Формат
заголовков соответствует общему формату заголовков текстовых сетевых
сообщений ARPA (RFC 822). Заголовки должны отделяться от тела сообщения
хотя бы

одной пустой строкой.

Примеры

заголовков
:

Server: Apache/2.2.11 (Win32) PHP/5.3.0

Last
-
Modified: Sat, 16 Jan 2010 21:16:42 GMT

Content
-
-
1251

Content
-
Language: ru


В примере выше каждая строка представляет собой один заголо
вок. При этом
то, что находится до первого двоеточия, называется именем (англ.
name
), а что
после неё


значением (англ.
value
).

Все заголовки разделяются на четыре основных группы:

1.

General Headers

(рус. Основные заголовки)


должны включаться в
любое сооб
щение клиента и сервера.

2.

Request Headers

(рус. Заголовки запроса)


используются только в
запросах клиента.

3.

Response Headers

(рус. Заголовки ответа)


только для ответов от
сервера.

4.

Entity Headers

(рус. Заголовки сущности)


сопровождают каждую
сущность со
общения.

Именно в таком порядке
рекомендуется

посылать заголовки получателю
.

Е
сли вам не будет хватать существующих

заголовков
, то можете смело
вводить свои. Традиционно к именам таких дополнительных заголовков
добавляют префикс «X
-
» для избежания конфликт
а имён с возможно
существующими. Например, как в заголовках
X
-
Powered
-
By

или
X
-
Cache
.
Некоторые разработчики используют свои индивидуальные префиксы.
Примерами таких заголовков могут служить
Ms
-
Echo
-
Request

и
Ms
-
Echo
-
Reply
, введённые корпорацией Microsoft
для расширения WebDAV.

В

Приложении

Х

представлены

наиболее

часто

встречающиеся

заголовки

всех

групп
,

мы

же

детально

остановимся

на

рассмотрении

только

некоторых

из

них.

Тело

сообщения

Тело

HTTP

сообщения

(
message
-
body
),

если

оно

присутствует,

используетс
я

для

передачи

тела

объекта,

связанного

с

запросом

или

ответом.

Тело

сообщения

(
message
-
body
)

отличается

от

тела

объекта

(
entity
-
body
)

только

в

том

случае,

когда

применяется

кодирование

передачи,

что

указывается

полем

заголовка

Transfer
-
Encoding
.

Поле

Tran
sfer
-
Encoding

должно

использоваться

для

указания

любого

кодирования

передачи,

примененного

приложением

в

целях

гарантирования

безопасной

и

правильной

передачи

сообщения.

Поле

Transfer
-
Encoding



это

свойство

сообщения,

а

не

объекта,

и,

таким

образом,

може
т

быть

добавлено

или

удалено

любым

приложением

в

цепочке

запросов/ответов.

Правила,

устанавливающие

допустимость

тела

сообщения

в

сообщении,

отличны

для

запросов

и

ответов.

Присутствие

т
ела

сообщения

в

запросе

отмечается

добавлением

к

заголовкам

запроса

поля

заголовка

Content
-
Length

или

Transfer
-
Encoding
.

Тело

сообщения

может

быть

добавлено

в

запрос

только

когда

метод

запроса

допускает

тело

объекта,

например,

при

использовании

методов

POS
T

и

PUT

.

Включается

или

не

включается

тело

сообщения

в

сообщение

ответа

зависит

как

от

метода

запроса,

так

и

от

кода

состояния

ответа.

Все

ответы

на

запрос

с

методом

HEAD

не

должны

включать

тело

сообщения,

даже

если

присутствуют

поля

заголовка

объекта,

за
ставляющие

поверить

в

присутствие

объекта.

Никакие

ответы

с

кодами

состояния

1xx

(Информационные),

204

(Нет

содержимого,

No

Content),

и

304

(Не

модифицирован,

Not

Modified)

не

должны

содержать

тела

сообщения.

Все

другие

ответы

содержат

тело

сообщения,

даже

если

оно

имеет

нулевую

длину.

Анализ

сообщений

клиента

и

сервера

Довольно

часто

при

работе

с

различными

ресурсами

в

Сети

нам

необходимо

знать

то,

каким

образом

«общаются»

между

собой

обычный

web
-
браузер

(например,

Internet

Explorer
)

и

сервер,

для

того,

чт
обы

на

основании

этих

данных

организовать

работу

своего

собственного

клиента

для

сайта/блог/форума.


Если

Вы

внимательно

прочитали

всё,

что

сказано

выше

про

HTTP
,

то

наверняка

уже

усвоили

то,

что

даже

не

имея

в

распоряжении

всего

сообщения,

оперируя

только

его

стартовой

строкой

и

заголовками
,

можно

определить,

какой

запрос

был

отправлен

на

сервер,

что

ответил

сервер

на

этот

запрос

и,

при

необходимости,

определить

причину

того

или

иного

ответа.


Для

примера

рассмотрим

такой

диалог

клиента

и

сервера:

Запрос

к
лиента
:


/wiki/страница

HTTP/1.1

Host:

ru.wikipedia.org

User
-
Agent:

Mozilla/5.0

(X11;

U;

Linux

i686;

ru;

rv:1.9b5)

Gecko/2008050509

Firefox/3.0b5

Accept:

text/html

Connection:

close

(
пустая

строка
)


Ответ

сервера
:

HTTP/1.1

200

OK

Date:

Wed,

11

Feb

2009

11:20:59

GMT

Server:

Apache

X
-
Powered
-
By:

PHP/5.2.4
-
2ubuntu5wm1

Last
-
Modified:

Wed,

11

Feb

2009

11:20:59

GMT

Content
-
Language:

ru

Content
-
Type:

text/html;

-
8

Content
-
Length:

1234

Connection:

close


Проанализируем

этот

диалог

по

порядку,

начиная

с

самой

первой

строки

запроса

клиента
.


/wiki/
страница

HTTP/1.1

Host
:

ru
.
wikipedia
.
org

Здесь

наш

клиент

запрашивает

документ

/wiki/страница
,

используя

версию

протокола

HTTP

1.1

с

ресурса

ru.wikipedia.org
.

User
-
Agent:

Mozilla/5.0

(X11;

U;

Linux

i686;

ru;

rv:1.9b5)

Gecko/2008050509

Firefox/3.0b5

В

этом

заголовке

клиент

указывает

свое

программное

обеспечение

(имя

и

версию

браузера

и

другую

информацию)
.

Accept
:

text
/
html

Connection
:

close

Клиент

указывает,

что

он

«понимает»

как

простой

plain
-
text

так

и

HTML
-
код
,

а

также

указывает

серверу

на

то,

что

после

окончания

передачи

сеанс

нужно

закрыть.

Теперь

разберем

ответ

сервера
:


HTTP
/1.1

200

OK

Date
:

Wed
,

11

Feb

2009

11:20:59

GMT


Здесь

сервер

сообщает

клиенту,

что

его

запрос

был

успешно

обработан

в

среду

11

февр
аля

2009

года

в

11:20:59

по

GMT
.

Server
:

Apache

Указывает

свое

название

X
-
Powered
-
By:

PHP/5.2.4
-
2ubuntu5wm1

В дополнительном заголовке сервер «рассказывает» клиенту о своем
программном обеспечении

Last
-
Modified
:

Wed
,

11

Feb

2009

11:20:59

GMT

Здесь

сервер

у
казывает

время

последнего

изменения

передаваемого

нам

документа.

Content
-
Language
:

ru

Язык

документа

-

русский

Content
-
Type:

text/html;

-
8

Тип

содержимого



простой

текст

или

html
-
код

в

кодировке

UTF
-
8

Content
-
Length:

1234

Объем документа (
именн
о документа
, а не всего сообщения) составил 1234
байта

Connection:

close

Соединение

будет

закрыто
.

Как

видите,

при

анализе

заголовков

клиента

и

сервера

мы

смогли

получить

массу

полезной



не

очень)

для

нас

информации,

включая

даты

последнего

изменения

док
умента,

кодировку

содержимого

в

ответе,

размер

содержимого,

название

и

программное

обеспечение

сервера

и

т.д.

От

того

на

сколько

правильно

мы

интерпретируем

«общение»

клиента

и

сервера

зависит

очень

много



вплоть

до

работоспособности

нашего

программного

о
беспечения

вообще.


Теперь,

вооружившись

этой

минимально

необходимой

для

дальнейшей

работы

информацией
,

мы

можем

приступать

к

рассмотрению

работы

библиотеки

Synapse

с

протоколом

HTTP
.

М
одул
ь

HTTPSend
.
pas

Для

работы

с

HTTP

в

Synapse

предназначен

модуль

HTTP
Send
.
pas
.

Этот

модуль

содержит

класс

THTTPSend
,

реализующего

клиент

HTTP

и

ряд

вспомогательных

функций

для

выполнения

наиболее

распространенных

операций

при

работе

с

HTTP

(
функции

для

скачивания

странички,

файла

и

т.д.).

Исследование

модуля

мы

начнем

с

к
ласса

THTTPSend
.

Класс

THTTPSend



THTTPSend
,

как

уже

было

сказано

выше,

представляет

с
обой

реализацию

клиента

для

работы

с

HTTP
.


Все

операции,

которые

Вы

планируете

выполнить

с

HTTP

в

Synapse

так

или

иначе

буду

связаны

с

использованием

именно

этого

клас
са.

Рассмотрим,

какие

свойства

и

методы

содержит

этот

класс.

Свойства

класса:

property

Headers:

TStringList

Заголовки
.

Перед

выполнением

HTTP

запроса

в

этот

список

можно

занести

любые

заголовки
,

за

исключением

следующих
:

'
Expect
:

100
-
continue
'
,

'
Content
-
Le
ngth
'
,

'
Content
-
Type
'
,

'
Connection
'
,

'
Authorization
'
,

'
Proxy
-
Authorization
'

and

'
Host
'

(
эти

заголовки

заполняются

самим

классом

непосредственно

перед

выполнением

запроса
).

После

выполнения

запроса

этот

список

будет

содержать

заголовки

ответа

сервера.

TObject
TSynaClient
THTTPSend
prop
erty

Cookies:

TStringList

Список
,

содержащий

все

куки

запроса
.

Куки

в

списке

представлены

парами

name
-
value
.


property

Document:

TMemoryStream

Поток,

содержащий

тело

запроса

(перед

его

отправкой

на

сервер)

или

тело

ответа

(после

выполнения

запроса)

prope
rty

RangeStart:

integer

Определяет

позицию

в

документе,

начиная

с

которой,

необходимо

его

получать

с

сервера.

Если

значение

равно

0

(по

умолчанию),

то

документ

запрашивается

с

самого

начала.

property

RangeEnd:

integer

Определяет

позицию

в

документе

до

кот
орой

его

необходимо

скачивать.

Если

значение

равно

0

(по

умолчанию)

то

запрашивается

весь

документ.

property


str
ing

Mime
-
тип

данных
,

которые

будут

отправлены

на

сервер
.

По

умолчанию


text/html


property

Protocol:

string

Версия

HTTP
.

Допустимые

значения
:

‘0.9’,’1.0’,

‘1.1’
.

Значение

по

умолчанию

‘1.0’

property

KeepAlive:

Boolean

Значение

по

умолчанию

-

True
.

Указывает,

следует

ли

поддерживать

постоянное

соединение

с

сервером

при

обмене

данными

property


integer

Значение

интерва
ла

в

секундах

в

течение

которого

необходимо

поддерживать

постоянное

соединение

property

Status100:

Boolean

True

означает,

что

перед

отправкой

данных

запроса

следует

ожидать

ответа

сервера

с

кодом

100

Continue
.

property

ProxyHost:

string

Доменное

имя

или

IP
-
адрес

прокси
-
сервера

property

ProxyPort:

string

Порт

прокси
-
сервера

property

ProxyUser:

string

Если

прокси
-
сервер

требует

авторизации

пользователя,

то

это

поле

содержит

логин

property

ProxyPass:

string

Если

прокси
-
сервер

требует

авторизации

пользоват
еля,

то

это

поле

содержит

пароль

доступа

к

серверу

property

UserAgent:

string

Значение

заголовка

User
-
Agent
.

По

умолчанию

содержит


Mozilla
/4.0

(
compatible
;

Synapse
)’

property

ResultCode:

Integer

Код

состояния

после

вып
олнения

запроса

property

ResultString:

string

Описание

код

состояния

property

DownloadSize:

integer

Содержит

размер

загруженных

данных

после

выполнения

запроса

property

UploadSize:

integer

Содержит

размер

отправленных

на

сервер

данных

после

выполнения

з
апроса.

property

Sock:


TCP

сокет,

используемый

для

выполнения

операций

по

протоколу

HTTP

property

AddPortNumberToHost:

Boolean

True

означает,

что

в

заголовок


Host:


будет

также

включаться

и

номер

порта.

Некоторые

серверы

могут

не

принима
ть

заголовок,

содержащий

номер

порта.

Значение

по

умолчанию

True
.


Методы

класса:

procedure

Clear;

Очищает

списки

заголовков

(
Headers
),

куки

(
Cookies
)

и

тело

запроса

(
Document
)

procedure

DecodeStatus
(
const

Value
:

string
);

Разбирает

стартовую

строку

ответа

сервера

и

заполняет

свойства

ResultCode

и

ResultString

function



URL:

string
):

Boolean;

Отправляет

запрос

методом


на

адрес,

определенный

в

параметре

URL
.

Это

единственный

метод

класса

для

отправки

любых

запросов

на

серве
р.

Если

запрос

выполнен

успешно,

то

функция

вернет

True
.

procedure

Abort
;

Прерывает

работу

TCP

сокета.



Вот

и

всё,

что

содержит

класс

THTTPSend
.

Немного,

неправда

ли?

Однако,

как

мы

увидим

далее,

этих

свойств

и

методов

вполне

достаточно,

чтобы

реализова
ть

практически

любой

обмен

информацией

по

HTTP
,

включая

и

использование

различных

прокси

и

сжатие

данных

(
GZip
)

и

т.д.

Кроме

класса

THTTPSend

модуль

HTTPSend
.
pas

содержит

также

и

ряд

вспомогательных

методов,

которые

облегчают

работы

с

наиболее

частыми

опер
ациями

при

работе

с

HTTP
.

Рассмотрим

их
.

Вспомогательные

методы


function

(
const

URL
:

string
;

const

Response
:

TStrings
):

Boolean
;


Эта

функция

получает

текст

страницы,

расположенной

по

адресу

URL

и

заносит

его

в

список

Response
.

Используется

ме
тод

.

В

случае,

если

запрос

выполнен

успешно

(т.е.

клиент

смог

получить

ответ

от

сервера)

результат

выполнения

метода

будет

равен

True
.

Следует

отметить,

что

метод


возвращает

только

тело

документа
,

т.е.

в

Response

будут

отсутствовать

заголов
ки

ответа,

куки

и

т.д.


function

(
const

URL
:

string
;

const

Response
:

TStream
):

Boolean
;


Метод,

аналогичный

методу

.

Получает

бинарные

данные,

расположенные

по

заданному

URL

и

записывает

эти

данные

в

поток

Response
.

В

случае

успеха

метод

возвращает

True.


function

HttpPostBinary
(
const

URL
:

string
;

const

Data
:

TStream
):

Boolean
;


Метод

отправляет

документ,

содержащийся

в

потоке

Data

методом

POST

на

заданный

в

URL

адрес.

В

случае

успешного

выполнения

запроса,

ответный

документ

также

будет

записан

в

поток

Data
.


function

HttpPostURL
(
const

URL
,

URLData
:

string
;

const

Data
:

TStream
):

Boolean
;


Метод

удобно

использовать

для

отправки

данных

web
-
форм

на

заданный

URL
.

Для

отправки

данных

используется

метод

POST
.

При

этом

поля

формы

предвари
тельно

должны

быть

записаны

в

URLData

и

закодированы

методом

EncodeURLElement
.

Данные

в

URLData

должны

представляться

в

той

же

последовательности,

что

и

в

web
-
форме

и

могут

выглядеть

следующим

образом:

fileld1=hello&fileld2=world

В

случае

успешного

выполне
ния

запроса

метод

вернет

True
,

а

ответный

документ

будет

записан

в

поток

Data
.


function

HttpPostFile
(
const

URL
,

FieldName
,

FileName
:

string
;

const

Data
:

TStream
;

const

ResultData
:

TStrings
):

Boolean
;


Метод может быть использован для отправки любого файл
а методом
POST

на заданный
URL
. При этом функция имитирует отправку файла в формате
multipart/form
-
data
.

Отправляемый файл должен содержаться в потоке
Data
, а
имя файла


в параметре
FileName
. В свою очередь, параметр
FileldName

должен содержать имя поля ф
ормы в который должно быть записано имя
файла. Если запрос выполнен успешно, то в
ResultData

будет записан
ответный документ, а метод вернет
True
.

Пример
ы

работы

с

HTTP

в

Synapse

В

качестве

примеров

работы

с

HTTP

в

Synapse

рассмотрим

несколько

небольших

пр
иложений,

демонстрирующих

различные

возможности

по

использованию

Synapse
,

которые

впоследствии

могут

Вам

пригодиться

при

разработке

своих

проектов.


Первый

пример:

скачиваем

большие

файлы

с

заполнением

ProgressBar

а

Достаточно

актуальный

и

важный

вопрос

пр
и

разработке

приложений

для

работы

с

HTTP



как

показать

прогресс

загрузки

большого

объема

данных

из

Сети?

Посмотрим

как

решается

эта

задача

с

использованием

Synapse
.

Начнем

с

теории.


Итак,

при

отправке

данных

клиенту

сервер

может

включить

в

список

заголо
вков

заголовок

Content
-
Length
, который будет содержать размер
сущности (тела ответа) в байтах.
Получив такой заголовок клиент может,
например, организовать загрузку файла частями или, как
в

нашем

случае,

инициировать

процесс

загрузки

с

заполнением

Progress
Bar

а.

Однако

следует

помнить

о

том,

что

сервер

не

обязан

отправлять

этот

заголовок

клиенту

и

клиент

должен

быть

готов

к

такой

ситуации,

когда

размер

загружаемых

данных

заранее

не

известен.


Таким

образом,

алгоритм

работы

нашего

приложение

может

быть

следу
ющим:

1.

Отправляем

запрос

методом

HEAD

на

сервер

и

получаем

список

заголовков

2.

Ищем

в

заголовках

Content
-
Length

a.

Если
Content
-
Length

найден, то получаем значение заголовка и
присваиваем это значение свойству
Max

у
ProgressBar

а

b.

Если заголовок
Content
-
Length

не

найден, то скачиваем файл без
заполнения
ProgressBar

а

Теперь

попробуем

реализовать

этот

алгоритм

в

приложении.

Создаем

новый

проект

Delphi

(
назовем

его

syna
_
progress
)

и

на

главной

форме

приложения

размещаем

компоненты

как

показано

на

рисунке

ниже:


Рису
нок
1

Интерфейс программы
syna
_
progress

На

форме

приложения

расположены

следующие

компоненты:



edURL
:

TEdit



поле

для

ввода

URL

с

которого

будет

загружен

файл



:

TButton



кнопка,

клик

по

которой

будет

запускать

процесс

загр
узки

файла



pbDownload
:

TProgressBar



индикатор

процесса

загрузки



lbProgress:

TLabel



метка

для

вывода

информации

о

размере

полученных

от

сервера

данных.

Теперь

приступим

к

написанию

кода

программы.

Начнем

с

функции,

которая

будет

возвращать

нам

размер

да
нных

для

загрузки.

Назовем

её

.

Подключаем

в

uses

модуль

httpsend

и

пишем

следующий

код
:

function

TfMain
.
(
const

AURL
:

string
):

int64
;

var

HTTPClient
:

THTTPSend
;


I
:

Integer
;


s
:

string
;

begin


Result
:=
-
1
;


HTTPClient
:=
THTTPSend
.
Creat
e
;


try


if

HTTPClient
.
(
'HEAD'
,
AURL
)

then


begin


for

I
:=

0

to

HTTPClient
.
Headers
.
Count
-
1

do


begin


if

pos(
'content
-
length'
,lowercase(
HTTPClient
.
Headers
[
i
]))

0

then


begin


s
:=

c
opy(
HTTPClient
.
Headers
[
i
],

16
,



Length(
HTTPClient
.
Headers
[
i
]

)
-
15
);


Result
:=StrToInt(
s
)+Length(
HTTPClient
.
Headers
.
Text
);


break
;


end
;


end
;


end


else


raise

Ex
ception
.
Create
(
'
Ошибка
:
не

удалось

получить

заголовки
'
);


finally


HTTPClient
.
Free


end
;

end
;


Здесь

мы

создаем

объект

типа

THTTPSend

и

пробуем

выполнить

метод

HEAD
.

Если

сервер

возвращает

ответ,

то

пробуем

найти

в

ответном

сообщении

заголовок

Content
-
Length

и

получить

его

значение.

При

этом,

обратите

внимание

на

строку

в

которой

мы

получаем

результат

функции:

Result
:=StrToInt(
s
)+Length(
HTTPClient
.
Headers
.
Text
);

Здесь

мы

к

значению

заголовка

Content
-
Length

также

прибавляем

и

длину

текста

в

списке

Headers
.

Причина

такого

действия

заключается

в

назначении

самого

заголовка

Content
-
Length



он

возвращает

только

размер

тела

документа

и

не

учитывает

длину

заголовков,

которые

также

будут

получены

при

загрузке

документа.

Тепе
рь

разберемся

с

тем,

как

будет

происходить

работа

с

ProgressBar
.

У

THTTPSend
,

как

мы

уже

знаем,

есть

свойство

DownloadSize
,

которое

содержит

размер

загруженных

с

сервера

данных,

однако

это

свойство

будет

иметь

значе
ние

0

до

тех

пор,

пока

все

данные

не

будут

получены.

То

есть,

в

нашем

случае,

это

свойство

оказывается

практически

бесполезным.

Вместе

с

этим,

у

THTTPSend

имеется

другое

свойство

-

Sock
,

используя

которое

мы

можем

легко

пол
учить

доступ

к

сокету,

который

используется

для

работы

с

Сетью.

И

именно

это

свойство

поможет

нам

в

решении

задачи.


В

нашем

случае,

для

реализации

заполнения

ProgressBar

а,

нам

потребуется

определить

обработчик

события

OnS
tatus

сокета

и

в

этом

обработчике

отслеживать

все

операции

чтения

данных

из

сокета.

Реализуем

всё

сказанное

в

нашей

программе.


Добавим

в

нашу

программу

две

переменные:

type


TfMain
=

class
(
TForm
)


[...]


private


downloaded
:

int64
;


size
:

int6
4
;


public


{
Public

declarations

}


end
;

Переменная

size

будет

содержать

размер

документа,

который

мы

будем

скачивать

с

сервера,

а

downloaded



размер

данных,

полученных

на

текущий

момент

выполнения

программы.

Теперь

напишем

обработчик

события

OnStatus

для

сокета

у

THTTPSend
.

Для

работы

с

сокетом

нам

потребуется

модуль

blcksock



подключаем

его

в

uses

модуля

приложения

и

пишем

такой

обработчик
:



procedure

TfMain
.
OnSockStatus
(
Sender
:

TObject
;

Reason
:

;


const

Value
:

String
);

const


cProgress
=

'%d of %d bytes'
;

begin


if

Reason
=
HR_ReadCount
then


begin


downloaded
:=
downloaded
+StrToInt(
Value
);


if

�size
0

then


begin


pbDownload
.
Position
:=
downloaded
;


lbProgress
.
C
aption
:=Format(
cProgress
,[
downloaded
,
pbDownload
.
Max
]);


end


else


lbProgress
.
Caption
:=IntToStr(
downloaded
)+
' bytes'
;


Application
.
ProcessMessages
;


end
;

end
;


Здесь

мы

отслеживаем

каждый

момент

чтения

данных

из

сокета

(
HR
_
ReadCo
unt
)

и

выполняем

следующие

операции:



если

переменная

size

содержит

не

нулевое

значение

(т.е.


вернула

нам

размер

документа),

то

заполняется

ProgressBar

и

прогресс

загрузки

выводится

в

метку

lbProgress



если

переменная

size

равна

нулю,

то

выводим

толь
ко

информацию

о

том,

сколько

байт

данных

было

загружено.

Теперь

нам

остается

только

написать

код

обработчика

события

OnClick

кнопки

«
Download
»

и

проверить

работу

программы.

Пишем

такой

обработчик

OnClick
:

procedure

TfMain
.
(
Sender
:

TObject
);

var

HTTPClient
:

THTTPSend
;

begin


downloaded
:=
0
;


size
:=
(
edURL
.
Text
);
//
получаем

размер

файла

для

загрузки


{
определяем

стиль

у

ProgressBar}


if

�size
0

then


begin


pbDownload
.
Style
:=
pbstNormal
;


pbDownload
.
Max
:=
size
;
//
указываем

максимал
ьное

значение


end


else


pbDownload
.
Style
:=
pbstMarquee
;




HTTPClient
:=
THTTPSend
.
Create
;


try


//
определяем

обработчик

события

OnStatus


HTTPClient
.
Sock
.
OnStatus
:=
OnSockStatus
;


{
Пробуем

скачать

файл
}


if

HTTPClient
.
(
,
e
dURL
.
Text
)

then


HTTPClient
.
Document
.
(
'file.pdf'
)


else


raise

Exception
.
Create
(
'Не удалось загрузить данные с сервера'
);


finally


HTTPClient
.
Free
;


end
;

end
;


Вот

и

все
.

Можете

запустить

программу

и

попробовать

скачать

какой
-
нибудь

файл

из

Сети.

Если

Ваш

сервер

вернет

заголовок

Content
-
Length
,

то

Вы

увидите
,

что

ProgressBar

плавно

заполняется

при

загрузке.

Если

же

сервер

не

вернет

необходимый

нам

заголовок,

то

весь

процесс

загрузки

файла

будет

отображаться

только

в

метке

lbPro
gress
.


Исходный код проекта можно найти в директории с примерами
Examples
/
ProgressBar
/



Второй

пример:

пишем

свой

аналог

свойства

HandleRedirects

для

THTTPSend

Задача

Представленный

выше

пример

работает,

однако

у

программы

есть

один

недостаток



вы

не

сможете

скачать

файл

доступ

к

которому

осуществляется

через

перенаправление.

Например,

в

блоге

webdelphi
.
ru

есть

ссылка

на

загрузку

справочника

по

компонентам

Ribbon

Controls
,

которая

выглядит

следующим

образом:

http://www.webdelphi.ru/wp
-
content/plugins/download
-
monitor/download.php?id=59


С

использованием

этого

URL

процесс

загрузки

с

сайта

выглядит

следующим

образом:

1.

Клиент

о
тправляет

запрос

методом


на

заданный

URL

2.

Сервер

возвращает

код

статуса

302

и

в

заголовке

Location

указывает

адрес

по

которому

находится

файл

3.

Клиент

получает

адрес

из

заголовка

Location

и

начинает

процесс

загрузки.

Для

владельца

сайта

такой

процесс

скач
ивания

файлов

может

быть

полезен,

в

случаях,

когда

перед

отдачей

клиенту

запрашиваемого

документа,

необходимо

выполнить

какие
-
либо

действия

(например,

нарастить

значение

счётчика

количества

скачиваний

файла).

Для

нас

же

такой

ход

загрузки

файла

создает

неб
ольшую

проблему

так

как

прежде,

чем

мы

начнем

скачивать

документ

и

заполнять

ProgressBar
,

нам

надо

быть

уверенными,

что

это

необходимый

нам

документ,

а

не

информация

о

расположении

файла
.


У

класса

THTTPSend

нет

такого

замечательного

свойства

как

Handle
Red
irects

в

Indy
,

которое

позволяет

забыть

об

отслеживании

перенаправлений.

Но

ведь

никто

не

запрещает

нам

самим

немного

расширить

возможности

класса

и

написать

свой

собственный

механизм

отслеживания

всех

перенаправлений

и

получения

целевого

URL
.


Итак,

в

это
м

примере

нам

необходимо

решить

следующую

задачу:

разработать

механизм

обработки

ответов

сервера

с

кодом

статуса

3
xx

и

дописать

пример

работы

с

ProgressBar

таким

образом,

чтобы

файлы

гарантированно

скачивались

с

сервера

(конечно,

при

условии,

что

файлы

дей
ствительно

присутствуют

на

сервере

и

доступ

к

ним

не

закрыт).

Решение


Для

решения

задачи

воспользуемся

одной

из

возможностей

Delphi

2010

и

выше



напишем

свой

помощник

класса

(
class

helper
)

THTTPSend
.

Для

этого

скопируем

предыдущий

пример

работы

с

Synapse

в

новую

директорию

и

переименуем

проект

из

syna
_
progress

в

syna_HandleRedirects
.

Теперь

определимся

с

тем,

чего

нам

не

хватает

в

классе

для

того,

чтобы

осуществить

автоматическое

перенаправление

на

URL
,

заданный

в

заголовке

Location
?


Во
-
первых,

нам

потре
буется

метод,

возвращающий

значение

заголовка

по

его

имени.

С

помощью

этого

методы

мы

будем

узнавать

значение

в

заголовке

Location
.

Во
-
вторых,

необходим

метод,

который

будет

анализировать

ответ

сервера

и

перенаправлять

нас

на

новый

URL

до

тех

пор,

пока

мы

не

получим

целевой

URL

или

количество

попыток

автоматического

перенаправления

не

достигнет

заранее

определенного

значения.

Теперь

реализуем

всё

сказанное

в

коде

нашей

программы.

Для

этого

добавим

в

проект

новый

модуль

(назовем

его

HTTPSendHelper
)

и

создади
м

в

этом

модуле

такой

помощник

класса

THTTPSend
:

type


THTTPSend_
=

class

helper
for

THTTPSend


function

HeaderByName
(
const

AHeaderName
:

string
):

string
;


function

(
const

AStartURL
:

string
;

ARedirectMaximum
:

integer

=

10
):

string
;


end
;


Метод

HeaderByName

возвращает

значение

заголовка

по

его

имени

и

выглядит

следующим

образом:

function

THTTPSend_
.
HeaderByName
(
const

AHeaderName
:

string
):

string
;

var


I
:

integer
;

begin


Result
:=

EmptyStr
;


for

I
:=

0

to

Headers
.
Count

-

1

do


begin



if

StartsText
(
AHeaderName
,

Headers
[
I
])

then


begin


Result
:=

copy(
Headers
[
I
],

Length(
AHeaderName
)

+

3
,


Length(
Headers
[
I
])

-

1
);


break
;


end
;


end
;

end
;


Метод


возвращает

URL

по

которому

находится

запрашиваемый

доку
мент.

При

этом

проверка

перенаправлений

начинается

с

адреса,

заданного

в

параметре

AStartURL

и

заканчивается

в

том

случае,

если

сервер

вернет

код

статуса

отличный

от

301/302

или,

если

количество

перенаправлений

превысит

значение

ARedirectMaximum
.

Код

метод
а

следующий
:

function

THTTPSend_
.
(
const

AStartURL
:

string
;


ARedirectMaximum
:

integer

=

10
):

string
;

var


ARedirectCount
:

integer
;


ALocationURL
:

string
;

begin


Result
:=

AStartURL
;


repeat


if

(
'HEAD'
,

Result
)

then


begin


ALocationURL
:=

HeaderByName
(
'Location'
);


if

Length(
ALocationURL
)


0

then


begin


Result
:=

ALocationURL
;


inc(
ARedirectCount
);


end
;


end


else


exit
;


until

((
�ResultCode
301
)

and

(
�ResultCode
302
))

or


(
ARedirectCount

�=
ARedirectMaximum
);

end
;


Теперь,

имея

в

распоряжении

оба

этих

метода

мы

можем

написать

новый

метод

получения

данных

от

сервера,

например,

такой:

function

THTTPSend_
.
(
const

AMehtod
,

AURL
:

string
;


const

AHandleRedirects
:

boolean
;

ARedirectMaximum
:

integer
):

boolean
;

var

:

string
;

begin


if

not

AHandleRedirects
then


Result
:=

(
AMehtod
,

AURL
)


else


begin


:=
(
AURL
,
ARedirectMaximum
);


Result
:=

(
AMehtod
,

aTarg
);


end
;

end
;


Здесь

кроме

названия

метода

и

URL

мы

также

можем

указать

будет

ли

использоваться

автоматическое

перенаправление

в

случае

получения

кода

статуса

из

группы

3хх

(
AHandleRedirects
)
,

а

также

определить

количество

попыток

автоматического

пер
енаправления

(
ARedirectMaximum
).

Теперь

воспользуемся

помощников

класса

и

допишем

приложение

из

предыдущего

примера,

чтобы

скачивать

файлы

даже

при

наличии

перенаправлений.

Подключим

в

uses

главного

модуля

программы

модуль

HTTPSendHelper

и

перепишем

обрабо
тчик

OnClick

кнопки

«
Download
»

следующим

образом:

procedure

TfMain
.
(
Sender
:

TObject
);

var


HTTPClient
:

THTTPSend
;


:

string
;

begin


downloaded
:=

0
;


HTTPClient
:=

THTTPSend
.
Create
;


try


//
получаем

целевой

URL


:=

HTTP
Client
.
(
edURL
.
Text
,

15
);


//запрашиваем размер документа


size
:=

(
);


//
задаем

стиль

ProgressBar


if

�size
0

then


begin


pbDownload
.
Style

:=

pbstNormal
;


pbDownload
.
Max

:=

size
;


end


else


p
bDownload
.
Style

:=

pbstMarquee
;


//
определяем

обработчик

события

OnStatus
сокета


HTTPClient
.
Sock
.
OnStatus

:=

OnSockStatus
;


//
пробуем

загрузить

документ


if

HTTPClient
.
(
,

)

then


HTTPClient
.
Document
.
(
'file.
pdf'
)


else


raise

Exception
.
Create
(
'Не удалось загрузить данные с сервера'
);


finally


HTTPClient
.
Free
;


end
;

end
;


Теперь

можете

запустить

приложение

и

задать,

например,

такой

URL

для

загрузки

файла:

http://www.webdelphi.ru/wp
-
content/plugins/download
-
monitor/download.php?id=59

и

убедитесь,

что

несмотря

на

то,

что

результатом

выполнения

запроса


на

этот

URL

будет

код

статуса

302

без

каких
-
либо

ош
ибок

с

заполнением

ProgressBar

а.

Как

было

сказано

выше,

предложенное

решение

работает

в

Delphi

версии

2010

и

выше.

Поэтому,

если

Вы

используете

более

раннюю

версию

Delphi
,

то

вашим

решением

задачи

может

стать

написание

наследника

THTTPSend

в

котором

будут

реализованы

те

же

самые

методы,

что

и

в

рассмотренном

помощнике.

Мы

класс
-
наследник

писать

не

будем,

а

перейдем

к

следующему

примеру

работы

с

HTTP

в

Synapse
.


Исходный код проекта можно найти в директории с примерами
Examples
/
HandleRedirects
/


Третий

пр
имер:

фильтр
уем

считываемые

данные
.

Часто
,

при

скачивании

какой
-
либо

странички

с

сайта,

нам

требуется

получить

не

весь

код

страницы,

а,

например,

только

часть

страницы,

в

которой

расположены

мета
-
теги.

В

этом

случае,

если

количество

запрашиваемых

страниц

с
айта

велико,

то

при

отсутствии

фильтрации

полученных

данных,

возможны

две

взаимосвязанные

проблемы:

1.

программа

будет

потреблять

намного

больше

http
-
трафика,

чем

требуется

для

решения

задачи

2.

запрос

к

серверу

будет

выполняться

дольше,

т.к.

будет

скачиваться

в
ся

страница,

а

не

только

необходимая

нам

часть.

Между

тем

Synapse

позволяет

нам

фильтровать

данные,

считываемые

из

сокета.

Для

этого

используется

событие

OnReadFilter
.

Рассмотрим

работу

с

этим

событием

и

напишем

програ
мму,

которая

будет

получать

с

сервера

только

ту

часть

web
-
страницы,

в

которой

содержатся

необходимые

нам

данные.

Создадим

новый

проект

Delphi

(
назовем

его

«
DataFilter
»
)

и

на

главной

форме

приложения

разместим

компоненты,

как

показано

на

рисунке

ниже:


Рис
унок
2

Интерфейс программы
DataFilter

На

форме

расположены

следующие

компоненты:



edURL:

TEdit



поле

ввода

URL



edStart
:

TEdit



поле

для

ввода

строки

с

которой

должна

начинаться

целевая

часть

страницы



edEnd
:

TEdit



поле

для

ввод
а

строки,

которой

должна

заканчиваться

целевая

часть

страницы



:

TButton



кнопка,

клик

по

которой

будет

запускаться

процесс

загрузки

данных

с

сервера



memResult
:

TMemo



текстовый

редактор

для

вывода

результата

работы

программы.

Для

решения

задачи

нам

потребуется

совсем

не

много

действий,

а

именно

написать

обработчик

события

OnReadFilter

сокета

у

THTTPSend

и

использовать

его

в

программе.

Так

как

мы

в

очередной

раз

будем

использовать

сокет,

то

подключаем

в

uses

моду
ли

httpsend

и

blcksock
.


Теперь

зададим

одну

переменную

в

разделе

private

главной

формы

приложения

и

определим

обработчик

On
Read
Filter
:

type


TForm1
=

class
(
TForm
)


[..]


private


AllDocument
:

string
;


proced
ure

Filter
(
Sender
:

TObject
;

var

Value
:

AnsiString
);


public


{ Public declarations }


end
;

Здесь

следует

пояснить,

зачем

нам

потребовалась

переменная

AllDocument
.

Причина

в

том,

что

в

параметре

Value

обработчика

события

OnReadFilter

возвращается

только

часть

данных,

которая

была

получена

при

последнем

чтении

данных

из

сокета

и

не
т

гарантии

того
,

что

эта

часть

данных

будет

содержать

полностью

искомую

строку.


Сам

обработчик

события

будет

выглядеть

следующим

образом:

procedure

TForm1
.
Filter
(
Sender
:

TObject
;

var

Value
:

AnsiString
);

var

IdxStart
,
IdxEnd
:

integer
;

begin


//
сохраняем

полученые

данные


AllDocument
:=
AllDocument
+
Value
;


//ищем строку в полученных данных


IdxStart
:=pos(
edStart
.
Text
,
AllDocument
);


IdxEnd
:=
pos(
edEnd
.
Text
,
AllDocument
);


if

(
�IdxStart
0
)

and

(
�IdxEnd
0
)

then


begin


//строка найдена
-

выводим её в TMemo


memResult
.
Lines
.
Add
(copy(
AllDocument
,
IdxStart
,


IdxEnd
+Length(
edEnd
.
Text
)
-
IdxStart
));


/
/
прекр
ащаем

работу

сокета


(
Sender
).
;


end
;

end
;


Теперь

напишем

обработчик

события

OnClick

кнопки:

procedure

TForm1
.
(
Sender
:

TObject
);

var

HTTPClient
:

THTTPSend
;

begin


//удаляем полученные ранее данные


AllDocument
:=
EmptyStr
;


memResult
.
Lines
.
Clear
;




HTTPClient
:=
THTTPSend
.
Create
;


try


//
определяем

обработчик

события


HTTPClient
.
Sock
.
OnReadFilter
:=
Filter
;


//
документ

был

скачан

полностью


if

HTTPClient
.
(
,
edURL
.
Text
)
then


memResult
.
Lines
.
Add
(
AllDocument
)


finally


HTTPClient
.
Free
;


end
;

end
;


В

представленном

выше

методе

следует

обратить

внимание

на

то,

что

вывод

данных

в

TMemo

осуществляется

только

в

том

случае,

если


возвращает

True
,

то

есть

работа

сокета

не

была

пре
рвана

в

обработчике

Filter

и

документ

был

скачан

полностью.


Теперь

можете

проверить

работу

программы.

Запустите

приложение

и

задайте,

например,

исходные

данные

как

показано

на

рисунке

ниже:


Рисунок
3

Исходные данные для програ
ммы

Результатом

работы

будет

следующая

строка:


Рисунок
4

Результат работы программы

И

пусть

вас

не

смущают

непонятные

символы

в

полученной

строке



Synapse

вернул

ровно

то,

что

отдал

нам

сервер
,

т.е.

строку

в

кодировке

UTF
-
8
.

П
римеры

работы

с

различными

кодировками

текста

в

Synapse

мы

рассмотрим

ниже.

Если

же

Вы

обратите

внимание

на

значение

переменной

AllDocument
,

то

увидите,

что

переменная

содержит

«сырые»

данные,

т.е.

абсолютно

всё,

что

было

передано

сервером,

включая

заголов
ки.


Подведем

итоги

по

приведенному

выше

примеру.

Событие

OnReadFilter

удобно

использовать

двух

случаях:

1.

Когда

необходимо

провести

фильтрацию

присылаемых

сервером

данных

до

того

как

эти

данные

будут

распределены

по

сво
йствам

THTTPSend
,

а

сами

данные

являются

простым

текстом,

например,

html
-
кодом

страницы
.

2.

Когда

необходимо

принудительно

разорвать

соединение

с

сервером

после

того

как

от

сервера

пришел

необходимый

объем

данных.

Если

же

Вам

необходимо

скачать

часть

какого
-
т
о

бинарного

файла

с

сервера,

то

здесь

OnReadFilter

уже

не

подойдет

и

необходимо

использовать

свойства

RangeStart

и

RangeEnd

у

THTTPSend
.

И

име
нно

этот

пример

мы

и

рассмотрим

далее.


Исходный код проекта можно найти в директории с примерами
Examples
/
DataFilter
/


Четвертый

пример:

частичное

скачивание

файла

с

сервера
.

Частичный

.


Теория

Что

такое

«частичный

»
?

Варианты

работы

с

байтовыми

диапазонами.


Клиенты

HTTP

довольно

часто

сталкиваются

с

ситуацией,

когда

загрузка

большого

объема

данных

может

быть

прервана

вследствие

различных

проблем,

например,

из
-
за

разрыва

соединения.

В

этом

случае,

когда

клиент

уже

содержит

часть

документа,

при

с
ледующем

запросе

желательно

запрашивать

не

весь

документ

целиком,

а

лишь

оставшуюся

часть.

Для

решения

этой

задачи

при

использовании

HTTP
/1.1

в

заголовках

запроса

возможно

использование

байтовых

диапазонов.

Подробную

информацию

о

байтовых

диапазонах

в

HTTP

вы

можете

найти

RFC

2616
.

Мы

же

в

этом

примере

рассмотрим

только

основные

моменты

использования

диапазонов

в

Synapse
.


Если

мы

запрашиваем

с

сервера

только

часть

документа,

то

такой

запрос

носит

название

частичный

.


Диапазон

запрашиваемых

данных

задается

в

заголовке

Range

и

может

представляться

в

нескольких

вариантах.


Вариант

1.

Запрос

п
роизвольн
ого

диапазон
а

В

этом

случае

заголовок

Range

зада
ется

следующим

образом:

Range:

bytes=3000
-
5900


При

этом

запрашивается

часть

документа,

начиная

с

3000

байта

по

5900

включительно.

Вариант

2.

Запрос

произвольного

байта

Это

частный

случай

первого

варианты.

При

запросе

одного

байта

значения

начала

и

конца

диапазона

совпадают.

Например,

так

мы

можем

запросить

305
1

байт

документа
:

Range:

bytes=30
51
-
3051


Вариант

3.

Запрос

диапазона

от

конца

документа

В

этом

случае

заголовок

задается

в

такой

форме:


Range:

bytes=
-
1000


При

таком

запросе

сервер

вернет

последние

1000

байт

документа

(на

рисунке



это

байты

от

5000

до

5999

включительно).

Вариант

4.

Запрос

диапазона

от

начала

документа

В

этом

случае

заголовок

задается

в

следующей

форме:

Range:

bytes=
5000
-


При

таком

запросе

сервер

вернет

первые

5000

байт

документа

(байты

с

0

по

5000)


Вариант

5.

Запрос

нескольких

фраг
ментов

документа

В

этом

случае

заголовок

задается

в

следующей

форме:

Range
:

bytes
=
100
-
1000,1000
-


В

этом

случае

здесь

мы

запросили

два

фрагмента

файла.


Как

проверить

поддерживает

ли

сервер

работы

с

байтовыми

диапазонами?

Чтобы

узнать

поддерживает

ли

серв
ер

работу

с

байтовыми

диапазонами

можно

использовать

два

способа:

1.

Отправить

на

сервер

запрос

методом

HEAD

и

проверить

наличие

заголовка


Accept
-
Ranges
:

bytes
”,

который

указывает

на

то,

что

сервер

поддерживает

частичные

.

2.

Отправить

на

сервер

частичные

GE
T
,

запросив

произвольный

фрагмент

документа

и

проверить

код

статуса.

Если

код

статуса

равен

206,

то

сервер

вернул

нам

запрошенную

часть

документа

и,

следовательно,

поддерживает

работу

с

байтовыми

диапазонами.

Практика


Создаем

новый

проект

Delphi

(назовем

его

Ranges
)

и

на

главной

форме

приложения

располагаем

компоненты,

как

показано

на

рисунке

ниже:


Рисунок
5
. Интерфейс программы
Ranges

На

форме

расположены

следующие

компоненты:



edURL
:

TEdit



поле

ввода

URL

расположения

докумен
та



btnCheck
:

TButton



кнопка

«Проверить

поддержку

заголовков

Ranges

сервером»



2

TRadioButton

для

выбора

способа

запроса

диапазона

документа:

с

использованием

свойств

RangeStart

и

R
angeEnd

класса

THTTPSend

или

же

с

использование

собственного

заголовка

Range
.



edRangeStart
,

edRangeEnd
:

TEdit



поля

ввода

начала

и

конца

запрашиваемого

диапазона



edRangeHeader
:

TEdit



поле

ввода

значения

заголовка

Range




TButton



кнопка

для

выпо
лнения

запроса

к

серверу



memLog
:

TMemo



текстовый

редактор

для

вывода

лога

работы

программы.

Для

начала,

воспользуемся

кодом

примера

№2

и

перепишем

метод


следующим

образом:

function

TForm2
.
(
co
nst

AStartURL
:

string
;


ARedirectMaximum
:

integer

=

10
):

string
;

var


ARedirectCount
:

integer
;


ALocationURL
:

string
;


HTTP
:
THTTPSend
;



function

HeaderByName
(
const

AHeaderName
:

string
):

string
;

var


I
:

integer
;

begin


Result
:=

EmptyStr
;


for

I
:=

0

to

HTTP
.
Headers
.
Count

-

1

do


begin


if

StartsText
(
AHeaderName
,

HTTP
.
Headers
[
I
])

then


begin


Result
:=

copy(
HTTP
.
Headers
[
I
],

Length(
AHeaderName
)

+

3
,


Length(
HTTP
.
Headers
[
I
])

-

1
);


break
;


end
;


end
;

end
;



begin


Result
:=

AStartURL
;


HTTP
:=
THTTPSend
.
Create
;

try


repeat


if

HTTP
.
(
'HEAD'
,

Result
)

then


begin


ALocationURL
:=

HeaderByName
(
'Location'
);


if

Length(
ALocationURL
)


0

then


begin


Result
:=

ALocationURL
;


inc(
ARedirec
tCount
);


end
;


end


else


exit
;


until

((
HTTP
.
ResultCode


301
)

and

(
HTTP
.
ResultCode


302
))

or


(
�ARedirectCount
=

ARedirectMaximum
);

finally


HTTP
.
Free
;

end
;

end
;



Теперь

напишем

функцию

для

проверки

поддержки

сервером

работы

с

б
айтовыми

диапазонами:

function

TForm2
.
AcceptRanges
:

boolean
;

var

:

string
;

begin


Result
:=
False
;


HTTPClient
.
Clear
;


:=
(
edURL
.
Text
);


HTTPClient
.
Clear
;


HTTPClient
.
RangeStart
:=
1
;


HTTPClient
.
RangeEnd
:=
1
;


if

HTTPClient
.
HTTPMe
thod
(
,
)

then


Result
:=
HTTPClient
.
ResultCode
=
206
;

end
;


Здесь

мы

проверяем

поддержку

сервером

работы

с

диапазонами

вторым

способом,

т.е.

пробуем

получить

от

сервера

произвольный

диапазон

данных



нашем

примере

запрашивается

1

байт).

В

случае,

если

сервер

ответит

нам

с

кодом

статуса

206,

то

мы

будем

знать,

что

сервер

гарантированно

поддерживает

работу

с

диапазонами.


Теперь,

используя

функцию

AcceptRanges

мы можем написать обработчик
события
OnClick

кнопки
btnCheck
:

procedure

TForm2
.
btnCheckCli
ck
(
Sender
:

TObject
);

begin


if

AcceptRanges
then


ShowMessage
(
'
Есть

поддержка

Ranges'
)


else


ShowMessage
(
'Ranges
не

поддерживается
'
);

end
;


И,

наконец,

пишем

обработчик

события

OnClick

у

кнопки

:

procedure

TForm2
.
(
Sender
:

TObject
)
;

var

:

string
;

begin


if

AcceptRanges
then


begin


HTTPClient
.
Clear
;


MemLog
.
Lines
.
Add
(
'
Есть

поддержка

Ranges.
Пробуем

выполнить

запрос
'
);


if

rbProperties
.
Checked

then


begin


HTTPClient
.
RangeStart
:=StrToInt(
ed
RangeStart
.
Text
);


HTTPClient
.
RangeEnd
:=StrToInt(
edRangeEnd
.
Text
);


end


else


HTTPClient
.
Headers
.
Add
(
'Range: bytes='
+
edRangeHeader
.
Text
);


if

HTTPClient
.
(
,
(
edURL
.
Text
))

then


begin



MemLog
.
Lines
.
Add
(
HTTPClient
.
Headers
.
Text
);


MemLog
.
Lines
.
Add
(Format(
'
С

сервера

загружено

%d
байт

данных
'
,[
HTTPClient
.
Document
.
Size
]));


end


else


raise

Exception
.
Create
(
'
Ошибка

выполнения

запроса
'
)


end

end
;


Здесь

мы

про
веряем

поддержку

работы

с

байтовыми

диапазонами

у

сервера

и

выполняем

частичный


в

зависимости

от

того,

какой

вариант

работы

был

выбран



использованием

свойств

RangeStart
/
Range
End

или

с

использованием

собственных

заголовков).

Теперь

проверим

работу

нашей

программы

и

посмотрим,

что

нам

будет

возвращать

сервер.

Для

начала,

запросим

с

сервера

произвольные

10

байт

документа,

используя

свойства

RangeStart
/
RangeEnd
:


Рисунок
6
.
Запрос произвольных 10 байт документа


Теперь

запросим

последние

данные

с

100

байта

и

до

конца

документа:


Рисунок
7
. Запрос док
умента с 100 байта и до конца

Запрос произвольного 1 байта данных:


Рисунок
8
.Запрос 1 байта данных

На

этом

возможности

использования

свойств

RangeStart

и

RangeEnd

заканчиваются.

Остальные

варианты

работы

с

байтовыми

диапазонами

реализуются

через

собственные

значения

заголовка

Range
.


Запросим

с

сервера

последние

100

байт

документа:


Рисунок
9
. Запрос последних 100 байт

И

после
дний

вариант



запрос

нескольких

произвольных

фрагментов

документа:


Рисунок
10
. Запрос нескольких фрагментов документа

Если

вы

сравните

все

предыдущие

ответы

сервера

с

последним

полученным,

то

увидите

следующее

«странное»

поведе
ние

THTTPSend
:

1.

Размер

полученных

с

сервера

данных

явно

не

соответствует

запрошенному.

Так,

последний

ответ

должен

был

содержать

два

фрагмента



на

100

и

2

байта

соответственно,

а

сервер

вернул

303

байта

данных.

2.

В

заголовках

последнего

ответа

отсутствует

за
головок

Content
-
Range
.

Последний

результат

работы

программы

также

корректен,

как

и

все

предыдущие,

просто

в

этом

случае

сервер

вернул

нам

множественное

содержимое
,

на

что

указывает

заголовок

ответа


Content
-
Type:

multipart/
byteranges
”.

Работу

с

множественн
ым

содержимым

мы

рассмотрим

ниже
.



Исходный код проекта можно найти в директории с примерами
Examples
/
DataFilter
/



Пятый пример:
Использование
GZip

в
Synapse
.

Теория

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

клиентом

способов кодирования
тела запроса, в
HTTP

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


Accept
-
Encoding
.

RFC

2616


определяет следующие возможные значения для этого заголовка
:



c
ompress



к
одирование с помощью Unix
-
утилиты «
compress
»
.

Д
ля
кодирования используется а
лгор
и
тм

LZW




gzip



кодирование с помощью
Unix
-
утилиты «
Gzip
»
.
Для кодирования
используется алгоритм
LZ
77

(
согласно
RFC
)



deflate



для кодирования используется алгоритм
deflate



i
dentity



данные передаются без сжатия.

Примечание: могут использоваться и другие значения, указывающие
способ кодирования
содержимого. Так, например, браузер
Google

Chrome

может использовать в заголовке
Accept
-
Encoding

значение SDCH
.

В

большинстве

случа
ев,

согласование

содержимого

происходит

в

два

этапа:

1.

Клиент

включает

в

запрос

заголовок

Accept
-
Encoding
,

который

содержит

поддерживаемые

способы

кодирования
.

Например
:


/

HTTP/1.1

Host:

www.webdelphi.ru

Accept
-
Encoding:

gzip,

deflate


Здесь

клиент

со
общает

серверу,

что

может

принять

данные

сжатые

с

помощью

gzip

или

deflate
.

2.

Если

сервер

поддерживает

данные

алгоритмы

сжатия,

он

может

вернуть

ресурс

закодированный

одним

из

этих

методов,

а

может

и

отказаться

от

кодирования,

вернув

ресурс

в

несжатом

вид
е
.

При

этом,

если

сервер

возвращает

сжатые

данные,

то

в

заголовки

ответа

включается

заголовок

Content
-
Encoding
,

указывающий

способ

кодирования.

Например,

ответ

на

указанный

выше

запрос

может

содержать

следующий

заголовок:

Content
-
Encoding:

gzip


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

в

заголовке

Content
-
Encoding

значения

identity

не

предусмотрено,

т.е.

если

ответ

сервера

не

содержит

заголовок

Content
-
Encoding
,

то

это

означает,

что

данные

были

переданы

в

несжатом

виде.

При

согласовании

содержимого

следует

учитывать

следующие

возможн
ые

ситуации:

1.

Если

в

запрос

не

включен

заголовок

Accept
-
Encoding
,

то

сервер

может

предполагать,

что

клиент

воспримет

любую

кодировку

информации

и

отправить

клиенту

данные

сжатые,

например,

с

помощью

gzip

2.

Сервер

может

вернуть

данные

в

несжатом

виде

даже

в

то
м

случае,

если

в

запрос

включен

заголовок

Accept
-
Encoding
.


3.

Сервер

может

вернуть

данные

в

сжатом

виде
,

даже,

если

Accept
-
Encoding

содержит

значение

identity
.

Первые

две

ситуации

зависят

от

настроек

сервера.

Так,

например,

сервер

может

устанавливать

минимал
ьную

версию

HTTP
,

необходимую

для

сжатия

ответа

или

установить

минимальную

длину

ответа,

необходимую

для

сжатия

и

т.д.


Третья

ситуация

относится

скорее

к

недоработкам

в

программном

обеспечении,

конкретного

ресурса

в

сети,

чем

к

нормальной

работе

сервера.

С

подобной

ситуацией

автор

столкнулся

при

работе

с

сайтом
,

использующем

CMS

WordPress
.

Включение

заголовка


Accept
-
Encoding
:

identity

в

запрос

ни

к

чему

не

приводило



данные

продолжали

присылаться

в

сжатом

виде.

Однако

стоило

удалить

на

сайте

плагин,

отве
чающий

за

сжатие

данных

перед

их

отправкой

клиенту

и

настроить

сжатие

самостоятельно

через

файл

.
htaccess



проблема

исчезла.

В

последствии

оказалось,

что

только

удаленный

плагин

обладал

такой

«особенностью»

-


сжимать

данные

даже,

когда

этого

не

требовало
сь.


В

протоколе

HTTP/1.1,

так

же

как

и

в

других

заголовках

согласования

содержимого,

в

заголовке

Accept
-
Encoding

появилась

возможность

использовать

параметр

q

-

оценка

качества

(qvalue).

Оценка

качества



это

число

от

0

до

1,

определяющее

степень

предпочт
ения

клиентом

того

или

иного

метода

кодирования.

Более

предпочтительные

методы

помечаются

более

высокой

оценкой

качества.

Если

оценка

качества

отсутствует,

то

по

умолчанию

присваивается

наивысшая

оценка

1.

Например,

заголовок

Accept
-
Encoding
:

gzip
;
q
=1.0,
i
dentity
;
q
=0.5


Будет

означать,

что

наиболее

предпочтительным

способом

кодирования

для

клиента

является

gzip
,

если

сервер

не

поддерживает

gzip
,

то

данные

следует

вернуть

в

несжатом

виде.


Так

же

в

HTTP/1.1

появилась

возможность

обобщать

все

методы

кодирова
ния

с

помощью

символа

звездочки

"*"

и

согласование

содержимого

со

стороны

сервера
.


Концепция

согласование

содержимого

со

стороны

сервера

заключается

в

следующем.

Запрос

клиента

предшествует

ответу

сервера,

поэтому

целесообразно

стратегию

согласования

соде
ржимого

начинать

с

предпочтений

клиента

(отправка

в

запросе

заголовка

Accept
-
Encoding
)
.

Если

же

сервер

определяет,

что

он

не

может

закодировать

содержимое

ресурса

ни

одним

из

указанных

клиентом

методов

кодирования,

он

должен

послать

ему

ответ

с

кодом

состо
яния

406

«
Not

Acceptable
»
.

Если

же

на

сервере

содержимое

ресурса

может

быть

закодировано

всего

одним

методом,

то

вместо

ответа

406

«
Not

Acceptable
»

спецификация

рекомендует

вернуть

ресурс,

игнорируя

заголовок

Accept
-
Encoding.



Практика

Исходя

из

всего

выш
еизложенного,

можно

определить

следующий

алгоритм

работы

нашей

программы:

1.

Если

нам

необходимо

получить

от

сервера

данные

в

сжатом

виде,

то

включаем

в

запрос

заголовок

Accept
-
Encoding

2.

Т.к.

сервер

может

установить

минимальную

версию

HTTP
,

необходимую

для

сжа
тия,

то

также

целесообразно

указать

версию

протокола


1.1


(на

данный

момент



это

последняя

версия

HTTP
)


3.

Отправляем

запрос

на

сервер

4.

Получаем

ответ

и

анализируем

его

заголовки:

a.

Если

присутствует

заголовок

Content
-
Encoding
,

то

данные

пришли

в

сжатом

виде



определяем

метод

сжатия,

разархивируем

данные

и

переходим

к

п.5.

b.

Если

заголовок

Content
-
Encoding

отсутствует,

то

считаем,

что

данные

пришли

в

несжатом

виде

и

переходим

к

п.5.

5.

Показываем

полученные

данные


В

этом

простом

алгоритме

остается

один

вопрос



ч
ем

разархивировать

данные
?

В

качестве

«универсальной»

библиотеки

для

работы

с

gzip
,

которая

будет

одинаково

хорошо

работать

на

любых

версиях

Delphi
,

начиная

с

Delphi

5,

можно

предложить

библиотеку

delphi

zlib
.

Это

бесплатная

библиотека

с

открытым

исходным

кодом,

которую

вы

можете

скачать

с

сайта

http://www.base2ti.com/?id=delphi.zlib
.

Теперь

реализуем

рассмотренный

выше

алгоритм

в

виде

небольшого

приложения
.

Создаем

новый

проект

Delphi

(
назовем

его

«
syn
a
_
gzip
»
)

и

размещаем

на

форме

приложения

компоненты

как

показано

на

рисунке:


Рисунок
11
. Внешний вид приложения "
syna
_
gzip
"


Теперь

скачиваем

библиотеку

Delphi

zlib

(
http://w
ww.base2ti.com/?id=delphi.zlib
),

заходим

в

настройки

проекта

(
Project



Options


Delphi

Compiler
)

и

указываем

в

поле

«
Search

Path
»

путь

к

модулям

библиотеки:


Рисунок
12
. Указание пути поиска библиотеки
Delphi

Zlib

Подключаем

в

uses

главного

приложения

следующие

модули:



HttpSend
,

SynaUtil

из

библиотеки

Synapse




ZLibExGZ

из

бибилотеки

Delphi

Zlib
.



Теперь

напишем

обработчик

кнопки

«
»,

в

котором

и

реализуем

наш

алгоритм

работы

с

Gzip

в

Synapse
:

procedure

Tfmain
.
(
Send
er
:

TObject
);

var

Client
:

THTTPSend
;


OutStr
:

string
;

begin


memData.Lines.Clear;


Client
:=
THTTPSend
.
Create
;


try


Client
.
Headers
.
Add
(
'Accept
-
Encoding: gzip'
);


Client
.
Protocol
:=
'1.1'
;


if

Client
.
(
,
edUrl
.
Text
)

then


begi
n


//привели заголовки к виду Name=Value


HeadersToList
(
Client
.
Headers
);


//
проверяем

заголовок


if

Trim(
Client
.
Headers
.
Values
[
'Content
-
Encoding'
])

=

'gzip'

then


begin


//
считываем

данные

из

потока


OutS
tr
:=
ReadStrFromStream
(
Client
.
Document
,
Client
.
Document
.
Size
);


//
разжимаем

данные


OutStr
:=
GZDecompressStr
(
OutStr
);


end


else


OutStr
:=
ReadStrFromStream
(
Client
.
Document
,
Client
.
Document
.
Size
);


//
выводим

текст

в

Memo


memData
.
Lines
.
Add
(
OutStr
);


end
;


finally


Client
.
Free


end
;

end
;

Теперь можно запустить приложение и проверить его работу:


Рисунок
13
. Результат работы приложения "
syna
_
gzip
"



Приложенные файлы

  • pdf 8812850
    Размер файла: 2 MB Загрузок: 1

Добавить комментарий