NPDS Simple Scripting Architecture

Introduction

During development of NPDS 2.0, I recieved a constant stream of requests for custom Server Side Includes. However, since every SSI added means additional parsing time, I couldn't very well add in lots of neat (but often of dubious usefulness) items like temperature, Longitude & Latitude, owner's Birthday, and so on, since not everyone would use them.

My answer to providing custom SSI is the NPDS Simple Scripting Architecture.

I've added the ability to link text-based scripts to user-defined HTML extensions. These scripts can return text values (most common) and/or can execute actions on the host machine. All that is required is a minimum of Newtonscript knowledge and the ability to follow the example scripts packaged with Script Editor.

Script Editor

On the right is a screenshot of your interface to NPDS Scripting, the Script Editor. The UI elements are as follows:

Writing Scripts

A NPDS Script is cast as a standard Newtonscript function that returns some value. Since most of what NPDS is used for is text-based, most NPDS Scripts should return Text. In this example, we're going to retrieve the version of NewtonOS in use on this Newton.

1. Open Script Editor and tap the "New Script" Button.

2. Enter the Following Text (Note that some of it is already written for you on the Notepad)

func(nullvar)

begin

local theOutput:= "NewtonOS ROM Version:" && Gestalt(0x1000003).ROMversionstring;

return theOutput;

end

3. Tap on the "Script Info" Box and enter a Name and SSI for this script. If you wish it to be available to NPDS right away, tap the "Script Is Active" checkbox. You may activate and inactivate scripts at will simply by changing the value of this checkbox. For purpose of explaining how to use this script, I am going to assume you gave it the SSI value "VERSION".

4. Let's try this function out. Tap the "Evaluate" button. You'll get a window that should give you either the text output of your function (or a cryptic error message and the Newtonscript Error Code).

5. To use this function in your WWW content, place the "HTML" code <VERSION> wherever you wish to return the value of this script. When that page is served, you'll see that the text that was returned when you evaluated the script is subsituted in place of the <VERSION> tag. To turn off this script, open it under the script editor, tap the information line, and un-check the "Script is Active" box.

Your Example Worked. What Do I Need to Know to Write My Own SSI?

Dummy Variable

Look at the head of the script: "func(nullvar)". The nullvar is a dummy variable that is required by NPDS's Compiler. You can change its name, but the fact remains that a NPDS Script must accept ONE and ONLY ONE argument. You cannot pass your own arguments to your script. (There is a way around this that I'll mention later)

Script Length

There is a limit on the length of NPDS Scripts to encourage you to write FAST and efficient code and currently that limit is 4096 Bytes.

Compiler Variables

4096 Bytes isn't a lot, especially in light of the fact that in order to command, for instance, nHTTPd, you usually need lengthy code like

GetRoot().|nHTPd:ALLPEN|

In anticipation of this, and also to save some typing, the NPDS Compiler supports symbols that stand for specific expressions.

Compiler Variable

Actual Value

|NPDS|

GetRoot().|nHTTPd:ALLPEN|

|NoteServ|

GetRoot().|pHTML:MAVON|

|DateServ|

GetRoot().|pDATES:MAVON|

|CardServ|

GetRoot().|pCARDS:MAVON|

CGI Environment Variables

If you've ever programmed in Perl, you'll recognize the names of these variables. These are the standard names for specific values that pertain to HTTP transactions on your server. To use these, place them in your script in the same context as you would a string. For instance, if you wanted to create a fully qualified URL to your server's DateServ, you could use the SERVER_NAME and SERVER_PORT variables like so:

"http://SERVER_NAME:SERVER_PORT/dates"

Notice that the variables aren't treated in any special manner. They will be substituted in at script-compile time with appropriate values.

Environment Variable

String Returned

SERVER_SOFTWARE

nHTTPd/2.02 (Newton)

SERVER_NAME

IP Address of Server

SERVER_PROTOCOL

HTTP/1.0

GATEWAY_INTERFACE

CGI/1.0 (NPDS_SSA)

SERVER_PORT

Current Server Port

REMOTE_ADDR

Client IP Address

REMOTE_HOST

Client DNS (If Resolved*)

HTTP_USER_AGENT

Client Browser

SCRIPT_NAME

Name of the currently executing script

Script Execution Time

When you Evaluate a script, also present in the results window will be a count of Ticks required for compiling and execution of your code. Script Editor will complain if your code takes longer than 360 Ticks (6 Seconds) to Execute.

While there is no limit on the size of function you create, keep in mind that when a NPDS Script is invoked, it is parsed, compiled, and evaluated on the fly during service of the page that invokes it. A script that takes 360 Ticks to run will add 6 seconds to the 1-4 seconds its takes NPDS to serve a page for a total of up to 10 seconds. Many browsers will time out at 10 seconds and close their connections. Hence the suggestion of keeping your scripts short and sweet...

If you want real POWER, learn the NPDS API and write your own custom plugins. With my user proto and documentation you can create a simple NPDS plugin in an afternoon, with the limiting step being your ability to write your own data generation functions.

Limit on Output Size

There is no longer a limit on output size. You're limited only by the free heap on your Newton. I'd suggest not generating more than about 2000 bytes, though. 

FAQ

Q: How many Scripts should I keep active at a given time?

A: Well, when a script is "Active", NPDS output is scanned for the presence of the SSI assigned to that scripts. This takes a couple of ticks per SSI. Not until an instance of the SSI is found is the script actually evaluated. However, since we're trying to keep things fast, I'd suggest no more than 10-12 User scripts active at any given time.

Q: How do I access the name of the file being requested?

A: During HTTP Request, nHTTPd fills a slot in its base frame called CURRENT_HTTP_REQUEST with the following data frame:

{ip: user's IP address (string),

path: the directory and filename requested (string),

time: HTTP formatted time of the request,

request: the path and filename of the request,

dns: the DNS value of the user's IP (if defined),

agent: the browser the client is using}

To access these items, use the following code:

|NPDS|.CURRENT_HTTP_REQUEST.slot

Q: What is that "Script Returns a Static Value" option?

A: NPDS keeps its speed despite the additional features I keep adding by doing a lot of pre-processing and cacheing. Some of NPDS' own SSI are evaluated at Startup and are held in memory throughout the lifetime of the session.

This option within the Scripting Architecture will be similar in function, but is not yet implemented. If you have scripts that will return a static value (say, the Latitude and Longitude of your City), you can check this option and when support for pre-evaluation is included in NPDS, your scripts will run much faster since they can be pre-compiled and evaluated when the server starts up.

Q: I hit Evaluate and get an exception.. I am sure my code is OK. and all I did was try to do something using NPDS..

A: It's good policy to keep nHTTPd open (but minimized) when you're writing scripts. You'll get better results without having to do some sneaky stuff to fool the system into thinking its open. See if this helps..

Q: Can I use these scripts as real CGIs (Programs that can be accessed directly by a HTTP request)?

A: Not yet. Some issues remain with getting this up and going in an easy to use and secure manner. But it's a definite goal. For now, you can define a pretty complicated SSI tag and use it as the total content of a note. Remember that you can generate a pretty large and complex chunk of text from one of these SSIs.