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()в вашем коде. Попробуйте заменить их на оператор?.