【Rust】axumでcsrfを使いたい(axum_csrf)

axum_csrf = “0.11.0”

use axum_csrf::{CsrfConfig, CsrfToken};

#[derive(Serialize, Deserialize)]
struct LoginForm {
    username: String,
    password: String,
    authenticity_token: String,
}

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

    // tracing_subscriber::fmt::init();
    let config = CsrfConfig::default();

    let serve_dir = ServeDir::new("static").not_found_service(ServeFile::new("static"));

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/login", post(handle_login))
        .route("/home", get(handle_home))
        .route("/upload", post(handle_upload))
        .layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
        .nest_service("/static", serve_dir.clone())
        .fallback_service(serve_dir)
        .with_state(config);

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

async fn handle_index(token: CsrfToken) -> impl IntoResponse {

    let keys = token.authenticity_token().unwrap();

    let tera = tera::Tera::new("templates/*").unwrap();

    let mut context = tera::Context::new();
    context.insert("title", "Index page");
    context.insert("Keys", &keys);

    let output = tera.render("test.html", &context);
    (token, axum::response::Html(output.unwrap()))
}

async fn handle_login(token: CsrfToken, axum::Form(loginform): axum::Form<LoginForm>)-> axum::response::Html<String> {
    if token.verify(&loginform.authenticity_token).is_err() {
        println!("Token is invalid");
    } else {
        println!("Token is Valid lets do stuff!");
    }
    let username = loginform.username;
    let password = loginform.password;
    let authenticity_token = loginform.authenticity_token;
    println!("username:{}, password:{}, authenticity_token: {}", username, password, authenticity_token);
 
}
    <form method="post" action="/login">
        <input type="hidden" name="authenticity_token" value="{{ Keys }}"/>
        <div class="mb-3 mt-3">
        <input type="text" class="form-control" placeholder="user name" name="username"/>
          </div>
        <input type="password" class="form-control" placeholder="password" name="password">
        <span class="fs-s">If you don't have account, you can <a href="/signup">Sign up</a>.</span><br/>
        <input type="submit" class="btn submit" value="login" />
     </form>

Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.90s
Running `target/debug/axum`
Token is Valid lets do stuff!
username:asdfggggg, password:asdfggggg, authenticity_token: QnDhKw70YahSelDPpBxJHzgdlXvhFCe2ZKOv+wH82zo=

おおおおおおおおおおおおお、なるほどこれは凄い!!!!
sessionにsession_tokenをセットするロジックを書く方法もありますが、これの方が楽ですね。

【Rust】マージソート

大容量データの計算量が早い。

fn merge_sort(data: Vec<usize>) -> Vec<usize>{
    if data.len() <= 1 {
        return data;
    }
        
    let mid = data.len() / 2;
    let mut left: Vec<usize> = merge_sort(data[0..mid].to_vec());
    let mut right: Vec<usize>  = merge_sort(data[mid..].to_vec());

    return merge(left, right);
}

fn merge(left: Vec<usize>, right: Vec<usize>) -> Vec<usize> {
    let mut result: Vec<usize> = Vec::new();

    let mut i = 0;
    let mut j = 0;

    while (i < left.len()) && (j < right.len()) {
        if left[i] <= right[j] {
            result.push(left[i]);
            i += 1;
        } else {
            result.push(right[j]);
            j += 1;
        }
    }

    if i < left.len() {
        result.append(&mut left[i..].to_vec());
    }
    if j < right.len() {
        result.append(&mut right[j..].to_vec());
    }
    return result;
}

fn main() {
    let mut data = vec![6, 15, 4, 2, 8, 5, 11, 9, 7, 13];
    println!("{:?}", merge_sort(data));
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/rust`
[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

何だこれは… 凄いな

【Rust】ヒープソート

ヒープは木構造で構成され、子ノードは常に親ノードより大きいか等しい。
ヒープに要素を追加する場合は、木構造の最後に要素を追加し、親と要素を比較して親よりも小さければ親と交換する。
最小値は必ずルートにある。
取り出した場合は、最後の要素を一番上に移して木を再構成する。

木構造が
A
B C
D E F G
の時、リストでは
[ABCDEFG] で表現する。


fn main() {
    
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 0..data.len() {
        let mut j = i;
        while ( j > 0 ) && (data[(j - 1) / 2] < data[j]) {
            data.swap(j, (j - 1) / 2);
            j = (j - 1) / 2;
        }
    }
    println!("{:?}", data);

    let mut n: Vec<usize> = Vec::new();
    for i in 1..(data.len()+1) {
        n.push(i.try_into().unwrap());
    }
    n.reverse();

    for i in n {
        data.swap(0, i - 1);
        let mut j = 0;
        while(((2 * j + 1) < (i - 1)) && (data[j] < data[2 * j + 1])) || (((2 * j + 2) < (i - 1)) && (data[j] < data[2 * j + 2])) {
            if ((2 * j + 2) == (i - 1)) || (data[2 * j + 1] > data[2 * j + 2]) {
                data.swap(2 * j + 1, j);
                j = 2 * j + 1;
            } else {
                data.swap(2 * j + 2, j);
                j = 2 * j + 2;
            } 
        }
    }
    println!("{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.37s
Running `target/debug/rust`
[15, 13, 11, 8, 9, 4, 5, 2, 7, 6]
[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

ヒープの構成のところで、
15
13 11
8 9 4 5
2 7 6
と、ヒープの構成になっていることがわかる。
heap sortはbubble, insert, selection sortより早いことがわかっている。

関数化する。

use std::collections::VecDeque;

fn heapify(data: &mut VecDeque<usize>, i: usize) {
    let left = 2 * i + 1;
    let right = 2 * i + 2;
    let size = data.len() - 1;
    let mut min = i;
    if left <= size && data[min] > data[left] {
        min = left;
    }
    if right <= size && data[min] > data[right] {
        min = right;
    }
    if min != i {
        data.swap(min, i);
        heapify(data, min);
    }
}

fn main() {
    
    let mut data = VecDeque::from([6, 15, 4, 2, 8, 5, 11, 9, 7, 13]);

    let mut n: Vec<usize> = Vec::new();
    for i in 0..(data.clone().len()/2) {
        n.push(i.try_into().unwrap());
    }
    n.reverse();

    println!("{:?}", n);

    for i in n {
        heapify(&mut data, i);
    }
    println!("{:?}", data);

    let mut sorted_data: VecDeque<usize> = VecDeque::new();

    for _ in 0..(data.len()) {
        let k = data.clone().len();
        data.swap(0, k - 1);
        sorted_data.push_back(data.pop_back().unwrap());
        println!("{:?}", sorted_data);
        println!("{:?}", data);
        if (k != 1) {
            heapify(&mut data, 0);
        }
    }

    println!("{:?}", sorted_data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.41s
Running `target/debug/rust`
[4, 3, 2, 1, 0]
[2, 6, 4, 7, 8, 5, 11, 9, 15, 13]
[2]
[13, 6, 4, 7, 8, 5, 11, 9, 15]
[2, 4]
[15, 6, 5, 7, 8, 13, 11, 9]
[2, 4, 5]
[9, 6, 11, 7, 8, 13, 15]
[2, 4, 5, 6]
[15, 7, 11, 9, 8, 13]
[2, 4, 5, 6, 7]
[13, 8, 11, 9, 15]
[2, 4, 5, 6, 7, 8]
[15, 9, 11, 13]
[2, 4, 5, 6, 7, 8, 9]
[15, 13, 11]
[2, 4, 5, 6, 7, 8, 9, 11]
[15, 13]
[2, 4, 5, 6, 7, 8, 9, 11, 13]
[15]
[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]
[]
[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

ほう、これは凄いな。というか、debugしながらやらないと理解できない。。

【Rust】スタック(push, pop)を実装する

rustのvectorのスタックはpush, popを使う

fn main() {
    
    let mut stack: Vec<i32> = Vec::new();

    stack.push(3);
    stack.push(5);
    stack.push(2);

    println!("{:?}", stack);

    let temp = stack.pop();

    println!("{:?}", temp);
    println!("{:?}", stack);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/rust`
[3, 5, 2]
Some(2)
[3, 5]

最初に入れたものから取り出すにはお馴染みのVecDequeを使います。

use std::collections::VecDeque;

fn main() {
    
    let mut stack: VecDeque<i32> = VecDeque::new();

    stack.push_back(3);
    stack.push_back(5);
    stack.push_back(2);

    println!("{:?}", stack);

    let temp = stack.pop_front();

    println!("{:?}", temp);
    println!("{:?}", stack);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.57s
Running `target/debug/rust`
[3, 5, 2]
Some(3)
[5, 2]

【Rust】バブルソート

後ろから順番に定まっているのが分かります。

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 0..data.len() {
        for j in 0..(data.len() - i - 1) {
            if data[j] > data[j + 1] {
                data.swap(j, (j + 1));
            }
            println!("sorted data:{:?}", data);
        }
    }
    println!("sorted data:{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.82s
Running `target/debug/rust`
sorted data:[6, 15, 4, 2, 8, 5, 11, 9, 7, 13]
sorted data:[6, 4, 15, 2, 8, 5, 11, 9, 7, 13]
sorted data:[6, 4, 2, 15, 8, 5, 11, 9, 7, 13]
sorted data:[6, 4, 2, 8, 15, 5, 11, 9, 7, 13]
sorted data:[6, 4, 2, 8, 5, 15, 11, 9, 7, 13]
sorted data:[6, 4, 2, 8, 5, 11, 15, 9, 7, 13]
sorted data:[6, 4, 2, 8, 5, 11, 9, 15, 7, 13]
sorted data:[6, 4, 2, 8, 5, 11, 9, 7, 15, 13]
sorted data:[6, 4, 2, 8, 5, 11, 9, 7, 13, 15]
sorted data:[4, 6, 2, 8, 5, 11, 9, 7, 13, 15]
sorted data:[4, 2, 6, 8, 5, 11, 9, 7, 13, 15]
sorted data:[4, 2, 6, 8, 5, 11, 9, 7, 13, 15]
sorted data:[4, 2, 6, 5, 8, 11, 9, 7, 13, 15]
sorted data:[4, 2, 6, 5, 8, 11, 9, 7, 13, 15]
sorted data:[4, 2, 6, 5, 8, 9, 11, 7, 13, 15]
sorted data:[4, 2, 6, 5, 8, 9, 7, 11, 13, 15]
sorted data:[4, 2, 6, 5, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 6, 5, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 6, 5, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 9, 7, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 8, 7, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    let mut change = true;
    for i in 0..data.len() {
        if (change != true)  {
            break;
        }
        change = false;
        for j in 0..(data.len() - i - 1) {
            if data[j] > data[j + 1] {
                data.swap(j, (j + 1));
            }
            println!("sorted data:{:?}", data);
            change = true;
        }
    }
    println!("sorted data:{:?}", data);
}

【Rust】挿入ソート

挿入ソートも n * (n – 1) / 2 の計算量になる。
対象データを一時領域に確保しておき、値が大きかどうかを判定して、大きい場合は入れ替えるという作業を繰り返す。
usizeは-にならないため、 j== 0の時は判定しています。

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 1..data.len() {
        let temp = data[i];
        let mut j = i - 1;
  
        while (j >= 0) && (data[j] > temp) {
            data[j + 1] = data[j];
            if j == 0 {
                break;
            }
            j -= 1;
        }
        if j == 0 && (data[j] > temp) {
            data[0] = temp;
        } else {
            data[j + 1] = temp;
        }
        println!("sorted data:{:?}", data);
    }
    println!("sorted data:{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
Running `target/debug/rust`
sorted data:[6, 15, 4, 2, 8, 5, 11, 9, 7, 13]
sorted data:[4, 6, 15, 2, 8, 5, 11, 9, 7, 13]
sorted data:[2, 4, 6, 15, 8, 5, 11, 9, 7, 13]
sorted data:[2, 4, 6, 8, 15, 5, 11, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 15, 11, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 11, 15, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 9, 11, 15, 7, 13]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 15, 13]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

途中経過を見ると美しいが、これも計算量が多くなる模様。

【Rust】選択ソート

1件ずつ調べていく手法です。最小値の抽出は以下の通り。

fn main() {
    let data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    let mut min = 0;
    for i in 0..data.len() {
        if data[min] > data[i] {
            min = i;
        }
    } 
    println!("minimal num:{}", data[min]);
}

### 選択ソート
n * (n – 1) / 2 の計算量が必要になる。もっとも小さいものを左から順番に並べていく。
mem::swap(&mut data[min], &mut data[i]);は使えない。

use std::mem;

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 0..data.len() {
        let mut min = i;
        let k = i + 1; 
        for j in k..data.len() {
            if data[min] > data[j] {
                min = j;
            }
        }
        // mem::swap(&mut data[min], &mut data[i]);
        data.swap(i, min);
    }   
    println!("sorted data:{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/rust`
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

なるほど、普段ソートとか考えたことなかったが、こうなってるのか。。面白いね。

【Rust】axumでファイルのアップロードを受け取る

HTML側

<form action="/upload" method="post" enctype="multipart/form-data">
    <div>
        <p>name</p>
        <input type="text" name="name"><br><br>
        <input type="file" name="testfile" onchange="previewFile(this);">
    </div>
    <div>
        <input type="submit" value="送信する">
    </div>
</form>
<p>プレビュー</p>
<img id="preview">

<script>
function previewFile(file) {
    var fileData = new FileReader();
    fileData.onload = (function() {
        document.getElementById('preview').setAttribute("style","width:100px;height:100px");
        document.getElementById('preview').src = fileData.result;
        
    });
    fileData.readAsDataURL(file.files[0]);
}
</script>

axum側
axum = { version=”0.8.1″, features = [“multipart”] }
tokio = { version = “1.25”, features = [“full”] }

use tower_http::services::{ServeDir, ServeFile};
use axum::{
    extract::Multipart,
    extract::DefaultBodyLimit,
    routing::post,
    routing::get,
    Router,
};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;


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

    let serve_dir = ServeDir::new("static").not_found_service(ServeFile::new("static"));

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/upload", post(handle_upload))
        .layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
        .nest_service("/static", serve_dir.clone())
        .fallback_service(serve_dir);

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

async fn handle_upload(mut multipart: Multipart)-> axum::response::Html<String> {

    while let Some(field) = multipart.next_field().await.unwrap(){
        let param_name = field.name().unwrap().to_string();
        match param_name.as_str() {
            "name" => {
                let name = field.text().await.unwrap();
                println!("tags: {}", name);
            }
            "testfile" => {
                let file_name = match field.file_name() {
                    Some(name) => name.to_owned(),
                    None => panic!("file_name is None"),
                };
                match field.bytes().await {
                    Ok(data) => {
                        println!("Length of `{}` is {} bytes", param_name, data.len());
                        let mut file = File::create(format!("./tmp/{}", file_name)).await.unwrap();
                        file.write_all(&data).await.unwrap();
                    }
                    Err(e) => {
                        eprintln!("Error reading `{}`: {}", param_name, e);
                        // return;
                    }
                }

            }
            _ => {
                println!("unknown param_name: {}", param_name);
            }
        }
    }

    let tera = tera::Tera::new("templates/*").unwrap();

    let mut context = tera::Context::new();
    context.insert("title", "Index page");

    let output = tera.render("test.html", &context);
    axum::response::Html(output.unwrap())
}

なるほど、なかなか面白い。

【Rust】S3のbucketへ画像アップロード

use dotenv::dotenv;
use std::env;
use tokio::io::AsyncReadExt;
use rusoto_s3::*;
use rusoto_core::*;

#[tokio::main]
async fn main() {
    dotenv();
    let aws_access_key = env::var("AWS_ACCESS_KEY_ID").unwrap();
    let aws_secret_key = env::var("AWS_SECRET_ACCESS_KEY").unwrap();

    std::env::set_var("AWS_ACCESS_KEY_ID", aws_access_key);
    std::env::set_var("AWS_SECRET_ACCESS_KEY", aws_secret_key);

    s3_upload().await;
}

async fn s3_upload() -> Result<(), Box<dyn std::error::Error>> {
    let s3_client = S3Client::new("ap-northeast-1".parse().unwrap());
    let mut file = tokio::fs::File::open("./data/test.png").await?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).await?;

    let result = s3_client.put_object(PutObjectRequest {
        bucket: String::from("hpscript"),
        key: "test.png".to_string(),
        body: Some(StreamingBody::from(buffer)),
        ..Default::default()
    }).await?;
    // do thing with result
    Ok(())
}

データがうまくアップロードされているのが分かります。なるほどね〜

【Rust】base64で画像を文字列に変換

想像以上に文字列のデータ量が多すぎる…

use std::fs::File;
use std::io::Write;
use std::path::Path;

static FILE_NAME: &'static str = "test";

fn main() {

    let path = "./data/img1.png";
    let base64 = image_base64::to_base64(path);
    println!("{:?}", base64);
    
    let img = image_base64::from_base64(base64);

    let file_type = "png";
    let mut output = File::create(&Path::new(&format!("./data/{}.{}", FILE_NAME, file_type))).unwrap();
    output.write_all(img.as_slice()).unwrap();

}


+mQS0RoL4XWkGRlzADREjATCW58eL2T9dzP754iZx5OBzfBHPz51wNvO7sxlwI3lWnIa/An5fSMLuH42HOzfZiABLKqp+KD7hAPsQB9L4eP1gFPJgwPDTUympfvNM9TWq/Z7iX4pAK4463hDa3MXJ4veh2slWxtqowOOXgnUoyHp4H24dllHc5QPw85K0EzUDsE7MjIY5YWH4WMe2ynw7WP0+Het1k3Xn6ReoDw9mo6EY5sccKOU2GADLZaDiBOZF38EDiSclHU0Imzpp7KCNqs9jUGmtGD3drKFe0HEcAGtKAjBmxnFJHgFg2QqCQFHPz6RzMLwzEMP8kUjwkRhB4EWQ7RGUlUzycFSDHCUF/uqs0bdbrPcs4NlPF3JuH46pOBAFj7DHJ5IAP9+fzYLO9dcreXgl8Kj67drmF5dzfzqfA6D9tiQdNfw7BXHgSJ/7NOjkRxsP53jvT/fcnbr6s6RVmMmzI3Y5ZtNujliCEQVbI5duj16GL36atHJ3yuq96R4Hs7xAFyrdGVi+Jxy/7xdFifhxIHwBpH+5tAmleNhSgpv2n2tbMFwIkiQMfgBUA6fx/gCSy/eG38mPwdf/fSEbb5HPYgdDnX61VJjxK3OHIhfkoKQq4WE2SD1gpdPl3WGoQ4D4/bAwvuIAoFdGa87SA/CycQ0D4GBn43A3U2UlBf4NaK7emUxiqOu2HaFlpdlVUH9eUpocIyphAiT5DQbAchnY88L7xj5pGbcO8jgyGNNNDO3qtoMewrODJp9gXQiBG2UDAXjeSALA1ValS7tlF4CpSAlSHMDwq2tbgHPhnu5GEhjDZXuKBFfQwa3hIPCjiFAStkVbKSsB2ou2bBDd/1j0bA/QDpCDWisQF/ofJJolHwfsTlud4jfXf9m0ZTNt3ScNmzDC0MpQC8Kkzp3aKyO5eaMHkvMO7dr069XFRK//mMH6zhMsF7par1s0JdprxvbY5WhtAnQfFCYA+4HKeJ0AaeTW+Cf2BH/e/Ai7hGv7I3JDFy12tzbRU4fdtzQ/FL/IUOMBH86d8K+45XeOxIDejOfBhXEzP6ZCHrJeIQCfAQD71gDANg0AYJwW5mrq52goEOKPJEbQ9QCwZrcOrfkOlMrt20SJTmBAS+ALGV2mWDAAbp6BKnSE6LjPVzsFVegB3dvXXcvCt4DQMJ4VrCM+dK54QwB4/kjtGgD481AZB2AKw1xR+ngygOHl5dyznwaGeriNMNMBXDUc4wC6ipgDsfEDR6SewDDRg4+fnUrBCNuEDXPWvmfvaGsO6OrbQ40IohpxVKI+H/gV6Rah2i6hcc+PrL575w56mr3GDTNYOt02eu1MZNXwzEJXGLYYotvbRc/3ir7eI3r0GX477CGKtqzfGb8yaeMcoHjIKpew1W7xPrNBZt6d5nHqI1/sbzC/D0D+6urmH89nodSM2jvS65syf1XUAcD4vf53AIZoGPbRgjd/gmHvmUPrYm+4D9aYat5XUH82f28iqz/LezAAltcIeHGAVqEVqqrQ+B96Ra6DpXWjpKsJDH0aqqaIm2GxYBQ
….

うーん、これはちょっと無理ですね。。