Auf dieser Seite finden Sie Codebeispiele für einige häufige Anwendungsfälle von Plug-ins.
Weitere Beispiele für Rust-, Go- und C++-Plug-ins finden Sie im GitHub-Repository für Service Extensions-Plug-ins.
Das Feature „Plug-ins“ befindet sich für Media CDN in der Vorschau.
HTTP-Anfrage- und -Antwortheader hinzufügen
Die folgenden Codebeispiele zeigen, wie Sie HTTP-Anfrageheader hinzufügen.
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;
}
}Die folgenden Codebeispiele zeigen, wie Sie HTTP-Antwortheader hinzufügen.
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;
}
}Anfrage-URL umschreiben
Die folgenden Codebeispiele zeigen, wie Sie die Anfrage-URL mithilfe regulärer Ausdrücke umschreiben. In den folgenden Codebeispielen wird ein Teil des Pfads entfernt. Es ist jedoch jede URI-Mutation möglich, z. B. Pfad, Abfrage oder Fragment.
Diese Beispiele zeigen auch Best Practices für reguläre Ausdrücke, nämlich die Verwendung von Bibliotheken für reguläre Ausdrücke mit linearer Zeitkomplexität und das Kompilieren der Ausdrücke bei der Plug-in-Initialisierung.
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 {
const auto path = getRequestHeader(":path");
if (!path) {
LOG_ERROR("Failed to get :path header");
return FilterHeadersStatus::Continue;
}
std::string edit = path->toString(); // mutable copy
if (re2::RE2::Replace(&edit, *root_->path_match, "/\\1/")) {
if (replaceRequestHeader(":path", edit) != WasmResult::Ok) {
LOG_ERROR("Failed to replace :path header");
}
}
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;
}
}Logging für benutzerdefinierte Variablen aktivieren
Die folgenden Codebeispiele zeigen, wie Sie eine grundlegende Überprüfung der Abfrageparameter der Anfrage durchführen und analysierte Informationen an Cloud Logging senden. Diese Beispiele zeigen auch, wie URLs geparst werden.
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;
}
}