Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Javascript

Javascript code is executed to generate a resulting value. Full scripts can be executed, or a macro can contain script code which is run.

A script is assumed, if the value is prefixed with %{script} then the entire rest of the value is executed as a script.
When using the GUI to enter values, a radio button allows you to select which language to use for scripting in the editor handles prefixing the string, and changing the editor to javascript mode for syntax hiliting.

If you start a string with %{file} then the actual contents of the script will be loaded from the file (meaning the file needs to exist on the gateway where it is executed).

Groovy scripts

Like with javascript, you can use groovy scripts to execute code - if the value is prefixed with %{script:groovy} then the rest of the value is executed as a groovy script. You can find more information about the groovy language here: http://groovy-lang.org/documentation.html and this page: http://groovy-lang.org/differences.html contains a quick list of the main differences between groovy and java languages.

Python scripts

By prefixing with %{script:python} you can execute python code - note that "jython" (http://www.jython.org/) is used for executing python scripts within the gateway - you can find very good information on integration with java here: https://wiki.python.org/jython/UserGuide

Request context

Script code has access to a variable called context which is of an instance of the class StateHolder - see Plugins for a description of its contents. It contains access to all data that the gateway uses internally, so it allows you to do anything (and also to mess up anything so be careful about what you do). 

Warning

Remember the saying - With Great Power comes Great Responsibility - take care what you do and ensure that you do not do stupid things like calling eval() on a string originating from outside, or constructing code dynamically based upon input from a browser query field.

If you e.g. call context.macro(context.getQueryOrPostParam('country')) and someone posts country with the value "%{script}java.lang.System.exit(1)" then you have just created a remote code execution vulnerability.

As long as you do not trust input from the outside, you are perfectly safe.


Global variables

All script code has access to a java.util.concurrent.ConcurrentHashMap called sharedState in which they can store data between requests - this data is globally shared between all scripts and all requests, so it can be used for caching data.

Request attributes within the context (accessed via context.requestAttributes is a hashMap containing request specific attributes - this is local for the request, and disappears once the request is finished.

Info

All scripts run within separate thread contexts, so any global variables a script assigns and reuses between runs are not shared between threads.

This is to ensure separation of requests so ensuring that scripts that are not thread-safe will work anyway when executed concurrently by multiple threads.
If your scripts contain variables that must be shared between threads, such as cached values, place them in the sharedState variable which is accessible from all scripts, for all requests and all script languages.

Return values

A script can return the value it generates, it can be e.g. true or false for scripts that run inside a condition, or it can be a string for a script that outputs an HTTP header value. In some cases (like when using python scripts that call a function to generate a result) it is easier to assign the result to a variable, to support this, Ceptor's script engine checks the return value from the script - if that is null, it will look in the variable "__result__" (two underscores + the word result + two underscores) - and if that exists after executing the script - its value will be used.

Using macros from scripts

If you need to use a macro from a script, you can call context.macro("xxxxx") - e.g. context.macro("https://%{HTTP_HOST}/myurl") will parse the macro and replace %{HTTP_HOST} with the appropriate value.

A word on performance

Executing scripts is significantly slower (usually several orders of magnitude slower) than using macros - so try to keep things simple and use macros whenever you can.

Ceptor precompiles the scripts on the first run, and reuses the precompiled version - When using Python scripts, this compilation can take several seconds on the first startup, since the Jython engine is quite expensive to start.

Javascript tends to be faster than both python and groovy after tends of thousands invocations once the JIT has optimized it

Literal values

If you (like with %{script}) prefix the string with %{literal} then the rest of the string is taken literally, meaning it is not parsed or processed in any way.
Note that this requires minimum version 5.66 to work.

Functions to call from scripts

...

Returns the value (or null if not found) of the state variable in the Ceptor Session.

context.agent

Allows access to the java API for Ceptor Agent - see Ceptor Agent for Java

.proxyTo(long maxRequestTimeMsecs, Config.Destination destination);
context.proxyTo(long maxRequestTimeMsecs, String destinationName);
context.proxyToServer(long maxRequestTimeMsecs, String scheme, String hostname, int port);

Proxy requests to a server immediately.

Note

When proxying from a script, all other actions for a location not yet completed are aborted and not done - so it depends on where you invoke it.

Since a location script is run as one of the first things when processing a location, any headers, authorization, authentication checks etc. that would otherwise occur later will not be done if proxying is invoked from a location script.
If proxying is invoked from e.g. an authorization script however, that is one of the last actions done as part of processing a location, so anything else (authentication, parameter checking, http header modification etc.) will already be done or queued up if proxying is invoked from such a script.

If in doubt, make sure all actions needed are done from an earlier location before you have a script which starts proxying programmatically.

Also note that compression of responses is not done when proxying is initated from a script.



context.agent

Allows access to the java API for Ceptor Agent - see Ceptor Agent for Java

Note

When accessing state variables from within a session, note that by default not all state variables are visible to gateway/dispatcher agents. These agents typically use a "restricted" connection to the session controller - this means that certain content is removed to reduce the risk of compromising sensitive data in case a gateway machine in a DMZ zone is compromised for any reason.

To make state variables visible for gateways, add them to safeStateVariables in the session controller configuration - see Session Controller Properties for more details.

...

Allows access to the trace, if Gateway Tracing is enabled for the request. Call with message and eventual exception.


context.addToMDC(key, value);

Stores a value in the SLF4J MDC which is then available for logging (requires minimum Ceptor v6.0.1).


context.getDeployedAPIsForLocation(<location>);

Code Block
/**
* Returns the list of deployed APIs for a given location - often <code>currentLocation</code> can be used to choose the current active location - otherwise you can get to locations via <code>config.locationList</code>
* 
* @param location Location to return APIs from
* 
* @return List of API versions currently deployed
*/
public List<Config.APIManagement.APIVersion> getDeployedAPIsForLocation(Config.Location location);

...

Code Block
languagejs
titleExamples
// Send response to client
context.gateway.sendResponse(context, 200, "OK", "application/json", '{"status": "ok"}');

// Send error back to client, error will be logged in access log and a warning in the system log
context.gateway.sendAndLogError(context, 403, "Access denied", "Didn't provide the magic password");

var locator = context.gateway.getGeoIP(context);
if (locator) {
  var countryCode = locator.getCountryCode();
  var countryName = locator.getCountryName();
  var city = locator.getCity();
  var postalCode = locator.getPostalCode();
  var region = locator.getRegion();
  var latitude = locator.getLatitude();
  var longitude = locator.gteLongitude();
  var isp = locator.getISP();
  var organisation = locator.getOrganization();
  var ip = locator.getIP();
  // Distance in kilometers from central london
  var distanceInKilometers = locator.distanceFrom(51.509865, -0.118092);
}

// If gateway trace is enabled, add this text to the trace
context.trace.trace("Some tracing information here");


Macros

Macros such as %{HTTP_HOST} are replaced with the contents they represent - in this case, the contents of the Host HTTP header sent from the client.
a macro is in the format %{<name>} or %{<prefix>:<name>}
The following prefixes exist:

  • base64 - base64 the value of the macro name.
  • urlencode - urlencode the value of the macro name.
  • htmlencode - htmlencode the value of the macro name.
  • requestcookie - Lookup the request cookie.
  • requestheader - Lookup the request HTTP header.
  • responseheader - Replace with response HTTP header.
  • requestattribute - Replace with named request attribute value, set in the location.
  • responsecookie - Replace with value of response cookie as set from server.
  • statevariable - Replace with state variable from Users Session.
  • query - Replace with the value of the specified query parameter.
  • compreq.cancelled - true, if the completed request was cancelled, e.g. because of timeout, and false if not.
  • compreq.exception - If the completed request failed with an exception, this contains the toString() output from the exception.
  • compreq.response - The response for the completed request, converted to a string.
  • compreq.response.code - Response code for a completed request, e.g. 200.
  • compreq.response.reason - Response reason for a completed request, e.g. OK.
  • compreq.response.headers - A JSON object containing all the response headers.
  • compreq.response.header - The header value (or JSON array of values) of the response header - the name of the header stake from the # in the name, e.g. %{compreq.response.header:sample#Content-Length} returns the Content-Length header for the completed response named "sample".

...