add runtime context

This commit is contained in:
syumai 2023-02-11 10:30:51 +09:00
parent 0e499ad79c
commit 9e87698fac
6 changed files with 88 additions and 13 deletions

View File

@ -1,10 +1,12 @@
package cloudflare
import "github.com/syumai/workers/internal/jsutil"
import (
"context"
)
// Getenv gets a value of an environment variable.
// - https://developers.cloudflare.com/workers/platform/environment-variables/
// - Technically, this function is just an alias for js.Global().Get(env_name).String().
func Getenv(name string) string {
return jsutil.Global.Get(name).String()
// - This function panics when a runtime context is not found.
func Getenv(ctx context.Context, name string) string {
return getRuntimeContextEnv(ctx).Get(name).String()
}

View File

@ -1,6 +1,7 @@
package cloudflare
import (
"context"
"fmt"
"io"
"syscall/js"
@ -17,9 +18,10 @@ type KVNamespace struct {
// NewKVNamespace returns KVNamespace for given variable name.
// - variable name must be defined in wrangler.toml as kv_namespace's binding.
// - if the given variable name doesn't exist on Global object, returns error.
func NewKVNamespace(varName string) (*KVNamespace, error) {
inst := js.Global().Get(varName)
// - if the given variable name doesn't exist on runtime context, returns error.
// - This function panics when a runtime context is not found.
func NewKVNamespace(ctx context.Context, varName string) (*KVNamespace, error) {
inst := getRuntimeContextEnv(ctx).Get(varName)
if inst.IsUndefined() {
return nil, fmt.Errorf("%s is undefined", varName)
}

View File

@ -1,6 +1,7 @@
package cloudflare
import (
"context"
"fmt"
"io"
"syscall/js"
@ -18,9 +19,10 @@ type R2Bucket struct {
// NewR2Bucket returns R2Bucket for given variable name.
// - variable name must be defined in wrangler.toml.
// - see example: https://github.com/syumai/workers/tree/main/examples/r2-image-viewer
// - if the given variable name doesn't exist on Global object, returns error.
func NewR2Bucket(varName string) (*R2Bucket, error) {
inst := js.Global().Get(varName)
// - if the given variable name doesn't exist on runtime context, returns error.
// - This function panics when a runtime context is not found.
func NewR2Bucket(ctx context.Context, varName string) (*R2Bucket, error) {
inst := getRuntimeContextEnv(ctx).Get(varName)
if inst.IsUndefined() {
return nil, fmt.Errorf("%s is undefined", varName)
}

View File

@ -0,0 +1,35 @@
package cloudflare
import (
"context"
"syscall/js"
"github.com/syumai/workers/internal/runtimecontext"
)
/**
* The type definition of RuntimeContext for Cloudflare Worker expects:
* ```ts
* type RuntimeContext {
* env: Env;
* ctx: ExecutionContext;
* }
* ```
* This type is based on the type definition of ExportedHandlerFetchHandler.
* - see: https://github.com/cloudflare/workers-types/blob/c8d9533caa4415c2156d2cf1daca75289d01ae70/index.d.ts#LL564
*/
// getRuntimeContextEnv gets object which holds environment variables bound to Cloudflare worker.
// - see: https://github.com/cloudflare/workers-types/blob/c8d9533caa4415c2156d2cf1daca75289d01ae70/index.d.ts#L566
func getRuntimeContextEnv(ctx context.Context) js.Value {
runtimeCtxValue := runtimecontext.MustExtract(ctx)
return runtimeCtxValue.Get("env")
}
// getExecutionContext gets ExecutionContext object from context.
// - see: https://github.com/cloudflare/workers-types/blob/c8d9533caa4415c2156d2cf1daca75289d01ae70/index.d.ts#L567
// - see also: https://github.com/cloudflare/workers-types/blob/c8d9533caa4415c2156d2cf1daca75289d01ae70/index.d.ts#L554
func getExecutionContext(ctx context.Context) js.Value {
runtimeCtxValue := runtimecontext.MustExtract(ctx)
return runtimeCtxValue.Get("ctx")
}

View File

@ -1,12 +1,14 @@
package workers
import (
"context"
"fmt"
"io"
"net/http"
"syscall/js"
"github.com/syumai/workers/internal/jsutil"
"github.com/syumai/workers/internal/runtimecontext"
)
var httpHandler http.Handler
@ -14,15 +16,20 @@ var httpHandler http.Handler
func init() {
var handleRequestCallback js.Func
handleRequestCallback = js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 1 {
if len(args) > 2 {
panic(fmt.Errorf("too many args given to handleRequest: %d", len(args)))
}
reqObj := args[0]
runtimeCtxObj := js.Null()
if len(args) > 1 {
runtimeCtxObj = args[1]
}
var cb js.Func
cb = js.FuncOf(func(_ js.Value, pArgs []js.Value) any {
defer cb.Release()
resolve := pArgs[0]
go func() {
res, err := handleRequest(args[0])
res, err := handleRequest(reqObj, runtimeCtxObj)
if err != nil {
panic(err)
}
@ -36,7 +43,7 @@ func init() {
}
// handleRequest accepts a Request object and returns Response object.
func handleRequest(reqObj js.Value) (js.Value, error) {
func handleRequest(reqObj js.Value, runtimeCtxObj js.Value) (js.Value, error) {
if httpHandler == nil {
return js.Value{}, fmt.Errorf("Serve must be called before handleRequest.")
}
@ -44,6 +51,8 @@ func handleRequest(reqObj js.Value) (js.Value, error) {
if err != nil {
panic(err)
}
ctx := runtimecontext.New(context.Background(), runtimeCtxObj)
req = req.WithContext(ctx)
reader, writer := io.Pipe()
w := &responseWriterBuffer{
header: http.Header{},

View File

@ -0,0 +1,25 @@
package runtimecontext
import (
"context"
"errors"
"syscall/js"
)
type runtimeCtxKey struct{}
func New(ctx context.Context, runtimeCtxObj js.Value) context.Context {
return context.WithValue(ctx, runtimeCtxKey{}, runtimeCtxObj)
}
var ErrRuntimeContextNotFound = errors.New("runtime context was not found")
// MustExtract extracts runtime context object from context.
// This function panics when runtime context object was not found.
func MustExtract(ctx context.Context) js.Value {
v, ok := ctx.Value(runtimeCtxKey{}).(js.Value)
if !ok {
panic(ErrRuntimeContextNotFound)
}
return v
}