-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Go net/http -> wasi:http Proxy (#34)
* feature: Go net/http -> wasi:http Proxy Signed-off-by: Lucas Fontes <[email protected]> * chore: tests Signed-off-by: Lucas Fontes <[email protected]> * chore: Updating golangci Signed-off-by: Lucas Fontes <[email protected]> * chore: Running go mod tidy Signed-off-by: Lucas Fontes <[email protected]> --------- Signed-off-by: Lucas Fontes <[email protected]>
- Loading branch information
Showing
109 changed files
with
48,537 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
http-server | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# http-server | ||
|
||
This example demonstrates how to forward requests to components exporting `wasi:http/incoming-handler`. | ||
|
||
It starts a http server listening on port 8080 containing 2 routes: | ||
|
||
- `/proxy`: Forwards the request to the component `http-component` | ||
- `/`: Serve the request directly from the provider | ||
|
||
# Internals | ||
|
||
Proxying uses a custom `http.RoundTripper` implementation that forwards requests to the component. | ||
In this example we forward to a single target ( `http-http_component` ). | ||
|
||
```go | ||
transport := wrpchttp.NewIncomingRoundTripper(wasmcloudprovider, wrpchttp.WithSingleTarget("http-http_component")) | ||
|
||
wasiIncomingClient := &http.Client{ | ||
Transport: transport, | ||
} | ||
|
||
wasiIncomingClient.Get("http://localhost:8080/proxy") | ||
``` | ||
|
||
You can also provide a custom `Director` function to select the target based on the request. | ||
|
||
```go | ||
func director(r *http.Request) string { | ||
if r.URL.Host == "api" { | ||
return "http-api" | ||
} | ||
return "http-ui" | ||
}) | ||
|
||
|
||
transport := wrpchttp.NewIncomingRoundTripper(wasmcloudprovider, wrpchttp.WithDirector(director)) | ||
|
||
wasiIncomingClient := &http.Client{ | ||
Transport: transport, | ||
} | ||
|
||
// forward to http-api component | ||
wasiIncomingClient.Get("http://api/users") | ||
|
||
// forward to http-ui component | ||
wasiIncomingClient.Get("http://ui/index.html") | ||
wasiIncomingClient.Get("http://anyothername/index.html") | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Generated by `wit-bindgen-wrpc-go` 0.8.0. DO NOT EDIT! | ||
// server package contains wRPC bindings for `server` world | ||
package server |
286 changes: 286 additions & 0 deletions
286
examples/http-server/bindings/wasi/clocks/monotonic_clock/bindings.wrpc.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
// Generated by `wit-bindgen-wrpc-go` 0.8.0. DO NOT EDIT! | ||
package monotonic_clock | ||
|
||
import ( | ||
bytes "bytes" | ||
context "context" | ||
binary "encoding/binary" | ||
errors "errors" | ||
fmt "fmt" | ||
wasi__io__poll "github.com/wasmCloud/provider-sdk-go/examples/http-server/bindings/wasi/io/poll" | ||
io "io" | ||
slog "log/slog" | ||
utf8 "unicode/utf8" | ||
wrpc "wrpc.io/go" | ||
) | ||
|
||
type Pollable = wasi__io__poll.Pollable | ||
|
||
// An instant in time, in nanoseconds. An instant is relative to an | ||
// unspecified initial value, and can only be compared to instances from | ||
// the same monotonic-clock. | ||
type Instant = uint64 | ||
|
||
// A duration of time, in nanoseconds. | ||
type Duration = uint64 | ||
|
||
// Read the current value of the clock. | ||
// | ||
// The clock is monotonic, therefore calling this function repeatedly will | ||
// produce a sequence of non-decreasing values. | ||
func Now(ctx__ context.Context, wrpc__ wrpc.Invoker) (r0__ uint64, err__ error) { | ||
var w__ wrpc.IndexWriteCloser | ||
var r__ wrpc.IndexReadCloser | ||
w__, r__, err__ = wrpc__.Invoke(ctx__, "wasi:clocks/[email protected]", "now", nil) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to invoke `now`: %w", err__) | ||
return | ||
} | ||
defer func() { | ||
if err := r__.Close(); err != nil { | ||
slog.ErrorContext(ctx__, "failed to close reader", "instance", "wasi:clocks/[email protected]", "name", "now", "err", err) | ||
} | ||
}() | ||
if cErr__ := w__.Close(); cErr__ != nil { | ||
slog.DebugContext(ctx__, "failed to close outgoing stream", "instance", "wasi:clocks/[email protected]", "name", "now", "err", cErr__) | ||
} | ||
r0__, err__ = func() (Instant, error) { | ||
v, err := func(r io.ByteReader) (uint64, error) { | ||
var x uint64 | ||
var s uint8 | ||
for i := 0; i < 10; i++ { | ||
slog.Debug("reading u64 byte", "i", i) | ||
b, err := r.ReadByte() | ||
if err != nil { | ||
if i > 0 && err == io.EOF { | ||
err = io.ErrUnexpectedEOF | ||
} | ||
return x, fmt.Errorf("failed to read u64 byte: %w", err) | ||
} | ||
if s == 63 && b > 0x01 { | ||
return x, errors.New("varint overflows a 64-bit integer") | ||
} | ||
if b < 0x80 { | ||
return x | uint64(b)<<s, nil | ||
} | ||
x |= uint64(b&0x7f) << s | ||
s += 7 | ||
} | ||
return x, errors.New("varint overflows a 64-bit integer") | ||
}(r__) | ||
return (Instant)(v), err | ||
}() | ||
|
||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to read result 0: %w", err__) | ||
return | ||
} | ||
return | ||
} | ||
|
||
// Query the resolution of the clock. Returns the duration of time | ||
// corresponding to a clock tick. | ||
func Resolution(ctx__ context.Context, wrpc__ wrpc.Invoker) (r0__ uint64, err__ error) { | ||
var w__ wrpc.IndexWriteCloser | ||
var r__ wrpc.IndexReadCloser | ||
w__, r__, err__ = wrpc__.Invoke(ctx__, "wasi:clocks/[email protected]", "resolution", nil) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to invoke `resolution`: %w", err__) | ||
return | ||
} | ||
defer func() { | ||
if err := r__.Close(); err != nil { | ||
slog.ErrorContext(ctx__, "failed to close reader", "instance", "wasi:clocks/[email protected]", "name", "resolution", "err", err) | ||
} | ||
}() | ||
if cErr__ := w__.Close(); cErr__ != nil { | ||
slog.DebugContext(ctx__, "failed to close outgoing stream", "instance", "wasi:clocks/[email protected]", "name", "resolution", "err", cErr__) | ||
} | ||
r0__, err__ = func() (Duration, error) { | ||
v, err := func(r io.ByteReader) (uint64, error) { | ||
var x uint64 | ||
var s uint8 | ||
for i := 0; i < 10; i++ { | ||
slog.Debug("reading u64 byte", "i", i) | ||
b, err := r.ReadByte() | ||
if err != nil { | ||
if i > 0 && err == io.EOF { | ||
err = io.ErrUnexpectedEOF | ||
} | ||
return x, fmt.Errorf("failed to read u64 byte: %w", err) | ||
} | ||
if s == 63 && b > 0x01 { | ||
return x, errors.New("varint overflows a 64-bit integer") | ||
} | ||
if b < 0x80 { | ||
return x | uint64(b)<<s, nil | ||
} | ||
x |= uint64(b&0x7f) << s | ||
s += 7 | ||
} | ||
return x, errors.New("varint overflows a 64-bit integer") | ||
}(r__) | ||
return (Duration)(v), err | ||
}() | ||
|
||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to read result 0: %w", err__) | ||
return | ||
} | ||
return | ||
} | ||
|
||
// Create a `pollable` which will resolve once the specified instant | ||
// occured. | ||
func SubscribeInstant(ctx__ context.Context, wrpc__ wrpc.Invoker, when uint64) (r0__ wrpc.Own[Pollable], err__ error) { | ||
var buf__ bytes.Buffer | ||
write0__, err__ := (func(wrpc.IndexWriter) error)(nil), func(v uint64, w io.Writer) (err error) { | ||
b := make([]byte, binary.MaxVarintLen64) | ||
i := binary.PutUvarint(b, uint64(v)) | ||
slog.Debug("writing u64") | ||
_, err = w.Write(b[:i]) | ||
return err | ||
}(when, &buf__) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to write `when` parameter: %w", err__) | ||
return | ||
} | ||
if write0__ != nil { | ||
err__ = errors.New("unexpected deferred write for synchronous `when` parameter") | ||
return | ||
} | ||
var w__ wrpc.IndexWriteCloser | ||
var r__ wrpc.IndexReadCloser | ||
w__, r__, err__ = wrpc__.Invoke(ctx__, "wasi:clocks/[email protected]", "subscribe-instant", buf__.Bytes()) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to invoke `subscribe-instant`: %w", err__) | ||
return | ||
} | ||
defer func() { | ||
if err := r__.Close(); err != nil { | ||
slog.ErrorContext(ctx__, "failed to close reader", "instance", "wasi:clocks/[email protected]", "name", "subscribe-instant", "err", err) | ||
} | ||
}() | ||
if cErr__ := w__.Close(); cErr__ != nil { | ||
slog.DebugContext(ctx__, "failed to close outgoing stream", "instance", "wasi:clocks/[email protected]", "name", "subscribe-instant", "err", cErr__) | ||
} | ||
r0__, err__ = func(r interface { | ||
io.ByteReader | ||
io.Reader | ||
}) (wrpc.Own[Pollable], error) { | ||
var x uint32 | ||
var s uint | ||
for i := 0; i < 5; i++ { | ||
slog.Debug("reading owned resource ID length byte", "i", i) | ||
b, err := r.ReadByte() | ||
if err != nil { | ||
if i > 0 && err == io.EOF { | ||
err = io.ErrUnexpectedEOF | ||
} | ||
return "", fmt.Errorf("failed to read owned resource ID length byte: %w", err) | ||
} | ||
if b < 0x80 { | ||
if i == 4 && b > 1 { | ||
return "", errors.New("owned resource ID length overflows a 32-bit integer") | ||
} | ||
x = x | uint32(b)<<s | ||
buf := make([]byte, x) | ||
slog.Debug("reading owned resource ID bytes", "len", x) | ||
_, err = r.Read(buf) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to read owned resource ID bytes: %w", err) | ||
} | ||
if !utf8.Valid(buf) { | ||
return "", errors.New("owned resource ID is not valid UTF-8") | ||
} | ||
return wrpc.Own[Pollable](buf), nil | ||
} | ||
x |= uint32(b&0x7f) << s | ||
s += 7 | ||
} | ||
return "", errors.New("owned resource ID length overflows a 32-bit integer") | ||
}(r__) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to read result 0: %w", err__) | ||
return | ||
} | ||
return | ||
} | ||
|
||
// Create a `pollable` which will resolve once the given duration has | ||
// elapsed, starting at the time at which this function was called. | ||
// occured. | ||
func SubscribeDuration(ctx__ context.Context, wrpc__ wrpc.Invoker, when uint64) (r0__ wrpc.Own[Pollable], err__ error) { | ||
var buf__ bytes.Buffer | ||
write0__, err__ := (func(wrpc.IndexWriter) error)(nil), func(v uint64, w io.Writer) (err error) { | ||
b := make([]byte, binary.MaxVarintLen64) | ||
i := binary.PutUvarint(b, uint64(v)) | ||
slog.Debug("writing u64") | ||
_, err = w.Write(b[:i]) | ||
return err | ||
}(when, &buf__) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to write `when` parameter: %w", err__) | ||
return | ||
} | ||
if write0__ != nil { | ||
err__ = errors.New("unexpected deferred write for synchronous `when` parameter") | ||
return | ||
} | ||
var w__ wrpc.IndexWriteCloser | ||
var r__ wrpc.IndexReadCloser | ||
w__, r__, err__ = wrpc__.Invoke(ctx__, "wasi:clocks/[email protected]", "subscribe-duration", buf__.Bytes()) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to invoke `subscribe-duration`: %w", err__) | ||
return | ||
} | ||
defer func() { | ||
if err := r__.Close(); err != nil { | ||
slog.ErrorContext(ctx__, "failed to close reader", "instance", "wasi:clocks/[email protected]", "name", "subscribe-duration", "err", err) | ||
} | ||
}() | ||
if cErr__ := w__.Close(); cErr__ != nil { | ||
slog.DebugContext(ctx__, "failed to close outgoing stream", "instance", "wasi:clocks/[email protected]", "name", "subscribe-duration", "err", cErr__) | ||
} | ||
r0__, err__ = func(r interface { | ||
io.ByteReader | ||
io.Reader | ||
}) (wrpc.Own[Pollable], error) { | ||
var x uint32 | ||
var s uint | ||
for i := 0; i < 5; i++ { | ||
slog.Debug("reading owned resource ID length byte", "i", i) | ||
b, err := r.ReadByte() | ||
if err != nil { | ||
if i > 0 && err == io.EOF { | ||
err = io.ErrUnexpectedEOF | ||
} | ||
return "", fmt.Errorf("failed to read owned resource ID length byte: %w", err) | ||
} | ||
if b < 0x80 { | ||
if i == 4 && b > 1 { | ||
return "", errors.New("owned resource ID length overflows a 32-bit integer") | ||
} | ||
x = x | uint32(b)<<s | ||
buf := make([]byte, x) | ||
slog.Debug("reading owned resource ID bytes", "len", x) | ||
_, err = r.Read(buf) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to read owned resource ID bytes: %w", err) | ||
} | ||
if !utf8.Valid(buf) { | ||
return "", errors.New("owned resource ID is not valid UTF-8") | ||
} | ||
return wrpc.Own[Pollable](buf), nil | ||
} | ||
x |= uint32(b&0x7f) << s | ||
s += 7 | ||
} | ||
return "", errors.New("owned resource ID length overflows a 32-bit integer") | ||
}(r__) | ||
if err__ != nil { | ||
err__ = fmt.Errorf("failed to read result 0: %w", err__) | ||
return | ||
} | ||
return | ||
} |
Oops, something went wrong.