Name: js-handler/node_modules/restify/lib/plugins/conditional_request.js 
1:
// Copyright 2012 Mark Cavage, Inc.  All rights reserved.
2:
 
3:
var errors = require('../errors');
4:
 
5:
 
6:
 
7:
///--- Globals
8:
 
9:
var BadRequestError = errors.BadRequestError;
10:
var PreconditionFailedError = errors.PreconditionFailedError;
11:
 
12:
var IF_MATCH_FAIL = 'if-match \'%s\' didn\'t match etag \'%s\'';
13:
var IF_NO_MATCH_FAIL = 'if-none-match \'%s\' matched etag \'%s\'';
14:
var IF_MOD_FAIL = 'object was modified at \'%s\'; if-modified-since \'%s\'';
15:
var IF_UNMOD_FAIL = 'object was modified at \'%s\'; if-unmodified-since \'%s\'';
16:
 
17:
 
18:
 
19:
///--- API
20:
// Reference RFC2616 section 14 for an explanation of what this all does.
21:
 
22:
function checkIfMatch(req, res, next) {
23:
        var clientETags;
24:
        var cur;
25:
        var etag = res.etag || res.getHeader('etag') || '';
26:
        var ifMatch;
27:
        var matched = false;
28:
 
29:
        if ((ifMatch = req.headers['if-match'])) {
30:
                /* JSSTYLED */
31:
                clientETags = ifMatch.split(/\s*,\s*/);
32:
 
33:
                for (var i = 0; i < clientETags.length; i++) {
34:
                        cur = clientETags[i];
35:
                        // only strong comparison
36:
                        /* JSSTYLED */
37:
                        cur = cur.replace(/^W\//, '');
38:
                        /* JSSTYLED */
39:
                        cur = cur.replace(/^"(\w*)"$/, '$1');
40:
 
41:
                        if (cur === '*' || cur === etag) {
42:
                                matched = true;
43:
                                break;
44:
                        }
45:
                }
46:
 
47:
                if (!matched) {
48:
                        var err = new PreconditionFailedError(IF_MATCH_FAIL,
49:
                                                              ifMatch,
50:
                                                              etag);
51:
                        return (next(err));
52:
                }
53:
        }
54:
 
55:
        return (next());
56:
}
57:
 
58:
 
59:
function checkIfNoneMatch(req, res, next) {
60:
        var clientETags;
61:
        var cur;
62:
        var etag = res.etag || res.getHeader('etag') || '';
63:
        var ifNoneMatch;
64:
        var matched = false;
65:
 
66:
        if ((ifNoneMatch = req.headers['if-none-match'])) {
67:
                /* JSSTYLED */
68:
                clientETags = ifNoneMatch.split(/\s*,\s*/);
69:
 
70:
                for (var i = 0; i < clientETags.length; i++) {
71:
                        cur = clientETags[i];
72:
                        // ignore weak validation
73:
                        /* JSSTYLED */
74:
                        cur = cur.replace(/^W\//, '');
75:
                        /* JSSTYLED */
76:
                        cur = cur.replace(/^"(\w*)"$/, '$1');
77:
 
78:
                        if (cur === '*' || cur === etag) {
79:
                                matched = true;
80:
                                break;
81:
                        }
82:
                }
83:
 
84:
                if (!matched)
85:
                        return (next());
86:
 
87:
                if (req.method !== 'GET' && req.method !== 'HEAD') {
88:
                        var err = new PreconditionFailedError(IF_NO_MATCH_FAIL,
89:
                                                              ifNoneMatch,
90:
                                                              etag);
91:
                        return (next(err));
92:
                }
93:
 
94:
                res.send(304);
95:
                return (next(false));
96:
        }
97:
 
98:
        return (next());
99:
}
100:
 
101:
 
102:
function checkIfModified(req, res, next) {
103:
        var code;
104:
        var err;
105:
        var ctime = req.header('if-modified-since');
106:
        var mtime = res.mtime || res.header('Last-Modified') || '';
107:
 
108:
        if (!mtime || !ctime) {
109:
                next();
110:
                return;
111:
        }
112:
 
113:
        try {
114:
                //
115:
                // TODO handle Range header modifications
116:
                //
117:
                // Note: this is not technically correct as per 2616 -
118:
                // 2616 only specifies semantics for GET requests, not
119:
                // any other method - but using if-modified-since with a
120:
                // PUT or DELETE seems like returning 412 is sane
121:
                //
122:
                if (Date.parse(mtime) <= Date.parse(ctime)) {
123:
                        switch (req.method) {
124:
                        case 'GET':
125:
                        case 'HEAD':
126:
                                code = 304;
127:
                                break;
128:
 
129:
                        default:
130:
                                err = new PreconditionFailedError(IF_MOD_FAIL,
131:
                                                                  mtime,
132:
                                                                  ctime);
133:
                                break;
134:
                        }
135:
                }
136:
        } catch (e) {
137:
                next(new BadRequestError(e.message));
138:
                return;
139:
        }
140:
 
141:
        if (code !== undefined) {
142:
                res.send(code);
143:
                next(false);
144:
                return;
145:
        }
146:
 
147:
        next(err);
148:
}
149:
 
150:
 
151:
function checkIfUnmodified(req, res, next) {
152:
        var err;
153:
        var ctime = req.headers['if-unmodified-since'];
154:
        var mtime = res.mtime || res.header('Last-Modified') || '';
155:
 
156:
        if (!mtime || !ctime) {
157:
                next();
158:
                return;
159:
        }
160:
 
161:
        try {
162:
                if (Date.parse(mtime) > Date.parse(ctime)) {
163:
                        err = new PreconditionFailedError(IF_UNMOD_FAIL,
164:
                                                          mtime,
165:
                                                          ctime);
166:
                }
167:
        } catch (e) {
168:
                next(new BadRequestError(e.message));
169:
                return;
170:
        }
171:
 
172:
        next(err);
173:
}
174:
 
175:
 
176:
 
177:
///--- Exports
178:
 
179:
/**
180:
 * Returns a set of plugins that will compare an already set ETag header with
181:
 * the client's If-Match and If-None-Match header, and an already set
182:
 * Last-Modified header with the client's If-Modified-Since and
183:
 * If-Unmodified-Since header.
184:
 */
185:
function conditionalRequest() {
186:
        var chain = [
187:
                checkIfMatch,
188:
                checkIfNoneMatch,
189:
                checkIfModified,
190:
                checkIfUnmodified
191:
        ];
192:
        return (chain);
193:
}
194:
 
195:
module.exports = conditionalRequest;