Remove Starlark, for now

This is temporary as we prepare for a stable v2 release. We don't want
to make promises we don't know we can keep, and the Starlark integration
deserves much more focused attention which resources and funding do not
currently permit. When the project is financially stable, I will be able
to revisit this properly and add flexible, robust Starlark scripting
support to Caddy 2.
This commit is contained in:
Matthew Holt 2020-02-06 18:46:52 -07:00
parent 4a07a5d41e
commit 8b28c36d48
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
12 changed files with 0 additions and 783 deletions

2
go.mod
View File

@ -24,11 +24,9 @@ require (
github.com/naoina/toml v0.1.1
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3
github.com/vulcand/oxy v1.0.0
github.com/yuin/goldmark v1.1.17
github.com/yuin/goldmark-highlighting v0.0.0-20191202084645-78f32c8dd6d5
go.starlark.net v0.0.0-20190919145610-979af19b165c
go.uber.org/multierr v1.2.0 // indirect
go.uber.org/zap v1.10.0
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413

4
go.sum
View File

@ -309,8 +309,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3 h1:/fBh1Ot84ILt/ociFHO98wJ9LxIMA3UG8B0unUJPFpY=
github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3/go.mod h1:pxOc2ZuBV+CNlQgzq/HJ9Z9G/eoEMHFeuGohOvva4Co=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -339,8 +337,6 @@ go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.starlark.net v0.0.0-20190919145610-979af19b165c h1:WR7X1xgXJlXhQBdorVc9Db3RhwG+J/kp6bLuMyJjfVw=
go.starlark.net v0.0.0-20190919145610-979af19b165c/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=

View File

@ -28,8 +28,6 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/pkg/caddyscript"
"go.starlark.net/starlark"
)
type (
@ -105,9 +103,6 @@ type (
Matchers MatcherSet `json:"-"`
}
// MatchStarlarkExpr matches requests by evaluating a Starlark expression.
MatchStarlarkExpr string
// MatchTable matches requests by values in the table.
MatchTable string // TODO: finish implementing
)
@ -123,7 +118,6 @@ func init() {
caddy.RegisterModule(new(MatchProtocol))
caddy.RegisterModule(MatchRemoteIP{})
caddy.RegisterModule(MatchNegate{})
caddy.RegisterModule(new(MatchStarlarkExpr))
}
// CaddyModule returns the Caddy module information.
@ -646,28 +640,6 @@ func (m MatchRemoteIP) Match(r *http.Request) bool {
return false
}
// CaddyModule returns the Caddy module information.
func (MatchStarlarkExpr) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.matchers.starlark",
New: func() caddy.Module { return new(MatchStarlarkExpr) },
}
}
// Match returns true if r matches m.
func (m MatchStarlarkExpr) Match(r *http.Request) bool {
input := string(m)
thread := new(starlark.Thread)
env := caddyscript.MatcherEnv(r)
val, err := starlark.Eval(thread, "", input, env)
if err != nil {
// TODO: Can we detect this in Provision or Validate instead?
log.Printf("caddyscript for matcher is invalid: attempting to evaluate expression `%v` error `%v`", input, err)
return false
}
return val.String() == "True"
}
// MatchRegexp is an embeddable type for matching
// using regular expressions. It adds placeholders
// to the request's replacer.
@ -834,7 +806,6 @@ var (
_ caddy.Provisioner = (*MatchRemoteIP)(nil)
_ RequestMatcher = (*MatchNegate)(nil)
_ caddy.Provisioner = (*MatchNegate)(nil)
_ RequestMatcher = (*MatchStarlarkExpr)(nil)
_ caddy.Provisioner = (*MatchRegexp)(nil)
_ caddyfile.Unmarshaler = (*MatchHost)(nil)

View File

@ -15,6 +15,5 @@ import (
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy/fastcgi"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/starlarkmw"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates"
)

View File

@ -1,19 +0,0 @@
{
"apps": {
"http": {
"servers": {
"MY_SERVER": {
"listen": [":3001"],
"routes": [
{
"handle": {
"handler": "starlark",
"script": "def setup(r):\n\t# create some middlewares specific to this request\n\ttemplates = loadModule('http.handlers.templates', {'file_root': './includes'})\n\tmidChain = execute([templates])\n\ndef serveHTTP (rw, r):\n\trw.Write('Hello world, from Starlark!')\n"
}
}
]
}
}
}
}
}

View File

@ -1,165 +0,0 @@
package lib
import (
"encoding/json"
"fmt"
"strings"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2"
"go.starlark.net/starlark"
)
// ResponderModule represents a module that satisfies the caddyhttp handler.
type ResponderModule struct {
Name string
Cfg json.RawMessage
Instance caddyhttp.Handler
}
func (r ResponderModule) Freeze() {}
func (r ResponderModule) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: responder module") }
func (r ResponderModule) String() string { return "responder module" }
func (r ResponderModule) Type() string { return "responder module" }
func (r ResponderModule) Truth() starlark.Bool { return true }
// Middleware represents a module that satisfies the starlark Value interface.
type Middleware struct {
Name string
Cfg json.RawMessage
Instance caddyhttp.MiddlewareHandler
}
func (r Middleware) Freeze() {}
func (r Middleware) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: middleware") }
func (r Middleware) String() string { return "middleware" }
func (r Middleware) Type() string { return "middleware" }
func (r Middleware) Truth() starlark.Bool { return true }
// LoadMiddleware represents the method exposed to starlark to load a Caddy module.
type LoadMiddleware struct {
Middleware Middleware
Ctx caddy.Context
}
func (r LoadMiddleware) Freeze() {}
func (r LoadMiddleware) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: loadMiddleware") }
func (r LoadMiddleware) String() string { return "loadMiddleware" }
func (r LoadMiddleware) Type() string { return "function: loadMiddleware" }
func (r LoadMiddleware) Truth() starlark.Bool { return true }
// Run is the method bound to the starlark loadMiddleware function.
func (r *LoadMiddleware) Run(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var name string
var cfg *starlark.Dict
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &name, &cfg)
if err != nil {
return starlark.None, fmt.Errorf("unpacking arguments: %v", err.Error())
}
js := json.RawMessage(cfg.String())
if !strings.Contains(name, "http.handlers.") {
name = fmt.Sprintf("http.handlers.%s", name)
}
inst, err := r.Ctx.LoadModuleByID(name, js)
if err != nil {
return starlark.None, err
}
mid, ok := inst.(caddyhttp.MiddlewareHandler)
if !ok {
return starlark.None, fmt.Errorf("could not assert as middleware handler")
}
m := Middleware{
Name: name,
Cfg: js,
Instance: mid,
}
r.Middleware = m
return m, nil
}
// LoadResponder represents the method exposed to starlark to load a Caddy middleware responder.
type LoadResponder struct {
Module ResponderModule
Ctx caddy.Context
}
func (r LoadResponder) Freeze() {}
func (r LoadResponder) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: loadModule") }
func (r LoadResponder) String() string { return "loadModule" }
func (r LoadResponder) Type() string { return "function: loadModule" }
func (r LoadResponder) Truth() starlark.Bool { return true }
// Run is the method bound to the starlark loadResponder function.
func (r *LoadResponder) Run(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var name string
var cfg *starlark.Dict
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &name, &cfg)
if err != nil {
return starlark.None, fmt.Errorf("unpacking arguments: %v", err.Error())
}
js := json.RawMessage(cfg.String())
if !strings.Contains(name, "http.handlers.") {
name = fmt.Sprintf("http.handlers.%s", name)
}
inst, err := r.Ctx.LoadModuleByID(name, js)
if err != nil {
return starlark.None, err
}
res, ok := inst.(caddyhttp.Handler)
if !ok {
return starlark.None, fmt.Errorf("could not assert as responder")
}
m := ResponderModule{
Name: name,
Cfg: js,
Instance: res,
}
r.Module = m
return m, nil
}
// Execute represents the method exposed to starlark to build a middleware chain.
type Execute struct {
Modules []Middleware
}
func (r Execute) Freeze() {}
func (r Execute) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: execute") }
func (r Execute) String() string { return "execute" }
func (r Execute) Type() string { return "function: execute" }
func (r Execute) Truth() starlark.Bool { return true }
// Run is the method bound to the starlark execute function.
func (r *Execute) Run(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var mids *starlark.List
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &mids)
if err != nil {
return starlark.None, fmt.Errorf("unpacking arguments: %v", err.Error())
}
for i := 0; i < mids.Len(); i++ {
val, ok := mids.Index(i).(Middleware)
if !ok {
return starlark.None, fmt.Errorf("cannot get module from execute")
}
r.Modules = append(r.Modules, val)
}
return starlark.None, nil
}

View File

@ -1,172 +0,0 @@
package starlarkmw
import (
"context"
"fmt"
"net/http"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/starlarkmw/internal/lib"
caddyscript "github.com/caddyserver/caddy/v2/pkg/caddyscript/lib"
"github.com/starlight-go/starlight/convert"
"go.starlark.net/starlark"
)
func init() {
caddy.RegisterModule(StarlarkMW{})
}
// StarlarkMW represents a middleware responder written in starlark
type StarlarkMW struct {
Script string `json:"script"`
serveHTTP *starlark.Function
setup *starlark.Function
thread *starlark.Thread
loadMiddleware *lib.LoadMiddleware
execute *lib.Execute
globals *starlark.StringDict
gctx caddy.Context
rctx caddy.Context
rcancel context.CancelFunc
}
// CaddyModule returns the Caddy module information.
func (StarlarkMW) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.starlark",
New: func() caddy.Module { return new(StarlarkMW) },
}
}
// ServeHTTP responds to an http request with starlark.
func (s *StarlarkMW) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
var mwcancel context.CancelFunc
var mwctx caddy.Context
// call setup() to prepare the middleware chain if it is defined
if s.setup != nil {
mwctx, mwcancel = caddy.NewContext(s.gctx)
defer mwcancel()
s.loadMiddleware.Ctx = mwctx
args := starlark.Tuple{caddyscript.HTTPRequest{Req: r}}
_, err := starlark.Call(new(starlark.Thread), s.setup, args, nil)
if err != nil {
return fmt.Errorf("starlark setup(), %v", err)
}
}
// dynamically build middleware chain for each request
var stack caddyhttp.Handler = caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
wr, err := convert.ToValue(w)
if err != nil {
return fmt.Errorf("cannot convert response writer to starlark value")
}
args := starlark.Tuple{wr, caddyscript.HTTPRequest{Req: r}}
v, err := starlark.Call(new(starlark.Thread), s.serveHTTP, args, nil)
if err != nil {
return fmt.Errorf("starlark serveHTTP(), %v", err)
}
// if a responder type was returned from starlark we should run it otherwise it
// is expected to handle the request
if resp, ok := v.(lib.ResponderModule); ok {
return resp.Instance.ServeHTTP(w, r)
}
return nil
})
// TODO :- make middlewareResponseWriter exported and wrap w with that
var mid []caddyhttp.Middleware
for _, m := range s.execute.Modules {
mid = append(mid, func(next caddyhttp.Handler) caddyhttp.Handler {
return caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return m.Instance.ServeHTTP(w, r, next)
})
})
}
for i := len(mid) - 1; i >= 0; i-- {
stack = mid[i](stack)
}
s.execute.Modules = nil
return stack.ServeHTTP(w, r)
}
// Cleanup cleans up any modules loaded during the creation of a starlark route.
func (s *StarlarkMW) Cleanup() error {
s.rcancel()
return nil
}
// Provision sets up the starlark env.
func (s *StarlarkMW) Provision(ctx caddy.Context) error {
// store global context
s.gctx = ctx
// setup context for cleaning up any modules loaded during starlark script parsing phase
rctx, cancel := caddy.NewContext(ctx)
s.rcancel = cancel
// setup starlark global env
env := make(starlark.StringDict)
loadMiddleware := lib.LoadMiddleware{}
loadResponder := lib.LoadResponder{
Ctx: rctx,
}
execute := lib.Execute{}
lr := starlark.NewBuiltin("loadResponder", loadResponder.Run)
lr = lr.BindReceiver(&loadResponder)
env["loadResponder"] = lr
lm := starlark.NewBuiltin("loadMiddleware", loadMiddleware.Run)
lm = lm.BindReceiver(&loadMiddleware)
env["loadMiddleware"] = lm
ex := starlark.NewBuiltin("execute", execute.Run)
ex = ex.BindReceiver(&execute)
env["execute"] = ex
// import caddyscript lib
env["time"] = caddyscript.Time{}
env["regexp"] = caddyscript.Regexp{}
// configure starlark
thread := new(starlark.Thread)
s.thread = thread
// run starlark script
globals, err := starlark.ExecFile(thread, "", s.Script, env)
if err != nil {
return fmt.Errorf("starlark exec file: %v", err.Error())
}
// extract defined methods to setup middleware chain and responder, setup is not required
var setup *starlark.Function
if _, ok := globals["setup"]; ok {
setup, ok = globals["setup"].(*starlark.Function)
if !ok {
return fmt.Errorf("setup function not defined in starlark script")
}
}
serveHTTP, ok := globals["serveHTTP"].(*starlark.Function)
if !ok {
return fmt.Errorf("serveHTTP function not defined in starlark script")
}
s.setup = setup
s.serveHTTP = serveHTTP
s.loadMiddleware = &loadMiddleware
s.execute = &execute
s.globals = &globals
return nil
}

View File

@ -1,40 +0,0 @@
# any module that provisions resources
proxyConfig = {
'load_balance_type': 'round_robin',
'upstreams': [
{
'host': 'http://localhost:8080',
'circuit_breaker': {
'type': 'status_ratio',
'threshold': 0.5
}
},
{
'host': 'http://localhost:8081'
}
]
}
sfConfig = {
'root': '/Users/dev/Desktop',
'browse': {},
}
proxy = loadResponder('reverse_proxy', proxyConfig)
static_files = loadResponder('file_server', sfConfig)
def setup(r):
# create some middlewares specific to this request
mid = []
if r.query.get('log') == 'true':
logMid = loadMiddleware('log', {'file': 'access.log'})
mid.append(logMid)
execute(mid)
def serveHTTP(w, r):
if r.url.find('static') > 0:
return static_files
return proxy

View File

@ -1,111 +0,0 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyscript
import (
"fmt"
"net/http"
"github.com/starlight-go/starlight/convert"
"go.starlark.net/starlark"
)
// HTTPRequest represents an http request type in caddyscript.
type HTTPRequest struct{ Req *http.Request }
// AttrNames defines what properties and methods are available on the HTTPRequest type.
func (r HTTPRequest) AttrNames() []string {
return []string{"header", "query", "url", "method", "host", "tls", "redirect"}
}
func (r HTTPRequest) Freeze() {}
func (r HTTPRequest) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: HTTPRequest") }
func (r HTTPRequest) String() string { return fmt.Sprint(r.Req) }
func (r HTTPRequest) Type() string { return "HTTPRequest" }
func (r HTTPRequest) Truth() starlark.Bool { return true }
// Header handles returning a header key.
func (r HTTPRequest) Header(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var key string
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &key)
if err != nil {
return starlark.None, fmt.Errorf("get request header: %v", err.Error())
}
return starlark.String(r.Req.Header.Get(key)), nil
}
// Redirect handles an http redirect from starlark code.
func (r HTTPRequest) Redirect(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var w starlark.Value
var req HTTPRequest
var newURL string
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 3, &w, &req, &newURL)
if err != nil {
return starlark.None, fmt.Errorf("unpacking arguments: %v", err.Error())
}
writer := convert.FromValue(w)
if w, ok := writer.(http.ResponseWriter); ok {
http.Redirect(w, req.Req, newURL, http.StatusSeeOther)
return starlark.None, nil
}
return starlark.None, fmt.Errorf("first provided argument is not http.ResponseWriter")
}
// Attr defines what happens when props or methods are called on the HTTPRequest type.
func (r HTTPRequest) Attr(name string) (starlark.Value, error) {
switch name {
case "redirect":
b := starlark.NewBuiltin("Redirect", r.Redirect)
b = b.BindReceiver(r)
return b, nil
case "tls":
tls := new(starlark.Dict)
tls.SetKey(starlark.String("cipher_suite"), starlark.MakeUint(uint(r.Req.TLS.CipherSuite)))
tls.SetKey(starlark.String("did_resume"), starlark.Bool(r.Req.TLS.DidResume))
tls.SetKey(starlark.String("handshake_complete"), starlark.Bool(r.Req.TLS.HandshakeComplete))
tls.SetKey(starlark.String("negotiated_protocol"), starlark.String(r.Req.TLS.NegotiatedProtocol))
tls.SetKey(starlark.String("negotiated_protocol_is_mutual"), starlark.Bool(r.Req.TLS.NegotiatedProtocolIsMutual))
tls.SetKey(starlark.String("server_name"), starlark.String(r.Req.TLS.ServerName))
tls.SetKey(starlark.String("version"), starlark.String(r.Req.TLS.Version))
return tls, nil
case "header":
b := starlark.NewBuiltin("Header", r.Header)
b = b.BindReceiver(r)
return b, nil
case "query":
qVals := r.Req.URL.Query()
query := starlark.NewDict(len(qVals))
for k, v := range qVals {
query.SetKey(starlark.String(k), starlark.String(v[0]))
}
return query, nil
case "url":
return starlark.String(r.Req.URL.Path), nil
case "method":
return starlark.String(r.Req.Method), nil
case "host":
return starlark.String(r.Req.Host), nil
}
return nil, nil
}

View File

@ -1,64 +0,0 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyscript
import (
"fmt"
"regexp"
"go.starlark.net/starlark"
)
// Regexp represents a regexp type for caddyscript.
type Regexp struct{}
// AttrNames defines what properties and methods are available on the Time type.
func (r Regexp) AttrNames() []string {
return []string{"match_string"}
}
// Attr defines what happens when props or methods are called on the Time type.
func (r Regexp) Attr(name string) (starlark.Value, error) {
switch name {
case "match_string":
b := starlark.NewBuiltin("match_string", r.MatchString)
b = b.BindReceiver(r)
return b, nil
}
return nil, nil
}
// MatchString reports whether the string s contains any match of the regular expression pattern. More complicated queries need to use Compile and the full Regexp interface.
func (r Regexp) MatchString(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var pattern, match string
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 2, &pattern, &match)
if err != nil {
return starlark.None, fmt.Errorf("could not unpack args: %v", err.Error())
}
matched, err := regexp.MatchString(pattern, match)
if err != nil {
return starlark.False, fmt.Errorf("matchstring: %v", err.Error())
}
return starlark.Bool(matched), nil
}
func (r Regexp) Freeze() {}
func (r Regexp) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: Regexp") }
func (r Regexp) String() string { return "Regexp" }
func (r Regexp) Type() string { return "Regexp" }
func (r Regexp) Truth() starlark.Bool { return true }

View File

@ -1,144 +0,0 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyscript
import (
"fmt"
ti "time"
"go.starlark.net/starlark"
)
// Time represents a time type for caddyscript.
type Time struct {
value int64 // time since epoch in nanoseconds
}
// AttrNames defines what properties and methods are available on the Time type.
func (r Time) AttrNames() []string {
return []string{"now", "parse", "add", "subtract", "minute", "hour", "day", "value"}
}
// Attr defines what happens when props or methods are called on the Time type.
func (r Time) Attr(name string) (starlark.Value, error) {
switch name {
case "now":
b := starlark.NewBuiltin("now", r.Now)
b = b.BindReceiver(r)
return b, nil
case "parse_duration":
b := starlark.NewBuiltin("parse_duration", r.ParseDuration)
b = b.BindReceiver(r)
return b, nil
case "add":
b := starlark.NewBuiltin("add", r.Add)
b = b.BindReceiver(r)
return b, nil
case "subtract":
b := starlark.NewBuiltin("subtract", r.Subtract)
b = b.BindReceiver(r)
return b, nil
case "minute":
b := starlark.NewBuiltin("minute", r.Minute)
b = b.BindReceiver(r)
return b, nil
case "hour":
b := starlark.NewBuiltin("hour", r.Hour)
b = b.BindReceiver(r)
return b, nil
case "day":
b := starlark.NewBuiltin("day", r.Day)
b = b.BindReceiver(r)
return b, nil
case "value":
return starlark.MakeInt64(r.value), nil
}
return nil, nil
}
func (r Time) Freeze() {}
func (r Time) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: Time") }
func (r Time) String() string { return fmt.Sprint(r.value) }
func (r Time) Type() string { return "Time" }
func (r Time) Truth() starlark.Bool { return true }
// Hour returns the current hour of a unix timestamp in range [0, 23].
func (r Time) Hour(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
t := ti.Unix(0, r.value)
return starlark.MakeInt(t.Hour()), nil
}
// Minute returns the current minute of the hour for a unix timestamp in range [0, 59].
func (r Time) Minute(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
t := ti.Unix(0, r.value)
return starlark.MakeInt(t.Minute()), nil
}
// Day returns the current day in a week of a unix timestamp... [Sunday = 0...]
func (r Time) Day(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
t := ti.Unix(0, r.value)
return starlark.MakeInt(int(t.Weekday())), nil
}
// Now returns the current time as a unix timestamp.
func (r Time) Now(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
val := ti.Now().UnixNano()
r.value = val
return r, nil
}
// ParseDuration parses a go duration string to a time type.
func (r Time) ParseDuration(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var dur string
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &dur)
if err != nil {
return starlark.None, fmt.Errorf("could not unpack args: %v", err.Error())
}
if parsed, err := ti.ParseDuration(dur); err == nil {
val := parsed.Nanoseconds()
r.value = val
return r, nil
}
return starlark.None, fmt.Errorf("time.parse_duration: argument cannot be parsed as a valid go time duration")
}
// Add adds time to a time type.
func (r Time) Add(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var t Time
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &t)
if err != nil {
return starlark.None, fmt.Errorf("could not unpack args: %v", err.Error())
}
val := r.value + t.value
r.value = val
return r, nil
}
// Subtract adds time to a time type.
func (r Time) Subtract(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var t Time
err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &t)
if err != nil {
return starlark.None, fmt.Errorf("could not unpack args: %v", err.Error())
}
val := r.value - t.value
r.value = val
return r, nil
}

View File

@ -1,32 +0,0 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyscript
import (
"net/http"
caddyscript "github.com/caddyserver/caddy/v2/pkg/caddyscript/lib"
"go.starlark.net/starlark"
)
// MatcherEnv sets up the global context for the matcher caddyscript environment.
func MatcherEnv(r *http.Request) starlark.StringDict {
env := make(starlark.StringDict)
env["req"] = caddyscript.HTTPRequest{Req: r}
env["time"] = caddyscript.Time{}
env["regexp"] = caddyscript.Regexp{}
return env
}