fastlike

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2021 License: MIT Imports: 18 Imported by: 0

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> -backend <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/example.wasm -backend <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 -backend localhost:8000 -b 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

Documentation

Index

Constants

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

Constants used for return values from ABI functions. See https://docs.rs/fastly-shared for more.

View Source
const (
	Http09 int32 = 0
	Http10 int32 = 1
	Http11 int32 = 2
	Http2  int32 = 3
	Http3  int32 = 4
)
View Source
const HandleInvalid = 4294967295 - 1

HandleInvalid is returned to guests when they attempt to obtain a handle that doesn't exist. For instance, opening a dictionary that is not registered Note that this is dictinct from XqdErrInvalidHandle, which is returned when callers attempt to *use* an otherwise invalid handle, such as attempting to send a request whose handle has not been created.

Variables

This section is empty.

Functions

This section is empty.

Types

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
}

BodyHandles is a slice of BodyHandle with methods to get and create

func NewBodyHandles

func NewBodyHandles() *BodyHandles

func (*BodyHandles) Close

func (bhs *BodyHandles) Close(id int) error

func (*BodyHandles) Get

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

Get returns the BodyHandle identified by id or nil if one does not exist

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)

NewReader creates a BodyHandle whose reader and closer is connected to the supplied ReadCloser

func (*BodyHandles) NewWriter

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

NewWriter creates a BodyHandle whose writer is connected to the supplied Writer

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 ...Option) *Fastlike

New returns a new Fastlike ready to create new instances from

func (*Fastlike) Instantiate

func (f *Fastlike) Instantiate(opts ...Option) *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

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 ...Option) *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 LineWriter added in v0.5.0

type LineWriter struct{ io.Writer }

LineWriter takes a writer and returns a new writer that ensures each Write call ends with a newline

func (LineWriter) Write added in v0.5.0

func (lw LineWriter) Write(data []byte) (int, error)

Write implements io.Writer for LineWriter

type LookupFunc added in v0.5.0

type LookupFunc func(key string) string

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 Option added in v0.5.0

type Option func(*Instance)

Option is a functional option applied to an Instance at creation time

func WithBackend added in v0.5.0

func WithBackend(name string, h http.Handler) Option

WithBackend registers an `http.Handler` identified by `name` used for subrequests targeting that backend

func WithDefaultBackend added in v0.5.0

func WithDefaultBackend(fn func(name string) http.Handler) Option

WithDefaultBackend is an Option to override the default subrequest backend.

func WithDefaultLogger added in v0.5.0

func WithDefaultLogger(fn func(name string) io.Writer) Option

WithDefaultLogger sets the default logger used for logs issued by the guest This one is different from WithLogger, because it accepts a name and returns a writer so that custom implementations can print the name, if they prefer

func WithDictionary added in v0.5.0

func WithDictionary(name string, fn LookupFunc) Option

WithDictionary registers a new dictionary with a corresponding lookup function

func WithGeo added in v0.5.0

func WithGeo(fn func(net.IP) Geo) Option

WithGeo replaces the default geographic lookup function

func WithLogger added in v0.5.0

func WithLogger(name string, w io.Writer) Option

WithLogger registers a new log endpoint usable from a wasm guest

func WithSecureFunc added in v0.5.0

func WithSecureFunc(fn func(*http.Request) bool) Option

WithSecureFunc is an Option 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 WithUserAgentParser added in v0.5.0

func WithUserAgentParser(fn UserAgentParser) Option

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

func WithVerbosity added in v0.5.0

func WithVerbosity(v int) Option

WithVerbosity controls how verbose the system level logs are. A verbosity of 2 prints all calls from the wasm guest into the host methods Currently, verbosity less than 2 does nothing

type PrefixWriter added in v0.5.0

type PrefixWriter struct {
	io.Writer
	// contains filtered or unexported fields
}

func NewPrefixWriter added in v0.5.0

func NewPrefixWriter(prefix string, w io.Writer) *PrefixWriter

func (*PrefixWriter) Write added in v0.5.0

func (w *PrefixWriter) Write(data []byte) (n int, err error)

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
}

RequestHandles is a slice of RequestHandle with functions to get and create

func (*RequestHandles) Get

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

Get returns the RequestHandle identified by id or nil if one does not exist.

func (*RequestHandles) New

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

New creates a new RequestHandle and returns its handle id and the handle itself.

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
}

ResponseHandles is a slice of ResponseHandle with functions to get and create

func (*ResponseHandles) Get

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

Get returns the ResponseHandle identified by id or nil if one does not exist.

func (*ResponseHandles) New

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

New creates a new ResponseHandle and returns its handle id and the handle itself.

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

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL