Skip to content

x/wasihttp: add wasihttp package #305

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
45 changes: 45 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,48 @@ jobs:

- name: Verify repo is unchanged
run: git diff --exit-code HEAD

test-wasihttp:
name: Test wasihttp
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
matrix:
go-version: ["1.23", "1.24"]
tinygo-version: ["0.34.0", "0.35.0", "0.36.0"]
exclude:
- go-version: "1.24"
tinygo-version: "0.34.0"
- go-version: "1.24"
tinygo-version: "0.35.0"
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Set up TinyGo
uses: acifani/setup-tinygo@v2
with:
tinygo-version: ${{ matrix.tinygo-version }}

- name: Set up wasm-tools
uses: bytecodealliance/actions/wasm-tools/setup@v1
with:
version: ${{ env.wasm-tools-version }}

- name: Set up Wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: ${{ env.wasmtime-version }}

- name: Test wasihttp basic
run: ./scripts/test-wasihttp-basic.sh

- name: Test wasihttp roundtrip
run: ./scripts/test-wasihttp-roundtrip.sh
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ use (
.
./cm
./tests
./x/wasihttp
)
27 changes: 27 additions & 0 deletions scripts/test-wasihttp-basic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
set -euo pipefail

echo "Building basic example..."
cd ./x/wasihttp/examples
tinygo build -target=wasip2-http.json -o basic.wasm ./basic

echo "Running basic example with wasmtime..."
wasmtime serve --addr 0.0.0.0:8088 -Scli basic.wasm &
SERVER_PID=$!

sleep 10

echo "Testing / endpoint..."
ROOT_RESPONSE=$(curl -s 'http://0.0.0.0:8088/')
if [[ "$ROOT_RESPONSE" != "Hello world!"* ]]; then
echo "ERROR: Root endpoint test failed"
echo "Expected response containing 'Hello world!'"
echo "Actual response: $ROOT_RESPONSE"
kill $SERVER_PID 2>/dev/null
exit 1
fi

kill $SERVER_PID 2>/dev/null
rm basic.wasm

echo "All basic tests passed!"
33 changes: 33 additions & 0 deletions scripts/test-wasihttp-roundtrip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
set -euo pipefail

echo "Building roundtrip example..."
cd ./x/wasihttp/examples
tinygo build -target=wasip2-roundtrip.json -o roundtrip.wasm ./roundtrip

echo "Running roundtrip example..."
output=$(wasmtime run -Shttp -Sinherit-network -Sinherit-env roundtrip.wasm)

# Verify GET request worked
if ! echo "$output" | grep -q "Status: 200" || ! echo "$output" | grep -q "https://postman-echo.com/get"; then
echo "ERROR: GET request verification failed"
echo "$output"
exit 1
fi

# Verify POST request worked
if ! echo "$output" | grep -q '"foo": "bar"'; then
echo "ERROR: POST request verification failed"
echo "$output"
exit 1
fi

# Verify PUT request worked
if ! echo "$output" | grep -q '"baz": "blah"'; then
echo "ERROR: PUT request verification failed"
echo "$output"
exit 1
fi

rm roundtrip.wasm
echo "All roundtrip tests passed!"
11 changes: 11 additions & 0 deletions x/wasihttp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Initial import of the WASI HTTP package from the standalone repository

### Changed
- Package import path is now `go.bytecodealliance.org/x/wasihttp`
7 changes: 7 additions & 0 deletions x/wasihttp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.PHONY: tools
tools:
go generate -tags tools ./...

.PHONY: go-bindings
go-bindings:
go run go.bytecodealliance.org/cmd/wit-bindgen-go generate -o internal/ ./wit
46 changes: 46 additions & 0 deletions x/wasihttp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# `go.bytecodealliance.org/x/wasihttp`

Package `go.bytecodealliance.org/x/wasihttp` implements [`wasi:http/proxy`](https://github.com/WebAssembly/wasi-http/blob/v0.2.0/proxy.md) for Go using standard [`net/http`](https://pkg.go.dev/net/http) interfaces.

This package allows Go applications to use the familiar `net/http` API when building WebAssembly modules that use the WASI HTTP standard.

## Prerequisites

To use this package, you'll need:

- [TinyGo](https://tinygo.org/) 0.34.0 or later.
- [Wasmtime](https://wasmtime.dev/) 26.0.0 or later.

## Usage

### Server

Create a standard Go HTTP server using the familiar `net/http` API:

```go
package main

import (
"net/http"
_ "go.bytecodealliance.org/x/wasihttp" // enable wasi-http
)

func init() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Go", "Gopher")
w.Write([]byte("Hello world!\n"))
})
}

func main() {}
```

Compile with:

```bash
tinygo build -target=wasip2-http.json -o server.wasm ./main.go
```

## Examples

For more examples, see the `examples` directory.
21 changes: 21 additions & 0 deletions x/wasihttp/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Release Process

This document describes how to release a new version of the `wasihttp` package.

## Release Steps

1. Update the [CHANGELOG.md](CHANGELOG.md) with the changes since the last release.
2. Ensure all tests pass and examples work correctly.

## Creating a Release

Create a new git tag for the release:

```bash
git tag x/wasihttp/v0.1.0
git push origin x/wasihttp/v0.1.0
```

## Version Numbering

The `wasihttp` package follows [semantic versioning](https://semver.org/).
11 changes: 11 additions & 0 deletions x/wasihttp/docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Package wasihttp implements [`wasi:http/proxy`](https://github.com/WebAssembly/wasi-http/blob/v0.2.0/proxy.md) for Go using standard [`net/http`](https://pkg.go.dev/net/http) interfaces
//
// It provides an implementation of the wasi:http/proxy interface that allows Go applications
// to use the standard net/http library for both client and server operations within a WebAssembly
// module. This allows Go developers to use familiar HTTP APIs when building WebAssembly modules
// that need to make or handle HTTP requests.
//
// To use this package in your Go application, simply import it:
//
// import _ "go.bytecodealliance.org/x/wasihttp"
package wasihttp
28 changes: 28 additions & 0 deletions x/wasihttp/examples/basic/basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This example implements a basic web server.
//
// working direcotry: "./x/wasihttp/examples"
// To run: `tinygo run -target=wasip2-http.json ./basic`
// Test /: `curl -v 'http://0.0.0.0:8080/'`
// Test /error: `curl -v 'http://0.0.0.0:8080/error'`

package main

import (
"net/http"

_ "go.bytecodealliance.org/x/wasihttp"
)

func init() {
// TODO: use "GET /" when TinyGo supports net/http from Go 1.22+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Go", "Gopher")
w.Write([]byte("Hello world!\n"))
})

http.HandleFunc("/error", func(w http.ResponseWriter, r *http.Request) {
// do nothing, force default response handling
})
}

func main() {}
34 changes: 34 additions & 0 deletions x/wasihttp/examples/counter/counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This example implements a web server with a counter running in a goroutine.
// This demonstrates instance reuse by the host.
//
// working directory: "./x/wasihttp/examples"
// To run: `tinygo run -target=wasip2-http.json ./counter`
// Test /: `curl -v 'http://0.0.0.0:8080/'`

package main

import (
"fmt"
"net/http"

_ "go.bytecodealliance.org/x/wasihttp"
)

func init() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
n := <-c
fmt.Fprintf(w, "%d", n)
})

go func() {
var n int
for {
c <- n
n++
}
}()
}

var c = make(chan int, 1)

func main() {}
Loading
Loading