https://joochang.tistory.com/37 참고
RFC7232 2.3 ETag
https://datatracker.ietf.org/doc/html/rfc7232#section-2.1
app.defaultConfiguration = function defaultConfiguration() {
var env = process.env.NODE_ENV || "development";
// default settings
this.enable("x-powered-by");
this.set("etag", "weak");
/**
*
* 현재까지 settings변수에 저장되어있는 프로퍼티
*
* x-powered-by : true
* etag : "weak"
* etag fn : ETag생성 함수
*/
this.set("env", env);
this.set("query parser", "extended");
this.set("subdomain offset", 2);
this.set("trust proxy", false);
// trust proxy inherit back-compat
Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
configurable: true,
value: true,
});
debug("booting in %s mode", env);
this.on("mount", function onmount(parent) {
// inherit trust proxy
if (
this.settings[trustProxyDefaultSymbol] === true &&
typeof parent.settings["trust proxy fn"] === "function"
) {
delete this.settings["trust proxy"];
delete this.settings["trust proxy fn"];
}
// inherit protos
setPrototypeOf(this.request, parent.request);
setPrototypeOf(this.response, parent.response);
setPrototypeOf(this.engines, parent.engines);
setPrototypeOf(this.settings, parent.settings);
});
// setup locals
this.locals = Object.create(null);
// top-most app is mounted at /
this.mountpath = "/";
// default locals
this.locals.settings = this.settings;
// default configuration
this.set("view", View);
this.set("views", resolve("views"));
this.set("jsonp callback name", "callback");
if (env === "production") {
this.enable("view cache");
}
Object.defineProperty(this, "router", {
get: function () {
throw new Error(
"'app.router' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app."
);
},
});
};
/**
* 1. 첫번째 인자만 들어오는 경우 : 첫번째 인자를 key로하여 value를 리턴한다.
* 2. 두번째 인자도 들어오는 경우 : 첫번째 인자를 key 두번째 인자를 value로 하여 settings개체에 저장한다.
* 3. 첫번째 인자의 값이 etag, query parser, trust proxy인경우 : etag fn, query parser fn, trust proxy fn을 key 컴파일한값을 value로 하여 값을 저장한다.
* 4. 첫번째 인자의 값이 trust proxy인경우 : @@symbol:trust_proxy_default를 key, false를 value로 하여 값을 저장한다.
*/
app.set = function set(setting, val) {
/**
* setting : settings의 key 값으로 사용
* val : settings 변수의 value 값으로 사용
*/
if (arguments.length === 1) {
// app.get(setting)
return this.settings[setting];
/**
* set함수에 입력된 인자의 개수가 하나인경우 입력된 인자를 키로하여 저장된 값을 리턴한다.
*/
}
debug('set "%s" to %o', setting, val);
// set value
this.settings[setting] = val;
/**
* settings에 값을 설정한다.
*/
// trigger matched settings
switch (setting) {
case "etag":
this.set("etag fn", compileETag(val));
break;
case "query parser":
this.set("query parser fn", compileQueryParser(val));
break;
case "trust proxy":
this.set("trust proxy fn", compileTrust(val));
// trust proxy inherit back-compat
Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
configurable: true,
value: false,
});
break;
}
/**
* setting값이 etag, query parser, trust proxy인경우
* val값을 기반으로 etag fn, query parser fn, truest proxy fn값을 설정한다.
* 이때 setting값이 trust proxy인경우 프로퍼티를 하나 더 추가한다.
* "@@symbol:trust_proxy_default" : true
*
* 이때 @@symbol:trust_proxy_default의 경우 Object.defineProperty로 추가하였기 때문에 enumerable이 기본값인 false로 설정되어있다.
*/
return this;
};
/**
* val의 타입이 함수인경우 해당 함수를 그대로 리턴하고,
* true 또는 "weak"인경우 wetag 함수를 리턴,
* "strong"인 경우 etag 함수를 리턴,
* false인경우 val자체 리턴,
* 그 외의 값이 들어오는 경우 타입에러를 발생시킨다.
*/
exports.compileETag = function (val) {
var fn;
if (typeof val === "function") {
return val;
}
switch (val) {
case true:
fn = exports.wetag;
break;
case false:
break;
case "strong":
fn = exports.etag;
break;
case "weak":
fn = exports.wetag;
break;
default:
throw new TypeError("unknown value for etag function: " + val);
}
return fn;
};
exports.etag = createETagGenerator({ weak: false });
exports.wetag = createETagGenerator({ weak: true });
/**
* body가 Buffer인지 확인하여 Buffer가 아닌경우 Buffer로 만들어 etag함수를 호출한다.
*/
function createETagGenerator(options) {
return function generateETag(body, encoding) {
var buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
return etag(buf, options);
};
}
/*!
* etag
* Copyright(c) 2014-2016 Douglas Christopher Wilson
* MIT Licensed
*/
"use strict";
/**
* Module exports.
* @public
*/
module.exports = etag;
/**
* Module dependencies.
* @private
*/
var crypto = require("crypto");
var Stats = require("fs").Stats;
/**
* Module variables.
* @private
*/
var toString = Object.prototype.toString;
/**
* Generate an entity tag.
*
* @param {Buffer|string} entity
* @return {string}
* @private
*/
function entitytag(entity) {
if (entity.length === 0) {
// fast-path empty
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
}
// compute hash of entity
var hash = crypto
.createHash("sha1")
.update(entity, "utf8")
.digest("base64")
.substring(0, 27);
/**
* createHash : 매개변수로 전달한 알고리즘에 해당하는 Hash 클래스 반환
*
* update : 해싱할 데이터를 매개변수로 전달
*
* digest : 매개벼수로 전달한 인코딩형식으로 해싱된 값을 가져온다.
*/
// compute length of entity
var len =
typeof entity === "string"
? Buffer.byteLength(entity, "utf8")
: entity.length;
/**
* entity의 길이를 가져온다.
*/
return '"' + len.toString(16) + "-" + hash + '"';
}
/**
* Create a simple ETag.
*
* @param {string|Buffer|Stats} entity
* @param {object} [options]
* @param {boolean} [options.weak]
* @return {String}
* @public
*/
/**
* entity : Stats or string or Buffer만 가능
*
* options.weak의 타입이 boolean인경우 해당값을 기준으로 strong weak을 결정한다.
*
* options개체가 없거나 options.weak의 데이터 타입이 boolean이 아닌경우 entity가 Stats개체이면 weak 아니면 strong이다.
*
*/
function etag(entity, options) {
if (entity == null) {
throw new TypeError("argument entity is required");
}
// support fs.Stats object
var isStats = isstats(entity);
var weak =
options && typeof options.weak === "boolean" ? options.weak : isStats;
/**
* options값이 존재하고 options.weak 프로퍼티의 타입이 boolean인경우 options.weak에 설정되어 있는 값을 weak값으로 설정한다.
* 그 이외의 경우 entity 데이터 타입의 Stats개체 여부에 따라 결정된다.
*/
// validate argument
if (!isStats && typeof entity !== "string" && !Buffer.isBuffer(entity)) {
throw new TypeError("argument entity must be string, Buffer, or fs.Stats");
}
/**
* entity값이 Stats개체가 아닌경우 entity의 타입은 string 또는 Buffer가 되어야한다.
*
* Buffer : Node.js에서 제공하는 Binary의 데이터를 담을 수 있는 Object
*/
// generate entity tag
var tag = isStats ? stattag(entity) : entitytag(entity);
return weak ? "W/" + tag : tag;
}
/**
* Determine if object is a Stats object.
*
* @param {object} obj
* @return {boolean}
* @api private
*/
/**
* isstats
* 입력받은 인자의 타입이 Stats인지 확인하는 함수
* 인자가 Stats이거나 Stats의 조건을 가지고 있는 경우 true를 리턴한다.
*/
/**
* typeof와 instanceof의 차이점
*
* typeof : 피연산자의 데이터 타입을 반환하는 연산자
*
* instanceof : 개체가 특정 클래스의 인스턴스인지 여부를 나타내는 boolean값으로 반환하는 비교 연산자
*/
/**
*
* fs.statSync : 파일 경로에 대한 정보(Stats개체)를 리턴하는 함수
*
* Stats개체 예시
*
* Stats {
* dev: 16777220,
* mode: 33188,
* nlink: 1,
* uid: 502,
* gid: 20,
* rdev: 0,
* blksize: 4096,
* ino: 17169940,
* size: 255,
* blocks: 8,
* atimeMs: 1624407156504.2395,
* mtimeMs: 1624407155094.901,
* ctimeMs: 1624407155094.901,
* birthtimeMs: 1624320820424.4988,
* atime: 2021-06-23T00:12:36.504Z,
* mtime: 2021-06-23T00:12:35.095Z,
* ctime: 2021-06-23T00:12:35.095Z,
* birthtime: 2021-06-22T00:13:40.424Z
*}
*/
function isstats(obj) {
// genuine fs.Stats
if (typeof Stats === "function" && obj instanceof Stats) {
return true;
}
// quack quack
return (
obj &&
typeof obj === "object" &&
"ctime" in obj &&
toString.call(obj.ctime) === "[object Date]" &&
"mtime" in obj &&
toString.call(obj.mtime) === "[object Date]" &&
"ino" in obj &&
typeof obj.ino === "number" &&
"size" in obj &&
typeof obj.size === "number"
);
}
/**
* Generate a tag for a stat.
*
* @param {object} stat
* @return {string}
* @private
*/
/**
* entity가 Stats개체인경우 etag를 생성하는 함수
*/
function stattag(stat) {
var mtime = stat.mtime.getTime().toString(16);
var size = stat.size.toString(16);
return '"' + size + "-" + mtime + '"';
}