【Rust】axumでstatusコードを返却する

### StatusCodeの種類
StatusCode::OK
StatusCode::NOT_FOUND
StatusCode::BAD_REQUEST
StatusCode::INTERNAL_SERVER_ERROR
StatusCode::CREATED

### レスポンスにStatusCodeを返却する

use axum::{
    response::{IntoResponse, Response},
    http::{StatusCode},
};
//

async fn handle_health() -> impl IntoResponse {
    (StatusCode::NOT_FOUND, format!("Status Code Test: Not Found"))
}

$ curl -X GET 192.168.33.10:3000/healtcurl -X GET 192.168.33.10:3000/health
Status Code Test: Not Found

$ curl -i 192.168.33.10:3000/health
HTTP/1.1 404 Not Found
content-type: text/plain; charset=utf-8
content-length: 27
date: Tue, 25 Feb 2025 07:33:11 GMT

なるほど、エラー時にStatusCodeとメッセージを返却したいですね。

【Rust】関数内でmutexの2回目のlockとdrop(binding)の使い方

std::sync::Mutexは、同じスレッド内で2回ロックしようとするとデッドロックしてしまう。
以下のようにdropして2回ロックする方法もあるが、

let mut binding = VECT.lock().unwrap();
    let objs = binding.deref_mut();

    let mut flag = 0;
    for v in objs {
        // 略
    }

    drop(binding);

そうすると、dropした後にasyc/awitを使おうとすると、axumではルーティングでエラーになってしまう。
.route(“/”, get(handle_index))
| — ^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(ConnectInfo) -> impl Future> {handle_index}`

そのため、ロックする場合は、関数でロックして処理するようにして、一つの関数の中で2回ロックしないようにする。
別の関数で呼び出す場合は、drop(binding);とする必要はない。

fn check(name: Name) -> bool {
    let mut binding = VECT.lock().unwrap();
    let objs = binding.deref_mut();

    let mut flag = 0;
    for v in objs {
        if name.family == v.family {
            if name.first == v.first {
                if name.age == v.age {
                    return false;
                }
            }
        }
    }
    return true
}

なるほど〜、なんでずっとエラーになっているか理由がわかった!

【Rust】Peer情報をnode ip listに書き込む

bitcoinのIPアドレスリストは、ipアドレスとUnix timeが対になっている
(参考↓)
https://qiita.com/onokatio/items/04f23b7300dec7e287cb
その他にも、ipv4/ipv6や、ping, lastsend, lastrec, id, versionなどの情報も含まれる。

handshake後にpeers.datのファイルに書き込むようにして、ping, pongの箇所は、enumでactive/inactiveのstatusで表現することにした。

use serde::{Serialize, Deserialize};
use std::{io::Write};
use chrono::{Utc,DateTime};
use std::fs::OpenOptions;

#[derive(Serialize, Deserialize, Clone, Debug)]
enum Status {
    ACTIVE,
    INACTIVE,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
struct Peer {
    ip: String,
    unixtime: i64,
    nodetype: String,
    version: String,
    status: Status,
}

fn main(){
    let utc_datetime: DateTime<Utc> = Utc::now();
    println!("{}", utc_datetime.timestamp());

    let peer = Peer {ip: "192.168.33.10".to_string(), unixtime: utc_datetime.timestamp(), nodetype: "c".to_string(), version: "1.0.1".to_string(), status: Status::ACTIVE};
    println!("{:?}", peer);
    let serialized: Vec<u8> = serde_json::to_vec(&peer).unwrap();
    let mut file_ref = OpenOptions::new()
                        .append(true)
                        .open("./data/peers.dat")
                        .expect("Unable to open file");
    file_ref.write_all(&serialized).expect("write failed");
    file_ref.write_all(b"\n").expect("write failed");
}

Running `target/debug/sample`
1740366182
Peer { ip: “192.168.33.10”, unixtime: 1740366182, nodetype: “c”, version: “1.0.1”, status: ACTIVE }

nodeipリストをどうゆう風にするか、ファイルを分割するか、データ型などずっと悩んでいたけど、他のチェーンの設計は参考になりますね。

ちなみに取り出す際↓

fn get_peers() -> Result<Vec<Peer>, Box<dyn std::error::Error>>  {

    let mut peers: Vec<Peer> = Vec::new();
    for result in BufReader::new(File::open("./data/peers.dat")?).lines() {
        let line: Peer = serde_json::from_str(&result?).unwrap();
        peers.push(line);
    }
    Ok(peers)
}

【Rust】reqwestでVPSのaxumにPOSTできるかテストする

reqwestでipを指定して送る場合は、client.post(“***.**.***.**:3000/post”) だと送れないが、client.post(“http://***.**.***.**:3000/post”) だと問題なく送ることができる。

送る側(ローカル開発環境)

async fn test()-> Result<(), Box<dyn std::error::Error>> {
    let n = Name { family: "tanaka".to_string(), first:"taro".to_string(), age: 20 };
    let json = json!(n);
    println!("{:?}", &json);
    let client = reqwest::Client::new();
    let resp = client.post("http://***.**.***.**:3000/post")
        .json(&json)
        .send()
        .await?;
    let body = resp.text().await?;   
    println!("{}", body);
    Ok(())
}

受け取る側(VPS: ***.**.***.**)

async fn main() {

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/post", post(handle_post))
        .route("/test", get(handle_test));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn handle_post(extract::Json(name): extract::Json<Name>) ->impl IntoResponse{
    println!("{:?}", name);
    return "All OK".into_response();
}

受け取る側
$ cargo run
Compiling axum v0.1.0 (/home/ubuntu/test/rust_test)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.90s
Running `target/debug/axum`
Name { family: “sato”, first: “taro”, age: 50 }
Name { family: “tanaka”, first: “taro”, age: 20 }

送る側
Compiling axum v0.1.0 (/home/vagrant/dev/rust/axum)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.59s
Running `target/debug/axum`
Object {“age”: Number(20), “family”: String(“tanaka”), “first”: String(“taro”)}
All OK

reqwestが使えるというのがわかったのは収穫だが、IPだけでなく、httpsかhttpかというプロトコルも一緒に取得する必要がある。

discussion
https://github.com/tokio-rs/axum/discussions/858

ちなみに、VPSでweb socketで待ち受けて、ws://***.**.***.**:3000/ws で接続しても問題なく接続できた。

async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>, ws: WebSocketUpgrade) -> Response {
    println!("{}", addr);
    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    while let Some(msg) = socket.recv().await {
        let mut msg = if let Ok(mut msg) = msg {
            msg
        } else {
            return;
        };
        if socket.send(msg).await.is_err() {
            // client disconnected
            return;
        }
    }
}

なるほど、reqwestが試せるだけで大分進んだ気がする。

【Rust】ダイクストラ法

fn dijkstra(edges: Vec<Vec<[usize; 2]>>, num_v: usize)  -> Vec<usize>{
    let inf = 999;
    let mut dist = Vec::new();
    for _ in 0..num_v {
        dist.push(inf);
    }
    dist[0] = 0;

    let mut q = Vec::new();
    for i in 0..num_v {
        q.push(i);
    }
    
    while *&q.len() > 0 {
        let mut r = q[0];
        for i in &q{
            if dist[*i] < dist[r] {
                r = *i;
            }
        } 
        let n = q.iter().position(|n| *n == r).unwrap();
        let u = q[n];
        q.retain(|&x| x != u);
        
        for i in edges[u].clone() {
            if dist[i[0]] > dist[u] + i[1] {
                dist[i[0]] = dist[u] + i[1];
            }
        }      
    }
    return dist;
}

fn main() {
    let edges = vec![
        vec![[1, 4], [2, 3]],
        vec![[2, 1], [3, 1], [4, 5]],
        vec![[5, 2]],
        vec![[4, 3]],
        vec![[6, 2]],
        vec![[4, 1], [6, 4]],
        vec![],
    ];
    println!("{:?}", edges);

    println!("{:?}", dijkstra(edges, 7));
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/rust`
[[[1, 4], [2, 3]], [[2, 1], [3, 1], [4, 5]], [[5, 2]], [[4, 3]], [[6, 2]], [[4, 1], [6, 4]], []]
[0, 4, 3, 5, 6, 5, 8]

おおおお、これは凄いな…

【Rust】bellman fordアルゴリズム

最短距離を探すアルゴリズム

fn bellman_ford(edges: Vec<[usize; 3]>, num_v: usize)  -> Vec<usize>{
    let inf = 999;
    let mut dist = Vec::new();
    for _ in 0..num_v {
        dist.push(inf);
    }
    

    dist[0] = 0;
    let mut changed = true;

    while changed == true {
        changed = false;
        for edge in &edges {
            if dist[edge[1]] > dist[edge[0]] + edge[2] {
                dist[edge[1]] =  dist[edge[0]] + edge[2];
                changed = true;
            }
        }
    }
    return dist;
}

fn main() {
    let edges = vec![[0, 1, 4], [0, 2, 3], [1, 2, 1], [1, 3, 1], 
    [1, 4, 5], [2, 5, 2], [4, 6, 2], [5, 4, 1], 
    [5, 6, 4]];

    let dist = bellman_ford(edges, 7);
    println!("{:?}", dist);
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/rust`
[0, 4, 3, 5, 6, 5, 8]

いまいちよーわからん…

【Python】ipv4の操作

import socket

host = socket.gethostname()
print(host)

ip = socket.gethostbyname(host)
print(ip)

$ python3 test.py
vagrant
127.0.1.1

$ pip3 install netaddr

from netaddr import *

ip = IPAddress('127.0.1.1')
print(ip.version)

print(IPAddress('127.0.1.1').ipv6())

rustの場合、is_ipv4でipv4の判定、is_ipv6判定などができる。
なるほど、ipv4, v6は深掘りする必要がありそう。

【Rust】UDPのやり取り

ncコマンドの場合、”-u”がUDPを指定している
$ nc -u -l 8888
$ echo “hoge” | nc -u 127.0.0.1 8888

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    {
        let socket = UdpSocket::bind("127.0.0.1:8080")?;

        let mut buf = [0; 100];
        let (amt, src) = socket.recv_from(&mut buf)?;

        let buf = &mut buf[..amt];
        buf.reverse();
        socket.send_to(buf, &src)?;
    }
    Ok(())
}

$ echo “hoge” | nc -u 127.0.0.1 8080

egoh

UDPのbindはできるけど、レスポンスが何かおかしい…

use std::net::UdpSocket;
use std::thread;
use std::str;

fn main() -> std::io::Result<()> {
    {
        let socket = UdpSocket::bind("127.0.0.1:8080")?;
        let mut buf = [0; 2048];
        loop {
            match socket.recv_from(&mut buf) {
                Ok((buf_size, src_addr)) => {
                    thread::spawn(move || {
                        let buf = &mut buf[..buf_size];
                        let req_msg = str::from_utf8(&buf).unwrap();
                        println!("request message: {:?}", req_msg);
                    });
                },
                Err(e) => {
                    println!("couldn't receive request: {:?}", e);
                }
            }
        }
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
Running `target/debug/app`
request message: “hoge\n”

なるほど、TCPだけでなく、UDPも割と簡単にできるのね。。音声、動画streamingはUDPが優れているが…