README

fastlike

fastlike is a Go project that implements the Fastly Compute@Edge ABI using wasmtime and exposes a http.Handler for you to use.

There's a proxy implementation in cmd/fastlike which you can run with:

$ go run ./cmd/fastlike -wasm <wasmfile> -proxy-to <proxy address>

You can use a provided wasm binary by building the testdata:

$ cd testdata; fastly compute build; cd ..
$ go run ./cmd/fastlike -wasm ./testdata/bin/main.wasm -proxy-to <proxy address>

You don't need the fastly CLI to build the test program either, as long as you have rust installed and the wasm32-wasi target available:

$ cd testdata; cargo build; cd ..
$ go run ./cmd/fastlike -wasm ./testdata/target/wasm32-wasi/debug/fastlike-example.wasm -proxy-to <proxy address>

However, the fastly cli will help you get your toolchains up to date.

For a more full-featured example:

# in one terminal:
$ cd testdata; fastly compute build; cd ..
$ go run ./cmd/fastlike -wasm ./testdata/bin/main.wasm -proxy-to localhost:8000 -bind localhost:5000

# in another
$ python3 -m http.server

# in a third
$ curl localhost:5000/testdata/src/main.rs

Go, running Rust, calling Go, proxying to Python.

TODO

  • How to handle Go errors? We just panic.
  • How to handle errors over the ABI? Just return the proper XQD status?
    • Maybe have Fastlike take a writer to send logs to, and abi methods can write warnings/errors there
  • Implement the rest of the ABI
Expand ▾ Collapse ▴

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GeoHandler

func GeoHandler(fn func(net.IP) Geo) http.Handler

Types

type BackendHandler

type BackendHandler func(backend string) http.Handler

BackendHandler is a function that takes a backend name and returns an http.Handler that can satisfy that backend

type BodyHandle

type BodyHandle struct {

	// contains filtered or unexported fields

}

BodyHandle represents a body. It could be readable or writable, but not both. For cases where it's already connected to a request or response body, the reader or writer properties will reference the original request or response respectively. For new bodies, `buf` will hold the contents and either the reader or writer will wrap it.

func (*BodyHandle) Close

func (b *BodyHandle) Close() error

Close implements io.Closer for a BodyHandle

func (*BodyHandle) Read

func (b *BodyHandle) Read(p []byte) (int, error)

Read implements io.Reader for a BodyHandle

func (*BodyHandle) Size

func (b *BodyHandle) Size() int64

func (*BodyHandle) Write

func (b *BodyHandle) Write(p []byte) (int, error)

Write implements io.Writer for a BodyHandle

type BodyHandles

type BodyHandles struct {
	// contains filtered or unexported fields

}

func (*BodyHandles) Get

func (bhs *BodyHandles) Get(id int) *BodyHandle

func (*BodyHandles) NewBuffer

func (bhs *BodyHandles) NewBuffer() (int, *BodyHandle)

NewBuffer creates a BodyHandle backed by a buffer which can be read from or written to

func (*BodyHandles) NewReader

func (bhs *BodyHandles) NewReader(rdr io.ReadCloser) (int, *BodyHandle)

func (*BodyHandles) NewWriter

func (bhs *BodyHandles) NewWriter(w io.Writer) (int, *BodyHandle)

type ByteMemory

type ByteMemory []byte

ByteMemory is a MemorySlice mostly used for tests, where you want to be able to write directly into the memory slice and read it out

func (ByteMemory) Cap

func (m ByteMemory) Cap() int

Cap is the total capacity of the memory slice

func (ByteMemory) Data

func (m ByteMemory) Data() []byte

Data returns the underlying byte slice

func (ByteMemory) Len

func (m ByteMemory) Len() int

Len is the current length of the memory slice

type Fastlike

type Fastlike struct {

	// contains filtered or unexported fields

}

Fastlike is the entrypoint to the package, used to construct new instances ready to serve incoming HTTP requests. It maintains a pool of compiled and linked wasm modules to amortize startup costs across multiple requests. In the case of a spike of incoming requests, new instances will be constructed on-demand and thrown away when the request is finished to avoid an ever-increasing memory cost.

func New

func New(wasmfile string, instanceOpts ...InstanceOption) *Fastlike

New returns a new Fastlike ready to create new instances from

func (*Fastlike) Instantiate

func (f *Fastlike) Instantiate(opts ...InstanceOption) *Instance

Instantiate returns an Instance ready to serve requests. This may come from the instance pool if one is available, but otherwise will be constructed fresh. This *must* be called for each request, as the XQD runtime is designed around a single request/response pair for each instance.

func (*Fastlike) ServeHTTP

func (f *Fastlike) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler for a Fastlike module. It's a convenience function over `Instantiate()` followed by `.ServeHTTP` on the returned instance.

func (*Fastlike) Warmup

func (f *Fastlike) Warmup(n int)

type Geo

type Geo struct {
	ASName           string  `json:"as_name"`
	ASNumber         int     `json:"as_number"`
	AreaCode         int     `json:"area_code"`
	City             string  `json:"city"`
	ConnSpeed        string  `json:"conn_speed"`
	ConnType         string  `json:"conn_type"`
	Continent        string  `json:"continent"`
	CountryCode      string  `json:"country_code"`
	CountryCode3     string  `json:"country_code3"`
	CountryName      string  `json:"country_name"`
	Latitude         float64 `json:"latitude"`
	Longitude        float64 `json:"longitude"`
	MetroCode        int     `json:"metro_code"`
	PostalCode       string  `json:"postal_code"`
	ProxyDescription string  `json:"proxy_description"`
	ProxyType        string  `json:"proxy_type"`
	Region           string  `json:"region,omitempty"`
	UTCOffset        int     `json:"utc_offset"`
}

Geo represents geographic data associated with a particular IP address See: https://docs.rs/crate/fastly/0.3.2/source/src/geo.rs

func DefaultGeo

func DefaultGeo(ip net.IP) Geo

type HttpVersion

type HttpVersion int32
const (
	Http09 HttpVersion = 0
	Http10 HttpVersion = 1
	Http11 HttpVersion = 2
	Http2  HttpVersion = 3
	Http3  HttpVersion = 4
)

type Instance

type Instance struct {

	// contains filtered or unexported fields

}

Instance is an implementation of the XQD ABI along with a wasmtime.Instance configured to use it TODO: This has no public methods or public members. Should it even be public? The API could just be New and Fastlike.ServeHTTP(w, r)?

func NewInstance

func NewInstance(wasmbytes []byte, opts ...InstanceOption) *Instance

NewInstance returns an http.Handler that can handle a single request.

func (*Instance) ServeHTTP

func (i *Instance) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves the supplied request and response pair. This is not safe to call twice.

type InstanceOption

type InstanceOption func(*Instance)

InstanceOption is a functional option applied to an Instance when it's created

func BackendHandlerOption

func BackendHandlerOption(b BackendHandler) InstanceOption

BackendHandlerOption is an InstanceOption which configures how subrequests are issued by backend

func GeoHandlerOption

func GeoHandlerOption(b http.Handler) InstanceOption

GeoHandlerOption is an InstanceOption which controls how geographic requests are handled

func LoggerConfigOption

func LoggerConfigOption(fn func(logger, abilogger *log.Logger)) InstanceOption

LoggerConfigOption is an InstanceOption that allows configuring the loggers

func SecureRequestOption

func SecureRequestOption(fn func(*http.Request) bool) InstanceOption

SecureRequestOption is an InstanceOption that determines if a request should be considered "secure" or not. If it returns true, the request url has the "https" scheme and the "fastly-ssl" header set when going into the wasm program. The default implementation checks if `req.TLS` is non-nil.

func UserAgentParserOption

func UserAgentParserOption(fn UserAgentParser) InstanceOption

UserAgUserAgentParserOption is an InstanceOption that converts user agent header values into UserAgent structs, called when the guest code uses the user agent parser XQD call.

type Memory

type Memory struct {
	MemorySlice
}

Memory is a wrapper around a MemorySlice that adds convenience functions for reading and writing

func (*Memory) PutInt32

func (m *Memory) PutInt32(v int32, offset int64)

func (*Memory) PutInt64

func (m *Memory) PutInt64(v int64, offset int64)

func (*Memory) PutUint16

func (m *Memory) PutUint16(v uint16, offset int64)

func (*Memory) PutUint32

func (m *Memory) PutUint32(v uint32, offset int64)

func (*Memory) PutUint64

func (m *Memory) PutUint64(v uint64, offset int64)

func (*Memory) PutUint8

func (m *Memory) PutUint8(v uint8, offset int64)

func (*Memory) ReadAt

func (m *Memory) ReadAt(p []byte, offset int64) (int, error)

func (*Memory) ReadUint8

func (m *Memory) ReadUint8(offset int64) uint8

func (*Memory) Uint16

func (m *Memory) Uint16(offset int64) uint16

func (*Memory) Uint32

func (m *Memory) Uint32(offset int64) uint32

func (*Memory) Uint64

func (m *Memory) Uint64(offset int64) uint64

func (*Memory) WriteAt

func (m *Memory) WriteAt(p []byte, offset int64) (int, error)

type MemorySlice

type MemorySlice interface {
	Data() []byte
	Len() int
	Cap() int
}

MemorySlice represents an underlying slice of memory from a wasm program. An implementation of MemorySlice is most often wrapped with a Memory, which provides convenience functions to read and write different values.

type RequestHandle

type RequestHandle struct {
	*http.Request

	// contains filtered or unexported fields

}

RequestHandle is an http.Request with extra metadata Notably, the request body is ignored and instead the guest will provide a BodyHandle to use

type RequestHandles

type RequestHandles struct {
	// contains filtered or unexported fields

}

func (*RequestHandles) Get

func (rhs *RequestHandles) Get(id int) *RequestHandle

func (*RequestHandles) New

func (rhs *RequestHandles) New() (int, *RequestHandle)

type ResponseHandle

type ResponseHandle struct {
	*http.Response

	// contains filtered or unexported fields

}

ResponseHandle is an http.Response with extra metadata Notably, the response body is ignored and instead the guest will provide a BodyHandle to use

type ResponseHandles

type ResponseHandles struct {
	// contains filtered or unexported fields

}

func (*ResponseHandles) Get

func (rhs *ResponseHandles) Get(id int) *ResponseHandle

func (*ResponseHandles) New

func (rhs *ResponseHandles) New() (int, *ResponseHandle)

type UserAgent

type UserAgent struct {
	Family string
	Major  string
	Minor  string
	Patch  string
}

UserAgent represents a user agent.

type UserAgentParser

type UserAgentParser func(uastring string) UserAgent

type XqdStatus

type XqdStatus int32

XqdStatus is a status code returned from every XQD ABI method as described in crate `fastly-shared`

const (
	XqdStatusOK           XqdStatus = 0
	XqdError              XqdStatus = 1
	XqdErrInvalidArgument XqdStatus = 2
	XqdErrInvalidHandle   XqdStatus = 3
	XqdErrBufferLength    XqdStatus = 4
	XqdErrUnsupported     XqdStatus = 5
	XqdErrBadAlignment    XqdStatus = 6
	XqdErrHttpParse       XqdStatus = 7
	XqdErrHttpUserInvalid XqdStatus = 8
	XqdErrHttpIncomplete  XqdStatus = 9
)

Directories

Path Synopsis
cmd/fastlike
MODULE caddy-fastlike