Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
*.dll
*.so
*.dylib
*.wasm

# Scratch directory used by regenerate_bindings.sh
tmp/

# Test binary, built with `go test -c`
*.test
Expand Down
8 changes: 8 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: "2"

linters:
exclusions:
# The wit-bindgen header ("Generated by `wit-bindgen` ... DO NOT EDIT!")
# doesn't match the strict Go convention regex, so use the lenient
# heuristic to skip the generated bindings in imports/ and exports/.
generated: lenient
110 changes: 106 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div align="center">
<h1><code>go-pkg</code></h1>
<p>
<strong>Golang packages for the Bytecode Alliance <a href="https://github.com/bytecodealliance/componentize-go">componentize-go</a> project</strong>
<strong>The Go library for building WebAssembly components with <a href="https://github.com/bytecodealliance/componentize-go">componentize-go</a></strong>
</p>
<strong>A <a href="https://bytecodealliance.org/">Bytecode Alliance</a> project</strong>
<p>
Expand All @@ -15,13 +15,115 @@

# Overview

This is a set of Golang packages for the Bytecode Alliance componentize-go project.
Module `go.bytecodealliance.org/pkg` is the Go library for Wasm components. It adapts standard-library interfaces (`net/http`, `log/slog`) to standard `wasi:*` interfaces and ships the committed bindings and WIT worlds needed to build HTTP components with [componentize-go](https://github.com/bytecodealliance/componentize-go). The use of this package significantly reduces the number of files generated and committed for a typical go application.

The library targets two worlds defined in [`wit/world.wit`](./wit/world.wit):

- **`bytecodealliance:pkg/wasip2`** (default): a sync WASI P2 component exporting `wasi:http/incoming-handler@0.2.8`, buildable with stock Go.
- **`bytecodealliance:pkg/wasip3`** (opt-in): an async WASI P3 component exporting `wasi:http/handler@0.3.0` with streaming bodies and native concurrency.

## Packages

| Package | Description |
| --- | --- |
| `wasihttp` | `net/http` adapter for `wasi:http`: serve incoming requests with a standard `http.Handler` and send outbound requests through an `http.RoundTripper`. One API, two implementations selected by build tag (see below). |
| `wasilog` | `slog.Handler` implementation over `wasi:logging`. |
| `wasiconfig` | Helpers over `wasi:config/store`. |
| `wit/types`, `wit/runtime`, `wit/async` | Core WIT value types (option, result, tuple, stream, future) and the canonical-ABI runtime support used by generated bindings. |
| `imports/...` | Committed generated bindings for the `wasi:*` interfaces imported by the two worlds (both the 0.2.8 and 0.3.0 families). |
| `exports/...` | Per-world generated `//go:wasmexport` glue and export trampolines. |

Bindings under `imports/` and `exports/` are generated by
[`regenerate_bindings.sh`](./regenerate_bindings.sh) — do not edit them.

## Updating WIT dependencies

The `wasi:*` WIT packages under `wit/deps/` are vendored verbatim from the WebAssembly package registry using [wkg](https://github.com/bytecodealliance/wasm-pkg-tools). To update a dependency:

1. Bump its version in [`fetch_wit_deps.sh`](./fetch_wit_deps.sh) and in
[`wit/world.wit`](./wit/world.wit).
2. Re-fetch the vendored WIT:

```console
$ ./fetch_wit_deps.sh
```

3. Regenerate the committed bindings and commit everything together:

```console
$ ./regenerate_bindings.sh
```

> **Note**: the script uses `wkg get` with exact versions rather than
> `wkg wit fetch` because the library intentionally depends on two versions
> of several packages (e.g. `wasi:http@0.2.8` and `wasi:http@0.3.0`), and
> `wkg wit fetch` resolves at most one version per package name.

## The `componentizego_async` build tag

`wasihttp` compiles to one of two implementations; the exported API is identical under both:

- **Default (no tag)**: sync WASI P2 (`wasi:http@0.2.8`). Matches the `bytecodealliance:pkg/wasip2` world.
- **`-tags componentizego_async`**: async WASI P3 (`wasi:http@0.3.0`) with streaming bodies and native concurrency. Matches the `bytecodealliance:pkg/wasip3` world.

componentize-go sets the tag automatically when building an async world.

## Quickstart

```go
package main

import (
"net/http"

"go.bytecodealliance.org/pkg/wasihttp"
)

func init() {
wasihttp.HandleFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, component!"))
})
}

func main() {}
```

Add componentize-go as a Go tool and build:

```console
$ go get -tool github.com/bytecodealliance/componentize-go
$ go tool componentize-go build
```

The default world (`bytecodealliance:pkg/wasip2@0.1.0`) is declared in [`componentize-go.toml`](./componentize-go.toml) and discovered automatically. To build the async WASI P3 world instead:

```console
$ go tool componentize-go -w bytecodealliance:pkg/wasip3 build
```

## Benchmarks

Pure-Go conversion logic (header conversion and friends) has microbenchmarks that run on the host:

```console
$ go test -bench=. -benchmem ./...
```

Packages that call `wasi:*` imports only link on wasm targets, so the benchmarks live in host-compilable packages (e.g. `internal/httpconv`).

For A/B comparisons use [benchstat](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat): collect ≥10 samples per side with the test filter disabled, then compare:

```console
$ go test -bench=. -benchmem -count=10 -run='^$' ./... > old.txt
$ # ... apply your change ...
$ go test -bench=. -benchmem -count=10 -run='^$' ./... > new.txt
$ benchstat old.txt new.txt
```

## Questions?

Ask over in the Bytecode Alliance <a href="https://bytecodealliance.zulipchat.com">Zulip</a>.

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing
to this repository.
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing to this repository.
9 changes: 9 additions & 0 deletions componentize-go.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Default world for components built against this library. componentize-go
# discovers this file via `go list -m all`, so `go tool componentize-go
# build` needs no flags in consuming apps.
#
# The default is the sync WASI P2 world, which builds with stock Go. To
# target the async WASI P3 world instead, build with:
# componentize-go -w bytecodealliance:pkg/wasip3 build
worlds = ["bytecodealliance:pkg/wasip2@0.1.0"]
wit_paths = ["wit"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Package export_wasi_http_0_2_8_incoming_handler is the export trampoline
// for the sync `wasip2` world's `wasi:http/incoming-handler@0.2.8` export.
// The generated wit_exports glue calls Handle; the library's wasihttp
// package assigns Exports.Handle at init time.
package export_wasi_http_0_2_8_incoming_handler

import (
"go.bytecodealliance.org/pkg/imports/wasi_http_0_2_8_types"
)

var Exports struct {
Handle func(request *wasi_http_0_2_8_types.IncomingRequest, responseOut *wasi_http_0_2_8_types.ResponseOutparam)
}

func Handle(request *wasi_http_0_2_8_types.IncomingRequest, responseOut *wasi_http_0_2_8_types.ResponseOutparam) {
Exports.Handle(request, responseOut)
}
3 changes: 3 additions & 0 deletions exports/bytecodealliance_pkg_wasip2_0_1_0/wit_exports/empty.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Generated by `wit-bindgen` 0.59.0. DO NOT EDIT!
//
// This code was generated from the following packages:
// wasi:io@0.2.8
// wasi:clocks@0.2.8
// wasi:filesystem@0.2.8
// wasi:sockets@0.2.8
// wasi:random@0.2.8
// wasi:cli@0.2.8
// wasi:config@0.2.0-rc.1
// wasi:logging@0.1.0-draft
// wasi:http@0.2.8
// wasi:clocks@0.3.0
// wasi:filesystem@0.3.0
// wasi:sockets@0.3.0
// wasi:random@0.3.0
// wasi:cli@0.3.0
// wasi:http@0.3.0
// bytecodealliance:pkg@0.1.0

package wit_exports

import (
"go.bytecodealliance.org/pkg/exports/bytecodealliance_pkg_wasip2_0_1_0/export_wasi_http_0_2_8_incoming_handler"
"go.bytecodealliance.org/pkg/imports/wasi_http_0_2_8_types"
witRuntime "go.bytecodealliance.org/pkg/wit/runtime"
"runtime"
)

var staticPinner = runtime.Pinner{}
var exportReturnArea = uintptr(witRuntime.Allocate(&staticPinner, 0, 1))
var syncExportPinner = runtime.Pinner{}

//go:wasmexport wasi:http/incoming-handler@0.2.8#handle
func wasm_export_wasi_http_0_2_8_incoming_handler_handle(arg0 int32, arg1 int32) {

export_wasi_http_0_2_8_incoming_handler.Handle(wasi_http_0_2_8_types.IncomingRequestFromOwnHandle(int32(uintptr(arg0))), wasi_http_0_2_8_types.ResponseOutparamFromOwnHandle(int32(uintptr(arg1))))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Package export_wasi_http_0_3_0_handler is the export trampoline for the
// async `wasip3` world's `wasi:http/handler@0.3.0` export. The generated
// wit_exports glue calls Handle; the library's wasihttp package assigns
// Exports.Handle at init time.
package export_wasi_http_0_3_0_handler

import (
"go.bytecodealliance.org/pkg/imports/wasi_http_0_3_0_types"
witTypes "go.bytecodealliance.org/pkg/wit/types"
)

var Exports struct {
Handle func(request *wasi_http_0_3_0_types.Request) witTypes.Result[*wasi_http_0_3_0_types.Response, wasi_http_0_3_0_types.ErrorCode]
}

func Handle(request *wasi_http_0_3_0_types.Request) witTypes.Result[*wasi_http_0_3_0_types.Response, wasi_http_0_3_0_types.ErrorCode] {
return Exports.Handle(request)
}
3 changes: 3 additions & 0 deletions exports/bytecodealliance_pkg_wasip3_0_1_0/wit_exports/empty.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
Loading
Loading