Préparer le code du plug-in

Le code personnalisé que vous créez pour les plug-ins Service Extensions doit être empaqueté et importé dans Artifact Registry avant que d'autres services puissent y accéder. Cette page explique comment créer du code de plug-in, l'empaqueter et l'importer dans un dépôt Artifact Registry.

Cette fonctionnalité est disponible en version preview pour Media CDN.

Pour en savoir plus sur les extensions de service, consultez la présentation des extensions de service.

Avant de commencer, consultez les bonnes pratiques pour écrire le code du plug-in.

Pour en savoir plus, consultez Exemples de code pour les plug-ins.

Avant de commencer

  1. Connectez-vous à votre compte Google Cloud . Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $de crédits sans frais pour exécuter, tester et déployer des charges de travail.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Network Services, Network Actions, Artifact Registry, Cloud Build, Cloud Logging, and Cloud Monitoring APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  5. Installez la Google Cloud CLI.

  6. Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.

  7. Pour initialiser la gcloud CLI, exécutez la commande suivante :

    gcloud init
  8. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  9. Verify that billing is enabled for your Google Cloud project.

  10. Enable the Network Services, Network Actions, Artifact Registry, Cloud Build, Cloud Logging, and Cloud Monitoring APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  11. Installez la Google Cloud CLI.

  12. Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.

  13. Pour initialiser la gcloud CLI, exécutez la commande suivante :

    gcloud init

Configurer la chaîne d'outils

C++

Le SDK Proxy-Wasm C++ permet aux développeurs d'utiliser C++ pour implémenter des plug-ins WebAssembly (Wasm) pour les extensions de service. Le SDK utilise la chaîne d'outils WebAssembly C++ Emscripten, ainsi que d'autres bibliothèques, telles que protobuf et, éventuellement, Abseil.

Étant donné que la création de plug-ins écrits en C++ dépend de versions spécifiques de ces outils et bibliothèques, nous vous recommandons d'utiliser l'image Docker fournie par le SDK Proxy-Wasm C++. Les instructions pour C++ sur cette page utilisent la méthode Docker. Pour créer des plug-ins C++ sans utiliser Docker, consultez la documentation du SDK Proxy-Wasm C++.

  1. Installez Docker si ce n'est pas déjà fait. Docker est inclus dans Cloud Shell, l'environnement de shell interactif Google Cloud .

  2. Téléchargez une copie du SDK C++ Proxy-Wasm. La façon la plus simple de le faire est de cloner le dépôt Git :

    git clone https://github.com/proxy-wasm/proxy-wasm-cpp-sdk.git
    
  3. Créez l'image Docker du SDK C++ Proxy-Wasm à partir du fichier Dockerfile fourni par le SDK :

    cd proxy-wasm-cpp-sdk
    docker build -t wasmsdk:v3 -f Dockerfile-sdk .
    

    Une fois la commande terminée, les bibliothèques et les dépendances du SDK sont créées. L'image Docker résultante est associée au tag spécifié, qui est wasmsdk:v3 dans cet exemple.

Go

Le SDK Go Proxy-Wasm fournit un SDK Go complet. Go offre de bonnes performances et une prise en charge étendue des bibliothèques tierces écrites en Go pur.

Installez la chaîne d'outils Go v1.24.0.

Rust

La fonctionnalité de personnalisation des extensions de service est fournie par l'utilisation de WebAssembly et de Proxy-Wasm. WebAssembly est compatible avec plusieurs langages de programmation. Google recommande Rust, car il offre une excellente compatibilité avec WebAssembly et Proxy-Wasm fournit un SDK Rust complet. Rust offre également de bonnes performances et une sécurité de type élevée.

  1. Installez la chaîne d'outils Rust.

    À la fin du processus d'installation, suivez les instructions imprimées sur la console pour terminer la configuration.

  2. Ajoutez la prise en charge de Wasm à la chaîne d'outils Rust :

    rustup target add wasm32-wasip1
    

Créer le package de plug-in

C++

  1. Créez un répertoire distinct de proxy-wasm-cpp-sdk :

    mkdir myproject
    
  2. Dans le répertoire, créez un fichier Makefile avec le contenu suivant :

    # Express any dependencies
    PROTOBUF=     # full / lite / none
    WASM_DEPS=    # absl_base re2 ...
    
    # Include the SDK Makefile
    PROXY_WASM_CPP_SDK=/sdk
    include ${PROXY_WASM_CPP_SDK}/Makefile
    
  3. Ajoutez un fichier source C++ pour le plug-in dans le même répertoire. Les noms des fichiers sources C++ doivent correspondre aux fichiers Wasm ciblés par le fichier Makefile, avec le suffixe .wasm remplacé par .cc. Dans cet exemple, le fichier source doit être nommé myproject.cc.

  4. Ajoutez le code de votre plug-in au fichier source.

    L'exemple de code source suivant est un plug-in qui réécrit l'hôte de la requête et émet un en-tête de réponse :

    #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 {
        LOG_INFO("onRequestHeaders: hello from wasm");
    
        // Route Extension example: host rewrite
        if (replaceRequestHeader(":authority", "service-extensions.com") != WasmResult::Ok) {
          LOG_ERROR("Failed to replace :authority header");
        }
        if (replaceRequestHeader(":path", "/") != WasmResult::Ok) {
          LOG_ERROR("Failed to replace :path header");
        }
        return FilterHeadersStatus::Continue;
      }
    
      FilterHeadersStatus onResponseHeaders(uint32_t headers,
                                            bool end_of_stream) override {
        LOG_INFO("onResponseHeaders: hello from wasm");
    
        // Traffic Extension example: add response header
        if (addResponseHeader("hello", "service-extensions") != WasmResult::Ok) {
          LOG_ERROR("Failed to add response header");
        }
        return FilterHeadersStatus::Continue;
      }
    };
    
    static RegisterContextFactory register_StaticContext(
        CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(RootContext));

    La méthode onRequestHeaders est un rappel que les extensions de service invoquent.

Go

  1. Créez un répertoire pour le plug-in :

    mkdir go-plugin
    
  2. Dans le répertoire, créez un fichier go.mod à l'aide de l'outil Go :

    go mod init go-plugin
    
  3. Dans le même répertoire, créez un fichier source nommé main.go et ajoutez-y le code de votre plug-in.

    L'exemple de code source suivant est un plug-in qui réécrit l'hôte de la requête et émet un en-tête de réponse :

    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 (*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)
    		}
    	}()
    	proxywasm.LogInfof("onRequestHeaders: hello from wasm")
    
    	// Route Extension example: host rewrite
    	err := proxywasm.ReplaceHttpRequestHeader(":authority", "service-extensions.com")
    	if err != nil {
    		panic(err)
    	}
    	err = proxywasm.ReplaceHttpRequestHeader(":path", "/")
    	if err != nil {
    		panic(err)
    	}
    	return types.ActionContinue
    }
    
    func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
    	defer func() {
    		err := recover()
    		if err != nil {
    			proxywasm.SendHttpResponse(500, [][2]string{}, []byte(fmt.Sprintf("%v", err)), 0)
    		}
    	}()
    	proxywasm.LogInfof("onResponseHeaders: hello from wasm")
    
    	// Traffic Extension example: add response header
    	err := proxywasm.AddHttpResponseHeader("hello", "service-extensions")
    	if err != nil {
    		panic(err)
    	}
    	return types.ActionContinue
    }
    

    La méthode OnHttpRequestHeaders est un rappel que les extensions de service invoquent.

  4. Pour télécharger et épingler les dépendances de bibliothèque, exécutez go mod tidy :

    go mod tidy
    

Rust

  1. Créez un répertoire de packages Rust à l'aide de la commande cargo new du gestionnaire de packages Rust, Cargo :

    cargo new --lib my-wasm-plugin
    

    La commande crée un répertoire contenant un fichier cargo.toml que vous pouvez mettre à jour pour décrire comment compiler le package Rust, ainsi qu'un répertoire src dans lequel stocker le code du plug-in.

  2. Mettez à jour le fichier cargo.toml pour spécifier les paramètres requis pour créer le package :

    [package]
    name = "my-wasm-plugin"
    version = "0.1.0"
    edition = "2021"
    
  3. Pour enregistrer le SDK Proxy-Wasm Rust et la prise en charge de la journalisation en tant que dépendances, ajoutez la section dependencies. Exemple :

    [dependencies]
    proxy-wasm = "0.2"
    log = "0.4"
    
  4. Pour créer une bibliothèque dynamique comme requis pour les plug-ins, ajoutez la section lib. Exemple :

    [lib]
    crate-type = ["cdylib"]
    
  5. Pour réduire la taille du plug-in compilé, ajoutez la section profile.release. Exemple :

    [profile.release]
    lto = true
    opt-level = 3
    codegen-units = 1
    panic = "abort"
    strip = "debuginfo"
    
  6. Ajoutez le code de votre plug-in au fichier lib.rs dans le répertoire src.

    L'exemple de code source suivant est un plug-in qui réécrit l'hôte de la requête et émet un en-tête de réponse :

    use log::info;
    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 {
            info!("onRequestHeaders: hello from wasm");
    
            // Route extension example: host rewrite
            self.set_http_request_header(":authority", Some("service-extensions.com"));
            self.set_http_request_header(":path", Some("/"));
            return Action::Continue;
        }
    
        fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
            info!("onResponseHeaders: hello from wasm");
    
            // Traffic extension example: add response header
            self.add_http_response_header("hello", "service-extensions");
            return Action::Continue;
        }
    }

    La méthode on_http_request_headers est un rappel que les extensions de service invoquent.

Compiler le plug-in

C++

Pour compiler le plug-in, exécutez la commande suivante dans le répertoire où se trouvent le fichier Makefile et les fichiers sources du plug-in C++ :

docker run -v $PWD:/work -w /work wasmsdk:v3 /build_wasm.sh myproject.wasm

Cette commande mappe le répertoire actuel au répertoire work dans l'image Docker, puis exécute le script build_wasm.sh fourni par l'image Docker pour compiler le code du plug-in. Une fois l'opération de compilation terminée, un fichier myproject.wasm contenant le bytecode Wasm compilé est créé dans le répertoire actuel.

La première fois que le code du plug-in est compilé à l'aide de l'image Docker, Emscripten génère les bibliothèques standards. Pour mettre ces éléments en cache dans l'image Docker afin qu'ils n'aient pas besoin d'être régénérés à chaque fois, validez l'image avec les bibliothèques standards après la première compilation réussie :

docker commit `docker ps -l | grep wasmsdk:v3 | awk '{print $1}'` wasmsdk:v3

Pour en savoir plus sur la création de plug-ins C++, consultez la documentation du SDK Proxy-Wasm C++.

Go

Pour compiler le code du plug-in, exécutez la commande go build :

env GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm main.go

Une compilation réussie crée un fichier main.wasm dans le répertoire actuel.

Rust

Pour compiler le code du plug-in, exécutez la commande cargo build :

cargo build --release --target wasm32-wasip1

Une fois la compilation terminée, un message Finished release [optimized] target(s) s'affiche. Si vous connaissez Envoy, vous pouvez charger le plug-in dans Envoy et vérifier son comportement.

Importer le code du plug-in compilé dans Artifact Registry

Importez le code du plug-in compilé dans un dépôt Artifact Registry afin que les services Google Cloud puissent y accéder.

Si vous utilisez des extensions de service avec un équilibreur de charge global, utilisez un dépôt dans un emplacement us multirégional. Si vous utilisez un équilibreur de charge régional, utilisez un dépôt dans la même région ou dans un emplacement multirégional sur le même continent.

Lorsque vous associez un plug-in à un équilibreur de charge mondial, Service Extensions copie le module Wasm du dépôt Artifact Registry vers son propre espace de stockage dans des emplacements du monde entier. L'emplacement du dépôt n'a donc aucune incidence sur la latence des requêtes envoyées à l'équilibreur de charge. Pour les équilibreurs de charge régionaux, le plug-in est copié dans les emplacements de la même région que celle où se trouve l'équilibreur de charge.

Les plug-ins Service Extensions peuvent être importés dans les dépôts Docker ou génériques Artifact Registry.

Pour une approche plus directe de la gestion des binaires autonomes, nous vous recommandons d'utiliser un dépôt générique pour stocker vos fichiers Wasm. Cette option est disponible en version bêta.

Dépôt générique

  1. Créez un répertoire package/ local et copiez-y votre module de plug-in publiable. L'artefact du plug-in doit être nommé plugin.wasm. L'exemple de commande suivant copie l'artefact du plug-in créé par Rust :

    mkdir -p package && cp -f target/wasm32-wasip1/release/my_wasm_plugin.wasm package/plugin.wasm
    
  2. Créez un dépôt Artifact Registry avec --repository-format défini sur generic.

  3. Vérifiez que vous disposez des autorisations requises pour le dépôt.

  4. Pour importer votre module Wasm en tant qu'artefact générique dans votre dépôt, utilisez la commande gcloud artifacts generic upload.

    gcloud artifacts generic upload \
        --project=PROJECT_ID \
        --location=LOCATION \
        --repository=REPOSITORY \
        --source=package/plugin.wasm \
        --package=PACKAGE \
        --version=VERSION
    

    Remplacez les éléments suivants :

    Une fois l'importation terminée, notez le nom de l'artefact générique mentionné avec le libellé name, par exemple add_header_plugin:v4 dans l'exemple suivant. Spécifiez ce nom lorsque vous créez un plug-in ou une version de plug-in.

    Uploading file: plugin.wasm...done.
    '@type': type.googleapis.com/google.devtools.artifactregistry.v1.GenericArtifact
    createTime: '2025-06-16T11:02:25.080248Z'
    name: projects/my-project/locations/us/repositories/my-generic-repo/genericArtifacts/add_header_plugin:v4
    updateTime: '2025-06-16T11:02:25.080248Z'
    version: v4
    
  5. Pour vérifier que l'artefact a bien été importé dans Artifact Registry, exécutez la commande gcloud artifacts files list et vérifiez que la liste contient un fichier nommé PACKAGE:VERSION:plugin.wasm.

     gcloud artifacts files list \
         --project=PROJECT_ID \
         --location=LOCATION \
         --repository=REPOSITORY
    

Dépôt Docker

  1. Créez un dépôt Artifact Registry avec --repository-format défini sur docker.

  2. Vérifiez que vous disposez des autorisations requises pour le dépôt.

  3. Créez un répertoire package/ local et copiez-y l'artefact de votre plug-in publiable. L'exemple suivant copie l'artefact de plug-in créé par Rust :

    mkdir -p package && cp -f target/wasm32-wasip1/release/my_wasm_plugin.wasm package/plugin.wasm
    
  4. Créez un fichier package/Dockerfile avec le contenu suivant :

    FROM scratch
    COPY plugin.wasm plugin.wasm
    
  5. Empaquetez le code du plug-in à l'aide de Cloud Build ou de Docker.

    Cloud Build

    1. Créez un fichier de configuration de compilation package/cloudbuild.yaml avec le contenu suivant :

      steps:
        - name: 'gcr.io/cloud-builders/docker'
          args: [ 'build', '--no-cache', '--platform', 'wasm',
                '-t', 'LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG', '.' ]
      images: [ 'LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG' ]
      

      Le chemin d'accès où vous stockez le plug-in doit respecter le format suivant :

      LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG
      

      Remplacez les éléments suivants :

      • LOCATION : l'emplacement régional ou multirégional du dépôt
      • PROJECT_ID : ID de votre projet dans la console Google Cloud
      • REPOSITORY : nom du dépôt dans lequel vous souhaitez stocker l'image
      • IMAGE : nom de l'image de conteneur dans le dépôt, par exemple us-docker.pkg.dev/my-project/my-repo/my-wasm-plugin
      • IMAGE_TAG : tag d'image à attribuer au conteneur, par exemple production
    2. Déclenchez l'opération pour créer le conteneur de plug-in et l'importer dans Artifact Registry :

      gcloud builds submit --config package/cloudbuild.yaml package/
      

    Docker

    1. Installez Docker si ce n'est pas déjà fait. Docker est inclus dans Cloud Shell, l'environnement de shell interactif Google Cloud .

    2. Configurez Docker pour qu'il s'authentifie auprès d'Artifact Registry. Exemple :

      gcloud auth login
      gcloud auth configure-docker LOCATION-docker.pkg.dev
      gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://LOCATION-docker.pkg.dev
      
    3. Créez l'image du conteneur :

      docker build --no-cache --platform wasm -t my-wasm-plugin package/
      
    4. Pour ajouter un tag à l'image locale avec le nom de l'image du dépôt, utilisez les tags d'image :

      docker tag my-wasm-plugin LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG
      

      Remplacez les éléments suivants :

      • LOCATION : emplacement régional ou multirégional du dépôt
      • PROJECT_ID : ID de votre projet dans la console Google Cloud
      • REPOSITORY : nom du dépôt dans lequel vous prévoyez de stocker l'image.
      • IMAGE : nom de l'image de conteneur dans le dépôt, par exemple us-docker.pkg.dev/my-project/my-repo/my-wasm-plugin.
      • IMAGE_TAG : tag d'image à attribuer au conteneur, par exemple production
    5. Importez votre image de conteneur taguée dans Artifact Registry.

      docker push LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG
      
  6. Pour vérifier que l'image a bien été importée dans Artifact Registry, exécutez la commande gcloud artifacts docker images list.

    gcloud artifacts docker images list LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE \
        --include-tags
    

Préparer et importer le fichier de configuration

Les plug-ins peuvent éventuellement recevoir des données de configuration, ce qui peut affecter leur comportement au moment de l'exécution. Les données de configuration peuvent être au format texte ou binaire, et dans n'importe quel format accepté par le plug-in. Si la taille des données de configuration est importante, vous devrez peut-être importer le fichier de configuration dans Artifact Registry.

Écrire le code du plug-in pour lire les données de configuration

C++

Les données de configuration sont transmises à la méthode onConfigure de l'objet de contexte racine, qui est instancié une fois au démarrage du plug-in et reste actif pendant toute la durée de vie du runtime Wasm qui héberge le plug-in. L'objet de contexte racine est une instance de la classe RootContext ou d'une sous-classe de RootContext.

Le code du plug-in peut contrôler la classe utilisée pour le contexte racine via la valeur RegisterContextFactory. Par exemple, le code de plug-in suivant enregistre MyRootContext et MyHttpContext comme classes à utiliser pour les instances de contexte racine et de flux. Il lit ensuite une valeur secrète à partir des données de configuration du plug-in, auxquelles les instances de contexte de flux peuvent accéder via l'objet de contexte racine.

#include <string>
#include <string_view>

#include "proxy_wasm_intrinsics.h"

class MyRootContext : public RootContext {
 public:
  explicit MyRootContext(uint32_t id, std::string_view root_id)
      : RootContext(id, root_id) {}

  bool onConfigure(size_t config_len) override {
    auto buffer = getBufferBytes(WasmBufferType::PluginConfiguration, 0, config_len);
    if (!buffer) {
      LOG_ERROR("Failed to retrieve plugin configuration");
      return false;
    }
    secret_ = buffer->toString();
    return true;
  }

  const std::string& secret() const { return secret_; }

 private:
  std::string secret_;
};

class MyHttpContext : public Context {
 public:
  explicit MyHttpContext(uint32_t id, RootContext* root)
      : Context(id, root),
        secret_(static_cast<MyRootContext*>(root)->secret()) {}

  FilterHeadersStatus onRequestHeaders(uint32_t headers,
                                       bool end_of_stream) override {
    // Use secret here...
    LOG_INFO("secret: " + secret_);
    return FilterHeadersStatus::Continue;
  }

 private:
  const std::string& secret_;
};

static RegisterContextFactory register_MyHttpContext(
    CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(MyRootContext));

Go

Les données de configuration sont lues lors de l'exécution de OnPluginStart, un récepteur de méthode que le runtime exécute sur la structure PluginContext une fois au démarrage du plug-in. Les données de configuration ne changent pas pendant la durée de vie du runtime Wasm qui héberge le plug-in. PluginContext est utile pour effectuer une logique d'initialisation et maintenir l'état sur plusieurs requêtes.

Le code du plug-in peut contrôler le contexte utilisé pour PluginContext en implémentant la méthode NewPluginContext sur VMContext, qui instancie PluginContext. PluginContext instancie ensuite les contextes HttpContext.

Par exemple, le code de plug-in suivant enregistre VMContext, qui instancie PluginContext, le contexte responsable de la lecture des données de configuration et de l'instanciation des contextes HttpContext selon les besoins. PluginContext lit une valeur secrète à partir des données de configuration du plug-in, la stocke dans le contexte PluginContext et la transmet à HttpContext dans NewHttpContext.

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
	secret string
}

type httpContext struct {
	types.DefaultHttpContext
	pluginContext *pluginContext
}

func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
	return &pluginContext{}
}

func (ctx *pluginContext) OnPluginStart(int) types.OnPluginStartStatus {
	config, err := proxywasm.GetPluginConfiguration()
	if err != nil {
		proxywasm.LogErrorf("Error reading the configuration: %v", err)
		return types.OnPluginStartStatusFailed
	}
	if config == nil {
		proxywasm.LogError("Configuration is nil")
		return types.OnPluginStartStatusFailed
	}
	ctx.secret = string(config)
	return types.OnPluginStartStatusOK
}

func (ctx *pluginContext) NewHttpContext(uint32) types.HttpContext {
	return &httpContext{pluginContext: ctx}
}

func (ctx *pluginContext) Secret() string {
	return ctx.secret
}

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)
		}
	}()
	// Use secret here...
	proxywasm.LogInfof("secret: %v", ctx.pluginContext.Secret())
	return types.ActionContinue
}

Rust

Les données de configuration sont lues à partir d'un trait RootContext, qui est instancié une fois au démarrage du plug-in et reste actif pendant toute la durée de vie de l'exécution Wasm qui héberge le plug-in. Les traits RootContext sont utiles pour effectuer des opérations ou maintenir un état sur plusieurs requêtes.

Le code du plug-in peut contrôler la classe utilisée pour le contexte racine via la méthode set_root_context, et le contexte racine instancie les contextes de flux. Par exemple, le code de plug-in suivant enregistre MyRootContext, qui instancie MyHttpContext selon les besoins. Le contexte racine lit une valeur secrète à partir des données de configuration du plug-in et la transmet aux contextes de flux.

use log::info;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
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 { secret: Rc::new("missing".to_string()) })
    });
} }

struct MyRootContext {
    secret: Rc<String>,
}

impl Context for MyRootContext {}

impl RootContext for MyRootContext {
    fn on_configure(&mut self, _: usize) -> bool {
        if let Some(config_bytes) = self.get_plugin_configuration() {
            self.secret = Rc::new(String::from_utf8(config_bytes).unwrap())
        }
        true
    }

    fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
        Some(Box::new(MyHttpContext {
            secret: self.secret.clone(), // shallow copy, ref count only
        }))
    }

    fn get_type(&self) -> Option<ContextType> {
        Some(ContextType::HttpContext)
    }
}

struct MyHttpContext {
    secret: Rc<String>,
}

impl Context for MyHttpContext {}

impl HttpContext for MyHttpContext {
    fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
        // Use secret here...
        info!("secret: {}", self.secret);
        Action::Continue
    }
}

Importer le fichier de configuration

Si la taille des données à fournir à votre plug-in dans on_configure dépasse 900 Kio, importez-les dans Artifact Registry en suivant la méthode décrite dans Importer le code de plug-in compilé dans Artifact Registry. Dans ce cas, enregistrez le fichier de configuration sous le nom plugin.config (et non plugin.wasm).

L'étape suivante consiste à créer un plug-in.

Lorsque vous créez le plug-in, vous devez fournir l'URI du module ou de l'image Wasm importés.

Créer une version du code du plug-in

Pour créer une version du code du plug-in, modifiez le fichier du plug-in. Ensuite, comme décrit dans les sections précédentes, compilez le code du plug-in, reconditionnez-le et importez-le dans Artifact Registry.

Rappels

Le code que vous compilez en Wasm peut définir des méthodes ou des fonctions arbitraires, mais certaines d'entre elles ont une signification particulière. Il s'agit des méthodes définies dans le SDK Proxy-Wasm pour le langage de votre choix et qui correspondent aux spécifications de l'interface binaire d'application (ABI) Proxy-Wasm. Les extensions de service appellent ces rappels en réponse aux requêtes des utilisateurs ou aux événements du cycle de vie des plug-ins.

Ces rappels sont listés dans le tableau suivant, dans l'ordre dans lequel ils sont généralement appelés :

Nom et description du rappel Nom de la méthode C++ Nom de la méthode Go Nom de la méthode Rust
START_PLUGIN : appelé lorsqu'un plug-in est démarré. RootContext::onStart VMContext.OnVmStart RootContext::on_vm_start
CONFIGURE_PLUGIN : invoqué après le démarrage d'un plug-in, pour fournir des données de configuration au plug-in. RootContext::onConfigure PluginContext.OnPluginStart RootContext::on_configure
CREATE_CONTEXT : appelé lorsqu'un nouveau contexte de flux est créé. Chaque flux correspond à une requête HTTP du client. Context::onCreate PluginContext.NewHttpContext RootContext::create_http_context
HTTP_REQUEST_HEADERS : invoqué pour traiter les en-têtes de requête HTTP. Context::onRequestHeaders HttpContext.OnHttpRequestHeaders HttpContext::on_http_request_headers
HTTP_REQUEST_BODY : appelé à plusieurs reprises pour traiter les blocs du corps de la requête HTTP. Context::onRequestBody HttpContext.OnHttpRequestBody HttpContext::on_http_request_body
HTTP_RESPONSE_HEADERS : invoqué pour traiter les en-têtes de réponse HTTP. Context::onResponseHeaders HttpContext.OnHttpResponseHeaders HttpContext::on_http_response_headers
HTTP_RESPONSE_BODY : appelé à plusieurs reprises pour traiter les blocs du corps de la réponse HTTP. Context::onResponseBody HttpContext.OnHttpResponseBody HttpContext::on_http_response_body
DONE : appelé lorsque le traitement d'un plug-in est terminé. Context::onDone HttpContext.OnHttpStreamDone Context::on_done
DELETE : invoqué lorsque l'objet de contexte de flux correspondant à une requête HTTP client est supprimé. Context::onDelete (pas de rappel) (pas de rappel)

Étapes suivantes