Skip to content

WIP: support GOOS=wasip1 #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ testdata:
@$(MAKE) build.wat
@$(MAKE) build.tinygo

tinygo_sources := $(wildcard examples/*/*.go) $(wildcard internal/test/testdata/*/*.go) $(wildcard internal/test/testdata/*/*/*.go) $(wildcard internal/test/testdata/*/*/*/*.go)
tinygo_sources := $(wildcard examples/*/*/*.go) $(wildcard internal/test/testdata/*/*.go) $(wildcard internal/test/testdata/*/*/*.go) $(wildcard internal/test/testdata/*/*/*/*.go)
build.tinygo: $(tinygo_sources)
@for f in $^; do \
tinygo build -o $$(echo $$f | sed -e 's/\.go/\.wasm/') -scheduler=none --no-debug -target=wasi $$f; \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func handleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32

If you make changes, you can rebuild this with TinyGo v0.28 or higher like so:
```sh
tinygo build -o examples/router/main.wasm -scheduler=none --no-debug -target=wasi examples/router/main.go
tinygo build -o examples/router/once.wasm -scheduler=none --no-debug -target=wasi examples/router/once.go
```

There are also more [examples](examples) you may wish to try out!
Expand Down
Binary file removed examples/router/main.wasm
Binary file not shown.
10 changes: 10 additions & 0 deletions examples/router/once/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/http-wasm/http-wasm-guest-tinygo/examples/router"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/once"
)

func main() {
once.Handle(router.Handler{})
}
Binary file added examples/router/once/main.wasm
Binary file not shown.
10 changes: 10 additions & 0 deletions examples/router/pool/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/http-wasm/http-wasm-guest-tinygo/examples/router"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/pool"
)

func main() {
pool.SetHandler(router.Handler{})
}
Binary file added examples/router/pool/main.wasm
Binary file not shown.
12 changes: 6 additions & 6 deletions examples/router/main.go → examples/router/router.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package main
package router

import (
"strings"

"github.com/http-wasm/http-wasm-guest-tinygo/handler"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

func main() {
handler.HandleRequestFn = handleRequest
// Handler implements a simple HTTP router.
type Handler struct {
api.UnimplementedHandler
}

// handleRequest implements a simple HTTP router.
func handleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32) {
// HandleRequest implements the same method as documented on api.Handler
func (Handler) HandleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32) {
// If the URI starts with /host, trim it and dispatch to the next handler.
if uri := req.GetURI(); strings.HasPrefix(uri, "/host") {
req.SetURI(uri[5:])
Expand Down
22 changes: 12 additions & 10 deletions examples/wasi/main.go → examples/wasi/log.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package main
package log

import (
"io"
"os"
"strconv"
"strings"

httpwasm "github.com/http-wasm/http-wasm-guest-tinygo/handler"
"github.com/http-wasm/http-wasm-guest-tinygo/handler"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

// main ensures buffering is available on the host.
// init ensures buffering is available on the host.
//
// Note: required features does not include api.FeatureTrailers because some
// hosts don't support them, and the impact is minimal for logging.
func main() {
func init() {
requiredFeatures := api.FeatureBufferRequest | api.FeatureBufferResponse
if want, have := requiredFeatures, httpwasm.Host.EnableFeatures(requiredFeatures); !have.IsEnabled(want) {
if want, have := requiredFeatures, handler.Host.EnableFeatures(requiredFeatures); !have.IsEnabled(want) {
panic("unexpected features, want: " + want.String() + ", have: " + have.String())
}
httpwasm.HandleRequestFn = handleRequest
httpwasm.HandleResponseFn = handleResponse
}

// handleRequest prints HTTP requests and responses to the console using os.Stdout.
// Handler prints HTTP requests and responses to the console using os.Stdout.
//
// Note: Internally, TinyGo uses WASI to implement os.Stdout. For example,
// writing is a call to the imported function `fd_write`.
func handleRequest(req api.Request, _ api.Response) (next bool, reqCtx uint32) {
type Handler struct{}

// HandleRequest implements the same method as documented on api.Handler
func (Handler) HandleRequest(req api.Request, _ api.Response) (next bool, reqCtx uint32) {
// Print the incoming request to the console.
printRequestLine(req)
printHeaders(req.Headers())
Expand All @@ -38,7 +39,8 @@ func handleRequest(req api.Request, _ api.Response) (next bool, reqCtx uint32) {
return
}

func handleResponse(_ uint32, req api.Request, resp api.Response, isError bool) {
// HandleResponse implements the same method as documented on api.Handler
func (Handler) HandleResponse(_ uint32, req api.Request, resp api.Response, isError bool) {
println()

if isError {
Expand Down
Binary file removed examples/wasi/main.wasm
Binary file not shown.
10 changes: 10 additions & 0 deletions examples/wasi/once/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
log "github.com/http-wasm/http-wasm-guest-tinygo/examples/wasi"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/once"
)

func main() {
once.Handle(log.Handler{})
}
Binary file added examples/wasi/once/main.wasm
Binary file not shown.
10 changes: 10 additions & 0 deletions examples/wasi/pool/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
log "github.com/http-wasm/http-wasm-guest-tinygo/examples/wasi"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/pool"
)

func main() {
pool.SetHandler(log.Handler{})
}
Binary file added examples/wasi/pool/main.wasm
Binary file not shown.
105 changes: 65 additions & 40 deletions handler/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,47 +48,72 @@ type Host interface {
Log(LogLevel, string)
}

// HandleRequest is the entrypoint the Host calls when processing an HTTP
// request. Default is to proceed to the next handler on the host.
//
// Implementations that construct a response locally should set `next=false`.
// Otherwise, set `next=true` to proceed to the next handler on the host.
// Handlers that require request correlation can optionally set reqCtx.
// The host will propagate this value as the first parameter to HandleResponse.
//
// Here are some examples:
//
// Modify the incoming request:
//
// func router(req Request, _ Response) (next bool, reqCtx uint32) {
// if req.GetURI() == "/v1.0/hi?name=panda" {
// req.SetURI("/v1.0/hello?name=teddy")
// }
// next = true
// return
// }
//
// Ex. To serve a response locally:
//
// func hello(req Request, resp Response) (next bool, reqCtx uint32) {
// resp.Body().WriteString("hello world")
// }
//
// Return an empty 200 response
//
// func noop(Request, Response) (next bool, reqCtx uint32) { }
//
// # Request Context
//
// Implementations who return `next=true` may also set request correlation
// data as reqCtx. The host will propagate this to HandleResponse.
type HandleRequest func(Request, Response) (next bool, reqCtx uint32)
// Handler is what implements the HTTP request/response lifecycle.
type Handler interface {
// HandleRequest is the entrypoint the Host calls when processing an HTTP
// request. Default is to proceed to the next handler on the host.
//
// Implementations that construct a response locally should set `next=false`.
// Otherwise, set `next=true` to proceed to the next handler on the host.
// Handlers that require request correlation can optionally set reqCtx.
// The host will propagate this value as the first parameter to HandleResponse.
//
// Here are some examples:
//
// Modify the incoming request:
//
// type router struct {
// api.UnimplementedHandler
// }
//
// func (router) HandleRequest(req api.Request, _ api.Response) (next bool, reqCtx uint32) {
// if req.GetURI() == "/v1.0/hi?name=panda" {
// req.SetURI("/v1.0/hello?name=teddy")
// }
// next = true
// return
// }
//
// Ex. To serve a response locally:
//
// type hello struct {
// api.UnimplementedHandler
// }
//
// func (hello) HandleRequest(_ api.Request, resp api.Response) (next bool, reqCtx uint32) {
// resp.Body().WriteString("hello world")
// }
//
// Return an empty 200 response
//
// type noop struct {
// api.UnimplementedHandler
// }
//
// func (noop) HandleRequest(api.Request, api.Response) (next bool, reqCtx uint32) { }
//
// # Request Context
//
// Implementations who return `next=true` may also set request correlation
// data as reqCtx. The host will propagate this to HandleResponse.
HandleRequest(Request, Response) (next bool, reqCtx uint32)

// HandleResponse is invoked when HandleRequest returned `next=true`. Its
// possibly zero `reqCtx` result is propagated here.
//
// `isError=true` when the host erred since returning from HandleRequest.
type HandleResponse func(reqCtx uint32, req Request, resp Response, isError bool)
// HandleResponse is invoked when HandleRequest returned `next=true`. Its
// possibly zero `reqCtx` result is propagated here.
//
// `isError=true` when the host erred since returning from HandleRequest.
HandleResponse(reqCtx uint32, req Request, resp Response, isError bool)
}

// UnimplementedHandler calls the next handler.
type UnimplementedHandler struct{}

func (UnimplementedHandler) HandleRequest(Request, Response) (next bool, reqCtx uint32) {
next = true
return
}

func (UnimplementedHandler) HandleResponse(uint32, Request, Response, bool) {}

// Request is the incoming HTTP request sent by the client or an upstream
// handler.
Expand Down
42 changes: 0 additions & 42 deletions handler/handler.go

This file was deleted.

8 changes: 8 additions & 0 deletions handler/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import (
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/mem"
)

// Host is the current host that invokes HandleRequestFn.
var Host api.Host = wasmHost{}

// Log is a convenience that calls Host then Log.
func Log(level api.LogLevel, msg string) {
Host.Log(level, msg)
}

// wasmHost implements api.Host with imported WebAssembly functions.
type wasmHost struct{}

Expand Down
2 changes: 1 addition & 1 deletion handler/body.go → handler/internal/body.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package handler
package internal

import (
"io"
Expand Down
2 changes: 1 addition & 1 deletion handler/header.go → handler/internal/header.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package handler
package internal

import (
"runtime"
Expand Down
2 changes: 1 addition & 1 deletion handler/internal/imports/imports.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build tinygo.wasm
//go:build tinygo.wasm || wasip1

package imports

Expand Down
2 changes: 1 addition & 1 deletion handler/internal/imports/imports_stub.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !tinygo.wasm
//go:build !(tinygo.wasm || wasip1)

package imports

Expand Down
7 changes: 3 additions & 4 deletions handler/request.go → handler/internal/request.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package handler
package internal

import (
"runtime"
Expand All @@ -8,12 +8,11 @@ import (
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/mem"
)

var WasmRequest api.Request = wasmRequest{}

// wasmRequest implements api.Request with imported WebAssembly functions.
type wasmRequest struct{}

// compile-time check to ensure wasmRequest implements api.Request.
var _ api.Request = wasmRequest{}

// GetMethod implements the same method as documented on api.Request.
func (wasmRequest) GetMethod() string {
return mem.GetString(imports.GetMethod)
Expand Down
7 changes: 3 additions & 4 deletions handler/response.go → handler/internal/response.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package handler
package internal

import (
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/imports"
)

var WasmResponse api.Response = wasmResponse{}

// wasmResponse implements api.Response with imported WebAssembly functions.
type wasmResponse struct{}

// compile-time check to ensure wasmResponse implements api.Response.
var _ api.Response = wasmResponse{}

// GetStatusCode implements the same method as documented on api.Response.
func (r wasmResponse) GetStatusCode() uint32 {
return imports.GetStatusCode()
Expand Down
22 changes: 22 additions & 0 deletions handler/once/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Package once uses an api.Handler for only one HTTP round trip.
package once

import (
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal"
)

// Handle invokes the handler and returns
func Handle(handler api.Handler) {
next, reqCtx := handler.HandleRequest(internal.WasmRequest, internal.WasmResponse)
ctxNext := uint64(reqCtx) << 32
if next {
ctxNext |= 1
}
isError := awaitResponse(ctxNext)
isErrorB := false
if isError == 1 {
isErrorB = true
}
handler.HandleResponse(reqCtx, internal.WasmRequest, internal.WasmResponse, isErrorB)
}
Loading