在先前的文章中有提到用Rocket框架回應任意資料的方式,在另一篇文章中也有介紹用Rocket框架來實現HTTP的ETag快取機制,要怎麼樣把這兩個東西結合在一起使用呢?
Etagged Raw Response for Rocket Framework
「Etagged Raw Response for Rocket Framework」是筆者開發的套件,可以使Rocket框架依據HTTP請求的If-None-Match
標頭欄位來判斷是否要直接回應一個Vec<u8>
實體、一個Reader或是一個檔案,並且快速地替其加入Content-Type
標頭和ETag
欄位。
Crates.io
Cargo.toml
rocket-etagged-raw-response = "*"
使用方法
rocket_etagged_raw_response
這個crate提供了EtaggedRawResponse
結構體,可以用來作為要回應的資料型別。EtaggedRawResponse
結構體的用法有點類似rocket_raw_response
這個crate提供的RawResponse
結構體,以下來介紹它們不同的地方。
首先在使用EtaggedRawResponse
結構體前,必須要產生其專屬的整流片,並註冊給Rocket框架來用,因為這個套件會把ETag值利用Rocket的應用程式狀態快取下來。最基本的註冊整流片的方式如下:
let rocket = rocket::ignite().attach(EtaggedRawResponse::fairing());
EtaggedRawResponse
結構體提供的fairing
關聯函數,可以回傳一個預設的整流片。這個整流片可以在Rocket應用程式狀態中建立一個大小為64
(筆)的快取空間。快取要用來做什麼?若EtaggedRawResponse
結構實體是用from_static
關聯函數或from_vec
關聯函數來產生的話,要透過參數設定一個型別為字串的鍵值(key),用來辨認是哪個&'static [u8]
或Vec<u8>
實體的資料。EtaggedRawResponse
結構實體在送出回應的階段,會去計算資料的CRC64-ECMA校驗值,並將其轉成16進制的字串再組合成ETag值。這個ETag值就會跟著鍵值一起被存入快取中,當下次要傳送同樣為這個鍵值時的資料時,EtaggedRawResponse
結構實體就會去判斷HTTP請求中的If-None-Match
標頭欄位值和快取中鍵值所對應到的ETag值是否能weak_eq
,如果可以的話就直接回傳304
(Not Modified)這個HTTP狀態碼;如果不行的話就回傳完整的HTTP回應。快取是採用LRU(Least Recently Used)演算法來決定當快取存滿時,要先刪除哪筆快取再存入新的快取。
若EtaggedRawResponse
結構實體是用from_file
關聯函數來產生的話,則會直接使用檔案路徑當作是快取的鍵值(不過這個鍵值會和from_vec
傳入的鍵值分開儲存,不會有重疊覆蓋的問題)。EtaggedRawResponse
結構實體在送出回應的階段,會去計算檔案內容的CRC64-ECMA校驗值,並將其轉成16進制的字串再組合成ETag值。這個ETag值和檔案的修改日期就會跟著鍵值一起被存入快取中,之後的行為都和from_vec
關聯函數產生出來的EtaggedRawResponse
一樣。只不過,即便檔案的ETag值還正在被快取,EtaggedRawResponse
結構實體還是會先去檢查檔案是否有被修改(依照檔案的修改日期),來決定是否需要重新計算ETag值來取代掉快取中的ETag值。不像用from_vec
關聯函數來產生的EtaggedRawResponse
結構實體,同一個鍵值只會在需要重新快取的時候才會去計算資料的ETag值。
若EtaggedRawResponse
結構實體是用from_reader
關聯函數來產生的話,就要直接在參數中提供這筆資料的ETag值來進行ETag機制,而這個ETag值就不會進到快取中啦!
舉個例子,假設工作目錄中有image.jpg
這張JPEG圖檔。若想要讓Rocket框架提供這個圖檔,並且讓網頁瀏覽器直接瀏覽這個圖檔,而不是將其下載到檔案系統,然後又要支援ETag機制的話,程式可以這樣寫:
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
extern crate rocket_etagged_raw_response;
use std::path::Path;
use rocket_etagged_raw_response::EtaggedRawResponse;
#[get("/")]
fn view() -> EtaggedRawResponse {
let path = Path::new("image(貓).jpg");
EtaggedRawResponse::from_file(path, None::<String>, None)
}
fn main() {
rocket::ignite().attach(EtaggedRawResponse::fairing()).mount("/", routes![view]).launch();
}
如果覺得預設的快取空間太小的話,可以使用EtaggedRawResponse
結構體的fairing_cache
關聯函數來產生整流片,這個fairing_cache
關聯函數要傳入一個閉包,閉包可以直接回傳要在應用程式狀態中建立的快取空間太小。
例如:
let rocket = rocket::ignite().attach(EtaggedRawResponse::fairing_cache(|| 512));