Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTTP-клиент

Цель этого упражнения — написать небольшой HTTP-клиент, который подключается к веб-сайту.

Подготовка

✅ Перейдите в папку intro/http-client.

✅ Откройте подготовленную заготовку проекта в intro/http-client.

✅ Добавьте свои данные для подключения к сети в cfg.toml, как при проверке оборудования.

✅ Откройте документацию этого проекта командой:

cargo doc --open

intro/http-client/examples/http_client.rs содержит решение. Запустить его можно командой:

cargo run --example http_client

Установка соединения

По умолчанию доступен только незашифрованный HTTP, что довольно сильно ограничивает выбор хостов для подключения. Мы будем использовать http://neverssl.com/.

В ESP-IDF HTTP-соединениями клиента управляет http::client::EspHttpClient из крейта esp-idf-svc. Он реализует трейт http::client::Client из embedded-svc, который определяет функции для методов HTTP-запросов, таких как GET или POST. Самое время посмотреть документацию, которую вы открыли командой cargo doc --open, для esp_idf_svc::http::client::EspHttpConnection и embedded_svc::http::client::Client. Изучите доступные методы создания экземпляров.

✅ Создайте новый EspHttpConnection с конфигурацией по умолчанию. Подходящий конструктор найдите в документации.

✅ Получите клиент из только что созданного соединения.

Вызов HTTP-методов клиента (например, get(url)) возвращает embedded_svc::http::client::Request. Его нужно отправить явно: клиент может передавать данные вместе с запросом.

Функция get использует as_ref(). Это значит, что вместо ограничения одним конкретным типом, например только String или только &str, функция принимает любой тип, реализующий трейт AsRef<str>, то есть любой тип, для которого вызов .as_ref() даёт &str. Это работает для String и &str, а также для перечисления Cow<str>, которое содержит один из двух предыдущих вариантов.

#![allow(unused)]
fn main() {
    let request = client.request(Method::Get, url.as_ref(), &headers)?;
}

Успешный ответ содержит код состояния в диапазоне 2xx, за которым следует исходный HTML сайта.

✅ Проверьте, что соединение прошло успешно.

✅ Возвращайте ошибку, если код состояния не входит в диапазон 2xx.

#![allow(unused)]
fn main() {
match status {
        200..=299 => {
        }
        _ => bail!("Unexpected response code: {}", status),
    }
}

Ошибку статуса можно вернуть с помощью крейта Anyhow. Он содержит различные средства для упрощения обработки ошибок на уровне приложения. Крейт предоставляет универсальный anyhow::Result<T>, который оборачивает успешный случай (Ok) в T и избавляет от необходимости указывать тип Err, при условии что каждая возвращаемая ошибка реализует std::error::Error.

✅ Читайте полученные данные по частям в буфер байтов (u8) с помощью Read::read(&mut reader,&mut buf). Read::read возвращает количество прочитанных байт. Чтение завершено, когда это значение равно 0.

✅ Выведите общее количество прочитанных байт.

✅ Выведите полученные данные в консоль. 💡 Ответ в буфере представлен байтами, поэтому может понадобиться метод для преобразования байтов в &str.

Дополнительные задания

✅ Обработайте коды 3xx, 4xx и 5xx, каждый в отдельной ветке match.

✅ Напишите собственное перечисление Error для представления этих ошибок. Реализуйте для него трейт std::error::Error.

Устранение неполадок

  • missing WiFi name/password: убедитесь, что cfg.toml настроен по образцу cfg.toml.example. Частая проблема — имя пакета и имя секции конфигурации не совпадают.
# Cargo.toml
#...
[package]
name = "http-client"
#...

# cfg.toml
[http-client]
wifi_ssid = "..."
wifi_psk = "..."
  • Guru Meditation Error: Core 0 panic'ed (Load access fault). Exception was unhandled. Это может быть вызвано .unwrap() в вашем коде. Попробуйте заменить их на оператор ?.