Questa pagina fornisce esempi di codice che riguardano alcuni casi d'uso comuni per i plug-in.
Per altri esempi di plug-in Rust, Go e C++, consulta il repository GitHub di Service Extensions per i plug-in.
La funzionalità dei plug-in è in anteprima per Media CDN.
Aggiungere intestazioni di richiesta e risposta HTTP
I seguenti esempi di codice mostrano come aggiungere intestazioni di richieste HTTP.
C++
#include "proxy_wasm_intrinsics.h"
class MyHttpContext : public Context {
public:
explicit MyHttpContext(uint32_t id, RootContext* root) : Context(id, root) {}
FilterHeadersStatus onRequestHeaders(uint32_t headers,
bool end_of_stream) override {
// Always be a friendly proxy.
addRequestHeader("Message", "hello");
replaceRequestHeader("Welcome", "warm");
return FilterHeadersStatus::Continue;
}
};
static RegisterContextFactory register_StaticContext(
CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(RootContext));Go
package main
import (
"fmt"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
types.DefaultVMContext
}
type pluginContext struct {
types.DefaultPluginContext
}
type httpContext struct {
types.DefaultHttpContext
}
func (vc *vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}
func (pc *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &httpContext{}
}
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
defer func() {
if err := recover(); err != nil {
proxywasm.SendHttpResponse(500, [][2]string{}, []byte(fmt.Sprintf("%v", err)), 0)
}
}()
// Add and replace headers.
if err := proxywasm.AddHttpRequestHeader("Message", "hello"); err != nil {
panic(err)
}
if err := proxywasm.ReplaceHttpRequestHeader("Welcome", "warm"); err != nil {
panic(err)
}
return types.ActionContinue
}
Ruggine
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(MyHttpContext) });
}}
struct MyHttpContext;
impl Context for MyHttpContext {}
impl HttpContext for MyHttpContext {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
// Always be a friendly proxy.
self.add_http_request_header("Message", "hello");
self.set_http_request_header("Welcome", Some("warm"));
return Action::Continue;
}
}I seguenti esempi di codice mostrano come aggiungere intestazioni della risposta HTTP.
C++
#include "proxy_wasm_intrinsics.h"
class MyHttpContext : public Context {
public:
explicit MyHttpContext(uint32_t id, RootContext* root) : Context(id, root) {}
FilterHeadersStatus onResponseHeaders(uint32_t headers,
bool end_of_stream) override {
// Conditionally add to a header value.
auto msg = getResponseHeader("Message");
if (msg && msg->view() == "foo") {
addResponseHeader("Message", "bar");
}
// Unconditionally remove a header.
removeResponseHeader("Welcome");
return FilterHeadersStatus::Continue;
}
};
static RegisterContextFactory register_StaticContext(
CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(RootContext));Go
package main
import (
"fmt"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
types.DefaultVMContext
}
type pluginContext struct {
types.DefaultPluginContext
}
type httpContext struct {
types.DefaultHttpContext
}
func (vc *vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}
func (pc *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &httpContext{}
}
func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
defer func() {
if err := recover(); err != nil {
proxywasm.SendHttpResponse(500, [][2]string{}, []byte(fmt.Sprintf("%v", err)), 0)
}
}()
// Conditionally add to a header value.
msgValue, err := proxywasm.GetHttpResponseHeader("Message")
if err != nil {
proxywasm.LogCriticalf("failed to get 'Message' header: %v", err)
} else if msgValue == "foo" {
if err := proxywasm.AddHttpResponseHeader("Message", "bar"); err != nil {
panic(err)
}
}
// Unconditionally remove the "Welcome" header.
if err := proxywasm.RemoveHttpResponseHeader("Welcome"); err != nil {
panic(err)
}
return types.ActionContinue
}
Ruggine
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(MyHttpContext) });
}}
struct MyHttpContext;
impl Context for MyHttpContext {}
impl HttpContext for MyHttpContext {
fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
// Conditionally add to a header value.
let msg = self.get_http_response_header("Message");
if msg.unwrap_or_default() == "foo" {
self.add_http_response_header("Message", "bar");
}
// Unconditionally remove a header.
self.set_http_response_header("Welcome", None);
return Action::Continue;
}
}Riscrivere l'URL della richiesta
I seguenti esempi di codice mostrano come riscrivere l'URL della richiesta utilizzando le espressioni regolari. I seguenti esempi di codice rimuovono parte del percorso, ma è fattibile qualsiasi mutazione dell'URI, ad esempio percorso, query o frammento.
Questi esempi mostrano anche le best practice relative alle espressioni regolari, ovvero l'utilizzo di librerie di espressioni regolari a tempo lineare e la compilazione delle espressioni al momento dell'inizializzazione del plug-in.
C++
#include "proxy_wasm_intrinsics.h"
#include "re2/re2.h"
class MyRootContext : public RootContext {
public:
explicit MyRootContext(uint32_t id, std::string_view root_id)
: RootContext(id, root_id) {}
bool onConfigure(size_t) override {
// Compile the regex expression at plugin setup time.
path_match.emplace("/foo-([^/]+)/");
return path_match->ok();
}
std::optional<re2::RE2> path_match;
};
class MyHttpContext : public Context {
public:
explicit MyHttpContext(uint32_t id, RootContext* root)
: Context(id, root), root_(static_cast<MyRootContext*>(root)) {}
FilterHeadersStatus onRequestHeaders(uint32_t headers,
bool end_of_stream) override {
auto path = getRequestHeader(":path");
if (path) {
std::string edit = path->toString(); // mutable copy
if (re2::RE2::Replace(&edit, *root_->path_match, "/\\1/")) {
replaceRequestHeader(":path", edit);
}
}
return FilterHeadersStatus::Continue;
}
private:
const MyRootContext* root_;
};
static RegisterContextFactory register_StaticContext(
CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(MyRootContext));Go
package main
import (
"fmt"
"regexp"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
types.DefaultVMContext
}
type pluginContext struct {
types.DefaultPluginContext
pathMatch *regexp.Regexp
}
type httpContext struct {
types.DefaultHttpContext
pluginContext *pluginContext
}
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}
func (ctx *pluginContext) OnPluginStart(int) types.OnPluginStartStatus {
var err error
// Compile the regex expression at plugin setup time.
ctx.pathMatch, err = regexp.Compile("/foo-([^/]+)/")
if err != nil {
proxywasm.LogErrorf("Error compiling the path regular expression: %v", err)
return types.OnPluginStartStatusFailed
}
return types.OnPluginStartStatusOK
}
func (ctx *pluginContext) NewHttpContext(uint32) types.HttpContext {
return &httpContext{pluginContext: ctx}
}
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
defer func() {
err := recover()
if err != nil {
proxywasm.SendHttpResponse(500, [][2]string{}, []byte(fmt.Sprintf("%v", err)), 0)
}
}()
path, err := proxywasm.GetHttpRequestHeader(":path")
if err != types.ErrorStatusNotFound {
if err != nil {
panic(err)
}
edit := ctx.pluginContext.pathMatch.ReplaceAllString(path, "/$1/")
if len(edit) != len(path) {
err = proxywasm.ReplaceHttpRequestHeader(":path", edit)
if err != nil {
panic(err)
}
}
}
return types.ActionContinue
}
Ruggine
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use regex::Regex;
use std::rc::Rc;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
Box::new(MyRootContext { path_match: None })
});
}}
struct MyRootContext {
path_match: Option<Rc<Regex>>,
}
impl Context for MyRootContext {}
impl RootContext for MyRootContext {
fn on_configure(&mut self, _: usize) -> bool {
self.path_match = Some(Rc::new(Regex::new(r"/foo-([^/]+)/").unwrap()));
return true;
}
fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(MyHttpContext {
path_match: self.path_match.as_ref().unwrap().clone(), // shallow copy, ref count only
}))
}
fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
}
struct MyHttpContext {
path_match: Rc<Regex>,
}
impl Context for MyHttpContext {}
impl HttpContext for MyHttpContext {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
if let Some(path) = self.get_http_request_header(":path") {
let edit = self.path_match.replace(&path, "/$1/");
if path.len() != edit.len() {
self.set_http_request_header(":path", Some(&edit));
}
}
return Action::Continue;
}
}Attivare la registrazione per le variabili personalizzate
I seguenti esempi di codice mostrano come eseguire un controllo di base sui parametri della stringa di query della richiesta ed emettere informazioni analizzate in Cloud Logging. Questi esempi mostrano anche come analizzare gli URL.
C++
#include <boost/url/parse.hpp>
#include <boost/url/url.hpp>
#include "proxy_wasm_intrinsics.h"
class MyHttpContext : public Context {
public:
explicit MyHttpContext(uint32_t id, RootContext* root) : Context(id, root) {}
FilterHeadersStatus onRequestHeaders(uint32_t headers,
bool end_of_stream) override {
WasmDataPtr path = getRequestHeader(":path");
if (path) {
std::string token = "<missing>";
boost::system::result<boost::urls::url_view> url =
boost::urls::parse_uri_reference(path->view());
if (url) {
auto it = url->params().find("token");
if (it != url->params().end()) {
token = (*it).value;
}
}
LOG_INFO("token: " + token);
}
return FilterHeadersStatus::Continue;
}
};
static RegisterContextFactory register_StaticContext(
CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(RootContext));Go
package main
import (
"fmt"
"net/url"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
types.DefaultVMContext
}
type pluginContext struct {
types.DefaultPluginContext
}
type httpContext struct {
types.DefaultHttpContext
}
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}
func (*pluginContext) NewHttpContext(uint32) types.HttpContext {
return &httpContext{}
}
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
defer func() {
err := recover()
if err != nil {
proxywasm.SendHttpResponse(500, [][2]string{}, []byte(fmt.Sprintf("%v", err)), 0)
}
}()
path, err := proxywasm.GetHttpRequestHeader(":path")
if err != types.ErrorStatusNotFound {
if err != nil {
panic(err)
}
u, err := url.Parse(path)
if err != nil {
panic(err)
}
token := u.Query().Get("token")
if token == "" {
token = "<missing>"
}
proxywasm.LogInfof("token: %s", token)
}
return types.ActionContinue
}
Ruggine
use log::info;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use url::Url;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace); // log everything, subject to plugin LogConfig
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(MyHttpContext) });
}}
struct MyHttpContext;
impl Context for MyHttpContext {}
impl HttpContext for MyHttpContext {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
if let Some(path) = self.get_http_request_header(":path") {
// Create dummy base/host to allow parsing relative paths.
let base = Url::parse("http://example.com").ok();
let options = Url::options().base_url(base.as_ref());
let token: Option<String> = match options.parse(&path) {
Err(_) => None,
Ok(url) => url.query_pairs().find_map(|(k, v)| {
if k == "token" {
Some(v.to_string())
} else {
None
}
}),
};
info!("token: {}", token.unwrap_or("<missing>".to_string()));
}
return Action::Continue;
}
}