Skip to content

Commit c6278fb

Browse files
committed
feat: implement spin-redirect as Non-WAGI component and add support for custom status codes
Signed-off-by: Thorsten Hans <[email protected]>
1 parent b84b210 commit c6278fb

File tree

6 files changed

+168
-11
lines changed

6 files changed

+168
-11
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ ENABLE_WASM_OPT ?= true
22

33
.PHONY: build
44
build:
5-
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -o redirect.wasm redirect.go
5+
tinygo build -target=wasi -gc=leaking -o redirect.wasm ./
66
ifeq ($(ENABLE_WASM_OPT),true)
77
wasm-opt -Os -o redirect.wasm redirect.wasm
88
endif

README.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,60 @@ This is a simple HTTP redirect component written in Go.
44

55
This is not a Spin application in itself but a component that can be used in applications to redirect a route.
66

7-
Example usage:
7+
## Configuration
88

9+
The `spin-redirect` component can be configured to address your needs in different scenarios. `spin-redirect` tries to load configuration data from multiple places in the specified order:
10+
11+
1. Spin component configuration
12+
2. Environment variables
13+
14+
The following table outlines available configuration values:
15+
16+
| Key | Description | Default Value |
17+
|---------------|-------------------------------------------------------|------------------|
18+
| `destination` | Where should the component redirect to | *(empty string)* |
19+
| `statuscode` | What HTTP status code should be used when redirecting | `302` |
20+
21+
The `spin-redirect` component tries to lookup the config value in the Spin component configuration using the keys shown in the table above (lower case). If desired key is not present, it tries transforms the key to upper case (e.g., `DESTINATION`) and checks environment variables.
22+
23+
## Example usage
24+
25+
The following snippet shows how to add and configure `spin-redirect` in your `spin.toml` using environment variables
26+
27+
```toml
28+
spin_manifest_version = "1"
29+
description = ""
30+
name = "test"
31+
trigger = { type = "http", base = "/" }
32+
version = "0.1.0"
33+
34+
# Redirect / to /index.html using HTTP status code 301
35+
[[component]]
36+
id = "redirect-sample"
37+
source = "path/to/redirect.wasm"
38+
environment = { DESTINATION = "/index.html", STATUSCODE = "301" }
39+
40+
[component.trigger]
41+
route = "/"
942
```
10-
# Redirect / to /index.html
43+
44+
Alternatively, you can use component configuration to configure `spin-redirect` as shown below:
45+
46+
```toml
47+
spin_manifest_version = "1"
48+
description = ""
49+
name = "test"
50+
trigger = { type = "http", base = "/" }
51+
version = "0.1.0"
52+
53+
# Redirect / to /index.html using HTTP status code 301
1154
[[component]]
12-
id = "redirect-to-index"
55+
id = "redirect-sample"
1356
source = "path/to/redirect.wasm"
14-
environment = { DESTINATION = "/index.html" }
57+
[component.config]
58+
destination="/index.html"
59+
statuscode="301"
1560
[component.trigger]
1661
route = "/"
17-
executor = { type = "wagi" }
62+
1863
```

config.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
config "github.com/fermyon/spin/sdk/go/config"
8+
)
9+
10+
type ConfigReader interface {
11+
Get(key string) string
12+
}
13+
14+
/*
15+
DefaultConfigReader implements ConfigReader interface
16+
and provides a Get method for reading configuration values.
17+
18+
It first tries to find the value in Spin configuration.
19+
If the key is not found in Spin configuration, it will try
20+
to find the value in the environment variables.
21+
*/
22+
type DefaultConfigReader struct{}
23+
24+
// NewDefaultConfigReader returns a new DefaultConfigReader
25+
func NewDefaultConfigReader() DefaultConfigReader {
26+
return DefaultConfigReader{}
27+
}
28+
29+
/*
30+
Get returns the configuration value for the given key
31+
If the key is not found in Spin configuration, it will try
32+
to find the value in the environment variables.
33+
34+
For looking up a value in spin configuration, keys are used as is (case sensitive).
35+
For looking up a value in environment variables, keys are converted to uppercase.
36+
37+
If key is neither found in Spin configuration nor in environment variables,
38+
an empty string is returned.
39+
40+
Usage:
41+
42+
cfg := NewDefaultConfigReader()
43+
value := cfg.Get("destination")
44+
*/
45+
func (c DefaultConfigReader) Get(key string) string {
46+
v, err := config.Get(key)
47+
if err != nil {
48+
return os.Getenv(strings.ToUpper(key))
49+
}
50+
return v
51+
}

go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
module github.com/fermyon/finicky-whiskers/redirect
22

3-
go 1.17
3+
go 1.17
4+
5+
require github.com/fermyon/spin/sdk/go v1.4.2
6+
7+
require github.com/julienschmidt/httprouter v1.3.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/fermyon/spin/sdk/go v1.4.2 h1:4U2J2WooKptCa4zeBetKctz/DEJxv3RvGauW0bZ+e6U=
2+
github.com/fermyon/spin/sdk/go v1.4.2/go.mod h1:yb8lGesopgj/GwPzLPATxcOeqWZT/HjrzEFfwbztAXE=
3+
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
4+
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=

redirect.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,64 @@
11
package main
22

33
import (
4-
"fmt"
5-
"os"
4+
"net/http"
5+
"strconv"
6+
7+
spinhttp "github.com/fermyon/spin/sdk/go/http"
8+
)
9+
10+
const (
11+
// Default value for HTTP status code
12+
DefaultStatusCode int = http.StatusFound
13+
// Key for loading desired destination
14+
destinationKey string = "destination"
15+
// Key for loading desired HTTP status code
16+
statusCodeKey string = "statuscode"
617
)
718

19+
func init() {
20+
r := NewSpinRedirect()
21+
spinhttp.Handle(r.handleFunc)
22+
}
23+
824
func main() {
9-
dest := os.Getenv("DESTINATION")
10-
fmt.Printf("status: 302\nlocation: %s\n\n", dest)
25+
}
26+
27+
// SpinRedirect is a struct that provides a handleFunc
28+
// for redirecting to a destination URL using configurable HTTP status code.
29+
type SpinRedirect struct {
30+
cfg ConfigReader
31+
}
32+
33+
// NewSpinRedirect returns a new SpinRedirect
34+
func NewSpinRedirect() SpinRedirect {
35+
return SpinRedirect{
36+
cfg: NewDefaultConfigReader(),
37+
}
38+
}
39+
40+
func (s SpinRedirect) handleFunc(w http.ResponseWriter, r *http.Request) {
41+
dest := s.getDestination()
42+
code := s.getStatusCode()
43+
44+
w.Header().Set("Location", dest)
45+
w.WriteHeader(code)
46+
}
47+
48+
// getDestination returns the destination URL
49+
// If no destination is found, an empty string is returned.
50+
func (s SpinRedirect) getDestination() string {
51+
return s.cfg.Get(destinationKey)
52+
}
53+
54+
// getStatusCode returns the HTTP status code
55+
// If no status code is found, or if the provided value is invalid,
56+
// DefaultStatusCode is returned.
57+
func (s SpinRedirect) getStatusCode() int {
58+
str := s.cfg.Get(statusCodeKey)
59+
code, err := strconv.Atoi(str)
60+
if err != nil {
61+
return DefaultStatusCode
62+
}
63+
return code
1164
}

0 commit comments

Comments
 (0)