Skip to content

Commit

Permalink
Added COOKIE_EXPIRY
Browse files Browse the repository at this point in the history
  • Loading branch information
lindell committed Jul 10, 2019
1 parent a203f23 commit 489d0bf
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 7 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ VERSION_GREEN_BACKGROUND_PROBABILITY=0.4
```

#### Setting to change the behavior of revaboxy
| Name | Default | Description |
| ---- | ------- | ----------- |
| `HOST` | ` ` | The host that the server should listen to, the default value makes it listen on all hosts |
| `PORT` | `80` | The port that server should listen on |
| `HEADER_NAME` | `Revaboxy-Name` | The header name sent to the downsteam application |
| `COOKIE_NAME` | `revaboxy-name` | The cookie name that is set at the client to keep track of which version was selected |
| Name | Default | Description |
| --------------- | --------------- | ----------------------------------------------------------------------------------------- |
| `HOST` | ` ` | The host that the server should listen to, the default value makes it listen on all hosts |
| `PORT` | `80` | The port that server should listen on |
| `HEADER_NAME` | `Revaboxy-Name` | The header name sent to the downsteam application |
| `COOKIE_NAME` | `revaboxy-name` | The cookie name that is set at the client to keep track of which version was selected |
| `COOKIE_EXPIRY` | `7d` | The time before the cookie containing the a/b test version expires |
1 change: 1 addition & 0 deletions build/Dockerfile-alpine
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM golang:latest as builder
WORKDIR /go/src/github.com/lindell/revaboxy
COPY cmd cmd
COPY pkg pkg
COPY internal internal
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o app cmd/revaboxy/main.go

FROM alpine:latest
Expand Down
1 change: 1 addition & 0 deletions build/Dockerfile-scratch
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM golang:latest as builder
WORKDIR /go/src/github.com/lindell/revaboxy
COPY cmd cmd
COPY pkg pkg
COPY internal internal
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o app cmd/revaboxy/main.go

FROM scratch
Expand Down
9 changes: 9 additions & 0 deletions cmd/revaboxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"strings"
"syscall"

"github.com/lindell/revaboxy/internal/time"

"github.com/lindell/revaboxy/pkg/revaboxy"
)

Expand All @@ -33,6 +35,13 @@ func main() {
if cookieName, ok := syscall.Getenv("COOKIE_NAME"); ok {
settings = append(settings, revaboxy.WithCookieName(cookieName))
}
if cookieExpiryStr, ok := syscall.Getenv("COOKIE_EXPIRY"); ok {
cookieExpiry, err := time.ParseDuration(cookieExpiryStr)
if err != nil {
log.Fatal("could not parse cookie expiry", err)
}
settings = append(settings, revaboxy.WithCookieExpiry(cookieExpiry))
}

proxy, err := revaboxy.New(
versions,
Expand Down
172 changes: 172 additions & 0 deletions internal/time/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// This package contains the parseDuration function from the time package in the standard library

package time

import (
"errors"
"time"
)

var errLeadingInt = errors.New("time: bad [0-9]*") // never printed

// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x > (1<<63-1)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
if x < 0 {
// overflow
return 0, "", errLeadingInt
}
}
return x, s[i:], nil
}

// leadingFraction consumes the leading [0-9]* from s.
// It is used only for fractions, so does not return an error on overflow,
// it just stops accumulating precision.
func leadingFraction(s string) (x int64, scale float64, rem string) {
i := 0
scale = 1
overflow := false
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if overflow {
continue
}
if x > (1<<63-1)/10 {
// It's possible for overflow to give a positive number, so take care.
overflow = true
continue
}
y := x*10 + int64(c) - '0'
if y < 0 {
overflow = true
continue
}
x = y
scale *= 10
}
return x, scale, s[i:]
}

var unitMap = map[string]int64{
"s": int64(time.Second),
"m": int64(time.Minute),
"h": int64(time.Hour),
"d": int64(time.Hour * 24),
}

// ParseDuration parses a duration string.
// A duration string is a possibly signed sequence of
// decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
func ParseDuration(s string) (time.Duration, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
var d int64
neg := false

// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("time: invalid duration " + orig)
}
for s != "" {
var (
v, f int64 // integers before, after decimal point
scale float64 = 1 // value = v + f/scale
)

var err error

// The next character must be [0-9.]
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
return 0, errors.New("time: invalid duration " + orig)
}
// Consume [0-9]*
pl := len(s)
v, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
pre := pl != len(s) // whether we consumed anything before a period

// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
f, scale, s = leadingFraction(s)
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("time: invalid duration " + orig)
}

// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || '0' <= c && c <= '9' {
break
}
}
if i == 0 {
return 0, errors.New("time: missing unit in duration " + orig)
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
}
if v > (1<<63-1)/unit {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
v *= unit
if f > 0 {
// float64 is needed to be nanosecond accurate for fractions of hours.
// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
v += int64(float64(f) * (float64(unit) / scale))
if v < 0 {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
}
d += v
if d < 0 {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
}

if neg {
d = -d
}
return time.Duration(d), nil
}
70 changes: 70 additions & 0 deletions internal/time/duration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package time

import (
"testing"
"time"
)

var parseDurationTests = []struct {
in string
ok bool
want time.Duration
}{
// simple
{"0", true, 0},
{"5s", true, 5 * time.Second},
{"30s", true, 30 * time.Second},
{"1478s", true, 1478 * time.Second},
// sign
{"-5s", true, -5 * time.Second},
{"+5s", true, 5 * time.Second},
{"-0", true, 0},
{"+0", true, 0},
// decimal
{"5.0s", true, 5 * time.Second},
{"5.6s", true, 5*time.Second + 600*time.Millisecond},
{"5.s", true, 5 * time.Second},
{".5s", true, 500 * time.Millisecond},
{"1.0s", true, 1 * time.Second},
{"1.00s", true, 1 * time.Second},
{"1.004s", true, 1*time.Second + 4*time.Millisecond},
{"1.0040s", true, 1*time.Second + 4*time.Millisecond},
{"100.00100s", true, 100*time.Second + 1*time.Millisecond},
// different units
{"14s", true, 14 * time.Second},
{"15m", true, 15 * time.Minute},
{"16h", true, 16 * time.Hour},
{"17d", true, 24 * 17 * time.Hour},
// composite durations
{"3h30m", true, 3*time.Hour + 30*time.Minute},
{"10.5s4m", true, 4*time.Minute + 10*time.Second + 500*time.Millisecond},
{"-2m3.4s", true, -(2*time.Minute + 3*time.Second + 400*time.Millisecond)},
{"39h9m14.425s", true, 39*time.Hour + 9*time.Minute + 14*time.Second + 425*time.Millisecond},
// more than 9 digits after decimal point, see https://golang.org/issue/6617
{"0.3333333333333333333h", true, 20 * time.Minute},
// huge string; issue 15011.
{"0.100000000000000000000h", true, 6 * time.Minute},
// This value tests the first overflow check in leadingFraction.
{"0.830103483285477580700h", true, 49*time.Minute + 48*time.Second + 372539827*time.Nanosecond},

// errors
{"", false, 0},
{"3", false, 0},
{"-", false, 0},
{"s", false, 0},
{".", false, 0},
{"-.", false, 0},
{".s", false, 0},
{"+.s", false, 0},
}

func TestParseDuration(t *testing.T) {
for _, tc := range parseDurationTests {
d, err := ParseDuration(tc.in)
if tc.ok && (err != nil || d != tc.want) {
t.Errorf("ParseDuration(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want)
} else if !tc.ok && err == nil {
t.Errorf("ParseDuration(%q) = _, nil, want _, non-nil", tc.in)
}
}
}
2 changes: 1 addition & 1 deletion pkg/revaboxy/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func New(vv []Version, settingChangers ...Setting) (*Revaboxy, error) {
logger: &nopLogger{},
headerName: "Revaboxy-Name",
cookieName: "revaboxy-name",
cookieExpiry: time.Hour * 24 * 3,
cookieExpiry: time.Hour * 24 * 7,
roundTripper: http.DefaultTransport,
}
// Apply all settings
Expand Down

0 comments on commit 489d0bf

Please sign in to comment.