From 1bc85f48638197a21b4b51b703f62bbf5bd55db4 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Mon, 15 May 2023 21:55:53 +0530 Subject: [PATCH 1/8] add go stub --- kits/go/go.mod | 3 +++ kits/go/worker.go | 1 + 2 files changed, 4 insertions(+) create mode 100644 kits/go/go.mod create mode 100644 kits/go/worker.go diff --git a/kits/go/go.mod b/kits/go/go.mod new file mode 100644 index 00000000..434c846d --- /dev/null +++ b/kits/go/go.mod @@ -0,0 +1,3 @@ +module github.com/vmware-labs/wasm-workers-server/kits/go + +go 1.20 diff --git a/kits/go/worker.go b/kits/go/worker.go new file mode 100644 index 00000000..4df0094f --- /dev/null +++ b/kits/go/worker.go @@ -0,0 +1 @@ +package worker From 9fc9368120b7e7817458b0e2e176effe624c7740 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Tue, 16 May 2023 23:26:33 +0530 Subject: [PATCH 2/8] add serve methods to go kit --- kits/go/go.mod | 7 ++ kits/go/go.sum | 7 ++ kits/go/worker.go | 1 - kits/go/worker/worker.go | 156 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 kits/go/go.sum delete mode 100644 kits/go/worker.go create mode 100644 kits/go/worker/worker.go diff --git a/kits/go/go.mod b/kits/go/go.mod index 434c846d..a67b3339 100644 --- a/kits/go/go.mod +++ b/kits/go/go.mod @@ -1,3 +1,10 @@ module github.com/vmware-labs/wasm-workers-server/kits/go go 1.20 + +require github.com/tidwall/gjson v1.14.4 + +require ( + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect +) diff --git a/kits/go/go.sum b/kits/go/go.sum new file mode 100644 index 00000000..57ae69a6 --- /dev/null +++ b/kits/go/go.sum @@ -0,0 +1,7 @@ +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= diff --git a/kits/go/worker.go b/kits/go/worker.go deleted file mode 100644 index 4df0094f..00000000 --- a/kits/go/worker.go +++ /dev/null @@ -1 +0,0 @@ -package worker diff --git a/kits/go/worker/worker.go b/kits/go/worker/worker.go new file mode 100644 index 00000000..c18c082c --- /dev/null +++ b/kits/go/worker/worker.go @@ -0,0 +1,156 @@ +package worker + +import ( + "encoding/base64" + "fmt" + "io" + "net/http" + "os" + "strings" + "unicode/utf8" + + "github.com/tidwall/gjson" +) + +type input struct { + Url string + Method string + Headers map[string]string + Body string + Kv map[string]string + Params map[string]string +} + +type output struct { + Data string + Headers map[string]string + Status uint16 + Kv map[string]string + Base64 bool + + httpHeader http.Header +} + +func (o *output) Header() http.Header { + if o.httpHeader == nil { + o.httpHeader = http.Header{} + } + + return o.httpHeader +} + +func (o *output) Write(data []byte) (int, error) { + if utf8.Valid(data) { + o.Data = string(data) + } else { + o.Base64 = true + o.Data = base64.StdEncoding.EncodeToString(data) + } + + if o.Status == 0 { + o.Status = 200 + } + + for k, v := range o.httpHeader { + o.Headers[k] = v[0] + } + + headersOut := "{" + + for k, v := range o.Headers { + headersOut += fmt.Sprintf(`"%s":"%s",`, k, v) + } + + headersOut = strings.TrimSuffix(headersOut, ",") + "}" + + kvOut := "{" + + for k, v := range o.Kv { + kvOut += fmt.Sprintf(`"%s":"%s",`, k, v) + } + + kvOut = strings.TrimSuffix(kvOut, ",") + "}" + + fmt.Printf("{\"data\":\"%s\",\"headers\":%s,\"status\":%d,\"kv\":%s,\"base64\":%t}", + o.Data, headersOut, o.Status, kvOut, o.Base64) + + return len(o.Data), nil +} + +func (o *output) WriteHeader(statusCode int) { + o.Status = uint16(statusCode) +} + +func readInput() *input { + stdin, err := io.ReadAll(os.Stdin) + if err != nil { + panic(err) + } + + in := &input{ + Url: gjson.GetBytes(stdin, "url").String(), + Method: gjson.GetBytes(stdin, "method").String(), + Body: gjson.GetBytes(stdin, "body").String(), + } + + if gjson.GetBytes(stdin, "headers").Exists() { + in.Headers = make(map[string]string) + + gjson.GetBytes(stdin, "headers").ForEach(func(key, value gjson.Result) bool { + in.Headers[key.String()] = value.String() + return true + }) + } + + if gjson.GetBytes(stdin, "kv").Exists() { + in.Kv = make(map[string]string) + + gjson.GetBytes(stdin, "kv").ForEach(func(key, value gjson.Result) bool { + in.Headers[key.String()] = value.String() + return true + }) + } + + if gjson.GetBytes(stdin, "params").Exists() { + in.Params = make(map[string]string) + + gjson.GetBytes(stdin, "params").ForEach(func(key, value gjson.Result) bool { + in.Headers[key.String()] = value.String() + return true + }) + } + + return in +} + +func createRequest(in *input) *http.Request { + req, err := http.NewRequest(in.Method, in.Url, strings.NewReader(in.Body)) + if err != nil { + panic(err) + } + + for k, v := range in.Headers { + req.Header.Set(k, v) + } + + return req +} + +func getWriterRequest() (*output, *http.Request) { + in := readInput() + req := createRequest(in) + w := &output{ + Headers: make(map[string]string), + Kv: in.Kv, + } + + return w, req +} + +func Serve(handler http.Handler) { + handler.ServeHTTP(getWriterRequest()) +} + +func ServeFunc(handler http.HandlerFunc) { + handler(getWriterRequest()) +} From 62b737c75ee3f23e9cff3f928f45704dbd4e17fb Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Wed, 17 May 2023 14:24:34 +0530 Subject: [PATCH 3/8] add go-basic example --- examples/go-basic/main.go | 59 +++++++++++++++++++++++++++++++++++++++ kits/go/go.mod => go.mod | 4 +-- kits/go/go.sum => go.sum | 3 +- kits/go/worker/worker.go | 2 +- 4 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 examples/go-basic/main.go rename kits/go/go.mod => go.mod (51%) rename kits/go/go.sum => go.sum (71%) diff --git a/examples/go-basic/main.go b/examples/go-basic/main.go new file mode 100644 index 00000000..fbc6673b --- /dev/null +++ b/examples/go-basic/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "io" + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" +) + +func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + var payload string + + reqBody, err := io.ReadAll(r.Body) + if err != nil { + panic(err) + } + r.Body.Close() + + if len(reqBody) == 0 { + payload = "-" + } else { + payload = string(reqBody) + } + + body := fmt.Sprintf(""+ + ""+ + "Wasm Workers Server"+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + "
"+ + "

Hello from Wasm Workers Server πŸ‘‹

"+ + "
Replying to %s
"+ + "Method: %s
"+ + "User Agent: %s
"+ + "Payload: %s
"+ + "

"+ + "This page was generated by a Go file running in WebAssembly."+ + "

"+ + "
"+ + "", r.URL.String(), r.Method, r.UserAgent(), payload) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) +} diff --git a/kits/go/go.mod b/go.mod similarity index 51% rename from kits/go/go.mod rename to go.mod index a67b3339..b37481b8 100644 --- a/kits/go/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/vmware-labs/wasm-workers-server/kits/go +module github.com/vmware-labs/wasm-workers-server go 1.20 @@ -6,5 +6,5 @@ require github.com/tidwall/gjson v1.14.4 require ( github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect ) diff --git a/kits/go/go.sum b/go.sum similarity index 71% rename from kits/go/go.sum rename to go.sum index 57ae69a6..bac61e79 100644 --- a/kits/go/go.sum +++ b/go.sum @@ -2,6 +2,5 @@ github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= diff --git a/kits/go/worker/worker.go b/kits/go/worker/worker.go index c18c082c..021261fc 100644 --- a/kits/go/worker/worker.go +++ b/kits/go/worker/worker.go @@ -72,7 +72,7 @@ func (o *output) Write(data []byte) (int, error) { kvOut = strings.TrimSuffix(kvOut, ",") + "}" fmt.Printf("{\"data\":\"%s\",\"headers\":%s,\"status\":%d,\"kv\":%s,\"base64\":%t}", - o.Data, headersOut, o.Status, kvOut, o.Base64) + strings.ReplaceAll(o.Data, "\"", "\\\""), headersOut, o.Status, kvOut, o.Base64) return len(o.Data), nil } From 38f7dad32b859f229fdba23233aaacb807216865 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Wed, 17 May 2023 15:42:45 +0530 Subject: [PATCH 4/8] add go kv example --- examples/go-kv/counter.go | 34 ++++++++++++++++++++++++++++++++++ examples/go-kv/counter.toml | 6 ++++++ kits/go/worker/worker.go | 15 +++++++++++---- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 examples/go-kv/counter.go create mode 100644 examples/go-kv/counter.toml diff --git a/examples/go-kv/counter.go b/examples/go-kv/counter.go new file mode 100644 index 00000000..aa341be3 --- /dev/null +++ b/examples/go-kv/counter.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" +) + +func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + cache, _ := r.Context().Value("CACHE").(map[string]string) + + var countNum uint32 + + if count, ok := cache["counter"]; ok { + n, _ := strconv.ParseUint(count, 10, 32) + countNum = uint32(n) + } + + body := fmt.Sprintf(""+ + ""+ + "

Key / Value store in Go

"+ + "

Counter: %d

"+ + "

This page was generated by a Wasm modules built from Go.

"+ + "", countNum) + + cache["counter"] = fmt.Sprintf("%d", countNum+1) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) +} diff --git a/examples/go-kv/counter.toml b/examples/go-kv/counter.toml new file mode 100644 index 00000000..aa9f6a95 --- /dev/null +++ b/examples/go-kv/counter.toml @@ -0,0 +1,6 @@ +name = "counter" +version = "1" + +[data] +[data.kv] +namespace = "counter" \ No newline at end of file diff --git a/kits/go/worker/worker.go b/kits/go/worker/worker.go index 021261fc..2d361b0e 100644 --- a/kits/go/worker/worker.go +++ b/kits/go/worker/worker.go @@ -1,6 +1,7 @@ package worker import ( + "context" "encoding/base64" "fmt" "io" @@ -12,6 +13,12 @@ import ( "github.com/tidwall/gjson" ) +var cache map[string]string + +func init() { + cache = make(map[string]string) +} + type input struct { Url string Method string @@ -65,7 +72,7 @@ func (o *output) Write(data []byte) (int, error) { kvOut := "{" - for k, v := range o.Kv { + for k, v := range cache { kvOut += fmt.Sprintf(`"%s":"%s",`, k, v) } @@ -106,7 +113,7 @@ func readInput() *input { in.Kv = make(map[string]string) gjson.GetBytes(stdin, "kv").ForEach(func(key, value gjson.Result) bool { - in.Headers[key.String()] = value.String() + cache[key.String()] = value.String() return true }) } @@ -133,7 +140,7 @@ func createRequest(in *input) *http.Request { req.Header.Set(k, v) } - return req + return req.WithContext(context.WithValue(req.Context(), "CACHE", cache)) } func getWriterRequest() (*output, *http.Request) { @@ -141,7 +148,7 @@ func getWriterRequest() (*output, *http.Request) { req := createRequest(in) w := &output{ Headers: make(map[string]string), - Kv: in.Kv, + Kv: make(map[string]string), } return w, req From 012d6b0a9f65ebef8b4293dfc42ab88cf095aab7 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Wed, 17 May 2023 17:53:54 +0530 Subject: [PATCH 5/8] add go params example --- examples/go-params/main.go | 41 +++++++++++++++ examples/go-params/public/main.css | 28 ++++++++++ examples/go-params/public/water.min.css | 30 +++++++++++ kits/go/worker/worker.go | 70 ++++++++++++------------- 4 files changed, 134 insertions(+), 35 deletions(-) create mode 100644 examples/go-params/main.go create mode 100644 examples/go-params/public/main.css create mode 100644 examples/go-params/public/water.min.css diff --git a/examples/go-params/main.go b/examples/go-params/main.go new file mode 100644 index 00000000..574478ab --- /dev/null +++ b/examples/go-params/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" +) + +func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + params, _ := r.Context().Value("PARAMS").(map[string]string) + id := "the value is not available" + + if val, ok := params["id"]; ok { + id = val + } + + body := fmt.Sprintf(""+ + ""+ + "Wasm Workers Server"+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + "
"+ + "

Hello from Wasm Workers Server πŸ‘‹

"+ + "

"+ + "This is a dynamic route! The [id].wasm worker, written in Go, is replying this URL."+ + "The id parameter value is: %s"+ + "

"+ + "

Read more about dynamic routes in the documentation

"+ + "
"+ + "", id) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) +} diff --git a/examples/go-params/public/main.css b/examples/go-params/public/main.css new file mode 100644 index 00000000..ca178402 --- /dev/null +++ b/examples/go-params/public/main.css @@ -0,0 +1,28 @@ +body { + max-width: 1000px; +} + +main { + margin: 5rem 0; +} + +h1, +p { + text-align: center; +} + +h1 { + margin-bottom: 2rem; +} + +pre { + font-size: .9rem; +} + +pre>code { + padding: 2rem; +} + +p { + margin-top: 2rem; +} \ No newline at end of file diff --git a/examples/go-params/public/water.min.css b/examples/go-params/public/water.min.css new file mode 100644 index 00000000..fddfc43d --- /dev/null +++ b/examples/go-params/public/water.min.css @@ -0,0 +1,30 @@ +/* + * The MIT License (MIT) + * + * Copyright Β© 2019 Kognise + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the β€œSoftware”), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + * Ref: https://github.com/kognise/water.css + */ +:root{--background-body:#fff;--background:#efefef;--background-alt:#f7f7f7;--selection:#9e9e9e;--text-main:#363636;--text-bright:#000;--text-muted:#70777f;--links:#0076d1;--focus:rgba(0,150,191,0.67);--border:#dbdbdb;--code:#000;--animation-duration:0.1s;--button-base:#d0cfcf;--button-hover:#9b9b9b;--scrollbar-thumb:#aaa;--scrollbar-thumb-hover:var(--button-hover);--form-placeholder:#949494;--form-text:#1d1d1d;--variable:#39a33c;--highlight:#ff0;--select-arrow:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23161f27'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E")}@media (prefers-color-scheme:dark){:root{--background-body:#202b38;--background:#161f27;--background-alt:#1a242f;--selection:#1c76c5;--text-main:#dbdbdb;--text-bright:#fff;--text-muted:#a9b1ba;--links:#41adff;--focus:rgba(0,150,191,0.67);--border:#526980;--code:#ffbe85;--animation-duration:0.1s;--button-base:#0c151c;--button-hover:#040a0f;--scrollbar-thumb:var(--button-hover);--scrollbar-thumb-hover:#000;--form-placeholder:#a9a9a9;--form-text:#fff;--variable:#d941e2;--highlight:#efdb43;--select-arrow:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23efefef'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E")}}html{scrollbar-color:#aaa #fff;scrollbar-color:var(--scrollbar-thumb) var(--background-body);scrollbar-width:thin}@media (prefers-color-scheme:dark){html{scrollbar-color:#040a0f #202b38;scrollbar-color:var(--scrollbar-thumb) var(--background-body)}}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Segoe UI Emoji,Apple Color Emoji,Noto Color Emoji,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;word-wrap:break-word;color:#363636;color:var(--text-main);background:#fff;background:var(--background-body);text-rendering:optimizeLegibility}@media (prefers-color-scheme:dark){body{background:#202b38;background:var(--background-body);color:#dbdbdb;color:var(--text-main)}}button{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}@media (prefers-color-scheme:dark){button{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}}input{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}@media (prefers-color-scheme:dark){input{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}}textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}@media (prefers-color-scheme:dark){textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px;margin-top:24px}h1{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h1{color:#fff;color:var(--text-bright)}}h2{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h2{color:#fff;color:var(--text-bright)}}h3{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h3{color:#fff;color:var(--text-bright)}}h4{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h4{color:#fff;color:var(--text-bright)}}h5{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h5{color:#fff;color:var(--text-bright)}}h6{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h6{color:#fff;color:var(--text-bright)}}strong{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){strong{color:#fff;color:var(--text-bright)}}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}q:after,q:before{content:none}blockquote{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus);margin:1.5em 0;padding:.5em 1em;font-style:italic}@media (prefers-color-scheme:dark){blockquote{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus)}}q{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus);margin:1.5em 0;padding:.5em 1em;font-style:italic}@media (prefers-color-scheme:dark){q{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus)}}blockquote>footer{font-style:normal;border:0}address,blockquote cite{font-style:normal}a[href^=mailto\:]:before{content:"πŸ“§ "}a[href^=tel\:]:before{content:"πŸ“ž "}a[href^=sms\:]:before{content:"πŸ’¬ "}mark{background-color:#ff0;background-color:var(--highlight);border-radius:2px;padding:0 2px;color:#000}@media (prefers-color-scheme:dark){mark{background-color:#efdb43;background-color:var(--highlight)}}a>code,a>strong{color:inherit}button,input[type=button],input[type=checkbox],input[type=radio],input[type=range],input[type=reset],input[type=submit],select{cursor:pointer}input,select{display:block}[type=checkbox],[type=radio]{display:initial}input{color:#1d1d1d;color:var(--form-text);background-color:#efefef;background-color:var(--background);font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}@media (prefers-color-scheme:dark){input{background-color:#161f27;background-color:var(--background);color:#fff;color:var(--form-text)}}button{color:#1d1d1d;color:var(--form-text);background-color:#efefef;background-color:var(--background);font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}@media (prefers-color-scheme:dark){button{background-color:#161f27;background-color:var(--background);color:#fff;color:var(--form-text)}}textarea{color:#1d1d1d;color:var(--form-text);background-color:#efefef;background-color:var(--background);font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}@media (prefers-color-scheme:dark){textarea{background-color:#161f27;background-color:var(--background);color:#fff;color:var(--form-text)}}select{color:#1d1d1d;color:var(--form-text);background-color:#efefef;background-color:var(--background);font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}@media (prefers-color-scheme:dark){select{background-color:#161f27;background-color:var(--background);color:#fff;color:var(--form-text)}}button{background-color:#d0cfcf;background-color:var(--button-base);padding-right:30px;padding-left:30px}@media (prefers-color-scheme:dark){button{background-color:#0c151c;background-color:var(--button-base)}}input[type=submit]{background-color:#d0cfcf;background-color:var(--button-base);padding-right:30px;padding-left:30px}@media (prefers-color-scheme:dark){input[type=submit]{background-color:#0c151c;background-color:var(--button-base)}}input[type=reset]{background-color:#d0cfcf;background-color:var(--button-base);padding-right:30px;padding-left:30px}@media (prefers-color-scheme:dark){input[type=reset]{background-color:#0c151c;background-color:var(--button-base)}}input[type=button]{background-color:#d0cfcf;background-color:var(--button-base);padding-right:30px;padding-left:30px}@media (prefers-color-scheme:dark){input[type=button]{background-color:#0c151c;background-color:var(--button-base)}}button:hover{background:#9b9b9b;background:var(--button-hover)}@media (prefers-color-scheme:dark){button:hover{background:#040a0f;background:var(--button-hover)}}input[type=submit]:hover{background:#9b9b9b;background:var(--button-hover)}@media (prefers-color-scheme:dark){input[type=submit]:hover{background:#040a0f;background:var(--button-hover)}}input[type=reset]:hover{background:#9b9b9b;background:var(--button-hover)}@media (prefers-color-scheme:dark){input[type=reset]:hover{background:#040a0f;background:var(--button-hover)}}input[type=button]:hover{background:#9b9b9b;background:var(--button-hover)}@media (prefers-color-scheme:dark){input[type=button]:hover{background:#040a0f;background:var(--button-hover)}}input[type=color]{min-height:2rem;padding:8px;cursor:pointer}input[type=checkbox],input[type=radio]{height:1em;width:1em}input[type=radio]{border-radius:100%}input{vertical-align:top}label{vertical-align:middle;margin-bottom:4px;display:inline-block}button,input:not([type=checkbox]):not([type=radio]),input[type=range],select,textarea{-webkit-appearance:none}textarea{display:block;margin-right:0;box-sizing:border-box;resize:vertical}textarea:not([cols]){width:100%}textarea:not([rows]){min-height:40px;height:140px}select{background:#efefef url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23161f27'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E") calc(100% - 12px) 50%/12px no-repeat;background:var(--background) var(--select-arrow) calc(100% - 12px) 50%/12px no-repeat;padding-right:35px}@media (prefers-color-scheme:dark){select{background:#161f27 url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23efefef'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E") calc(100% - 12px) 50%/12px no-repeat;background:var(--background) var(--select-arrow) calc(100% - 12px) 50%/12px no-repeat}}select::-ms-expand{display:none}select[multiple]{padding-right:10px;background-image:none;overflow-y:auto}input:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}@media (prefers-color-scheme:dark){input:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}}select:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}@media (prefers-color-scheme:dark){select:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}}button:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}@media (prefers-color-scheme:dark){button:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}}textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}@media (prefers-color-scheme:dark){textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=range]:active,input[type=reset]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-moz-placeholder{color:#949494;color:var(--form-placeholder)}:-ms-input-placeholder{color:#949494;color:var(--form-placeholder)}::-ms-input-placeholder{color:#949494;color:var(--form-placeholder)}::placeholder{color:#949494;color:var(--form-placeholder)}@media (prefers-color-scheme:dark){::-moz-placeholder{color:#a9a9a9;color:var(--form-placeholder)}:-ms-input-placeholder{color:#a9a9a9;color:var(--form-placeholder)}::-ms-input-placeholder{color:#a9a9a9;color:var(--form-placeholder)}::placeholder{color:#a9a9a9;color:var(--form-placeholder)}}fieldset{border:1px solid rgba(0,150,191,.67);border:1px solid var(--focus);border-radius:6px;margin:0 0 12px;padding:10px}@media (prefers-color-scheme:dark){fieldset{border:1px solid rgba(0,150,191,.67);border:1px solid var(--focus)}}legend{font-size:.9em;font-weight:600}input[type=range]{margin:10px 0;padding:10px 0;background:transparent}input[type=range]:focus{outline:none}input[type=range]::-webkit-slider-runnable-track{width:100%;height:9.5px;-webkit-transition:.2s;transition:.2s;background:#efefef;background:var(--background);border-radius:3px}@media (prefers-color-scheme:dark){input[type=range]::-webkit-slider-runnable-track{background:#161f27;background:var(--background)}}input[type=range]::-webkit-slider-thumb{box-shadow:0 1px 1px #000,0 0 1px #0d0d0d;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border);-webkit-appearance:none;margin-top:-7px}@media (prefers-color-scheme:dark){input[type=range]::-webkit-slider-thumb{background:#526980;background:var(--border)}}input[type=range]:focus::-webkit-slider-runnable-track{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-webkit-slider-runnable-track{background:#161f27;background:var(--background)}}input[type=range]::-moz-range-track{width:100%;height:9.5px;-moz-transition:.2s;transition:.2s;background:#efefef;background:var(--background);border-radius:3px}@media (prefers-color-scheme:dark){input[type=range]::-moz-range-track{background:#161f27;background:var(--background)}}input[type=range]::-moz-range-thumb{box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border)}@media (prefers-color-scheme:dark){input[type=range]::-moz-range-thumb{background:#526980;background:var(--border)}}input[type=range]::-ms-track{width:100%;height:9.5px;background:transparent;border-color:transparent;border-width:16px 0;color:transparent}input[type=range]::-ms-fill-lower{background:#efefef;background:var(--background);border:.2px solid #010101;border-radius:3px;box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d}@media (prefers-color-scheme:dark){input[type=range]::-ms-fill-lower{background:#161f27;background:var(--background)}}input[type=range]::-ms-fill-upper{background:#efefef;background:var(--background);border:.2px solid #010101;border-radius:3px;box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d}@media (prefers-color-scheme:dark){input[type=range]::-ms-fill-upper{background:#161f27;background:var(--background)}}input[type=range]::-ms-thumb{box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d;border:1px solid #000;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border)}@media (prefers-color-scheme:dark){input[type=range]::-ms-thumb{background:#526980;background:var(--border)}}input[type=range]:focus::-ms-fill-lower{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-ms-fill-lower{background:#161f27;background:var(--background)}}input[type=range]:focus::-ms-fill-upper{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-ms-fill-upper{background:#161f27;background:var(--background)}}a{text-decoration:none;color:#0076d1;color:var(--links)}@media (prefers-color-scheme:dark){a{color:#41adff;color:var(--links)}}a:hover{text-decoration:underline}code{background:#efefef;background:var(--background);color:#000;color:var(--code);padding:2.5px 5px;border-radius:6px;font-size:1em}@media (prefers-color-scheme:dark){code{color:#ffbe85;color:var(--code);background:#161f27;background:var(--background)}}samp{background:#efefef;background:var(--background);color:#000;color:var(--code);padding:2.5px 5px;border-radius:6px;font-size:1em}@media (prefers-color-scheme:dark){samp{color:#ffbe85;color:var(--code);background:#161f27;background:var(--background)}}time{background:#efefef;background:var(--background);color:#000;color:var(--code);padding:2.5px 5px;border-radius:6px;font-size:1em}@media (prefers-color-scheme:dark){time{color:#ffbe85;color:var(--code);background:#161f27;background:var(--background)}}pre>code{padding:10px;display:block;overflow-x:auto}var{color:#39a33c;color:var(--variable);font-style:normal;font-family:monospace}@media (prefers-color-scheme:dark){var{color:#d941e2;color:var(--variable)}}kbd{background:#efefef;background:var(--background);border:1px solid #dbdbdb;border:1px solid var(--border);border-radius:2px;color:#363636;color:var(--text-main);padding:2px 4px}@media (prefers-color-scheme:dark){kbd{color:#dbdbdb;color:var(--text-main);border:1px solid #526980;border:1px solid var(--border);background:#161f27;background:var(--background)}}img,video{max-width:100%;height:auto}hr{border:none;border-top:1px solid #dbdbdb;border-top:1px solid var(--border)}@media (prefers-color-scheme:dark){hr{border-top:1px solid #526980;border-top:1px solid var(--border)}}table{border-collapse:collapse;margin-bottom:10px;width:100%;table-layout:fixed}table caption,td,th{text-align:left}td,th{padding:6px;vertical-align:top;word-wrap:break-word}thead{border-bottom:1px solid #dbdbdb;border-bottom:1px solid var(--border)}@media (prefers-color-scheme:dark){thead{border-bottom:1px solid #526980;border-bottom:1px solid var(--border)}}tfoot{border-top:1px solid #dbdbdb;border-top:1px solid var(--border)}@media (prefers-color-scheme:dark){tfoot{border-top:1px solid #526980;border-top:1px solid var(--border)}}tbody tr:nth-child(2n){background-color:#efefef;background-color:var(--background)}@media (prefers-color-scheme:dark){tbody tr:nth-child(2n){background-color:#161f27;background-color:var(--background)}}tbody tr:nth-child(2n) button{background-color:#f7f7f7;background-color:var(--background-alt)}@media (prefers-color-scheme:dark){tbody tr:nth-child(2n) button{background-color:#1a242f;background-color:var(--background-alt)}}tbody tr:nth-child(2n) button:hover{background-color:#fff;background-color:var(--background-body)}@media (prefers-color-scheme:dark){tbody tr:nth-child(2n) button:hover{background-color:#202b38;background-color:var(--background-body)}}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#efefef;background:var(--background);border-radius:6px}@media (prefers-color-scheme:dark){::-webkit-scrollbar-track{background:#161f27;background:var(--background)}}::-webkit-scrollbar-thumb{background:#aaa;background:var(--scrollbar-thumb);border-radius:6px}@media (prefers-color-scheme:dark){::-webkit-scrollbar-thumb{background:#040a0f;background:var(--scrollbar-thumb)}}::-webkit-scrollbar-thumb:hover{background:#9b9b9b;background:var(--scrollbar-thumb-hover)}@media (prefers-color-scheme:dark){::-webkit-scrollbar-thumb:hover{background:#000;background:var(--scrollbar-thumb-hover)}}::-moz-selection{background-color:#9e9e9e;background-color:var(--selection);color:#000;color:var(--text-bright)}::selection{background-color:#9e9e9e;background-color:var(--selection);color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){::-moz-selection{color:#fff;color:var(--text-bright)}::selection{color:#fff;color:var(--text-bright)}}@media (prefers-color-scheme:dark){::-moz-selection{background-color:#1c76c5;background-color:var(--selection)}::selection{background-color:#1c76c5;background-color:var(--selection)}}details{display:flex;flex-direction:column;align-items:flex-start;background-color:#f7f7f7;background-color:var(--background-alt);padding:10px 10px 0;margin:1em 0;border-radius:6px;overflow:hidden}@media (prefers-color-scheme:dark){details{background-color:#1a242f;background-color:var(--background-alt)}}details[open]{padding:10px}details>:last-child{margin-bottom:0}details[open] summary{margin-bottom:10px}summary{display:list-item;background-color:#efefef;background-color:var(--background);padding:10px;margin:-10px -10px 0;cursor:pointer;outline:none}@media (prefers-color-scheme:dark){summary{background-color:#161f27;background-color:var(--background)}}summary:focus,summary:hover{text-decoration:underline}details>:not(summary){margin-top:0}summary::-webkit-details-marker{color:#363636;color:var(--text-main)}@media (prefers-color-scheme:dark){summary::-webkit-details-marker{color:#dbdbdb;color:var(--text-main)}}dialog{background-color:#f7f7f7;background-color:var(--background-alt);color:#363636;color:var(--text-main);border-radius:6px;border:#dbdbdb;border-color:var(--border);padding:10px 30px}@media (prefers-color-scheme:dark){dialog{border-color:#526980;border-color:var(--border);color:#dbdbdb;color:var(--text-main);background-color:#1a242f;background-color:var(--background-alt)}}dialog>header:first-child{background-color:#efefef;background-color:var(--background);border-radius:6px 6px 0 0;margin:-10px -30px 10px;padding:10px;text-align:center}@media (prefers-color-scheme:dark){dialog>header:first-child{background-color:#161f27;background-color:var(--background)}}dialog::-webkit-backdrop{background:rgba(0,0,0,.61);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}dialog::backdrop{background:rgba(0,0,0,.61);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}footer{border-top:1px solid #dbdbdb;border-top:1px solid var(--border);padding-top:10px;color:#70777f;color:var(--text-muted)}@media (prefers-color-scheme:dark){footer{color:#a9b1ba;color:var(--text-muted);border-top:1px solid #526980;border-top:1px solid var(--border)}}body>footer{margin-top:40px}@media print{body,button,code,details,input,pre,summary,textarea{background-color:#fff}button,input,textarea{border:1px solid #000}body,button,code,footer,h1,h2,h3,h4,h5,h6,input,pre,strong,summary,textarea{color:#000}summary::marker{color:#000}summary::-webkit-details-marker{color:#000}tbody tr:nth-child(2n){background-color:#f2f2f2}a{color:#00f;text-decoration:underline}} \ No newline at end of file diff --git a/kits/go/worker/worker.go b/kits/go/worker/worker.go index 2d361b0e..2513c4f9 100644 --- a/kits/go/worker/worker.go +++ b/kits/go/worker/worker.go @@ -20,20 +20,19 @@ func init() { } type input struct { - Url string - Method string - Headers map[string]string - Body string - Kv map[string]string - Params map[string]string + url string + method string + headers map[string]string + body string + params map[string]string } type output struct { - Data string - Headers map[string]string - Status uint16 - Kv map[string]string - Base64 bool + data string + headers map[string]string + status uint16 + kv map[string]string + base64 bool httpHeader http.Header } @@ -48,23 +47,23 @@ func (o *output) Header() http.Header { func (o *output) Write(data []byte) (int, error) { if utf8.Valid(data) { - o.Data = string(data) + o.data = string(data) } else { - o.Base64 = true - o.Data = base64.StdEncoding.EncodeToString(data) + o.base64 = true + o.data = base64.StdEncoding.EncodeToString(data) } - if o.Status == 0 { - o.Status = 200 + if o.status == 0 { + o.status = 200 } for k, v := range o.httpHeader { - o.Headers[k] = v[0] + o.headers[k] = v[0] } headersOut := "{" - for k, v := range o.Headers { + for k, v := range o.headers { headersOut += fmt.Sprintf(`"%s":"%s",`, k, v) } @@ -79,13 +78,13 @@ func (o *output) Write(data []byte) (int, error) { kvOut = strings.TrimSuffix(kvOut, ",") + "}" fmt.Printf("{\"data\":\"%s\",\"headers\":%s,\"status\":%d,\"kv\":%s,\"base64\":%t}", - strings.ReplaceAll(o.Data, "\"", "\\\""), headersOut, o.Status, kvOut, o.Base64) + strings.ReplaceAll(o.data, "\"", "\\\""), headersOut, o.status, kvOut, o.base64) - return len(o.Data), nil + return len(o.data), nil } func (o *output) WriteHeader(statusCode int) { - o.Status = uint16(statusCode) + o.status = uint16(statusCode) } func readInput() *input { @@ -95,23 +94,21 @@ func readInput() *input { } in := &input{ - Url: gjson.GetBytes(stdin, "url").String(), - Method: gjson.GetBytes(stdin, "method").String(), - Body: gjson.GetBytes(stdin, "body").String(), + url: gjson.GetBytes(stdin, "url").String(), + method: gjson.GetBytes(stdin, "method").String(), + body: gjson.GetBytes(stdin, "body").String(), } if gjson.GetBytes(stdin, "headers").Exists() { - in.Headers = make(map[string]string) + in.headers = make(map[string]string) gjson.GetBytes(stdin, "headers").ForEach(func(key, value gjson.Result) bool { - in.Headers[key.String()] = value.String() + in.headers[key.String()] = value.String() return true }) } if gjson.GetBytes(stdin, "kv").Exists() { - in.Kv = make(map[string]string) - gjson.GetBytes(stdin, "kv").ForEach(func(key, value gjson.Result) bool { cache[key.String()] = value.String() return true @@ -119,10 +116,10 @@ func readInput() *input { } if gjson.GetBytes(stdin, "params").Exists() { - in.Params = make(map[string]string) + in.params = make(map[string]string) gjson.GetBytes(stdin, "params").ForEach(func(key, value gjson.Result) bool { - in.Headers[key.String()] = value.String() + in.params[key.String()] = value.String() return true }) } @@ -131,24 +128,27 @@ func readInput() *input { } func createRequest(in *input) *http.Request { - req, err := http.NewRequest(in.Method, in.Url, strings.NewReader(in.Body)) + req, err := http.NewRequest(in.method, in.url, strings.NewReader(in.body)) if err != nil { panic(err) } - for k, v := range in.Headers { + for k, v := range in.headers { req.Header.Set(k, v) } - return req.WithContext(context.WithValue(req.Context(), "CACHE", cache)) + req = req.WithContext(context.WithValue(req.Context(), "CACHE", cache)) + req = req.WithContext(context.WithValue(req.Context(), "PARAMS", in.params)) + + return req } func getWriterRequest() (*output, *http.Request) { in := readInput() req := createRequest(in) w := &output{ - Headers: make(map[string]string), - Kv: make(map[string]string), + headers: make(map[string]string), + kv: make(map[string]string), } return w, req From 7a95208f633b485be56cba0ca46fa61beba4a868 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Wed, 17 May 2023 19:43:36 +0530 Subject: [PATCH 6/8] add go envs example --- examples/go-envs/envs.go | 18 ++++++++++++++++++ examples/go-envs/envs.toml | 5 +++++ 2 files changed, 23 insertions(+) create mode 100644 examples/go-envs/envs.go create mode 100644 examples/go-envs/envs.toml diff --git a/examples/go-envs/envs.go b/examples/go-envs/envs.go new file mode 100644 index 00000000..dbb3049b --- /dev/null +++ b/examples/go-envs/envs.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "net/http" + "os" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" +) + +func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + body := fmt.Sprintf("The environment variable value is: %s", os.Getenv("MESSAGE")) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) +} diff --git a/examples/go-envs/envs.toml b/examples/go-envs/envs.toml new file mode 100644 index 00000000..4c90dfc5 --- /dev/null +++ b/examples/go-envs/envs.toml @@ -0,0 +1,5 @@ +name = "envs" +version = "1" + +[vars] +MESSAGE = "Hello! This message comes from an environment variable" \ No newline at end of file From 694d45fc1ed29e861ce202f4bb9a762ebecdfb15 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Mon, 29 May 2023 23:56:36 +0530 Subject: [PATCH 7/8] use sjson and add Go docs --- README.md | 1 + docs/docs/features/dynamic-routes.md | 1 + docs/docs/features/environment-variables.md | 1 + docs/docs/features/key-value.md | 1 + docs/docs/get-started/quickstart.md | 1 + docs/docs/languages/go.md | 11 ++ docs/src/components/HomepageFeatures/index.js | 2 +- docs/src/css/custom.css | 6 +- docs/static/img/languages/go.svg | 8 ++ examples/go-kv/counter.go | 4 +- examples/go-params/main.go | 2 +- go.mod | 7 +- go.sum | 6 +- kits/go/worker/doc.go | 13 ++ kits/go/worker/worker.go | 129 ++++++++++-------- 15 files changed, 128 insertions(+), 65 deletions(-) create mode 100644 docs/docs/languages/go.md create mode 100644 docs/static/img/languages/go.svg create mode 100644 kits/go/worker/doc.go diff --git a/README.md b/README.md index 0738fbbe..2a0bd674 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Wasm Workers Server focuses on simplicity. We want you to run workers (written i | --- | --- | --- | | Rust | βœ… | No | | JavaScript | βœ… | No | +| Go | βœ… | No | | Ruby | βœ… | [Yes](https://workers.wasmlabs.dev/docs/languages/ruby#installation) | | Python | βœ… | [Yes](https://workers.wasmlabs.dev/docs/languages/python#installation) | | ... | ... | ... | diff --git a/docs/docs/features/dynamic-routes.md b/docs/docs/features/dynamic-routes.md index 55416621..c4dea997 100644 --- a/docs/docs/features/dynamic-routes.md +++ b/docs/docs/features/dynamic-routes.md @@ -21,6 +21,7 @@ Check these guides to understand how to read parameters in the different support * [Dynamic routes in Rust](../languages/rust.md#dynamic-routes) * [Dynamic routes in Python](../languages/python.md#dynamic-routes) * [Dynamic routes in Ruby](../languages/ruby.md#dynamic-routes) +* [Dynamic routes in Go](../languages/go.md#dynamic-routes) ## Dynamic routes and folders diff --git a/docs/docs/features/environment-variables.md b/docs/docs/features/environment-variables.md index b46d13c6..4df03032 100644 --- a/docs/docs/features/environment-variables.md +++ b/docs/docs/features/environment-variables.md @@ -22,6 +22,7 @@ Then, you can read them in your worker: * [Read environment variables in Rust](../languages/rust.md#read-environment-variables) * [Read environment variables in Python](../languages/python.md#read-environment-variables) * [Read environment variables in Ruby](../languages/ruby.md#read-environment-variables) +* [Read environment variables in Go](../languages/go.md#read-environment-variables) ## Inject existing environment variables diff --git a/docs/docs/features/key-value.md b/docs/docs/features/key-value.md index ac0c8874..f077183d 100644 --- a/docs/docs/features/key-value.md +++ b/docs/docs/features/key-value.md @@ -20,6 +20,7 @@ The worker may access all the data and perform changes over it. Then, a new K/V * [Add a K/V store to Rust workers](../languages/rust.md#add-a-key--value-store) * [Add a K/V store to Python workers](../languages/python.md#add-a-key--value-store) * [Add a K/V store to Ruby workers](../languages/ruby.md#add-a-key--value-store) +* [Add a K/V store to Go workers](../languages/go.md#add-a-key--value-store) ## Limitations diff --git a/docs/docs/get-started/quickstart.md b/docs/docs/get-started/quickstart.md index 77de0384..92ee3e12 100644 --- a/docs/docs/get-started/quickstart.md +++ b/docs/docs/get-started/quickstart.md @@ -63,5 +63,6 @@ Now you got the taste of Wasm Workers, it's time to create your first worker: * [Create your first Rust worker](../languages/rust.md) * [Create your first Python worker](../languages/python.md) * [Create your first Ruby worker](../languages/ruby.md) +* [Create your first Go worker](../languages/go.md) And if you are curious, here you have a guide about [how it works](./how-it-works.md). \ No newline at end of file diff --git a/docs/docs/languages/go.md b/docs/docs/languages/go.md new file mode 100644 index 00000000..6a2a00a2 --- /dev/null +++ b/docs/docs/languages/go.md @@ -0,0 +1,11 @@ +--- +sidebar_position: 5 +--- + +# Go + +Go workers are compiled into a WASI module using [TinyGo](https://tinygo.org/docs/guides/webassembly/). Then, they are loaded by Wasm Workers Server and start processing requests. + +## Your first Go worker + +Workers can be implemented either as an [http.Handler](https://pkg.go.dev/net/http#Handler) or an [http.HandlerFunc](https://pkg.go.dev/net/http#HandlerFunc). diff --git a/docs/src/components/HomepageFeatures/index.js b/docs/src/components/HomepageFeatures/index.js index 0bfce5bc..42b4e46a 100644 --- a/docs/src/components/HomepageFeatures/index.js +++ b/docs/src/components/HomepageFeatures/index.js @@ -17,7 +17,7 @@ const FeatureList = [ emoji: "βš™οΈ", description: ( <> - Create workers in different languages like JavaScript, Ruby, Python and Rust thanks to WebAssembly. + Create workers in different languages like JavaScript, Ruby, Python, Rust and Go thanks to WebAssembly. ), }, diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 4b3844ec..08b1d584 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -99,4 +99,8 @@ a.menu__link[href*="languages/ruby"]::before { a.menu__link[href*="languages/rust"]::before { background-image: url(/img/languages/rust.svg); -} \ No newline at end of file +} + +a.menu__link[href*="languages/go"]::before { + background-image: url(/img/languages/go.svg); +} diff --git a/docs/static/img/languages/go.svg b/docs/static/img/languages/go.svg new file mode 100644 index 00000000..a17ee14f --- /dev/null +++ b/docs/static/img/languages/go.svg @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/examples/go-kv/counter.go b/examples/go-kv/counter.go index aa341be3..b08c2f67 100644 --- a/examples/go-kv/counter.go +++ b/examples/go-kv/counter.go @@ -10,7 +10,7 @@ import ( func main() { worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { - cache, _ := r.Context().Value("CACHE").(map[string]string) + cache, _ := r.Context().Value(worker.CacheKey).(map[string]string) var countNum uint32 @@ -23,7 +23,7 @@ func main() { ""+ "

Key / Value store in Go

"+ "

Counter: %d

"+ - "

This page was generated by a Wasm modules built from Go.

"+ + "

This page was generated by a Wasm module built from Go.

"+ "", countNum) cache["counter"] = fmt.Sprintf("%d", countNum+1) diff --git a/examples/go-params/main.go b/examples/go-params/main.go index 574478ab..63215a53 100644 --- a/examples/go-params/main.go +++ b/examples/go-params/main.go @@ -9,7 +9,7 @@ import ( func main() { worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { - params, _ := r.Context().Value("PARAMS").(map[string]string) + params, _ := r.Context().Value(worker.ParamsKey).(map[string]string) id := "the value is not available" if val, ok := params["id"]; ok { diff --git a/go.mod b/go.mod index b37481b8..5d47074a 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,12 @@ module github.com/vmware-labs/wasm-workers-server go 1.20 -require github.com/tidwall/gjson v1.14.4 +require ( + github.com/tidwall/gjson v1.14.4 + github.com/tidwall/sjson v1.2.5 +) require ( github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect ) diff --git a/go.sum b/go.sum index bac61e79..a70a5e0a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,10 @@ +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= diff --git a/kits/go/worker/doc.go b/kits/go/worker/doc.go new file mode 100644 index 00000000..2f1fbafb --- /dev/null +++ b/kits/go/worker/doc.go @@ -0,0 +1,13 @@ +/** + * + * === Go support for WASM Workers Server === + * + * This package provides a simple way to write WASM workers in Go. It uses the gjson, sjson libraries instead + * of Go's standard encoding/json package due to the following reasons: + * -- as of writing this file, the default Go compiler does not support the WASI backend, + * -- TinyGo (which does support WASI) does not support reflection and hence, we need to rely on a JSON library + * that does not use reflection + * + */ + +package worker diff --git a/kits/go/worker/worker.go b/kits/go/worker/worker.go index 2513c4f9..373f496c 100644 --- a/kits/go/worker/worker.go +++ b/kits/go/worker/worker.go @@ -11,32 +11,44 @@ import ( "unicode/utf8" "github.com/tidwall/gjson" + "github.com/tidwall/sjson" ) -var cache map[string]string +type ContextKey string -func init() { - cache = make(map[string]string) -} +const ( + CacheKey ContextKey = "CACHE" + ParamsKey ContextKey = "PARAMS" +) type input struct { - url string - method string - headers map[string]string - body string - params map[string]string + Url string + Method string + Headers map[string]string + Body string } type output struct { - data string - headers map[string]string - status uint16 - kv map[string]string - base64 bool + Data string + Headers map[string]string + Status uint16 + Base64 bool httpHeader http.Header } +var ( + cache map[string]string + params map[string]string +) + +func init() { + cache = make(map[string]string) + params = make(map[string]string) +} + +// output implements the http.ResponseWriter interface + func (o *output) Header() http.Header { if o.httpHeader == nil { o.httpHeader = http.Header{} @@ -47,63 +59,59 @@ func (o *output) Header() http.Header { func (o *output) Write(data []byte) (int, error) { if utf8.Valid(data) { - o.data = string(data) + o.Data = string(data) } else { - o.base64 = true - o.data = base64.StdEncoding.EncodeToString(data) + o.Base64 = true + o.Data = base64.StdEncoding.EncodeToString(data) } - if o.status == 0 { - o.status = 200 + if o.Status == 0 { + o.Status = 200 } for k, v := range o.httpHeader { - o.headers[k] = v[0] + o.Headers[k] = v[0] } - headersOut := "{" + out, _ := sjson.Set("", "data", o.Data) + out, _ = sjson.Set(out, "status", o.Status) + out, _ = sjson.Set(out, "base64", o.Base64) + out, _ = sjson.SetRaw(out, "headers", "{}") + out, _ = sjson.SetRaw(out, "kv", "{}") - for k, v := range o.headers { - headersOut += fmt.Sprintf(`"%s":"%s",`, k, v) + for k, v := range o.Headers { + out, _ = sjson.Set(out, fmt.Sprintf("headers.%s", k), v) } - headersOut = strings.TrimSuffix(headersOut, ",") + "}" - - kvOut := "{" - for k, v := range cache { - kvOut += fmt.Sprintf(`"%s":"%s",`, k, v) + out, _ = sjson.Set(out, fmt.Sprintf("kv.%s", k), v) } - kvOut = strings.TrimSuffix(kvOut, ",") + "}" - - fmt.Printf("{\"data\":\"%s\",\"headers\":%s,\"status\":%d,\"kv\":%s,\"base64\":%t}", - strings.ReplaceAll(o.data, "\"", "\\\""), headersOut, o.status, kvOut, o.base64) + fmt.Println(out) - return len(o.data), nil + return len(o.Data), nil } func (o *output) WriteHeader(statusCode int) { - o.status = uint16(statusCode) + o.Status = uint16(statusCode) } -func readInput() *input { +func readInput() (*input, error) { stdin, err := io.ReadAll(os.Stdin) if err != nil { - panic(err) + return nil, err } in := &input{ - url: gjson.GetBytes(stdin, "url").String(), - method: gjson.GetBytes(stdin, "method").String(), - body: gjson.GetBytes(stdin, "body").String(), + Url: gjson.GetBytes(stdin, "url").String(), + Method: gjson.GetBytes(stdin, "method").String(), + Body: gjson.GetBytes(stdin, "body").String(), + Headers: make(map[string]string), } if gjson.GetBytes(stdin, "headers").Exists() { - in.headers = make(map[string]string) - gjson.GetBytes(stdin, "headers").ForEach(func(key, value gjson.Result) bool { - in.headers[key.String()] = value.String() + in.Headers[key.String()] = value.String() return true }) } @@ -116,39 +124,46 @@ func readInput() *input { } if gjson.GetBytes(stdin, "params").Exists() { - in.params = make(map[string]string) - gjson.GetBytes(stdin, "params").ForEach(func(key, value gjson.Result) bool { - in.params[key.String()] = value.String() + params[key.String()] = value.String() return true }) } - return in + return in, nil } -func createRequest(in *input) *http.Request { - req, err := http.NewRequest(in.method, in.url, strings.NewReader(in.body)) +func createRequest(in *input) (*http.Request, error) { + req, err := http.NewRequest(in.Method, in.Url, strings.NewReader(in.Body)) if err != nil { - panic(err) + return nil, err } - for k, v := range in.headers { + for k, v := range in.Headers { req.Header.Set(k, v) } - req = req.WithContext(context.WithValue(req.Context(), "CACHE", cache)) - req = req.WithContext(context.WithValue(req.Context(), "PARAMS", in.params)) + req = req.WithContext(context.WithValue(req.Context(), CacheKey, cache)) + req = req.WithContext(context.WithValue(req.Context(), ParamsKey, params)) - return req + return req, nil } func getWriterRequest() (*output, *http.Request) { - in := readInput() - req := createRequest(in) + in, err := readInput() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + req, err := createRequest(in) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + w := &output{ - headers: make(map[string]string), - kv: make(map[string]string), + Headers: make(map[string]string), } return w, req From d50c561d86f6f798ac4ac386a3474208a544f40d Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Tue, 30 May 2023 13:16:29 +0530 Subject: [PATCH 8/8] finish Go documentation --- docs/docs/languages/go.md | 320 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) diff --git a/docs/docs/languages/go.md b/docs/docs/languages/go.md index 6a2a00a2..9cac573c 100644 --- a/docs/docs/languages/go.md +++ b/docs/docs/languages/go.md @@ -9,3 +9,323 @@ Go workers are compiled into a WASI module using [TinyGo](https://tinygo.org/doc ## Your first Go worker Workers can be implemented either as an [http.Handler](https://pkg.go.dev/net/http#Handler) or an [http.HandlerFunc](https://pkg.go.dev/net/http#HandlerFunc). + +In this example, the worker will get a request and print all the related information. + +1. Create a new Go mod project + + ``` + go mod init workers-in-go + ``` + +1. Add the Wasm Workers Server Go dependency + + ``` + go get -u github.com/vmware-labs/wasm-workers-server/kits/go/worker + ``` + +1. Create a `worker.go` file with the following contents: + + ```go title="worker.go" + package main + + import ( + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte("Hello wasm!")) + }) + } + ``` + +1. Additionally, you can now go further add all the information from the received `http.Request`: + + ```go title="worker.go" + package main + + import ( + "fmt" + "io" + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + var payload string + + reqBody, err := io.ReadAll(r.Body) + if err != nil { + panic(err) + } + r.Body.Close() + + if len(reqBody) == 0 { + payload = "-" + } else { + payload = string(reqBody) + } + + body := fmt.Sprintf(""+ + ""+ + "Wasm Workers Server"+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + "
"+ + "

Hello from Wasm Workers Server πŸ‘‹

"+ + "
Replying to %s
"+ + "Method: %s
"+ + "User Agent: %s
"+ + "Payload: %s
"+ + "

"+ + "This page was generated by a Go file running in WebAssembly."+ + "

"+ + "
"+ + "", r.URL.String(), r.Method, r.UserAgent(), payload) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) + } + ``` + +1. In this case, you need to compile the project to Wasm ([WASI](https://wasi.dev/)). To do this, make sure you have installed the TinyGo compiler by following the steps [here](https://tinygo.org/getting-started/install/): + + ```bash + tinygo build -o worker.wasm -target wasi worker.go + ``` + +1. Run your worker with `wws`. If you didn't download the `wws` server yet, check our [Getting Started](../get-started/quickstart.md) guide. + + ```bash + wws . + + βš™οΈ Loading routes from: . + πŸ—Ί Detected routes: + - http://127.0.0.1:8080/worker + => worker.wasm (name: default) + πŸš€ Start serving requests at http://127.0.0.1:8080 + ``` + +1. Finally, open in your browser. + +## Add a Key / Value store + +Wasm Workers allows you to add a Key / Value store to your workers. Read more information about this feature in the [Key / Value store](../features/key-value.md) section. + +To add a KV store to your worker, follow these steps: + +1. Create a new Go project: + + ```bash + go mod init worker-kv + ``` + +1. Add the Wasm Workers Server Go dependency + + ``` + go get -u github.com/vmware-labs/wasm-workers-server/kits/go/worker + ``` + +1. Create a `worker-kv.go` file with the following contents: + + ```go title="worker-kv.go" + package main + + import ( + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte("Hello wasm!")) + }) + } + ``` + +1. Then, let's read a value from the cache and update it: + + ```go title="worker-kv.go" + package main + + import ( + "fmt" + "net/http" + "strconv" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + cache, _ := r.Context().Value(worker.CacheKey).(map[string]string) + + var countNum uint32 + + if count, ok := cache["counter"]; ok { + n, _ := strconv.ParseUint(count, 10, 32) + countNum = uint32(n) + } + + body := fmt.Sprintf(""+ + ""+ + "

Key / Value store in Go

"+ + "

Counter: %d

"+ + "

This page was generated by a Wasm module built from Go.

"+ + "", countNum) + + cache["counter"] = fmt.Sprintf("%d", countNum+1) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) + } + ``` + +1. Compile the project to Wasm ([WASI](https://wasi.dev/)): + + ```bash + tinygo build -o worker-kv.wasm -target wasi worker-kv.go + ``` + +1. Create a `worker-kv.toml` file with the following content. Note the name of the TOML file must match the name of the worker. In this case we have `worker-kv.wasm` and `worker-kv.toml` in the same folder: + + ```toml title="worker-kv.toml" + name = "workerkv" + version = "1" + + [data] + [data.kv] + namespace = "workerkv" + ``` + +1. Run your worker with `wws`. If you didn't download the `wws` server yet, check our [Getting Started](../get-started/quickstart.md) guide. + + ```bash + wws . + + βš™οΈ Loading routes from: . + πŸ—Ί Detected routes: + - http://127.0.0.1:8080/worker-kv + => worker-kv.wasm (name: default) + πŸš€ Start serving requests at http://127.0.0.1:8080 + ``` + +1. Finally, open in your browser. + +## Dynamic routes + +You can define [dynamic routes by adding route parameters to your worker files](../features/dynamic-routes.md) (like `[id].wasm`). To read them in Go, follow these steps: + +1. Use the `worker.ParamsKey` context value to read in the passed in parameters: + + ```go title="main.go" + package main + + import ( + "fmt" + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + params, _ := r.Context().Value(worker.ParamsKey).(map[string]string) + ... + }) + } + ``` + +1. Then, you can read the values as follows: + + ```go title="main.go" + package main + + import ( + "fmt" + "net/http" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" + ) + + func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + params, _ := r.Context().Value(worker.ParamsKey).(map[string]string) + id := "the value is not available" + + if val, ok := params["id"]; ok { + id = val + } + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(fmt.Sprintf("Hey! The parameter is: %s", id))) + }) + } + ``` + +## Read environment variables + +Environment variables are configured [via the related TOML configuration file](../features/environment-variables.md). These variables are accessible via `os.Getenv` in your worker. To read them, just use the same name you configured in your TOML file: + +```toml title="envs.toml" +name = "envs" +version = "1" + +[vars] +MESSAGE = "Hello πŸ‘‹! This message comes from an environment variable" +``` + +Now, you can read the `MESSAGE` variable using the [`os.Getenv`](https://pkg.go.dev/os#Getenv) function: + +```go title="envs.go" +package main + +import ( + "fmt" + "net/http" + "os" + + "github.com/vmware-labs/wasm-workers-server/kits/go/worker" +) + +func main() { + worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) { + body := fmt.Sprintf("The message is: %s", os.Getenv("MESSAGE")) + + w.Header().Set("x-generated-by", "wasm-workers-server") + w.Write([]byte(body)) + }) +} + +``` + +If you prefer, you can configure the environment variable value dynamically by following [these instructions](../features/environment-variables.md#inject-existing-environment-variables). + +## Other examples + +* [Basic](https://github.com/vmware-labs/wasm-workers-server/tree/main/examples/go-basic) +* [Counter](https://github.com/vmware-labs/wasm-workers-server/tree/main/examples/go-kv) + +The Go kit was originally authored by Mohammed Nafees ([@mnafees](https://github.com/mnafees))