diff --git a/imports/wasi_http/README.md b/imports/wasi_http/README.md index c4ac022..e5d4e2f 100644 --- a/imports/wasi_http/README.md +++ b/imports/wasi_http/README.md @@ -4,6 +4,29 @@ The specification is in active development/flux as is the [`wit-bindgen`](https: You should expect a degree of instability in these interfaces for the foreseeable future. +## Versioning & Clients +WASI-http doesn't have a great versioning story currently, so explicitly tying to git commit SHA and +`wit-bindgen` tooling is the best we can do. + +Currently, the `wasi-go` codebase has the following versions: +* `v1` corresponds to [244e068c2d](https://github.com/WebAssembly/wasi-http/tree/244e068c2de43088bda308fcdf51ed2479d885f5) and [`wasmtime` 9.0.0](https://github.com/bytecodealliance/wasmtime/tree/release-9.0.0) + +If you want to generate guest code for your particular language, you will need to use [`wit-bindgen` 0.4.0](https://github.com/bytecodealliance/wit-bindgen/releases/tag/wit-bindgen-cli-0.4.0) + +Here is an example for generating the Golang guest bindings: + +```sh +# Setup +git clone https://github.com/WebAssembly/wasi-http +cd wasi-http +# Sync to the definitions for the v1 +git checkout 244e068 +cd .. + +# Generate +wit-bindgen tiny-go wasi-http/wit -w proxy +``` + ## Example guest code There are existing examples of working guest code in the following languages * [Golang](https://github.com/dev-wasm/dev-wasm-go/tree/main/http) diff --git a/imports/wasi_http/http_test.go b/imports/wasi_http/http_test.go index 945b8a3..1306a88 100644 --- a/imports/wasi_http/http_test.go +++ b/imports/wasi_http/http_test.go @@ -3,7 +3,7 @@ package wasi_http import ( "context" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "os" @@ -19,13 +19,15 @@ import ( ) type handler struct { - urls []string - bodies []string + urls []string + bodies []string + methods []string } func (h *handler) reset() { h.bodies = []string{} h.urls = []string{} + h.methods = []string{} } func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) { @@ -33,7 +35,7 @@ func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) { if req.Body != nil { defer req.Body.Close() - data, err := ioutil.ReadAll(req.Body) + data, err := io.ReadAll(req.Body) if err != nil { panic(err.Error()) } @@ -45,15 +47,13 @@ func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) { h.urls = append(h.urls, req.URL.String()) h.bodies = append(h.bodies, body) + h.methods = append(h.methods, req.Method) } func TestHttpClient(t *testing.T) { - filePaths, _ := filepath.Glob("../../testdata/c/http/http*.wasm") - for _, file := range filePaths { - fmt.Printf("%v\n", file) - } + filePaths, _ := filepath.Glob("../../testdata/*/http/http*.wasm") if len(filePaths) == 0 { - t.Log("nothing to test") + t.Error("nothing to test") } h := handler{} @@ -64,6 +64,12 @@ func TestHttpClient(t *testing.T) { { "/get?some=arg&goes=here", "/post", + "/put", + }, + { + "/get?some=arg&goes=here", + "/post", + "/put", }, } @@ -71,6 +77,25 @@ func TestHttpClient(t *testing.T) { { "", "{\"foo\": \"bar\"}", + "{\"baz\": \"blah\"}", + }, + { + "", + "{\"foo\": \"bar\"}", + "{\"baz\": \"blah\"}", + }, + } + + expectedMethods := [][]string{ + { + "GET", + "POST", + "PUT", + }, + { + "GET", + "POST", + "PUT", }, } @@ -128,6 +153,9 @@ func TestHttpClient(t *testing.T) { if !reflect.DeepEqual(expectedBodies[testIx], h.bodies) { t.Errorf("Unexpected paths: %v vs %v", h.bodies, expectedBodies[testIx]) } + if !reflect.DeepEqual(expectedMethods[testIx], h.methods) { + t.Errorf("Unexpected paths: %v vs %v", h.methods, expectedMethods[testIx]) + } h.reset() }) @@ -135,12 +163,9 @@ func TestHttpClient(t *testing.T) { } func TestServer(t *testing.T) { - filePaths, _ := filepath.Glob("../../testdata/c/http/server*.wasm") - for _, file := range filePaths { - fmt.Printf("%v\n", file) - } + filePaths, _ := filepath.Glob("../../testdata/*/http/server*.wasm") if len(filePaths) == 0 { - t.Log("nothing to test") + t.Error("nothing to test") } for _, test := range filePaths { @@ -192,12 +217,12 @@ func TestServer(t *testing.T) { for i := 0; i < 3; i++ { res, err := http.Get(s.URL) if err != nil { - t.Error("Failed to read from server.") + t.Errorf("Failed to read from server: %s", err.Error()) continue } defer res.Body.Close() - data, err := ioutil.ReadAll(res.Body) + data, err := io.ReadAll(res.Body) if err != nil { t.Error("Failed to read body.") continue diff --git a/imports/wasi_http/types/request.go b/imports/wasi_http/types/request.go index 5bd35b2..a764c0e 100644 --- a/imports/wasi_http/types/request.go +++ b/imports/wasi_http/types/request.go @@ -25,7 +25,11 @@ type Request struct { } func (r Request) Url() string { - return fmt.Sprintf("%s://%s%s%s", r.Scheme, r.Authority, r.Path, r.Query) + u := fmt.Sprintf("%s://%s%s", r.Scheme, r.Authority, r.Path) + if len(r.Query) > 0 { + u = u + "?" + r.Query + } + return u } type Requests struct { diff --git a/testdata/c/http/Makefile b/testdata/c/http/Makefile index acf2285..02118ba 100644 --- a/testdata/c/http/Makefile +++ b/testdata/c/http/Makefile @@ -4,3 +4,7 @@ default: http.wasm http.wasm: http.c ${cc} proxy.c proxy_component_type.o http.c -o http.wasm + +server.wasm: server.c + ${cc} proxy.c proxy_component_type.o server.c -o server.wasm + diff --git a/testdata/c/http/http.c b/testdata/c/http/http.c index d0a0356..ee50479 100644 --- a/testdata/c/http/http.c +++ b/testdata/c/http/http.c @@ -117,7 +117,7 @@ int request(uint8_t method_tag, uint8_t scheme_tag, const char * authority_str, int main() { const char *authority = getenv("SERVER"); - int r = request(TYPES_METHOD_GET, TYPES_SCHEME_HTTP, authority, "/get", "?some=arg&goes=here", NULL); + int r = request(TYPES_METHOD_GET, TYPES_SCHEME_HTTP, authority, "/get", "some=arg&goes=here", NULL); if (r != 0) { return r; } @@ -125,5 +125,9 @@ int main() { if (r != 0) { return r; } + r = request(TYPES_METHOD_PUT, TYPES_SCHEME_HTTP, authority, "/put", "", "{\"baz\": \"blah\"}"); + if (r != 0) { + return r; + } return 0; } diff --git a/testdata/c/http/http.wasm b/testdata/c/http/http.wasm index bc4cdeb..0b3e9f8 100755 Binary files a/testdata/c/http/http.wasm and b/testdata/c/http/http.wasm differ diff --git a/testdata/tinygo/http/go.mod b/testdata/tinygo/http/go.mod new file mode 100644 index 0000000..0702f72 --- /dev/null +++ b/testdata/tinygo/http/go.mod @@ -0,0 +1,5 @@ +module github.com/stealthrocket/wasi-go/testdata/tinygo/http + +go 1.20 + +require github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd // indirect diff --git a/testdata/tinygo/http/go.sum b/testdata/tinygo/http/go.sum new file mode 100644 index 0000000..8f44c05 --- /dev/null +++ b/testdata/tinygo/http/go.sum @@ -0,0 +1,2 @@ +github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd h1:fLCnhqCMaCHXhA6Ozb7sfTyqt/g5mp1dHOjbHo/PacM= +github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd/go.mod h1:K7HKbanDfLH3UL/hvExI1hVPetqnn9OZ4IOFcD5Pzw0= diff --git a/testdata/tinygo/http/http.go b/testdata/tinygo/http/http.go new file mode 100644 index 0000000..f57c94c --- /dev/null +++ b/testdata/tinygo/http/http.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + + wasiclient "github.com/dev-wasm/dev-wasm-go/http/client" +) + +func printResponse(r *http.Response) { + fmt.Printf("Status: %d\n", r.StatusCode) + for k, v := range r.Header { + fmt.Printf("%s: %s\n", k, v[0]) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + fmt.Println(err.Error()) + os.Exit(4) + } + fmt.Printf("Body: \n%s\n", body) +} + +func main() { + server := os.Getenv("SERVER") + client := http.Client{ + Transport: wasiclient.WasiRoundTripper{}, + } + res, err := client.Get("http://" + server + "/get?some=arg&goes=here") + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + printResponse(res) + res.Body.Close() + + res, err = client.Post("http://"+server+"/post", "application/json", wasiclient.BodyReaderCloser([]byte("{\"foo\": \"bar\"}"))) + if err != nil { + fmt.Println(err.Error()) + os.Exit(2) + } + printResponse(res) + res.Body.Close() + + res, err = wasiclient.Put(&client, "http://"+server+"/put", "application/json", wasiclient.BodyReaderCloser([]byte("{\"baz\": \"blah\"}"))) + if err != nil { + fmt.Println(err.Error()) + os.Exit(3) + } + printResponse(res) + res.Body.Close() + + os.Exit(0) +} diff --git a/testdata/tinygo/http/http.wasm b/testdata/tinygo/http/http.wasm new file mode 100755 index 0000000..fa8a2b3 Binary files /dev/null and b/testdata/tinygo/http/http.wasm differ diff --git a/testdata/tinygo/http/server.go b/testdata/tinygo/http/server.go new file mode 100644 index 0000000..e66abb1 --- /dev/null +++ b/testdata/tinygo/http/server.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/dev-wasm/dev-wasm-go/http/server" +) + +var count int = 0 + +func main() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Server", "Wasirun 0.0.1") + w.WriteHeader(200) + body := fmt.Sprintf("Hello from WASM! (%d)", count) + count = count + 1 + w.Write([]byte(body)) + }) + server.ListenAndServe(nil) +} diff --git a/testdata/tinygo/http/server.wasm b/testdata/tinygo/http/server.wasm new file mode 100755 index 0000000..8ab56ca Binary files /dev/null and b/testdata/tinygo/http/server.wasm differ