Flex Gateway新着情報
Governance新着情報
Monitoring API ManagerFlex Gateway ポリシー開発キット (PDK) を使用すると、リクエスト/レスポンスヘッダーおよびボディの読み取りと書き込みを行うことができます。
要求および応答を絞り込むときに、Proxy Wasm はヘッダーとボディの処理を必ず特定の順序で発生する 2 つのメインイベントに分割します。ポリシーでボディを処理する前に、すべてのポリシーでヘッダーイベントが完全に伝播されている必要があります。たとえば、2 つのポリシーが適用される API のイベントフローは次のようになります。
ポリシー 1 はリクエストヘッダーイベントを処理します。
ポリシー 2 はリクエストヘッダーイベントを処理します。
バックエンドサービスはリクエストヘッダーを受信します。
ポリシー 1 はリクエストボディイベントを処理します。
ポリシー 2 はリクエストボディイベントを処理します。
バックエンドはリクエストボディを受信します。
バックエンドは応答を送信します。
ポリシー 2 はレスポンスヘッダーイベントを処理します。
ポリシー 1 はレスポンスヘッダーイベントを処理します。
クライアントはレスポンスヘッダーを受信します。
ポリシー 2 はレスポンスボディイベントを処理します。
ポリシー 1 はレスポンスボディイベントを処理します。
クライアントはレスポンスボディを受信します。
この順序では、次の制限が生じます。
ポリシーはボディを読み取った後にヘッダーを変更できない。
すべてのポリシーはボディを読み取る前にヘッダーイベントを完全に処理する必要がある。ボディのデータをあるポリシーに保存し、別のポリシーのボディデータを使用してヘッダーを変更することはできません。
すべてのポリシーはヘッダーイベントを処理するときにバックエンドサービスに到達する。拒否された要求からのデータがバックエンドサービスに到達しないようにするには、リクエストヘッダーのみに基づいてヘッダーを拒否する必要があります。ボディに基づいて要求を拒否すると、大部分のサーバーおよびクライアントは中断された要求を検出して、リクエストヘッダーを破棄できます。
ポリシーソースコードでボディを読み取ると、ヘッダーとボディのイベントが分割されます。たとえば、ボディを読み取るために宣言 let body_state = headers_state.into_body_state().await; が使用されている場合、宣言前のすべてがヘッダーイベントで発生し、宣言後のすべてがボディイベントで発生します。
リクエストヘッダーとレスポンスヘッダーの両方にアクセスするには、into_headers_state() メソッドを呼び出して、RequestState または ResponseState をヘッダー状態に変換して、処理が完了するのを待ちます。メソッドを呼び出したら、HeadersHandler trait の関数を呼び出してヘッダーにアクセスして操作します。
pub trait HeadersHandler {
fn headers(&self) -> Vec<(String, String)>;
fn header(&self, name: &str) -> Option<String>;
fn add_header(&self, name: &str, value: &str);
fn set_header(&self, name: &str, value: &str);
fn set_headers(&self, headers: Vec<(&str, &str)>);
fn remove_header(&self, name: &str);
}
以下のコードを実装することで、ラップされた関数の on_request または on_response でヘッダーにアクセスすることができます。
async fn request_filter(request_state: RequestState, _config: &Config) {
let headers_state = request_state.into_headers_state().await;
let headers_handler = headers_state.handler();
let old_value = headers_handler.header("request-header").unwrap_or_default();
let new_value = "--replaced--";
logger::info!("Old request header value: {old_value}, New value: {new_value}");
headers_handler.set_header("request-header", new_value);
}
async fn response_filter(response_state: ResponseState, _config: &Config) {
let headers_state = response_state.into_headers_state().await;
let headers_handler = headers_state.handler();
let old_headers = headers_handler.header("request-header").unwrap_or_default();
let new_value = vec![("response-header1", "--replaced--"), ("response-header2", "--replaced--")];
logger::info!("Old request header value: {old_headers:?}, New value: {new_value:?}");
headers_handler.set_headers(new_value);
}
Envoy はメソッド、スキーム、パス、承認、状況コードをヘッダーとして扱います。PDK は要求の Envoy ヘッダーである :method、:scheme、:path、:authority、:status にアクセスして変更するために以下のメソッドを提供しています。
async fn request_filter(request_state: RequestState, _config: &Config) {
let headers_state = request_state.into_headers_state().await;
let method = headers_state.method();
let scheme = headers_state.scheme();
let authority = headers_state.authority();
let path = headers_state.path();
...
}
async fn response_filter(response_state: ResponseState, _config: &Config) {
let headers_state = response_state.into_headers_state().await;
let status = headers_state.status_code();
}
| ボディの読み取りと書き込みのペイロードサイズは 1MB 未満に制限されています。 |
リクエストボディとレスポンスボディの両方にアクセスするには、into_body_state() メソッドを呼び出して、RequestState または ResponseState をボディ状態に変換して、処理が完了するのを待ちます。
let body_state = request_state.into_body_state().await;
前の .await は、キャンセルポイントになります。要求中にフローがキャンセルされると、.await が再開されない可能性があります。
|
元の状態がすでにヘッダー状態に変換されている場合は、同じ関数を呼び出してボディ状態に変換します。例:
let headers_state = request_state.into_headers_state().await;
let body_state = headers_state.into_body_state().await;
into_body_state() を呼び出したら、BodyHandler trait の関数を呼び出してヘッダーにアクセスして操作します。
pub trait BodyHandler {
fn body(&self) -> Vec<u8>;
fn set_body(&self, body: &[u8]) -> Result<(), BodyError>;
}
Envoy はヘッダーとボディのデータを共有するために同じバッファを使用するため、ポリシーはヘッダーとボディに同時にアクセスすることはできません。もしポリシーが両方を読み取る必要がある場合は、次の手順に従ってください。
ヘッダーを読み取り、必要な値を変数に保存します。
ボディを読み取ります。
応答と要求の両方で、ヘッダーとボディを読み取ることができます。ただし、ボディを読み取った後でヘッダーを変更することはできません。ボディを読み取る前にすべてのヘッダーの修正を完了してください。例:
async fn request_filter(request_state: RequestState) {
let headers_state = request_state.into_headers_state().await;
let headers_handler = headers_state.handler();
let agent = headers_handler.header("User-Agent").unwrap_or_else(|| "Undefined".to_string());
// Removing old content length header before manipulating body
headers_handler.remove_header("content-length");
let body_state = headers_state.into_body_state().await;
let body_handler = body_state.handler();
let body = body_handler.body();
logger::info!("User: {agent} sent: {}", String::from_utf8_lossy(body.as_slice()));
let new_body = "new body".as_bytes();
match body_handler.set_body(&new_body) {
Ok(_) => logger::info!("Body updated"),
Err(e) => logger::info!("Unable to set body. Reason: {e:?}),
}
}
このコードでは content-length ヘッダーを削除します。これはボディを修正するために必要です。
|
BodyHandler::set_body() メソッドは、Result<(), BodyError> オブジェクトを返します。ボディの更新は、以下が原因で失敗する可能性があります。
BodyError::BodyNotSent: 現在の HTTP フローにボディ (GET 要求など) がありません。
BodyError::ExceededBodySize: 新しいボディが Envoy でサポートされる最大ボディバッファサイズを超えています。