diff --git a/cloudflare/fetch/client.go b/cloudflare/fetch/client.go new file mode 100644 index 0000000..4b9c573 --- /dev/null +++ b/cloudflare/fetch/client.go @@ -0,0 +1,20 @@ +package fetch + +import ( + "syscall/js" + + "github.com/syumai/workers/internal/jsutil" +) + +// Client is an HTTP client. +type Client struct { + // namespace - Objects that Fetch API belongs to. Default is Global + namespace js.Value +} + +// NewClient returns new Client +func NewClient() *Client { + return &Client{ + namespace: jsutil.Global, + } +} diff --git a/cloudflare/fetch/http.go b/cloudflare/fetch/http.go new file mode 100644 index 0000000..35a8faa --- /dev/null +++ b/cloudflare/fetch/http.go @@ -0,0 +1,22 @@ +package fetch + +import ( + "net/http" + + "github.com/syumai/workers/internal/jshttp" + "github.com/syumai/workers/internal/jsutil" +) + +// Do sends an HTTP request and returns an HTTP response +func (c *Client) Do(req *Request) (*http.Response, error) { + jsReq := jshttp.ToJSRequest(req.Request) + + init := jsutil.NewObject() + promise := c.namespace.Call("fetch", jsReq, init) + jsRes, err := jsutil.AwaitPromise(promise) + if err != nil { + return nil, err + } + + return jshttp.ToResponse(jsRes) +} diff --git a/cloudflare/fetch/request.go b/cloudflare/fetch/request.go new file mode 100644 index 0000000..a52eba9 --- /dev/null +++ b/cloudflare/fetch/request.go @@ -0,0 +1,25 @@ +package fetch + +import ( + "context" + "io" + "net/http" +) + +// Request represents an HTTP request and is part of the Fetch API. +// Docs: https://developers.cloudflare.com/workers/runtime-apis/request/ +type Request struct { + *http.Request +} + +// NewRequest returns new Request given a method, URL, and optional body +func NewRequest(ctx context.Context, method string, url string, body io.Reader) (*Request, error) { + req, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + return nil, err + } + + return &Request{ + Request: req, + }, nil +} diff --git a/examples/fetch/.gitignore b/examples/fetch/.gitignore new file mode 100644 index 0000000..53c37a1 --- /dev/null +++ b/examples/fetch/.gitignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/examples/fetch/Makefile b/examples/fetch/Makefile new file mode 100644 index 0000000..320ddc3 --- /dev/null +++ b/examples/fetch/Makefile @@ -0,0 +1,12 @@ +.PHONY: dev +dev: + wrangler dev + +.PHONY: build +build: + mkdir -p dist + tinygo build -o ./dist/app.wasm -target wasm ./... + +.PHONY: publish +publish: + wrangler publish diff --git a/examples/fetch/README.md b/examples/fetch/README.md new file mode 100644 index 0000000..bfda689 --- /dev/null +++ b/examples/fetch/README.md @@ -0,0 +1,21 @@ +# fetch + +* Get the latest release of this repository from the Github API. +* (wip) + +## Development + +### Requirements + +This project requires these tools to be installed globally. + +* wrangler +* tinygo + +### Commands + +``` +make dev # run dev server +make build # build Go Wasm binary +make publish # publish worker +``` \ No newline at end of file diff --git a/examples/fetch/go.mod b/examples/fetch/go.mod new file mode 100644 index 0000000..eb5269f --- /dev/null +++ b/examples/fetch/go.mod @@ -0,0 +1,7 @@ +module github.com/syumai/fetch + +go 1.18 + +require github.com/syumai/workers v0.0.0 + +replace github.com/syumai/workers => ../../ \ No newline at end of file diff --git a/examples/fetch/go.sum b/examples/fetch/go.sum new file mode 100644 index 0000000..8c27871 --- /dev/null +++ b/examples/fetch/go.sum @@ -0,0 +1,2 @@ +github.com/syumai/workers v0.1.0 h1:z5QfQR2X+PCKzom7RodpI5J4D5YF7NT7Qwzb9AM9dgY= +github.com/syumai/workers v0.1.0/go.mod h1:alXIDhTyeTwSzh0ZgQ3cb9HQPyyYfIejupE4Z3efr14= diff --git a/examples/fetch/main.go b/examples/fetch/main.go new file mode 100644 index 0000000..99bec46 --- /dev/null +++ b/examples/fetch/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "io" + "net/http" + + "github.com/syumai/workers" + "github.com/syumai/workers/cloudflare/fetch" +) + +func main() { + handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + cli := fetch.NewClient() + + r, err := fetch.NewRequest(req.Context(), http.MethodGet, "https://api.github.com/repos/syumai/workers/releases/latest", nil) + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + r.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0") + + res, err := cli.Do(r) + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("content-type", "application/json") + io.Copy(w, res.Body) + }) + workers.Serve(handler) +} diff --git a/examples/fetch/worker.mjs b/examples/fetch/worker.mjs new file mode 100644 index 0000000..649ccf0 --- /dev/null +++ b/examples/fetch/worker.mjs @@ -0,0 +1,22 @@ +import "../assets/polyfill_performance.js"; +import "../assets/wasm_exec.js"; +import mod from "./dist/app.wasm"; + +const go = new Go(); + +const readyPromise = new Promise((resolve) => { + globalThis.ready = resolve; +}); + +const load = WebAssembly.instantiate(mod, go.importObject).then((instance) => { + go.run(instance); + return instance; +}); + +export default { + async fetch(req, env, ctx) { + await load; + await readyPromise; + return handleRequest(req, { env, ctx }); + } +} diff --git a/examples/fetch/wrangler.toml b/examples/fetch/wrangler.toml new file mode 100644 index 0000000..7e607bb --- /dev/null +++ b/examples/fetch/wrangler.toml @@ -0,0 +1,6 @@ +name = "fetch" +main = "./worker.mjs" +compatibility_date = "2023-02-24" + +[build] +command = "make build"