feat(sdk/go): Implement spin-http for TinyGo
TinyGo bindings of the spin-http wit.
14603bb71f/wit/ephemeral/spin-http.wit
This commit is contained in:
parent
f4426a4019
commit
9b8549d9c2
|
@ -6,30 +6,31 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
spin_http "github.com/fermyon/spin/sdk/go/http"
|
||||
spinhttp "github.com/fermyon/spin/sdk/go/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
spin_http.HandleRequest(func(w http.ResponseWriter, r *http.Request) {
|
||||
r1, _ := spin_http.Get("https://some-random-api.ml/facts/dog")
|
||||
func init() {
|
||||
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
|
||||
r1, _ := spinhttp.Get("https://some-random-api.ml/facts/dog")
|
||||
|
||||
fmt.Fprintln(w, r1.Body)
|
||||
fmt.Fprintln(w, r1.Header.Get("content-type"))
|
||||
|
||||
r2, _ := spin_http.Post("https://postman-echo.com/post", "text/plain", r.Body)
|
||||
r2, _ := spinhttp.Post("https://postman-echo.com/post", "text/plain", r.Body)
|
||||
fmt.Fprintln(w, r2.Body)
|
||||
|
||||
req, _ := http.NewRequest("PUT", "https://postman-echo.com/put", bytes.NewBufferString("General Kenobi!"))
|
||||
req.Header.Add("foo", "bar")
|
||||
r3, _ := spin_http.Send(req)
|
||||
r3, _ := spinhttp.Send(req)
|
||||
|
||||
fmt.Fprintln(w, r3.Body)
|
||||
|
||||
// `spin.toml` is not configured to allow outbound HTTP requests to this host,
|
||||
// so this request will fail.
|
||||
_, err := spin_http.Get("https://fermyon.com")
|
||||
if err != nil {
|
||||
if _, err := spinhttp.Get("https://fermyon.com"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Cannot send HTTP request: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
|
|
@ -8,7 +8,6 @@ version = "1.0.0"
|
|||
[[component]]
|
||||
id = "tinygo-hello"
|
||||
source = "main.wasm"
|
||||
allowed_http_hosts = [ "https://some-random-api.ml", "https://postman-echo.com" ]
|
||||
allowed_http_hosts = ["https://some-random-api.ml", "https://postman-echo.com"]
|
||||
[component.trigger]
|
||||
route = "/hello"
|
||||
executor = { type = "wagi" }
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
build:
|
||||
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -o main.wasm main.go
|
||||
|
||||
. PHONY: serve
|
||||
.PHONY: serve
|
||||
serve:
|
||||
RUST_LOG=spin=trace spin up --file spin.toml
|
||||
|
|
|
@ -2,13 +2,35 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
spin "github.com/fermyon/spin/sdk/go/http"
|
||||
spinhttp "github.com/fermyon/spin/sdk/go/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
spin.HandleRequest(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "Hello, Fermyon!")
|
||||
func init() {
|
||||
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Header().Set("foo", "bar")
|
||||
|
||||
fmt.Fprintln(w, "== REQUEST ==")
|
||||
fmt.Fprintln(w, "URL: ", r.URL)
|
||||
fmt.Fprintln(w, "Method: ", r.Method)
|
||||
fmt.Fprintln(w, "Headers:")
|
||||
for k, v := range r.Header {
|
||||
fmt.Fprintf(w, " %q: %q \n", k, v[0])
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Body Error: ", err)
|
||||
} else {
|
||||
fmt.Fprintln(w, "Body: ", string(body))
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "== RESPONSE ==")
|
||||
fmt.Fprintln(w, "Hello Fermyon!")
|
||||
})
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
|
|
@ -10,4 +10,3 @@ id = "tinygo-hello"
|
|||
source = "main.wasm"
|
||||
[component.trigger]
|
||||
route = "/hello"
|
||||
executor = { type = "wagi" }
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
.PHONY: generate
|
||||
generate:
|
||||
wit-bindgen c --import ../../../wit/ephemeral/wasi-outbound-http.wit
|
||||
wit-bindgen c --export ../../../wit/ephemeral/spin-http.wit
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm wasi-outbound-http.h wasi-outbound-http.c
|
||||
rm spin-http.h spin-http.c wasi-outbound-http.h wasi-outbound-http.c
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
tinygo test -wasm-abi=generic -target=wasi -gc=leaking -v
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
//nolint:staticcheck
|
||||
|
||||
// Package http contains the helper functions for writing Spin HTTP components
|
||||
// in TinyGo, as well as for sending outbound HTTP requests.
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cgi"
|
||||
"os"
|
||||
)
|
||||
|
||||
// HandleRequest is the entrypoint handler for a Spin HTTP component.
|
||||
//
|
||||
// This is currently handled using CGI to form the request and reponse,
|
||||
// but as Go implements support for the component model, the underlying
|
||||
// implementation of this function can change, but the exported signature
|
||||
// can continue to always be
|
||||
// `func HandleRequest(h func(w http.ResponseWriter, r *http.Request)) error`.
|
||||
func HandleRequest(h func(w http.ResponseWriter, r *http.Request)) error {
|
||||
return cgi.Serve(http.HandlerFunc(h))
|
||||
// handler is the function that will be called by the http trigger in Spin.
|
||||
var handler = defaultHandler
|
||||
|
||||
// defaultHandler is a placeholder for returning a useful error to stdout when
|
||||
// the handler is not set.
|
||||
var defaultHandler = func(http.ResponseWriter, *http.Request) {
|
||||
fmt.Fprintln(os.Stderr, "http handler undefined")
|
||||
}
|
||||
|
||||
// Handle sets the handler function for the http trigger.
|
||||
// It must be set in an init() function.
|
||||
func Handle(fn func(http.ResponseWriter, *http.Request)) {
|
||||
handler = fn
|
||||
}
|
||||
|
||||
// Get creates a GET HTTP request to a given URL and returns the HTTP response.
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
package http
|
||||
|
||||
// #cgo CFLAGS: -Wno-unused-parameter -Wno-switch-bool
|
||||
// #include<spin-http.h>
|
||||
// #include<stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//export spin_http_handle_http_request
|
||||
func handle_http_request(req *C.spin_http_request_t, res *C.spin_http_response_t) {
|
||||
defer C.spin_http_request_free(req)
|
||||
|
||||
sr := (*spinRequest)(req)
|
||||
|
||||
// NOTE Host is not included in the URL
|
||||
r, err := http.NewRequest(sr.Method(), sr.URL(), bytes.NewReader(sr.Body()))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
|
||||
r.Header = fromSpinHeaders(&req.headers)
|
||||
r.Host = r.Header.Get("Host")
|
||||
|
||||
w := newResponse()
|
||||
|
||||
// call user function
|
||||
handler(w, r)
|
||||
|
||||
res.status = C.uint16_t(w.status)
|
||||
if len(w.header) > 0 {
|
||||
res.headers = C.spin_http_option_headers_t{
|
||||
tag: true,
|
||||
val: toSpinHeaders(w.header),
|
||||
}
|
||||
} else {
|
||||
res.headers = C.spin_http_option_headers_t{tag: false}
|
||||
}
|
||||
|
||||
res.body, err = toSpinBody(w.w)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func toSpinHeaders(hm http.Header) C.spin_http_headers_t {
|
||||
var reqHeaders C.spin_http_headers_t
|
||||
|
||||
headersLen := len(hm)
|
||||
|
||||
if headersLen > 0 {
|
||||
reqHeaders.len = C.ulong(headersLen)
|
||||
var x C.spin_http_string_t
|
||||
headersPtr := C.malloc(C.size_t(headersLen) * C.size_t(unsafe.Sizeof(x)))
|
||||
ptr := (*[1 << 30]C.spin_http_tuple2_string_string_t)(unsafe.Pointer(&headersPtr))[:headersLen:headersLen]
|
||||
|
||||
idx := 0
|
||||
for k, v := range hm {
|
||||
ptr[idx] = newSpinHeader(k, v[0])
|
||||
idx++
|
||||
}
|
||||
reqHeaders.ptr = &ptr[0]
|
||||
}
|
||||
return reqHeaders
|
||||
}
|
||||
|
||||
func toSpinBody(body io.Reader) (C.spin_http_option_body_t, error) {
|
||||
var spinBody C.spin_http_option_body_t
|
||||
spinBody.tag = false
|
||||
|
||||
if body == nil {
|
||||
return spinBody, nil
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
len, err := buf.ReadFrom(body)
|
||||
if err != nil {
|
||||
return spinBody, err
|
||||
}
|
||||
|
||||
if len > 0 {
|
||||
spinBody.tag = true
|
||||
spinBody.val = C.spin_http_body_t{
|
||||
ptr: &buf.Bytes()[0],
|
||||
len: C.size_t(len),
|
||||
}
|
||||
}
|
||||
|
||||
return spinBody, nil
|
||||
}
|
||||
|
||||
// spinString is the go type for C.spin_http_string_t.
|
||||
type spinString C.spin_http_string_t
|
||||
|
||||
// newSpinString creates a new spinString with the given string.
|
||||
func newSpinString(s string) spinString {
|
||||
return C.spin_http_string_t{ptr: C.CString(s), len: C.size_t(len(s))}
|
||||
}
|
||||
|
||||
// String returns the spinString as a go string.
|
||||
func (ss spinString) String() string {
|
||||
return C.GoStringN(ss.ptr, C.int(ss.len))
|
||||
}
|
||||
|
||||
// spinHeader is the go type for C.spin_http_tuple2_string_string_t.
|
||||
type spinHeader C.spin_http_tuple2_string_string_t
|
||||
|
||||
// newSpinHeader creates a new spinHeader with the given key/value.
|
||||
func newSpinHeader(k, v string) spinHeader {
|
||||
return C.spin_http_tuple2_string_string_t{
|
||||
f0: newSpinString(k),
|
||||
f1: newSpinString(v),
|
||||
}
|
||||
}
|
||||
|
||||
// Values returns the key/value as strings.
|
||||
func (sh spinHeader) Values() (string, string) {
|
||||
k := spinString(sh.f0).String()
|
||||
v := spinString(sh.f1).String()
|
||||
return k, v
|
||||
}
|
||||
|
||||
// spinBody is the go type for C.spin_http_body_t.
|
||||
type spinBody C.spin_http_body_t
|
||||
|
||||
// Bytes returns the body as a go []byte.
|
||||
func (sb spinBody) Bytes() []byte {
|
||||
return C.GoBytes(unsafe.Pointer(sb.ptr), C.int(sb.len))
|
||||
}
|
||||
|
||||
// spinRequest is the go type for C.spin_http_request_t.
|
||||
type spinRequest C.spin_http_request_t
|
||||
|
||||
var methods = [...]string{
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"PATCH",
|
||||
"HEAD",
|
||||
"OPTIONS",
|
||||
}
|
||||
|
||||
// Method returns the request method as a go string.
|
||||
func (sr spinRequest) Method() string {
|
||||
return methods[sr.method]
|
||||
}
|
||||
|
||||
// URL returns the request URL as a go string.
|
||||
func (sr spinRequest) URL() string {
|
||||
return spinString(sr.uri).String()
|
||||
}
|
||||
|
||||
// Body returns the request body as a go []byte.
|
||||
func (sr spinRequest) Body() []byte {
|
||||
if !sr.body.tag {
|
||||
return nil
|
||||
}
|
||||
return spinBody(sr.body.val).Bytes()
|
||||
}
|
||||
|
||||
func fromSpinHeaders(hm *C.spin_http_headers_t) http.Header {
|
||||
headersLen := int(hm.len)
|
||||
headers := make(http.Header, headersLen)
|
||||
|
||||
var headersArr *C.spin_http_tuple2_string_string_t = hm.ptr
|
||||
headersSlice := unsafe.Slice(headersArr, headersLen)
|
||||
for i := 0; i < headersLen; i++ {
|
||||
tuple := headersSlice[i]
|
||||
k := C.GoStringN(tuple.f0.ptr, C.int(tuple.f0.len))
|
||||
v := C.GoStringN(tuple.f1.ptr, C.int(tuple.f1.len))
|
||||
|
||||
headers.Add(k, v)
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package http
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
tt := "hello"
|
||||
if tt != newSpinString(tt).String() {
|
||||
t.Fatal("strings do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeader(t *testing.T) {
|
||||
k, v := "hello", "world"
|
||||
gotK, gotV := newSpinHeader(k, v).Values()
|
||||
if k != gotK {
|
||||
t.Fatal("keys do not match")
|
||||
}
|
||||
if v != gotV {
|
||||
t.Fatal("values did not match")
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -56,36 +57,35 @@ func send(req *http.Request) (*http.Response, error) {
|
|||
ptr: C.CString(req.URL.String()),
|
||||
len: C.ulong(len(req.URL.String())),
|
||||
}
|
||||
spinReq.headers = toSpinReqHeaders(req.Header)
|
||||
spinReq.body, err = toSpinReqBody(req.Body)
|
||||
spinReq.headers = toOutboundHeaders(req.Header)
|
||||
spinReq.body, err = toOutboundReqBody(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
code := C.wasi_outbound_http_request(&spinReq, &spinRes)
|
||||
|
||||
err = toErr(code, req.URL.String())
|
||||
if err != nil {
|
||||
if err := toErr(code, req.URL.String()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toStdHttpResp(&spinRes)
|
||||
return toResponse(&spinRes)
|
||||
}
|
||||
|
||||
func method(m string) (int, error) {
|
||||
switch m {
|
||||
case "GET", "get":
|
||||
switch strings.ToUpper(m) {
|
||||
case "GET":
|
||||
return 0, nil
|
||||
case "POST", "post":
|
||||
case "POST":
|
||||
return 1, nil
|
||||
case "PUT", "put":
|
||||
case "PUT":
|
||||
return 2, nil
|
||||
case "DELETE", "delete":
|
||||
case "DELETE":
|
||||
return 3, nil
|
||||
case "PATCH", "patch":
|
||||
case "PATCH":
|
||||
return 4, nil
|
||||
case "HEAD", "head":
|
||||
case "HEAD":
|
||||
return 5, nil
|
||||
case "OPTIONS", "options":
|
||||
case "OPTIONS":
|
||||
return 6, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("Unknown HTTP method %v", m)
|
||||
|
@ -93,7 +93,7 @@ func method(m string) (int, error) {
|
|||
}
|
||||
|
||||
// Transform a C outbound HTTP response to a Go *http.Response.
|
||||
func toStdHttpResp(res *C.wasi_outbound_http_response_t) (*http.Response, error) {
|
||||
func toResponse(res *C.wasi_outbound_http_response_t) (*http.Response, error) {
|
||||
var body []byte
|
||||
if res.body.tag {
|
||||
body = C.GoBytes(unsafe.Pointer(res.body.val.ptr), C.int(res.body.val.len))
|
||||
|
@ -108,12 +108,25 @@ func toStdHttpResp(res *C.wasi_outbound_http_response_t) (*http.Response, error)
|
|||
Body: ioutil.NopCloser(bytes.NewBuffer(body)),
|
||||
ContentLength: int64(len(body)),
|
||||
Request: nil, // we don't really have a request to populate with here
|
||||
Header: toStdResHeaders(&res.headers),
|
||||
Header: toHeaders(&res.headers),
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func toSpinReqHeaders(hm http.Header) C.wasi_outbound_http_headers_t {
|
||||
// outboundString is the go type for C.wasi_outbound_http_string_t.
|
||||
type outboundString C.wasi_outbound_http_string_t
|
||||
|
||||
// newOutboundString creates a new spinString with the given string.
|
||||
func newOutboundString(s string) spinString {
|
||||
return C.wasi_outbound_http_string_t{ptr: C.CString(s), len: C.size_t(len(s))}
|
||||
}
|
||||
|
||||
// String returns the outboundString as a go string.
|
||||
func (ws outboundString) String() string {
|
||||
return C.GoStringN(ws.ptr, C.int(ws.len))
|
||||
}
|
||||
|
||||
func toOutboundHeaders(hm http.Header) C.wasi_outbound_http_headers_t {
|
||||
var reqHeaders C.wasi_outbound_http_headers_t
|
||||
headersLen := len(hm)
|
||||
|
||||
|
@ -125,8 +138,8 @@ func toSpinReqHeaders(hm http.Header) C.wasi_outbound_http_headers_t {
|
|||
|
||||
idx := 0
|
||||
for k, v := range hm {
|
||||
ptr[idx].f0 = C.wasi_outbound_http_string_t{ptr: C.CString(k), len: C.ulong(len(k))}
|
||||
ptr[idx].f1 = C.wasi_outbound_http_string_t{ptr: C.CString(v[0]), len: C.ulong(len(v[0]))}
|
||||
ptr[idx].f0 = newOutboundString(k)
|
||||
ptr[idx].f1 = newOutboundString(v[0])
|
||||
idx++
|
||||
}
|
||||
reqHeaders.ptr = &ptr[0]
|
||||
|
@ -135,7 +148,7 @@ func toSpinReqHeaders(hm http.Header) C.wasi_outbound_http_headers_t {
|
|||
return reqHeaders
|
||||
}
|
||||
|
||||
func toSpinReqBody(body io.Reader) (C.wasi_outbound_http_option_body_t, error) {
|
||||
func toOutboundReqBody(body io.Reader) (C.wasi_outbound_http_option_body_t, error) {
|
||||
var spinBody C.wasi_outbound_http_option_body_t
|
||||
spinBody.tag = false
|
||||
|
||||
|
@ -158,7 +171,7 @@ func toSpinReqBody(body io.Reader) (C.wasi_outbound_http_option_body_t, error) {
|
|||
return spinBody, nil
|
||||
}
|
||||
|
||||
func toStdResHeaders(hm *C.wasi_outbound_http_option_headers_t) http.Header {
|
||||
func toHeaders(hm *C.wasi_outbound_http_option_headers_t) http.Header {
|
||||
if !hm.tag {
|
||||
return make(map[string][]string, 0)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var _ http.ResponseWriter = (*response)(nil)
|
||||
|
||||
// response implements http.ResponseWriter
|
||||
type response struct {
|
||||
// status code passed to WriteHeader
|
||||
status int
|
||||
|
||||
header http.Header
|
||||
w *bytes.Buffer
|
||||
}
|
||||
|
||||
func newResponse() *response {
|
||||
return &response{
|
||||
// set default status to StatusOK
|
||||
status: http.StatusOK,
|
||||
|
||||
header: make(http.Header),
|
||||
w: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *response) Header() http.Header {
|
||||
return r.header
|
||||
}
|
||||
|
||||
func (r *response) WriteHeader(statusCode int) {
|
||||
r.status = statusCode
|
||||
}
|
||||
|
||||
func (r *response) Write(data []byte) (int, error) {
|
||||
return r.w.Write(data)
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
#include <stdlib.h>
|
||||
#include <spin-http.h>
|
||||
|
||||
__attribute__((weak, export_name("canonical_abi_realloc")))
|
||||
void *canonical_abi_realloc(
|
||||
void *ptr,
|
||||
size_t orig_size,
|
||||
size_t org_align,
|
||||
size_t new_size
|
||||
) {
|
||||
void *ret = realloc(ptr, new_size);
|
||||
if (!ret)
|
||||
abort();
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((weak, export_name("canonical_abi_free")))
|
||||
void canonical_abi_free(
|
||||
void *ptr,
|
||||
size_t size,
|
||||
size_t align
|
||||
) {
|
||||
free(ptr);
|
||||
}
|
||||
#include <string.h>
|
||||
|
||||
void spin_http_string_set(spin_http_string_t *ret, const char *s) {
|
||||
ret->ptr = (char*) s;
|
||||
ret->len = strlen(s);
|
||||
}
|
||||
|
||||
void spin_http_string_dup(spin_http_string_t *ret, const char *s) {
|
||||
ret->len = strlen(s);
|
||||
ret->ptr = canonical_abi_realloc(NULL, 0, 1, ret->len);
|
||||
memcpy(ret->ptr, s, ret->len);
|
||||
}
|
||||
|
||||
void spin_http_string_free(spin_http_string_t *ret) {
|
||||
canonical_abi_free(ret->ptr, ret->len, 1);
|
||||
ret->ptr = NULL;
|
||||
ret->len = 0;
|
||||
}
|
||||
void spin_http_body_free(spin_http_body_t *ptr) {
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
|
||||
}
|
||||
void spin_http_tuple2_string_string_free(spin_http_tuple2_string_string_t *ptr) {
|
||||
spin_http_string_free(&ptr->f0);
|
||||
spin_http_string_free(&ptr->f1);
|
||||
}
|
||||
void spin_http_headers_free(spin_http_headers_t *ptr) {
|
||||
for (size_t i = 0; i < ptr->len; i++) {
|
||||
spin_http_tuple2_string_string_free(&ptr->ptr[i]);
|
||||
}
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 16, 4);
|
||||
}
|
||||
void spin_http_params_free(spin_http_params_t *ptr) {
|
||||
for (size_t i = 0; i < ptr->len; i++) {
|
||||
spin_http_tuple2_string_string_free(&ptr->ptr[i]);
|
||||
}
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 16, 4);
|
||||
}
|
||||
void spin_http_uri_free(spin_http_uri_t *ptr) {
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
|
||||
}
|
||||
void spin_http_option_body_free(spin_http_option_body_t *ptr) {
|
||||
switch (ptr->tag) {
|
||||
case 1: {
|
||||
spin_http_body_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void spin_http_request_free(spin_http_request_t *ptr) {
|
||||
spin_http_uri_free(&ptr->uri);
|
||||
spin_http_headers_free(&ptr->headers);
|
||||
spin_http_params_free(&ptr->params);
|
||||
spin_http_option_body_free(&ptr->body);
|
||||
}
|
||||
void spin_http_option_headers_free(spin_http_option_headers_t *ptr) {
|
||||
switch (ptr->tag) {
|
||||
case 1: {
|
||||
spin_http_headers_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void spin_http_response_free(spin_http_response_t *ptr) {
|
||||
spin_http_option_headers_free(&ptr->headers);
|
||||
spin_http_option_body_free(&ptr->body);
|
||||
}
|
||||
static int64_t RET_AREA[7];
|
||||
__attribute__((export_name("handle-http-request")))
|
||||
int32_t __wasm_export_spin_http_handle_http_request(int32_t arg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8) {
|
||||
spin_http_option_body_t variant;
|
||||
variant.tag = arg6;
|
||||
switch ((int32_t) variant.tag) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
variant.val = (spin_http_body_t) { (uint8_t*)(arg7), (size_t)(arg8) };
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_http_request_t arg9 = (spin_http_request_t) {
|
||||
arg,
|
||||
(spin_http_uri_t) { (char*)(arg0), (size_t)(arg1) },
|
||||
(spin_http_headers_t) { (spin_http_tuple2_string_string_t*)(arg2), (size_t)(arg3) },
|
||||
(spin_http_params_t) { (spin_http_tuple2_string_string_t*)(arg4), (size_t)(arg5) },
|
||||
variant,
|
||||
};
|
||||
spin_http_response_t ret;
|
||||
spin_http_handle_http_request(&arg9, &ret);
|
||||
int32_t variant11;
|
||||
int32_t variant12;
|
||||
int32_t variant13;
|
||||
switch ((int32_t) ((ret).headers).tag) {
|
||||
case 0: {
|
||||
variant11 = 0;
|
||||
variant12 = 0;
|
||||
variant13 = 0;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const spin_http_headers_t *payload10 = &((ret).headers).val;
|
||||
variant11 = 1;
|
||||
variant12 = (int32_t) (*payload10).ptr;
|
||||
variant13 = (int32_t) (*payload10).len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32_t variant16;
|
||||
int32_t variant17;
|
||||
int32_t variant18;
|
||||
switch ((int32_t) ((ret).body).tag) {
|
||||
case 0: {
|
||||
variant16 = 0;
|
||||
variant17 = 0;
|
||||
variant18 = 0;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const spin_http_body_t *payload15 = &((ret).body).val;
|
||||
variant16 = 1;
|
||||
variant17 = (int32_t) (*payload15).ptr;
|
||||
variant18 = (int32_t) (*payload15).len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32_t ptr = (int32_t) &RET_AREA;
|
||||
*((int32_t*)(ptr + 48)) = variant18;
|
||||
*((int32_t*)(ptr + 40)) = variant17;
|
||||
*((int32_t*)(ptr + 32)) = variant16;
|
||||
*((int32_t*)(ptr + 24)) = variant13;
|
||||
*((int32_t*)(ptr + 16)) = variant12;
|
||||
*((int32_t*)(ptr + 8)) = variant11;
|
||||
*((int32_t*)(ptr + 0)) = (int32_t) ((ret).status);
|
||||
return ptr;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef __BINDINGS_SPIN_HTTP_H
|
||||
#define __BINDINGS_SPIN_HTTP_H
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
size_t len;
|
||||
} spin_http_string_t;
|
||||
|
||||
void spin_http_string_set(spin_http_string_t *ret, const char *s);
|
||||
void spin_http_string_dup(spin_http_string_t *ret, const char *s);
|
||||
void spin_http_string_free(spin_http_string_t *ret);
|
||||
typedef struct {
|
||||
uint8_t *ptr;
|
||||
size_t len;
|
||||
} spin_http_body_t;
|
||||
void spin_http_body_free(spin_http_body_t *ptr);
|
||||
typedef struct {
|
||||
spin_http_string_t f0;
|
||||
spin_http_string_t f1;
|
||||
} spin_http_tuple2_string_string_t;
|
||||
void spin_http_tuple2_string_string_free(spin_http_tuple2_string_string_t *ptr);
|
||||
typedef struct {
|
||||
spin_http_tuple2_string_string_t *ptr;
|
||||
size_t len;
|
||||
} spin_http_headers_t;
|
||||
void spin_http_headers_free(spin_http_headers_t *ptr);
|
||||
typedef uint8_t spin_http_http_error_t;
|
||||
#define SPIN_HTTP_HTTP_ERROR_SUCCESS 0
|
||||
#define SPIN_HTTP_HTTP_ERROR_DESTINATION_NOT_ALLOWED 1
|
||||
#define SPIN_HTTP_HTTP_ERROR_INVALID_URL 2
|
||||
#define SPIN_HTTP_HTTP_ERROR_REQUEST_ERROR 3
|
||||
#define SPIN_HTTP_HTTP_ERROR_RUNTIME_ERROR 4
|
||||
typedef uint16_t spin_http_http_status_t;
|
||||
typedef uint8_t spin_http_method_t;
|
||||
#define SPIN_HTTP_METHOD_GET 0
|
||||
#define SPIN_HTTP_METHOD_POST 1
|
||||
#define SPIN_HTTP_METHOD_PUT 2
|
||||
#define SPIN_HTTP_METHOD_DELETE 3
|
||||
#define SPIN_HTTP_METHOD_PATCH 4
|
||||
#define SPIN_HTTP_METHOD_HEAD 5
|
||||
#define SPIN_HTTP_METHOD_OPTIONS 6
|
||||
typedef struct {
|
||||
spin_http_tuple2_string_string_t *ptr;
|
||||
size_t len;
|
||||
} spin_http_params_t;
|
||||
void spin_http_params_free(spin_http_params_t *ptr);
|
||||
typedef spin_http_string_t spin_http_uri_t;
|
||||
void spin_http_uri_free(spin_http_uri_t *ptr);
|
||||
typedef struct {
|
||||
// `true` if `val` is present, `false` otherwise
|
||||
bool tag;
|
||||
spin_http_body_t val;
|
||||
} spin_http_option_body_t;
|
||||
void spin_http_option_body_free(spin_http_option_body_t *ptr);
|
||||
typedef struct {
|
||||
spin_http_method_t method;
|
||||
spin_http_uri_t uri;
|
||||
spin_http_headers_t headers;
|
||||
spin_http_params_t params;
|
||||
spin_http_option_body_t body;
|
||||
} spin_http_request_t;
|
||||
void spin_http_request_free(spin_http_request_t *ptr);
|
||||
typedef struct {
|
||||
// `true` if `val` is present, `false` otherwise
|
||||
bool tag;
|
||||
spin_http_headers_t val;
|
||||
} spin_http_option_headers_t;
|
||||
void spin_http_option_headers_free(spin_http_option_headers_t *ptr);
|
||||
typedef struct {
|
||||
spin_http_http_status_t status;
|
||||
spin_http_option_headers_t headers;
|
||||
spin_http_option_body_t body;
|
||||
} spin_http_response_t;
|
||||
void spin_http_response_free(spin_http_response_t *ptr);
|
||||
void spin_http_handle_http_request(spin_http_request_t *req, spin_http_response_t *ret0);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -1,231 +1,193 @@
|
|||
#include <stdlib.h>
|
||||
#include "wasi-outbound-http.h"
|
||||
#include <wasi-outbound-http.h>
|
||||
|
||||
__attribute__((weak, export_name("canonical_abi_realloc"))) void *canonical_abi_realloc(
|
||||
void *ptr,
|
||||
size_t orig_size,
|
||||
size_t org_align,
|
||||
size_t new_size)
|
||||
{
|
||||
__attribute__((weak, export_name("canonical_abi_realloc")))
|
||||
void *canonical_abi_realloc(
|
||||
void *ptr,
|
||||
size_t orig_size,
|
||||
size_t org_align,
|
||||
size_t new_size
|
||||
) {
|
||||
void *ret = realloc(ptr, new_size);
|
||||
if (!ret)
|
||||
abort();
|
||||
abort();
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((weak, export_name("canonical_abi_free"))) void canonical_abi_free(
|
||||
void *ptr,
|
||||
size_t size,
|
||||
size_t align)
|
||||
{
|
||||
__attribute__((weak, export_name("canonical_abi_free")))
|
||||
void canonical_abi_free(
|
||||
void *ptr,
|
||||
size_t size,
|
||||
size_t align
|
||||
) {
|
||||
free(ptr);
|
||||
}
|
||||
#include <string.h>
|
||||
|
||||
void wasi_outbound_http_string_set(wasi_outbound_http_string_t *ret, const char *s)
|
||||
{
|
||||
ret->ptr = (char *)s;
|
||||
void wasi_outbound_http_string_set(wasi_outbound_http_string_t *ret, const char *s) {
|
||||
ret->ptr = (char*) s;
|
||||
ret->len = strlen(s);
|
||||
}
|
||||
|
||||
void wasi_outbound_http_string_dup(wasi_outbound_http_string_t *ret, const char *s)
|
||||
{
|
||||
void wasi_outbound_http_string_dup(wasi_outbound_http_string_t *ret, const char *s) {
|
||||
ret->len = strlen(s);
|
||||
ret->ptr = canonical_abi_realloc(NULL, 0, 1, ret->len);
|
||||
memcpy(ret->ptr, s, ret->len);
|
||||
}
|
||||
|
||||
void wasi_outbound_http_string_free(wasi_outbound_http_string_t *ret)
|
||||
{
|
||||
void wasi_outbound_http_string_free(wasi_outbound_http_string_t *ret) {
|
||||
canonical_abi_free(ret->ptr, ret->len, 1);
|
||||
ret->ptr = NULL;
|
||||
ret->len = 0;
|
||||
}
|
||||
void wasi_outbound_http_body_free(wasi_outbound_http_body_t *ptr)
|
||||
{
|
||||
void wasi_outbound_http_body_free(wasi_outbound_http_body_t *ptr) {
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
|
||||
}
|
||||
void wasi_outbound_http_tuple2_string_string_free(wasi_outbound_http_tuple2_string_string_t *ptr)
|
||||
{
|
||||
void wasi_outbound_http_tuple2_string_string_free(wasi_outbound_http_tuple2_string_string_t *ptr) {
|
||||
wasi_outbound_http_string_free(&ptr->f0);
|
||||
wasi_outbound_http_string_free(&ptr->f1);
|
||||
}
|
||||
void wasi_outbound_http_headers_free(wasi_outbound_http_headers_t *ptr)
|
||||
{
|
||||
for (size_t i = 0; i < ptr->len; i++)
|
||||
{
|
||||
void wasi_outbound_http_headers_free(wasi_outbound_http_headers_t *ptr) {
|
||||
for (size_t i = 0; i < ptr->len; i++) {
|
||||
wasi_outbound_http_tuple2_string_string_free(&ptr->ptr[i]);
|
||||
}
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 16, 4);
|
||||
}
|
||||
void wasi_outbound_http_params_free(wasi_outbound_http_params_t *ptr)
|
||||
{
|
||||
for (size_t i = 0; i < ptr->len; i++)
|
||||
{
|
||||
void wasi_outbound_http_params_free(wasi_outbound_http_params_t *ptr) {
|
||||
for (size_t i = 0; i < ptr->len; i++) {
|
||||
wasi_outbound_http_tuple2_string_string_free(&ptr->ptr[i]);
|
||||
}
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 16, 4);
|
||||
}
|
||||
void wasi_outbound_http_uri_free(wasi_outbound_http_uri_t *ptr)
|
||||
{
|
||||
void wasi_outbound_http_uri_free(wasi_outbound_http_uri_t *ptr) {
|
||||
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
|
||||
}
|
||||
void wasi_outbound_http_option_body_free(wasi_outbound_http_option_body_t *ptr)
|
||||
{
|
||||
switch (ptr->tag)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
wasi_outbound_http_body_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
void wasi_outbound_http_option_body_free(wasi_outbound_http_option_body_t *ptr) {
|
||||
switch (ptr->tag) {
|
||||
case 1: {
|
||||
wasi_outbound_http_body_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void wasi_outbound_http_request_free(wasi_outbound_http_request_t *ptr)
|
||||
{
|
||||
void wasi_outbound_http_request_free(wasi_outbound_http_request_t *ptr) {
|
||||
wasi_outbound_http_uri_free(&ptr->uri);
|
||||
wasi_outbound_http_headers_free(&ptr->headers);
|
||||
wasi_outbound_http_params_free(&ptr->params);
|
||||
wasi_outbound_http_option_body_free(&ptr->body);
|
||||
}
|
||||
void wasi_outbound_http_option_headers_free(wasi_outbound_http_option_headers_t *ptr)
|
||||
{
|
||||
switch (ptr->tag)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
wasi_outbound_http_headers_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
void wasi_outbound_http_option_headers_free(wasi_outbound_http_option_headers_t *ptr) {
|
||||
switch (ptr->tag) {
|
||||
case 1: {
|
||||
wasi_outbound_http_headers_free(&ptr->val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void wasi_outbound_http_response_free(wasi_outbound_http_response_t *ptr)
|
||||
{
|
||||
void wasi_outbound_http_response_free(wasi_outbound_http_response_t *ptr) {
|
||||
wasi_outbound_http_option_headers_free(&ptr->headers);
|
||||
wasi_outbound_http_option_body_free(&ptr->body);
|
||||
}
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
// 0 if `val` is `ok`, 1 otherwise
|
||||
uint8_t tag;
|
||||
union
|
||||
{
|
||||
union {
|
||||
wasi_outbound_http_response_t ok;
|
||||
wasi_outbound_http_http_error_t err;
|
||||
} val;
|
||||
} wasi_outbound_http_expected_response_http_error_t;
|
||||
static int64_t RET_AREA[8];
|
||||
__attribute__((import_module("wasi-outbound-http"), import_name("request"))) void __wasm_import_wasi_outbound_http_request(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
|
||||
wasi_outbound_http_http_error_t wasi_outbound_http_request(wasi_outbound_http_request_t *req, wasi_outbound_http_response_t *ret0)
|
||||
{
|
||||
__attribute__((import_module("wasi-outbound-http"), import_name("request")))
|
||||
void __wasm_import_wasi_outbound_http_request(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
|
||||
wasi_outbound_http_http_error_t wasi_outbound_http_request(wasi_outbound_http_request_t *req, wasi_outbound_http_response_t *ret0) {
|
||||
int32_t variant;
|
||||
switch ((int32_t)(*req).method)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
variant = 0;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
variant = 1;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
variant = 2;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
variant = 3;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
variant = 4;
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
variant = 5;
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
variant = 6;
|
||||
break;
|
||||
}
|
||||
switch ((int32_t) (*req).method) {
|
||||
case 0: {
|
||||
variant = 0;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
variant = 1;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
variant = 2;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
variant = 3;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
variant = 4;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
variant = 5;
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
variant = 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32_t variant8;
|
||||
int32_t variant9;
|
||||
int32_t variant10;
|
||||
switch ((int32_t)((*req).body).tag)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
variant8 = 0;
|
||||
variant9 = 0;
|
||||
variant10 = 0;
|
||||
break;
|
||||
switch ((int32_t) ((*req).body).tag) {
|
||||
case 0: {
|
||||
variant8 = 0;
|
||||
variant9 = 0;
|
||||
variant10 = 0;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const wasi_outbound_http_body_t *payload7 = &((*req).body).val;
|
||||
variant8 = 1;
|
||||
variant9 = (int32_t) (*payload7).ptr;
|
||||
variant10 = (int32_t) (*payload7).len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
const wasi_outbound_http_body_t *payload7 = &((*req).body).val;
|
||||
variant8 = 1;
|
||||
variant9 = (int32_t)(*payload7).ptr;
|
||||
variant10 = (int32_t)(*payload7).len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32_t ptr = (int32_t)&RET_AREA;
|
||||
__wasm_import_wasi_outbound_http_request(variant, (int32_t)((*req).uri).ptr, (int32_t)((*req).uri).len, (int32_t)((*req).headers).ptr, (int32_t)((*req).headers).len, (int32_t)((*req).params).ptr, (int32_t)((*req).params).len, variant8, variant9, variant10, ptr);
|
||||
int32_t ptr = (int32_t) &RET_AREA;
|
||||
__wasm_import_wasi_outbound_http_request(variant, (int32_t) ((*req).uri).ptr, (int32_t) ((*req).uri).len, (int32_t) ((*req).headers).ptr, (int32_t) ((*req).headers).len, (int32_t) ((*req).params).ptr, (int32_t) ((*req).params).len, variant8, variant9, variant10, ptr);
|
||||
wasi_outbound_http_expected_response_http_error_t variant13;
|
||||
variant13.tag = *((int32_t *)(ptr + 0));
|
||||
switch ((int32_t)variant13.tag)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
wasi_outbound_http_option_headers_t variant11;
|
||||
variant11.tag = *((int32_t *)(ptr + 16));
|
||||
switch ((int32_t)variant11.tag)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
variant11.val = (wasi_outbound_http_headers_t){(wasi_outbound_http_tuple2_string_string_t *)(*((int32_t *)(ptr + 24))), (size_t)(*((int32_t *)(ptr + 32)))};
|
||||
break;
|
||||
}
|
||||
}
|
||||
wasi_outbound_http_option_body_t variant12;
|
||||
variant12.tag = *((int32_t *)(ptr + 40));
|
||||
switch ((int32_t)variant12.tag)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
variant12.val = (wasi_outbound_http_body_t){(uint8_t *)(*((int32_t *)(ptr + 48))), (size_t)(*((int32_t *)(ptr + 56)))};
|
||||
break;
|
||||
}
|
||||
}
|
||||
variant13.val.ok = (wasi_outbound_http_response_t){
|
||||
(uint16_t)(*((int32_t *)(ptr + 8))),
|
||||
variant13.tag = *((int32_t*) (ptr + 0));
|
||||
switch ((int32_t) variant13.tag) {
|
||||
case 0: {
|
||||
wasi_outbound_http_option_headers_t variant11;
|
||||
variant11.tag = *((int32_t*) (ptr + 16));
|
||||
switch ((int32_t) variant11.tag) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
variant11.val = (wasi_outbound_http_headers_t) { (wasi_outbound_http_tuple2_string_string_t*)(*((int32_t*) (ptr + 24))), (size_t)(*((int32_t*) (ptr + 32))) };
|
||||
break;
|
||||
}
|
||||
}
|
||||
wasi_outbound_http_option_body_t variant12;
|
||||
variant12.tag = *((int32_t*) (ptr + 40));
|
||||
switch ((int32_t) variant12.tag) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
variant12.val = (wasi_outbound_http_body_t) { (uint8_t*)(*((int32_t*) (ptr + 48))), (size_t)(*((int32_t*) (ptr + 56))) };
|
||||
break;
|
||||
}
|
||||
}
|
||||
variant13.val.ok = (wasi_outbound_http_response_t) {
|
||||
(uint16_t) (*((int32_t*) (ptr + 8))),
|
||||
variant11,
|
||||
variant12,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
variant13.val.err = *((int32_t *)(ptr + 8));
|
||||
break;
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
variant13.val.err = *((int32_t*) (ptr + 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
*ret0 = variant13.val.ok;
|
||||
return variant13.tag ? variant13.val.err : -1;
|
||||
|
|
Loading…
Reference in New Issue