3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rust研究:HTTPクライアント

Last updated at Posted at 2023-02-17

Rust初心者です。
Rust を使い始めて一か月くらいたちました。
簡単な HTTP クライアントの書き方を勉強しました。
とりあえずコンパイルが通るところまできましたのでメモしておきます。

お題

  • 指定したURLに接続した結果をファイルに保存し、そのときのステータスコードを標準出力に表示するツールを作りたい。
  • 使い方はこんな感じで。
// 使い方
C:\work>study0217.exe
[USAGE] study0217.exe url out_file

// Rust ロゴのダウンロード
C:\work>study0217.exe https://docs.rs/-/rustdoc.static/rust-logo-151179464ae7ed46.svg rust.svg
status code = 200

ソースコード

Cargo.toml

[package]
name = "study0217"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bytes = "1.4.0"
reqwest = "0.11.14"
tokio = {version = "1.25.0", features = ["full"] }
// main.rs
use std::io;
use std::env;
use std::fs::File;
use bytes::Buf;
use reqwest;

#[tokio::main]
async fn main() {

    // 引数をチェック
    let args:Vec<String> = env::args().collect();
    if args.len() < 3 {
        eprintln!("[USAGE] study0217.exe url out_file");
        return
    }

    // 引数から接続先URLと出力先ファイル名を取得
    let url = args[1].as_str();
    let out_file = args[2].as_str();

    match run(&url, &out_file).await {
        Ok(status_code) => println!("status code = {}", status_code),
        Err(e) => eprintln!("[ERROR] {:?}", e)
    }
}

async fn run(url:&str, out_file:&str) -> Result<u16, Box<dyn std::error::Error>> {

    // URL に対して GET メソッドでアクセス
    let res = reqwest::get(url)
        .await?;
    
    // ステータスコードを取得
    let status_code = res.status().as_u16();

    // レスポンスボディーの Read ストリームを取得
    let mut r = res.bytes()
        .await?
        .reader();

    // 出力先ファイルオープン
    let mut w = File::create(out_file)?;

    // レスポンスボディーを出力先ファイルにコピー
    io::copy(&mut r, &mut w)?;

    // ステータスコードをリターン
    Ok(status_code)
}

ここまでの感想。

  • ついに Rust の非同期コードに触れてしまいました。まったく非同期ではありませんが。
  • とりあえずの reqwest ライブラリを使用。ググって調べたところ、見つかったサンプルではどれも Response から text() メソッドでテキストデータを取り出す作例ばかりでした。
  • テキストじゃないコンテンツも想定して bytes() メソッドでバイト列を取り出し、reader() メソッドでストリーム化してみました。あとは io::copy で出力先ファイルにコピーするという。
  • 今回ちょっとした発見がありました。run 関数の返り値のように dyn std::error::Error のインスタンスを Result で包んで返すようにしておけば、複数種類の Error を一元的に返せることがわかりました。この例では run 関数の中で reqwest::Error と std:: io::Error を ? で返す箇所が混在しているのですが、これならどちらも受け取れます。おかげで今後のエラー処理のスタイルが確立できそうな気がしています。
  • 入門書もまともに読まずに見様見真似で一か月やってきました。順番が少しおかしいのですが、そろそろ Rust 本を一冊読むだけの下準備ができたような気がしています。
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?