File Explorer

/var/runtime/node_modules/@aws-sdk/node_modules/undici/lib/core

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

request.js12.5 KB · 465 lines
'use strict' const {  InvalidArgumentError,  NotSupportedError} = require('./errors')const assert = require('node:assert')const {  isValidHTTPToken,  isValidHeaderValue,  isStream,  destroy,  isBuffer,  isFormDataLike,  isIterable,  hasSafeIterator,  isBlobLike,  serializePathWithQuery,  assertRequestHandler,  getServerName,  normalizedMethodRecords,  getProtocolFromUrlString} = require('./util')const { channels } = require('./diagnostics.js')const { headerNameLowerCasedRecord } = require('./constants') // Verifies that a given path is valid does not contain control chars \x00 to \x20const invalidPathRegex = /[^\u0021-\u00ff]/ function isValidContentLengthHeaderValue (val) {  if (typeof val !== 'string' || val.length === 0) {    return false  }   for (let i = 0; i < val.length; i++) {    const charCode = val.charCodeAt(i)    if (charCode < 48 || charCode > 57) {      return false    }  }   return true} const kHandler = Symbol('handler') class Request {  constructor (origin, {    path,    method,    body,    headers,    query,    idempotent,    blocking,    upgrade,    headersTimeout,    bodyTimeout,    reset,    expectContinue,    servername,    throwOnError,    maxRedirections,    typeOfService  }, handler) {    if (typeof path !== 'string') {      throw new InvalidArgumentError('path must be a string')    } else if (      path[0] !== '/' &&      !(path.startsWith('http://') || path.startsWith('https://')) &&      method !== 'CONNECT'    ) {      throw new InvalidArgumentError('path must be an absolute URL or start with a slash')    } else if (invalidPathRegex.test(path)) {      throw new InvalidArgumentError('invalid request path')    }     if (typeof method !== 'string') {      throw new InvalidArgumentError('method must be a string')    } else if (normalizedMethodRecords[method] === undefined && !isValidHTTPToken(method)) {      throw new InvalidArgumentError('invalid request method')    }     if (upgrade && typeof upgrade !== 'string') {      throw new InvalidArgumentError('upgrade must be a string')    }     if (upgrade && !isValidHeaderValue(upgrade)) {      throw new InvalidArgumentError('invalid upgrade header')    }     if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {      throw new InvalidArgumentError('invalid headersTimeout')    }     if (bodyTimeout != null && (!Number.isFinite(bodyTimeout) || bodyTimeout < 0)) {      throw new InvalidArgumentError('invalid bodyTimeout')    }     if (reset != null && typeof reset !== 'boolean') {      throw new InvalidArgumentError('invalid reset')    }     if (expectContinue != null && typeof expectContinue !== 'boolean') {      throw new InvalidArgumentError('invalid expectContinue')    }     if (throwOnError != null) {      throw new InvalidArgumentError('invalid throwOnError')    }     if (maxRedirections != null && maxRedirections !== 0) {      throw new InvalidArgumentError('maxRedirections is not supported, use the redirect interceptor')    }     if (typeOfService != null && (!Number.isInteger(typeOfService) || typeOfService < 0 || typeOfService > 255)) {      throw new InvalidArgumentError('typeOfService must be an integer between 0 and 255')    }     this.headersTimeout = headersTimeout     this.bodyTimeout = bodyTimeout     this.method = method     this.typeOfService = typeOfService ?? 0     this.abort = null     if (body == null) {      this.body = null    } else if (isStream(body)) {      this.body = body       const rState = this.body._readableState      if (!rState || !rState.autoDestroy) {        this.endHandler = function autoDestroy () {          destroy(this)        }        this.body.on('end', this.endHandler)      }       this.errorHandler = err => {        if (this.abort) {          this.abort(err)        } else {          this.error = err        }      }      this.body.on('error', this.errorHandler)    } else if (isBuffer(body)) {      this.body = body.byteLength ? body : null    } else if (ArrayBuffer.isView(body)) {      this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null    } else if (body instanceof ArrayBuffer) {      this.body = body.byteLength ? Buffer.from(body) : null    } else if (typeof body === 'string') {      this.body = body.length ? Buffer.from(body) : null    } else if (isFormDataLike(body) || isIterable(body) || isBlobLike(body)) {      this.body = body    } else {      throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')    }     this.completed = false    this.aborted = false     this.upgrade = upgrade || null     this.path = query ? serializePathWithQuery(path, query) : path     // TODO: shall we maybe standardize it to an URL object?    this.origin = origin     this.protocol = getProtocolFromUrlString(origin)     this.idempotent = idempotent == null      ? method === 'HEAD' || method === 'GET'      : idempotent     this.blocking = blocking ?? this.method !== 'HEAD'     this.reset = reset == null ? null : reset     this.host = null     this.contentLength = null     this.contentType = null     this.headers = []     // Only for H2    this.expectContinue = expectContinue != null ? expectContinue : false     if (Array.isArray(headers)) {      if (headers.length % 2 !== 0) {        throw new InvalidArgumentError('headers array must be even')      }      for (let i = 0; i < headers.length; i += 2) {        processHeader(this, headers[i], headers[i + 1])      }    } else if (headers && typeof headers === 'object') {      if (hasSafeIterator(headers)) {        for (const header of headers) {          if (!Array.isArray(header) || header.length !== 2) {            throw new InvalidArgumentError('headers must be in key-value pair format')          }          processHeader(this, header[0], header[1])        }      } else {        const keys = Object.keys(headers)        for (let i = 0; i < keys.length; ++i) {          processHeader(this, keys[i], headers[keys[i]])        }      }    } else if (headers != null) {      throw new InvalidArgumentError('headers must be an object or an array')    }     assertRequestHandler(handler, method, upgrade)     this.servername = servername || getServerName(this.host) || null     this[kHandler] = handler     if (channels.create.hasSubscribers) {      channels.create.publish({ request: this })    }  }   onBodySent (chunk) {    if (channels.bodyChunkSent.hasSubscribers) {      channels.bodyChunkSent.publish({ request: this, chunk })    }    if (this[kHandler].onBodySent) {      try {        return this[kHandler].onBodySent(chunk)      } catch (err) {        this.abort(err)      }    }  }   onRequestSent () {    if (channels.bodySent.hasSubscribers) {      channels.bodySent.publish({ request: this })    }     if (this[kHandler].onRequestSent) {      try {        return this[kHandler].onRequestSent()      } catch (err) {        this.abort(err)      }    }  }   onConnect (abort) {    assert(!this.aborted)    assert(!this.completed)     if (this.error) {      abort(this.error)    } else {      this.abort = abort      return this[kHandler].onConnect(abort)    }  }   onResponseStarted () {    return this[kHandler].onResponseStarted?.()  }   onHeaders (statusCode, headers, resume, statusText) {    assert(!this.aborted)    assert(!this.completed)     if (channels.headers.hasSubscribers) {      channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })    }     try {      return this[kHandler].onHeaders(statusCode, headers, resume, statusText)    } catch (err) {      this.abort(err)    }  }   onData (chunk) {    assert(!this.aborted)    assert(!this.completed)     if (channels.bodyChunkReceived.hasSubscribers) {      channels.bodyChunkReceived.publish({ request: this, chunk })    }    try {      return this[kHandler].onData(chunk)    } catch (err) {      this.abort(err)      return false    }  }   onUpgrade (statusCode, headers, socket) {    assert(!this.aborted)    assert(!this.completed)     return this[kHandler].onUpgrade(statusCode, headers, socket)  }   onComplete (trailers) {    this.onFinally()     assert(!this.aborted)    assert(!this.completed)     this.completed = true    if (channels.trailers.hasSubscribers) {      channels.trailers.publish({ request: this, trailers })    }     try {      return this[kHandler].onComplete(trailers)    } catch (err) {      // TODO (fix): This might be a bad idea?      this.onError(err)    }  }   onError (error) {    this.onFinally()     if (channels.error.hasSubscribers) {      channels.error.publish({ request: this, error })    }     if (this.aborted) {      return    }    this.aborted = true     return this[kHandler].onError(error)  }   onFinally () {    if (this.errorHandler) {      this.body.off('error', this.errorHandler)      this.errorHandler = null    }     if (this.endHandler) {      this.body.off('end', this.endHandler)      this.endHandler = null    }  }   addHeader (key, value) {    processHeader(this, key, value)    return this  }} function processHeader (request, key, val) {  if (val && (typeof val === 'object' && !Array.isArray(val))) {    throw new InvalidArgumentError(`invalid ${key} header`)  } else if (val === undefined) {    return  }   let headerName = headerNameLowerCasedRecord[key]   if (headerName === undefined) {    headerName = key.toLowerCase()    if (headerNameLowerCasedRecord[headerName] === undefined && !isValidHTTPToken(headerName)) {      throw new InvalidArgumentError('invalid header key')    }  }   if (Array.isArray(val)) {    const arr = []    for (let i = 0; i < val.length; i++) {      if (typeof val[i] === 'string') {        if (!isValidHeaderValue(val[i])) {          throw new InvalidArgumentError(`invalid ${key} header`)        }        arr.push(val[i])      } else if (val[i] === null) {        arr.push('')      } else if (typeof val[i] === 'object') {        throw new InvalidArgumentError(`invalid ${key} header`)      } else {        // Coerce primitives (and reject unsafe coercions such as functions        // with a crafted toString/Symbol.toPrimitive).        const str = `${val[i]}`        if (!isValidHeaderValue(str)) {          throw new InvalidArgumentError(`invalid ${key} header`)        }        arr.push(str)      }    }    val = arr  } else if (typeof val === 'string') {    if (!isValidHeaderValue(val)) {      throw new InvalidArgumentError(`invalid ${key} header`)    }  } else if (val === null) {    val = ''  } else {    // Coerce primitives (and reject unsafe coercions such as functions    // with a crafted toString/Symbol.toPrimitive).    val = `${val}`    if (!isValidHeaderValue(val)) {      throw new InvalidArgumentError(`invalid ${key} header`)    }  }   if (headerName === 'host') {    if (request.host !== null) {      throw new InvalidArgumentError('duplicate host header')    }    if (typeof val !== 'string') {      throw new InvalidArgumentError('invalid host header')    }    // Consumed by Client    request.host = val  } else if (headerName === 'content-length') {    if (request.contentLength !== null) {      throw new InvalidArgumentError('duplicate content-length header')    }    if (!isValidContentLengthHeaderValue(val)) {      throw new InvalidArgumentError('invalid content-length header')    }    request.contentLength = parseInt(val, 10)  } else if (request.contentType === null && headerName === 'content-type') {    request.contentType = val    request.headers.push(key, val)  } else if (headerName === 'transfer-encoding' || headerName === 'keep-alive' || headerName === 'upgrade') {    throw new InvalidArgumentError(`invalid ${headerName} header`)  } else if (headerName === 'connection') {    // Per RFC 7230 Section 6.1, Connection header can contain    // a comma-separated list of connection option tokens (header names)    const value = typeof val === 'string' ? val : null    if (value === null) {      throw new InvalidArgumentError('invalid connection header')    }     for (const token of value.toLowerCase().split(',')) {      const trimmed = token.trim()      if (!isValidHTTPToken(trimmed)) {        throw new InvalidArgumentError('invalid connection header')      }      if (trimmed === 'close') {        request.reset = true      }    }  } else if (headerName === 'expect') {    throw new NotSupportedError('expect header not supported')  } else {    request.headers.push(key, val)  }} module.exports = Request