הכנת קוד הפלאגין

צריך לארוז את הקוד המותאם אישית שיוצרים עבור תוספים של Service Extensions ולהעלות אותו ל-Artifact Registry לפני ששירותים אחרים יוכלו לגשת אליו. בדף הזה מוסבר איך ליצור קוד של פלאגין, לארוז את הקוד ולהעלות אותו למאגר של Artifact Registry.

התכונה הזו נמצאת בגרסת טרום-השקה ב-Media CDN.

למידע נוסף על תוספי שירות, קראו את הסקירה הכללית על תוספי שירות.

לפני שמתחילים, מומלץ לעיין בשיטות המומלצות לכתיבת קוד של תוסף.

דוגמאות נוספות מופיעות במאמר דוגמאות קוד לתוספים.

לפני שמתחילים

  1. 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

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

  3. Enable the Network Services, Network Actions, Artifact Registry, Cloud Build, Cloud Logging, and Cloud Monitoring APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  4. Install the Google Cloud CLI.

  5. Configure the gcloud CLI to use your federated identity.

    For more information, see Sign in to the gcloud CLI with your federated identity.

  6. To initialize the gcloud CLI, run the following command:

    gcloud init

הגדרת שרשרת הכלים

C++‎

‫Proxy-Wasm C++ SDK מאפשר למפתחים להשתמש ב-C++‎ כדי להטמיע תוספים של WebAssembly ‏ (Wasm) לתוספי שירות. ה-SDK משתמש בערכת הכלים של C++‎ WebAssembly‏ Emscripten, וגם בספריות אחרות, כמו protobuf, ובאופן אופציונלי, Abseil.

בגלל שנדרשות גרסאות ספציפיות של הכלים והספריות האלה כדי ליצור פלאגינים שנכתבו ב-C++, אנחנו ממליצים להשתמש בקובץ האימג' של Docker שסופק על ידי Proxy-Wasm C++ SDK. ההוראות ל-C++‎ בדף הזה משתמשות בשיטת Docker. כדי ליצור תוספים ל-C++ בלי להשתמש ב-Docker, אפשר לעיין במסמכי ה-SDK של Proxy-Wasm C++.

  1. מתקינים את Docker אם הוא עדיין לא מותקן. ‫Docker כלול ב-Cloud Shell, סביבת המעטפת האינטראקטיבית של Google Cloud .

  2. מורידים עותק של Proxy-Wasm C++ SDK. הדרך הכי פשוטה לעשות את זה היא לשכפל את מאגר Git:

    git clone https://github.com/proxy-wasm/proxy-wasm-cpp-sdk.git
    
  3. יוצרים את קובץ האימג' של Docker ל-Proxy-Wasm C++ SDK מקובץ ה-Docker שסופק על ידי ה-SDK:

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

    אחרי שהפקודה מסיימת ליצור את ספריות ה-SDK ואת התלות שלהן, קובץ האימג' של Docker שנוצר משויך לתג שצוין, שהוא wasmsdk:v3 בדוגמה הזו.

המשך

‫Proxy-Wasm Go SDK מספק ערכת SDK מלאה של Go. היא מספקת ביצועים טובים ותמיכה עשירה בספריות של צד שלישי שנכתבו ב-Go בלבד.

מתקינים את ערכת הכלים של Go בגרסה 1.24.0.

חלודה

יכולת ההתאמה האישית של Service Extensions מתבצעת באמצעות WebAssembly ו-Proxy-Wasm. ‫WebAssembly תומך במספר שפות תכנות. ‫Google ממליצה על Rust כי היא מספקת תמיכה מצוינת ב-WebAssembly, ו-Proxy-Wasm מספקת ערכת SDK מלאה של Rust. בנוסף, Rust מספקת ביצועים טובים ובטיחות חזקה של טיפוסים.

  1. מתקינים את Rust toolchain.

    בסיום תהליך ההתקנה, פועלים לפי ההוראות שמוצגות במסוף כדי לסיים את תהליך ההגדרה.

  2. הוספת תמיכה ב-Wasm לשרשרת הכלים של Rust:

    rustup target add wasm32-wasip1
    

יצירת חבילת הפלאגין

C++‎

  1. יוצרים ספרייה חדשה, נפרדת מ-proxy-wasm-cpp-sdk:

    mkdir myproject
    
  2. בספרייה, יוצרים קובץ Makefile עם התוכן הבא:

    # 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. מוסיפים קובץ מקור C++ לתוסף באותה תיקייה. השמות של קובצי המקור ב-C++‎ צריכים להיות זהים לקובצי ה-Wasm שה-Makefile מטרגט, עם הסיומת .cc במקום .wasm. בדוגמה הזו, שם קובץ המקור צריך להיות myproject.cc.

  4. מוסיפים את קוד הפלאגין לקובץ המקור.

    דוגמת קוד המקור הבאה היא פלאגין שכותב מחדש את מארח הבקשה ושולח כותרת תגובה:

    #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));

    השיטה onRequestHeaders היא קריאה חוזרת (callback) שמופעלת על ידי Service Extensions.

המשך

  1. יוצרים ספרייה חדשה לתוסף:

    mkdir go-plugin
    
  2. בספרייה, יוצרים קובץ go.mod באמצעות הכלי Go:

    go mod init go-plugin
    
  3. באותה ספרייה, יוצרים קובץ מקור בשם main.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 (*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
    }
    

    השיטה OnHttpRequestHeaders היא קריאה חוזרת (callback) שמופעלת על ידי Service Extensions.

  4. כדי להוריד ולצמיד תלות של הפרויקט בספריות, מריצים את הפקודה go mod tidy:

    go mod tidy
    

חלודה

  1. יוצרים ספריית חבילות של Rust באמצעות הפקודה cargo new מכלי ניהול החבילות של Rust, ‏Cargo:

    cargo new --lib my-wasm-plugin
    

    הפקודה יוצרת ספרייה שמכילה קובץ cargo.toml שאפשר לעדכן כדי לתאר איך ליצור את חבילת Rust, וספריית src שבה מאחסנים את קוד הפלאגין.

  2. מעדכנים את הקובץ cargo.toml כדי לציין את הפרמטרים שנדרשים ליצירת החבילה:

    [package]
    name = "my-wasm-plugin"
    version = "0.1.0"
    edition = "2021"
    
  3. כדי לרשום את Proxy-Wasm Rust SDK ואת התמיכה ברישום ביומן כיחסי תלות, מוסיפים את הקטע dependencies. לדוגמה:

    [dependencies]
    proxy-wasm = "0.2"
    log = "0.4"
    
  4. כדי ליצור ספרייה דינמית כמו שנדרש לפלאגינים, מוסיפים את הקטע lib. לדוגמה:

    [lib]
    crate-type = ["cdylib"]
    
  5. כדי להקטין את הגודל של הפלאגין המקומפל, מוסיפים את הקטע profile.release. לדוגמה:

    [profile.release]
    lto = true
    opt-level = 3
    codegen-units = 1
    panic = "abort"
    strip = "debuginfo"
    
  6. מוסיפים את קוד הפלאגין לקובץ lib.rs בספרייה src.

    דוגמת קוד המקור הבאה היא פלאגין שכותב מחדש את מארח הבקשה ושולח כותרת תגובה:

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

    ה-method‏ on_http_request_headers היא קריאה חוזרת (callback) שמופעלת על ידי Service Extensions.

הידור הפלאגין

C++‎

כדי לקמפל את הפלאגין, מריצים את הפקודה הבאה מתוך הספרייה שבה נמצאים קובץ ה-Makefile וקבצי המקור של הפלאגין ב-C++‎:

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

הפקודה הזו ממפה את הספרייה הנוכחית לספרייה work בקובץ האימג' של Docker, ואז מריצה את הסקריפט build_wasm.sh שסופק על ידי קובץ האימג' של Docker כדי ליצור את קוד הפלאגין. בסיום פעולת ההידור, אם היא הושלמה בהצלחה, נוצר קובץ myproject.wasm שמכיל את קוד הבייט של Wasm שעבר הידור, בספרייה הנוכחית.

בפעם הראשונה שקוד הפלאגין עובר קומפילציה באמצעות קובץ אימג' של Docker,‏ Emscripten יוצר את הספריות הרגילות. כדי לשמור אותן במטמון בתמונת Docker, כך שלא יהיה צורך ליצור אותן מחדש בכל פעם, צריך לבצע commit לתמונה עם הספריות הרגילות אחרי ההידור הראשון שבוצע בהצלחה:

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

מידע נוסף על יצירת תוספים ל-C++ זמין במאמרי העזרה בנושא Proxy-Wasm C++ SDK.

המשך

כדי לקמפל את קוד הפלאגין, מריצים את הפקודה go build:

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

אם ההידור יצליח, ייווצר קובץ main.wasm בספרייה הנוכחית.

Rust

כדי להדר את קוד הפלאגין, מריצים את הפקודה cargo build:

cargo build --release --target wasm32-wasip1

בסיום ה-build יופיע Finished release [optimized] target(s) אישור. אם אתם מכירים את Envoy, תוכלו לטעון את הפלאגין ב-Envoy ולאמת את ההתנהגות שלו.

העלאת קוד הפלאגין שעבר קומפילציה אל Artifact Registry

מעלים את קוד הפלאגין המקומפל למאגר Artifact Registry כדי ששירותי Google Cloud יוכלו לגשת אליו.

אם אתם משתמשים בתוספים לשירות עם מאזן עומסים גלובלי, השתמשו במאגר במיקום בכמה אזורים של us. אם אתם משתמשים במאזן עומסים אזורי, אתם צריכים להשתמש במאגר באותו אזור או במיקום רב-אזורי באותה יבשת.

כשמצרפים פלאגין למאזן עומסים גלובלי, Service Extensions מעתיק את מודול ה-Wasm ממאגר Artifact Registry לאחסון משלו במיקומים ברחבי העולם. לכן, המיקום של המאגר לא משפיע על זמן האחזור של הבקשות שנשלחות למאזן העומסים. במאזני עומסים אזוריים, הפלאגין מועתק למיקומים באותו אזור שבו נמצא מאזן העומסים.

אפשר להעלות תוספים של Service Extensions למאגרי Docker או למאגרים כלליים ב-Artifact Registry.

כדי לנהל קבצים בינאריים עצמאיים בצורה ישירה יותר, מומלץ להשתמש במאגר כללי לאחסון קובצי Wasm. האפשרות הזו נמצאת בגרסת טרום-השקה.

מאגר גנרי

  1. יוצרים ספרייה מקומית בשם package/ ומעתיקים אליה את מודול הפלאגין שאפשר לפרסם. שם הארטיפקט של הפלאגין צריך להיות plugin.wasm הפקודה לדוגמה הבאה מעתיקה את הארטיפקט של הפלאגין שנבנה על ידי Rust:

    mkdir -p package && cp -f target/wasm32-wasip1/release/my_wasm_plugin.wasm package/plugin.wasm
    
  2. יוצרים מאגר Artifact Registry עם --repository-format שמוגדר ל-generic.

  3. מוודאים שיש לכם את ההרשאות הנדרשות למאגר.

  4. כדי להעלות את מודול ה-Wasm שלכם כארטיפקט גנרי למאגר, משתמשים בפקודה gcloud artifacts generic upload.

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

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫PROJECT_ID: Google Cloud מזהה הפרויקט
    • LOCATION: המיקום של המאגר, אזורי או במספר אזורים
    • ‫REPOSITORY: השם של המאגר שאליו רוצים להעלות את מודול ה-Wasm
    • ‫PACKAGE: שם החבילה של הקובץ
    • ‫VERSION: הגרסה של מודול ה-Wasm

    בסיום ההעלאה, שימו לב לשם של הפריט הגנרי שמופיע עם התווית name, לדוגמה add_header_plugin:v4 בדוגמה הבאה. מציינים את השם הזה כשיוצרים פלאגין או גרסת פלאגין.

    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. כדי לוודא שהארטיפקט הועלה ל-Artifact Registry, מריצים את הפקודה gcloud artifacts files list ומוודאים שברשימה מופיע קובץ בשם PACKAGE:VERSION:plugin.wasm.

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

מאגר Docker

  1. יוצרים מאגר ב-Artifact Registry עם --repository-format שמוגדר ל-docker.

  2. מוודאים שיש לכם את ההרשאות הנדרשות למאגר.

  3. יוצרים ספרייה מקומית בשם package/ ומעתיקים אליה את ארטיפקט הפלאגין שניתן לפרסום. הדוגמה הבאה מעתיקה את פריט המידע שנוצר בתהליך פיתוח (Artifact) של הפלאגין שנבנה על ידי Rust:

    mkdir -p package && cp -f target/wasm32-wasip1/release/my_wasm_plugin.wasm package/plugin.wasm
    
  4. יוצרים קובץ package/Dockerfile עם התוכן הבא:

    FROM scratch
    COPY plugin.wasm plugin.wasm
    
  5. אורזים את קוד הפלאגין באמצעות Cloud Build או Docker.

    Cloud Build

    1. יוצרים קובץ package/cloudbuild.yaml build config עם התוכן הבא:

      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' ]
      

      הנתיב שבו מאחסנים את הפלאגין צריך להיות בפורמט הבא:

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

      מחליפים את מה שכתוב בשדות הבאים:

      • ‫LOCATION: המיקום האזורי או הרב-אזורי של המאגר
      • ‫PROJECT_ID: מזהה הפרויקט במסוף Google Cloud
      • ‫REPOSITORY: השם של המאגר שבו אתם רוצים לאחסן את התמונה
      • ‫IMAGE: שם קובץ האימג' של הקונטיינר במאגר, לדוגמה us-docker.pkg.dev/my-project/my-repo/my-wasm-plugin
      • ‫IMAGE_TAG: תג התמונה שיוקצה לקונטיינר, לדוגמה production
    2. מפעילים את הפעולה כדי ליצור את מאגר הפלאגין ולהעלות אותו ל-Artifact Registry:

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

    Docker

    1. מתקינים את Docker אם הוא עדיין לא מותקן. ‫Docker כלול ב-Cloud Shell, סביבת המעטפת האינטראקטיבית של Google Cloud .

    2. הגדרת אימות של Docker ל-Artifact Registry. לדוגמה:

      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. יוצרים את קובץ האימג' של הקונטיינר:

      docker build --no-cache --platform wasm -t my-wasm-plugin package/
      
    4. כדי לתייג את התמונה המקומית בשם התמונה במאגר, משתמשים בתגי תמונה:

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

      מחליפים את מה שכתוב בשדות הבאים:

      • ‫LOCATION: המיקום של המאגר, באזור אחד או בכמה אזורים
      • ‫PROJECT_ID: מזהה הפרויקט ב Google Cloud מסוף
      • ‫REPOSITORY: השם של המאגר שבו אתם רוצים לאחסן את התמונה
      • ‫IMAGE: שם קובץ האימג' של הקונטיינר במאגר, לדוגמה us-docker.pkg.dev/my-project/my-repo/my-wasm-plugin.
      • ‫IMAGE_TAG: תג האימג' להקצאה לקונטיינר, לדוגמה production
    5. מעלים את קובץ אימג' של קונטיינר המתויג אל Artifact Registry.

      docker push LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:IMAGE_TAG
      
  6. כדי לוודא שהתמונה הועלתה בהצלחה ל-Artifact Registry, מריצים את הפקודה gcloud artifacts docker images list.

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

הכנה והעלאה של קובץ התצורה

תוספים יכולים לקבל נתוני הגדרה, שיכולים להשפיע על ההתנהגות של התוסף בזמן הריצה. נתוני ההגדרה יכולים להיות טקסט או בינאריים, ובכל פורמט שהתוסף מקבל. אם גודל נתוני התצורה גדול, יכול להיות שתצטרכו להעלות את קובץ התצורה ל-Artifact Registry.

כתיבת קוד של תוסף לקריאת נתוני ההגדרה

C++‎

נתוני ההגדרה מועברים לשיטה onConfigure של אובייקט ההקשר הבסיסי, שנוצר פעם אחת בהפעלת הפלאגין ונשאר פעיל למשך משך החיים של זמן הריצה של Wasm שמארח את הפלאגין. אובייקט ההקשר הבסיסי הוא מופע של המחלקה RootContext או של מחלקת משנה של RootContext.

קוד הפלאגין יכול לקבוע באיזו מחלקה להשתמש בהקשר הבסיסי באמצעות הערך RegisterContextFactory. לדוגמה, קוד הפלאגין הבא רושם את MyRootContext ואת MyHttpContext כסוגי המחלקות שבהן יש להשתמש עבור מופעים של הקשרים root ו-stream. לאחר מכן, התוסף קורא ערך סודי מנתוני ההגדרה של התוסף, שמופעים של הקשר של הזרם יכולים לגשת אליהם דרך אובייקט ההקשר הבסיסי.

#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));

המשך

נתוני ההגדרות נקראים במהלך ההפעלה של OnPluginStart, מקבל שיטה שהסביבה בזמן הריצה מפעילה במבנה PluginContext פעם אחת בהפעלת הפלאגין. נתוני ההגדרה לא משתנים במהלך מחזור החיים של זמן הריצה של Wasm שמארח את הפלאגין. הפונקציה PluginContext שימושית להפעלת לוגיקת אתחול ולשמירה על המצב בהרבה בקשות.

קוד התוסף יכול לקבוע באיזה הקשר נעשה שימוש ב-PluginContext על ידי הטמעה של ה-method‏ NewPluginContext ב-VMContext, שמבצעת יצירה של מופע של PluginContext. ‫PluginContext יוצר מופעים של הקשרים HttpContext.

לדוגמה, קוד הפלאגין הבא רושם את VMContext, שיוצר מופע של PluginContext, ההקשר שאחראי לקריאת נתוני ההגדרה וליצירת מופעים של הקשרי HttpContext לפי הצורך. ‫PluginContext קורא ערך סודי מנתוני התצורה של התוסף, מאחסן אותו בהקשר PluginContext ומעביר אותו אל HttpContext ב-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
}

חלודה

נתוני ההגדרה נקראים מRootContext מאפיין, שמוגדר פעם אחת בהפעלת הפלאגין ונשאר פעיל למשך משך החיים של זמן הריצה של Wasm שמארח את הפלאגין. התכונות של RootContext שימושיות לביצוע פעולות או לשמירה על המצב בהרבה בקשות.

קוד הפלאגין יכול לשלוט באיזה סוג משמש להקשר הבסיסי באמצעות השיטה set_root_context, וההקשר הבסיסי יוצר מופעים של הקשרי הזרם. לדוגמה, קוד הפלאגין הבא רושם את MyRootContext, שיוצר מופע של MyHttpContext לפי הצורך. ההקשר הבסיסי קורא ערך סודי מנתוני ההגדרה של הפלאגין ומעביר אותו להקשרי הזרם.

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

העלאת קובץ התצורה

אם גודל הנתונים שצריך להעביר לתוסף ב-on_configure גדול מ-900 KiB, צריך להעלות אותם ל-Artifact Registry בשיטה שמתוארת במאמר העלאת קוד התוסף שעבר קומפילציה ל-Artifact Registry. במקרה כזה, שומרים את קובץ ההגדרות בשם plugin.config (ולא plugin.wasm).

השלב הבא הוא יצירת פלאגין.

בזמן יצירת הפלאגין, צריך לספק את ה-URI של מודול ה-Wasm או התמונה שהועלו.

יצירת גרסה חדשה של קוד הפלאגין

כדי ליצור גרסה חדשה של קוד הפלאגין, עורכים את קובץ הפלאגין. אחר כך, כמו שמתואר בקטעים הקודמים, מקמפלים את קוד הפלאגין, אורזים אותו מחדש ומעלים אותו ל-Artifact Registry.

התקשרות חזרה

הקוד שאתם קומפלו ל-Wasm יכול להגדיר שיטות או פונקציות שרירותיות, אבל לחלק מהן יש משמעות מיוחדת. אלה השיטות שמוגדרות ב-Proxy-Wasm SDK בשפה שבחרתם, והן ממופות למפרטים של Proxy-Wasm Application Binary Interface ‏ (ABI). הפונקציות האלה מופעלות על ידי Service Extensions בתגובה לבקשות של משתמשים או בתגובה לאירועים במחזור החיים של התוסף.

הקריאות החוזרות האלה מפורטות בטבלה הבאה לפי הסדר שבו הן בדרך כלל מופעלות:

שם ופרטים של פונקציית הקריאה החוזרת שם השיטה ב-C++‎ שם שיטת Go שם ה-method ב-Rust
‫START_PLUGIN: מופעל כשמפעילים תוסף. RootContext::onStart VMContext.OnVmStart RootContext::on_vm_start
‫CONFIGURE_PLUGIN: מופעל אחרי שהפלאגין מתחיל, כדי לספק נתוני הגדרות לפלאגין. RootContext::onConfigure PluginContext.OnPluginStart RootContext::on_configure
‫CREATE_CONTEXT: מופעל כשנוצר הקשר של מקור נתונים חדש. כל מקור נתונים מתאים לבקשת HTTP של לקוח. Context::onCreate PluginContext.NewHttpContext RootContext::create_http_context
‫HTTP_REQUEST_HEADERS: מופעל כדי לעבד כותרות של בקשות HTTP. Context::onRequestHeaders HttpContext.OnHttpRequestHeaders HttpContext::on_http_request_headers
‫HTTP_REQUEST_BODY: מופעל שוב ושוב כדי לעבד נתחים של גוף בקשת HTTP. Context::onRequestBody HttpContext.OnHttpRequestBody HttpContext::on_http_request_body
‫HTTP_RESPONSE_HEADERS: מופעל כדי לעבד את הכותרות של תגובת HTTP. Context::onResponseHeaders HttpContext.OnHttpResponseHeaders HttpContext::on_http_response_headers
‫HTTP_RESPONSE_BODY: מופעל שוב ושוב כדי לעבד נתחים של גוף התגובה של HTTP. Context::onResponseBody HttpContext.OnHttpResponseBody HttpContext::on_http_response_body
‫DONE: מופעל כשעיבוד הפלאגין מסתיים. Context::onDone HttpContext.OnHttpStreamDone Context::on_done
‫DELETE: מופעל כשנמחק אובייקט ההקשר של הסטרימינג שתואם לבקשת HTTP של לקוח. Context::onDelete (אין קריאה חוזרת) (no callback)

המאמרים הבאים