Code samples for plugins

This page provides code samples that address some common use cases for plugins.

For more Rust, Go, and C++ plugin samples, see the Service Extensions GitHub repository for plugins.

The plugins feature is in Preview for Media CDN.

Add HTTP request and response headers

The following code samples show how to add HTTP request headers.

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;
    }
}

The following code samples show how to add HTTP response headers.

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;
    }
}

Rewrite the request URL

The following code samples show how to rewrite the request URL by using regular expressions. The following code samples remove part of the path, but any URI mutation, such as path, query, or fragment, is feasible.

These samples also show best practices around regular expressions, namely using linear-time regular expression libraries and compiling the expressions at plugin initialization time.

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_;
};