Permalink
Browse files

Add API docs to README and make HttpClient.write behave more like net…

….Stream.write.
  • Loading branch information...
1 parent f50fc81 commit d0283c3e146e656d32df99159da16d75f704a49b @mscdex committed Jul 26, 2010
Showing with 173 additions and 41 deletions.
  1. +135 −5 README.md
  2. +12 −14 lib/grappler.js
  3. +26 −22 lib/http.client.js
View
140 README.md
@@ -1,19 +1,25 @@
Grappler
========
-Grappler is a minimalistic server for hanging ("comet") TCP/HTTP connections that exposes a single, consistent API across all transports.
-It supports the following transports:
+Grappler is a minimalistic server for "comet" and TCP connections that exposes a single, consistent API across all transports.
+Grappler currently supports the following transports (each with a list of currently supported browsers):
-- WebSockets (with Flash policy support)
+- WebSockets (with Flash policy support -- watches for policy requests on the same port as the grappler server)
+ - Firefox 4, Chrome 4, Safari 5, or any browser that supports at least Flash 9.x
- XHR Long Polling
+ - Any browser that supports XMLHttpRequest*
- XHR Multipart Streaming
+ - Firefox 3
- Server-Sent Events
+ - Chrome 6, Safari 5, Opera 9.x-10.x (DOM only)
- Plain TCP connections (Not yet implemented)
+* - Some browsers' XMLHttpRequest implementations contain unexpected quirks (i.e. the built-in web browser for Android 1.6)
+
Requirements
============
-- Tested with Node v0.1.100+
+- Node.JS v0.1.100+
- A client supporting one of the aforementioned transports.
- For HTTP (non-WebSocket) clients, cookies must be enabled for clients ONLY if they are going to send messages (i.e. via POST) to the server.
@@ -26,4 +32,128 @@ Visit the example server's test page in your browser: http://serverip:8080/test
API
===
-TODO :-)
+Grappler exports a few objects, with the main object being: `Server`.
+The others include the `LOG` and `STATE` objects, which contain constants used for when logging messages and representing the state of a client respectively.
+
+The `LOG` object is:
+ {
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3
+ }
+
+The `STATE` object is:
+ {
+ ACCEPTED: 1, // internal use only
+ TEMP: 2, // internal use only
+ PROTO_HTTP: 4, // client is HTTP-based
+ PROTO_WEBSOCKET: 8, // client is WebSocket-based
+ PROTO_TCP: 16 // client is plain TCP-based
+ }
+
+## Server
+
+### Constructor: new Server([options], [fnHandleNormalHTTP], [fnAcceptClient])
+
+Creates a new instance of a grappler server.
+
+`options` is an object with the following default values:
+ {
+ // A callback for receiving "debug" information. It is called with two arguments: message and message level.
+ // Message level is one of the values in the `LOG` object.
+ logger: function(msg, msgLevel) {},
+
+ // A string or array of strings which denote who is allowed to connect to this grappler Server instance.
+ // The format of the string is: "hostname:port", "hostname", or an asterisk substituted for either the hostname
+ // or port, or both, to act as a wildcard.
+ origins: "*:*",
+
+ // An integer value in milliseconds representing the interval in which a ping is sent to an HTTP client for those
+ // transports that need to do so.
+ pingInterval: 3000
+ }
+
+`fnHandleNormalHTTP` is a callback which is able to override grappler for an incoming HTTP connection.
+If no headers are sent, then grappler takes control of the connection. The arguments provided to this callback are the
+same for `http.Server`'s `request` event, that is the http.ServerRequest and http.ServerResponse objects. It should be
+noted that if you want grappler to automatically handle all incoming HTTP connections but want to specify a callback
+for `fnAcceptClient`, you need to specify `null` or `false` for `fnHandleNormalHTTP`.
+
+`fnAcceptClient` is a callback that is executed the moment a client connects (even before they are deemed an HTTP or plain TCP
+client). The main purpose of this callback is to have the chance to immediately deny a client further access to the grappler server.
+For example, your application may maintain a blacklist or may automatically blacklist/throttle back a certain IP after x connections in y time.
+If this callback returns false, the connection will automatically be dropped, otherwise the connection will be permitted.
+The callback receives one argument, which is the `net.Stream` object representing the connection.
+
+### Event: connection
+
+`function(client) { }`
+
+This event is emitted every time a new client has successfully connected to the system.
+`client` is an instance of `Client`.
+
+### Event: data
+
+`function(data, client) { }`
+
+This event is emitted when a client sends data to the server.
+`data` is a Buffer containing the data and `client` is the `Client` who sent it.
+
+### Event: error
+
+`function(err) { }`
+
+Emitted when an unexpected error occurs.
+
+### listen(port, [host])
+
+Starts the server listening on the specified `port` and `host`. If `host` is omitted, the server will listen on any IP address.
+
+### broadcast(data)
+
+Sends `data` to every connected client.
+
+### shutdown()
+
+Shuts down the server by no longer listening for incoming connections and severing any existing client connections.
+
+
+## Client
+
+There is one other important object that is used in grappler, and that is the `Client` object.
+`Client` represents a user connected to the server and can be used to send that user data.
+
+### Event: drain
+
+`function() { }`
+
+Emitted when the client's write buffer becomes empty.
+
+### Event: close
+
+`function() { }`
+
+Emitted when the client has disconnected.
+
+### state
+
+A bit field containing the current state of the client. See the aforementioned `STATE` object for valid bits.
+
+### remoteAddress
+
+The IP address of the client.
+
+### write(data, [encoding])
+
+Sends `data` using an optional encoding to the client.
+This function returns `true` if the entire data was flushed successfully to the kernel
+buffer. Otherwise, it will return `false` if all or part of the data was queued in user memory.
+`drain` will be emitted when the kernel buffer is free again.
+
+### broadcast(data)
+
+Sends `data` to every connected client except itself.
+
+### disconnect()
+
+Forcefully severs the client's connection to the server.
View
@@ -22,7 +22,7 @@ try {
var LOG = exports.LOG = {
INFO: 1,
WARN: 2,
- ERROR: 4
+ ERROR: 3
};
var STATE = exports.STATE = {
@@ -163,7 +163,7 @@ function Server(options/*, fnHandleNormalHTTP, fnAcceptClient*/) {
// in the time determined by options.detectTimeout
if (!(socket.client.state & STATE.PROTO_HTTP)) {
socket.client.state |= STATE.PROTO_TCP;
- connections[socket.client.id] = new TcpClient(socket.client);
+ connections[socket.client._id] = new TcpClient(socket.client);
}
});
socket.setTimeout(self.options.detectTimeout);
@@ -173,10 +173,10 @@ function Server(options/*, fnHandleNormalHTTP, fnAcceptClient*/) {
var fnClose = function() {
if (socket.isMarked == undefined) {
- if (connections[socket.client.id])
- connections[socket.client.id].disconnect();
+ if (connections[socket.client._id])
+ connections[socket.client._id].disconnect();
socket.isMarked = true;
- logger('Server :: Connection closed: id == ' + socket.client.id, LOG.INFO);
+ logger('Server :: Connection closed: id == ' + socket.client._id, LOG.INFO);
}
};
socket.addListener('close', fnClose);
@@ -185,10 +185,10 @@ function Server(options/*, fnHandleNormalHTTP, fnAcceptClient*/) {
if (!cbAccept(socket)) {
// The incoming connection was denied for one reason or another
socket.destroy();
- logger('Server :: Incoming connection denied: id == ' + socket.client.id, LOG.INFO);
+ logger('Server :: Incoming connection denied: id == ' + socket.client._id, LOG.INFO);
} else {
socket.client.state |= STATE.ACCEPTED;
- logger('Server :: Incoming connection accepted: id == ' + socket.client.id, LOG.INFO);
+ logger('Server :: Incoming connection accepted: id == ' + socket.client._id, LOG.INFO);
}
});
server.addListener('request', function(req, res) {
@@ -206,7 +206,7 @@ function Server(options/*, fnHandleNormalHTTP, fnAcceptClient*/) {
if (!res._header)
new HttpClient(req, res);
else
- logger('Server :: HTTP connection handled by callback. id == ' + req.connection.client.id, LOG.INFO);
+ logger('Server :: HTTP connection handled by callback. id == ' + req.connection.client._id, LOG.INFO);
}
});
server.addListener('upgrade', function(req, socket, head) {
@@ -219,19 +219,18 @@ function Server(options/*, fnHandleNormalHTTP, fnAcceptClient*/) {
if (!cbHandleHTTP(req, socket))
new HttpClient(req, socket, head);
else
- logger('Server :: HTTP Upgrade request handled by callback. id == ' + req.connection.client.id, LOG.INFO);
+ logger('Server :: HTTP Upgrade request handled by callback. id == ' + req.connection.client._id, LOG.INFO);
}
});
server.addListener('error', function(err) {
self.emit('error', err);
});
- this.close = function() {
+ this.shutdown = function() {
server.close();
for (var i=0,keys=Object.keys(connections),len=keys.length; i<len; ++i)
connections[keys[i]].disconnect();
- self.emit('close');
};
}
sys.inherits(Server, EventEmitter);
@@ -248,16 +247,15 @@ Server.prototype.broadcast = function(data, except) {
};
function Client(srv, ip) {
- this.id = Math.floor(Math.random()*1e5).toString() + (new Date()).getTime().toString();
+ this._id = Math.floor(Math.random()*1e5).toString() + (new Date()).getTime().toString();
this.state = STATE.TEMP;
this.remoteAddress = ip;
var server = srv;
- var self = this;
this.__defineGetter__('server', function() { return server; });
this.broadcast = function(data) {
- server.broadcast(data, id);
+ server.broadcast(data, this._id);
};
}
exports.Client = Client;
Oops, something went wrong.

0 comments on commit d0283c3

Please sign in to comment.