From 0304fa57950ea89ac4679824285e14966941ac7d Mon Sep 17 00:00:00 2001 From: joshblakeley Date: Tue, 6 Nov 2018 09:49:19 +0000 Subject: [PATCH] add jsvm to lint schema and update otto vendor --- cli/lint/schema.go | 3 + jsvm_goja.go | 12 +-- jsvm_otto.go | 5 +- mw_js_plugin.go | 3 +- mw_virtual_endpoint.go | 5 +- mw_virtual_endpoint_test.go | 2 +- .../robertkrimen/otto/README.markdown | 2 +- .../robertkrimen/otto/builtin_array.go | 5 +- vendor/github.com/robertkrimen/otto/error.go | 1 + vendor/github.com/robertkrimen/otto/inline.pl | 14 +-- .../github.com/robertkrimen/otto/runtime.go | 94 ++++++++++++++++++- .../robertkrimen/otto/type_function.go | 50 +++++++++- vendor/vendor.json | 6 +- 13 files changed, 174 insertions(+), 28 deletions(-) diff --git a/cli/lint/schema.go b/cli/lint/schema.go index f131bd62629..e881344bb0f 100644 --- a/cli/lint/schema.go +++ b/cli/lint/schema.go @@ -236,6 +236,9 @@ const confSchema = `{ "enable_jsvm": { "type": "boolean" }, + "jsvm":{ + "type": "string" + }, "jsvm_timeout": { "type": "integer" }, diff --git a/jsvm_goja.go b/jsvm_goja.go index ac4c4318dd4..91e1b1d2672 100644 --- a/jsvm_goja.go +++ b/jsvm_goja.go @@ -12,10 +12,11 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/dop251/goja" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/user" - "github.com/dop251/goja" ) type TykJSVM interface { @@ -154,7 +155,6 @@ func (j *GojaJSVM) RunJSRequestDynamic(d *DynamicMiddleware, logger *logrus.Entr func (j *GojaJSVM) RunJSRequestVirtual(d *VirtualEndpoint, logger *logrus.Entry, vmeta *apidef.VirtualMeta, requestAsJson string, sessionAsJson string, specAsJson string) (error, int, string) { - interrupt := make(chan func(), 1) d.Logger().Debug("Running: ", vmeta.ResponseFunctionName) // buffered, leaving no chance of a goroutine leak since the // spawned goroutine will send 0 or 1 values. @@ -180,14 +180,12 @@ func (j *GojaJSVM) RunJSRequestVirtual(d *VirtualEndpoint, logger *logrus.Entry, return nil, -1, "" } t.Stop() + case <-t.C: t.Stop() d.Logger().Error("JS middleware timed out after ", j.GetTimeout()) - interrupt <- func() { - // only way to stop the VM is to send it a func - // that panics. - panic("stop") - } + + j.VM.Interrupt("Stopping VM for timeout") return nil, -1, "" } returnDataStr := returnRaw.String() diff --git a/jsvm_otto.go b/jsvm_otto.go index 12da7cd99a3..6e76df183c3 100644 --- a/jsvm_otto.go +++ b/jsvm_otto.go @@ -12,11 +12,12 @@ import ( "strings" "time" + "github.com/robertkrimen/otto" + _ "github.com/robertkrimen/otto/underscore" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/user" - "github.com/robertkrimen/otto" - _ "github.com/robertkrimen/otto/underscore" "github.com/Sirupsen/logrus" ) diff --git a/mw_js_plugin.go b/mw_js_plugin.go index 68dc0cffb8f..1fe037caf06 100644 --- a/mw_js_plugin.go +++ b/mw_js_plugin.go @@ -139,9 +139,10 @@ func (d *DynamicMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Reques err, code, returnDataStr := d.Spec.JSVM.RunJSRequestDynamic(d, logger, string(requestAsJson), string(sessionAsJson), specAsJson) if err != nil { log.Errorf("JSVM error: %v", err) + return err, code } - if code != -1 { + if code != -1 || returnDataStr == "" { return nil, code } // Decode the return object diff --git a/mw_virtual_endpoint.go b/mw_virtual_endpoint.go index 65ac150a037..ad6971a46f9 100644 --- a/mw_virtual_endpoint.go +++ b/mw_virtual_endpoint.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/json" "errors" - "fmt" "io" "io/ioutil" "net/http" @@ -172,8 +171,8 @@ func (d *VirtualEndpoint) ServeHTTPForCache(w http.ResponseWriter, r *http.Reque // Run the middleware err, code, returnDataStr := d.Spec.JSVM.RunJSRequestVirtual(d, d.logger, vmeta, string(requestAsJson), string(sessionAsJson), specAsJson) if err != nil { - //TODO - fmt.Println(err) + log.Errorf("JSVM VE error: %v", err) + return nil } if code != -1 { return nil diff --git a/mw_virtual_endpoint_test.go b/mw_virtual_endpoint_test.go index 44ec0a975bf..7bb33ef102b 100644 --- a/mw_virtual_endpoint_test.go +++ b/mw_virtual_endpoint_test.go @@ -64,7 +64,7 @@ func TestVirtualEndpointOtto(t *testing.T) { ts := newTykTestServer() defer ts.Close() - testPrepareVirtualEndpoint(virtTestJS, "GET") + testPrepareVirtualEndpoint(virtTestJS, "GET", "/virt", true) ts.Run(t, test.TestCase{ Path: "/virt", diff --git a/vendor/github.com/robertkrimen/otto/README.markdown b/vendor/github.com/robertkrimen/otto/README.markdown index a1ae7d1ae60..40584d32ba9 100644 --- a/vendor/github.com/robertkrimen/otto/README.markdown +++ b/vendor/github.com/robertkrimen/otto/README.markdown @@ -101,7 +101,7 @@ result, _ = vm.Run(` sayHello(); // Hello, undefined result = twoPlus(2.0); // 4 -`) +`) ``` ### Parser diff --git a/vendor/github.com/robertkrimen/otto/builtin_array.go b/vendor/github.com/robertkrimen/otto/builtin_array.go index 557ffc02480..56dd95ab693 100644 --- a/vendor/github.com/robertkrimen/otto/builtin_array.go +++ b/vendor/github.com/robertkrimen/otto/builtin_array.go @@ -170,7 +170,10 @@ func builtinArray_splice(call FunctionCall) Value { length := int64(toUint32(thisObject.get("length"))) start := valueToRangeIndex(call.Argument(0), length, false) - deleteCount := valueToRangeIndex(call.Argument(1), int64(length)-start, true) + deleteCount := length - start + if arg, ok := call.getArgument(1); ok { + deleteCount = valueToRangeIndex(arg, length-start, true) + } valueArray := make([]Value, deleteCount) for index := int64(0); index < deleteCount; index++ { diff --git a/vendor/github.com/robertkrimen/otto/error.go b/vendor/github.com/robertkrimen/otto/error.go index 41a5c10e7c1..c8b5af446ef 100644 --- a/vendor/github.com/robertkrimen/otto/error.go +++ b/vendor/github.com/robertkrimen/otto/error.go @@ -56,6 +56,7 @@ type _frame struct { file *file.File offset int callee string + fn interface{} } var ( diff --git a/vendor/github.com/robertkrimen/otto/inline.pl b/vendor/github.com/robertkrimen/otto/inline.pl index c3620b4a2b1..e9029048954 100755 --- a/vendor/github.com/robertkrimen/otto/inline.pl +++ b/vendor/github.com/robertkrimen/otto/inline.pl @@ -31,11 +31,11 @@ package otto func _newContext(runtime *_runtime) { @{[ join "\n", $self->newContext() ]} -} +} func newConsoleObject(runtime *_runtime) *_object { @{[ join "\n", $self->newConsoleObject() ]} -} +} _END_ for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) { @@ -730,7 +730,7 @@ sub propertyOrder { sub globalObject { my $self = shift; my $name = shift; - + my $propertyMap = ""; if (@_) { $propertyMap = join "\n", $self->propertyMap(@_); @@ -754,7 +754,7 @@ sub globalFunction { my $self = shift; my $name = shift; my $length = shift; - + my $builtin = "builtin${name}"; my $builtinNew = "builtinNew${name}"; my $prototype = "runtime.global.${name}Prototype"; @@ -874,7 +874,7 @@ sub newFunction { property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, $propertyOrder value: @{[ $self->nativeFunctionOf($name, $func) ]}, -} +} _END_ ); @@ -896,7 +896,7 @@ sub newObject { extensible: true, property: $propertyMap, $propertyOrder, -} +} _END_ } @@ -922,7 +922,7 @@ sub newPrototypeObject { property: $propertyMap, $propertyOrder, $value -} +} _END_ } diff --git a/vendor/github.com/robertkrimen/otto/runtime.go b/vendor/github.com/robertkrimen/otto/runtime.go index 7d29ecca0df..9941278f64d 100644 --- a/vendor/github.com/robertkrimen/otto/runtime.go +++ b/vendor/github.com/robertkrimen/otto/runtime.go @@ -1,6 +1,7 @@ package otto import ( + "encoding" "errors" "fmt" "math" @@ -8,6 +9,7 @@ import ( "reflect" "runtime" "strconv" + "strings" "sync" "github.com/robertkrimen/otto/ast" @@ -296,7 +298,23 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Valu if v.kind == valueObject { if gso, ok := v._object().value.(*_goStructObject); ok { if gso.value.Type().AssignableTo(t) { - return gso.value + // please see TestDynamicFunctionReturningInterface for why this exists + if t.Kind() == reflect.Interface && gso.value.Type().ConvertibleTo(t) { + return gso.value.Convert(t) + } else { + return gso.value + } + } + } + + if gao, ok := v._object().value.(*_goArrayObject); ok { + if gao.value.Type().AssignableTo(t) { + // please see TestDynamicFunctionReturningInterface for why this exists + if t.Kind() == reflect.Interface && gao.value.Type().ConvertibleTo(t) { + return gao.value.Convert(t) + } else { + return gao.value + } } } } @@ -444,6 +462,66 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Valu return []reflect.Value{self.convertCallParameter(rv, t.Out(0))} }) } + case reflect.Struct: + if o := v._object(); o != nil && o.class == "Object" { + s := reflect.New(t) + + for _, k := range o.propertyOrder { + var f *reflect.StructField + + for i := 0; i < t.NumField(); i++ { + ff := t.Field(i) + + if j := ff.Tag.Get("json"); j != "" { + if j == "-" { + continue + } + + a := strings.Split(j, ",") + + if a[0] == k { + f = &ff + break + } + } + + if ff.Name == k { + f = &ff + break + } + + if strings.EqualFold(ff.Name, k) { + f = &ff + } + } + + if f == nil { + panic(self.panicTypeError("can't convert object; field %q was supplied but does not exist on target %v", k, t)) + } + + ss := s + + for _, i := range f.Index { + if ss.Kind() == reflect.Ptr { + if ss.IsNil() { + if !ss.CanSet() { + panic(self.panicTypeError("can't set embedded pointer to unexported struct: %v", ss.Type().Elem())) + } + + ss.Set(reflect.New(ss.Type().Elem())) + } + + ss = ss.Elem() + } + + ss = ss.Field(i) + } + + ss.Set(self.convertCallParameter(o.get(k), ss.Type())) + } + + return s.Elem() + } } if tk == reflect.String { @@ -464,6 +542,20 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Valu return reflect.ValueOf(v.String()) } + if v.kind == valueString { + var s encoding.TextUnmarshaler + + if reflect.PtrTo(t).Implements(reflect.TypeOf(&s).Elem()) { + r := reflect.New(t) + + if err := r.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(v.string())); err != nil { + panic(self.panicSyntaxError("can't convert to %s: %s", t.String(), err.Error())) + } + + return r.Elem() + } + } + s := "OTTO DOES NOT UNDERSTAND THIS TYPE" switch v.kind { case valueBoolean: diff --git a/vendor/github.com/robertkrimen/otto/type_function.go b/vendor/github.com/robertkrimen/otto/type_function.go index 5637dc60512..ad4b1f16f55 100644 --- a/vendor/github.com/robertkrimen/otto/type_function.go +++ b/vendor/github.com/robertkrimen/otto/type_function.go @@ -37,7 +37,7 @@ type _nativeFunctionObject struct { construct _constructFunction // [[Construct]] } -func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { +func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { self := runtime.newClassObject("Function") self.value = _nativeFunctionObject{ name: name, @@ -46,10 +46,35 @@ func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, na call: native, construct: defaultConstruct, } + self.defineProperty("name", toValue_string(name), 0000, false) self.defineProperty("length", toValue_int(length), 0000, false) return self } +func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { + self := runtime._newNativeFunctionObject(name, file, line, native, length) + self.defineOwnProperty("caller", _property{ + value: _propertyGetSet{ + runtime._newNativeFunctionObject("get", "internal", 0, func(fc FunctionCall) Value { + for sc := runtime.scope; sc != nil; sc = sc.outer { + if sc.frame.fn == self { + if sc.outer == nil || sc.outer.frame.fn == nil { + return nullValue + } + + return runtime.toValue(sc.outer.frame.fn) + } + } + + return nullValue + }, 0), + &_nilGetSetObject, + }, + mode: 0000, + }, false) + return self +} + // =================== // // _bindFunctionObject // // =================== // @@ -72,6 +97,7 @@ func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, arg if length < 0 { length = 0 } + self.defineProperty("name", toValue_string("bound "+target.get("name").String()), 0000, false) self.defineProperty("length", toValue_int(length), 0000, false) self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError @@ -106,7 +132,27 @@ func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash node: node, stash: stash, } + self.defineProperty("name", toValue_string(node.name), 0000, false) self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) + self.defineOwnProperty("caller", _property{ + value: _propertyGetSet{ + runtime.newNativeFunction("get", "internal", 0, func(fc FunctionCall) Value { + for sc := runtime.scope; sc != nil; sc = sc.outer { + if sc.frame.fn == self { + if sc.outer == nil || sc.outer.frame.fn == nil { + return nullValue + } + + return runtime.toValue(sc.outer.frame.fn) + } + } + + return nullValue + }), + &_nilGetSetObject, + }, + mode: 0000, + }, false) return self } @@ -145,6 +191,7 @@ func (self *_object) call(this Value, argumentList []Value, eval bool, frame _fr nativeLine: fn.line, callee: fn.name, file: nil, + fn: self, } defer func() { rt.leaveScope() @@ -171,6 +218,7 @@ func (self *_object) call(this Value, argumentList []Value, eval bool, frame _fr rt.scope.frame = _frame{ callee: fn.node.name, file: fn.node.file, + fn: self, } defer func() { rt.leaveScope() diff --git a/vendor/vendor.json b/vendor/vendor.json index f67de3bcdda..3d08b96f58d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -449,10 +449,10 @@ "revisionTime": "2016-01-27T17:00:04Z" }, { - "checksumSHA1": "ZSYig6eYkc34ELHl6O5gRtJvr6o=", + "checksumSHA1": "WdorbyEJiNbjIOiQzM4TLb/XA7Q=", "path": "github.com/robertkrimen/otto", - "revision": "fc2eb1bbf1c5c462313c810bb02dc93cd964aebe", - "revisionTime": "2017-07-21T19:43:36Z" + "revision": "15f95af6e78dcd2030d8195a138bd88d4f403546", + "revisionTime": "2018-06-17T13:11:54Z" }, { "checksumSHA1": "q1HKpLIWi3tTT5W5LqMlwPcGmyQ=",