Esta página fornece exemplos de código que abordam alguns casos de uso comuns para plug-ins.
Para mais exemplos de plug-ins em Rust, Go e C++, consulte o repositório do GitHub do Service Extensions para plug-ins.
O recurso de plug-ins está em pré-lançamento para a Media CDN.
Adicionar cabeçalhos de solicitação e resposta HTTP
Os exemplos de código a seguir mostram como adicionar cabeçalhos de solicitação 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
}
Rust
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;
}
}Os exemplos de código a seguir mostram como adicionar cabeçalhos de resposta 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
}
Rust
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;
}
}Reescrever o URL da solicitação
Os exemplos de código a seguir mostram como reescrever o URL da solicitação usando expressões regulares. Os exemplos de código a seguir removem parte do caminho, mas qualquer mutação de URI, como caminho, consulta ou fragmento, é possível.
Esses exemplos também mostram práticas recomendadas sobre expressões regulares, ou seja, usar bibliotecas de expressões regulares de tempo linear e compilar as expressões no tempo de inicialização do 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
}
Rust
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;
}
}Ativar a geração de registros para variáveis personalizadas
Os exemplos de código a seguir mostram como realizar uma verificação básica nos parâmetros de string de consulta da solicitação e emitir informações analisadas para o Cloud Logging. Esses exemplos também mostram como analisar URLs.
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
}
Rust
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;
}
}