Протокол HTTP используется преимущественно браузерами для загрузки и отправки контента. Кроме того, благодаря своей простоте и универсальности, он часто используется как высокоуровневый протокол клиент-серверного взаимодействия.
Большинтсво серверов работают с версией протокола HTTP/1.1
, который подразумевает взаимодействие в текстовом виде через TCP-сокет. Клиент отправляет на сервер текстовый запрос, который содержит:
- Команду запроса
- Заголовки запроса
- Пустую строку - признак окончания заголовков запроса
- Передаваемые данные, если они подразумеваются
В ответ сервер должен отправить:
- Статус обработки запроса
- Заголовки ответа
- Пустую строку - признак окончания заголовков ответа
- Передаваемые данные, если они подразумеваются
Стандартным портом для HTTP
является порт 80, для HTTPS
- порт с номером 443, но это жёстко не регламентировано, и при необходимости номер порта может быть любым.
GET
- получить содержимое по указанному URL;HEAD
- получить только метаинформацию (заголовки) по указанному URL, но не содержимое;POST
- отправить данные на сервер и получить ответ.
Кроме основных команд, в протоколе HTTP можно определять произвольные дополнительные команды в текстовом виде (естественно, для этого потребуется поддержка как со стороны сервера, так и клиента). Например, расширение WebDAV протокола HTTP, предназначенное для передачи файлов, дополнительно определяет команды PUT
, DELETE
, MKCOL
, COPY
, MOVE
.
Заголовки - это строки вида ключ: значение
, определяющие дополнительную метаинформацию запроса или ответа.
По стандарту HTTP/1.1
, в любом запросе должен быть как минимум один заголовок - Host
, определяющий имя сервера. Это связано с тем, что с одним IP-адресом, на котором работает HTTP-сервер, может быть связано много доменных имен.
Полный список заголовков можно посмотреть в Википедии.
Пример взаимодействия:
$ telnet ejudge.atp-fivt.org
$ telnet ejudge.atp-fivt.org 80
Trying 87.251.82.74...
Connected to ejudge.atp-fivt.org.
Escape character is '^]'.
GET / HTTP/1.1
Host: ejudge.atp-fivt.org
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Tue, 23 Apr 2019 21:18:43 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 4383
Connection: keep-alive
Last-Modified: Mon, 04 Feb 2019 17:01:28 GMT
ETag: "111f-58114719b3ca3"
Accept-Ranges: bytes
<html>
<head>
<meta charset="utf-8"/>
<title>АКОС ФИВТ МФТИ</title>
</head>
...
Протокол HTTPS - это реализация протокола HTTP поверх дополнительного уровня SSL, который, в свою очередь работает через TCP-сокет. На уровне SSL осуществляется проверка сертификата сервера и обмен ключами шифрования. После этого - начинается обычное взаимодействие по протоколу HTTP в текстовом виде, но это заимодейтвие передается по сети в зашифрованном виде.
Аналогом telnet
для работы поверх SSL является инструмент s_client
из состава OpenSSL:
$ openssl s_client -connect yandex.ru:443
Универсальным инструментом для взаимодействия по HTTP в Linux считается curl, которая входит в базовый состав всех дистрибутивов. Работает не только по протоколу HTTP, но и HTTPS.
Основные опции curl
:
-v
- отобразить взаимодействие по протоколу HTTP;-X КОМАНДА
- отправить вместоGET
произвольную текстовую команду в запросе;-H "Ключ: значение"
- отправить дополнительный заголовок в запросе; таких опций может быть несколько;--data-binary "какой-то текст"
- отправить строку в качестве данных (например, дляPOST
);--data-binary @имя_файла
- отправить в качестве данных содержимое указанного файла.
У утилиты curl
есть программный API, который можно использовать в качестве библиотеки, не запуская отдельный процесс.
API состоит из двух частей: полнофункциональный асинхронный интерфейс (multi
), и упрощённый с блокирующим вводом-выводом (easy
).
Пример использования упрощённого интерфейса:
#include <curl/curl.h>
CURL *curl = curl_easy_init();
if(curl) {
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
Этот код эквивалентен команде
$ curl http://example.com
Дополнительные параметры, эквивалентные отдельным опциям команды curl
, определяются функцией curl_easy_setopt
.
Выполнение HTTP-запроса приводит к записи результата на стандартный поток вывода, но обычно бывает нужно получить данные для дальнейшей обработки.
Это делается установкой одной из callback-функций, которая ответственна за вывод:
#include <curl/curl.h>
typedef struct {
char *data;
size_t length;
} buffer_t;
static size_t
callback_function(
char *ptr, // буфер с прочитанными данными
size_t chunk_size, // размер фрагмента данных
size_t nmemb, // количество фрагментов данных
void *user_data // произвольные данные пользователя
)
{
buffer_t *buffer = user_data;
size_t total_size = chunk_size * nmemb;
// в предположении, что достаточно места
memcpy(buffer->data, ptr, total_size);
buffer->length += total_size;
return total_size;
}
int main(int argc, char *argv[]) {
CURL *curl = curl_easy_init();
if(curl) {
CURLcode res;
// регистрация callback-функции записи
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_function);
// указатель &buffer будет передан в callback-функцию
// параметром void *user_data
buffer_t buffer;
buffer.data = calloc(100*1024*1024, 1);
buffer.length = 0;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
res = curl_easy_perform(curl);
// дальше можно что-то делать с данными,
// прочитанными в buffer
free(buffer.data);
curl_easy_cleanup(curl);
}
}