@supercharge/request-ip
Version:
Retrieve a request’s IP address in Node.js
305 lines (304 loc) • 8.8 kB
JavaScript
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Request = void 0;
const net_1 = __importDefault(require("net"));
const interacts_with_headers_1 = require("./interacts-with-headers");
class Request extends interacts_with_headers_1.InteractsWithHeaders {
/**
* Create a new instance for the given `request`.
*
* @param {Object} request
*/
constructor(request) {
request = request || {};
super(request.headers);
this.request = request;
}
/**
* Returns the client IP address.
*
* @returns {String|undefined}
*/
getClientIp() {
var _a, _b, _c, _d, _e;
return (_e = (_d = (_c = (_b = (_a = this.fromHeaders()) !== null && _a !== void 0 ? _a : this.fromSocket()) !== null && _b !== void 0 ? _b : this.fromConnection()) !== null && _c !== void 0 ? _c : this.fromInfo()) !== null && _d !== void 0 ? _d : this.fromRaw()) !== null && _e !== void 0 ? _e : this.fromRequestContext();
}
/**
* Returns the IP address if available in the HTTP request headers.
*
* @returns {String|undefined}
*/
fromHeaders() {
if (this.hasHeaders()) {
// nginx (if configured), load balancers (AWS ELB), and other proxies
if (this.hasIpInForwardedFor()) {
return this.getFromForwardedFor();
}
// Heroku, AWS EC2, nginx (if configured), and others
if (this.hasIpInHeader('x-client-ip')) {
return this.ipInHeader('x-client-ip');
}
// used by some proxies, like nginx
if (this.hasIpInHeader('x-real-ip')) {
return this.header('x-real-ip');
}
// Cloudflare
if (this.hasIpInHeader('cf-connecting-ip')) {
return this.header('cf-connecting-ip');
}
// Fastly and Firebase
if (this.hasIpInHeader('fastly-client-ip')) {
return this.header('fastly-client-ip');
}
// Akamai, Cloudflare
if (this.hasIpInHeader('true-client-ip')) {
return this.header('true-client-ip');
}
// Rackspace
if (this.hasIpInHeader('x-cluster-client-ip')) {
return this.header('x-cluster-client-ip');
}
}
}
/**
* Determine whether a valid IP address is available in the “x-forwarded-for” HTTP header.
*
* @returns {Boolean}
*/
hasIpInForwardedFor() {
return this.isIp(this.getFromForwardedFor());
}
/**
* Returns the IP address if available from the “x-forwarded-for” HTTP header.
*
* @returns {String|undefined}
*/
getFromForwardedFor() {
if (this.hasIpInHeader('x-forwarded-for')) {
return this.ipInHeader('x-forwarded-for');
}
if (this.hasIpInHeader('x-forwarded')) {
return this.ipInHeader('x-forwarded');
}
if (this.hasIpInHeader('forwarded-for')) {
return this.ipInHeader('forwarded-for');
}
if (this.hasIpInHeader('forwarded')) {
return this.ipInHeader('forwarded');
}
}
/**
* Determine whether the request IP comes from the given header `name`.
*
* @param {String} name - the header name
*
* @returns {Boolean}
*/
hasIpInHeader(name) {
return !!this.ipInHeader(name);
}
/**
* Returns the first IP address from the `name`d header.
*
* @param {String} name
*
* @returns {String|undefined}
*/
ipInHeader(name) {
var _a;
return this.findIp((_a = this.header(name)) === null || _a === void 0 ? void 0 : _a.split(','));
}
/**
* Returns the first valid IP address from the list of IP address candidates.
*
* @param {Array} ips
*
* @returns {String|undefined}
*/
findIp(ips = []) {
return ips
.map(ip => ip.trim())
.map(ip => this.removePortFrom(ip))
.find(ip => this.isIp(ip));
}
/**
* Returns the plain IP v4 address without the port number.
*
* @param {String} ip
*
* @returns {String}
*/
removePortFrom(ip) {
if (this.isIpv6(ip)) {
return ip;
}
return ip.includes(':')
? ip.split(':')[0]
: ip;
}
/**
* Returns the IP address if available in the request connection.
*
* @returns {String|undefined}
*/
fromConnection() {
if (!this.hasConnection()) {
return;
}
if (this.isIp(this.request.connection.remoteAddress)) {
return this.request.connection.remoteAddress;
}
if (!this.request.connection.socket) {
return;
}
if (this.isIp(this.request.connection.socket.remoteAddress)) {
return this.request.connection.socket.remoteAddress;
}
}
/**
* Determine whether the request has a `connection` object assigned.
*
* @returns {Boolean}
*/
hasConnection() {
return !!this.request.connection;
}
/**
* Returns the IP address if available in the request socket.
*
* @returns {String|undefined}
*/
fromSocket() {
if (!this.hasSocket()) {
return;
}
if (this.isIp(this.request.socket.remoteAddress)) {
return this.request.socket.remoteAddress;
}
}
/**
* Determine whether the request has a `socket` object assigned.
*
* @returns {Boolean}
*/
hasSocket() {
return !!this.request.socket;
}
/**
* Returns the IP address if available in the request info object.
*
* @returns {String|undefined}
*/
fromInfo() {
if (!this.hasInfo()) {
return;
}
if (this.isIp(this.request.info.remoteAddress)) {
return this.request.info.remoteAddress;
}
}
/**
* Determine whether the request has an `info` object assigned.
*
* @returns {Boolean}
*/
hasInfo() {
return !!this.request.info;
}
/**
* Returns the IP address if available from the raw request object. The
* `raw` request object is typically available in web frameworks like
* Fastify or hapi providing the original Node.js request instance.
*
* @returns {String|undefined}
*/
fromRaw() {
if (this.hasRaw()) {
return new Request(this.request.raw).getClientIp();
}
}
/**
* Determine whether the request has a `requestContext` object assigned.
*
* @returns {Boolean}
*/
hasRaw() {
return !!this.raw();
}
/**
* Returns the raw request object.
*
* @returns {Object}
*/
raw() {
return this.request.raw;
}
/**
* Returns the IP address if available in the request context. The request
* context is typically available in serverless functions, like AWS Lambda.
*
* @returns {String|undefined}
*/
fromRequestContext() {
// AWS API Gateway/Lambda
if (!this.hasRequestContext()) {
return;
}
if (!this.requestContext().identity) {
return;
}
if (this.isIp(this.requestContext().identity.sourceIp)) {
return this.requestContext().identity.sourceIp;
}
}
/**
* Determine whether the request has a `requestContext` object assigned.
*
* @returns {Boolean}
*/
hasRequestContext() {
return !!this.requestContext();
}
/**
* Returns the request context.
*
* @returns {*}
*/
requestContext() {
return this.request.requestContext;
}
/**
* Determine whether it’s a valid `ip` address.
*
* @param {String} ip
*
* @returns {Boolean}
*/
isIp(ip) {
return this.isIpv4(ip) || this.isIpv6(ip);
}
/**
* Determine whether the given `ip` address is a valid IP v4 address.
*
* @param {String} ip
*
* @returns {Boolean}
*/
isIpv4(ip) {
return net_1.default.isIP(ip !== null && ip !== void 0 ? ip : '') === 4;
}
/**
* Determine whether the given `ip` address is a valid IP v4 address.
*
* @param {String} ip
*
* @returns {Boolean}
*/
isIpv6(ip) {
return net_1.default.isIP(ip !== null && ip !== void 0 ? ip : '') === 6;
}
}
exports.Request = Request;