This module main goal is to make nginx as scriptable as it could to be.
Every scripting language known to me has its own standard library. This is a very good opportunity for a programmer that uses a language to write standard (i.e. multithreaded, synchronous, apache-based) web applications. But it is no good for the embedding into an async application like nginx. For example the node.js developers have to re-implement almost all existing libraries from scratch (and they do so). If you don't fully understand the difference between the applications based on sync and async principles please read about asynchronous I/O first.
And SpiderMonkey doesn't have any library. And nobody expect to have it in this language. More of that, the browser interface is also async and event-based. So we can relax and just script nginx instead of making another Web 2.0 framework.
In other words the nxg_http_js_module tries to reflect the nginx functionality in JS.
You can touch the ground running if you have ever seen ngx_http_perl_module or mod_perl before.
- full port of
ngx_http_perl_module
; - support for native nginx sub-requests with JS callback for the the response and its body;
- cosy headers management (via
Nginx.HeadersIn
andNginx.HeaderOut
classes); - fast native cookies support with the same code nginx uses itself (via
Nginx.HeadersIn.cookies
); - environment variables handling with the code taken from Mozilla's JS interpreter;
- lots of useful properties of nginx object (
Nginx.time
,Nginx.prefix
,Nginx.version
, etc); - plain
require()
function that finds JS files walking throughNginx.prefix
+JSLIB
environment variable (likeRUBYLIB
andPERL5LIB
); - initial support for files via
Nginx.File
(create/delete/rename, simple open/close/read/write, all in UTF-8); - handy tests suit written in plain JavaScript (using asynchronous test framework from liby.js)
The code uses ngx_assert()
and ngx_log_debug()
almost everywhere, so the debugging must not be a pain.
Installation is straightforward. Just add this module with a familiar --add-module
configuration directive in nginx wiki.
With a fresh SpiderMonkey build from sources the ngx_http_js_module module was successfully tested on:
- Ubuntu 8.04.3 32-bit (2.6.18-virtuozzo; 2.6.24-23-openvz in VirtualBox)
- Ubuntu 10.04 32-bit (VirtualBox 3.2.4)
- FreeBSD 8.0 32-bit (Parallels Desktop 5.0 Mac)
- FreeBSD 7.3 32-bit (VirtualBox 3.2.4)
- OpenBSD 4.7 64-bit (VirtualBox 3.2.4)
- Debian 5.0 32-bit (VirtualBox 3.2.4)
- Debian 5.0 64-bit (VirtualBox 3.2.4)
- Debian 5.0 32-bit PowerPC (Mac mini G4)
- Mac OS 10.6.3 32-bit and 64-bit (Core Duo and Core 2 Duo iMacs)
- nginx versions: 0.8.11 (tested up to 0.8.54), 0.9.7, 1.0.1;
- SpiderMonkey 1.7.0;
- curl near 7.19 for automated testing.
This module requires the SpiderMonkey 1.7 (with the JSAPI on 2010-03-26) being properly installed in your system.
Notes on SpiderMonkey support on different platforms (2010-05-27):
- Ubuntu has relatively bad support for SpiderMonkey at the moment: 1.5 and 1.8 versions only;
- Debian has much greater SpiderMonkey in it and may be useful;
- MacPorts is no good;
- FreeBSD got the same as MacPorts.
In short all those are suitable to run ngx_http_js_module, but those may lack a support for the JSON module or UTF-8 strings.
Any way we can always build SpiderMonkey ourselves from sources. Firefox 3.6 sources (or my github mirror of the js/
sub-folder) ships with an independent SpiderMonkey source tree. This means we can build a SpiderMonkey library and install it with all the header files without even touching the Firefox source code. All we have to do is the following:
cd firefox-sources/js/src
./configure [--prefix=/usr/] [--disable-jit] [--disable-tests] [--enable-debug]
make
sudo make install
If you have a 64-bit Mac (even with only 32-bit kernel) use --disable-jit
to be able to make
SpiderMonkey. Use --enable-debug
if you plan to develop a little ;)
On a clear FreeBSD 8.0 at least the following have to be installed from ports: gmake, perl, python, zip. Run gmake
and gmake install
after ./configure
.
This could create the following in prefix
:
- bin/js-config
- include/js/*
- lib/libmozjs.(so|dylib)
- lib/libjs_static.a
ngx_http_js_module relies only on libmozjs.(so|dylib)
library and on include/js/*
headers.
If you get an error like the following:
[error]: reserved slot index out of range at <no filename>:0
[error]: Can`t JS_InitStandardClasses() at <no filename>:0
[emerg]: global object initialization failed in /www/ngx_http_js_module/nginx.conf:24
it means you have a wrong libjs
version installed. Try to uninstall it first (with kinda like sudo apt-get remove libmozjs-dev
) and then install the latest version.
The JS module could be compiled as any other nginx module:
./configure --add-module=/absolute/path/to/the/ngx_http_js_module/
If you have installed Spidermonkey at non-standard path, or nginx cannot automatically find the library, you should set some variables before running configure:
export SPIDERMONKEY_INC=/path/to/spidermonkey/include # allows config to find <jsapi.h>
export SPIDERMONKEY_LIB=/path/to/spidermonkey/lib # allows config to find libmozjs
./configure ...
If you are on an ELF-based platform and do not want to bother with LD_LIBRARY_PATH
please define LD_RUN_PATH
to set rpath like so:
export LD_RUN_PATH=/path/to/spidermonkey/lib # adds the lib path to the directories that nginx
# will search to find libmozjs on Linux/Solaris
./configure ...
If you want to look into the guts and do something there, please configure like the following:
HTTP_JS_COLOR=yes ./configure --with-debug --add-module=/absolute/path/to/the/ngx_http_js_module/
in which:
--with-debug
flag compiles nginx with all the debug features (debugging log for example);HTTP_JS_COLOR=yes
environment variable enables the colored logging for the JS module.
If you want to have both the vanilla and JS-flavored nginx use --prefix=
./configure --prefix=/usr/local/nginx-js/ --add-module=/absolute/path/to/the/ngx_http_js_module/
Then run make as you usual do:
make
After the make process has successfully completed you may run some simple tests for JS module (before the actual installation) with this:
make test-js
This will run nginx on 19090 port and issue some requests to it with curl.
If you get the following error:
.../objs/nginx: error while loading shared libraries: libmozjs.so: cannot open shared object file: No such file or directory
please, try to set LD_LIBRARY_PATH
environment variable like so:
export LD_LIBRARY_PATH="/usr/local/lib/"
Or try to define the LD_RUN_PATH
as described above and re-compile.
And then you may run make test-js
again.
make test-js
(calling run-tests
from the module sources) tries to set the LD_LIBRARY_PATH
environment variable for you.
Then you may peacefully run: make install
This module tries to mimic the perl modules in most cases.
location /demo/random {
js 'function (r) { r.sendString(Math.random()); return Nginx.OK }';
}
curl http://localhost/demo/random
0.540526149221515
http {
js_load "js/demos/handler.js";
server {
location = /demo/handler {
js Hello.handler;
}
}
}
in handler.js:
Hello = {
handler: function (r) {
r.sendHttpHeader('text/html')
if (r.headerOnly) {
return Nginx.OK
}
r.print('hello!\n<br/>')
if (Nginx.File.exists(r.filename)) {
r.print(' exists!\n')
}
r.sendSpecial(Nginx.HTTP_LAST)
return Nginx.OK
}
}
curl -I http://localhost/demo/handler
HTTP/1.1 200 OK
Server: nginx/0.8.37
Date: Sun, 23 May 2010 15:18:26 GMT
Content-Type: text/html
Connection: keep-alive
curl http://localhost/demo/handler
hello!
<br/>
js_set $msie6 '
function (r) {
var ua = r.headersIn["User-Agent"]
if (/Opera/.test(ua)) {
return ""
}
if (/ MSIE [6-9]\.\d+/.test(ua)) {
return "1"
}
return "";
}
';
location = /demo/msie6 {
if ($msie6) {
return 404;
}
rewrite ^ /;
}
curl http://localhost/demo/msie6
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
</body>
</html>
location = /demo/secret-data {
js_access '
function (r) {
if (r.headersIn.cookies.goodUser)
return Nginx.OK
return Nginx.HTTP_FORBIDDEN
}
';
}
curl http://localhost/demo/secret-data
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/0.7.62</center>
</body>
</html>
syntax: js object.property | 'function (r) { ... }' | any other JS expression that returns a Function
default: none
context: location
Defines a JS handler for current location. The request object is passed to a function as the first argument.
syntax: js_access object.property | 'function (r) { ... }' | any other JS expression that returns a Function
default: none
context: location
Defines a JS handler for the current location at an access phase. The request object is passed to a function as the first argument.
Return Nginx.OK
to pass the request further or return Nginx.HTTP_FORBIDDEN
to deny an access to the location.
syntax: js_load path
default: none
context: http
Simply loads and executes the file specified by path. A script from the file will be compiled and run in the global scope.
syntax: js_set $variable object.property | 'function (r) { ... }' | any other JS expression that returns a Function
default: none
context: http
Defines a variable read handler (write handler is planned). The request object is passed to a function as the first argument.
This object of class Nginx.Request is the most important one. It let us do almost everything we could expect in the HTTP server: read and write headers of both a request and a response, check and then get or reject a request body, send data back to the client with over keep-alive connection, set a timer on a request, redirect a request and so on.
Every time a request reaches a JS handler a native request structure will be wrapped in an Nginx.Request instance and passed to the JS code. Then some work is doing in JS. After that nginx terminates the native request structure with all its data (headers, variables, request body, etc.), and the module will mark the wrapper object (and all satellite objects like Nginx.HeadersIn and Nginx.Cookies) as inactive. Every call to such a deactivated wrapper will cause an exception.
There are some thing that must be implemented to get the full and intuitive request wrapper:
- safe properties enumeration: now we may somehow interact with a request enumerating its properties;
- send “last chunk” on the request completeness: we have to send the HTTP_LAST_ manually for now;
- binary data API is needed: today we can response with a plain text only;
- sane return values and exceptions: for now the returned
Nginx.OK
is a most common sign of success.
Tell us the uri of a request.
Gives us a request method: GET
, POST
, etc.
Maps the uri to the filename respecting the current nginx configuration. Returned path may (and always will) point to an inexistent file.
Return a string representation (i.e. "81.19.68.137"
) of the client IP.
Returns a wrapper object (of class Nginx.HeadersIn) for the input headers structure. This wrapper helps us to get (and some times set) the request headers.
r.print(r.headersIn['User-Agent'])
Return a wrapper object (of class Nginx.HeadersOut) for the output headers structure. This wrapper gives us a chance to set (and get, for example in the header/body filter) the output headers.
r.headersOut['X-Powered-By'] = 'MegaGigaAwesome CMS'
Stores the arguments (nginx handles request URI and arguments separately).
Indicates does the client expect a body in the response or not. It will be true
for a HEAD
request for example.
Tell us in which file nginx have the request body stored. We have to getBody()
before use this property to be sure that nginx has the body received already. nginx could store the request body in a temporary file if it does not fit in memory or if nginx was configured to so by the client_body_in_file_only directive.
Indicates the presence of the request body. It is a sister of the headerOnly
property but in reflection around the wire.
If the request body fits in memory (can be tweaked with client_body_buffer_size) we can get it with the body
property. Otherwise use the bodyFilename
property.
Gives an access to the request variables. It return a wrapper object (of type Nginx.Variables) which can read and write all the variables the rewrite module can set. Our own variables defined with js_set
are also available through the variables
property.
if (r.variables.ancient_browser)
return Nginx.HTTP_FORBIDDEN
Allows and disallows range request.
if (r.headersIn.cookies.goodUser)
r.allowRanges = true
Indicates if the request is “internal” in terms of nginx. We can switch it if we want.
// do some security checks
r.internal = true
Logs a message
through the nginx built-in log mechanism. Does the same as Nginx.logError()
but with the request log context preserved.
If level
is omitted Nginx.LOG_INFO
is used.
Sends the header. In addition this method sets the response status to 200
if not it set, and sets the headersOut["Content-Type"]
to the value of argument contentType
.
On success returns Nginx.OK
.
r.sendHttpHeader('text/html; charset=utf-8')
Sends data of a UTF-8 encoded string
. This method is able to send only text, not binary data. This means we can not send a string containing the binary zero symbol (i.e. "\0"
). Also this method could not send an empty string, it just returns without doing any work.
On success returns Nginx.OK
.
r.print('Hello, World!')
This method is pretty simple, it just flushes the request output chain. In terms of nginx it sends a special buffer with a flush
field set on.
On success returns Nginx.OK
(the same as print()
).
// sending the response body in two chunks
r.print('Hello')
r.flush()
r.print(', World!')
This is a combo-method. It does many thing at once: calculates the size in bytes of the string
, sets the Content-Length
header, sets an optional Content-Type
header, then sends the response headers (like sendHttpHeader()
does) and sends the string
(like print()
does). Overcomplicated? Yes, but all this must be done on the nginx side when the client doesn't support HTTP/1.1. The main thing in all this is that the JS can not calculate a real bytes count will be send on the wire. JS cal only tell us a count of UTF-16 characters in a string, while we need the count of bytes. In short, just do not use this method if you do not really need it ;)
On success returns Nginx.OK
. Due to the overcomplicating, this method may return an error and throw an exception.
var body = ''
// ...
body += 'Hello'
// ...
body += ', World!'
r.sendString(body, 'text/plain')
Send a “special” value through the request. The only tested special value is Nginx.HTTP_LAST
(the NGX_HTTP_LAST
in terms of nginx).
On success returns Nginx.OK
.
r.puts('The reques is done.')
r.sendSpecial(Nginx.HTTP_LAST)
AFAIK, sending the Nginx.HTTP_LAST
signals nginx to send a last chunk in a chunked response, otherwise a connection will hang.
As far as nginx is asynchronous by nature, we can't get the response body at once. We have to wait for it to arrive on the wire, go through the OS kernel buffers and only then we can catch the body data. This method asks nginx to wait for all this things to happen and then call the callback
. In the callback it is guarantied that the request body related things (r.body
and r.bodyFilename
) will be useful to get the data of the request body.
On success returns Nginx.OK
if the body is ready ATM and Nginx.AGAIN
if the network could be touched before the body is ready.
The following example shows how to get all the request body in memory.
In the nginx.conf:
location /ajax {
client_max_body_size 512K;
client_body_buffer_size 512K;
js handler;
}
The handler:
function handler (r) {
r.sendHttpHeader('text/plain; charset=utf-8')
r.print('waiting for body')
r.flush()
function onbody ()
{
r.print('got a body: ' + r.body)
r.sendSpecial(Nginx.HTTP_LAST)
}
r.getBody(body)
return Nginx.OK
}
As of 0.2.16 we can return a HTTP status code just from the callback. If not Nginx.OK
is used.
This method ask nginx to discard body with all the tenderness it has. It is not a trivial thing ignoring request body, but we can relax relying on nginx wisdom ;)
On success returns Nginx.OK
.
function handler (r) {
r.discardBody()
r.sendHttpHeader('text/plain; charset=utf-8')
r.puts('the request body is not good for me')
r.sendSpecial(Nginx.HTTP_LAST)
return Nginx.OK
}
This method helps to add the content of a file to the request body. We can send more then one file and even the same file more then once. We can set the frame in file to be sent with the offset
and bytes
arguments. The two arguments are optional. If only offset
is specified, nginx will send the file from the offset
byte from the begin of the file and till the end of the file. If neither offset
nor byte
was specified, nginx will send the entire file to the client.
As far as sendfile()
adds just a file buf into the output chain, we can send files mixed with strings, specials and flushes.
On success returns Nginx.OK
(the same as print()
).
In file.txt:
send me please!
in handler:
r.sendfile('file.txt')
r.print('\ncan be split into: ')
r.flush()
r.sendfile('file.txt', 0, 4)
r.print(', ')
r.sendfile('file.txt', 5, 2)
r.print(' and ')
r.sendfile('file.txt', 8, 7)
r.sendSpecial(Nginx.HTTP_LAST)
the result:
send me please!
can be split into: send, me, please!
Yes, it's kinda like rewrite :)
Note that nginx stores uri (is used while finding a location) and arguments (the data after the ?
character) separately. And to avoid additional uri parsing we can specify the uri
and args
arguments for redirect()
.
On success returns Nginx.OK
.
var cookies = r.headersIn.cookies
if (Nginx.md5(cookies.username + ':a secret') != cookies.signature)
{
r.redirect('/login', 'from=' + r.uri)
return Nginx.OK
}
This method creates a nginx internal timer associated with the request. It means that the timer will be automatically cleared on the request destruction. And that the only one timer may be set at once. To be able to set more that one timer per request, see the the timers.js in js/
folder of the module.
The callback
must be a function (a closure) and timeout
is specified in milliseconds.
On success returns Nginx.OK
.
Example of a cascade timer setting:
function handler (r)
{
r.sendHttpHeader('text/html')
var count = 10
function sayHello ()
{
r.print('Hello # ' + count + '!\n')
r.flush()
if (--count > 0)
r.setTimer(sayHello, 250)
else
r.sendSpecial(Nginx.HTTP_LAST)
}
sayHello()
return Nginx.OK
}
produces (with 250ms delay for each line):
Hello # 10!
Hello # 9!
Hello # 8!
Hello # 7!
Hello # 6!
Hello # 5!
Hello # 4!
Hello # 3!
Hello # 2!
Hello # 1!
In this example nginx will wait till all the sayHello()
's will fire and only after that finalize the request.
Just clears the timer if set. There is no arguments as far as the only one timer can be set per request (without a third-party library as timers.js).
Sub-requests in nginx are by no means an AJAX requests. Sub-requests are quite useless at the point as far as they share the same headers and variables set in the main request, in the same time requests are processed in parallel. In short use this if and only if you know what you are doing ;)
This methods creates a sub-request (a dependent request with shared almost everything) and directs it to the uri
. After the sub-request is complete (always asynchronous) nginx will invoke the callback
.
On the successful sub-request creation the method return a sub-request object (actually a Nginx.Request
instance). It looks like a general request but it is not, be careful with it as far as we do not know all the cases in which it can crash ;)
The callback itself is very interesting part:
function callback (sr, body, rc) { /* ... */ }
The callback takes three parameters: sub-request (sr
) in which context it was invoked, the response body data (body
) and the sub-request “result code” (rc
). The last one is an internal nginx code and now is used for test purposes only.
This is a good idea to issue a sub-request through the proxy_passs
even to the nginx itself. This trick helps to deal with other modules in cost of establishing a loopback connection from nginx to nginx itself.
In nginx.conf:
location /nginx.org
{
proxy_pass http://nginx.org/;
proxy_buffers 3 512K;
proxy_buffer_size 512K;
}
the handler:
function handler (r)
{
var uri = '/nginx.org'
r.sendHttpHeader('text/plain; charset=utf-8')
r.print('accessing ' + uri + '\n')
r.flush()
function callback (sr, body, rc)
{
r.print('got body with length ' + body.length + '\n')
r.sendSpecial(Nginx.HTTP_LAST)
}
r.subrequest(uri, callback)
}
This is all-in-one variable. It collects those things doesn't fit anywhere else. Nginx.md5()
is a good example of that kind of things.
Every call to new Date()
issues a syscall and gives the most current date time available. nginx in its turn caches time for a performance reasons. It stores it in the every event process “cycle” so the time can be obtained without a syscall. Nginx.time
takes that cached time for us.
Return a number with milliseconds from the epoch (like +new Date()
does).
Gives a string with a current nginx prefix. Prefix is the path to the “home directory” of nginx configuration. It may be set with different ways see the configuration time prefix and -p
option. Note that some versions of nginx may take the prefix from the configuration file path.
To pid or not to pid? It is just a process id. AFAIK is also cached as a time.
nginx version as a number. 8038
for example.
if (Nginx.version >= 8038)
// do sime crazy stuff
nginx version as a string. ""0.8.38""
for example.
r.print('we are using nginx ' + Nginx.VERSION + ', and you?')
Reflects NGX_LOG_*
constants:
- LOG_STDERR
- LOG_EMERG
- LOG_ALERT
- LOG_CRIT
- LOG_ERR
- LOG_WARN
- LOG_NOTICE
- LOG_INFO
- LOG_DEBUG
Log at debug level:
Nginx.logError(this.LOG_WARN, 'do not forget to buy some milk!')
See more about error_log.
Reflects NGX_HTTP_*
constants:
- HTTP_OK
- HTTP_CREATED
- HTTP_ACCEPTED
- HTTP_NO_CONTENT
- HTTP_PARTIAL_CONTENT
- HTTP_SPECIAL_RESPONSE
- HTTP_MOVED_PERMANENTLY
- HTTP_MOVED_TEMPORARILY
- HTTP_NOT_MODIFIED
- HTTP_BAD_REQUEST
- HTTP_UNAUTHORIZED
- HTTP_FORBIDDEN
- HTTP_NOT_FOUND
- HTTP_NOT_ALLOWED
- HTTP_REQUEST_TIME_OUT
- HTTP_CONFLICT
- HTTP_LENGTH_REQUIRED
- HTTP_PRECONDITION_FAILED
- HTTP_REQUEST_ENTITY_TOO_LARGE
- HTTP_REQUEST_URI_TOO_LARGE
- HTTP_UNSUPPORTED_MEDIA_TYPE
- HTTP_RANGE_NOT_SATISFIABLE
- HTTP_CLOSE
- HTTP_OWN_CODES
- HTTPS_CERT_ERROR
- HTTPS_NO_CERT
- HTTP_TO_HTTPS
- HTTP_CLIENT_CLOSED_REQUEST
- HTTP_INTERNAL_SERVER_ERROR
- HTTP_NOT_IMPLEMENTED
- HTTP_BAD_GATEWAY
- HTTP_SERVICE_UNAVAILABLE
- HTTP_GATEWAY_TIME_OUT
- HTTP_INSUFFICIENT_STORAGE
Indicate a bad request:
function handler (r) {
if (r.args.length > 100)
return Nginx.HTTP_BAD_REQUEST
}
Reflects some NGX_HTTP_*
flags:
- HTTP_LAST
- HTTP_FLUSH
For example send the last chunk:
function handler (r) {
r.sendHttpHeader('text/plain')
r.print('Hello, World!')
r.sendSpecial(Nginx.HTTP_LAST)
return Nginx.OK
}
Reflects some NGX_*
constants:
- OK
- ERROR
- AGAIN
- BUSY
- DONE
- DECLINED
- ABORT
Tell a caller that all is OK:
function handler (r) {
r.sendString('All is OK!')
return Nginx.OK
}
Calculates a MD5 sum of the given string str
. As far as nobody expect it to be calculated on the raw UTF-16 bytes vector of the string, md5()
first converts the string to a UTF-8 representation and then does the main work. The can be some issues with this method applied on a real unicode string. If you encounter some call me anytime ;)
Writes a message
to the global nginx error log at the level
.
Nginx.logError(this.LOG_EMERG, 'forgot to buy the milk!!!')
See more about error_log.
Nginx.HeadersIn
with Nginx.HeadersOut
do all the job with headers. It is useful to learn how does headers work under the hood.
We can't create Nginx.HeadersIn
instance directly but only with r.headersIn
. That's because of the headers instance have to know to which request object (and headers_in
structure in terms of nginx) it belongs.
All the properties we set or get with Nginx.headersIn
instance will be map to the native request headers. So we can not set any property on the instance without nginx knowing about it. Think of it as a hash implementation with some smart logic behind it.
r.headersIn['Content-Length']
There are a few special read-only properties described below.
It is another wrapper to the request headers just like headersIn
itself, but it focuses on the request cookies directly. We can read the cookie value by its key like so:
r.headersIn.cookies['session']
or like so:
var cookies = r.headersIn.cookies
cookies.session
the result could be the same.
See the Nginx.Cookies
class description for details (coming soon).
This property is used for test purposes only. It reflects the multilevel cache architecture of headers in nginx.
We can set the Content-Length
header with this:
r.headersIn['Content-Length] = '555'
and then check:
r.headersIn.$contentLength === '555'
This property is just a reader for r->headers_in.content_length
.
Like the $contentLength
this property is used for test purposes only.
We can set the Content-Length
header with this:
r.headersIn['Content-Length] = '555'
and then check:
r.headersIn.$contentLengthN === 555
Note that type of $contentLengthN
is number here. nginx does the same (with r->headers_in.content_length_n
) on the native side.
Like the $contentLength
this property is used for test purposes only.
It does fully duplicate the logic of $contentLength
but for the Range
header.
Again, as for Nginx.HeadersIn
we can not create Nginx.HeadersOut
instance directly but only with r.headersOut
request property. That's because of the output headers instance have to know to which request object (and headers_out
structure in terms of nginx) it belongs.
r.headersOut['WWW-Authenticate'] = 'Basic realm="Nginx Area"'
This class is almost the same as Nginx.HeadersIn
, except in some special properties.
All properties are proxies to or from the native request output headers.
Number (in seconds) representation of Date-Time
output header. Reflects r->headers_out.date_time
of the native side.
Number (in bytes) representation of Content-Length
output header. Reflects r->headers_out.content_length_n
of the native side.
Number (in seconds) representation of Last-Modified
output header. Reflects r->headers_out.last_modified_time
of the native side.
Number (in bytes) representation of Content-Type
output header. Reflects r->headers_out.content_type_len
of the native side.
Lowercased string representation of Content-Type
output header. Reflects r->headers_out.content_type_lowcase
of the native side.
This class is just a fast and lightweight wrapper for cookies in nginx. It does not parse Cookie
header only search trough it if we read a property. This way of access to the cookies is not very fast if we have to read many values many times. Cookie names can not be enumerated. If you need a full-featured cookie management experience you may just parse the Cookie
header and store its data in a simple object.
In short use this class if you want to do only few lookups and go.
As in headers wrappers all the properties in Nginx.Cookie
instance are mapped to the content of Cookie
header with a functions built in nginx.
At the moment we can't delete or add or edit cookies value with this class (try to use r.headersOut['Cookie'] = 'all cokies here'
instead).
This class instances have some additional functionality good for test purposes only.
Return a count of cookies.
Marks the cookies headers array as empty. This method does not try to fully delete cookies headers, just marks the native array as empty.
Variables in nginx are even more complicated thing then the headers. AFAIK, variable may be cached or not, indexed or not, has getter/setter or not. Every module that adds a variable may handle its value with different ways: share the value between different variables, invalidate cache or change the setter and getter function. Huge amount of flexibility! And all this is a subject to change (the last one was in 0.8.36).
We can access all the nginx variables defined by variouse modules with a simple hash-like inteface (yeap, like headers and cookies):
r.variables.limit_rate = "4096" // 4k
and it should work ;)
On an attempt to set inexistent variable this class could throw an exception (can't find variable …
). On getting inexistent variable just returns undefined
.
In short we can safely get a variable value as far as it is supported by nginx. Setting a value is much more complicated thing. This module tries to duplicate the logic from the rewrite module.
This class is a tiny wrapper around ngx_fd_t
. File descriptor (ngx_fd_t
) has such a value that fits in a pointer. This makes a work with Nginx.File
object relatively fast and its memory footprint almost nothing.
There are some simple things left for future:
- full support for
File.open
: now we can open a file only one way; - add support for open file cache: for now the
Nginx.File
is just a lightweight wrapper forfd
; - File#seek.
Gives a count of files open with Nginx.File
. Very useful for debugging. See “close and openFiles” test in tests/file.js.
Reflects some NGX_FILE_*
constants:
- RDONLY
- WRONLY
- RDWR
- CREATE_OR_OPEN
- OPEN
- TRUNCATE
- APPEND
- NONBLOCK
- DEFAULT_ACCESS
- OWNER_ACCESS
Reflects some NGX_FILE_*
constants:
- INVALID
- ERROR
Reflects some NGX_FILE_*
constants:
- HAVE_CASELESS_FILESYSTEM
Tries to open file and on success return an instance of Nginx.File
.
var file = Nginx.File.open(Nginx.prefix + 'nginx.conf')
For now file will be created or opened (Nginx.File.CREATE_OR_OPEN
), with read/write access (Nginx.File.RDWR
) and with a default file access (Nginx.File.DEFAULT_ACCESS
).
On failure return null
.
Just renames file src
to file dst
.
Just removes file this path
.
Checks if path
directs to a file.
Return null
if path does not exist at all, false
if there is something but not a file and return true
on an existent plain file.
Returns an access code of fname
. If fname
points to inexistent entry this mthod returns null
.
var access = Nginx.File.getAccess('db.json')
if (access != 0644)
return Nginx.ERROR
Returns an access code of fname
. As simple as it could to be.
if (Nginx.File.setAccess('db.json', 0644) == Nginx.ERROR)
return Nginx.HTTP_INTERNAL_SERVER_ERROR
Return a size of a file in bytes.
An equivalent to the Ruby's File.read
(reads the entire file in memory):
Nginx.File.read = function (name) {
var file = this.open(name)
if (!file)
return null
return file.read(file.size)
}
Reads count
of bytes from the current file position and tries to convert the bytes to a string.
On success returns a string with the content of the file. Otherwise return null
.
Converts string
to the UTF-8 bytes sequence and writes to the file from the current position.
Set a position in the file to offset
. For now SEEK_SET
is supported only.
Closes the corresponding ngx_fd_t
stored within this Nginx.File
instance and marks the instance as deactivated. Do not use a closed file at all, it would throw an exception.
Very simple interface to file system directories.
Creates a directory with path
if all the path except the last part is already exists.
if (Dir.create('users/' + uname, 0700) == File.ERROR)
throw new Error('could not create user home dir')
Removes the last directory specified by path
. Acts just like rm
shell command.
Creates full path
step by step. It acts like mkdir -p
shell command.
On success return Nginx.OK
. On error returns errno
.
Nginx.Dir.createPath('/a/b/c/ddd/', 0755)
Note that createPath()
expects absolute path or relative path starting with './'
. The trailing slash is required also. Expecting absolute path it ignores one leading char (i.e. '/'
) on Unices and tree leading chars (i.e. C:/
) on windows.
Walks trough the directories starting at path
and deletes everything it meets: files, directories, sockets, fifos, etc.
On success returns Nginx.OK
. Otherwise errno
.
USE WITH CARE! ngx_walk_tree()
(on which removeTree()
is based) is very a very smart function, much smarter than me ;)
This method is a straight forward interface to the smart ngx_walk_tree()
function. It takes four callbacks:
onfile
is called if file is met; takes four parameters: path, size, access, and mtime;ondirenter
is called on entering a directory; takes three parameters: path, access, mtime;ondirleave
is called on leaving a directory; takes three parameters: path, access, mtime (the same as inondirenter
);onspecial
is called if some special entry is met (like socket or fifo); takes only one parameter: path.
Note that all the additional parameters like mtime
and size
are taken from the directory entry, so it costs almost nothing. For more detailed definition please see the source of ngx_walk_tree()
.
On success returns Nginx.OK
. Otherwise errno
or the value returned by a callback.
Peter Leonov (Пётр Леонов) gojpeg@gmail.com with a help from:
Initially developed for fun and Inshaker, the cocktail site (in Russian, very AJAXy, best viewed with Google Chrome in-browser translation).
Copyright (c) 2008-2010, Peter Leonov gojpeg@gmail.com.
This module is licensed under the terms of the BSD license.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the Inshaker nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- nginx Lua module — embed power of Lua into nginx;
- nginx echo module — brings
echo
,sleep
,time
,exec
and more shell-style goodies to nginx config file; - nginx V8 module (github mirror) — enables you to run any JavaScript script Google's V8 JavaScript Engine supports;
- node.js — evented I/O for V8 JavaScript.