Skip to content

Commit

Permalink
GH-30 Support for overriding header manipulation
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Cavage committed Jul 6, 2011
1 parent b479b0c commit 1af5a2f
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 68 deletions.
75 changes: 49 additions & 26 deletions docs/restify.3
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,22 @@ To create an instance of the restify HTTP server, simply call the \fBcreateServe
clockSkew: 300, // Allow up to N seconds of skew in the Date header
accept: [
\'application/json\', // Allow these Accept types
\'application/foo\'
\'application/foo\'
],
contentHandlers: { // A hash of custom content\-type handlers
\'application/foo\': function(body) {
return JSON\.parse(body);
}
return JSON\.parse(body);
}
},
contentWriters: { // A hash of custom serializers
\'application/foo\': function(obj) {
return JSON\.stringify(obj);
}
return JSON\.stringify(obj);
}
},
headers: { // A hash of customer headers
\'X\-Foo\': function(res) {
return \'bar\';
}
},
key: <PEM>, // Together with `cert`, create an SSL server
cert: <PEM> // Together with `key`, create an SSL server
Expand All @@ -74,33 +79,51 @@ To create an instance of the restify HTTP server, simply call the \fBcreateServe
.P
Defaults/Details for the paramters above:
.
.TP
version
By default, there is no version handling invoked\. However, if you do specify this header, then \fBx\-api\-version: 1\.2\.3\fR will be returned in all HTTP responses\. Additionally, clients MUST send an \fBx\-api\-version\fR header that satisfies that version\. See restify\-versions(7) for more details, as this is a large feature set\.
.IP "\(bu" 4
version: By default, there is no version handling invoked\. However, if you do specify this header, then \fBx\-api\-version: 1\.2\.3\fR will be returned in all HTTP responses\. Additionally, clients MUST send an \fBx\-api\-version\fR header that satisfies that version\. See restify\-versions(7) for more details, as this is a large feature set\.
.
.TP
serverName
Simple string that is not interpreted and returned in the \'Server:\' HTTP header\. Defaults to \fBnode\.js\fR\.
.IP "\(bu" 4
serverName: Simple string that is not interpreted and returned in the \'Server:\' HTTP header\. Defaults to \fBnode\.js\fR\.
.
.TP
maxRequestSize
Caps the amount of data a client can send to your HTTP server, in bytes\. Defaults to 8192 (8k)\.
.IP "\(bu" 4
maxRequestSize: Caps the amount of data a client can send to your HTTP server, in bytes\. Defaults to 8192 (8k)\.
.
.TP
clockSkew
If the client sends a \'Date\' header, it is checked to be within \fBclockSkew\fR seconds, in either direction\. Defaults to 300s (5m)\.
.IP "\(bu" 4
clockSkew: If the client sends a \'Date\' header, it is checked to be within \fBclockSkew\fR seconds, in either direction\. Defaults to 300s (5m)\.
.
.TP
accept
An array of Acceptable content types this server can process\. Defaults to \fBapplication/json\fR\. Not really useful as of yet\.
.IP "\(bu" 4
accept: An array of Acceptable content types this server can process\. Defaults to \fBapplication/json\fR\. Not really useful as of yet\.
.
.TP
contentHandlers
By default, restify supports (and doesn\'t let you override) clients sending content in \fBapplication/json\fR, \fBapplication/x\-www\-form\-urlencoded\fR and \fBmultipart/form\-data\fR\. If you want your clients to be able to send content in a custom format, you can specify it in this option; keys are (lowercase) MIME type, value is a function that takes a (UTF\-8 string) body, and returns an object of k/v pairs that get merged into \fBrequest\.params\fR\. Note that this is only for parsing, not for using \fBres\.send\fR\.
.IP "\(bu" 4
contentHandlers: By default, restify supports (and doesn\'t let you override) clients sending content in \fBapplication/json\fR, \fBapplication/x\-www\-form\-urlencoded\fR and \fBmultipart/form\-data\fR\. If you want your clients to be able to send content in a custom format, you can specify it in this option; keys are (lowercase) MIME type, value is a function that takes a (UTF\-8 string) body, and returns an object of k/v pairs that get merged into \fBrequest\.params\fR\. Note that this is only for parsing, not for using \fBres\.send\fR\.
.
.IP "\(bu" 4
contentWriters: By default, restify supports (and doesn\'t let you override) servers sending content in \fBapplication/json\fR, \fBapplication/x\-www\-form\-urlencoded\fR\. If you want to use \fBres\.send\fR with custom content types, you will need to add a custom writer to this object\. \fIYou must additionally set the\fR \fBaccept\fR \fIarray to allow it!\fR\. Using this dictionary, you can then use the \fBres\.send\fR naturally with content\-types not built\-in to restify\.
.
.IP "\(bu" 4
.
.IP "\(bu" 4
X\-Api\-Version (if versioned)
.
.TP
contentWriters
By default, restify supports (and doesn\'t let you override) servers sending content in \fBapplication/json\fR, \fBapplication/x\-www\-form\-urlencoded\fR\. If you want to use \fBres\.send\fR with custom content types, you will need to add a custom writer to this object\. \fIYou must additionally set the\fR \fBaccept\fR \fIarray to allow it!\fR\. Using this dictionary, you can then use the \fBres\.send\fR naturally with content\-types not built\-in to restify\.
.IP "\(bu" 4
X\-Request\-Id
.
.IP "\(bu" 4
X\-Response\-Time
.
.IP "\(bu" 4
Content\-(Length|Type|MD5)
.
.IP "\(bu" 4
Access\-Control\-Allow\-(Origin|Methods|Headers)
.
.IP "\(bu" 4
Access\-Control\-Expose\-Headers
.
.IP "" 0

.
.IP "" 0
.
.SH "ROUTING"
Routing is similar to, but different than, express\. Defining a route still looks like a \fBmethod\fR name tacked onto the server object, but you can (1) pass in versions (so you can define a versioned route), (2) you can pass in whatever combination of functions you see fit, with the exception that if \fIboth the first and last\fR handlers are an array of functions, they are treated as \fBpre\fR and \fBpost\fR interceptors\. This has the following semnatics:
Expand Down
30 changes: 25 additions & 5 deletions docs/restify.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,22 @@ the following syntax:
clockSkew: 300, // Allow up to N seconds of skew in the Date header
accept: [
'application/json', // Allow these Accept types
'application/foo'
'application/foo'
],
contentHandlers: { // A hash of custom content-type handlers
'application/foo': function(body) {
return JSON.parse(body);
}
return JSON.parse(body);
}
},
contentWriters: { // A hash of custom serializers
'application/foo': function(obj) {
return JSON.stringify(obj);
}
return JSON.stringify(obj);
}
},
headers: { // A hash of customer headers
'X-Foo': function(res) {
return 'bar';
}
},
key: <PEM>, // Together with `cert`, create an SSL server
cert: <PEM> // Together with `key`, create an SSL server
Expand Down Expand Up @@ -94,6 +99,21 @@ Defaults/Details for the paramters above:
custom writer to this object. *You must additionally set the* `accept`
*array to allow it!*. Using this dictionary, you can then use the
`res.send` naturally with content-types not built-in to restify.
* headers:
An object of global headers that are sent back on all requests. Restify
automatically sets the list below. If you don't set those particular keys,
restify fills in default functions; if you do set them, you can fully override
restify's defaults. Note that like contentWriters, this is an object with a
string key as the header name, and the value is a function of the form
f(response) which must return a string. Defaults:
- X-Api-Version (if versioned)
- X-Request-Id
- X-Response-Time
- Content-(Length|Type|MD5)
- Access-Control-Allow-(Origin|Methods|Headers)
- Access-Control-Expose-Headers



## ROUTING

Expand Down
48 changes: 16 additions & 32 deletions lib/http_extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ http.ServerResponse.prototype.send = function(options) {
} else {
throw new TypeError('invalid argument type: ' + typeof(options));
}
this.options = _opts;

this._code = _opts.code;
this._time = now.getTime() - this.startTime;
Expand All @@ -72,54 +73,37 @@ http.ServerResponse.prototype.send = function(options) {
headers = _opts.headers;
}

// These headers allow strict clients (i.e. Google Chrome) to know that
// this server does allow itself to be invoked from pages that aren't
// part of the same origin policy.
headers['Access-Control-Allow-Origin'] = '*';
if (this._allowedMethods && this._allowedMethods.length)
headers['Access-Control-Allow-Methods'] = this._allowedMethods.join(', ');

if (!_opts.noClose)
headers.Connection = 'close';

if (this._version)
headers['X-Api-Version'] = this._version;

headers.Date = utils.newHttpDate(now);
headers.Server = this._config.serverName;
headers['X-Request-Id'] = this.requestId;
headers['X-Response-Time'] = this._time;

if (_opts.body && _opts.code !== HttpCodes.NoContent) {
headers['Content-Type'] = this._accept;
if (this._accept === 'application/x-www-form-urlencoded') {
data = querystring.stringify(_opts.body);
data = data + '\n';
} else if (this._accept === 'application/json') {
data = JSON.stringify(_opts.body);
data = data + '\n';
} else if (this._config.contentWriters[this._accept]) {
data = this._config.contentWriters[this._accept](_opts.body);
} else {
// Just pass it through "broken", since we should never
// actually be in this case.
data = _opts.body;
data = _opts.body + '';
}
}

this._data = data;
this._bytes = 0;
if (data && _opts.code !== HttpCodes.NoContent) {
data = data + '\n';
this._bytes = Buffer.byteLength(data, 'utf8');

if (!_opts.noContentMD5) {
var hash = crypto.createHash('md5');
hash.update(data);
headers['Content-MD5'] = hash.digest('base64');

if (!_opts.noClose)
headers.Connection = 'close';

headers.Date = utils.newHttpDate(now);
headers.Server = this._config.serverName;
for (var k in this._config.headers) {
if (this._config.headers.hasOwnProperty(k)) {
var val = this._config.headers[k](this);
if (val !== undefined && val !== null)
headers[k] = val;
}
}

if (!_opts.noEnd && this._method !== 'HEAD')
headers['Content-Length'] = this._bytes;

log.trace('response.send: code=%d, headers=%o, body=%s',
_opts.code, headers, data);

Expand Down

0 comments on commit 1af5a2f

Please sign in to comment.