Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
http.js686 linesDownload Raw Back to adapters
1'use strict';2 3import utils from './../utils.js';4import settle from './../core/settle.js';5import buildFullPath from '../core/buildFullPath.js';6import buildURL from './../helpers/buildURL.js';7import {getProxyForUrl} from 'proxy-from-env';8import http from 'http';9import https from 'https';10import util from 'util';11import followRedirects from 'follow-redirects';12import zlib from 'zlib';13import {VERSION} from '../env/data.js';14import transitionalDefaults from '../defaults/transitional.js';15import AxiosError from '../core/AxiosError.js';16import CanceledError from '../cancel/CanceledError.js';17import platform from '../platform/index.js';18import fromDataURI from '../helpers/fromDataURI.js';19import stream from 'stream';20import AxiosHeaders from '../core/AxiosHeaders.js';21import AxiosTransformStream from '../helpers/AxiosTransformStream.js';22import {EventEmitter} from 'events';23import formDataToStream from "../helpers/formDataToStream.js";24import readBlob from "../helpers/readBlob.js";25import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js';26import callbackify from "../helpers/callbackify.js";27 28const zlibOptions = {29  flush: zlib.constants.Z_SYNC_FLUSH,30  finishFlush: zlib.constants.Z_SYNC_FLUSH31};32 33const brotliOptions = {34  flush: zlib.constants.BROTLI_OPERATION_FLUSH,35  finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH36}37 38const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);39 40const {http: httpFollow, https: httpsFollow} = followRedirects;41 42const isHttps = /https:?/;43 44const supportedProtocols = platform.protocols.map(protocol => {45  return protocol + ':';46});47 48/**49 * If the proxy or config beforeRedirects functions are defined, call them with the options50 * object.51 *52 * @param {Object<string, any>} options - The options object that was passed to the request.53 *54 * @returns {Object<string, any>}55 */56function dispatchBeforeRedirect(options, responseDetails) {57  if (options.beforeRedirects.proxy) {58    options.beforeRedirects.proxy(options);59  }60  if (options.beforeRedirects.config) {61    options.beforeRedirects.config(options, responseDetails);62  }63}64 65/**66 * If the proxy or config afterRedirects functions are defined, call them with the options67 *68 * @param {http.ClientRequestArgs} options69 * @param {AxiosProxyConfig} configProxy configuration from Axios options object70 * @param {string} location71 *72 * @returns {http.ClientRequestArgs}73 */74function setProxy(options, configProxy, location) {75  let proxy = configProxy;76  if (!proxy && proxy !== false) {77    const proxyUrl = getProxyForUrl(location);78    if (proxyUrl) {79      proxy = new URL(proxyUrl);80    }81  }82  if (proxy) {83    // Basic proxy authorization84    if (proxy.username) {85      proxy.auth = (proxy.username || '') + ':' + (proxy.password || '');86    }87 88    if (proxy.auth) {89      // Support proxy auth object form90      if (proxy.auth.username || proxy.auth.password) {91        proxy.auth = (proxy.auth.username || '') + ':' + (proxy.auth.password || '');92      }93      const base64 = Buffer94        .from(proxy.auth, 'utf8')95        .toString('base64');96      options.headers['Proxy-Authorization'] = 'Basic ' + base64;97    }98 99    options.headers.host = options.hostname + (options.port ? ':' + options.port : '');100    const proxyHost = proxy.hostname || proxy.host;101    options.hostname = proxyHost;102    // Replace 'host' since options is not a URL object103    options.host = proxyHost;104    options.port = proxy.port;105    options.path = location;106    if (proxy.protocol) {107      options.protocol = proxy.protocol.includes(':') ? proxy.protocol : `${proxy.protocol}:`;108    }109  }110 111  options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {112    // Configure proxy for redirected request, passing the original config proxy to apply113    // the exact same logic as if the redirected request was performed by axios directly.114    setProxy(redirectOptions, configProxy, redirectOptions.href);115  };116}117 118const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process';119 120// temporary hotfix121 122const wrapAsync = (asyncExecutor) => {123  return new Promise((resolve, reject) => {124    let onDone;125    let isDone;126 127    const done = (value, isRejected) => {128      if (isDone) return;129      isDone = true;130      onDone && onDone(value, isRejected);131    }132 133    const _resolve = (value) => {134      done(value);135      resolve(value);136    };137 138    const _reject = (reason) => {139      done(reason, true);140      reject(reason);141    }142 143    asyncExecutor(_resolve, _reject, (onDoneHandler) => (onDone = onDoneHandler)).catch(_reject);144  })145};146 147const resolveFamily = ({address, family}) => {148  if (!utils.isString(address)) {149    throw TypeError('address must be a string');150  }151  return ({152    address,153    family: family || (address.indexOf('.') < 0 ? 6 : 4)154  });155}156 157const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family});158 159/*eslint consistent-return:0*/160export default isHttpAdapterSupported && function httpAdapter(config) {161  return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {162    let {data, lookup, family} = config;163    const {responseType, responseEncoding} = config;164    const method = config.method.toUpperCase();165    let isDone;166    let rejected = false;167    let req;168 169    if (lookup) {170      const _lookup = callbackify(lookup, (value) => utils.isArray(value) ? value : [value]);171      // hotfix to support opt.all option which is required for node 20.x172      lookup = (hostname, opt, cb) => {173        _lookup(hostname, opt, (err, arg0, arg1) => {174          if (err) {175            return cb(err);176          }177 178          const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];179 180          opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);181        });182      }183    }184 185    // temporary internal emitter until the AxiosRequest class will be implemented186    const emitter = new EventEmitter();187 188    const onFinished = () => {189      if (config.cancelToken) {190        config.cancelToken.unsubscribe(abort);191      }192 193      if (config.signal) {194        config.signal.removeEventListener('abort', abort);195      }196 197      emitter.removeAllListeners();198    }199 200    onDone((value, isRejected) => {201      isDone = true;202      if (isRejected) {203        rejected = true;204        onFinished();205      }206    });207 208    function abort(reason) {209      emitter.emit('abort', !reason || reason.type ? new CanceledError(null, config, req) : reason);210    }211 212    emitter.once('abort', reject);213 214    if (config.cancelToken || config.signal) {215      config.cancelToken && config.cancelToken.subscribe(abort);216      if (config.signal) {217        config.signal.aborted ? abort() : config.signal.addEventListener('abort', abort);218      }219    }220 221    // Parse url222    const fullPath = buildFullPath(config.baseURL, config.url);223    const parsed = new URL(fullPath, 'http://localhost');224    const protocol = parsed.protocol || supportedProtocols[0];225 226    if (protocol === 'data:') {227      let convertedData;228 229      if (method !== 'GET') {230        return settle(resolve, reject, {231          status: 405,232          statusText: 'method not allowed',233          headers: {},234          config235        });236      }237 238      try {239        convertedData = fromDataURI(config.url, responseType === 'blob', {240          Blob: config.env && config.env.Blob241        });242      } catch (err) {243        throw AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config);244      }245 246      if (responseType === 'text') {247        convertedData = convertedData.toString(responseEncoding);248 249        if (!responseEncoding || responseEncoding === 'utf8') {250          convertedData = utils.stripBOM(convertedData);251        }252      } else if (responseType === 'stream') {253        convertedData = stream.Readable.from(convertedData);254      }255 256      return settle(resolve, reject, {257        data: convertedData,258        status: 200,259        statusText: 'OK',260        headers: new AxiosHeaders(),261        config262      });263    }264 265    if (supportedProtocols.indexOf(protocol) === -1) {266      return reject(new AxiosError(267        'Unsupported protocol ' + protocol,268        AxiosError.ERR_BAD_REQUEST,269        config270      ));271    }272 273    const headers = AxiosHeaders.from(config.headers).normalize();274 275    // Set User-Agent (required by some servers)276    // See https://github.com/axios/axios/issues/69277    // User-Agent is specified; handle case where no UA header is desired278    // Only set header if it hasn't been set in config279    headers.set('User-Agent', 'axios/' + VERSION, false);280 281    const onDownloadProgress = config.onDownloadProgress;282    const onUploadProgress = config.onUploadProgress;283    const maxRate = config.maxRate;284    let maxUploadRate = undefined;285    let maxDownloadRate = undefined;286 287    // support for spec compliant FormData objects288    if (utils.isSpecCompliantForm(data)) {289      const userBoundary = headers.getContentType(/boundary=([-_\w\d]{10,70})/i);290 291      data = formDataToStream(data, (formHeaders) => {292        headers.set(formHeaders);293      }, {294        tag: `axios-${VERSION}-boundary`,295        boundary: userBoundary && userBoundary[1] || undefined296      });297      // support for https://www.npmjs.com/package/form-data api298    } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) {299      headers.set(data.getHeaders());300 301      if (!headers.hasContentLength()) {302        try {303          const knownLength = await util.promisify(data.getLength).call(data);304          Number.isFinite(knownLength) && knownLength >= 0 && headers.setContentLength(knownLength);305          /*eslint no-empty:0*/306        } catch (e) {307        }308      }309    } else if (utils.isBlob(data)) {310      data.size && headers.setContentType(data.type || 'application/octet-stream');311      headers.setContentLength(data.size || 0);312      data = stream.Readable.from(readBlob(data));313    } else if (data && !utils.isStream(data)) {314      if (Buffer.isBuffer(data)) {315        // Nothing to do...316      } else if (utils.isArrayBuffer(data)) {317        data = Buffer.from(new Uint8Array(data));318      } else if (utils.isString(data)) {319        data = Buffer.from(data, 'utf-8');320      } else {321        return reject(new AxiosError(322          'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream',323          AxiosError.ERR_BAD_REQUEST,324          config325        ));326      }327 328      // Add Content-Length header if data exists329      headers.setContentLength(data.length, false);330 331      if (config.maxBodyLength > -1 && data.length > config.maxBodyLength) {332        return reject(new AxiosError(333          'Request body larger than maxBodyLength limit',334          AxiosError.ERR_BAD_REQUEST,335          config336        ));337      }338    }339 340    const contentLength = utils.toFiniteNumber(headers.getContentLength());341 342    if (utils.isArray(maxRate)) {343      maxUploadRate = maxRate[0];344      maxDownloadRate = maxRate[1];345    } else {346      maxUploadRate = maxDownloadRate = maxRate;347    }348 349    if (data && (onUploadProgress || maxUploadRate)) {350      if (!utils.isStream(data)) {351        data = stream.Readable.from(data, {objectMode: false});352      }353 354      data = stream.pipeline([data, new AxiosTransformStream({355        length: contentLength,356        maxRate: utils.toFiniteNumber(maxUploadRate)357      })], utils.noop);358 359      onUploadProgress && data.on('progress', progress => {360        onUploadProgress(Object.assign(progress, {361          upload: true362        }));363      });364    }365 366    // HTTP basic authentication367    let auth = undefined;368    if (config.auth) {369      const username = config.auth.username || '';370      const password = config.auth.password || '';371      auth = username + ':' + password;372    }373 374    if (!auth && parsed.username) {375      const urlUsername = parsed.username;376      const urlPassword = parsed.password;377      auth = urlUsername + ':' + urlPassword;378    }379 380    auth && headers.delete('authorization');381 382    let path;383 384    try {385      path = buildURL(386        parsed.pathname + parsed.search,387        config.params,388        config.paramsSerializer389      ).replace(/^\?/, '');390    } catch (err) {391      const customErr = new Error(err.message);392      customErr.config = config;393      customErr.url = config.url;394      customErr.exists = true;395      return reject(customErr);396    }397 398    headers.set(399      'Accept-Encoding',400      'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''), false401      );402 403    const options = {404      path,405      method: method,406      headers: headers.toJSON(),407      agents: { http: config.httpAgent, https: config.httpsAgent },408      auth,409      protocol,410      family,411      beforeRedirect: dispatchBeforeRedirect,412      beforeRedirects: {}413    };414 415    // cacheable-lookup integration hotfix416    !utils.isUndefined(lookup) && (options.lookup = lookup);417 418    if (config.socketPath) {419      options.socketPath = config.socketPath;420    } else {421      options.hostname = parsed.hostname;422      options.port = parsed.port;423      setProxy(options, config.proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path);424    }425 426    let transport;427    const isHttpsRequest = isHttps.test(options.protocol);428    options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;429    if (config.transport) {430      transport = config.transport;431    } else if (config.maxRedirects === 0) {432      transport = isHttpsRequest ? https : http;433    } else {434      if (config.maxRedirects) {435        options.maxRedirects = config.maxRedirects;436      }437      if (config.beforeRedirect) {438        options.beforeRedirects.config = config.beforeRedirect;439      }440      transport = isHttpsRequest ? httpsFollow : httpFollow;441    }442 443    if (config.maxBodyLength > -1) {444      options.maxBodyLength = config.maxBodyLength;445    } else {446      // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited447      options.maxBodyLength = Infinity;448    }449 450    if (config.insecureHTTPParser) {451      options.insecureHTTPParser = config.insecureHTTPParser;452    }453 454    // Create the request455    req = transport.request(options, function handleResponse(res) {456      if (req.destroyed) return;457 458      const streams = [res];459 460      const responseLength = +res.headers['content-length'];461 462      if (onDownloadProgress) {463        const transformStream = new AxiosTransformStream({464          length: utils.toFiniteNumber(responseLength),465          maxRate: utils.toFiniteNumber(maxDownloadRate)466        });467 468        onDownloadProgress && transformStream.on('progress', progress => {469          onDownloadProgress(Object.assign(progress, {470            download: true471          }));472        });473 474        streams.push(transformStream);475      }476 477      // decompress the response body transparently if required478      let responseStream = res;479 480      // return the last request in case of redirects481      const lastRequest = res.req || req;482 483      // if decompress disabled we should not decompress484      if (config.decompress !== false && res.headers['content-encoding']) {485        // if no content, but headers still say that it is encoded,486        // remove the header not confuse downstream operations487        if (method === 'HEAD' || res.statusCode === 204) {488          delete res.headers['content-encoding'];489        }490 491        switch ((res.headers['content-encoding'] || '').toLowerCase()) {492        /*eslint default-case:0*/493        case 'gzip':494        case 'x-gzip':495        case 'compress':496        case 'x-compress':497          // add the unzipper to the body stream processing pipeline498          streams.push(zlib.createUnzip(zlibOptions));499 500          // remove the content-encoding in order to not confuse downstream operations501          delete res.headers['content-encoding'];502          break;503        case 'deflate':504          streams.push(new ZlibHeaderTransformStream());505 506          // add the unzipper to the body stream processing pipeline507          streams.push(zlib.createUnzip(zlibOptions));508 509          // remove the content-encoding in order to not confuse downstream operations510          delete res.headers['content-encoding'];511          break;512        case 'br':513          if (isBrotliSupported) {514            streams.push(zlib.createBrotliDecompress(brotliOptions));515            delete res.headers['content-encoding'];516          }517        }518      }519 520      responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];521 522      const offListeners = stream.finished(responseStream, () => {523        offListeners();524        onFinished();525      });526 527      const response = {528        status: res.statusCode,529        statusText: res.statusMessage,530        headers: new AxiosHeaders(res.headers),531        config,532        request: lastRequest533      };534 535      if (responseType === 'stream') {536        response.data = responseStream;537        settle(resolve, reject, response);538      } else {539        const responseBuffer = [];540        let totalResponseBytes = 0;541 542        responseStream.on('data', function handleStreamData(chunk) {543          responseBuffer.push(chunk);544          totalResponseBytes += chunk.length;545 546          // make sure the content length is not over the maxContentLength if specified547          if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) {548            // stream.destroy() emit aborted event before calling reject() on Node.js v16549            rejected = true;550            responseStream.destroy();551            reject(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded',552              AxiosError.ERR_BAD_RESPONSE, config, lastRequest));553          }554        });555 556        responseStream.on('aborted', function handlerStreamAborted() {557          if (rejected) {558            return;559          }560 561          const err = new AxiosError(562            'maxContentLength size of ' + config.maxContentLength + ' exceeded',563            AxiosError.ERR_BAD_RESPONSE,564            config,565            lastRequest566          );567          responseStream.destroy(err);568          reject(err);569        });570 571        responseStream.on('error', function handleStreamError(err) {572          if (req.destroyed) return;573          reject(AxiosError.from(err, null, config, lastRequest));574        });575 576        responseStream.on('end', function handleStreamEnd() {577          try {578            let responseData = responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer);579            if (responseType !== 'arraybuffer') {580              responseData = responseData.toString(responseEncoding);581              if (!responseEncoding || responseEncoding === 'utf8') {582                responseData = utils.stripBOM(responseData);583              }584            }585            response.data = responseData;586          } catch (err) {587            return reject(AxiosError.from(err, null, config, response.request, response));588          }589          settle(resolve, reject, response);590        });591      }592 593      emitter.once('abort', err => {594        if (!responseStream.destroyed) {595          responseStream.emit('error', err);596          responseStream.destroy();597        }598      });599    });600 601    emitter.once('abort', err => {602      reject(err);603      req.destroy(err);604    });605 606    // Handle errors607    req.on('error', function handleRequestError(err) {608      // @todo remove609      // if (req.aborted && err.code !== AxiosError.ERR_FR_TOO_MANY_REDIRECTS) return;610      reject(AxiosError.from(err, null, config, req));611    });612 613    // set tcp keep alive to prevent drop connection by peer614    req.on('socket', function handleRequestSocket(socket) {615      // default interval of sending ack packet is 1 minute616      socket.setKeepAlive(true, 1000 * 60);617    });618 619    // Handle request timeout620    if (config.timeout) {621      // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.622      const timeout = parseInt(config.timeout, 10);623 624      if (Number.isNaN(timeout)) {625        reject(new AxiosError(626          'error trying to parse `config.timeout` to int',627          AxiosError.ERR_BAD_OPTION_VALUE,628          config,629          req630        ));631 632        return;633      }634 635      // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.636      // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.637      // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.638      // And then these socket which be hang up will devouring CPU little by little.639      // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.640      req.setTimeout(timeout, function handleRequestTimeout() {641        if (isDone) return;642        let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';643        const transitional = config.transitional || transitionalDefaults;644        if (config.timeoutErrorMessage) {645          timeoutErrorMessage = config.timeoutErrorMessage;646        }647        reject(new AxiosError(648          timeoutErrorMessage,649          transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,650          config,651          req652        ));653        abort();654      });655    }656 657 658    // Send the request659    if (utils.isStream(data)) {660      let ended = false;661      let errored = false;662 663      data.on('end', () => {664        ended = true;665      });666 667      data.once('error', err => {668        errored = true;669        req.destroy(err);670      });671 672      data.on('close', () => {673        if (!ended && !errored) {674          abort(new CanceledError('Request stream has been aborted', config, req));675        }676      });677 678      data.pipe(req);679    } else {680      req.end(data);681    }682  });683}684 685export const __setProxy = setProxy;686 
codekingpro/portable-devtools · Team Ai