CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
request.js351 linesDownload Raw Back to lib
1'use strict'2 3const proxyAddr = require('@fastify/proxy-addr')4const {5  kHasBeenDecorated,6  kSchemaBody,7  kSchemaHeaders,8  kSchemaParams,9  kSchemaQuerystring,10  kSchemaController,11  kOptions,12  kRequestCacheValidateFns,13  kRouteContext,14  kRequestOriginalUrl15} = require('./symbols')16const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')17 18const HTTP_PART_SYMBOL_MAP = {19  body: kSchemaBody,20  headers: kSchemaHeaders,21  params: kSchemaParams,22  querystring: kSchemaQuerystring,23  query: kSchemaQuerystring24}25 26function Request (id, params, req, query, log, context) {27  this.id = id28  this[kRouteContext] = context29  this.params = params30  this.raw = req31  this.query = query32  this.log = log33  this.body = undefined34}35Request.props = []36 37function getTrustProxyFn (tp) {38  if (typeof tp === 'function') {39    return tp40  }41  if (tp === true) {42    // Support trusting everything43    return null44  }45  if (typeof tp === 'number') {46    // Support trusting hop count47    return function (a, i) { return i < tp }48  }49  if (typeof tp === 'string') {50    // Support comma-separated tps51    const values = tp.split(',').map(it => it.trim())52    return proxyAddr.compile(values)53  }54  return proxyAddr.compile(tp)55}56 57function buildRequest (R, trustProxy) {58  if (trustProxy) {59    return buildRequestWithTrustProxy(R, trustProxy)60  }61 62  return buildRegularRequest(R)63}64 65function buildRegularRequest (R) {66  const props = R.props.slice()67  function _Request (id, params, req, query, log, context) {68    this.id = id69    this[kRouteContext] = context70    this.params = params71    this.raw = req72    this.query = query73    this.log = log74    this.body = undefined75 76    let prop77    for (let i = 0; i < props.length; i++) {78      prop = props[i]79      this[prop.key] = prop.value80    }81  }82  Object.setPrototypeOf(_Request.prototype, R.prototype)83  Object.setPrototypeOf(_Request, R)84  _Request.props = props85  _Request.parent = R86 87  return _Request88}89 90function getLastEntryInMultiHeaderValue (headerValue) {91  // we use the last one if the header is set more than once92  const lastIndex = headerValue.lastIndexOf(',')93  return lastIndex === -1 ? headerValue.trim() : headerValue.slice(lastIndex + 1).trim()94}95 96function buildRequestWithTrustProxy (R, trustProxy) {97  const _Request = buildRegularRequest(R)98  const proxyFn = getTrustProxyFn(trustProxy)99 100  // This is a more optimized version of decoration101  _Request[kHasBeenDecorated] = true102 103  Object.defineProperties(_Request.prototype, {104    ip: {105      get () {106        const addrs = proxyAddr.all(this.raw, proxyFn)107        return addrs[addrs.length - 1]108      }109    },110    ips: {111      get () {112        return proxyAddr.all(this.raw, proxyFn)113      }114    },115    host: {116      get () {117        if (this.ip !== undefined && this.headers['x-forwarded-host']) {118          return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])119        }120        /**121         * The last fallback supports the following cases:122         * 1. http.requireHostHeader === false123         * 2. HTTP/1.0 without a Host Header124         * 3. Headers schema that may remove the Host Header125         */126        return this.headers.host ?? this.headers[':authority'] ?? ''127      }128    },129    protocol: {130      get () {131        if (this.headers['x-forwarded-proto']) {132          return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])133        }134        if (this.socket) {135          return this.socket.encrypted ? 'https' : 'http'136        }137      }138    }139  })140 141  return _Request142}143 144Object.defineProperties(Request.prototype, {145  server: {146    get () {147      return this[kRouteContext].server148    }149  },150  url: {151    get () {152      return this.raw.url153    }154  },155  originalUrl: {156    get () {157      /* istanbul ignore else */158      if (!this[kRequestOriginalUrl]) {159        this[kRequestOriginalUrl] = this.raw.originalUrl || this.raw.url160      }161      return this[kRequestOriginalUrl]162    }163  },164  method: {165    get () {166      return this.raw.method167    }168  },169  routeOptions: {170    get () {171      const context = this[kRouteContext]172      const routeLimit = context._parserOptions.limit173      const serverLimit = context.server.initialConfig.bodyLimit174      const version = context.server.hasConstraintStrategy('version') ? this.raw.headers['accept-version'] : undefined175      const options = {176        method: context.config?.method,177        url: context.config?.url,178        bodyLimit: (routeLimit || serverLimit),179        attachValidation: context.attachValidation,180        logLevel: context.logLevel,181        exposeHeadRoute: context.exposeHeadRoute,182        prefixTrailingSlash: context.prefixTrailingSlash,183        handler: context.handler,184        version185      }186 187      Object.defineProperties(options, {188        config: {189          get: () => context.config190        },191        schema: {192          get: () => context.schema193        }194      })195 196      return Object.freeze(options)197    }198  },199  is404: {200    get () {201      return this[kRouteContext].config?.url === undefined202    }203  },204  socket: {205    get () {206      return this.raw.socket207    }208  },209  ip: {210    get () {211      if (this.socket) {212        return this.socket.remoteAddress213      }214    }215  },216  host: {217    get () {218      /**219       * The last fallback supports the following cases:220       * 1. http.requireHostHeader === false221       * 2. HTTP/1.0 without a Host Header222       * 3. Headers schema that may remove the Host Header223       */224      return this.raw.headers.host ?? this.raw.headers[':authority'] ?? ''225    }226  },227  hostname: {228    get () {229      return this.host.split(':', 1)[0]230    }231  },232  port: {233    get () {234      // first try taking port from host235      const portFromHost = parseInt(this.host.split(':').slice(-1)[0])236      if (!isNaN(portFromHost)) {237        return portFromHost238      }239      // now fall back to port from host/:authority header240      const host = (this.headers.host ?? this.headers[':authority'] ?? '')241      const portFromHeader = parseInt(host.split(':').slice(-1)[0])242      if (!isNaN(portFromHeader)) {243        return portFromHeader244      }245      // fall back to null246      return null247    }248  },249  protocol: {250    get () {251      if (this.socket) {252        return this.socket.encrypted ? 'https' : 'http'253      }254    }255  },256  headers: {257    get () {258      if (this.additionalHeaders) {259        return Object.assign({}, this.raw.headers, this.additionalHeaders)260      }261      return this.raw.headers262    },263    set (headers) {264      this.additionalHeaders = headers265    }266  },267  getValidationFunction: {268    value: function (httpPartOrSchema) {269      if (typeof httpPartOrSchema === 'string') {270        const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]271        return this[kRouteContext][symbol]272      } else if (typeof httpPartOrSchema === 'object') {273        return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)274      }275    }276  },277  compileValidationSchema: {278    value: function (schema, httpPart = null) {279      const { method, url } = this280 281      if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {282        return this[kRouteContext][kRequestCacheValidateFns].get(schema)283      }284 285      const validatorCompiler = this[kRouteContext].validatorCompiler ||286        this.server[kSchemaController].validatorCompiler ||287        (288          // We compile the schemas if no custom validatorCompiler is provided289          // nor set290          this.server[kSchemaController].setupValidator(this.server[kOptions]) ||291          this.server[kSchemaController].validatorCompiler292        )293 294      const validateFn = validatorCompiler({295        schema,296        method,297        url,298        httpPart299      })300 301      // We create a WeakMap to compile the schema only once302      // Its done lazily to avoid add overhead by creating the WeakMap303      // if it is not used304      // TODO: Explore a central cache for all the schemas shared across305      // encapsulated contexts306      if (this[kRouteContext][kRequestCacheValidateFns] == null) {307        this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()308      }309 310      this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)311 312      return validateFn313    }314  },315  validateInput: {316    value: function (input, schema, httpPart) {317      httpPart = typeof schema === 'string' ? schema : httpPart318 319      const symbol = (httpPart != null && typeof httpPart === 'string') && HTTP_PART_SYMBOL_MAP[httpPart]320      let validate321 322      if (symbol) {323        // Validate using the HTTP Request Part schema324        validate = this[kRouteContext][symbol]325      }326 327      // We cannot compile if the schema is missed328      if (validate == null && (schema == null ||329        typeof schema !== 'object' ||330        Array.isArray(schema))331      ) {332        throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart)333      }334 335      if (validate == null) {336        if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {337          validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)338        } else {339          // We proceed to compile if there's no validate function yet340          validate = this.compileValidationSchema(schema, httpPart)341        }342      }343 344      return validate(input)345    }346  }347})348 349module.exports = Request350module.exports.buildRequest = buildRequest351