Auf dieser Seite finden Sie Codebeispiele für einige gängige Anwendungsfälle für Plug-ins.
Weitere Beispiele für Rust-, Go- und C++-Plug-ins finden Sie im GitHub-Repository für Service Extensions-Plug-ins.
Die Funktion „Plug-ins“ befindet sich für Media CDN im Vorschaumodus.
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 von regulären Ausdrücken neu schreiben. In den folgenden Codebeispielen wird ein Teil des Pfads entfernt. Jede URI-Mutation, z. B. Pfad, Abfrage oder Fragment, ist jedoch möglich.
Diese Beispiele zeigen auch Best Practices für reguläre Ausdrücke, nämlich die Verwendung von Bibliotheken für reguläre Ausdrücke in linearer Zeit und das Kompilieren der Ausdrücke bei der Initialisierung des Plug-ins.
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;
}
}Logging für benutzerdefinierte Variablen aktivieren
Die folgenden Codebeispiele zeigen, wie Sie eine einfache Prüfung der Parameter des Anfrage-Query-Strings durchführen und geparste Informationen an Cloud Logging senden. In diesen Beispielen wird auch gezeigt, 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;
}
}