JS Middleware is not working

Hello, I’m using k8s tyk solution and testing JS middleware. I found the sample JS middleware in gateway/middleware folder.

// ---- Sample middleware creation by end-user -----
var samplePreProcessMiddleware = new TykJS.TykMiddleware.NewMiddleware({});

samplePreProcessMiddleware.NewProcessRequest(function(request, session) {
    // You can log to Tyk console output by calloing the built-in log() function:
    log("Running sample  PRE PROCESSOR JSVM middleware")

    // Set and Delete headers in an outbound request
    request.SetHeaders["User-Agent"] = "Tyk-Custom-JSVM-Middleware";
    //request.DeleteHeaders.push("Authorization");

    // Change the outbound URL Path (only fragment, domain is fixed)
    // request.URL = "/get";

    // Add or delete request parmeters, these are encoded for the request as needed.
    request.AddParams["test_param"] = "My Teapot";
    request.DeleteParams.push("delete_me");

    // Override the body:
    request.Body = "New Request body"

    // You MUST return both the request and session metadata
    return samplePreProcessMiddleware.ReturnData(request, {});
});

// Ensure init with a post-declaration log message

So I add this JS middleware to my API definition like this.
This API is just calling “http://httpbin.org”.
Of course, I set “enable_jsvm”: true in tyk.conf.

 "custom_middleware": {
      "pre": [
        {
          "name": "samplePreProcessMiddleware",
          "path": "middleware/samplePreProcessMiddleware.js",
          "require_session": false,
          "raw_body_only": false
        }
      ],
      "post": [],

But when I call my API using postman, it shows “Error: socket hang up”.

This is tyk-dash-gateway pod’s error.

2022/04/26 06:47:35 http: panic serving 172.17.0.1:11311: runtime error: invalid memory address or nil pointer dereference
goroutine 72638 [running]:
net/http.(*conn).serve.func1(0xc000899360)
	/usr/local/go/src/net/http/server.go:1769 +0x139
panic(0x1dcb060, 0x3664fd0)
	/usr/local/go/src/runtime/panic.go:522 +0x1b5
vendor/github.com/robertkrimen/otto.(*Otto).Copy(...)
	/go/src/vendor/github.com/robertkrimen/otto/otto.go:644
github.com/TykTechnologies/tyk/gateway.(*DynamicMiddleware).ProcessRequest(0xc0008eb940, 0x2667f20, 0xc00087a8c0, 0xc000782b00, 0x0, 0x0, 0x0, 0x0, 0x0)
	/go/src/github.com/TykTechnologies/tyk/gateway/mw_js_plugin.go:157 +0xaf6
github.com/TykTechnologies/tyk/gateway.TraceMiddleware.ProcessRequest(0x2682a80, 0xc0008eb940, 0x2667f20, 0xc00087a8c0, 0xc000782b00, 0x0, 0x0, 0x0, 0x0, 0x0)
	/go/src/github.com/TykTechnologies/tyk/gateway/middleware.go:73 +0x3fd
github.com/TykTechnologies/tyk/gateway.createMiddleware.func1.1(0x2667f20, 0xc00087a8c0, 0xc000782b00)
	/go/src/github.com/TykTechnologies/tyk/gateway/middleware.go:137 +0x528
net/http.HandlerFunc.ServeHTTP(0xc0008ebcc0, 0x2667f20, 0xc00087a8c0, 0xc000782b00)
	/usr/local/go/src/net/http/server.go:1995 +0x44
vendor/github.com/gorilla/mux.(*Router).ServeHTTP(0xc000108540, 0x2667f20, 0xc00087a8c0, 0xc000782900)
	/go/src/vendor/github.com/gorilla/mux/mux.go:212 +0xe3
github.com/TykTechnologies/tyk/gateway.(*handleWrapper).ServeHTTP(0xc0001f09e8, 0x2667f20, 0xc00087a8c0, 0xc000782900)
	/go/src/github.com/TykTechnologies/tyk/gateway/proxy_muxer.go:52 +0x16f
net/http.serverHandler.ServeHTTP(0xc0007b1ad0, 0x2667f20, 0xc00087a8c0, 0xc000782900)
	/usr/local/go/src/net/http/server.go:2774 +0xa8
net/http.(*conn).serve(0xc000899360, 0x266d660, 0xc000817400)
	/usr/local/go/src/net/http/server.go:1878 +0x851
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2884 +0x2f4

I think error came from this part. (tyk/gateway/mw_js_plugin.go:157).
My API dosen’t have complex setting and just uses Bearer Authentication.

	requestData := MiniRequestObject{
		Headers:        headers,
		SetHeaders:     map[string]string{},
		DeleteHeaders:  []string{},
		Body:           originalBody,
		URL:            r.URL.String(),
		Params:         r.URL.Query(),
		AddParams:      map[string]string{},
		ExtendedParams: map[string][]string{},
		DeleteParams:   []string{},
		Method:         r.Method,
		RequestURI:     r.RequestURI,
		Scheme:         scheme,
	}

	requestAsJson, err := json.Marshal(requestData)
	if err != nil {
		**logger.WithError(err).Error("Failed to encode request object for dynamic middleware")**
		return nil, http.StatusOK
	}

Did I miss something ?

Hi @Juyoung_Oh and welcome to the community. It doesn’t appear anything is missing. The preprocessor middleware looks similar to the default sample bundled in with Tyk (with only a one line minor difference).

If you don’t mind me asking, what version of the gateway are you running?

Hi @Olu
We are using Tyk gateway v3.2.1.
Yes, that middleware is the default sample and I didn’t change anything. If I remove middleware the setting in API definition, it works well.

Thanks for sharing the gateway version. On docker this works fine. So I am checking this on k8

It also works fine on k8. I got the runtime issue you got when the config file was missing the "enable_jsvm": true property.

Kindly check the config file has been properly updated with the parameter. Or you could use the environment variable TYK_GW_ENABLEJVSM in the yaml file as shown below

image

1 Like

Thanks for answer. It works well.
However, I have one more question…
I followed your guide (added env value to valuse.yaml), but there was no “enable_jsvm” in gateway pod’s tyk.conf. So I thought “enable_jsvm” options didn’t apply. But JS middleware worked well.

I have no name!@gateway-tyk-pro-tq2bt:/opt/tyk-gateway$ cat tyk.conf
{
  "listen_address": "",
  "listen_port": 8080,
  "secret": "352d20ee67be67f6340b4c0605b044b7",
  "template_path": "/opt/tyk-gateway/templates",
  "use_db_app_configs": false,
  "app_path": "/opt/tyk-gateway/apps",
  "middleware_path": "/opt/tyk-gateway/middleware",
  "storage": {
    "type": "redis",
    "host": "localhost",
    "port": 6379,
    "username": "",
    "password": "",
    "database": 0,
    "optimisation_max_idle": 2000,
    "optimisation_max_active": 4000
  },
  "enable_analytics": false,
  "analytics_config": {
    "type": "",
    "ignored_ips": []
  },
  "dns_cache": {
    "enabled": false,
    "ttl": 3600,
    "check_interval": 60
  },
  "allow_master_keys": false,
  "policies": {
    "policy_source": "file"
  },
  "hash_keys": true,
  "hash_key_function": "murmur64",
  "suppress_redis_signal_reload": false,
  "force_global_session_lifetime": false,
  "max_idle_connections_per_host": 500
}

Also, there is another weird thing.
The original question I asked was 1) get tyk-gateway-docker source code 2) add “enable_jsvm:true” into tyk.standalone.conf 3) build docker image and push to my harbor 4) change docker repo in helm chart.
I checked that “jvsn_enable” was true in tyk-gateway pod’s tyk.conf. . However as you know, the error occured.

I was confused because it behaved in the opposite way to the value set in the pod’s tyk.conf…

Good to know it’s working and I am glad I could help. For the error I think it could be either of two things:

  1. The environment variable (env) overrides the value in the Tyk/Gateway config (conf) file. So for example if you set it to true in the conf but false in the env then the env value is used instead. As you have seen specifying it in the env overrides the value in the conf even if it is not specified in the conf file. However, it is worth nothing that the default conf value is false if not specified in the conf.
  2. The name of the key / property may be incorrect in the conf. In your response I see the name “jvsn_enable” is misspelled and reversed. So it may be worth cross-checking that

One way you can check if JVSM is enabled is in the gateway logs. There is an info log when JVSM loads the files in the middleware directory. So if you don’t see the log line, then it’s most likely that JVSM isn’t enabled. I have dropped a sample below.

level=info msg=“Loading JS File: middleware/samplePreProcessMiddleware.js” prefix=jsvm

Oh, “jsvn_enable” was just typo…
Thanks a lot! I will double check it :slight_smile:

It is a typo here, the correct one is:

TYK_GW_ENABLEJSVM

1 Like

Is this working in version 4.2.3? I downgraded to version 3.2.1 and it works.
It never loads the middleware during pod startup.
Here’s part of the deployment config

env:
            - name: TYK_GW_LISTENPORT
              value: "8080"
            - name: TYK_GW_SECRET
              value: "foo"
            - name: TYK_GW_STORAGE_HOST
              value: "redis"
            - name: TYK_GW_STORAGE_PORT
              value: "6379"
            - name: TYK_GW_STORAGE_PASSWORD
              value: ""
            - name: TYK_LOGLEVEL
              value: "info"
            - name: GODEBUG
              value: "netdns=cgo"
            - name: TYK_GW_ENABLEJSVM
              value: "true"
          volumeMounts:
            - name: tyk-gateway-conf
              mountPath: /opt/tyk-gateway/tyk.conf
              subPath: tyk.conf
            - name: tyk-gateway-middleware
              mountPath: /opt/tyk-gateway/middleware
            - name: tyk-gateway-apps
              mountPath: /opt/tyk-gateway/apps
          resources:
            limits:
              memory: 512Mi
              cpu: "1"
            requests:
              memory: 256Mi
              cpu: "0.2"
      volumes:
        - name: tyk-gateway-conf
          configMap:
            name: tyk-gateway-conf
            items:
              - key: tyk.conf
                path: tyk.conf
        - name: tyk-gateway-middleware
          hostPath:
            path: /run/desktop/mnt/host/c/Projects/k8s/tyk-oss-k8s-deployment/middleware
            type: DirectoryOrCreate
        - name: tyk-gateway-apps
          hostPath:
            path: /run/desktop/mnt/host/c/Projects/k8s/tyk-oss-k8s-deployment/apps
            type: DirectoryOrCreate

The files get copied across. The apps get deployed but not the middleware.

var fhirTransform = new TykJS.TykMiddleware.NewMiddleware({});
fhirTransform.NewProcessRequest(function(request, session, spec) {
  log('********* Starting Middleware Pre Execution **************')
 ...
  return fhirTransform.ReturnData(request, session.meta_data);
});

And inside an app that uses it -

 "custom_middleware": {
      "pre": [
        {
          "name": "fhirTransform",
          "path": "middleware/fhir-post-middleware.js",
          "require_session": false,
          "raw_body_only": false
        }
      ]
   },

Looks like you got a type too :wink: