1. Motivation
  2. Benchmarks
  3. Download
  4. Build
  5. API
    1. Timers
    2. File System
    3. TCP
    4. HTTP
      1. Server
      2. Client
    5. Modules

Node

Node is a purely asynchronous I/O framework for V8 javascript.

This is an example of a web server written with Node which listens on port 8000 and responds with "Hello World" after waiting two seconds:

new node.http.Server(function (req, res) {
  setTimeout(function () {
    res.sendHeader(200, [["Content-Type", "text/plain"]]);
    res.sendBody("Hello World");
    res.finish();
  }, 2000);
}).listen(8000);

Check out the API documentation for more examples.

Node is free to download, use, and build upon.

Motivation

Evented Programming Makes More Sense

Difference between blocking/non-blocking design

There are many methods to write internet servers but they can fundamentally be divided into two camps: evented and threaded; non-blocking and blocking. A blocking server accepts a connection and launches a new thread to handle the connection. Because the concurrency is handled by the thread scheduler, a blocking server can make function calls which preform full network requests.

var response = db.execute("SELECT * FROM table");
// do something

An evented server manages its concurrency itself. All connections are handled in a single thread and callbacks are executed on certain events: "socket 23 is has data to read", "socket 65's write buffer is empty". An evented server executes small bits of code but never blocks the process. In the evented world callbacks are used instead of functions

db.execute("SELECT * FROM table", function (response) {
  // do something
});

I/O latency

l1 cache ~ 3
l2 cache ~ 14
     ram ~ 250
    disk ~ 41000000
 network ~ 240000000

purely evented interfaces rule out a lot of stupidity

Evented programs are more efficient

  1. pthread stack size 2mb default stack size on linux (1mb on windows, 64kb on FreeBSD) of course this is adjustable
  2. context switching benchmark
  3. Apache vs. Nginx
  4. event machine vs mongrel (neverblock)

The appropriateness of Javascript

  1. No I/O

    Javascript is without I/O. In the browser the DOM provides I/O, but non-browser javascript interpreters have only non-standardized functions to allow them print to console or access the network.

  2. No Threads
  3. Good compiler
  4. Universality of the language

    Contemporary computer infrastructure has two irreplaceable languages: C and Javascript. C is the language of operating systems. POSIX, the universal operating system API, is defined in C. So while you can interface with operating systems in Java and Haskell, those languages access must make system calls in C. Similarly, Javascript is the language of the web operating system. In place of POSIX is the DOM. You can wrap Javascript, you can compile to Javascript, but in the end browsers must be interfaced with in Javascript. Portable low-level systems tend to be written in C and portable web-level systems are written in Javascript.

Benchmarks

TODO

Download

TODO

Build

./configure
make
make install

Application Programming Interface

Conventions: Callbacks are object members which are prefixed with on. All methods and members are camel cased. Constructors always have a capital first letter.

Timers

Timers allow one to schedule execution of a function for a later time.

Timers in Node work as they do in the browser: setTimeout, setInterval, clearTimeout, clearInterval. See Mozilla's documentation for more information.

File System

TCP

HTTP (node.http)

Node provides a web server and client interface. The interface is rather low-level but complete. For example, it does not parse application/x-www-form-urlencoded message bodies. The interface does abstract the Transfer-Encoding (i.e. chuncked or identity), Message boundarys, and Keep-Alive connections.

HTTP Server (node.http.Server)

new Server(request_handler, options)

Creates a new web server. The options argument accepts the same values as the options argument for node.tcp.Server does. The options argument is optional.

The request_handler is a function which is called on each request with a Message object argument.

server.listen(port, hostname)

Begin accepting connections on the specified port and hostname. If the hostname is omitted, the server will accept connections directed to any address.

server.close()

Stops the server. Requests currently in progress will not be interrupted.

HTTP Request Message (node.http.Message)

This object is only created internally—not by the user. It is passed as an argument to the request_handler callback in a web server.

This object, unlike in other HTTP APIs, is used as an interface for both the request and response. Members and callbacks reference request data, like msg.method and msg.onBody. The methods are for sending a response to this message. Like msg.sendHeader() and msg.sendBody().

msg.method
The request method as a string. Read only. Example: "GET", "DELETE".
msg.uri
The request URI as a string. Read only. Example: "/index.html?hello=world".
msg.headers
The request headers expressed as an array of 2-element arrays. Read only. Example:
[ ["Content-Length", "123"]
, ["Content-Type", "text/plain"]
, ["Connection", "keep-alive"]
, ["Accept", "*/*"]
]
msg.http_version
The HTTP protocol version as a string. Read only. Examples: "1.1", "1.0"
msg.connection
A reference to the node.tcp.Connection object. Read only. Note that multiple messages can be sent on a single connection.
msg.onBody
Callback. Should be set by the user to be informed of when a piece of the message body is received. Example:
msg.onBody = function (chunk) {
  puts("part of the body: " + chunk);
}
A chunk of the body is given as the single argument. The transfer-encoding has been removed.

The body chunk is either a String in the case of utf8 encoding or an array of numbers in the case of raw encoding.

msg.onBodyComplete
Callback. Made exactly once for each message. No arguments. After onBodyComplete is executed onBody will no longer be called.
msg.setBodyEncoding(encoding)
Set the encoding for the request body. Either "utf8" or "raw". Defaults to raw. TODO
msg.sendHeader(status_code, headers)
Sends a response header to the request. The status code is a 3-digit HTTP status code, like 404. The second argument, headers, should be an array of 2-element arrays, representing the response headers.

Example:

var body = "hello world";
msg.sendHeader( 200
              , [ ["Content-Length", body.length] 
                , ["Content-Type", "text/plain"] 
                ]
              );
This method must only be called once on a message and it must be called before msg.finish() is called.
msg.sendBody(chunk)
This method must be called after sendHeader was called. It sends a chunk of the response body. This method may be called multiple times to provide successive parts of the body.
msg.finish()
This method signals that all of the response headers and body has been sent; that server should consider this message complete. The method, msg.finish(), MUST be called on each response.

Modules

Node has a simple module loading system. In Node, files and modules are in one-to-one correspondence.

As an example, foo.js loads the module mjsunit.js.

The contents of foo.js:

include("mjsunit");
function onLoad () {
  assertEquals(1, 2);
}

The contents of mjsunit.js:

function fail (expected, found, name_opt) {
  // ...
}
function deepEquals (a, b) {
  // ...
}
exports.assertEquals = function (expected, found, name_opt) {
  if (!deepEquals(found, expected)) {
    fail(expected, found, name_opt);
  }
};

Here the module mjsunit.js has exported the function assertEquals(). mjsunit.js must be in the same directory as foo.js for include() to find it. The module path is relative to the file calling include(). The module path does not include filename extensions like .js.

include() inserts the exported objects from the specified module into the global namespace.

Because file loading does not happen instantaneously, and because Node has a policy of never blocking, the callback onLoad() is provided to notify the user when all the included modules are loaded. Each file can have its own onLoad() callback. onLoad() will always be called exactly once for each file.

To export an object, add to the special exports object.

The functions fail and deepEquals are not exported and remain private to the module.

In addition to include() a module can use require(). Instead of loading the exported objects into the global namespace, it will return a namespace object. The exported objects can only be guaranteed to exist after the onLoad() callback is made. For example:

var mjsunit = require("mjsunit");

function onLoad () {
  mjsunit.assertEquals(1, 2);
}

include() and require() cannot be used after onLoad() is called. So put them at the beginning of your file.