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 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. |
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
...
context.proxyTo(long maxRequestTimeMsecs, Config.Destination destination);
context.proxyTo(long maxRequestTimeMsecs, String destinationName);
context.proxyToServer(long maxRequestTimeMsecs, String scheme, String hostname, int port);
context.proxyToServer(long maxRequestTimeMsecs, String scheme, String hostname, int port, boolean ignoreSSLCert); // Requires minimum Ceptor v6.4.1
context.proxyToServer(long maxRequestTimeMsecs, String scheme, String hostname, int port, boolean ignoreSSLCert, int timeoutSeconds); // Requires minimum Ceptor v6.4.7
context.proxyToServer(long maxRequestTimeMsecs, String scheme, String hostname, int port, boolean ignoreSSLCert, int timeoutSeconds, String templateJSON); // Requires minimum Ceptor v6.4.7
Proxy requests to a server immediately.
...
Code Block | ||||
---|---|---|---|---|
| ||||
// 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".
...