__toc__

Пірнаймо! Філософськи, веб-сервіси HTTP можна описати як обмін даними з віддаленим сервером використовуючи лише засоби протоколу HTTP. Якщо ви хочете отримати дані з сервера - використовуйте HTTP GET. Якщо хочете відправити дані на сервер - HTTP POST. API деяких передових веб-сервісів також дозволяють створення, модифікацію та видалення даних за допомогою HTTP PUT, та HTTP DELETE. Це все. Ніяких регістрів, конвертів, обгорток, тунелювання. "Дієслова" вбудовані в протокол HTTP (GET, POST, PUT та DELETE) напряму пов’язані з операціями прикладного рівня для отримання, створення, модифікації та видалення даних.

Основна перевага такого підходу - простота, а простота зарекомендувала себе дуже популярною. Дані - зазвичай XML або JSON можуть зберігатись статично, чи генеруватись динамічно скриптом на стороні сервера, і всі основні мови програмування (включаючи Python звісно!) включать бібліотеку для доступу до них через HTTP. Зневадження також набагато простіше, бо так як кожен ресурс веб-сервісу має унікальну адресу в формі URL, ви можете просто відкрити його в веб-браузері щоб одразу побачити чисті дані.

Приклади веб-сервісів:

Google Data API які дозволяють вам взаємодіяти з широким спектром сервісів Google включаючи такі як Blogger та YouTube Сервіси Flickr які дозволяють завантажувати фотографії З і НА сайт Flickr Twitter API дозволяє публікувати оновлення статусу в Twitter ... і багато інших

Python 3 постачається з двома бібліотеками для взаємодії з веб-сервісами HTTP:

http.client - низькорівнева бібліотека яка реалізує RFC2616 (протокол HTTP) urllib.request - шар абстракції над http.client. Він надає стандартне API для взаємодії як з HTTP, так і з FTP серверами, автоматично переходить за перенаправленнями, і справляється з деякими типовими видами HTTP аутентифікації.

Так яку з них варто використовувати? Жодну! Натомість, ви повинні використовувати httplib2, сторонню бібліотеку з відкритим кодом, яка реалізує HTTP повніше ніж http.client і надає кращий рівень абстракції ніж urllib.request.

Щоб зрозуміти чому httplib2 - правильний вибір, вам спершу потрібно зрозуміти HTTP.

Особливості HTTP Існує п’ять важливих особливостей, які повинні підтримуватись всіма клієнтами.

Кешування Найважливіше що потрібно розуміти про будь-який веб-сервіс це те, що доступ до мережі неймовірно дорогий. І я не маю на увазі долари й центи, хоча, звісно, трафік теж не безкоштовний. Я маю на увазі, що, щоб відкрити з’єднання, послати запит, отримати відповідь від сервера, потрібно неймовірно багато часу. Навіть на найшвидших з’єднаннях латентність (час який проходить від відправлення запиту до отримання відповіді) може перевищити ваші очікування. Маршрутизатор працює погано, пакети губляться, проміжне проксі перебуває під атакою - завжди знайдеться причина, і ви нічого не зможете з цим зробити.

HTTP був створений з думкою про кешування. Існує цілий клас пристроїв (які називаються "кешуючими проксі"), єдине завдання яких - знаходитись між вами і рештою світу та мінімізувати навантаження на мережу. Ваша компанія чи провайдер інтернету майже напевне підтримує кешуючі проксі, хоча ви можете про них і не здогадуватись. Вони працюють, тому що кешування вбудоване в протокол HTTP.

Ви відкриваєте в своєму браузері сторінку яка може містити якісь зображення. Коли браузер просить віддати це зображення сервер додає у відповідь такі заголовки HTTP:

HTTP/1.1 200 OK Date: Sun, 31 May 2009 17:14:04 GMT Server: Apache Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT ETag: "3075-ddc8d800" Accept-Ranges: bytes Content-Length: 12405

Connection: close Content-Type: image/jpeg

Такі заголовки Cahce-Control та Expires повідомляють вашому браузеру (і кожному кешуючому проксі перед ним), що зображення може зберігатись в кеші ще рік. Рік! І якщо через рік ви відвідаєте іншу сторінку, яка теж включає це зображення, ваш браузер візьме це зображення з кешу не створюючи ніякого навантаження на мережу взагалі.

І навіть краще. Скажімо ваш браузер видалив це зображення з якоїсь причини. Можливо закінчився вільний простір на диску, можливо ви вручну очистили кеш. Без різниці. Але заголовки HTTP кажуть, що це зображення може кешуватись публічними кешуючими проксі. (Якщо говорити технічно точно, то важливо не те що кажуть заголовки, а те чого вони не кажуть. Заголовок Cache-Control не містить ключового слова private, тому ці дані кешуються за замовчуванням.) В кешуючих проксі передбачені гектари місця для зберігання даних, ймовірно, на порядок більше ніж може собі дозволити ваш локальний браузер.

Якщо ваш інтернет-провайдер підтримує кешуючий проксі-сервер, він може все ще зберігати закешоване зображення. Коли ви відвідуєте сторінку вдруге, браузер подивиться в локальний кеш, не знайде там потрібної картинки і зробить запит до мережі намагаючись завантажити її з віддаленого сервера. Але якщо кешуючий проксі досі зберігає копію картинки, він перехопить запит і віддасть картинку зі свого кешу. Це означає, що ваш запит ніколи не досягне віддаленого сервера і, можливо, навіть не покине мережу вашої компанії. Це пришвидшить завантаження (менша кількість передач пакетів між маршрутизаторами) і зекономить гроші компанії (менше даних буде завантажено з зовнішнього світу).

Кешування HTTP працює лише, коли кожен робить свою справу. З одного боку, сервери повинні відправляти з відповіддю коректні заголовки. З іншого боку, клієнти повинні розуміти і зважати на ті заголовки, перш ніж запитувати ті ж дані вдруге. Проксі посередині не є панацеєю, вони розумні рівно настільки, наскільки їм дозволяють клієнти й сервери.

Стандартні бібліотеки мови Python не підтримують кешування, зате httplib2 підтримує.

Перевірка Last-Modified Деякі дані не змінюються ніколи, а деякі постійно. Між ними ще є широкий діапазон даних, які можуть змінитись, але не змінились. Фід CNN.com оновлююється щохвилини, а фід мого блогу може не змінюватись днями а то й тижнями. В другому випадку я не хочу казати клієнтам кешувати мій фід цілий тиждень, бо тоді, коли я нарешті щось опублікую, люди не дізнаються про це ще тиждень (тому що вони зважають на заголовки, які кажуть "не перевіряти цей фід ще тиждень"). З іншого боку я не хочу щоб клієнти завантажували ввесь мій фід щогодини аби перевірити чи не з’явилось нічого нового.

Протокол HTTP має рішення і для цього. Коли ви запитуєте дані вперше, сервер може повернути заголовок Last-Modified. Він містить інформацію про час останньої зміни даних.

HTTP/1.1 200 OK Date: Sun, 31 May 2009 17:14:04 GMT Server: Apache

ETag: "3075-ddc8d800" Accept-Ranges: bytes Content-Length: 12405 Cache-Control: max-age=31536000, public Expires: Mon, 31 May 2010 17:14:04 GMT Connection: close Content-Type: image/jpeg

Коли ви запитуєте ті ж дані другий (чи третій, чи четвертий) раз, ви можете з запитом відправити заголовок If-Modified-Since в якому вказати час, який повернув вам сервер минулого разу. Якщо з того часу дані змінились, сервер поверне вам нові дані, з кодом статусу 200. Але, якщо дані з того часу не змінювались, сервер поверне спеціальний код статусу HTTP 304, який означає "дані не змінились з того моменту коли ви запитували їх востаннє". Можете перевірити це з командного рядка, використовуючи curl:

you@localhost:~$ curl -I http://wearehugh.com/m.jpg HTTP/1.1 304 Not Modified Date: Sun, 31 May 2009 18:04:39 GMT Server: Apache Connection: close ETag: "3075-ddc8d800" Expires: Mon, 31 May 2010 18:04:39 GMT Cache-Control: max-age=31536000, public

Це дуже добре, бо коли сервер повертає статус 304, він не відсилає заново всі дані. Від відсилає лише код статусу. Навіть якщо термін зберігання копії в кеші закінчився, last-modified гарантує, що ви не будете завантажувати ті самі дані двічі, якщо вони не змінились. (Як додатковий бонус, з відповіддю 304 також приходять заголовки кешування. Проксі зберігатимуть копію даних навіть якщо в них офіційно закінчиться термін зберігання, сподіваючись що якщо дані не змінились, то з наступним запитом повернеться відповідь з кодом 304 і оновленими термінами кешування.)

Стандартні бібліотеки мови Python не підтримують перевірку часу останньої модифікації, зате httplib2 підтримує.

Перевірка ETag ETag-и це альтернативний спосіб досягти того ж самого, що й за допомогою перевірки last-modified. З Etags, сервер відправляє разом з даними хеш-код в заголовку ETag. (Як точно визначається цей хеш цілком залежить від сервера. Єдина вимога - він повинен змінюватись, коли змінюються дані.) Фонове зображення, що отримується з diveintomark.org має заголовок ETag.

HTTP/1.1 200 OK Date: Sun, 31 May 2009 17:14:04 GMT Server: Apache Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT

Accept-Ranges: bytes Content-Length: 12405 Cache-Control: max-age=31536000, public Expires: Mon, 31 May 2010 17:14:04 GMT Connection: close Content-Type: image/jpeg

Коли наступного разу ви зробите запит тих самих даних, то включите хеш ETag в заголовок If-None-Match свого запиту. Якщо дані не змінились, сервер відішле нам назад код статусу 304. Так само як і при перевірці last-modified сервер відправить назад лише код 304, він не буде відправляти ті ж дані повторно. Включаючи хеш ETag в свій другий запит, ви повідомляєте сервер, що немає потреби пересилати ті ж самі дані повторно, якщо вони все ще відповідають хешу, так як ми зберегли дані з останнього запиту.

Знову за допомогою curl:

you@localhost:~$ curl -I -H http://wearehugh.com/m.jpg HTTP/1.1 304 Not Modified Date: Sun, 31 May 2009 18:04:39 GMT Server: Apache Connection: close ETag: "3075-ddc8d800" Expires: Mon, 31 May 2010 18:04:39 GMT Cache-Control: max-age=31536000, public

ETag-и зазвичай поміщуються в лапки, але лапки є частиною значення. Це означає, що потрібно відправляти й лапки назад на сервер в заголовку If-None-Match.

Стандартні бібліотеки мови Python не підтримують перевірку хешу ETags, зате httplib2 підтримує.

Стиснення Коли ми говоримо про веб-сервіси HTTP, ми майже завжди говоримо про передачу текстових даних туди-сюди по мережі. Можливо це XML, можливо JSON, можливо це всього лиш простий текст. Незалежно від формату текст чудово стискається. Наприклад фід в розділі про XML має розмір 3070 байт, але займатиме лише 941 байт після gzip стиснення. А це всього лише 30% від початкового розміру!

HTTP підтримує кілька алгоритмів стиснення. Двома найбільш вживаними є gzip та deflate. Коли ви запитуєте ресурс через HTTP, ви можете попросити сервер прислати його в стиснутому форматі. Для цього потрібно включити в запит заголовок Accept-encoding, в якому перелічити підтримувані алгоритми стиснення. Якщо сервер теж підтримує один з тих алгоритмів, він відправить вам стиснені дані (з заголовком Content-encoding який визначає використаний алгоритм). Після цього розкодування даних в ваших руках.

Стандартні бібліотеки мови Python не підтримують стиснення, зате httplib2 підтримує.

Перенаправлення Круті URI не змінюються, але багато URI дуже не круті. Веб-сайти реорганізовуються, сторінки переміщуються на нові адреси. Навіть веб-сервіси переорганізовуються. Синдикований фід за адресою http://example.com/index.xml може переміститись в http://example.com/xml/atom.xml. Чи ввесь домен може переміститись, в процесі росту і перебудови організації. http://www.example.com/index.xml може стати http://server-farm-1.example.com/index.xml.

Щоразу як ви запитуєте будь-який ресурс з HTTP сервера, сервер додає код статусу в свою відповідь. Код статусу 200 означає "все нормально, ось сторінка яку ви просили". Код статусу 404 означає "сторінка не знайдена". (Ви напевне вже бачили помилки 404 переглядаючи веб). Коди статусу від 300 і до 400 вказують на якусь форму перенаправлення.