1. response 要帶 expire header + cache control, 同時出現時 cache control 優先權高, 在 expire 之前瀏覽器不會來問
2. 每個 request 要帶上 Etag + Last-Modified header, 第二次 request 的時候 browser 會把這兩個資訊帶過來問, request with 'if-modified-since' header, 這也要處理
var ext = path.extname(realPath);
ext = ext ? ext.slice(1) : 'unknown';
if (ext.match(config.Expires.fileMatch)) {
var expires = new Date();
expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);
response.setHeader("Expires", expires.toUTCString()); // HTTP 1.0
response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge); // HTTP 1.1
}
3. 為所有 response 都加上 Last-Modified header
fs.stat(realPath, function (err, stat) {
var lastModified = stat.mtime.toUTCString();
response.setHeader("Last-Modified", lastModified);
});
4. 檢查 If-Modified-Since header
if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {
response.writeHead(304, "Not Modified");
response.end();
}
5. 加上 gzip
var raw = fs.createReadStream(realPath);
var acceptEncoding = request.headers['accept-encoding'] || "";
var matched = ext.match(config.Compress.match);
if (matched && acceptEncoding.match(/\bgzip\b/)) {
response.writeHead(200, "Ok", {'Content-Encoding': 'gzip'});
raw.pipe(zlib.createGzip()).pipe(response);
} else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
response.writeHead(200, "Ok", {'Content-Encoding': 'deflate'});
raw.pipe(zlib.createDeflate()).pipe(response);
} else {
response.writeHead(200, "Ok");
raw.pipe(response);
}
6. range:
bytes=0-99,从0到99之间的数据字节。
bytes=-100,文件的最后100个字节。
bytes=100-,第100个字节开始之后的所有字节。
bytes=0-99,200-299,从0到99之间的数据字节和200到299之间的数据字节。
exports.parseRange = function (str, size) {
if (str.indexOf(",") != -1) {
return;
}
var range = str.split("-"),
start = parseInt(range0], 10),
end = parseInt(range[1], 10);
// Case: -100
if (isNaN(start)) {
start = size - end;
end = size - 1;
// Case: 100-
} else if (isNaN(end)) {
end = size - 1;
}
// Invalid
if (isNaN(start) || isNaN(end) || start > end || end > size) {
return;
}
return {start: start, end: end};
};
var compressHandle = function (raw, statusCode, reasonPhrase) {
var stream = raw;
var acceptEncoding = request.headers['accept-encoding'] || "";
var matched = ext.match(config.Compress.match);
if (matched && acceptEncoding.match(/\bgzip\b/)) {
response.setHeader("Content-Encoding", "gzip");
stream = raw.pipe(zlib.createGzip());
} else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
response.setHeader("Content-Encoding", "deflate");
stream = raw.pipe(zlib.createDeflate());
}
response.writeHead(statusCode, reasonPhrase);
stream.pipe(response);
};
if (request.headers["range"]) {
var range = utils.parseRange(request.headers["range"], stats.size);
if (range) {
response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stats.size);
response.setHeader("Content-Length", (range.end - range.start + 1));
var raw = fs.createReadStream(realPath, {"start": range.start, "end": range.end});
compressHandle(raw, 206, "Partial Content");
} else {
response.removeHeader("Content-Length");
response.writeHead(416, "Request Range Not Satisfiable");
response.end();
}
} else {
var raw = fs.createReadStream(realPath);
compressHandle(raw, 200, "Ok");
}
test with curl:
curl --header "Range:0-20" -i http://localhost:8000/index.html
沒有留言:
張貼留言