Name: js-handler/node_modules/restify/lib/upgrade.js 
1:
// Copyright (c) 2013, Joyent, Inc. All rights reserved.
2:
 
3:
var EventEmitter = require('events').EventEmitter;
4:
var util = require('util');
5:
var assert = require('assert-plus');
6:
 
7:
function InvalidUpgradeStateError(msg) {
8:
        if (Error.captureStackTrace)
9:
                Error.captureStackTrace(this, InvalidUpgradeStateError);
10:
 
11:
        this.message = msg;
12:
        this.name = 'InvalidUpgradeStateError';
13:
}
14:
util.inherits(InvalidUpgradeStateError, Error);
15:
 
16:
//
17:
// The Node HTTP Server will, if we handle the 'upgrade' event, swallow any
18:
// Request with the 'Connection: upgrade' header set.  While doing this it
19:
// detaches from the 'data' events on the Socket and passes the socket to
20:
// us, so that we may take over handling for the connection.
21:
//
22:
// Unfortunately, the API does not presently provide a http.ServerResponse
23:
// for us to use in the event that we do not wish to upgrade the connection.
24:
// This factory method provides a skeletal implementation of a
25:
// restify-compatible response that is sufficient to allow the existing
26:
// request handling path to work, while allowing us to perform _at most_ one
27:
// of either:
28:
//
29:
//   - Return a basic HTTP Response with a provided Status Code and
30:
//     close the socket.
31:
//   - Upgrade the connection and stop further processing.
32:
//
33:
// To determine if an upgrade is requested, a route handler would check for
34:
// the 'claimUpgrade' method on the Response.  The object this method
35:
// returns will have the 'socket' and 'head' Buffer emitted with the
36:
// 'upgrade' event by the http.Server.  If the upgrade is not possible, such
37:
// as when the HTTP head (or a full request) has already been sent by some
38:
// other handler, this method will throw.
39:
//
40:
function createServerUpgradeResponse(req, socket, head) {
41:
        return (new ServerUpgradeResponse(socket, head));
42:
}
43:
 
44:
function ServerUpgradeResponse(socket, head) {
45:
        assert.object(socket, 'socket');
46:
        assert.buffer(head, 'head');
47:
 
48:
        EventEmitter.call(this);
49:
 
50:
        this.sendDate = true;
51:
        this.statusCode = 400;
52:
 
53:
        this._upgrade = {
54:
                socket: socket,
55:
                head: head
56:
        };
57:
 
58:
        this._headWritten = false;
59:
        this._upgradeClaimed = false;
60:
}
61:
util.inherits(ServerUpgradeResponse, EventEmitter);
62:
 
63:
function notImplemented(method) {
64:
        if (!method.throws) {
65:
                return function () {
66:
                        return (method.returns);
67:
                };
68:
        } else {
69:
                return function () {
70:
                        throw (new Error('Method ' + method.name + ' is not ' +
71:
                            'implemented!'));
72:
                };
73:
        }
74:
}
75:
 
76:
var NOT_IMPLEMENTED = [
77:
        { name: 'writeContinue', throws: true },
78:
        { name: 'setHeader', throws: false, returns: null },
79:
        { name: 'getHeader', throws: false, returns: null },
80:
        { name: 'getHeaders', throws: false, returns: {} },
81:
        { name: 'removeHeader', throws: false, returns: null },
82:
        { name: 'addTrailer', throws: false, returns: null },
83:
        { name: 'cache', throws: false, returns: 'public' },
84:
        { name: 'format', throws: true },
85:
        { name: 'set', throws: false, returns: null },
86:
        { name: 'get', throws: false, returns: null },
87:
        { name: 'headers', throws: false, returns: {} },
88:
        { name: 'header', throws: false, returns: null },
89:
        { name: 'json', throws: false, returns: null },
90:
        { name: 'link', throws: false, returns: null }
91:
];
92:
NOT_IMPLEMENTED.forEach(function (method) {
93:
        ServerUpgradeResponse.prototype[method.name] = notImplemented(method);
94:
});
95:
 
96:
ServerUpgradeResponse.prototype._writeHeadImpl = function _writeHeadImpl(
97:
statusCode, reason) {
98:
        if (this._headWritten)
99:
                return;
100:
        this._headWritten = true;
101:
 
102:
        if (this._upgradeClaimed)
103:
                throw new InvalidUpgradeStateError('Upgrade already claimed!');
104:
 
105:
        var head = [
106:
                'HTTP/1.1 ' + statusCode + ' ' + reason,
107:
                'Connection: close'
108:
        ];
109:
        if (this.sendDate)
110:
                head.push('Date: ' + new Date().toUTCString());
111:
 
112:
        this._upgrade.socket.write(head.join('\r\n') + '\r\n');
113:
};
114:
 
115:
ServerUpgradeResponse.prototype.status = function status(code) {
116:
        assert.number(code, 'code');
117:
        this.statusCode = code;
118:
        return (code);
119:
};
120:
 
121:
ServerUpgradeResponse.prototype.send = function send(code, body) {
122:
        if (typeof (code) === 'number')
123:
                this.statusCode = code;
124:
        else
125:
                body = code;
126:
 
127:
        if (typeof (body) === 'object') {
128:
                if (typeof (body.statusCode) === 'number')
129:
                        this.statusCode = body.statusCode;
130:
                if (typeof (body.message) === 'string')
131:
                        this.statusReason = body.message;
132:
        }
133:
 
134:
        return (this.end());
135:
};
136:
 
137:
ServerUpgradeResponse.prototype.end = function end() {
138:
        this._writeHeadImpl(this.statusCode, 'Connection Not Upgraded');
139:
        this._upgrade.socket.end('\r\n');
140:
        return (true);
141:
};
142:
 
143:
ServerUpgradeResponse.prototype.write = function write() {
144:
        this._writeHeadImpl(this.statusCode, 'Connection Not Upgraded');
145:
        return (true);
146:
};
147:
 
148:
ServerUpgradeResponse.prototype.writeHead = function writeHead(statusCode,
149:
reason) {
150:
        assert.number(statusCode, 'statusCode');
151:
        assert.optionalString(reason, 'reason');
152:
 
153:
        this.statusCode = statusCode;
154:
        if (!reason)
155:
                reason = 'Connection Not Upgraded';
156:
 
157:
        if (this._headWritten)
158:
                throw new Error('Head already written!');
159:
 
160:
        return (this._writeHeadImpl(statusCode, reason));
161:
};
162:
 
163:
ServerUpgradeResponse.prototype.claimUpgrade = function claimUpgrade() {
164:
        if (this._upgradeClaimed)
165:
                throw new InvalidUpgradeStateError('Upgrade already claimed!');
166:
 
167:
        if (this._headWritten)
168:
                throw new InvalidUpgradeStateError('Upgrade already aborted!');
169:
 
170:
        this._upgradeClaimed = true;
171:
 
172:
        return (this._upgrade);
173:
};
174:
 
175:
module.exports = {
176:
        createResponse: createServerUpgradeResponse,
177:
 
178:
        InvalidUpgradeStateError: InvalidUpgradeStateError
179:
};
180:
 
181:
// vim: set et ts=8 sts=8 sw=8: